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