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