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