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