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