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