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