]> git.sesse.net Git - vlc/blob - modules/video_filter/deinterlace.c
08f135d8707e5a781800ce428c273ef3c71cc43f
[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_FOURCC('I','4','2','2');
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_FOURCC('I','4','2','2') )
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_FOURCC('I','4','2','2');
347             break;
348         default:
349             p_dst->i_chroma = VLC_FOURCC('I','4','2','0');
350             break;
351         }
352     }
353 }
354
355 static bool IsChromaSupported( vlc_fourcc_t i_chroma )
356 {
357     return i_chroma == VLC_FOURCC('I','4','2','0') ||
358            i_chroma == VLC_FOURCC('I','Y','U','V') ||
359            i_chroma == VLC_FOURCC('Y','V','1','2') ||
360            i_chroma == VLC_FOURCC('I','4','2','2');
361 }
362
363 /*****************************************************************************
364  * Init: initialize Deinterlace video thread output method
365  *****************************************************************************/
366 static int Init( vout_thread_t *p_vout )
367 {
368     I_OUTPUTPICTURES = 0;
369
370     if( !IsChromaSupported( p_vout->render.i_chroma ) )
371         return VLC_EGENERIC; /* unknown chroma */
372
373     /* Initialize the output structure, full of directbuffers since we want
374      * the decoder to output directly to our structures. */
375     p_vout->output.i_chroma = p_vout->render.i_chroma;
376     p_vout->output.i_width  = p_vout->render.i_width;
377     p_vout->output.i_height = p_vout->render.i_height;
378     p_vout->output.i_aspect = p_vout->render.i_aspect;
379     p_vout->fmt_out = p_vout->fmt_in;
380
381     /* Try to open the real video output */
382     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
383
384     if( p_vout->p_sys->p_vout == NULL )
385     {
386         /* Everything failed */
387         msg_Err( p_vout, "cannot open vout, aborting" );
388
389         return VLC_EGENERIC;
390     }
391
392     var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
393
394     vout_filter_AllocateDirectBuffers( p_vout, VOUT_MAX_PICTURES );
395
396     vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
397
398     return VLC_SUCCESS;
399 }
400
401 /*****************************************************************************
402  * SpawnRealVout: spawn the real video output.
403  *****************************************************************************/
404 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout )
405 {
406     msg_Dbg( p_vout, "spawning the real video output" );
407
408     video_format_t fmt;
409     GetOutputFormat( p_vout, &fmt, &p_vout->fmt_out );
410
411     return vout_Create( p_vout, &fmt );
412 }
413
414 /*****************************************************************************
415  * End: terminate Deinterlace video thread output method
416  *****************************************************************************/
417 static void End( vout_thread_t *p_vout )
418 {
419     vout_sys_t *p_sys = p_vout->p_sys;
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_FOURCC('I','4','2','0'):
612         case VLC_FOURCC('I','Y','U','V'):
613         case VLC_FOURCC('Y','V','1','2'):
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_FOURCC('I','4','2','2'):
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_FOURCC('I','4','2','0'):
677             case VLC_FOURCC('I','Y','U','V'):
678             case VLC_FOURCC('Y','V','1','2'):
679                 /* For BOTTOM field we need to add the first line */
680                 if( i_field == 1 )
681                 {
682                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
683                     p_in += p_pic->p[i_plane].i_pitch;
684                     p_out += p_outpic->p[i_plane].i_pitch;
685                 }
686
687                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
688
689                 for( ; p_out < p_out_end ; )
690                 {
691                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
692
693                     p_out += p_outpic->p[i_plane].i_pitch;
694
695                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
696
697                     p_in += 2 * p_pic->p[i_plane].i_pitch;
698                     p_out += p_outpic->p[i_plane].i_pitch;
699                 }
700
701                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
702
703                 /* For TOP field we need to add the last line */
704                 if( i_field == 0 )
705                 {
706                     p_in += p_pic->p[i_plane].i_pitch;
707                     p_out += p_outpic->p[i_plane].i_pitch;
708                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
709                 }
710                 break;
711
712             case VLC_FOURCC('I','4','2','2'):
713                 /* For BOTTOM field we need to add the first line */
714                 if( i_field == 1 )
715                 {
716                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
717                     p_in += p_pic->p[i_plane].i_pitch;
718                     p_out += p_outpic->p[i_plane].i_pitch;
719                 }
720
721                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
722
723                 if( i_plane == Y_PLANE )
724                 {
725                     for( ; p_out < p_out_end ; )
726                     {
727                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
728
729                         p_out += p_outpic->p[i_plane].i_pitch;
730
731                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
732
733                         p_in += 2 * p_pic->p[i_plane].i_pitch;
734                         p_out += p_outpic->p[i_plane].i_pitch;
735                     }
736                 }
737                 else
738                 {
739                     for( ; p_out < p_out_end ; )
740                     {
741                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
742
743                         p_out += p_outpic->p[i_plane].i_pitch;
744                         p_in += 2 * p_pic->p[i_plane].i_pitch;
745                     }
746                 }
747
748                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
749
750                 /* For TOP field we need to add the last line */
751                 if( i_field == 0 )
752                 {
753                     p_in += p_pic->p[i_plane].i_pitch;
754                     p_out += p_outpic->p[i_plane].i_pitch;
755                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
756                 }
757                 break;
758         }
759     }
760 }
761
762 #define Merge p_vout->p_sys->pf_merge
763 #define EndMerge if(p_vout->p_sys->pf_end_merge) p_vout->p_sys->pf_end_merge
764
765 /*****************************************************************************
766  * RenderLinear: BOB with linear interpolation
767  *****************************************************************************/
768 static void RenderLinear( vout_thread_t *p_vout,
769                           picture_t *p_outpic, picture_t *p_pic, int i_field )
770 {
771     int i_plane;
772
773     /* Copy image and skip lines */
774     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
775     {
776         uint8_t *p_in, *p_out_end, *p_out;
777
778         p_in = p_pic->p[i_plane].p_pixels;
779         p_out = p_outpic->p[i_plane].p_pixels;
780         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
781                              * p_outpic->p[i_plane].i_visible_lines;
782
783         /* For BOTTOM field we need to add the first line */
784         if( i_field == 1 )
785         {
786             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
787             p_in += p_pic->p[i_plane].i_pitch;
788             p_out += p_outpic->p[i_plane].i_pitch;
789         }
790
791         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
792
793         for( ; p_out < p_out_end ; )
794         {
795             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
796
797             p_out += p_outpic->p[i_plane].i_pitch;
798
799             Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
800                    p_pic->p[i_plane].i_pitch );
801
802             p_in += 2 * p_pic->p[i_plane].i_pitch;
803             p_out += p_outpic->p[i_plane].i_pitch;
804         }
805
806         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
807
808         /* For TOP field we need to add the last line */
809         if( i_field == 0 )
810         {
811             p_in += p_pic->p[i_plane].i_pitch;
812             p_out += p_outpic->p[i_plane].i_pitch;
813             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
814         }
815     }
816     EndMerge();
817 }
818
819 static void RenderMean( vout_thread_t *p_vout,
820                         picture_t *p_outpic, picture_t *p_pic )
821 {
822     int i_plane;
823
824     /* Copy image and skip lines */
825     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
826     {
827         uint8_t *p_in, *p_out_end, *p_out;
828
829         p_in = p_pic->p[i_plane].p_pixels;
830
831         p_out = p_outpic->p[i_plane].p_pixels;
832         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
833                              * p_outpic->p[i_plane].i_visible_lines;
834
835         /* All lines: mean value */
836         for( ; p_out < p_out_end ; )
837         {
838             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
839                    p_pic->p[i_plane].i_pitch );
840
841             p_out += p_outpic->p[i_plane].i_pitch;
842             p_in += 2 * p_pic->p[i_plane].i_pitch;
843         }
844     }
845     EndMerge();
846 }
847
848 static void RenderBlend( vout_thread_t *p_vout,
849                          picture_t *p_outpic, picture_t *p_pic )
850 {
851     int i_plane;
852
853     /* Copy image and skip lines */
854     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
855     {
856         uint8_t *p_in, *p_out_end, *p_out;
857
858         p_in = p_pic->p[i_plane].p_pixels;
859
860         p_out = p_outpic->p[i_plane].p_pixels;
861         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
862                              * p_outpic->p[i_plane].i_visible_lines;
863
864         switch( p_vout->render.i_chroma )
865         {
866             case VLC_FOURCC('I','4','2','0'):
867             case VLC_FOURCC('I','Y','U','V'):
868             case VLC_FOURCC('Y','V','1','2'):
869                 /* First line: simple copy */
870                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
871                 p_out += p_outpic->p[i_plane].i_pitch;
872
873                 /* Remaining lines: mean value */
874                 for( ; p_out < p_out_end ; )
875                 {
876                     Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
877                            p_pic->p[i_plane].i_pitch );
878
879                     p_out += p_outpic->p[i_plane].i_pitch;
880                     p_in += p_pic->p[i_plane].i_pitch;
881                 }
882                 break;
883
884             case VLC_FOURCC('I','4','2','2'):
885                 /* First line: simple copy */
886                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
887                 p_out += p_outpic->p[i_plane].i_pitch;
888
889                 /* Remaining lines: mean value */
890                 if( i_plane == Y_PLANE )
891                 {
892                     for( ; p_out < p_out_end ; )
893                     {
894                         Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
895                                p_pic->p[i_plane].i_pitch );
896
897                         p_out += p_outpic->p[i_plane].i_pitch;
898                         p_in += p_pic->p[i_plane].i_pitch;
899                     }
900                 }
901
902                 else
903                 {
904                     for( ; p_out < p_out_end ; )
905                     {
906                         Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
907                                p_pic->p[i_plane].i_pitch );
908
909                         p_out += p_outpic->p[i_plane].i_pitch;
910                         p_in += 2*p_pic->p[i_plane].i_pitch;
911                     }
912                 }
913                 break;
914         }
915     }
916     EndMerge();
917 }
918
919 #undef Merge
920
921 static void MergeGeneric( void *_p_dest, const void *_p_s1,
922                           const void *_p_s2, size_t i_bytes )
923 {
924     uint8_t* p_dest = (uint8_t*)_p_dest;
925     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
926     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
927     uint8_t* p_end = p_dest + i_bytes - 8;
928
929     while( p_dest < p_end )
930     {
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         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
938         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
939     }
940
941     p_end += 8;
942
943     while( p_dest < p_end )
944     {
945         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
946     }
947 }
948
949 #if defined(CAN_COMPILE_MMXEXT)
950 static void MergeMMXEXT( void *_p_dest, const void *_p_s1, const void *_p_s2,
951                          size_t i_bytes )
952 {
953     uint8_t* p_dest = (uint8_t*)_p_dest;
954     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
955     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
956     uint8_t* p_end = p_dest + i_bytes - 8;
957     while( p_dest < p_end )
958     {
959         __asm__  __volatile__( "movq %2,%%mm1;"
960                                "pavgb %1, %%mm1;"
961                                "movq %%mm1, %0" :"=m" (*p_dest):
962                                                  "m" (*p_s1),
963                                                  "m" (*p_s2) );
964         p_dest += 8;
965         p_s1 += 8;
966         p_s2 += 8;
967     }
968
969     p_end += 8;
970
971     while( p_dest < p_end )
972     {
973         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
974     }
975 }
976 #endif
977
978 #if defined(CAN_COMPILE_3DNOW)
979 static void Merge3DNow( void *_p_dest, const void *_p_s1, const void *_p_s2,
980                         size_t i_bytes )
981 {
982     uint8_t* p_dest = (uint8_t*)_p_dest;
983     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
984     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
985     uint8_t* p_end = p_dest + i_bytes - 8;
986     while( p_dest < p_end )
987     {
988         __asm__  __volatile__( "movq %2,%%mm1;"
989                                "pavgusb %1, %%mm1;"
990                                "movq %%mm1, %0" :"=m" (*p_dest):
991                                                  "m" (*p_s1),
992                                                  "m" (*p_s2) );
993         p_dest += 8;
994         p_s1 += 8;
995         p_s2 += 8;
996     }
997
998     p_end += 8;
999
1000     while( p_dest < p_end )
1001     {
1002         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1003     }
1004 }
1005 #endif
1006
1007 #if defined(CAN_COMPILE_SSE)
1008 static void MergeSSE2( void *_p_dest, const void *_p_s1, const void *_p_s2,
1009                        size_t i_bytes )
1010 {
1011     uint8_t* p_dest = (uint8_t*)_p_dest;
1012     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
1013     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
1014     uint8_t* p_end;
1015     while( (uintptr_t)p_s1 % 16 )
1016     {
1017         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1018     }
1019     p_end = p_dest + i_bytes - 16;
1020     while( p_dest < p_end )
1021     {
1022         __asm__  __volatile__( "movdqu %2,%%xmm1;"
1023                                "pavgb %1, %%xmm1;"
1024                                "movdqu %%xmm1, %0" :"=m" (*p_dest):
1025                                                  "m" (*p_s1),
1026                                                  "m" (*p_s2) );
1027         p_dest += 16;
1028         p_s1 += 16;
1029         p_s2 += 16;
1030     }
1031
1032     p_end += 16;
1033
1034     while( p_dest < p_end )
1035     {
1036         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1037     }
1038 }
1039 #endif
1040
1041 #if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
1042 static void EndMMX( void )
1043 {
1044     __asm__ __volatile__( "emms" :: );
1045 }
1046 #endif
1047
1048 #if defined(CAN_COMPILE_3DNOW)
1049 static void End3DNow( void )
1050 {
1051     __asm__ __volatile__( "femms" :: );
1052 }
1053 #endif
1054
1055 #ifdef CAN_COMPILE_C_ALTIVEC
1056 static void MergeAltivec( void *_p_dest, const void *_p_s1,
1057                           const void *_p_s2, size_t i_bytes )
1058 {
1059     uint8_t *p_dest = (uint8_t *)_p_dest;
1060     uint8_t *p_s1   = (uint8_t *)_p_s1;
1061     uint8_t *p_s2   = (uint8_t *)_p_s2;
1062     uint8_t *p_end  = p_dest + i_bytes - 15;
1063
1064     /* Use C until the first 16-bytes aligned destination pixel */
1065     while( (int)p_dest & 0xF )
1066     {
1067         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1068     }
1069
1070     if( ( (int)p_s1 & 0xF ) | ( (int)p_s2 & 0xF ) )
1071     {
1072         /* Unaligned source */
1073         vector unsigned char s1v, s2v, destv;
1074         vector unsigned char s1oldv, s2oldv, s1newv, s2newv;
1075         vector unsigned char perm1v, perm2v;
1076
1077         perm1v = vec_lvsl( 0, p_s1 );
1078         perm2v = vec_lvsl( 0, p_s2 );
1079         s1oldv = vec_ld( 0, p_s1 );
1080         s2oldv = vec_ld( 0, p_s2 );
1081
1082         while( p_dest < p_end )
1083         {
1084             s1newv = vec_ld( 16, p_s1 );
1085             s2newv = vec_ld( 16, p_s2 );
1086             s1v    = vec_perm( s1oldv, s1newv, perm1v );
1087             s2v    = vec_perm( s2oldv, s2newv, perm2v );
1088             s1oldv = s1newv;
1089             s2oldv = s2newv;
1090             destv  = vec_avg( s1v, s2v );
1091             vec_st( destv, 0, p_dest );
1092
1093             p_s1   += 16;
1094             p_s2   += 16;
1095             p_dest += 16;
1096         }
1097     }
1098     else
1099     {
1100         /* Aligned source */
1101         vector unsigned char s1v, s2v, destv;
1102
1103         while( p_dest < p_end )
1104         {
1105             s1v   = vec_ld( 0, p_s1 );
1106             s2v   = vec_ld( 0, p_s2 );
1107             destv = vec_avg( s1v, s2v );
1108             vec_st( destv, 0, p_dest );
1109
1110             p_s1   += 16;
1111             p_s2   += 16;
1112             p_dest += 16;
1113         }
1114     }
1115
1116     p_end += 15;
1117
1118     while( p_dest < p_end )
1119     {
1120         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1121     }
1122 }
1123 #endif
1124
1125 /*****************************************************************************
1126  * RenderX: This algo works on a 8x8 block basic, it copies the top field
1127  * and apply a process to recreate the bottom field :
1128  *  If a 8x8 block is classified as :
1129  *   - progressive: it applies a small blend (1,6,1)
1130  *   - interlaced:
1131  *    * in the MMX version: we do a ME between the 2 fields, if there is a
1132  *    good match we use MC to recreate the bottom field (with a small
1133  *    blend (1,6,1) )
1134  *    * otherwise: it recreates the bottom field by an edge oriented
1135  *    interpolation.
1136   *****************************************************************************/
1137
1138 /* XDeint8x8Detect: detect if a 8x8 block is interlaced.
1139  * XXX: It need to access to 8x10
1140  * We use more than 8 lines to help with scrolling (text)
1141  * (and because XDeint8x8Frame use line 9)
1142  * XXX: smooth/uniform area with noise detection doesn't works well
1143  * but it's not really a problem because they don't have much details anyway
1144  */
1145 static inline int ssd( int a ) { return a*a; }
1146 static inline int XDeint8x8DetectC( uint8_t *src, int i_src )
1147 {
1148     int y, x;
1149     int ff, fr;
1150     int fc;
1151
1152     /* Detect interlacing */
1153     fc = 0;
1154     for( y = 0; y < 7; y += 2 )
1155     {
1156         ff = fr = 0;
1157         for( x = 0; x < 8; x++ )
1158         {
1159             fr += ssd(src[      x] - src[1*i_src+x]) +
1160                   ssd(src[i_src+x] - src[2*i_src+x]);
1161             ff += ssd(src[      x] - src[2*i_src+x]) +
1162                   ssd(src[i_src+x] - src[3*i_src+x]);
1163         }
1164         if( ff < 6*fr/8 && fr > 32 )
1165             fc++;
1166
1167         src += 2*i_src;
1168     }
1169
1170     return fc < 1 ? false : true;
1171 }
1172 #ifdef CAN_COMPILE_MMXEXT
1173 static inline int XDeint8x8DetectMMXEXT( uint8_t *src, int i_src )
1174 {
1175
1176     int y, x;
1177     int32_t ff, fr;
1178     int fc;
1179
1180     /* Detect interlacing */
1181     fc = 0;
1182     pxor_r2r( mm7, mm7 );
1183     for( y = 0; y < 9; y += 2 )
1184     {
1185         ff = fr = 0;
1186         pxor_r2r( mm5, mm5 );
1187         pxor_r2r( mm6, mm6 );
1188         for( x = 0; x < 8; x+=4 )
1189         {
1190             movd_m2r( src[        x], mm0 );
1191             movd_m2r( src[1*i_src+x], mm1 );
1192             movd_m2r( src[2*i_src+x], mm2 );
1193             movd_m2r( src[3*i_src+x], mm3 );
1194
1195             punpcklbw_r2r( mm7, mm0 );
1196             punpcklbw_r2r( mm7, mm1 );
1197             punpcklbw_r2r( mm7, mm2 );
1198             punpcklbw_r2r( mm7, mm3 );
1199
1200             movq_r2r( mm0, mm4 );
1201
1202             psubw_r2r( mm1, mm0 );
1203             psubw_r2r( mm2, mm4 );
1204
1205             psubw_r2r( mm1, mm2 );
1206             psubw_r2r( mm1, mm3 );
1207
1208             pmaddwd_r2r( mm0, mm0 );
1209             pmaddwd_r2r( mm4, mm4 );
1210             pmaddwd_r2r( mm2, mm2 );
1211             pmaddwd_r2r( mm3, mm3 );
1212             paddd_r2r( mm0, mm2 );
1213             paddd_r2r( mm4, mm3 );
1214             paddd_r2r( mm2, mm5 );
1215             paddd_r2r( mm3, mm6 );
1216         }
1217
1218         movq_r2r( mm5, mm0 );
1219         psrlq_i2r( 32, mm0 );
1220         paddd_r2r( mm0, mm5 );
1221         movd_r2m( mm5, fr );
1222
1223         movq_r2r( mm6, mm0 );
1224         psrlq_i2r( 32, mm0 );
1225         paddd_r2r( mm0, mm6 );
1226         movd_r2m( mm6, ff );
1227
1228         if( ff < 6*fr/8 && fr > 32 )
1229             fc++;
1230
1231         src += 2*i_src;
1232     }
1233     return fc;
1234 }
1235 #endif
1236
1237 static inline void XDeint8x8MergeC( uint8_t *dst, int i_dst,
1238                                     uint8_t *src1, int i_src1,
1239                                     uint8_t *src2, int i_src2 )
1240 {
1241     int y, x;
1242
1243     /* Progressive */
1244     for( y = 0; y < 8; y += 2 )
1245     {
1246         memcpy( dst, src1, 8 );
1247         dst  += i_dst;
1248
1249         for( x = 0; x < 8; x++ )
1250             dst[x] = (src1[x] + 6*src2[x] + src1[i_src1+x] + 4 ) >> 3;
1251         dst += i_dst;
1252
1253         src1 += i_src1;
1254         src2 += i_src2;
1255     }
1256 }
1257
1258 #ifdef CAN_COMPILE_MMXEXT
1259 static inline void XDeint8x8MergeMMXEXT( uint8_t *dst, int i_dst,
1260                                          uint8_t *src1, int i_src1,
1261                                          uint8_t *src2, int i_src2 )
1262 {
1263     static const uint64_t m_4 = INT64_C(0x0004000400040004);
1264     int y, x;
1265
1266     /* Progressive */
1267     pxor_r2r( mm7, mm7 );
1268     for( y = 0; y < 8; y += 2 )
1269     {
1270         for( x = 0; x < 8; x +=4 )
1271         {
1272             movd_m2r( src1[x], mm0 );
1273             movd_r2m( mm0, dst[x] );
1274
1275             movd_m2r( src2[x], mm1 );
1276             movd_m2r( src1[i_src1+x], mm2 );
1277
1278             punpcklbw_r2r( mm7, mm0 );
1279             punpcklbw_r2r( mm7, mm1 );
1280             punpcklbw_r2r( mm7, mm2 );
1281             paddw_r2r( mm1, mm1 );
1282             movq_r2r( mm1, mm3 );
1283             paddw_r2r( mm3, mm3 );
1284             paddw_r2r( mm2, mm0 );
1285             paddw_r2r( mm3, mm1 );
1286             paddw_m2r( m_4, mm1 );
1287             paddw_r2r( mm1, mm0 );
1288             psraw_i2r( 3, mm0 );
1289             packuswb_r2r( mm7, mm0 );
1290             movd_r2m( mm0, dst[i_dst+x] );
1291         }
1292         dst += 2*i_dst;
1293         src1 += i_src1;
1294         src2 += i_src2;
1295     }
1296 }
1297
1298 #endif
1299
1300 /* For debug */
1301 static inline void XDeint8x8Set( uint8_t *dst, int i_dst, uint8_t v )
1302 {
1303     int y;
1304     for( y = 0; y < 8; y++ )
1305         memset( &dst[y*i_dst], v, 8 );
1306 }
1307
1308 /* XDeint8x8FieldE: Stupid deinterlacing (1,0,1) for block that miss a
1309  * neighbour
1310  * (Use 8x9 pixels)
1311  * TODO: a better one for the inner part.
1312  */
1313 static inline void XDeint8x8FieldEC( uint8_t *dst, int i_dst,
1314                                      uint8_t *src, int i_src )
1315 {
1316     int y, x;
1317
1318     /* Interlaced */
1319     for( y = 0; y < 8; y += 2 )
1320     {
1321         memcpy( dst, src, 8 );
1322         dst += i_dst;
1323
1324         for( x = 0; x < 8; x++ )
1325             dst[x] = (src[x] + src[2*i_src+x] ) >> 1;
1326         dst += 1*i_dst;
1327         src += 2*i_src;
1328     }
1329 }
1330 #ifdef CAN_COMPILE_MMXEXT
1331 static inline void XDeint8x8FieldEMMXEXT( uint8_t *dst, int i_dst,
1332                                           uint8_t *src, int i_src )
1333 {
1334     int y;
1335
1336     /* Interlaced */
1337     for( y = 0; y < 8; y += 2 )
1338     {
1339         movq_m2r( src[0], mm0 );
1340         movq_r2m( mm0, dst[0] );
1341         dst += i_dst;
1342
1343         movq_m2r( src[2*i_src], mm1 );
1344         pavgb_r2r( mm1, mm0 );
1345
1346         movq_r2m( mm0, dst[0] );
1347
1348         dst += 1*i_dst;
1349         src += 2*i_src;
1350     }
1351 }
1352 #endif
1353
1354 /* XDeint8x8Field: Edge oriented interpolation
1355  * (Need -4 and +5 pixels H, +1 line)
1356  */
1357 static inline void XDeint8x8FieldC( uint8_t *dst, int i_dst,
1358                                     uint8_t *src, int i_src )
1359 {
1360     int y, x;
1361
1362     /* Interlaced */
1363     for( y = 0; y < 8; y += 2 )
1364     {
1365         memcpy( dst, src, 8 );
1366         dst += i_dst;
1367
1368         for( x = 0; x < 8; x++ )
1369         {
1370             uint8_t *src2 = &src[2*i_src];
1371             /* I use 8 pixels just to match the MMX version, but it's overkill
1372              * 5 would be enough (less isn't good) */
1373             const int c0 = abs(src[x-4]-src2[x-2]) + abs(src[x-3]-src2[x-1]) +
1374                            abs(src[x-2]-src2[x+0]) + abs(src[x-1]-src2[x+1]) +
1375                            abs(src[x+0]-src2[x+2]) + abs(src[x+1]-src2[x+3]) +
1376                            abs(src[x+2]-src2[x+4]) + abs(src[x+3]-src2[x+5]);
1377
1378             const int c1 = abs(src[x-3]-src2[x-3]) + abs(src[x-2]-src2[x-2]) +
1379                            abs(src[x-1]-src2[x-1]) + abs(src[x+0]-src2[x+0]) +
1380                            abs(src[x+1]-src2[x+1]) + abs(src[x+2]-src2[x+2]) +
1381                            abs(src[x+3]-src2[x+3]) + abs(src[x+4]-src2[x+4]);
1382
1383             const int c2 = abs(src[x-2]-src2[x-4]) + abs(src[x-1]-src2[x-3]) +
1384                            abs(src[x+0]-src2[x-2]) + abs(src[x+1]-src2[x-1]) +
1385                            abs(src[x+2]-src2[x+0]) + abs(src[x+3]-src2[x+1]) +
1386                            abs(src[x+4]-src2[x+2]) + abs(src[x+5]-src2[x+3]);
1387
1388             if( c0 < c1 && c1 <= c2 )
1389                 dst[x] = (src[x-1] + src2[x+1]) >> 1;
1390             else if( c2 < c1 && c1 <= c0 )
1391                 dst[x] = (src[x+1] + src2[x-1]) >> 1;
1392             else
1393                 dst[x] = (src[x+0] + src2[x+0]) >> 1;
1394         }
1395
1396         dst += 1*i_dst;
1397         src += 2*i_src;
1398     }
1399 }
1400 #ifdef CAN_COMPILE_MMXEXT
1401 static inline void XDeint8x8FieldMMXEXT( uint8_t *dst, int i_dst,
1402                                          uint8_t *src, int i_src )
1403 {
1404     int y, x;
1405
1406     /* Interlaced */
1407     for( y = 0; y < 8; y += 2 )
1408     {
1409         memcpy( dst, src, 8 );
1410         dst += i_dst;
1411
1412         for( x = 0; x < 8; x++ )
1413         {
1414             uint8_t *src2 = &src[2*i_src];
1415             int32_t c0, c1, c2;
1416
1417             movq_m2r( src[x-2], mm0 );
1418             movq_m2r( src[x-3], mm1 );
1419             movq_m2r( src[x-4], mm2 );
1420
1421             psadbw_m2r( src2[x-4], mm0 );
1422             psadbw_m2r( src2[x-3], mm1 );
1423             psadbw_m2r( src2[x-2], mm2 );
1424
1425             movd_r2m( mm0, c2 );
1426             movd_r2m( mm1, c1 );
1427             movd_r2m( mm2, c0 );
1428
1429             if( c0 < c1 && c1 <= c2 )
1430                 dst[x] = (src[x-1] + src2[x+1]) >> 1;
1431             else if( c2 < c1 && c1 <= c0 )
1432                 dst[x] = (src[x+1] + src2[x-1]) >> 1;
1433             else
1434                 dst[x] = (src[x+0] + src2[x+0]) >> 1;
1435         }
1436
1437         dst += 1*i_dst;
1438         src += 2*i_src;
1439     }
1440 }
1441 #endif
1442
1443 /* NxN arbitray size (and then only use pixel in the NxN block)
1444  */
1445 static inline int XDeintNxNDetect( uint8_t *src, int i_src,
1446                                    int i_height, int i_width )
1447 {
1448     int y, x;
1449     int ff, fr;
1450     int fc;
1451
1452
1453     /* Detect interlacing */
1454     /* FIXME way too simple, need to be more like XDeint8x8Detect */
1455     ff = fr = 0;
1456     fc = 0;
1457     for( y = 0; y < i_height - 2; y += 2 )
1458     {
1459         const uint8_t *s = &src[y*i_src];
1460         for( x = 0; x < i_width; x++ )
1461         {
1462             fr += ssd(s[      x] - s[1*i_src+x]);
1463             ff += ssd(s[      x] - s[2*i_src+x]);
1464         }
1465         if( ff < fr && fr > i_width / 2 )
1466             fc++;
1467     }
1468
1469     return fc < 2 ? false : true;
1470 }
1471
1472 static inline void XDeintNxNFrame( uint8_t *dst, int i_dst,
1473                                    uint8_t *src, int i_src,
1474                                    int i_width, int i_height )
1475 {
1476     int y, x;
1477
1478     /* Progressive */
1479     for( y = 0; y < i_height; y += 2 )
1480     {
1481         memcpy( dst, src, i_width );
1482         dst += i_dst;
1483
1484         if( y < i_height - 2 )
1485         {
1486             for( x = 0; x < i_width; x++ )
1487                 dst[x] = (src[x] + 2*src[1*i_src+x] + src[2*i_src+x] + 2 ) >> 2;
1488         }
1489         else
1490         {
1491             /* Blend last line */
1492             for( x = 0; x < i_width; x++ )
1493                 dst[x] = (src[x] + src[1*i_src+x] ) >> 1;
1494         }
1495         dst += 1*i_dst;
1496         src += 2*i_src;
1497     }
1498 }
1499
1500 static inline void XDeintNxNField( uint8_t *dst, int i_dst,
1501                                    uint8_t *src, int i_src,
1502                                    int i_width, int i_height )
1503 {
1504     int y, x;
1505
1506     /* Interlaced */
1507     for( y = 0; y < i_height; y += 2 )
1508     {
1509         memcpy( dst, src, i_width );
1510         dst += i_dst;
1511
1512         if( y < i_height - 2 )
1513         {
1514             for( x = 0; x < i_width; x++ )
1515                 dst[x] = (src[x] + src[2*i_src+x] ) >> 1;
1516         }
1517         else
1518         {
1519             /* Blend last line */
1520             for( x = 0; x < i_width; x++ )
1521                 dst[x] = (src[x] + src[i_src+x]) >> 1;
1522         }
1523         dst += 1*i_dst;
1524         src += 2*i_src;
1525     }
1526 }
1527
1528 static inline void XDeintNxN( uint8_t *dst, int i_dst, uint8_t *src, int i_src,
1529                               int i_width, int i_height )
1530 {
1531     if( XDeintNxNDetect( src, i_src, i_width, i_height ) )
1532         XDeintNxNField( dst, i_dst, src, i_src, i_width, i_height );
1533     else
1534         XDeintNxNFrame( dst, i_dst, src, i_src, i_width, i_height );
1535 }
1536
1537
1538 static inline int median( int a, int b, int c )
1539 {
1540     int min = a, max =a;
1541     if( b < min )
1542         min = b;
1543     else
1544         max = b;
1545
1546     if( c < min )
1547         min = c;
1548     else if( c > max )
1549         max = c;
1550
1551     return a + b + c - min - max;
1552 }
1553
1554
1555 /* XDeintBand8x8:
1556  */
1557 static inline void XDeintBand8x8C( uint8_t *dst, int i_dst,
1558                                    uint8_t *src, int i_src,
1559                                    const int i_mbx, int i_modx )
1560 {
1561     int x;
1562
1563     for( x = 0; x < i_mbx; x++ )
1564     {
1565         int s;
1566         if( ( s = XDeint8x8DetectC( src, i_src ) ) )
1567         {
1568             if( x == 0 || x == i_mbx - 1 )
1569                 XDeint8x8FieldEC( dst, i_dst, src, i_src );
1570             else
1571                 XDeint8x8FieldC( dst, i_dst, src, i_src );
1572         }
1573         else
1574         {
1575             XDeint8x8MergeC( dst, i_dst,
1576                              &src[0*i_src], 2*i_src,
1577                              &src[1*i_src], 2*i_src );
1578         }
1579
1580         dst += 8;
1581         src += 8;
1582     }
1583
1584     if( i_modx )
1585         XDeintNxN( dst, i_dst, src, i_src, i_modx, 8 );
1586 }
1587 #ifdef CAN_COMPILE_MMXEXT
1588 static inline void XDeintBand8x8MMXEXT( uint8_t *dst, int i_dst,
1589                                         uint8_t *src, int i_src,
1590                                         const int i_mbx, int i_modx )
1591 {
1592     int x;
1593
1594     /* Reset current line */
1595     for( x = 0; x < i_mbx; x++ )
1596     {
1597         int s;
1598         if( ( s = XDeint8x8DetectMMXEXT( src, i_src ) ) )
1599         {
1600             if( x == 0 || x == i_mbx - 1 )
1601                 XDeint8x8FieldEMMXEXT( dst, i_dst, src, i_src );
1602             else
1603                 XDeint8x8FieldMMXEXT( dst, i_dst, src, i_src );
1604         }
1605         else
1606         {
1607             XDeint8x8MergeMMXEXT( dst, i_dst,
1608                                   &src[0*i_src], 2*i_src,
1609                                   &src[1*i_src], 2*i_src );
1610         }
1611
1612         dst += 8;
1613         src += 8;
1614     }
1615
1616     if( i_modx )
1617         XDeintNxN( dst, i_dst, src, i_src, i_modx, 8 );
1618 }
1619 #endif
1620
1621 static void RenderX( picture_t *p_outpic, picture_t *p_pic )
1622 {
1623     int i_plane;
1624
1625     /* Copy image and skip lines */
1626     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
1627     {
1628         const int i_mby = ( p_outpic->p[i_plane].i_visible_lines + 7 )/8 - 1;
1629         const int i_mbx = p_outpic->p[i_plane].i_visible_pitch/8;
1630
1631         const int i_mody = p_outpic->p[i_plane].i_visible_lines - 8*i_mby;
1632         const int i_modx = p_outpic->p[i_plane].i_visible_pitch - 8*i_mbx;
1633
1634         const int i_dst = p_outpic->p[i_plane].i_pitch;
1635         const int i_src = p_pic->p[i_plane].i_pitch;
1636
1637         int y, x;
1638
1639         for( y = 0; y < i_mby; y++ )
1640         {
1641             uint8_t *dst = &p_outpic->p[i_plane].p_pixels[8*y*i_dst];
1642             uint8_t *src = &p_pic->p[i_plane].p_pixels[8*y*i_src];
1643
1644 #ifdef CAN_COMPILE_MMXEXT
1645             if( vlc_CPU() & CPU_CAPABILITY_MMXEXT )
1646                 XDeintBand8x8MMXEXT( dst, i_dst, src, i_src, i_mbx, i_modx );
1647             else
1648 #endif
1649                 XDeintBand8x8C( dst, i_dst, src, i_src, i_mbx, i_modx );
1650         }
1651
1652         /* Last line (C only)*/
1653         if( i_mody )
1654         {
1655             uint8_t *dst = &p_outpic->p[i_plane].p_pixels[8*y*i_dst];
1656             uint8_t *src = &p_pic->p[i_plane].p_pixels[8*y*i_src];
1657
1658             for( x = 0; x < i_mbx; x++ )
1659             {
1660                 XDeintNxN( dst, i_dst, src, i_src, 8, i_mody );
1661
1662                 dst += 8;
1663                 src += 8;
1664             }
1665
1666             if( i_modx )
1667                 XDeintNxN( dst, i_dst, src, i_src, i_modx, i_mody );
1668         }
1669     }
1670
1671 #ifdef CAN_COMPILE_MMXEXT
1672     if( vlc_CPU() & CPU_CAPABILITY_MMXEXT )
1673         emms();
1674 #endif
1675 }
1676
1677 /*****************************************************************************
1678  * FilterCallback: called when changing the deinterlace method on the fly.
1679  *****************************************************************************/
1680 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1681                            vlc_value_t oldval, vlc_value_t newval,
1682                            void *p_data )
1683 {
1684     VLC_UNUSED(psz_cmd); VLC_UNUSED(p_data); VLC_UNUSED(oldval);
1685     vout_thread_t * p_vout = (vout_thread_t *)p_this;
1686
1687     msg_Dbg( p_vout, "using %s deinterlace mode", newval.psz_string );
1688
1689     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
1690     const bool b_old_half_height = p_vout->p_sys->b_half_height;
1691
1692     SetFilterMethod( p_vout, newval.psz_string );
1693
1694     if( !b_old_half_height == !p_vout->p_sys->b_half_height )
1695     {
1696         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1697         return VLC_SUCCESS;
1698     }
1699
1700     /* We need to kill the old vout */
1701     if( p_vout->p_sys->p_vout )
1702     {
1703         vout_filter_DelChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
1704         vout_CloseAndRelease( p_vout->p_sys->p_vout );
1705     }
1706
1707     /* Try to open a new video output */
1708     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
1709
1710     if( p_vout->p_sys->p_vout == NULL )
1711     {
1712         /* Everything failed */
1713         msg_Err( p_vout, "cannot open vout, aborting" );
1714
1715         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1716         return VLC_EGENERIC;
1717     }
1718
1719     vout_filter_AddChild( p_vout, p_vout->p_sys->p_vout, MouseEvent );
1720
1721     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1722     return VLC_SUCCESS;
1723 }
1724
1725 /*****************************************************************************
1726  * video filter2 functions
1727  *****************************************************************************/
1728 static picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
1729 {
1730     vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_sys;
1731     picture_t *p_pic_dst;
1732
1733     /* Request output picture */
1734     p_pic_dst = filter_NewPicture( p_filter );
1735     if( p_pic_dst == NULL )
1736     {
1737         picture_Release( p_pic );
1738         return NULL;
1739     }
1740
1741     switch( p_vout->p_sys->i_mode )
1742     {
1743         case DEINTERLACE_DISCARD:
1744             RenderDiscard( p_vout, p_pic_dst, p_pic, 0 );
1745             break;
1746
1747         case DEINTERLACE_BOB:
1748 #if 0
1749             RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
1750             RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
1751             break;
1752 #endif
1753
1754         case DEINTERLACE_LINEAR:
1755 #if 0
1756             RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
1757             RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
1758 #endif
1759             msg_Err( p_vout, "doubling the frame rate is not supported yet" );
1760             picture_Release( p_pic_dst );
1761             picture_Release( p_pic );
1762             return NULL;
1763
1764         case DEINTERLACE_MEAN:
1765             RenderMean( p_vout, p_pic_dst, p_pic );
1766             break;
1767
1768         case DEINTERLACE_BLEND:
1769             RenderBlend( p_vout, p_pic_dst, p_pic );
1770             break;
1771
1772         case DEINTERLACE_X:
1773             RenderX( p_pic_dst, p_pic );
1774             break;
1775     }
1776
1777     picture_CopyProperties( p_pic_dst, p_pic );
1778     p_pic_dst->b_progressive = true;
1779
1780     picture_Release( p_pic );
1781     return p_pic_dst;
1782 }
1783
1784 /*****************************************************************************
1785  * OpenFilter:
1786  *****************************************************************************/
1787 static int OpenFilter( vlc_object_t *p_this )
1788 {
1789     filter_t *p_filter = (filter_t*)p_this;
1790     vout_thread_t *p_vout;
1791     vlc_value_t val;
1792
1793     if( !IsChromaSupported( p_filter->fmt_in.video.i_chroma ) )
1794         return VLC_EGENERIC;
1795
1796     /* Impossible to use VLC_OBJECT_VOUT here because it would be used
1797      * by spu filters */
1798     p_vout = vlc_object_create( p_filter, sizeof(vout_thread_t) );
1799     vlc_object_attach( p_vout, p_filter );
1800     p_filter->p_sys = (filter_sys_t *)p_vout;
1801     p_vout->render.i_chroma = p_filter->fmt_in.video.i_chroma;
1802
1803     config_ChainParse( p_filter, FILTER_CFG_PREFIX, ppsz_filter_options,
1804                    p_filter->p_cfg );
1805     var_Get( p_filter, FILTER_CFG_PREFIX "mode", &val );
1806
1807     var_Create( p_filter, "deinterlace-mode", VLC_VAR_STRING );
1808     var_Set( p_filter, "deinterlace-mode", val );
1809     free( val.psz_string );
1810
1811     if( Create( VLC_OBJECT(p_vout) ) != VLC_SUCCESS )
1812     {
1813         vlc_object_detach( p_vout );
1814         vlc_object_release( p_vout );
1815         return VLC_EGENERIC;
1816     }
1817
1818     video_format_t fmt;
1819     GetOutputFormat( p_vout, &fmt, &p_filter->fmt_in.video );
1820     if( !p_filter->b_allow_fmt_out_change &&
1821         ( fmt.i_chroma != p_filter->fmt_in.video.i_chroma ||
1822           fmt.i_height != p_filter->fmt_in.video.i_height ) )
1823     {
1824         CloseFilter( VLC_OBJECT(p_filter) );
1825         return VLC_EGENERIC;
1826     }
1827     p_filter->fmt_out.video = fmt;
1828     p_filter->fmt_out.i_codec = fmt.i_chroma;
1829     p_filter->pf_video_filter = Deinterlace;
1830
1831     msg_Dbg( p_filter, "deinterlacing" );
1832
1833     return VLC_SUCCESS;
1834 }
1835
1836 /*****************************************************************************
1837  * CloseFilter: clean up the filter
1838  *****************************************************************************/
1839 static void CloseFilter( vlc_object_t *p_this )
1840 {
1841     filter_t *p_filter = (filter_t*)p_this;
1842     vout_thread_t *p_vout = (vout_thread_t *)p_filter->p_sys;
1843
1844     Destroy( VLC_OBJECT(p_vout) );
1845     vlc_object_detach( p_vout );
1846     vlc_object_release( p_vout );
1847 }
1848