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