]> git.sesse.net Git - vlc/blob - modules/video_filter/deinterlace.c
Move the add/del_callback call (to be sure to have the needed ressources).
[vlc] / modules / video_filter / deinterlace.c
1 /*****************************************************************************
2  * deinterlace.c : deinterlacer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001, 2002, 2003 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <errno.h>
33
34 #ifdef HAVE_ALTIVEC_H
35 #   include <altivec.h>
36 #endif
37
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_vout.h>
41 #include <vlc_sout.h>
42 #include "vlc_filter.h"
43
44 #ifdef CAN_COMPILE_MMXEXT
45 #   include "mmx.h"
46 #endif
47
48 #include "filter_common.h"
49
50 #define DEINTERLACE_DISCARD 1
51 #define DEINTERLACE_MEAN    2
52 #define DEINTERLACE_BLEND   3
53 #define DEINTERLACE_BOB     4
54 #define DEINTERLACE_LINEAR  5
55 #define DEINTERLACE_X       6
56
57 /*****************************************************************************
58  * Local protypes
59  *****************************************************************************/
60 static int  Create    ( vlc_object_t * );
61 static void Destroy   ( vlc_object_t * );
62
63 static int  Init      ( vout_thread_t * );
64 static void End       ( vout_thread_t * );
65 static void Render    ( vout_thread_t *, picture_t * );
66
67 static int  MouseEvent( vlc_object_t *p_this, char const *psz_var,
68                         vlc_value_t oldval, vlc_value_t newval, void *p_data );
69
70 static void RenderDiscard( vout_thread_t *, picture_t *, picture_t *, int );
71 static void RenderBob    ( vout_thread_t *, picture_t *, picture_t *, int );
72 static void RenderMean   ( vout_thread_t *, picture_t *, picture_t * );
73 static void RenderBlend  ( vout_thread_t *, picture_t *, picture_t * );
74 static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
75 static void RenderX      ( picture_t *, picture_t * );
76
77 static void MergeGeneric ( void *, const void *, const void *, size_t );
78 #if defined(CAN_COMPILE_C_ALTIVEC)
79 static void MergeAltivec ( void *, const void *, const void *, size_t );
80 #endif
81 #if defined(CAN_COMPILE_MMXEXT)
82 static void MergeMMXEXT  ( void *, const void *, const void *, size_t );
83 #endif
84 #if defined(CAN_COMPILE_3DNOW)
85 static void Merge3DNow   ( void *, const void *, const void *, size_t );
86 #endif
87 #if defined(CAN_COMPILE_SSE)
88 static void MergeSSE2    ( void *, const void *, const void *, size_t );
89 #endif
90 #if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
91 static void EndMMX       ( void );
92 #endif
93 #if defined(CAN_COMPILE_3DNOW)
94 static void End3DNow     ( void );
95 #endif
96
97 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method );
98 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout );
99
100 static int OpenFilter( vlc_object_t *p_this );
101 static void CloseFilter( vlc_object_t *p_this );
102
103 /*****************************************************************************
104  * Callback prototypes
105  *****************************************************************************/
106 static int FilterCallback( vlc_object_t *, char const *,
107                            vlc_value_t, vlc_value_t, void * );
108
109 /*****************************************************************************
110  * Module descriptor
111  *****************************************************************************/
112 #define MODE_TEXT N_("Deinterlace mode")
113 #define MODE_LONGTEXT N_("Deinterlace method to use for local playback.")
114
115 #define SOUT_MODE_TEXT N_("Streaming deinterlace mode")
116 #define SOUT_MODE_LONGTEXT N_("Deinterlace method to use for streaming.")
117
118 #define FILTER_CFG_PREFIX "sout-deinterlace-"
119
120 static const char *const mode_list[] = {
121     "discard", "blend", "mean", "bob", "linear", "x" };
122 static const char *const mode_list_text[] = {
123     N_("Discard"), N_("Blend"), N_("Mean"), N_("Bob"), N_("Linear"), "X" };
124
125 vlc_module_begin ()
126     set_description( N_("Deinterlacing video filter") )
127     set_shortname( N_("Deinterlace" ))
128     set_capability( "video filter", 0 )
129     set_category( CAT_VIDEO )
130     set_subcategory( SUBCAT_VIDEO_VFILTER )
131
132     set_section( N_("Display"),NULL)
133     add_string( "deinterlace-mode", "discard", NULL, MODE_TEXT,
134                 MODE_LONGTEXT, false )
135         change_string_list( mode_list, mode_list_text, 0 )
136         change_safe ()
137
138     add_shortcut( "deinterlace" )
139     set_callbacks( Create, Destroy )
140
141     add_submodule ()
142     set_capability( "video filter2", 0 )
143     set_section( N_("Streaming"),NULL)
144     add_string( FILTER_CFG_PREFIX "mode", "blend", NULL, SOUT_MODE_TEXT,
145                 SOUT_MODE_LONGTEXT, false )
146         change_string_list( mode_list, mode_list_text, 0 )
147     add_shortcut( "deinterlace" )
148     set_callbacks( OpenFilter, CloseFilter )
149 vlc_module_end ()
150
151 static const char *const ppsz_filter_options[] = {
152     "mode", NULL
153 };
154
155 /*****************************************************************************
156  * vout_sys_t: Deinterlace video output method descriptor
157  *****************************************************************************
158  * This structure is part of the video output thread descriptor.
159  * It describes the Deinterlace specific properties of an output thread.
160  *****************************************************************************/
161 struct vout_sys_t
162 {
163     int        i_mode;        /* Deinterlace mode */
164     bool b_double_rate; /* Shall we double the framerate? */
165     bool b_half_height; /* Shall be devide the height by 2 */
166
167     mtime_t    last_date;
168     mtime_t    next_date;
169
170     vout_thread_t *p_vout;
171
172     vlc_mutex_t filter_lock;
173
174     void (*pf_merge) ( void *, const void *, const void *, size_t );
175     void (*pf_end_merge) ( void );
176 };
177
178 /*****************************************************************************
179  * Control: control facility for the vout (forwards to child vout)
180  *****************************************************************************/
181 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
182 {
183     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
184 }
185
186 /*****************************************************************************
187  * Create: allocates Deinterlace video thread output method
188  *****************************************************************************
189  * This function allocates and initializes a Deinterlace vout method.
190  *****************************************************************************/
191 static int Create( vlc_object_t *p_this )
192 {
193     vout_thread_t *p_vout = (vout_thread_t *)p_this;
194     vlc_value_t val;
195
196     /* Allocate structure */
197     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
198     if( p_vout->p_sys == NULL )
199         return VLC_ENOMEM;
200
201     p_vout->pf_init = Init;
202     p_vout->pf_end = End;
203     p_vout->pf_manage = NULL;
204     p_vout->pf_render = Render;
205     p_vout->pf_display = NULL;
206     p_vout->pf_control = Control;
207
208     p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
209     p_vout->p_sys->b_double_rate = false;
210     p_vout->p_sys->b_half_height = true;
211     p_vout->p_sys->last_date = 0;
212     p_vout->p_sys->p_vout = 0;
213     vlc_mutex_init( &p_vout->p_sys->filter_lock );
214
215 #if defined(CAN_COMPILE_C_ALTIVEC)
216     if( vlc_CPU() & CPU_CAPABILITY_ALTIVEC )
217     {
218         p_vout->p_sys->pf_merge = MergeAltivec;
219         p_vout->p_sys->pf_end_merge = NULL;
220     }
221     else
222 #endif
223 #if defined(CAN_COMPILE_SSE)
224     if( vlc_CPU() & CPU_CAPABILITY_SSE2 )
225     {
226         p_vout->p_sys->pf_merge = MergeSSE2;
227         p_vout->p_sys->pf_end_merge = EndMMX;
228     }
229     else
230 #endif
231 #if defined(CAN_COMPILE_MMXEXT)
232     if( vlc_CPU() & CPU_CAPABILITY_MMXEXT )
233     {
234         p_vout->p_sys->pf_merge = MergeMMXEXT;
235         p_vout->p_sys->pf_end_merge = EndMMX;
236     }
237     else
238 #endif
239 #if defined(CAN_COMPILE_3DNOW)
240     if( vlc_CPU() & CPU_CAPABILITY_3DNOW )
241     {
242         p_vout->p_sys->pf_merge = Merge3DNow;
243         p_vout->p_sys->pf_end_merge = End3DNow;
244     }
245     else
246 #endif
247     {
248         p_vout->p_sys->pf_merge = MergeGeneric;
249         p_vout->p_sys->pf_end_merge = NULL;
250     }
251
252     /* Look what method was requested */
253     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING );
254     var_Change( p_vout, "deinterlace-mode", VLC_VAR_INHERITVALUE, &val, NULL );
255
256     if( val.psz_string == NULL )
257     {
258         msg_Err( p_vout, "configuration variable deinterlace-mode empty" );
259         msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
260
261         val.psz_string = strdup( "discard" );
262     }
263
264     msg_Dbg( p_vout, "using %s deinterlace mode", val.psz_string );
265
266     SetFilterMethod( p_vout, val.psz_string );
267
268     free( val.psz_string );
269
270     return VLC_SUCCESS;
271 }
272
273 /*****************************************************************************
274  * SetFilterMethod: setup the deinterlace method to use.
275  *****************************************************************************/
276 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method )
277 {
278     if( !strcmp( psz_method, "mean" ) )
279     {
280         p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
281         p_vout->p_sys->b_double_rate = false;
282         p_vout->p_sys->b_half_height = true;
283     }
284     else if( !strcmp( psz_method, "blend" )
285              || !strcmp( psz_method, "average" )
286              || !strcmp( psz_method, "combine-fields" ) )
287     {
288         p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
289         p_vout->p_sys->b_double_rate = false;
290         p_vout->p_sys->b_half_height = false;
291     }
292     else if( !strcmp( psz_method, "bob" )
293              || !strcmp( psz_method, "progressive-scan" ) )
294     {
295         p_vout->p_sys->i_mode = DEINTERLACE_BOB;
296         p_vout->p_sys->b_double_rate = true;
297         p_vout->p_sys->b_half_height = false;
298     }
299     else if( !strcmp( psz_method, "linear" ) )
300     {
301         p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
302         p_vout->p_sys->b_double_rate = true;
303         p_vout->p_sys->b_half_height = false;
304     }
305     else if( !strcmp( psz_method, "x" ) )
306     {
307         p_vout->p_sys->i_mode = DEINTERLACE_X;
308         p_vout->p_sys->b_double_rate = false;
309         p_vout->p_sys->b_half_height = false;
310     }
311     else
312     {
313         const bool b_i422 = p_vout->render.i_chroma == VLC_CODEC_I422;
314         if( strcmp( psz_method, "discard" ) )
315             msg_Err( p_vout, "no valid deinterlace mode provided, "
316                      "using \"discard\"" );
317
318         p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
319         p_vout->p_sys->b_double_rate = false;
320         p_vout->p_sys->b_half_height = !b_i422;
321     }
322
323     msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
324 }
325
326 static void GetOutputFormat( vout_thread_t *p_vout,
327                              video_format_t *p_dst, const video_format_t *p_src )
328 {
329     *p_dst = *p_src;
330
331     if( p_vout->p_sys->b_half_height )
332     {
333         p_dst->i_height /= 2;
334         p_dst->i_visible_height /= 2;
335         p_dst->i_y_offset /= 2;
336         p_dst->i_sar_den *= 2;
337     }
338
339     if( p_src->i_chroma == VLC_CODEC_I422 )
340     {
341         switch( p_vout->p_sys->i_mode )
342         {
343         case DEINTERLACE_MEAN:
344         case DEINTERLACE_LINEAR:
345         case DEINTERLACE_X:
346             p_dst->i_chroma = VLC_CODEC_I422;
347             break;
348         default:
349             p_dst->i_chroma = VLC_CODEC_I420;
350             break;
351         }
352     }
353 }
354
355 static bool IsChromaSupported( vlc_fourcc_t i_chroma )
356 {
357     return i_chroma == VLC_CODEC_I420 ||
358            i_chroma == VLC_CODEC_YV12 ||
359            i_chroma == VLC_CODEC_I422;
360 }
361
362 /*****************************************************************************
363  * Init: initialize Deinterlace video thread output method
364  *****************************************************************************/
365 static int Init( vout_thread_t *p_vout )
366 {
367     I_OUTPUTPICTURES = 0;
368
369     if( !IsChromaSupported( p_vout->render.i_chroma ) )
370         return VLC_EGENERIC; /* unknown chroma */
371
372     /* Initialize the output structure, full of directbuffers since we want
373      * the decoder to output directly to our structures. */
374     p_vout->output.i_chroma = p_vout->render.i_chroma;
375     p_vout->output.i_width  = p_vout->render.i_width;
376     p_vout->output.i_height = p_vout->render.i_height;
377     p_vout->output.i_aspect = p_vout->render.i_aspect;
378     p_vout->fmt_out = p_vout->fmt_in;
379
380     /* Try to open the real video output */
381     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
382
383     if( p_vout->p_sys->p_vout == NULL )
384     {
385         /* Everything failed */
386         msg_Err( p_vout, "cannot open vout, aborting" );
387
388         return VLC_EGENERIC;
389     }
390
391     vout_filter_AllocateDirectBuffers( p_vout, VOUT_MAX_PICTURES );
392
393     vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
394
395     var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
396
397     return VLC_SUCCESS;
398 }
399
400 /*****************************************************************************
401  * SpawnRealVout: spawn the real video output.
402  *****************************************************************************/
403 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout )
404 {
405     msg_Dbg( p_vout, "spawning the real video output" );
406
407     video_format_t fmt;
408     GetOutputFormat( p_vout, &fmt, &p_vout->fmt_out );
409
410     return vout_Create( p_vout, &fmt );
411 }
412
413 /*****************************************************************************
414  * End: terminate Deinterlace video thread output method
415  *****************************************************************************/
416 static void End( vout_thread_t *p_vout )
417 {
418     vout_sys_t *p_sys = p_vout->p_sys;
419
420     var_DelCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
421
422     if( p_sys->p_vout )
423     {
424         vout_filter_DelChild( p_vout, p_sys->p_vout, MouseEvent );
425         vout_CloseAndRelease( p_sys->p_vout );
426     }
427
428     vout_filter_ReleaseDirectBuffers( p_vout );
429 }
430
431 /*****************************************************************************
432  * Destroy: destroy Deinterlace video thread output method
433  *****************************************************************************
434  * Terminate an output method created by DeinterlaceCreateOutputMethod
435  *****************************************************************************/
436 static void Destroy( vlc_object_t *p_this )
437 {
438     vout_thread_t *p_vout = (vout_thread_t *)p_this;
439     vlc_mutex_destroy( &p_vout->p_sys->filter_lock );
440     free( p_vout->p_sys );
441 }
442
443 /**
444  * Forward mouse event with proper conversion.
445  */
446 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
447                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
448 {
449     vout_thread_t *p_vout = p_data;
450     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
451
452     if( !strcmp( psz_var, "mouse-y" ) && p_vout->p_sys->b_half_height )
453         newval.i_int *= 2;
454
455     return var_Set( p_vout, psz_var, newval );
456 }
457
458 /*****************************************************************************
459  * Render: displays previously rendered output
460  *****************************************************************************
461  * This function send the currently rendered image to Deinterlace image,
462  * waits until it is displayed and switch the two rendering buffers, preparing
463  * next frame.
464  *****************************************************************************/
465 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
466 {
467     vout_sys_t *p_sys = p_vout->p_sys;
468     picture_t *pp_outpic[2];
469
470     /* FIXME are they needed ? */
471     p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
472     p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
473     p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
474     p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
475
476     /* FIXME p_sys->p_vout->* should NOT be changed FIXME */
477     p_sys->p_vout->fmt_in.i_x_offset = p_vout->fmt_out.i_x_offset;
478     p_sys->p_vout->fmt_in.i_y_offset = p_vout->fmt_out.i_y_offset;
479     p_sys->p_vout->fmt_in.i_visible_width = p_vout->fmt_out.i_visible_width;
480     p_sys->p_vout->fmt_in.i_visible_height = p_vout->fmt_in.i_visible_height;
481     if( p_vout->p_sys->b_half_height )
482     {
483         p_sys->p_vout->fmt_in.i_y_offset /= 2;
484         p_sys->p_vout->fmt_in.i_visible_height /= 2;
485     }
486
487     if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
488     {
489         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
490
491         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
492         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
493         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
494
495         video_format_t fmt = p_vout->fmt_out;
496         if( p_vout->p_sys->b_half_height )
497         {
498             fmt.i_height /= 2; fmt.i_visible_height /= 2; fmt.i_y_offset /= 2;
499             fmt.i_sar_den *= 2;
500         }
501
502         p_sys->p_vout = vout_Request( p_vout, p_sys->p_vout, &fmt );
503     }
504     if( !p_sys->p_vout )
505         return;
506
507     pp_outpic[0] = pp_outpic[1] = NULL;
508
509     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
510
511     /* Get a new picture */
512     while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
513                                                 0, 0, 0 ) )
514               == NULL )
515     {
516         if( !vlc_object_alive( p_vout ) || p_vout->b_error )
517         {
518             vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
519             return;
520         }
521         msleep( VOUT_OUTMEM_SLEEP );
522     }
523
524     pp_outpic[0]->date = p_pic->date;
525
526     /* If we are using double rate, get an additional new picture */
527     if( p_vout->p_sys->b_double_rate )
528     {
529         while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
530                                                  0, 0, 0 ) )
531                   == NULL )
532         {
533             if( !vlc_object_alive( p_vout ) || p_vout->b_error )
534             {
535                 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
536                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
537                 return;
538             }
539             msleep( VOUT_OUTMEM_SLEEP );
540         }
541
542         /* 20ms is a bit arbitrary, but it's only for the first image we get */
543         if( !p_vout->p_sys->last_date )
544             pp_outpic[1]->date = p_pic->date + 20000;
545         else
546             pp_outpic[1]->date = (3 * p_pic->date - p_vout->p_sys->last_date) / 2;
547         p_vout->p_sys->last_date = p_pic->date;
548     }
549
550     switch( p_vout->p_sys->i_mode )
551     {
552         case DEINTERLACE_DISCARD:
553             RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
554             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
555             break;
556
557         case DEINTERLACE_BOB:
558             RenderBob( p_vout, pp_outpic[0], p_pic, p_pic->b_top_field_first ? 0 : 1 );
559             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
560             RenderBob( p_vout, pp_outpic[1], p_pic, p_pic->b_top_field_first ? 1 : 0 );
561             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
562             break;
563
564         case DEINTERLACE_LINEAR:
565             RenderLinear( p_vout, pp_outpic[0], p_pic, p_pic->b_top_field_first ? 0 : 1 );
566             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
567             RenderLinear( p_vout, pp_outpic[1], p_pic, p_pic->b_top_field_first ? 1 : 0 );
568             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
569             break;
570
571         case DEINTERLACE_MEAN:
572             RenderMean( p_vout, pp_outpic[0], p_pic );
573             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
574             break;
575
576         case DEINTERLACE_BLEND:
577             RenderBlend( p_vout, pp_outpic[0], p_pic );
578             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
579             break;
580
581         case DEINTERLACE_X:
582             RenderX( pp_outpic[0], p_pic );
583             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
584             break;
585     }
586     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
587 }
588
589 /*****************************************************************************
590  * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
591  *****************************************************************************/
592 static void RenderDiscard( vout_thread_t *p_vout,
593                            picture_t *p_outpic, picture_t *p_pic, int i_field )
594 {
595     int i_plane;
596
597     /* Copy image and skip lines */
598     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
599     {
600         uint8_t *p_in, *p_out_end, *p_out;
601         int i_increment;
602
603         p_in = p_pic->p[i_plane].p_pixels
604                    + i_field * p_pic->p[i_plane].i_pitch;
605
606         p_out = p_outpic->p[i_plane].p_pixels;
607         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
608                              * p_outpic->p[i_plane].i_visible_lines;
609
610         switch( p_vout->render.i_chroma )
611         {
612         case VLC_CODEC_I420:
613         case VLC_CODEC_YV12:
614
615             for( ; p_out < p_out_end ; )
616             {
617                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
618
619                 p_out += p_outpic->p[i_plane].i_pitch;
620                 p_in += 2 * p_pic->p[i_plane].i_pitch;
621             }
622             break;
623
624         case VLC_CODEC_I422:
625
626             i_increment = 2 * p_pic->p[i_plane].i_pitch;
627
628             if( i_plane == Y_PLANE )
629             {
630                 for( ; p_out < p_out_end ; )
631                 {
632                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
633                     p_out += p_outpic->p[i_plane].i_pitch;
634                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
635                     p_out += p_outpic->p[i_plane].i_pitch;
636                     p_in += i_increment;
637                 }
638             }
639             else
640             {
641                 for( ; p_out < p_out_end ; )
642                 {
643                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
644                     p_out += p_outpic->p[i_plane].i_pitch;
645                     p_in += i_increment;
646                 }
647             }
648             break;
649
650         default:
651             break;
652         }
653     }
654 }
655
656 /*****************************************************************************
657  * RenderBob: renders a BOB picture - simple copy
658  *****************************************************************************/
659 static void RenderBob( vout_thread_t *p_vout,
660                        picture_t *p_outpic, picture_t *p_pic, int i_field )
661 {
662     int i_plane;
663
664     /* Copy image and skip lines */
665     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
666     {
667         uint8_t *p_in, *p_out_end, *p_out;
668
669         p_in = p_pic->p[i_plane].p_pixels;
670         p_out = p_outpic->p[i_plane].p_pixels;
671         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
672                              * p_outpic->p[i_plane].i_visible_lines;
673
674         switch( p_vout->render.i_chroma )
675         {
676             case VLC_CODEC_I420:
677             case VLC_CODEC_YV12:
678                 /* For BOTTOM field we need to add the first line */
679                 if( i_field == 1 )
680                 {
681                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
682                     p_in += p_pic->p[i_plane].i_pitch;
683                     p_out += p_outpic->p[i_plane].i_pitch;
684                 }
685
686                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
687
688                 for( ; p_out < p_out_end ; )
689                 {
690                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
691
692                     p_out += p_outpic->p[i_plane].i_pitch;
693
694                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
695
696                     p_in += 2 * p_pic->p[i_plane].i_pitch;
697                     p_out += p_outpic->p[i_plane].i_pitch;
698                 }
699
700                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
701
702                 /* For TOP field we need to add the last line */
703                 if( i_field == 0 )
704                 {
705                     p_in += p_pic->p[i_plane].i_pitch;
706                     p_out += p_outpic->p[i_plane].i_pitch;
707                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
708                 }
709                 break;
710
711             case VLC_CODEC_I422:
712                 /* For BOTTOM field we need to add the first line */
713                 if( i_field == 1 )
714                 {
715                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
716                     p_in += p_pic->p[i_plane].i_pitch;
717                     p_out += p_outpic->p[i_plane].i_pitch;
718                 }
719
720                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
721
722                 if( i_plane == Y_PLANE )
723                 {
724                     for( ; p_out < p_out_end ; )
725                     {
726                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
727
728                         p_out += p_outpic->p[i_plane].i_pitch;
729
730                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
731
732                         p_in += 2 * p_pic->p[i_plane].i_pitch;
733                         p_out += p_outpic->p[i_plane].i_pitch;
734                     }
735                 }
736                 else
737                 {
738                     for( ; p_out < p_out_end ; )
739                     {
740                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
741
742                         p_out += p_outpic->p[i_plane].i_pitch;
743                         p_in += 2 * p_pic->p[i_plane].i_pitch;
744                     }
745                 }
746
747                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
748
749                 /* For TOP field we need to add the last line */
750                 if( i_field == 0 )
751                 {
752                     p_in += p_pic->p[i_plane].i_pitch;
753                     p_out += p_outpic->p[i_plane].i_pitch;
754                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
755                 }
756                 break;
757         }
758     }
759 }
760
761 #define Merge p_vout->p_sys->pf_merge
762 #define EndMerge if(p_vout->p_sys->pf_end_merge) p_vout->p_sys->pf_end_merge
763
764 /*****************************************************************************
765  * RenderLinear: BOB with linear interpolation
766  *****************************************************************************/
767 static void RenderLinear( vout_thread_t *p_vout,
768                           picture_t *p_outpic, picture_t *p_pic, int i_field )
769 {
770     int i_plane;
771
772     /* Copy image and skip lines */
773     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
774     {
775         uint8_t *p_in, *p_out_end, *p_out;
776
777         p_in = p_pic->p[i_plane].p_pixels;
778         p_out = p_outpic->p[i_plane].p_pixels;
779         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
780                              * p_outpic->p[i_plane].i_visible_lines;
781
782         /* For BOTTOM field we need to add the first line */
783         if( i_field == 1 )
784         {
785             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
786             p_in += p_pic->p[i_plane].i_pitch;
787             p_out += p_outpic->p[i_plane].i_pitch;
788         }
789
790         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
791
792         for( ; p_out < p_out_end ; )
793         {
794             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
795
796             p_out += p_outpic->p[i_plane].i_pitch;
797
798             Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
799                    p_pic->p[i_plane].i_pitch );
800
801             p_in += 2 * p_pic->p[i_plane].i_pitch;
802             p_out += p_outpic->p[i_plane].i_pitch;
803         }
804
805         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
806
807         /* For TOP field we need to add the last line */
808         if( i_field == 0 )
809         {
810             p_in += p_pic->p[i_plane].i_pitch;
811             p_out += p_outpic->p[i_plane].i_pitch;
812             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
813         }
814     }
815     EndMerge();
816 }
817
818 static void RenderMean( vout_thread_t *p_vout,
819                         picture_t *p_outpic, picture_t *p_pic )
820 {
821     int i_plane;
822
823     /* Copy image and skip lines */
824     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
825     {
826         uint8_t *p_in, *p_out_end, *p_out;
827
828         p_in = p_pic->p[i_plane].p_pixels;
829
830         p_out = p_outpic->p[i_plane].p_pixels;
831         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
832                              * p_outpic->p[i_plane].i_visible_lines;
833
834         /* All lines: mean value */
835         for( ; p_out < p_out_end ; )
836         {
837             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
838                    p_pic->p[i_plane].i_pitch );
839
840             p_out += p_outpic->p[i_plane].i_pitch;
841             p_in += 2 * p_pic->p[i_plane].i_pitch;
842         }
843     }
844     EndMerge();
845 }
846
847 static void RenderBlend( vout_thread_t *p_vout,
848                          picture_t *p_outpic, picture_t *p_pic )
849 {
850     int i_plane;
851
852     /* Copy image and skip lines */
853     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
854     {
855         uint8_t *p_in, *p_out_end, *p_out;
856
857         p_in = p_pic->p[i_plane].p_pixels;
858
859         p_out = p_outpic->p[i_plane].p_pixels;
860         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
861                              * p_outpic->p[i_plane].i_visible_lines;
862
863         switch( p_vout->render.i_chroma )
864         {
865             case VLC_CODEC_I420:
866             case VLC_CODEC_YV12:
867                 /* First line: simple copy */
868                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
869                 p_out += p_outpic->p[i_plane].i_pitch;
870
871                 /* Remaining lines: mean value */
872                 for( ; p_out < p_out_end ; )
873                 {
874                     Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
875                            p_pic->p[i_plane].i_pitch );
876
877                     p_out += p_outpic->p[i_plane].i_pitch;
878                     p_in += p_pic->p[i_plane].i_pitch;
879                 }
880                 break;
881
882             case VLC_CODEC_I422:
883                 /* First line: simple copy */
884                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
885                 p_out += p_outpic->p[i_plane].i_pitch;
886
887                 /* Remaining lines: mean value */
888                 if( i_plane == Y_PLANE )
889                 {
890                     for( ; p_out < p_out_end ; )
891                     {
892                         Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
893                                p_pic->p[i_plane].i_pitch );
894
895                         p_out += p_outpic->p[i_plane].i_pitch;
896                         p_in += p_pic->p[i_plane].i_pitch;
897                     }
898                 }
899
900                 else
901                 {
902                     for( ; p_out < p_out_end ; )
903                     {
904                         Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
905                                p_pic->p[i_plane].i_pitch );
906
907                         p_out += p_outpic->p[i_plane].i_pitch;
908                         p_in += 2*p_pic->p[i_plane].i_pitch;
909                     }
910                 }
911                 break;
912         }
913     }
914     EndMerge();
915 }
916
917 #undef Merge
918
919 static void MergeGeneric( void *_p_dest, const void *_p_s1,
920                           const void *_p_s2, size_t i_bytes )
921 {
922     uint8_t* p_dest = (uint8_t*)_p_dest;
923     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
924     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
925     uint8_t* p_end = p_dest + i_bytes - 8;
926
927     while( p_dest < p_end )
928     {
929         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
930         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
931         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
932         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
933         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
934         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
935         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
936         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
937     }
938
939     p_end += 8;
940
941     while( p_dest < p_end )
942     {
943         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
944     }
945 }
946
947 #if defined(CAN_COMPILE_MMXEXT)
948 static void MergeMMXEXT( void *_p_dest, const void *_p_s1, const void *_p_s2,
949                          size_t i_bytes )
950 {
951     uint8_t* p_dest = (uint8_t*)_p_dest;
952     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
953     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
954     uint8_t* p_end = p_dest + i_bytes - 8;
955     while( p_dest < p_end )
956     {
957         __asm__  __volatile__( "movq %2,%%mm1;"
958                                "pavgb %1, %%mm1;"
959                                "movq %%mm1, %0" :"=m" (*p_dest):
960                                                  "m" (*p_s1),
961                                                  "m" (*p_s2) );
962         p_dest += 8;
963         p_s1 += 8;
964         p_s2 += 8;
965     }
966
967     p_end += 8;
968
969     while( p_dest < p_end )
970     {
971         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
972     }
973 }
974 #endif
975
976 #if defined(CAN_COMPILE_3DNOW)
977 static void Merge3DNow( void *_p_dest, const void *_p_s1, const void *_p_s2,
978                         size_t i_bytes )
979 {
980     uint8_t* p_dest = (uint8_t*)_p_dest;
981     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
982     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
983     uint8_t* p_end = p_dest + i_bytes - 8;
984     while( p_dest < p_end )
985     {
986         __asm__  __volatile__( "movq %2,%%mm1;"
987                                "pavgusb %1, %%mm1;"
988                                "movq %%mm1, %0" :"=m" (*p_dest):
989                                                  "m" (*p_s1),
990                                                  "m" (*p_s2) );
991         p_dest += 8;
992         p_s1 += 8;
993         p_s2 += 8;
994     }
995
996     p_end += 8;
997
998     while( p_dest < p_end )
999     {
1000         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1001     }
1002 }
1003 #endif
1004
1005 #if defined(CAN_COMPILE_SSE)
1006 static void MergeSSE2( void *_p_dest, const void *_p_s1, const void *_p_s2,
1007                        size_t i_bytes )
1008 {
1009     uint8_t* p_dest = (uint8_t*)_p_dest;
1010     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
1011     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
1012     uint8_t* p_end;
1013     while( (uintptr_t)p_s1 % 16 )
1014     {
1015         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1016     }
1017     p_end = p_dest + i_bytes - 16;
1018     while( p_dest < p_end )
1019     {
1020         __asm__  __volatile__( "movdqu %2,%%xmm1;"
1021                                "pavgb %1, %%xmm1;"
1022                                "movdqu %%xmm1, %0" :"=m" (*p_dest):
1023                                                  "m" (*p_s1),
1024                                                  "m" (*p_s2) );
1025         p_dest += 16;
1026         p_s1 += 16;
1027         p_s2 += 16;
1028     }
1029
1030     p_end += 16;
1031
1032     while( p_dest < p_end )
1033     {
1034         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1035     }
1036 }
1037 #endif
1038
1039 #if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
1040 static void EndMMX( void )
1041 {
1042     __asm__ __volatile__( "emms" :: );
1043 }
1044 #endif
1045
1046 #if defined(CAN_COMPILE_3DNOW)
1047 static void End3DNow( void )
1048 {
1049     __asm__ __volatile__( "femms" :: );
1050 }
1051 #endif
1052
1053 #ifdef CAN_COMPILE_C_ALTIVEC
1054 static void MergeAltivec( void *_p_dest, const void *_p_s1,
1055                           const void *_p_s2, size_t i_bytes )
1056 {
1057     uint8_t *p_dest = (uint8_t *)_p_dest;
1058     uint8_t *p_s1   = (uint8_t *)_p_s1;
1059     uint8_t *p_s2   = (uint8_t *)_p_s2;
1060     uint8_t *p_end  = p_dest + i_bytes - 15;
1061
1062     /* Use C until the first 16-bytes aligned destination pixel */
1063     while( (int)p_dest & 0xF )
1064     {
1065         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1066     }
1067
1068     if( ( (int)p_s1 & 0xF ) | ( (int)p_s2 & 0xF ) )
1069     {
1070         /* Unaligned source */
1071         vector unsigned char s1v, s2v, destv;
1072         vector unsigned char s1oldv, s2oldv, s1newv, s2newv;
1073         vector unsigned char perm1v, perm2v;
1074
1075         perm1v = vec_lvsl( 0, p_s1 );
1076         perm2v = vec_lvsl( 0, p_s2 );
1077         s1oldv = vec_ld( 0, p_s1 );
1078         s2oldv = vec_ld( 0, p_s2 );
1079
1080         while( p_dest < p_end )
1081         {
1082             s1newv = vec_ld( 16, p_s1 );
1083             s2newv = vec_ld( 16, p_s2 );
1084             s1v    = vec_perm( s1oldv, s1newv, perm1v );
1085             s2v    = vec_perm( s2oldv, s2newv, perm2v );
1086             s1oldv = s1newv;
1087             s2oldv = s2newv;
1088             destv  = vec_avg( s1v, s2v );
1089             vec_st( destv, 0, p_dest );
1090
1091             p_s1   += 16;
1092             p_s2   += 16;
1093             p_dest += 16;
1094         }
1095     }
1096     else
1097     {
1098         /* Aligned source */
1099         vector unsigned char s1v, s2v, destv;
1100
1101         while( p_dest < p_end )
1102         {
1103             s1v   = vec_ld( 0, p_s1 );
1104             s2v   = vec_ld( 0, p_s2 );
1105             destv = vec_avg( s1v, s2v );
1106             vec_st( destv, 0, p_dest );
1107
1108             p_s1   += 16;
1109             p_s2   += 16;
1110             p_dest += 16;
1111         }
1112     }
1113
1114     p_end += 15;
1115
1116     while( p_dest < p_end )
1117     {
1118         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1119     }
1120 }
1121 #endif
1122
1123 /*****************************************************************************
1124  * RenderX: This algo works on a 8x8 block basic, it copies the top field
1125  * and apply a process to recreate the bottom field :
1126  *  If a 8x8 block is classified as :
1127  *   - progressive: it applies a small blend (1,6,1)
1128  *   - interlaced:
1129  *    * in the MMX version: we do a ME between the 2 fields, if there is a
1130  *    good match we use MC to recreate the bottom field (with a small
1131  *    blend (1,6,1) )
1132  *    * otherwise: it recreates the bottom field by an edge oriented
1133  *    interpolation.
1134   *****************************************************************************/
1135
1136 /* XDeint8x8Detect: detect if a 8x8 block is interlaced.
1137  * XXX: It need to access to 8x10
1138  * We use more than 8 lines to help with scrolling (text)
1139  * (and because XDeint8x8Frame use line 9)
1140  * XXX: smooth/uniform area with noise detection doesn't works well
1141  * but it's not really a problem because they don't have much details anyway
1142  */
1143 static inline int ssd( int a ) { return a*a; }
1144 static inline int XDeint8x8DetectC( uint8_t *src, int i_src )
1145 {
1146     int y, x;
1147     int ff, fr;
1148     int fc;
1149
1150     /* Detect interlacing */
1151     fc = 0;
1152     for( y = 0; y < 7; y += 2 )
1153     {
1154         ff = fr = 0;
1155         for( x = 0; x < 8; x++ )
1156         {
1157             fr += ssd(src[      x] - src[1*i_src+x]) +
1158                   ssd(src[i_src+x] - src[2*i_src+x]);
1159             ff += ssd(src[      x] - src[2*i_src+x]) +
1160                   ssd(src[i_src+x] - src[3*i_src+x]);
1161         }
1162         if( ff < 6*fr/8 && fr > 32 )
1163             fc++;
1164
1165         src += 2*i_src;
1166     }
1167
1168     return fc < 1 ? false : true;
1169 }
1170 #ifdef CAN_COMPILE_MMXEXT
1171 static inline int XDeint8x8DetectMMXEXT( uint8_t *src, int i_src )
1172 {
1173
1174     int y, x;
1175     int32_t ff, fr;
1176     int fc;
1177
1178     /* Detect interlacing */
1179     fc = 0;
1180     pxor_r2r( mm7, mm7 );
1181     for( y = 0; y < 9; y += 2 )
1182     {
1183         ff = fr = 0;
1184         pxor_r2r( mm5, mm5 );
1185         pxor_r2r( mm6, mm6 );
1186         for( x = 0; x < 8; x+=4 )
1187         {
1188             movd_m2r( src[        x], mm0 );
1189             movd_m2r( src[1*i_src+x], mm1 );
1190             movd_m2r( src[2*i_src+x], mm2 );
1191             movd_m2r( src[3*i_src+x], mm3 );
1192
1193             punpcklbw_r2r( mm7, mm0 );
1194             punpcklbw_r2r( mm7, mm1 );
1195             punpcklbw_r2r( mm7, mm2 );
1196             punpcklbw_r2r( mm7, mm3 );
1197
1198             movq_r2r( mm0, mm4 );
1199
1200             psubw_r2r( mm1, mm0 );
1201             psubw_r2r( mm2, mm4 );
1202
1203             psubw_r2r( mm1, mm2 );
1204             psubw_r2r( mm1, mm3 );
1205
1206             pmaddwd_r2r( mm0, mm0 );
1207             pmaddwd_r2r( mm4, mm4 );
1208             pmaddwd_r2r( mm2, mm2 );
1209             pmaddwd_r2r( mm3, mm3 );
1210             paddd_r2r( mm0, mm2 );
1211             paddd_r2r( mm4, mm3 );
1212             paddd_r2r( mm2, mm5 );
1213             paddd_r2r( mm3, mm6 );
1214         }
1215
1216         movq_r2r( mm5, mm0 );
1217         psrlq_i2r( 32, mm0 );
1218         paddd_r2r( mm0, mm5 );
1219         movd_r2m( mm5, fr );
1220
1221         movq_r2r( mm6, mm0 );
1222         psrlq_i2r( 32, mm0 );
1223         paddd_r2r( mm0, mm6 );
1224         movd_r2m( mm6, ff );
1225
1226         if( ff < 6*fr/8 && fr > 32 )
1227             fc++;
1228
1229         src += 2*i_src;
1230     }
1231     return fc;
1232 }
1233 #endif
1234
1235 static inline void XDeint8x8MergeC( uint8_t *dst, int i_dst,
1236                                     uint8_t *src1, int i_src1,
1237                                     uint8_t *src2, int i_src2 )
1238 {
1239     int y, x;
1240
1241     /* Progressive */
1242     for( y = 0; y < 8; y += 2 )
1243     {
1244         memcpy( dst, src1, 8 );
1245         dst  += i_dst;
1246
1247         for( x = 0; x < 8; x++ )
1248             dst[x] = (src1[x] + 6*src2[x] + src1[i_src1+x] + 4 ) >> 3;
1249         dst += i_dst;
1250
1251         src1 += i_src1;
1252         src2 += i_src2;
1253     }
1254 }
1255
1256 #ifdef CAN_COMPILE_MMXEXT
1257 static inline void XDeint8x8MergeMMXEXT( uint8_t *dst, int i_dst,
1258                                          uint8_t *src1, int i_src1,
1259                                          uint8_t *src2, int i_src2 )
1260 {
1261     static const uint64_t m_4 = INT64_C(0x0004000400040004);
1262     int y, x;
1263
1264     /* Progressive */
1265     pxor_r2r( mm7, mm7 );
1266     for( y = 0; y < 8; y += 2 )
1267     {
1268         for( x = 0; x < 8; x +=4 )
1269         {
1270             movd_m2r( src1[x], mm0 );
1271             movd_r2m( mm0, dst[x] );
1272
1273             movd_m2r( src2[x], mm1 );
1274             movd_m2r( src1[i_src1+x], mm2 );
1275
1276             punpcklbw_r2r( mm7, mm0 );
1277             punpcklbw_r2r( mm7, mm1 );
1278             punpcklbw_r2r( mm7, mm2 );
1279             paddw_r2r( mm1, mm1 );
1280             movq_r2r( mm1, mm3 );
1281             paddw_r2r( mm3, mm3 );
1282             paddw_r2r( mm2, mm0 );
1283             paddw_r2r( mm3, mm1 );
1284             paddw_m2r( m_4, mm1 );
1285             paddw_r2r( mm1, mm0 );
1286             psraw_i2r( 3, mm0 );
1287             packuswb_r2r( mm7, mm0 );
1288             movd_r2m( mm0, dst[i_dst+x] );
1289         }
1290         dst += 2*i_dst;
1291         src1 += i_src1;
1292         src2 += i_src2;
1293     }
1294 }
1295
1296 #endif
1297
1298 /* For debug */
1299 static inline void XDeint8x8Set( uint8_t *dst, int i_dst, uint8_t v )
1300 {
1301     int y;
1302     for( y = 0; y < 8; y++ )
1303         memset( &dst[y*i_dst], v, 8 );
1304 }
1305
1306 /* XDeint8x8FieldE: Stupid deinterlacing (1,0,1) for block that miss a
1307  * neighbour
1308  * (Use 8x9 pixels)
1309  * TODO: a better one for the inner part.
1310  */
1311 static inline void XDeint8x8FieldEC( uint8_t *dst, int i_dst,
1312                                      uint8_t *src, int i_src )
1313 {
1314     int y, x;
1315
1316     /* Interlaced */
1317     for( y = 0; y < 8; y += 2 )
1318     {
1319         memcpy( dst, src, 8 );
1320         dst += i_dst;
1321
1322         for( x = 0; x < 8; x++ )
1323             dst[x] = (src[x] + src[2*i_src+x] ) >> 1;
1324         dst += 1*i_dst;
1325         src += 2*i_src;
1326     }
1327 }
1328 #ifdef CAN_COMPILE_MMXEXT
1329 static inline void XDeint8x8FieldEMMXEXT( uint8_t *dst, int i_dst,
1330                                           uint8_t *src, int i_src )
1331 {
1332     int y;
1333
1334     /* Interlaced */
1335     for( y = 0; y < 8; y += 2 )
1336     {
1337         movq_m2r( src[0], mm0 );
1338         movq_r2m( mm0, dst[0] );
1339         dst += i_dst;
1340
1341         movq_m2r( src[2*i_src], mm1 );
1342         pavgb_r2r( mm1, mm0 );
1343
1344         movq_r2m( mm0, dst[0] );
1345
1346         dst += 1*i_dst;
1347         src += 2*i_src;
1348     }
1349 }
1350 #endif
1351
1352 /* XDeint8x8Field: Edge oriented interpolation
1353  * (Need -4 and +5 pixels H, +1 line)
1354  */
1355 static inline void XDeint8x8FieldC( uint8_t *dst, int i_dst,
1356                                     uint8_t *src, int i_src )
1357 {
1358     int y, x;
1359
1360     /* Interlaced */
1361     for( y = 0; y < 8; y += 2 )
1362     {
1363         memcpy( dst, src, 8 );
1364         dst += i_dst;
1365
1366         for( x = 0; x < 8; x++ )
1367         {
1368             uint8_t *src2 = &src[2*i_src];
1369             /* I use 8 pixels just to match the MMX version, but it's overkill
1370              * 5 would be enough (less isn't good) */
1371             const int c0 = abs(src[x-4]-src2[x-2]) + abs(src[x-3]-src2[x-1]) +
1372                            abs(src[x-2]-src2[x+0]) + abs(src[x-1]-src2[x+1]) +
1373                            abs(src[x+0]-src2[x+2]) + abs(src[x+1]-src2[x+3]) +
1374                            abs(src[x+2]-src2[x+4]) + abs(src[x+3]-src2[x+5]);
1375
1376             const int c1 = abs(src[x-3]-src2[x-3]) + abs(src[x-2]-src2[x-2]) +
1377                            abs(src[x-1]-src2[x-1]) + abs(src[x+0]-src2[x+0]) +
1378                            abs(src[x+1]-src2[x+1]) + abs(src[x+2]-src2[x+2]) +
1379                            abs(src[x+3]-src2[x+3]) + abs(src[x+4]-src2[x+4]);
1380
1381             const int c2 = abs(src[x-2]-src2[x-4]) + abs(src[x-1]-src2[x-3]) +
1382                            abs(src[x+0]-src2[x-2]) + abs(src[x+1]-src2[x-1]) +
1383                            abs(src[x+2]-src2[x+0]) + abs(src[x+3]-src2[x+1]) +
1384                            abs(src[x+4]-src2[x+2]) + abs(src[x+5]-src2[x+3]);
1385
1386             if( c0 < c1 && c1 <= c2 )
1387                 dst[x] = (src[x-1] + src2[x+1]) >> 1;
1388             else if( c2 < c1 && c1 <= c0 )
1389                 dst[x] = (src[x+1] + src2[x-1]) >> 1;
1390             else
1391                 dst[x] = (src[x+0] + src2[x+0]) >> 1;
1392         }
1393
1394         dst += 1*i_dst;
1395         src += 2*i_src;
1396     }
1397 }
1398 #ifdef CAN_COMPILE_MMXEXT
1399 static inline void XDeint8x8FieldMMXEXT( uint8_t *dst, int i_dst,
1400                                          uint8_t *src, int i_src )
1401 {
1402     int y, x;
1403
1404     /* Interlaced */
1405     for( y = 0; y < 8; y += 2 )
1406     {
1407         memcpy( dst, src, 8 );
1408         dst += i_dst;
1409
1410         for( x = 0; x < 8; x++ )
1411         {
1412             uint8_t *src2 = &src[2*i_src];
1413             int32_t c0, c1, c2;
1414
1415             movq_m2r( src[x-2], mm0 );
1416             movq_m2r( src[x-3], mm1 );
1417             movq_m2r( src[x-4], mm2 );
1418
1419             psadbw_m2r( src2[x-4], mm0 );
1420             psadbw_m2r( src2[x-3], mm1 );
1421             psadbw_m2r( src2[x-2], mm2 );
1422
1423             movd_r2m( mm0, c2 );
1424             movd_r2m( mm1, c1 );
1425             movd_r2m( mm2, c0 );
1426
1427             if( c0 < c1 && c1 <= c2 )
1428                 dst[x] = (src[x-1] + src2[x+1]) >> 1;
1429             else if( c2 < c1 && c1 <= c0 )
1430                 dst[x] = (src[x+1] + src2[x-1]) >> 1;
1431             else
1432                 dst[x] = (src[x+0] + src2[x+0]) >> 1;
1433         }
1434
1435         dst += 1*i_dst;
1436         src += 2*i_src;
1437     }
1438 }
1439 #endif
1440
1441 /* NxN arbitray size (and then only use pixel in the NxN block)
1442  */
1443 static inline int XDeintNxNDetect( uint8_t *src, int i_src,
1444                                    int i_height, int i_width )
1445 {
1446     int y, x;
1447     int ff, fr;
1448     int fc;
1449
1450
1451     /* Detect interlacing */
1452     /* FIXME way too simple, need to be more like XDeint8x8Detect */
1453     ff = fr = 0;
1454     fc = 0;
1455     for( y = 0; y < i_height - 2; y += 2 )
1456     {
1457         const uint8_t *s = &src[y*i_src];
1458         for( x = 0; x < i_width; x++ )
1459         {
1460             fr += ssd(s[      x] - s[1*i_src+x]);
1461             ff += ssd(s[      x] - s[2*i_src+x]);
1462         }
1463         if( ff < fr && fr > i_width / 2 )
1464             fc++;
1465     }
1466
1467     return fc < 2 ? false : true;
1468 }
1469
1470 static inline void XDeintNxNFrame( uint8_t *dst, int i_dst,
1471                                    uint8_t *src, int i_src,
1472                                    int i_width, int i_height )
1473 {
1474     int y, x;
1475
1476     /* Progressive */
1477     for( y = 0; y < i_height; y += 2 )
1478     {
1479         memcpy( dst, src, i_width );
1480         dst += i_dst;
1481
1482         if( y < i_height - 2 )
1483         {
1484             for( x = 0; x < i_width; x++ )
1485                 dst[x] = (src[x] + 2*src[1*i_src+x] + src[2*i_src+x] + 2 ) >> 2;
1486         }
1487         else
1488         {
1489             /* Blend last line */
1490             for( x = 0; x < i_width; x++ )
1491                 dst[x] = (src[x] + src[1*i_src+x] ) >> 1;
1492         }
1493         dst += 1*i_dst;
1494         src += 2*i_src;
1495     }
1496 }
1497
1498 static inline void XDeintNxNField( uint8_t *dst, int i_dst,
1499                                    uint8_t *src, int i_src,
1500                                    int i_width, int i_height )
1501 {
1502     int y, x;
1503
1504     /* Interlaced */
1505     for( y = 0; y < i_height; y += 2 )
1506     {
1507         memcpy( dst, src, i_width );
1508         dst += i_dst;
1509
1510         if( y < i_height - 2 )
1511         {
1512             for( x = 0; x < i_width; x++ )
1513                 dst[x] = (src[x] + src[2*i_src+x] ) >> 1;
1514         }
1515         else
1516         {
1517             /* Blend last line */
1518             for( x = 0; x < i_width; x++ )
1519                 dst[x] = (src[x] + src[i_src+x]) >> 1;
1520         }
1521         dst += 1*i_dst;
1522         src += 2*i_src;
1523     }
1524 }
1525
1526 static inline void XDeintNxN( uint8_t *dst, int i_dst, uint8_t *src, int i_src,
1527                               int i_width, int i_height )
1528 {
1529     if( XDeintNxNDetect( src, i_src, i_width, i_height ) )
1530         XDeintNxNField( dst, i_dst, src, i_src, i_width, i_height );
1531     else
1532         XDeintNxNFrame( dst, i_dst, src, i_src, i_width, i_height );
1533 }
1534
1535
1536 static inline int median( int a, int b, int c )
1537 {
1538     int min = a, max =a;
1539     if( b < min )
1540         min = b;
1541     else
1542         max = b;
1543
1544     if( c < min )
1545         min = c;
1546     else if( c > max )
1547         max = c;
1548
1549     return a + b + c - min - max;
1550 }
1551
1552
1553 /* XDeintBand8x8:
1554  */
1555 static inline void XDeintBand8x8C( uint8_t *dst, int i_dst,
1556                                    uint8_t *src, int i_src,
1557                                    const int i_mbx, int i_modx )
1558 {
1559     int x;
1560
1561     for( x = 0; x < i_mbx; x++ )
1562     {
1563         int s;
1564         if( ( s = XDeint8x8DetectC( src, i_src ) ) )
1565         {
1566             if( x == 0 || x == i_mbx - 1 )
1567                 XDeint8x8FieldEC( dst, i_dst, src, i_src );
1568             else
1569                 XDeint8x8FieldC( dst, i_dst, src, i_src );
1570         }
1571         else
1572         {
1573             XDeint8x8MergeC( dst, i_dst,
1574                              &src[0*i_src], 2*i_src,
1575                              &src[1*i_src], 2*i_src );
1576         }
1577
1578         dst += 8;
1579         src += 8;
1580     }
1581
1582     if( i_modx )
1583         XDeintNxN( dst, i_dst, src, i_src, i_modx, 8 );
1584 }
1585 #ifdef CAN_COMPILE_MMXEXT
1586 static inline void XDeintBand8x8MMXEXT( uint8_t *dst, int i_dst,
1587                                         uint8_t *src, int i_src,
1588                                         const int i_mbx, int i_modx )
1589 {
1590     int x;
1591
1592     /* Reset current line */
1593     for( x = 0; x < i_mbx; x++ )
1594     {
1595         int s;
1596         if( ( s = XDeint8x8DetectMMXEXT( src, i_src ) ) )
1597         {
1598             if( x == 0 || x == i_mbx - 1 )
1599                 XDeint8x8FieldEMMXEXT( dst, i_dst, src, i_src );
1600             else
1601                 XDeint8x8FieldMMXEXT( dst, i_dst, src, i_src );
1602         }
1603         else
1604         {
1605             XDeint8x8MergeMMXEXT( dst, i_dst,
1606                                   &src[0*i_src], 2*i_src,
1607                                   &src[1*i_src], 2*i_src );
1608         }
1609
1610         dst += 8;
1611         src += 8;
1612     }
1613
1614     if( i_modx )
1615         XDeintNxN( dst, i_dst, src, i_src, i_modx, 8 );
1616 }
1617 #endif
1618
1619 static void RenderX( picture_t *p_outpic, picture_t *p_pic )
1620 {
1621     int i_plane;
1622
1623     /* Copy image and skip lines */
1624     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
1625     {
1626         const int i_mby = ( p_outpic->p[i_plane].i_visible_lines + 7 )/8 - 1;
1627         const int i_mbx = p_outpic->p[i_plane].i_visible_pitch/8;
1628
1629         const int i_mody = p_outpic->p[i_plane].i_visible_lines - 8*i_mby;
1630         const int i_modx = p_outpic->p[i_plane].i_visible_pitch - 8*i_mbx;
1631
1632         const int i_dst = p_outpic->p[i_plane].i_pitch;
1633         const int i_src = p_pic->p[i_plane].i_pitch;
1634
1635         int y, x;
1636
1637         for( y = 0; y < i_mby; y++ )
1638         {
1639             uint8_t *dst = &p_outpic->p[i_plane].p_pixels[8*y*i_dst];
1640             uint8_t *src = &p_pic->p[i_plane].p_pixels[8*y*i_src];
1641
1642 #ifdef CAN_COMPILE_MMXEXT
1643             if( vlc_CPU() & CPU_CAPABILITY_MMXEXT )
1644                 XDeintBand8x8MMXEXT( dst, i_dst, src, i_src, i_mbx, i_modx );
1645             else
1646 #endif
1647                 XDeintBand8x8C( dst, i_dst, src, i_src, i_mbx, i_modx );
1648         }
1649
1650         /* Last line (C only)*/
1651         if( i_mody )
1652         {
1653             uint8_t *dst = &p_outpic->p[i_plane].p_pixels[8*y*i_dst];
1654             uint8_t *src = &p_pic->p[i_plane].p_pixels[8*y*i_src];
1655
1656             for( x = 0; x < i_mbx; x++ )
1657             {
1658                 XDeintNxN( dst, i_dst, src, i_src, 8, i_mody );
1659
1660                 dst += 8;
1661                 src += 8;
1662             }
1663
1664             if( i_modx )
1665                 XDeintNxN( dst, i_dst, src, i_src, i_modx, i_mody );
1666         }
1667     }
1668
1669 #ifdef CAN_COMPILE_MMXEXT
1670     if( vlc_CPU() & CPU_CAPABILITY_MMXEXT )
1671         emms();
1672 #endif
1673 }
1674
1675 /*****************************************************************************
1676  * FilterCallback: called when changing the deinterlace method on the fly.
1677  *****************************************************************************/
1678 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1679                            vlc_value_t oldval, vlc_value_t newval,
1680                            void *p_data )
1681 {
1682     VLC_UNUSED(psz_cmd); VLC_UNUSED(p_data); VLC_UNUSED(oldval);
1683     vout_thread_t * p_vout = (vout_thread_t *)p_this;
1684
1685     msg_Dbg( p_vout, "using %s deinterlace mode", newval.psz_string );
1686
1687     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
1688     const bool b_old_half_height = p_vout->p_sys->b_half_height;
1689
1690     SetFilterMethod( p_vout, newval.psz_string );
1691
1692     if( !b_old_half_height == !p_vout->p_sys->b_half_height )
1693     {
1694         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1695         return VLC_SUCCESS;
1696     }
1697
1698     /* We need to kill the old vout */
1699     if( p_vout->p_sys->p_vout )
1700     {
1701         vout_filter_DelChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
1702         vout_CloseAndRelease( p_vout->p_sys->p_vout );
1703     }
1704
1705     /* Try to open a new video output */
1706     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
1707
1708     if( p_vout->p_sys->p_vout == NULL )
1709     {
1710         /* Everything failed */
1711         msg_Err( p_vout, "cannot open vout, aborting" );
1712
1713         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1714         return VLC_EGENERIC;
1715     }
1716
1717     vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
1718
1719     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1720     return VLC_SUCCESS;
1721 }
1722
1723 /*****************************************************************************
1724  * video filter2 functions
1725  *****************************************************************************/
1726 static picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
1727 {
1728     vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_sys;
1729     picture_t *p_pic_dst;
1730
1731     /* Request output picture */
1732     p_pic_dst = filter_NewPicture( p_filter );
1733     if( p_pic_dst == NULL )
1734     {
1735         picture_Release( p_pic );
1736         return NULL;
1737     }
1738
1739     switch( p_vout->p_sys->i_mode )
1740     {
1741         case DEINTERLACE_DISCARD:
1742             RenderDiscard( p_vout, p_pic_dst, p_pic, 0 );
1743             break;
1744
1745         case DEINTERLACE_BOB:
1746 #if 0
1747             RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
1748             RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
1749             break;
1750 #endif
1751
1752         case DEINTERLACE_LINEAR:
1753 #if 0
1754             RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
1755             RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
1756 #endif
1757             msg_Err( p_vout, "doubling the frame rate is not supported yet" );
1758             picture_Release( p_pic_dst );
1759             picture_Release( p_pic );
1760             return NULL;
1761
1762         case DEINTERLACE_MEAN:
1763             RenderMean( p_vout, p_pic_dst, p_pic );
1764             break;
1765
1766         case DEINTERLACE_BLEND:
1767             RenderBlend( p_vout, p_pic_dst, p_pic );
1768             break;
1769
1770         case DEINTERLACE_X:
1771             RenderX( p_pic_dst, p_pic );
1772             break;
1773     }
1774
1775     picture_CopyProperties( p_pic_dst, p_pic );
1776     p_pic_dst->b_progressive = true;
1777
1778     picture_Release( p_pic );
1779     return p_pic_dst;
1780 }
1781
1782 /*****************************************************************************
1783  * OpenFilter:
1784  *****************************************************************************/
1785 static int OpenFilter( vlc_object_t *p_this )
1786 {
1787     filter_t *p_filter = (filter_t*)p_this;
1788     vout_thread_t *p_vout;
1789     vlc_value_t val;
1790
1791     if( !IsChromaSupported( p_filter->fmt_in.video.i_chroma ) )
1792         return VLC_EGENERIC;
1793
1794     /* Impossible to use VLC_OBJECT_VOUT here because it would be used
1795      * by spu filters */
1796     p_vout = vlc_object_create( p_filter, sizeof(vout_thread_t) );
1797     vlc_object_attach( p_vout, p_filter );
1798     p_filter->p_sys = (filter_sys_t *)p_vout;
1799     p_vout->render.i_chroma = p_filter->fmt_in.video.i_chroma;
1800
1801     config_ChainParse( p_filter, FILTER_CFG_PREFIX, ppsz_filter_options,
1802                    p_filter->p_cfg );
1803     var_Get( p_filter, FILTER_CFG_PREFIX "mode", &val );
1804
1805     var_Create( p_filter, "deinterlace-mode", VLC_VAR_STRING );
1806     var_Set( p_filter, "deinterlace-mode", val );
1807     free( val.psz_string );
1808
1809     if( Create( VLC_OBJECT(p_vout) ) != VLC_SUCCESS )
1810     {
1811         vlc_object_detach( p_vout );
1812         vlc_object_release( p_vout );
1813         return VLC_EGENERIC;
1814     }
1815
1816     video_format_t fmt;
1817     GetOutputFormat( p_vout, &fmt, &p_filter->fmt_in.video );
1818     if( !p_filter->b_allow_fmt_out_change &&
1819         ( fmt.i_chroma != p_filter->fmt_in.video.i_chroma ||
1820           fmt.i_height != p_filter->fmt_in.video.i_height ) )
1821     {
1822         CloseFilter( VLC_OBJECT(p_filter) );
1823         return VLC_EGENERIC;
1824     }
1825     p_filter->fmt_out.video = fmt;
1826     p_filter->fmt_out.i_codec = fmt.i_chroma;
1827     p_filter->pf_video_filter = Deinterlace;
1828
1829     msg_Dbg( p_filter, "deinterlacing" );
1830
1831     return VLC_SUCCESS;
1832 }
1833
1834 /*****************************************************************************
1835  * CloseFilter: clean up the filter
1836  *****************************************************************************/
1837 static void CloseFilter( vlc_object_t *p_this )
1838 {
1839     filter_t *p_filter = (filter_t*)p_this;
1840     vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_sys;
1841
1842     Destroy( VLC_OBJECT(p_vout) );
1843     vlc_object_detach( p_vout );
1844     vlc_object_release( p_vout );
1845 }
1846