* include/video_output.h, ALL: changed api for vout_Request()/vout_Create() to be...
[vlc.git] / 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     video_format_t fmt = {0};
324
325     msg_Dbg( p_vout, "spawning the real video output" );
326
327     fmt.i_width = fmt.i_visible_width = p_vout->output.i_width;
328     fmt.i_height = fmt.i_visible_height = p_vout->output.i_height;
329     fmt.i_x_offset = fmt.i_y_offset = 0;
330     fmt.i_chroma = p_vout->output.i_chroma;
331     fmt.i_aspect = p_vout->output.i_aspect;
332     fmt.i_sar_num = p_vout->output.i_aspect * fmt.i_height / fmt.i_width;
333     fmt.i_sar_den = VOUT_ASPECT_FACTOR;
334
335     switch( p_vout->render.i_chroma )
336     {
337     case VLC_FOURCC('I','4','2','0'):
338     case VLC_FOURCC('I','Y','U','V'):
339     case VLC_FOURCC('Y','V','1','2'):
340         switch( p_vout->p_sys->i_mode )
341         {
342         case DEINTERLACE_MEAN:
343         case DEINTERLACE_DISCARD:
344             fmt.i_height = fmt.i_visible_height = p_vout->output.i_height / 2;
345             p_real_vout = vout_Create( p_vout, &fmt );
346             break;
347
348         case DEINTERLACE_BOB:
349         case DEINTERLACE_BLEND:
350         case DEINTERLACE_LINEAR:
351             p_real_vout = vout_Create( p_vout, &fmt );
352             break;
353         }
354         break;
355
356     case VLC_FOURCC('I','4','2','2'):
357         fmt.i_chroma = VLC_FOURCC('I','4','2','0');
358         p_real_vout = vout_Create( p_vout, &fmt );
359         break;
360
361     default:
362         break;
363     }
364
365     return p_real_vout;
366 }
367
368 /*****************************************************************************
369  * End: terminate Deinterlace video thread output method
370  *****************************************************************************/
371 static void End( vout_thread_t *p_vout )
372 {
373     int i_index;
374
375     /* Free the fake output buffers we allocated */
376     for( i_index = I_OUTPUTPICTURES ; i_index ; )
377     {
378         i_index--;
379         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
380     }
381 }
382
383 /*****************************************************************************
384  * Destroy: destroy Deinterlace video thread output method
385  *****************************************************************************
386  * Terminate an output method created by DeinterlaceCreateOutputMethod
387  *****************************************************************************/
388 static void Destroy( vlc_object_t *p_this )
389 {
390     vout_thread_t *p_vout = (vout_thread_t *)p_this;
391
392     if( p_vout->p_sys->p_vout )
393     {
394         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
395         vlc_object_detach( p_vout->p_sys->p_vout );
396         vout_Destroy( p_vout->p_sys->p_vout );
397     }
398
399     DEL_PARENT_CALLBACKS( SendEventsToChild );
400
401     free( p_vout->p_sys );
402 }
403
404 /*****************************************************************************
405  * Render: displays previously rendered output
406  *****************************************************************************
407  * This function send the currently rendered image to Deinterlace image,
408  * waits until it is displayed and switch the two rendering buffers, preparing
409  * next frame.
410  *****************************************************************************/
411 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
412 {
413     picture_t *pp_outpic[2];
414
415     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
416
417     /* Get a new picture */
418     while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
419                                              0, 0, 0 ) )
420               == NULL )
421     {
422         if( p_vout->b_die || p_vout->b_error )
423         {
424             vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
425             return;
426         }
427         msleep( VOUT_OUTMEM_SLEEP );
428      }
429
430     vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
431
432     /* If we are using double rate, get an additional new picture */
433     if( p_vout->p_sys->b_double_rate )
434     {
435         while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
436                                                  0, 0, 0 ) )
437                   == NULL )
438         {
439             if( p_vout->b_die || p_vout->b_error )
440             {
441                 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
442                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
443                 return;
444             }
445             msleep( VOUT_OUTMEM_SLEEP );
446         }
447
448         /* 20ms is a bit arbitrary, but it's only for the first image we get */
449         if( !p_vout->p_sys->last_date )
450         {
451             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
452                               p_pic->date + 20000 );
453         }
454         else
455         {
456             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
457                       (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
458         }
459         p_vout->p_sys->last_date = p_pic->date;
460     }
461
462     switch( p_vout->p_sys->i_mode )
463     {
464         case DEINTERLACE_DISCARD:
465             RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
466             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
467             break;
468
469         case DEINTERLACE_BOB:
470             RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
471             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
472             RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
473             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
474             break;
475
476         case DEINTERLACE_LINEAR:
477             RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
478             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
479             RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
480             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
481             break;
482
483         case DEINTERLACE_MEAN:
484             RenderMean( p_vout, pp_outpic[0], p_pic );
485             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
486             break;
487
488         case DEINTERLACE_BLEND:
489             RenderBlend( p_vout, pp_outpic[0], p_pic );
490             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
491             break;
492     }
493
494     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
495 }
496
497 /*****************************************************************************
498  * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
499  *****************************************************************************/
500 static void RenderDiscard( vout_thread_t *p_vout,
501                            picture_t *p_outpic, picture_t *p_pic, int i_field )
502 {
503     int i_plane;
504
505     /* Copy image and skip lines */
506     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
507     {
508         uint8_t *p_in, *p_out_end, *p_out;
509         int i_increment;
510
511         p_in = p_pic->p[i_plane].p_pixels
512                    + i_field * p_pic->p[i_plane].i_pitch;
513
514         p_out = p_outpic->p[i_plane].p_pixels;
515         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
516                              * p_outpic->p[i_plane].i_visible_lines;
517
518         switch( p_vout->render.i_chroma )
519         {
520         case VLC_FOURCC('I','4','2','0'):
521         case VLC_FOURCC('I','Y','U','V'):
522         case VLC_FOURCC('Y','V','1','2'):
523
524             for( ; p_out < p_out_end ; )
525             {
526                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
527                                           p_pic->p[i_plane].i_pitch );
528
529                 p_out += p_pic->p[i_plane].i_pitch;
530                 p_in += 2 * p_pic->p[i_plane].i_pitch;
531             }
532             break;
533
534         case VLC_FOURCC('I','4','2','2'):
535
536             i_increment = 2 * p_pic->p[i_plane].i_pitch;
537
538             if( i_plane == Y_PLANE )
539             {
540                 for( ; p_out < p_out_end ; )
541                 {
542                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
543                                               p_pic->p[i_plane].i_pitch );
544                     p_out += p_pic->p[i_plane].i_pitch;
545                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
546                                               p_pic->p[i_plane].i_pitch );
547                     p_out += p_pic->p[i_plane].i_pitch;
548                     p_in += i_increment;
549                 }
550             }
551             else
552             {
553                 for( ; p_out < p_out_end ; )
554                 {
555                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
556                                               p_pic->p[i_plane].i_pitch );
557                     p_out += p_pic->p[i_plane].i_pitch;
558                     p_in += i_increment;
559                 }
560             }
561             break;
562
563         default:
564             break;
565         }
566     }
567 }
568
569 /*****************************************************************************
570  * RenderBob: renders a BOB picture - simple copy
571  *****************************************************************************/
572 static void RenderBob( vout_thread_t *p_vout,
573                        picture_t *p_outpic, picture_t *p_pic, int i_field )
574 {
575     int i_plane;
576
577     /* Copy image and skip lines */
578     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
579     {
580         uint8_t *p_in, *p_out_end, *p_out;
581
582         p_in = p_pic->p[i_plane].p_pixels;
583         p_out = p_outpic->p[i_plane].p_pixels;
584         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
585                              * p_outpic->p[i_plane].i_visible_lines;
586
587         switch( p_vout->render.i_chroma )
588         {
589             case VLC_FOURCC('I','4','2','0'):
590             case VLC_FOURCC('I','Y','U','V'):
591             case VLC_FOURCC('Y','V','1','2'):
592                 /* For BOTTOM field we need to add the first line */
593                 if( i_field == 1 )
594                 {
595                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
596                                               p_pic->p[i_plane].i_pitch );
597                     p_in += p_pic->p[i_plane].i_pitch;
598                     p_out += p_pic->p[i_plane].i_pitch;
599                 }
600
601                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
602
603                 for( ; p_out < p_out_end ; )
604                 {
605                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
606                                               p_pic->p[i_plane].i_pitch );
607
608                     p_out += p_pic->p[i_plane].i_pitch;
609
610                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
611                                               p_pic->p[i_plane].i_pitch );
612
613                     p_in += 2 * p_pic->p[i_plane].i_pitch;
614                     p_out += p_pic->p[i_plane].i_pitch;
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             case VLC_FOURCC('I','4','2','2'):
631                 /* For BOTTOM field we need to add the first line */
632                 if( i_field == 1 )
633                 {
634                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
635                                               p_pic->p[i_plane].i_pitch );
636                     p_in += p_pic->p[i_plane].i_pitch;
637                     p_out += p_pic->p[i_plane].i_pitch;
638                 }
639
640                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
641
642                 if( i_plane == Y_PLANE )
643                 {
644                     for( ; p_out < p_out_end ; )
645                     {
646                         p_vout->p_vlc->pf_memcpy( p_out, p_in,
647                                                   p_pic->p[i_plane].i_pitch );
648
649                         p_out += p_pic->p[i_plane].i_pitch;
650
651                         p_vout->p_vlc->pf_memcpy( p_out, p_in,
652                                                   p_pic->p[i_plane].i_pitch );
653
654                         p_in += 2 * p_pic->p[i_plane].i_pitch;
655                         p_out += p_pic->p[i_plane].i_pitch;
656                     }
657                 }
658                 else
659                 {
660                     for( ; p_out < p_out_end ; )
661                     {
662                         p_vout->p_vlc->pf_memcpy( p_out, p_in,
663                                                   p_pic->p[i_plane].i_pitch );
664
665                         p_out += p_pic->p[i_plane].i_pitch;
666                         p_in += 2 * p_pic->p[i_plane].i_pitch;
667                     }
668                 }
669
670                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
671                                           p_pic->p[i_plane].i_pitch );
672
673                 /* For TOP field we need to add the last line */
674                 if( i_field == 0 )
675                 {
676                     p_in += p_pic->p[i_plane].i_pitch;
677                     p_out += p_pic->p[i_plane].i_pitch;
678                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
679                                               p_pic->p[i_plane].i_pitch );
680                 }
681                 break;
682         }
683     }
684 }
685
686 #define Merge p_vout->p_sys->pf_merge
687 #define EndMerge if(p_vout->p_sys->pf_end_merge) p_vout->p_sys->pf_end_merge
688
689 /*****************************************************************************
690  * RenderLinear: BOB with linear interpolation
691  *****************************************************************************/
692 static void RenderLinear( vout_thread_t *p_vout,
693                           picture_t *p_outpic, picture_t *p_pic, int i_field )
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         p_out = p_outpic->p[i_plane].p_pixels;
704         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
705                              * p_outpic->p[i_plane].i_visible_lines;
706
707         /* For BOTTOM field we need to add the first line */
708         if( i_field == 1 )
709         {
710             p_vout->p_vlc->pf_memcpy( p_out, p_in,
711                                       p_pic->p[i_plane].i_pitch );
712             p_in += p_pic->p[i_plane].i_pitch;
713             p_out += p_pic->p[i_plane].i_pitch;
714         }
715
716         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
717
718         for( ; p_out < p_out_end ; )
719         {
720             p_vout->p_vlc->pf_memcpy( p_out, p_in,
721                                       p_pic->p[i_plane].i_pitch );
722
723             p_out += p_pic->p[i_plane].i_pitch;
724
725             Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
726                    p_pic->p[i_plane].i_pitch );
727
728             p_in += 2 * p_pic->p[i_plane].i_pitch;
729             p_out += p_pic->p[i_plane].i_pitch;
730         }
731
732         p_vout->p_vlc->pf_memcpy( p_out, p_in,
733                                   p_pic->p[i_plane].i_pitch );
734
735         /* For TOP field we need to add the last line */
736         if( i_field == 0 )
737         {
738             p_in += p_pic->p[i_plane].i_pitch;
739             p_out += p_pic->p[i_plane].i_pitch;
740             p_vout->p_vlc->pf_memcpy( p_out, p_in,
741                                       p_pic->p[i_plane].i_pitch );
742         }
743     }
744     EndMerge();
745 }
746
747 static void RenderMean( vout_thread_t *p_vout,
748                         picture_t *p_outpic, picture_t *p_pic )
749 {
750     int i_plane;
751
752     /* Copy image and skip lines */
753     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
754     {
755         uint8_t *p_in, *p_out_end, *p_out;
756
757         p_in = p_pic->p[i_plane].p_pixels;
758
759         p_out = p_outpic->p[i_plane].p_pixels;
760         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
761                              * p_outpic->p[i_plane].i_visible_lines;
762
763         /* All lines: mean value */
764         for( ; p_out < p_out_end ; )
765         {
766             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
767                    p_pic->p[i_plane].i_pitch );
768
769             p_out += p_pic->p[i_plane].i_pitch;
770             p_in += 2 * p_pic->p[i_plane].i_pitch;
771         }
772     }
773     EndMerge();
774 }
775
776 static void RenderBlend( vout_thread_t *p_vout,
777                          picture_t *p_outpic, picture_t *p_pic )
778 {
779     int i_plane;
780
781     /* Copy image and skip lines */
782     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
783     {
784         uint8_t *p_in, *p_out_end, *p_out;
785
786         p_in = p_pic->p[i_plane].p_pixels;
787
788         p_out = p_outpic->p[i_plane].p_pixels;
789         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
790                              * p_outpic->p[i_plane].i_visible_lines;
791
792         switch( p_vout->render.i_chroma )
793         {
794             case VLC_FOURCC('I','4','2','0'):
795             case VLC_FOURCC('I','Y','U','V'):
796             case VLC_FOURCC('Y','V','1','2'):
797                 /* First line: simple copy */
798                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
799                                           p_pic->p[i_plane].i_pitch );
800                 p_out += p_pic->p[i_plane].i_pitch;
801
802                 /* Remaining lines: mean value */
803                 for( ; p_out < p_out_end ; )
804                 {
805                    Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
806                           p_pic->p[i_plane].i_pitch );
807
808                     p_out += p_pic->p[i_plane].i_pitch;
809                     p_in += p_pic->p[i_plane].i_pitch;
810                 }
811                 break;
812
813             case VLC_FOURCC('I','4','2','2'):
814                 /* First line: simple copy */
815                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
816                                           p_pic->p[i_plane].i_pitch );
817                 p_out += p_pic->p[i_plane].i_pitch;
818
819                 /* Remaining lines: mean value */
820                 if( i_plane == Y_PLANE )
821                 {
822                     for( ; p_out < p_out_end ; )
823                     {
824                         Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
825                                p_pic->p[i_plane].i_pitch );
826
827                         p_out += p_pic->p[i_plane].i_pitch;
828                         p_in += p_pic->p[i_plane].i_pitch;
829                     }
830                 }
831
832                 else
833                 {
834                     for( ; p_out < p_out_end ; )
835                     {
836                         Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
837                                p_pic->p[i_plane].i_pitch );
838
839                         p_out += p_pic->p[i_plane].i_pitch;
840                         p_in += 2*p_pic->p[i_plane].i_pitch;
841                     }
842                 }
843                 break;
844         }
845     }
846     EndMerge();
847 }
848
849 #undef Merge
850
851 static void MergeGeneric( void *_p_dest, const void *_p_s1,
852                           const void *_p_s2, size_t i_bytes )
853 {
854     uint8_t* p_dest = (uint8_t*)_p_dest;
855     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
856     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
857     uint8_t* p_end = p_dest + i_bytes - 8;
858
859     while( p_dest < p_end )
860     {
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         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
868         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
869     }
870
871     p_end += 8;
872
873     while( p_dest < p_end )
874     {
875         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
876     }
877 }
878
879 #if defined(CAN_COMPILE_MMXEXT)
880 static void MergeMMX( void *_p_dest, const void *_p_s1, const void *_p_s2,
881                       size_t i_bytes )
882 {
883     uint8_t* p_dest = (uint8_t*)_p_dest;
884     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
885     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
886     uint8_t* p_end = p_dest + i_bytes - 8;
887     while( p_dest < p_end )
888     {
889         __asm__  __volatile__( "movq %2,%%mm1;"
890                                "pavgb %1, %%mm1;"
891                                "movq %%mm1, %0" :"=m" (*p_dest):
892                                                  "m" (*p_s1),
893                                                  "m" (*p_s2) );
894         p_dest += 8;
895         p_s1 += 8;
896         p_s2 += 8;
897     }
898
899     p_end += 8;
900
901     while( p_dest < p_end )
902     {
903         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
904     }
905 }
906 #endif
907
908 #if defined(CAN_COMPILE_SSE)
909 static void MergeSSE2( void *_p_dest, const void *_p_s1, const void *_p_s2,
910                        size_t i_bytes )
911 {
912     uint8_t* p_dest = (uint8_t*)_p_dest;
913     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
914     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
915     uint8_t* p_end;
916     while( (int)p_s1 % 16 )
917     {
918         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
919     }        
920     p_end = p_dest + i_bytes - 16;
921     while( p_dest < p_end )
922     {
923         __asm__  __volatile__( "movdqu %2,%%xmm1;"
924                                "pavgb %1, %%xmm1;"
925                                "movdqu %%xmm1, %0" :"=m" (*p_dest):
926                                                  "m" (*p_s1),
927                                                  "m" (*p_s2) );
928         p_dest += 16;
929         p_s1 += 16;
930         p_s2 += 16;
931     }
932
933     p_end += 16;
934
935     while( p_dest < p_end )
936     {
937         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
938     }
939 }
940 #endif
941
942 #if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
943 static void EndMMX( void )
944 {
945     __asm__ __volatile__( "emms" :: );
946 }
947 #endif
948
949 #ifdef CAN_COMPILE_C_ALTIVEC
950 static void MergeAltivec( void *_p_dest, const void *_p_s1,
951                           const void *_p_s2, size_t i_bytes )
952 {
953     uint8_t *p_dest = (uint8_t *)_p_dest;
954     uint8_t *p_s1   = (uint8_t *)_p_s1;
955     uint8_t *p_s2   = (uint8_t *)_p_s2;
956     uint8_t *p_end  = p_dest + i_bytes - 15;
957
958     /* Use C until the first 16-bytes aligned destination pixel */
959     while( (int)p_dest & 0xF )
960     {
961         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
962     }
963
964     if( ( (int)p_s1 & 0xF ) | ( (int)p_s2 & 0xF ) )
965     {
966         /* Unaligned source */
967         vector unsigned char s1v, s2v, destv;
968         vector unsigned char s1oldv, s2oldv, s1newv, s2newv;
969         vector unsigned char perm1v, perm2v;
970
971         perm1v = vec_lvsl( 0, p_s1 );
972         perm2v = vec_lvsl( 0, p_s2 );
973         s1oldv = vec_ld( 0, p_s1 );
974         s2oldv = vec_ld( 0, p_s2 );
975
976         while( p_dest < p_end )
977         {
978             s1newv = vec_ld( 16, p_s1 );
979             s2newv = vec_ld( 16, p_s2 );
980             s1v    = vec_perm( s1oldv, s1newv, perm1v );
981             s2v    = vec_perm( s2oldv, s2newv, perm2v );
982             s1oldv = s1newv;
983             s2oldv = s2newv;
984             destv  = vec_avg( s1v, s2v );
985             vec_st( destv, 0, p_dest );
986
987             p_s1   += 16;
988             p_s2   += 16;
989             p_dest += 16;
990         }
991     }
992     else
993     {
994         /* Aligned source */
995         vector unsigned char s1v, s2v, destv;
996
997         while( p_dest < p_end )
998         {
999             s1v   = vec_ld( 0, p_s1 );
1000             s2v   = vec_ld( 0, p_s2 );
1001             destv = vec_avg( s1v, s2v );
1002             vec_st( destv, 0, p_dest );
1003
1004             p_s1   += 16;
1005             p_s2   += 16;
1006             p_dest += 16;
1007         }
1008     }
1009
1010     p_end += 15;
1011
1012     while( p_dest < p_end )
1013     {
1014         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1015     }
1016 }
1017 #endif
1018
1019 /*****************************************************************************
1020  * SendEvents: forward mouse and keyboard events to the parent p_vout
1021  *****************************************************************************/
1022 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
1023                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
1024 {
1025     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
1026     vlc_value_t sentval = newval;
1027
1028     if( !strcmp( psz_var, "mouse-y" ) )
1029     {
1030         switch( p_vout->p_sys->i_mode )
1031         {
1032             case DEINTERLACE_MEAN:
1033             case DEINTERLACE_DISCARD:
1034                 sentval.i_int *= 2;
1035                 break;
1036         }
1037     }
1038
1039     var_Set( p_vout, psz_var, sentval );
1040
1041     return VLC_SUCCESS;
1042 }
1043
1044 /*****************************************************************************
1045  * FilterCallback: called when changing the deinterlace method on the fly.
1046  *****************************************************************************/
1047 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1048                            vlc_value_t oldval, vlc_value_t newval,
1049                            void *p_data )
1050 {
1051     vout_thread_t * p_vout = (vout_thread_t *)p_this;
1052     int i_old_mode = p_vout->p_sys->i_mode;
1053
1054     msg_Dbg( p_vout, "using %s deinterlace mode", newval.psz_string );
1055
1056     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
1057
1058     SetFilterMethod( p_vout, newval.psz_string );
1059
1060     switch( p_vout->render.i_chroma )
1061     {
1062     case VLC_FOURCC('I','4','2','2'):
1063         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1064         return VLC_SUCCESS;
1065         break;
1066
1067     case VLC_FOURCC('I','4','2','0'):
1068     case VLC_FOURCC('I','Y','U','V'):
1069     case VLC_FOURCC('Y','V','1','2'):
1070         switch( p_vout->p_sys->i_mode )
1071         {
1072         case DEINTERLACE_MEAN:
1073         case DEINTERLACE_DISCARD:
1074             if( ( i_old_mode == DEINTERLACE_MEAN )
1075                 || ( i_old_mode == DEINTERLACE_DISCARD ) )
1076             {
1077                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1078                 return VLC_SUCCESS;
1079             }
1080             break;
1081
1082         case DEINTERLACE_BOB:
1083         case DEINTERLACE_BLEND:
1084         case DEINTERLACE_LINEAR:
1085             if( ( i_old_mode == DEINTERLACE_BOB )
1086                 || ( i_old_mode == DEINTERLACE_BLEND )
1087                 || ( i_old_mode == DEINTERLACE_LINEAR ) )
1088             {
1089                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1090                 return VLC_SUCCESS;
1091             }
1092             break;
1093         }
1094         break;
1095
1096     default:
1097         break;
1098     }
1099
1100     /* We need to kill the old vout */
1101
1102     DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
1103
1104     vlc_object_detach( p_vout->p_sys->p_vout );
1105     vout_Destroy( p_vout->p_sys->p_vout );
1106
1107     /* Try to open a new video output */
1108     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
1109
1110     if( p_vout->p_sys->p_vout == NULL )
1111     {
1112         /* Everything failed */
1113         msg_Err( p_vout, "cannot open vout, aborting" );
1114
1115         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1116         return VLC_EGENERIC;
1117     }
1118
1119     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
1120
1121     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1122     return VLC_SUCCESS;
1123 }
1124
1125 /*****************************************************************************
1126  * SendEventsToChild: forward events to the child/children vout
1127  *****************************************************************************/
1128 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
1129                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1130 {
1131     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1132     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
1133     return VLC_SUCCESS;
1134 }