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