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