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