]> git.sesse.net Git - vlc/blob - plugins/filter/deinterlace.c
* ./BUGS: added a list of known bugs. Please add your findings!
[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.3 2002/01/04 14:01:34 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     mtime_t last_date;
80
81 } vout_sys_t;
82
83 /*****************************************************************************
84  * Local prototypes
85  *****************************************************************************/
86 static int  vout_Probe     ( probedata_t *p_data );
87 static int  vout_Create    ( struct vout_thread_s * );
88 static int  vout_Init      ( struct vout_thread_s * );
89 static void vout_End       ( struct vout_thread_s * );
90 static void vout_Destroy   ( struct vout_thread_s * );
91 static int  vout_Manage    ( struct vout_thread_s * );
92 static void vout_Render    ( struct vout_thread_s *, struct picture_s * );
93 static void vout_Display   ( struct vout_thread_s *, struct picture_s * );
94
95 /*****************************************************************************
96  * Functions exported as capabilities. They are declared as static so that
97  * we don't pollute the namespace too much.
98  *****************************************************************************/
99 static void vout_getfunctions( function_list_t * p_function_list )
100 {
101     p_function_list->pf_probe = vout_Probe;
102     p_function_list->functions.vout.pf_create     = vout_Create;
103     p_function_list->functions.vout.pf_init       = vout_Init;
104     p_function_list->functions.vout.pf_end        = vout_End;
105     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
106     p_function_list->functions.vout.pf_manage     = vout_Manage;
107     p_function_list->functions.vout.pf_render     = vout_Render;
108     p_function_list->functions.vout.pf_display    = vout_Display;
109     p_function_list->functions.vout.pf_setpalette = NULL;
110 }
111
112 /*****************************************************************************
113  * intf_Probe: return a score
114  *****************************************************************************/
115 static int vout_Probe( probedata_t *p_data )
116 {
117     return( 0 );
118 }
119
120 /*****************************************************************************
121  * vout_Create: allocates Deinterlace video thread output method
122  *****************************************************************************
123  * This function allocates and initializes a Deinterlace vout method.
124  *****************************************************************************/
125 static int vout_Create( vout_thread_t *p_vout )
126 {
127     char *psz_method;
128
129     /* Allocate structure */
130     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
131     if( p_vout->p_sys == NULL )
132     {
133         intf_ErrMsg("error: %s", strerror(ENOMEM) );
134         return( 1 );
135     }
136
137     /* Look what method was requested */
138     psz_method = main_GetPszVariable( VOUT_FILTER_VAR, "" );
139
140     while( *psz_method && *psz_method != ':' )
141     {
142         psz_method++;
143     }
144
145     if( !strcmp( psz_method, ":bob" ) )
146     {
147         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
148     }
149     else if( !strcmp( psz_method, ":blend" ) )
150     {
151         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BLEND;
152     }
153     else
154     {
155         intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
156                      "using deinterlace:bob" );
157         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
158     }
159
160     return( 0 );
161 }
162
163 /*****************************************************************************
164  * vout_Init: initialize Deinterlace video thread output method
165  *****************************************************************************/
166 static int vout_Init( vout_thread_t *p_vout )
167 {
168     int i_index;
169     char *psz_filter;
170     picture_t *p_pic;
171     
172     I_OUTPUTPICTURES = 0;
173
174     /* Initialize the output structure, full of directbuffers since we want
175      * the decoder to output directly to our structures. */
176     switch( p_vout->render.i_chroma )
177     {
178         case FOURCC_I420:
179         case FOURCC_IYUV:
180         case FOURCC_YV12:
181         case FOURCC_I422:
182             p_vout->output.i_chroma = p_vout->render.i_chroma;
183             p_vout->output.i_width  = p_vout->render.i_width;
184             p_vout->output.i_height = p_vout->render.i_height;
185             p_vout->output.i_aspect = p_vout->render.i_aspect;
186             break;
187
188         default:
189             return( 0 ); /* unknown chroma */
190             break;
191     }
192
193     /* Try to open the real video output, with half the height our images */
194     psz_filter = main_GetPszVariable( VOUT_FILTER_VAR, "" );
195     main_PutPszVariable( VOUT_FILTER_VAR, "" );
196
197     intf_WarnMsg( 1, "filter: spawning the real video output" );
198
199     switch( p_vout->render.i_chroma )
200     {
201     case FOURCC_I420:
202     case FOURCC_IYUV:
203     case FOURCC_YV12:
204         switch( p_vout->p_sys->i_mode )
205         {
206         case DEINTERLACE_MODE_BOB:
207             p_vout->p_sys->p_vout =
208                 vout_CreateThread( NULL,
209                        p_vout->output.i_width, p_vout->output.i_height / 2,
210                        p_vout->output.i_chroma, p_vout->output.i_aspect );
211             break;
212
213         case DEINTERLACE_MODE_BLEND:
214             p_vout->p_sys->p_vout =
215                 vout_CreateThread( NULL,
216                        p_vout->output.i_width, p_vout->output.i_height,
217                        p_vout->output.i_chroma, p_vout->output.i_aspect );
218             break;
219         }
220         break;
221
222     case FOURCC_I422:
223         p_vout->p_sys->p_vout =
224             vout_CreateThread( NULL,
225                        p_vout->output.i_width, p_vout->output.i_height,
226                        FOURCC_I420, p_vout->output.i_aspect );
227         break;
228
229     default:
230         break;
231     }
232
233     /* Everything failed */
234     if( p_vout->p_sys->p_vout == NULL )
235     {
236         intf_ErrMsg( "filter error: can't open vout, aborting" );
237
238         return( 0 );
239     }
240  
241     p_vout->p_sys->last_date = 0;
242
243     main_PutPszVariable( VOUT_FILTER_VAR, psz_filter );
244
245     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
246
247     return( 0 );
248 }
249
250 /*****************************************************************************
251  * vout_End: terminate Deinterlace video thread output method
252  *****************************************************************************/
253 static void vout_End( vout_thread_t *p_vout )
254 {
255     int i_index;
256
257     /* Free the fake output buffers we allocated */
258     for( i_index = I_OUTPUTPICTURES ; i_index ; )
259     {
260         i_index--;
261         free( PP_OUTPUTPICTURE[ i_index ]->p_data );
262     }
263 }
264
265 /*****************************************************************************
266  * vout_Destroy: destroy Deinterlace video thread output method
267  *****************************************************************************
268  * Terminate an output method created by DeinterlaceCreateOutputMethod
269  *****************************************************************************/
270 static void vout_Destroy( vout_thread_t *p_vout )
271 {
272     vout_DestroyThread( p_vout->p_sys->p_vout, NULL );
273
274     free( p_vout->p_sys );
275 }
276
277 /*****************************************************************************
278  * vout_Manage: handle Deinterlace events
279  *****************************************************************************
280  * This function should be called regularly by video output thread. It manages
281  * console events. It returns a non null value on error.
282  *****************************************************************************/
283 static int vout_Manage( vout_thread_t *p_vout )
284 {
285     return( 0 );
286 }
287
288 /*****************************************************************************
289  * vout_Render: displays previously rendered output
290  *****************************************************************************
291  * This function send the currently rendered image to Deinterlace image,
292  * waits until it is displayed and switch the two rendering buffers, preparing
293  * next frame.
294  *****************************************************************************/
295 static void vout_Render ( vout_thread_t *p_vout, picture_t *p_pic )
296 {
297     picture_t *p_outpic;
298     int i_plane, i_field;
299     /* 20ms is a bit arbitrary, but it's only for the first image we get */
300     mtime_t new_date = p_vout->p_sys->last_date
301                        ? ( 3 * p_pic->date - p_vout->p_sys->last_date ) / 2
302                        : p_pic->date + 20000;
303
304     p_vout->p_sys->last_date = p_pic->date;
305
306     for( i_field = 0 ; i_field < 2 ; i_field++ )
307     {
308         /* Get a structure from the video_output. */
309         while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout,
310                                                 0, 0, 0 ) )
311                   == NULL )
312         {
313             if( p_vout->b_die || p_vout->b_error )
314             {
315                 return;
316             }
317             msleep( VOUT_OUTMEM_SLEEP );
318         }   
319
320         vout_DatePicture( p_vout->p_sys->p_vout, p_outpic,
321                           p_pic->date + i_field ? new_date : p_pic->date );
322
323         /* Copy image and skip lines */
324         for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
325         {
326             u8 *p_in, *p_out_end, *p_out;
327             int i_increment;
328
329             p_in = p_pic->p[i_plane].p_pixels
330                        + i_field * p_pic->p[i_plane].i_pitch;
331
332             p_out = p_outpic->p[i_plane].p_pixels;
333             p_out_end = p_out + p_outpic->p[i_plane].i_pitch
334                                  * p_outpic->p[i_plane].i_lines;
335
336             switch( p_vout->render.i_chroma )
337             {
338             case FOURCC_I420:
339             case FOURCC_IYUV:
340             case FOURCC_YV12:
341
342                 switch( p_vout->p_sys->i_mode )
343                 {
344                 case DEINTERLACE_MODE_BOB:
345                     for( ; p_out < p_out_end ; )
346                     {
347                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
348
349                         p_out += p_pic->p[i_plane].i_pitch;
350                         p_in += 2 * p_pic->p[i_plane].i_pitch;
351                     }
352                     break;
353
354                 case DEINTERLACE_MODE_BLEND:
355                     if( i_plane != Y_PLANE )
356                     {
357                         for( ; p_out < p_out_end ; )
358                         {
359                             FAST_MEMCPY( p_out, p_in,
360                                          p_pic->p[i_plane].i_pitch );
361
362                             p_out += p_pic->p[i_plane].i_pitch;
363
364                             FAST_MEMCPY( p_out, p_in,
365                                          p_pic->p[i_plane].i_pitch );
366
367                             p_out += p_pic->p[i_plane].i_pitch;
368                             p_in += 2 * p_pic->p[i_plane].i_pitch;
369                         }
370                         break;
371                     }
372
373                     if( i_field == 0 )
374                     {
375                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
376                         p_in += 2 * p_pic->p[i_plane].i_pitch;
377                         p_out += p_pic->p[i_plane].i_pitch;
378                     }
379
380                     for( ; p_out < p_out_end ; )
381                     {
382                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
383
384                         p_out += p_pic->p[i_plane].i_pitch;
385
386                         memblend( p_out, p_in,
387                                   p_in + 2 * p_pic->p[i_plane].i_pitch,
388                                   p_pic->p[i_plane].i_pitch );
389
390                         p_in += 2 * p_pic->p[i_plane].i_pitch;
391                         p_out += p_pic->p[i_plane].i_pitch;
392                     }
393                     break;
394                 }
395                 break;
396
397             case FOURCC_I422:
398
399                 i_increment = 2 * p_pic->p[i_plane].i_pitch;
400
401                 if( i_plane == Y_PLANE )
402                 {
403                     for( ; p_out < p_out_end ; )
404                     {
405                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
406                         p_out += p_pic->p[i_plane].i_pitch;
407                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
408                         p_out += p_pic->p[i_plane].i_pitch;
409                         p_in += i_increment;
410                     }
411                 }
412                 else
413                 {
414                     for( ; p_out < p_out_end ; )
415                     {
416                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
417                         p_out += p_pic->p[i_plane].i_pitch;
418                         p_in += i_increment;
419                     }
420                 }
421                 break;
422
423             default:
424                 break;
425             }
426         }
427
428         vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
429     }
430 }
431
432 /*****************************************************************************
433  * vout_Display: displays previously rendered output
434  *****************************************************************************
435  * This function send the currently rendered image to Invert image, waits
436  * until it is displayed and switch the two rendering buffers, preparing next
437  * frame.
438  *****************************************************************************/
439 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
440 {
441     ;
442 }
443
444 static void *memblend( void *p_dest, const void *p_s1,
445                        const void *p_s2, size_t i_bytes )
446 {
447     u8* p_end = (u8*)p_dest + i_bytes - 8;
448
449     while( (u8*)p_dest < p_end )
450     {
451         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
452         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
453         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
454         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
455         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
456         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
457         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
458         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
459     }
460
461     p_end += 8;
462
463     while( (u8*)p_dest < p_end )
464     {
465         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
466     }
467
468     return p_dest;
469 }
470