* modules/*: sanitization of the modules description strings.
[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.11 2003/03/30 18:14:38 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     val.psz_string = config_GetPsz( p_vout, "deinterlace-mode" );
140
141     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING );
142
143     if( val.psz_string == NULL )
144     {
145         msg_Err( p_vout, "configuration variable %s empty",
146                          "deinterlace-mode" );
147         msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
148
149         val.psz_string = strdup( "discard" );
150     }
151
152     var_Set( p_vout, "deinterlace-mode", val );
153
154     SetFilterMethod( p_vout, val.psz_string );
155
156     free( val.psz_string );
157
158     var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
159
160     return VLC_SUCCESS;
161 }
162
163 /*****************************************************************************
164  * SetFilterMethod: setup the deinterlace method to use.
165  *****************************************************************************/
166 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method )
167 {
168     if( !strcmp( psz_method, "discard" ) )
169     {
170         p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
171         p_vout->p_sys->b_double_rate = 0;
172     }
173     else if( !strcmp( psz_method, "mean" ) )
174     {
175         p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
176         p_vout->p_sys->b_double_rate = 0;
177     }
178     else if( !strcmp( psz_method, "blend" )
179              || !strcmp( psz_method, "average" )
180              || !strcmp( psz_method, "combine-fields" ) )
181     {
182         p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
183         p_vout->p_sys->b_double_rate = 0;
184     }
185     else if( !strcmp( psz_method, "bob" )
186              || !strcmp( psz_method, "progressive-scan" ) )
187     {
188         p_vout->p_sys->i_mode = DEINTERLACE_BOB;
189         p_vout->p_sys->b_double_rate = 1;
190     }
191     else if( !strcmp( psz_method, "linear" ) )
192     {
193         p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
194         p_vout->p_sys->b_double_rate = 1;
195     }
196     else
197     {
198         msg_Err( p_vout, "no valid deinterlace mode provided, "
199                  "using \"discard\"" );
200     }
201
202     msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
203 }
204
205 /*****************************************************************************
206  * Init: initialize Deinterlace video thread output method
207  *****************************************************************************/
208 static int Init( vout_thread_t *p_vout )
209 {
210     int i_index;
211     picture_t *p_pic;
212
213     I_OUTPUTPICTURES = 0;
214
215     /* Initialize the output structure, full of directbuffers since we want
216      * the decoder to output directly to our structures. */
217     switch( p_vout->render.i_chroma )
218     {
219         case VLC_FOURCC('I','4','2','0'):
220         case VLC_FOURCC('I','Y','U','V'):
221         case VLC_FOURCC('Y','V','1','2'):
222         case VLC_FOURCC('I','4','2','2'):
223             p_vout->output.i_chroma = p_vout->render.i_chroma;
224             p_vout->output.i_width  = p_vout->render.i_width;
225             p_vout->output.i_height = p_vout->render.i_height;
226             p_vout->output.i_aspect = p_vout->render.i_aspect;
227             break;
228
229         default:
230             return VLC_EGENERIC; /* unknown chroma */
231             break;
232     }
233
234     /* Try to open the real video output */
235     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
236
237     if( p_vout->p_sys->p_vout == NULL )
238     {
239         /* Everything failed */
240         msg_Err( p_vout, "cannot open vout, aborting" );
241
242         return VLC_EGENERIC;
243     }
244
245     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
246
247     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
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     free( p_vout->p_sys );
331 }
332
333 /*****************************************************************************
334  * Render: displays previously rendered output
335  *****************************************************************************
336  * This function send the currently rendered image to Deinterlace image,
337  * waits until it is displayed and switch the two rendering buffers, preparing
338  * next frame.
339  *****************************************************************************/
340 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
341 {
342     picture_t *pp_outpic[2];
343
344     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
345
346     /* Get a new picture */
347     while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
348                                              0, 0, 0 ) )
349               == NULL )
350     {
351         if( p_vout->b_die || p_vout->b_error )
352         {
353             vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
354             return;
355         }
356         msleep( VOUT_OUTMEM_SLEEP );
357      }
358
359     vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
360
361     /* If we are using double rate, get an additional new picture */
362     if( p_vout->p_sys->b_double_rate )
363     {
364         while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
365                                                  0, 0, 0 ) )
366                   == NULL )
367         {
368             if( p_vout->b_die || p_vout->b_error )
369             {
370                 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
371                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
372                 return;
373             }
374             msleep( VOUT_OUTMEM_SLEEP );
375         }
376
377         /* 20ms is a bit arbitrary, but it's only for the first image we get */
378         if( !p_vout->p_sys->last_date )
379         {
380             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
381                               p_pic->date + 20000 );
382         }
383         else
384         {
385             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
386                       (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
387         }
388         p_vout->p_sys->last_date = p_pic->date;
389     }
390
391     switch( p_vout->p_sys->i_mode )
392     {
393         case DEINTERLACE_DISCARD:
394             RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
395             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
396             break;
397
398         case DEINTERLACE_BOB:
399             RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
400             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
401             RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
402             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
403             break;
404
405         case DEINTERLACE_LINEAR:
406             RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
407             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
408             RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
409             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
410             break;
411
412         case DEINTERLACE_MEAN:
413             RenderMean( p_vout, pp_outpic[0], p_pic );
414             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
415             break;
416
417         case DEINTERLACE_BLEND:
418             RenderBlend( p_vout, pp_outpic[0], p_pic );
419             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
420             break;
421     }
422
423     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
424 }
425
426 /*****************************************************************************
427  * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
428  *****************************************************************************/
429 static void RenderDiscard( vout_thread_t *p_vout,
430                            picture_t *p_outpic, picture_t *p_pic, int i_field )
431 {
432     int i_plane;
433
434     /* Copy image and skip lines */
435     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
436     {
437         uint8_t *p_in, *p_out_end, *p_out;
438         int i_increment;
439
440         p_in = p_pic->p[i_plane].p_pixels
441                    + i_field * p_pic->p[i_plane].i_pitch;
442
443         p_out = p_outpic->p[i_plane].p_pixels;
444         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
445                              * p_outpic->p[i_plane].i_lines;
446
447         switch( p_vout->render.i_chroma )
448         {
449         case VLC_FOURCC('I','4','2','0'):
450         case VLC_FOURCC('I','Y','U','V'):
451         case VLC_FOURCC('Y','V','1','2'):
452
453             for( ; p_out < p_out_end ; )
454             {
455                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
456                                           p_pic->p[i_plane].i_pitch );
457
458                 p_out += p_pic->p[i_plane].i_pitch;
459                 p_in += 2 * p_pic->p[i_plane].i_pitch;
460             }
461             break;
462
463         case VLC_FOURCC('I','4','2','2'):
464
465             i_increment = 2 * p_pic->p[i_plane].i_pitch;
466
467             if( i_plane == Y_PLANE )
468             {
469                 for( ; p_out < p_out_end ; )
470                 {
471                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
472                                               p_pic->p[i_plane].i_pitch );
473                     p_out += p_pic->p[i_plane].i_pitch;
474                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
475                                               p_pic->p[i_plane].i_pitch );
476                     p_out += p_pic->p[i_plane].i_pitch;
477                     p_in += i_increment;
478                 }
479             }
480             else
481             {
482                 for( ; p_out < p_out_end ; )
483                 {
484                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
485                                               p_pic->p[i_plane].i_pitch );
486                     p_out += p_pic->p[i_plane].i_pitch;
487                     p_in += i_increment;
488                 }
489             }
490             break;
491
492         default:
493             break;
494         }
495     }
496 }
497
498 /*****************************************************************************
499  * RenderBob: renders a BOB picture - simple copy
500  *****************************************************************************/
501 static void RenderBob( vout_thread_t *p_vout,
502                        picture_t *p_outpic, picture_t *p_pic, int i_field )
503 {
504     int i_plane;
505
506     /* Copy image and skip lines */
507     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
508     {
509         uint8_t *p_in, *p_out_end, *p_out;
510
511         p_in = p_pic->p[i_plane].p_pixels;
512         p_out = p_outpic->p[i_plane].p_pixels;
513         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
514                              * p_outpic->p[i_plane].i_lines;
515
516         /* For BOTTOM field we need to add the first line */
517         if( i_field == 1 )
518         {
519             p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
520             p_in += p_pic->p[i_plane].i_pitch;
521             p_out += p_pic->p[i_plane].i_pitch;
522         }
523
524         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
525
526         for( ; p_out < p_out_end ; )
527         {
528             p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
529
530             p_out += p_pic->p[i_plane].i_pitch;
531
532             p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
533
534             p_in += 2 * p_pic->p[i_plane].i_pitch;
535             p_out += p_pic->p[i_plane].i_pitch;
536         }
537
538         p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
539
540         /* For TOP field we need to add the last line */
541         if( i_field == 0 )
542         {
543             p_in += p_pic->p[i_plane].i_pitch;
544             p_out += p_pic->p[i_plane].i_pitch;
545             p_vout->p_vlc->pf_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
546         }
547     }
548 }
549
550 /*****************************************************************************
551  * RenderLinear: BOB with linear interpolation
552  *****************************************************************************/
553 static void RenderLinear( vout_thread_t *p_vout,
554                           picture_t *p_outpic, picture_t *p_pic, int i_field )
555 {
556     int i_plane;
557
558     /* Copy image and skip lines */
559     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
560     {
561         uint8_t *p_in, *p_out_end, *p_out;
562
563         p_in = p_pic->p[i_plane].p_pixels;
564         p_out = p_outpic->p[i_plane].p_pixels;
565         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
566                              * p_outpic->p[i_plane].i_lines;
567
568         /* For BOTTOM field we need to add the first line */
569         if( i_field == 1 )
570         {
571             p_vout->p_vlc->pf_memcpy( p_out, p_in,
572                                       p_pic->p[i_plane].i_pitch );
573             p_in += p_pic->p[i_plane].i_pitch;
574             p_out += p_pic->p[i_plane].i_pitch;
575         }
576
577         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
578
579         for( ; p_out < p_out_end ; )
580         {
581             p_vout->p_vlc->pf_memcpy( p_out, p_in,
582                                       p_pic->p[i_plane].i_pitch );
583
584             p_out += p_pic->p[i_plane].i_pitch;
585
586             Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
587                    p_pic->p[i_plane].i_pitch );
588
589             p_in += 2 * p_pic->p[i_plane].i_pitch;
590             p_out += p_pic->p[i_plane].i_pitch;
591         }
592
593         p_vout->p_vlc->pf_memcpy( p_out, p_in,
594                                   p_pic->p[i_plane].i_pitch );
595
596         /* For TOP field we need to add the last line */
597         if( i_field == 0 )
598         {
599             p_in += p_pic->p[i_plane].i_pitch;
600             p_out += p_pic->p[i_plane].i_pitch;
601             p_vout->p_vlc->pf_memcpy( p_out, p_in,
602                                       p_pic->p[i_plane].i_pitch );
603         }
604     }
605 }
606
607 static void RenderMean( vout_thread_t *p_vout,
608                         picture_t *p_outpic, picture_t *p_pic )
609 {
610     int i_plane;
611
612     /* Copy image and skip lines */
613     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
614     {
615         uint8_t *p_in, *p_out_end, *p_out;
616
617         p_in = p_pic->p[i_plane].p_pixels;
618
619         p_out = p_outpic->p[i_plane].p_pixels;
620         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
621                              * p_outpic->p[i_plane].i_lines;
622
623         /* All lines: mean value */
624         for( ; p_out < p_out_end ; )
625         {
626             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
627                    p_pic->p[i_plane].i_pitch );
628
629             p_out += p_pic->p[i_plane].i_pitch;
630             p_in += 2 * p_pic->p[i_plane].i_pitch;
631         }
632     }
633 }
634
635 static void RenderBlend( vout_thread_t *p_vout,
636                          picture_t *p_outpic, picture_t *p_pic )
637 {
638     int i_plane;
639
640     /* Copy image and skip lines */
641     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
642     {
643         uint8_t *p_in, *p_out_end, *p_out;
644
645         p_in = p_pic->p[i_plane].p_pixels;
646
647         p_out = p_outpic->p[i_plane].p_pixels;
648         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
649                              * p_outpic->p[i_plane].i_lines;
650
651         /* First line: simple copy */
652         p_vout->p_vlc->pf_memcpy( p_out, p_in,
653                                   p_pic->p[i_plane].i_pitch );
654         p_out += p_pic->p[i_plane].i_pitch;
655
656         /* Remaining lines: mean value */
657         for( ; p_out < p_out_end ; )
658         {
659             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
660                    p_pic->p[i_plane].i_pitch );
661
662             p_out += p_pic->p[i_plane].i_pitch;
663             p_in += p_pic->p[i_plane].i_pitch;
664         }
665     }
666 }
667
668 static void Merge( void *_p_dest, const void *_p_s1,
669                    const void *_p_s2, size_t i_bytes )
670 {
671     uint8_t* p_dest = (uint8_t*)_p_dest;
672     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
673     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
674     uint8_t* p_end = p_dest + i_bytes - 8;
675
676     while( p_dest < p_end )
677     {
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         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
686     }
687
688     p_end += 8;
689
690     while( p_dest < p_end )
691     {
692         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
693     }
694 }
695
696 /*****************************************************************************
697  * SendEvents: forward mouse and keyboard events to the parent p_vout
698  *****************************************************************************/
699 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
700                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
701 {
702     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
703     vlc_value_t sentval = newval;
704
705     if( !strcmp( psz_var, "mouse-y" ) )
706     {
707         switch( p_vout->p_sys->i_mode )
708         {
709             case DEINTERLACE_MEAN:
710             case DEINTERLACE_DISCARD:
711                 sentval.i_int *= 2;
712                 break;
713         }
714     }
715
716     var_Set( p_vout, psz_var, sentval );
717
718     return VLC_SUCCESS;
719 }
720
721 /*****************************************************************************
722  * FilterCallback: called when changing the deinterlace method on the fly.
723  *****************************************************************************/
724 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
725                            vlc_value_t oldval, vlc_value_t newval,
726                            void *p_data )
727 {
728     vout_thread_t * p_vout = (vout_thread_t *)p_this;
729     int i_old_mode = p_vout->p_sys->i_mode;
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 }