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