]> git.sesse.net Git - vlc/blob - modules/video_filter/deinterlace/deinterlace.c
* include/configuration.h: some small re-work of the config declaration macros.
[vlc] / modules / video_filter / deinterlace / deinterlace.c
1 /*****************************************************************************
2  * deinterlace.c : deinterlacer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001, 2002, 2003 VideoLAN
5  * $Id: deinterlace.c,v 1.16 2003/11/05 00:39:17 gbazin 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 <vlc/vlc.h>
32 #include <vlc/vout.h>
33
34 #include "../filter_common.h"
35
36 #define DEINTERLACE_DISCARD 1
37 #define DEINTERLACE_MEAN    2
38 #define DEINTERLACE_BLEND   3
39 #define DEINTERLACE_BOB     4
40 #define DEINTERLACE_LINEAR  5
41
42 /*****************************************************************************
43  * Local protypes
44  *****************************************************************************/
45 static int  Create    ( vlc_object_t * );
46 static void Destroy   ( vlc_object_t * );
47
48 static int  Init      ( vout_thread_t * );
49 static void End       ( vout_thread_t * );
50 static void Render    ( vout_thread_t *, picture_t * );
51
52 static void RenderDiscard( vout_thread_t *, picture_t *, picture_t *, int );
53 static void RenderBob    ( vout_thread_t *, picture_t *, picture_t *, int );
54 static void RenderMean   ( vout_thread_t *, picture_t *, picture_t * );
55 static void RenderBlend  ( vout_thread_t *, picture_t *, picture_t * );
56 static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
57
58 static void Merge        ( void *, const void *, const void *, size_t );
59
60 static int  SendEvents   ( vlc_object_t *, char const *,
61                            vlc_value_t, vlc_value_t, void * );
62
63 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method );
64 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout );
65
66 /*****************************************************************************
67  * Callback prototypes
68  *****************************************************************************/
69 static int FilterCallback ( vlc_object_t *, char const *,
70                             vlc_value_t, vlc_value_t, void * );
71
72 /*****************************************************************************
73  * Module descriptor
74  *****************************************************************************/
75 #define MODE_TEXT N_("Deinterlace mode")
76 #define MODE_LONGTEXT N_("You can choose the default deinterlace mode")
77
78 static char *mode_list[] = { "discard", "blend", "mean", "bob", "linear" };
79 static char *mode_list_text[] = { N_("discard"), N_("Blend"), N_("Mean"),
80                                   N_("Bob"), N_("Linear") };
81
82 vlc_module_begin();
83     add_category_hint( N_("Deinterlace"), NULL, VLC_FALSE );
84     add_string( "deinterlace-mode", "discard", NULL, MODE_TEXT,
85                 MODE_LONGTEXT, VLC_FALSE );
86         change_string_list( mode_list, mode_list_text, 0 );
87     set_description( _("video deinterlacing filter") );
88     set_capability( "video filter", 0 );
89     add_shortcut( "deinterlace" );
90     set_callbacks( Create, Destroy );
91 vlc_module_end();
92
93 /*****************************************************************************
94  * vout_sys_t: Deinterlace video output method descriptor
95  *****************************************************************************
96  * This structure is part of the video output thread descriptor.
97  * It describes the Deinterlace specific properties of an output thread.
98  *****************************************************************************/
99 struct vout_sys_t
100 {
101     int        i_mode;        /* Deinterlace mode */
102     vlc_bool_t b_double_rate; /* Shall we double the framerate? */
103
104     mtime_t    last_date;
105     mtime_t    next_date;
106
107     vout_thread_t *p_vout;
108
109     vlc_mutex_t filter_lock;
110 };
111
112 /*****************************************************************************
113  * Create: allocates Deinterlace video thread output method
114  *****************************************************************************
115  * This function allocates and initializes a Deinterlace vout method.
116  *****************************************************************************/
117 static int Create( vlc_object_t *p_this )
118 {
119     vout_thread_t *p_vout = (vout_thread_t *)p_this;
120     vlc_value_t val;
121
122     /* Allocate structure */
123     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
124     if( p_vout->p_sys == NULL )
125     {
126         msg_Err( p_vout, "out of memory" );
127         return VLC_ENOMEM;
128     }
129
130     p_vout->pf_init = Init;
131     p_vout->pf_end = End;
132     p_vout->pf_manage = NULL;
133     p_vout->pf_render = Render;
134     p_vout->pf_display = NULL;
135
136     p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
137     p_vout->p_sys->b_double_rate = 0;
138     p_vout->p_sys->last_date = 0;
139     vlc_mutex_init( p_vout, &p_vout->p_sys->filter_lock );
140
141     /* Look what method was requested */
142     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING );
143     var_Change( p_vout, "deinterlace-mode", VLC_VAR_INHERITVALUE, &val, NULL );
144
145     if( val.psz_string == NULL )
146     {
147         msg_Err( p_vout, "configuration variable deinterlace-mode empty" );
148         msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
149
150         val.psz_string = strdup( "discard" );
151     }
152
153     msg_Dbg( p_vout, "using %s deinterlace mode", val.psz_string );
154
155     SetFilterMethod( p_vout, val.psz_string );
156
157     free( val.psz_string );
158
159     var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
160
161     return VLC_SUCCESS;
162 }
163
164 /*****************************************************************************
165  * SetFilterMethod: setup the deinterlace method to use.
166  *****************************************************************************/
167 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method )
168 {
169     if( !strcmp( psz_method, "discard" ) )
170     {
171         p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
172         p_vout->p_sys->b_double_rate = 0;
173     }
174     else if( !strcmp( psz_method, "mean" ) )
175     {
176         p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
177         p_vout->p_sys->b_double_rate = 0;
178     }
179     else if( !strcmp( psz_method, "blend" )
180              || !strcmp( psz_method, "average" )
181              || !strcmp( psz_method, "combine-fields" ) )
182     {
183         p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
184         p_vout->p_sys->b_double_rate = 0;
185     }
186     else if( !strcmp( psz_method, "bob" )
187              || !strcmp( psz_method, "progressive-scan" ) )
188     {
189         p_vout->p_sys->i_mode = DEINTERLACE_BOB;
190         p_vout->p_sys->b_double_rate = 1;
191     }
192     else if( !strcmp( psz_method, "linear" ) )
193     {
194         p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
195         p_vout->p_sys->b_double_rate = 1;
196     }
197     else
198     {
199         msg_Err( p_vout, "no valid deinterlace mode provided, "
200                  "using \"discard\"" );
201     }
202
203     msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
204 }
205
206 /*****************************************************************************
207  * Init: initialize Deinterlace video thread output method
208  *****************************************************************************/
209 static int Init( vout_thread_t *p_vout )
210 {
211     int i_index;
212     picture_t *p_pic;
213
214     I_OUTPUTPICTURES = 0;
215
216     /* Initialize the output structure, full of directbuffers since we want
217      * the decoder to output directly to our structures. */
218     switch( p_vout->render.i_chroma )
219     {
220         case VLC_FOURCC('I','4','2','0'):
221         case VLC_FOURCC('I','Y','U','V'):
222         case VLC_FOURCC('Y','V','1','2'):
223         case VLC_FOURCC('I','4','2','2'):
224             p_vout->output.i_chroma = p_vout->render.i_chroma;
225             p_vout->output.i_width  = p_vout->render.i_width;
226             p_vout->output.i_height = p_vout->render.i_height;
227             p_vout->output.i_aspect = p_vout->render.i_aspect;
228             break;
229
230         default:
231             return VLC_EGENERIC; /* unknown chroma */
232             break;
233     }
234
235     /* Try to open the real video output */
236     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
237
238     if( p_vout->p_sys->p_vout == NULL )
239     {
240         /* Everything failed */
241         msg_Err( p_vout, "cannot open vout, aborting" );
242
243         return VLC_EGENERIC;
244     }
245
246     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
247
248     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
249
250     ADD_PARENT_CALLBACKS( SendEventsToChild );
251
252     return VLC_SUCCESS;
253 }
254
255 /*****************************************************************************
256  * SpawnRealVout: spawn the real video output.
257  *****************************************************************************/
258 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout )
259 {
260     vout_thread_t *p_real_vout = NULL;
261
262     msg_Dbg( p_vout, "spawning the real video output" );
263
264     switch( p_vout->render.i_chroma )
265     {
266     case VLC_FOURCC('I','4','2','0'):
267     case VLC_FOURCC('I','Y','U','V'):
268     case VLC_FOURCC('Y','V','1','2'):
269         switch( p_vout->p_sys->i_mode )
270         {
271         case DEINTERLACE_MEAN:
272         case DEINTERLACE_DISCARD:
273             p_real_vout =
274                 vout_Create( p_vout,
275                        p_vout->output.i_width, p_vout->output.i_height / 2,
276                        p_vout->output.i_chroma, p_vout->output.i_aspect );
277             break;
278
279         case DEINTERLACE_BOB:
280         case DEINTERLACE_BLEND:
281         case DEINTERLACE_LINEAR:
282             p_real_vout =
283                 vout_Create( p_vout,
284                        p_vout->output.i_width, p_vout->output.i_height,
285                        p_vout->output.i_chroma, p_vout->output.i_aspect );
286             break;
287         }
288         break;
289
290     case VLC_FOURCC('I','4','2','2'):
291         p_real_vout =
292             vout_Create( p_vout,
293                        p_vout->output.i_width, p_vout->output.i_height,
294                        VLC_FOURCC('I','4','2','0'), p_vout->output.i_aspect );
295         break;
296
297     default:
298         break;
299     }
300
301     return p_real_vout;
302 }
303
304 /*****************************************************************************
305  * End: terminate Deinterlace video thread output method
306  *****************************************************************************/
307 static void End( vout_thread_t *p_vout )
308 {
309     int i_index;
310
311     /* Free the fake output buffers we allocated */
312     for( i_index = I_OUTPUTPICTURES ; i_index ; )
313     {
314         i_index--;
315         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
316     }
317 }
318
319 /*****************************************************************************
320  * Destroy: destroy Deinterlace video thread output method
321  *****************************************************************************
322  * Terminate an output method created by DeinterlaceCreateOutputMethod
323  *****************************************************************************/
324 static void Destroy( vlc_object_t *p_this )
325 {
326     vout_thread_t *p_vout = (vout_thread_t *)p_this;
327
328     DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
329
330     vlc_object_detach( p_vout->p_sys->p_vout );
331     vout_Destroy( p_vout->p_sys->p_vout );
332
333     DEL_PARENT_CALLBACKS( SendEventsToChild );
334
335     free( p_vout->p_sys );
336 }
337
338 /*****************************************************************************
339  * Render: displays previously rendered output
340  *****************************************************************************
341  * This function send the currently rendered image to Deinterlace image,
342  * waits until it is displayed and switch the two rendering buffers, preparing
343  * next frame.
344  *****************************************************************************/
345 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
346 {
347     picture_t *pp_outpic[2];
348
349     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
350
351     /* Get a new picture */
352     while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
353                                              0, 0, 0 ) )
354               == NULL )
355     {
356         if( p_vout->b_die || p_vout->b_error )
357         {
358             vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
359             return;
360         }
361         msleep( VOUT_OUTMEM_SLEEP );
362      }
363
364     vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
365
366     /* If we are using double rate, get an additional new picture */
367     if( p_vout->p_sys->b_double_rate )
368     {
369         while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
370                                                  0, 0, 0 ) )
371                   == NULL )
372         {
373             if( p_vout->b_die || p_vout->b_error )
374             {
375                 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
376                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
377                 return;
378             }
379             msleep( VOUT_OUTMEM_SLEEP );
380         }
381
382         /* 20ms is a bit arbitrary, but it's only for the first image we get */
383         if( !p_vout->p_sys->last_date )
384         {
385             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
386                               p_pic->date + 20000 );
387         }
388         else
389         {
390             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
391                       (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
392         }
393         p_vout->p_sys->last_date = p_pic->date;
394     }
395
396     switch( p_vout->p_sys->i_mode )
397     {
398         case DEINTERLACE_DISCARD:
399             RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
400             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
401             break;
402
403         case DEINTERLACE_BOB:
404             RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
405             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
406             RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
407             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
408             break;
409
410         case DEINTERLACE_LINEAR:
411             RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
412             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
413             RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
414             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
415             break;
416
417         case DEINTERLACE_MEAN:
418             RenderMean( p_vout, pp_outpic[0], p_pic );
419             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
420             break;
421
422         case DEINTERLACE_BLEND:
423             RenderBlend( p_vout, pp_outpic[0], p_pic );
424             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
425             break;
426     }
427
428     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
429 }
430
431 /*****************************************************************************
432  * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
433  *****************************************************************************/
434 static void RenderDiscard( vout_thread_t *p_vout,
435                            picture_t *p_outpic, picture_t *p_pic, int i_field )
436 {
437     int i_plane;
438
439     /* Copy image and skip lines */
440     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
441     {
442         uint8_t *p_in, *p_out_end, *p_out;
443         int i_increment;
444
445         p_in = p_pic->p[i_plane].p_pixels
446                    + i_field * p_pic->p[i_plane].i_pitch;
447
448         p_out = p_outpic->p[i_plane].p_pixels;
449         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
450                              * p_outpic->p[i_plane].i_lines;
451
452         switch( p_vout->render.i_chroma )
453         {
454         case VLC_FOURCC('I','4','2','0'):
455         case VLC_FOURCC('I','Y','U','V'):
456         case VLC_FOURCC('Y','V','1','2'):
457
458             for( ; p_out < p_out_end ; )
459             {
460                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
461                                           p_pic->p[i_plane].i_pitch );
462
463                 p_out += p_pic->p[i_plane].i_pitch;
464                 p_in += 2 * p_pic->p[i_plane].i_pitch;
465             }
466             break;
467
468         case VLC_FOURCC('I','4','2','2'):
469
470             i_increment = 2 * p_pic->p[i_plane].i_pitch;
471
472             if( i_plane == Y_PLANE )
473             {
474                 for( ; p_out < p_out_end ; )
475                 {
476                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
477                                               p_pic->p[i_plane].i_pitch );
478                     p_out += p_pic->p[i_plane].i_pitch;
479                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
480                                               p_pic->p[i_plane].i_pitch );
481                     p_out += p_pic->p[i_plane].i_pitch;
482                     p_in += i_increment;
483                 }
484             }
485             else
486             {
487                 for( ; p_out < p_out_end ; )
488                 {
489                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
490                                               p_pic->p[i_plane].i_pitch );
491                     p_out += p_pic->p[i_plane].i_pitch;
492                     p_in += i_increment;
493                 }
494             }
495             break;
496
497         default:
498             break;
499         }
500     }
501 }
502
503 /*****************************************************************************
504  * RenderBob: renders a BOB picture - simple copy
505  *****************************************************************************/
506 static void RenderBob( vout_thread_t *p_vout,
507                        picture_t *p_outpic, picture_t *p_pic, int i_field )
508 {
509     int i_plane;
510
511     /* Copy image and skip lines */
512     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
513     {
514         uint8_t *p_in, *p_out_end, *p_out;
515
516         p_in = p_pic->p[i_plane].p_pixels;
517         p_out = p_outpic->p[i_plane].p_pixels;
518         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
519                              * p_outpic->p[i_plane].i_lines;
520
521         /* For BOTTOM field we need to add the first line */
522         if( i_field == 1 )
523         {
524             p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
525             p_in += p_pic->p[i_plane].i_pitch;
526             p_out += p_pic->p[i_plane].i_pitch;
527         }
528
529         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
530
531         for( ; p_out < p_out_end ; )
532         {
533             p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
534
535             p_out += p_pic->p[i_plane].i_pitch;
536
537             p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
538
539             p_in += 2 * p_pic->p[i_plane].i_pitch;
540             p_out += p_pic->p[i_plane].i_pitch;
541         }
542
543         p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
544
545         /* For TOP field we need to add the last line */
546         if( i_field == 0 )
547         {
548             p_in += p_pic->p[i_plane].i_pitch;
549             p_out += p_pic->p[i_plane].i_pitch;
550             p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
551         }
552     }
553 }
554
555 /*****************************************************************************
556  * RenderLinear: BOB with linear interpolation
557  *****************************************************************************/
558 static void RenderLinear( vout_thread_t *p_vout,
559                           picture_t *p_outpic, picture_t *p_pic, int i_field )
560 {
561     int i_plane;
562
563     /* Copy image and skip lines */
564     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
565     {
566         uint8_t *p_in, *p_out_end, *p_out;
567
568         p_in = p_pic->p[i_plane].p_pixels;
569         p_out = p_outpic->p[i_plane].p_pixels;
570         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
571                              * p_outpic->p[i_plane].i_lines;
572
573         /* For BOTTOM field we need to add the first line */
574         if( i_field == 1 )
575         {
576             p_vout->p_vlc->pf_memcpy( p_out, p_in,
577                                       p_pic->p[i_plane].i_pitch );
578             p_in += p_pic->p[i_plane].i_pitch;
579             p_out += p_pic->p[i_plane].i_pitch;
580         }
581
582         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
583
584         for( ; p_out < p_out_end ; )
585         {
586             p_vout->p_vlc->pf_memcpy( p_out, p_in,
587                                       p_pic->p[i_plane].i_pitch );
588
589             p_out += p_pic->p[i_plane].i_pitch;
590
591             Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
592                    p_pic->p[i_plane].i_pitch );
593
594             p_in += 2 * p_pic->p[i_plane].i_pitch;
595             p_out += p_pic->p[i_plane].i_pitch;
596         }
597
598         p_vout->p_vlc->pf_memcpy( p_out, p_in,
599                                   p_pic->p[i_plane].i_pitch );
600
601         /* For TOP field we need to add the last line */
602         if( i_field == 0 )
603         {
604             p_in += p_pic->p[i_plane].i_pitch;
605             p_out += p_pic->p[i_plane].i_pitch;
606             p_vout->p_vlc->pf_memcpy( p_out, p_in,
607                                       p_pic->p[i_plane].i_pitch );
608         }
609     }
610 }
611
612 static void RenderMean( vout_thread_t *p_vout,
613                         picture_t *p_outpic, picture_t *p_pic )
614 {
615     int i_plane;
616
617     /* Copy image and skip lines */
618     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
619     {
620         uint8_t *p_in, *p_out_end, *p_out;
621
622         p_in = p_pic->p[i_plane].p_pixels;
623
624         p_out = p_outpic->p[i_plane].p_pixels;
625         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
626                              * p_outpic->p[i_plane].i_lines;
627
628         /* All lines: mean value */
629         for( ; p_out < p_out_end ; )
630         {
631             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
632                    p_pic->p[i_plane].i_pitch );
633
634             p_out += p_pic->p[i_plane].i_pitch;
635             p_in += 2 * p_pic->p[i_plane].i_pitch;
636         }
637     }
638 }
639
640 static void RenderBlend( vout_thread_t *p_vout,
641                          picture_t *p_outpic, picture_t *p_pic )
642 {
643     int i_plane;
644
645     /* Copy image and skip lines */
646     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
647     {
648         uint8_t *p_in, *p_out_end, *p_out;
649
650         p_in = p_pic->p[i_plane].p_pixels;
651
652         p_out = p_outpic->p[i_plane].p_pixels;
653         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
654                              * p_outpic->p[i_plane].i_lines;
655
656         /* First line: simple copy */
657         p_vout->p_vlc->pf_memcpy( p_out, p_in,
658                                   p_pic->p[i_plane].i_pitch );
659         p_out += p_pic->p[i_plane].i_pitch;
660
661         /* Remaining lines: mean value */
662         for( ; p_out < p_out_end ; )
663         {
664             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
665                    p_pic->p[i_plane].i_pitch );
666
667             p_out += p_pic->p[i_plane].i_pitch;
668             p_in += p_pic->p[i_plane].i_pitch;
669         }
670     }
671 }
672
673 static void Merge( void *_p_dest, const void *_p_s1,
674                    const void *_p_s2, size_t i_bytes )
675 {
676     uint8_t* p_dest = (uint8_t*)_p_dest;
677     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
678     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
679     uint8_t* p_end = p_dest + i_bytes - 8;
680
681     while( p_dest < p_end )
682     {
683         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
684         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
685         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
686         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
687         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
688         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
689         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
690         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
691     }
692
693     p_end += 8;
694
695     while( p_dest < p_end )
696     {
697         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
698     }
699 }
700
701 /*****************************************************************************
702  * SendEvents: forward mouse and keyboard events to the parent p_vout
703  *****************************************************************************/
704 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
705                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
706 {
707     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
708     vlc_value_t sentval = newval;
709
710     if( !strcmp( psz_var, "mouse-y" ) )
711     {
712         switch( p_vout->p_sys->i_mode )
713         {
714             case DEINTERLACE_MEAN:
715             case DEINTERLACE_DISCARD:
716                 sentval.i_int *= 2;
717                 break;
718         }
719     }
720
721     var_Set( p_vout, psz_var, sentval );
722
723     return VLC_SUCCESS;
724 }
725
726 /*****************************************************************************
727  * FilterCallback: called when changing the deinterlace method on the fly.
728  *****************************************************************************/
729 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
730                            vlc_value_t oldval, vlc_value_t newval,
731                            void *p_data )
732 {
733     vout_thread_t * p_vout = (vout_thread_t *)p_this;
734     int i_old_mode = p_vout->p_sys->i_mode;
735
736     msg_Dbg( p_vout, "using %s deinterlace mode", newval.psz_string );
737
738     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
739
740     SetFilterMethod( p_vout, newval.psz_string );
741
742     switch( p_vout->render.i_chroma )
743     {
744     case VLC_FOURCC('I','4','2','2'):
745         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
746         return VLC_SUCCESS;
747         break;
748
749     case VLC_FOURCC('I','4','2','0'):
750     case VLC_FOURCC('I','Y','U','V'):
751     case VLC_FOURCC('Y','V','1','2'):
752         switch( p_vout->p_sys->i_mode )
753         {
754         case DEINTERLACE_MEAN:
755         case DEINTERLACE_DISCARD:
756             if( ( i_old_mode == DEINTERLACE_MEAN )
757                 || ( i_old_mode == DEINTERLACE_DISCARD ) )
758             {
759                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
760                 return VLC_SUCCESS;
761             }
762             break;
763
764         case DEINTERLACE_BOB:
765         case DEINTERLACE_BLEND:
766         case DEINTERLACE_LINEAR:
767             if( ( i_old_mode == DEINTERLACE_BOB )
768                 || ( i_old_mode == DEINTERLACE_BLEND )
769                 || ( i_old_mode == DEINTERLACE_LINEAR ) )
770             {
771                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
772                 return VLC_SUCCESS;
773             }
774             break;
775         }
776         break;
777
778     default:
779         break;
780     }
781
782     /* We need to kill the old vout */
783
784     DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
785
786     vlc_object_detach( p_vout->p_sys->p_vout );
787     vout_Destroy( p_vout->p_sys->p_vout );
788
789     /* Try to open a new video output */
790     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
791
792     if( p_vout->p_sys->p_vout == NULL )
793     {
794         /* Everything failed */
795         msg_Err( p_vout, "cannot open vout, aborting" );
796
797         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
798         return VLC_EGENERIC;
799     }
800
801     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
802
803     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
804     return VLC_SUCCESS;
805 }
806
807 /*****************************************************************************
808  * SendEventsToChild: forward events to the child/children vout
809  *****************************************************************************/
810 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
811                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
812 {
813     vout_thread_t *p_vout = (vout_thread_t *)p_this;
814     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
815     return VLC_SUCCESS;
816 }