]> git.sesse.net Git - vlc/blob - plugins/filter/deinterlace.c
* Forgot a file in my last commit.
[vlc] / plugins / filter / deinterlace.c
1 /*****************************************************************************
2  * deinterlace.c : deinterlacer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: deinterlace.c,v 1.1 2001/12/30 07:09:55 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <errno.h>
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <videolan/vlc.h>
32
33 #include "video.h"
34 #include "video_output.h"
35
36 #include "filter_common.h"
37
38 #define DEINTERLACE_MODE_BOB     1
39 #define DEINTERLACE_MODE_BLEND   2
40
41 /*****************************************************************************
42  * Capabilities defined in the other files.
43  *****************************************************************************/
44 static void vout_getfunctions( function_list_t * p_function_list );
45
46 static void *memblend( void *, const void *, const void *, size_t );
47
48 /*****************************************************************************
49  * Build configuration tree.
50  *****************************************************************************/
51 MODULE_CONFIG_START
52 MODULE_CONFIG_STOP
53
54 MODULE_INIT_START
55     SET_DESCRIPTION( "deinterlacing module" )
56     /* Capability score set to 0 because we don't want to be spawned
57      * as a video output unless explicitly requested to */
58     ADD_CAPABILITY( VOUT, 0 )
59     ADD_SHORTCUT( "deinterlace" )
60 MODULE_INIT_STOP
61
62 MODULE_ACTIVATE_START
63     vout_getfunctions( &p_module->p_functions->vout );
64 MODULE_ACTIVATE_STOP
65
66 MODULE_DEACTIVATE_START
67 MODULE_DEACTIVATE_STOP
68
69 /*****************************************************************************
70  * vout_sys_t: Deinterlace video output method descriptor
71  *****************************************************************************
72  * This structure is part of the video output thread descriptor.
73  * It describes the Deinterlace specific properties of an output thread.
74  *****************************************************************************/
75 typedef struct vout_sys_s
76 {
77     int i_mode;
78     struct vout_thread_s *p_vout;
79
80 } vout_sys_t;
81
82 /*****************************************************************************
83  * Local prototypes
84  *****************************************************************************/
85 static int  vout_Probe     ( probedata_t *p_data );
86 static int  vout_Create    ( struct vout_thread_s * );
87 static int  vout_Init      ( struct vout_thread_s * );
88 static void vout_End       ( struct vout_thread_s * );
89 static void vout_Destroy   ( struct vout_thread_s * );
90 static int  vout_Manage    ( struct vout_thread_s * );
91 static void vout_Display   ( struct vout_thread_s *, struct picture_s * );
92
93 /*****************************************************************************
94  * Functions exported as capabilities. They are declared as static so that
95  * we don't pollute the namespace too much.
96  *****************************************************************************/
97 static void vout_getfunctions( function_list_t * p_function_list )
98 {
99     p_function_list->pf_probe = vout_Probe;
100     p_function_list->functions.vout.pf_create     = vout_Create;
101     p_function_list->functions.vout.pf_init       = vout_Init;
102     p_function_list->functions.vout.pf_end        = vout_End;
103     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
104     p_function_list->functions.vout.pf_manage     = vout_Manage;
105     p_function_list->functions.vout.pf_display    = vout_Display;
106     p_function_list->functions.vout.pf_setpalette = NULL;
107 }
108
109 /*****************************************************************************
110  * intf_Probe: return a score
111  *****************************************************************************/
112 static int vout_Probe( probedata_t *p_data )
113 {
114     return( 0 );
115 }
116
117 /*****************************************************************************
118  * vout_Create: allocates Deinterlace video thread output method
119  *****************************************************************************
120  * This function allocates and initializes a Deinterlace vout method.
121  *****************************************************************************/
122 static int vout_Create( vout_thread_t *p_vout )
123 {
124     char *psz_method;
125
126     /* Allocate structure */
127     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
128     if( p_vout->p_sys == NULL )
129     {
130         intf_ErrMsg("error: %s", strerror(ENOMEM) );
131         return( 1 );
132     }
133
134     /* Look what method was requested */
135     psz_method = main_GetPszVariable( VOUT_FILTER_VAR, "" );
136
137     while( *psz_method && *psz_method != ':' )
138     {
139         psz_method++;
140     }
141
142     if( !strcmp( psz_method, ":bob" ) )
143     {
144         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
145     }
146     else if( !strcmp( psz_method, ":blend" ) )
147     {
148         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BLEND;
149     }
150     else
151     {
152         intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
153                      "using deinterlace:bob" );
154         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
155     }
156
157     return( 0 );
158 }
159
160 /*****************************************************************************
161  * vout_Init: initialize Deinterlace video thread output method
162  *****************************************************************************/
163 static int vout_Init( vout_thread_t *p_vout )
164 {
165     int i_index;
166     char *psz_filter;
167     picture_t *p_pic;
168     
169     I_OUTPUTPICTURES = 0;
170
171     /* Initialize the output structure, full of directbuffers since we want
172      * the decoder to output directly to our structures. */
173     switch( p_vout->render.i_chroma )
174     {
175         case YUV_420_PICTURE:
176         case YUV_422_PICTURE:
177             p_vout->output.i_chroma = p_vout->render.i_chroma;
178             p_vout->output.i_width  = p_vout->render.i_width;
179             p_vout->output.i_height = p_vout->render.i_height;
180             p_vout->output.i_aspect = p_vout->render.i_aspect;
181             break;
182
183         default:
184             return( 0 ); /* unknown chroma */
185             break;
186     }
187
188     /* Try to open the real video output, with half the height our images */
189     psz_filter = main_GetPszVariable( VOUT_FILTER_VAR, "" );
190     main_PutPszVariable( VOUT_FILTER_VAR, "" );
191
192     intf_WarnMsg( 1, "filter: spawning the real video output" );
193
194     switch( p_vout->render.i_chroma )
195     {
196     case YUV_420_PICTURE:
197         switch( p_vout->p_sys->i_mode )
198         {
199         case DEINTERLACE_MODE_BOB:
200             p_vout->p_sys->p_vout =
201                 vout_CreateThread( NULL,
202                        p_vout->output.i_width, p_vout->output.i_height / 2,
203                        p_vout->output.i_chroma, p_vout->output.i_aspect );
204             break;
205
206         case DEINTERLACE_MODE_BLEND:
207             p_vout->p_sys->p_vout =
208                 vout_CreateThread( NULL,
209                        p_vout->output.i_width, p_vout->output.i_height,
210                        p_vout->output.i_chroma, p_vout->output.i_aspect );
211             break;
212         }
213         break;
214
215     case YUV_422_PICTURE:
216         p_vout->p_sys->p_vout =
217             vout_CreateThread( NULL,
218                        p_vout->output.i_width, p_vout->output.i_height,
219                        YUV_420_PICTURE, p_vout->output.i_aspect );
220         break;
221
222     default:
223         break;
224     }
225
226     /* Everything failed */
227     if( p_vout->p_sys->p_vout == NULL )
228     {
229         intf_ErrMsg( "filter error: can't open vout, aborting" );
230
231         return( 0 );
232     }
233  
234     main_PutPszVariable( VOUT_FILTER_VAR, psz_filter );
235
236     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
237
238     return( 0 );
239 }
240
241 /*****************************************************************************
242  * vout_End: terminate Deinterlace video thread output method
243  *****************************************************************************/
244 static void vout_End( vout_thread_t *p_vout )
245 {
246     int i_index;
247
248     /* Free the fake output buffers we allocated */
249     for( i_index = I_OUTPUTPICTURES ; i_index ; )
250     {
251         i_index--;
252         free( PP_OUTPUTPICTURE[ i_index ]->planes[ 0 ].p_data );
253     }
254 }
255
256 /*****************************************************************************
257  * vout_Destroy: destroy Deinterlace video thread output method
258  *****************************************************************************
259  * Terminate an output method created by DeinterlaceCreateOutputMethod
260  *****************************************************************************/
261 static void vout_Destroy( vout_thread_t *p_vout )
262 {
263     vout_DestroyThread( p_vout->p_sys->p_vout, NULL );
264
265     free( p_vout->p_sys );
266 }
267
268 /*****************************************************************************
269  * vout_Manage: handle Deinterlace events
270  *****************************************************************************
271  * This function should be called regularly by video output thread. It manages
272  * console events. It returns a non null value on error.
273  *****************************************************************************/
274 static int vout_Manage( vout_thread_t *p_vout )
275 {
276     return( 0 );
277 }
278
279 /*****************************************************************************
280  * vout_Display: displays previously rendered output
281  *****************************************************************************
282  * This function send the currently rendered image to Deinterlace image,
283  * waits until it is displayed and switch the two rendering buffers, preparing
284  * next frame.
285  *****************************************************************************/
286 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
287 {
288     picture_t *p_outpic;
289     int i_index, i_field;
290
291     for( i_field = 0 ; i_field < 2 ; i_field++ )
292     {
293         /* Get a structure from the video_output. */
294         while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout,
295                                                 0, 0, 0 ) )
296                   == NULL )
297         {
298             if( p_vout->b_die || p_vout->b_error )
299             {
300                 return;
301             }
302             msleep( VOUT_OUTMEM_SLEEP );
303         }   
304
305         /* XXX: completely arbitrary values ! */
306         vout_DatePicture( p_vout->p_sys->p_vout, p_outpic,
307                           mdate() + (mtime_t)(50000 + i_field * 20000) );
308
309         /* Copy image and skip lines */
310         for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
311         {
312             pixel_data_t *p_in, *p_out_end, *p_out;
313             int i_increment;
314
315             p_in = p_pic->planes[ i_index ].p_data
316                        + i_field * p_pic->planes[ i_index ].i_line_bytes;
317
318             p_out = p_outpic->planes[ i_index ].p_data;
319             p_out_end = p_out + p_outpic->planes[ i_index ].i_bytes;
320
321             switch( p_vout->render.i_chroma )
322             {
323             case YUV_420_PICTURE:
324
325                 switch( p_vout->p_sys->i_mode )
326                 {
327                 case DEINTERLACE_MODE_BOB:
328                     for( ; p_out < p_out_end ; )
329                     {
330                         FAST_MEMCPY( p_out, p_in,
331                                      p_pic->planes[ i_index ].i_line_bytes );
332
333                         p_out += p_pic->planes[ i_index ].i_line_bytes;
334                         p_in += 2 * p_pic->planes[ i_index ].i_line_bytes;
335                     }
336                     break;
337
338                 case DEINTERLACE_MODE_BLEND:
339                     if( i_index != Y_PLANE )
340                     {
341                         for( ; p_out < p_out_end ; )
342                         {
343                             FAST_MEMCPY( p_out, p_in,
344                                      p_pic->planes[ i_index ].i_line_bytes );
345
346                             p_out += p_pic->planes[ i_index ].i_line_bytes;
347
348                             FAST_MEMCPY( p_out, p_in,
349                                      p_pic->planes[ i_index ].i_line_bytes );
350
351                             p_out += p_pic->planes[ i_index ].i_line_bytes;
352                             p_in += 2 * p_pic->planes[ i_index ].i_line_bytes;
353                         }
354                         break;
355                     }
356
357                     if( i_field == 0 )
358                     {
359                         FAST_MEMCPY( p_out, p_in,
360                                      p_pic->planes[ i_index ].i_line_bytes );
361                         p_in += 2 * p_pic->planes[ i_index ].i_line_bytes;
362                         p_out += p_pic->planes[ i_index ].i_line_bytes;
363                     }
364
365                     for( ; p_out < p_out_end ; )
366                     {
367                         FAST_MEMCPY( p_out, p_in,
368                                      p_pic->planes[ i_index ].i_line_bytes );
369
370                         p_out += p_pic->planes[ i_index ].i_line_bytes;
371
372                         memblend( p_out, p_in, p_in + 2 * p_pic->planes[ i_index ].i_line_bytes, p_pic->planes[ i_index ].i_line_bytes );
373
374                         p_in += 2 * p_pic->planes[ i_index ].i_line_bytes;
375                         p_out += p_pic->planes[ i_index ].i_line_bytes;
376                     }
377                     break;
378                 }
379                 break;
380
381             case YUV_422_PICTURE:
382
383                 i_increment = 2 * p_pic->planes[ i_index ].i_line_bytes;
384
385                 if( i_index == Y_PLANE )
386                 {
387                     for( ; p_out < p_out_end ; )
388                     {
389                         FAST_MEMCPY( p_out, p_in,
390                                  p_pic->planes[ i_index ].i_line_bytes );
391                         p_out += p_pic->planes[ i_index ].i_line_bytes;
392                         FAST_MEMCPY( p_out, p_in,
393                                  p_pic->planes[ i_index ].i_line_bytes );
394                         p_out += p_pic->planes[ i_index ].i_line_bytes;
395                         p_in += i_increment;
396                     }
397                 }
398                 else
399                 {
400                     for( ; p_out < p_out_end ; )
401                     {
402                         FAST_MEMCPY( p_out, p_in,
403                                  p_pic->planes[ i_index ].i_line_bytes );
404                         p_out += p_pic->planes[ i_index ].i_line_bytes;
405                         p_in += i_increment;
406                     }
407                 }
408                 break;
409
410             default:
411                 break;
412             }
413         }
414
415         vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
416     }
417 }
418
419 static void *memblend( void *p_dest, const void *p_s1,
420                        const void *p_s2, size_t i_bytes )
421 {
422     u8* p_end = (u8*)p_dest + i_bytes - 8;
423
424     while( (u8*)p_dest < p_end )
425     {
426         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
427         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
428         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
429         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
430         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
431         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
432         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
433         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
434     }
435
436     p_end += 8;
437
438     while( (u8*)p_dest < p_end )
439     {
440         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
441     }
442
443     return p_dest;
444 }
445