root/modules/video_filter/deinterlace.c

Revision 18d635e38d62ea7afe5caa7b1c4d1dcc52ee9aa2, 66.1 kB (checked in by Rémi Duraffort <ivoire@videolan.org>, 2 weeks ago)

Fix memleak.

  • Property mode set to 100644
Line 
1 /*****************************************************************************
2  * deinterlace.c : deinterlacer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001, 2002, 2003 the VideoLAN team
5  * $Id$
6  *
7  * Author: Sam Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <errno.h>
33
34 #ifdef HAVE_ALTIVEC_H
35 #   include <altivec.h>
36 #endif
37
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_vout.h>
41 #include <vlc_sout.h>
42 #include "vlc_filter.h"
43
44 #ifdef CAN_COMPILE_MMXEXT
45 #   include "mmx.h"
46 #endif
47
48 #include "filter_common.h"
49
50 #define DEINTERLACE_DISCARD 1
51 #define DEINTERLACE_MEAN    2
52 #define DEINTERLACE_BLEND   3
53 #define DEINTERLACE_BOB     4
54 #define DEINTERLACE_LINEAR  5
55 #define DEINTERLACE_X       6
56
57 /*****************************************************************************
58  * Local protypes
59  *****************************************************************************/
60 static int  Create    ( vlc_object_t * );
61 static void Destroy   ( vlc_object_t * );
62
63 static int  Init      ( vout_thread_t * );
64 static void End       ( vout_thread_t * );
65 static void Render    ( vout_thread_t *, picture_t * );
66
67 static void RenderDiscard( vout_thread_t *, picture_t *, picture_t *, int );
68 static void RenderBob    ( vout_thread_t *, picture_t *, picture_t *, int );
69 static void RenderMean   ( vout_thread_t *, picture_t *, picture_t * );
70 static void RenderBlend  ( vout_thread_t *, picture_t *, picture_t * );
71 static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
72 static void RenderX      ( picture_t *, picture_t * );
73
74 static void MergeGeneric ( void *, const void *, const void *, size_t );
75 #if defined(CAN_COMPILE_C_ALTIVEC)
76 static void MergeAltivec ( void *, const void *, const void *, size_t );
77 #endif
78 #if defined(CAN_COMPILE_MMXEXT)
79 static void MergeMMXEXT  ( void *, const void *, const void *, size_t );
80 #endif
81 #if defined(CAN_COMPILE_3DNOW)
82 static void Merge3DNow   ( void *, const void *, const void *, size_t );
83 #endif
84 #if defined(CAN_COMPILE_SSE)
85 static void MergeSSE2    ( void *, const void *, const void *, size_t );
86 #endif
87 #if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
88 static void EndMMX       ( void );
89 #endif
90 #if defined(CAN_COMPILE_3DNOW)
91 static void End3DNow     ( void );
92 #endif
93
94 static int  SendEvents   ( vlc_object_t *, char const *,
95                            vlc_value_t, vlc_value_t, void * );
96
97 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method );
98 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout );
99
100 static int OpenFilter( vlc_object_t *p_this );
101 static void CloseFilter( vlc_object_t *p_this );
102
103 /*****************************************************************************
104  * Callback prototypes
105  *****************************************************************************/
106 static int FilterCallback ( vlc_object_t *, char const *,
107                             vlc_value_t, vlc_value_t, void * );
108
109 /*****************************************************************************
110  * Module descriptor
111  *****************************************************************************/
112 #define MODE_TEXT N_("Deinterlace mode")
113 #define MODE_LONGTEXT N_("Deinterlace method to use for local playback.")
114
115 #define SOUT_MODE_TEXT N_("Streaming deinterlace mode")
116 #define SOUT_MODE_LONGTEXT N_("Deinterlace method to use for streaming.")
117
118 #define FILTER_CFG_PREFIX "sout-deinterlace-"
119
120 static const char *const mode_list[] = {
121     "discard", "blend", "mean", "bob", "linear", "x" };
122 static const char *const mode_list_text[] = {
123     N_("Discard"), N_("Blend"), N_("Mean"), N_("Bob"), N_("Linear"), "X" };
124
125 vlc_module_begin();
126     set_description( N_("Deinterlacing video filter") );
127     set_shortname( N_("Deinterlace" ));
128     set_capability( "video filter", 0 );
129     set_category( CAT_VIDEO );
130     set_subcategory( SUBCAT_VIDEO_VFILTER );
131
132     set_section( N_("Display"),NULL);
133     add_string( "deinterlace-mode", "discard", NULL, MODE_TEXT,
134                 MODE_LONGTEXT, false );
135         change_string_list( mode_list, mode_list_text, 0 );
136
137     add_shortcut( "deinterlace" );
138     set_callbacks( Create, Destroy );
139
140     add_submodule();
141     set_capability( "video filter2", 0 );
142     set_section( N_("Streaming"),NULL);
143     add_string( FILTER_CFG_PREFIX "mode", "blend", NULL, SOUT_MODE_TEXT,
144                 SOUT_MODE_LONGTEXT, false );
145         change_string_list( mode_list, mode_list_text, 0 );
146     set_callbacks( OpenFilter, CloseFilter );
147 vlc_module_end();
148
149 static const char *const ppsz_filter_options[] = {
150     "mode", NULL
151 };
152
153 /*****************************************************************************
154  * vout_sys_t: Deinterlace video output method descriptor
155  *****************************************************************************
156  * This structure is part of the video output thread descriptor.
157  * It describes the Deinterlace specific properties of an output thread.
158  *****************************************************************************/
159 struct vout_sys_t
160 {
161     int        i_mode;        /* Deinterlace mode */
162     bool b_double_rate; /* Shall we double the framerate? */
163
164     mtime_t    last_date;
165     mtime_t    next_date;
166
167     vout_thread_t *p_vout;
168
169     vlc_mutex_t filter_lock;
170
171     void (*pf_merge) ( void *, const void *, const void *, size_t );
172     void (*pf_end_merge) ( void );
173 };
174
175 /*****************************************************************************
176  * Control: control facility for the vout (forwards to child vout)
177  *****************************************************************************/
178 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
179 {
180     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
181 }
182
183 /*****************************************************************************
184  * Create: allocates Deinterlace video thread output method
185  *****************************************************************************
186  * This function allocates and initializes a Deinterlace vout method.
187  *****************************************************************************/
188 static int Create( vlc_object_t *p_this )
189 {
190     vout_thread_t *p_vout = (vout_thread_t *)p_this;
191     vlc_value_t val;
192
193     /* Allocate structure */
194     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
195     if( p_vout->p_sys == NULL )
196         return VLC_ENOMEM;
197
198     p_vout->pf_init = Init;
199     p_vout->pf_end = End;
200     p_vout->pf_manage = NULL;
201     p_vout->pf_render = Render;
202     p_vout->pf_display = NULL;
203     p_vout->pf_control = Control;
204
205     p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
206     p_vout->p_sys->b_double_rate = false;
207     p_vout->p_sys->last_date = 0;
208     p_vout->p_sys->p_vout = 0;
209     vlc_mutex_init( &p_vout->p_sys->filter_lock );
210
211 #if defined(CAN_COMPILE_C_ALTIVEC)
212     if( vlc_CPU() & CPU_CAPABILITY_ALTIVEC )
213     {
214         p_vout->p_sys->pf_merge = MergeAltivec;
215         p_vout->p_sys->pf_end_merge = NULL;
216     }
217     else
218 #endif
219 #if defined(CAN_COMPILE_SSE)
220     if( vlc_CPU() & CPU_CAPABILITY_SSE2 )
221     {
222         p_vout->p_sys->pf_merge = MergeSSE2;
223         p_vout->p_sys->pf_end_merge = EndMMX;
224     }
225     else
226 #endif
227 #if defined(CAN_COMPILE_MMXEXT)
228     if( vlc_CPU() & CPU_CAPABILITY_MMXEXT )
229     {
230         p_vout->p_sys->pf_merge = MergeMMXEXT;
231         p_vout->p_sys->pf_end_merge = EndMMX;
232     }
233     else
234 #endif
235 #if defined(CAN_COMPILE_3DNOW)
236     if( vlc_CPU() & CPU_CAPABILITY_3DNOW )
237     {
238         p_vout->p_sys->pf_merge = Merge3DNow;
239         p_vout->p_sys->pf_end_merge = End3DNow;
240     }
241     else
242 #endif
243     {
244         p_vout->p_sys->pf_merge = MergeGeneric;
245         p_vout->p_sys->pf_end_merge = NULL;
246     }
247
248     /* Look what method was requested */
249     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING );
250     var_Change( p_vout, "deinterlace-mode", VLC_VAR_INHERITVALUE, &val, NULL );
251
252     if( val.psz_string == NULL )
253     {
254         msg_Err( p_vout, "configuration variable deinterlace-mode empty" );
255         msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
256
257         val.psz_string = strdup( "discard" );
258     }
259
260     msg_Dbg( p_vout, "using %s deinterlace mode", val.psz_string );
261
262     SetFilterMethod( p_vout, val.psz_string );
263
264     free( val.psz_string );
265
266     return VLC_SUCCESS;
267 }
268
269 /*****************************************************************************
270  * SetFilterMethod: setup the deinterlace method to use.
271  *****************************************************************************/
272 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method )
273 {
274     if( !strcmp( psz_method, "discard" ) )
275     {
276         p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
277         p_vout->p_sys->b_double_rate = false;
278     }
279     else if( !strcmp( psz_method, "mean" ) )
280     {
281         p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
282         p_vout->p_sys->b_double_rate = false;
283     }
284     else if( !strcmp( psz_method, "blend" )
285              || !strcmp( psz_method, "average" )
286              || !strcmp( psz_method, "combine-fields" ) )
287     {
288         p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
289         p_vout->p_sys->b_double_rate = false;
290     }
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     }
297     else if( !strcmp( psz_method, "linear" ) )
298     {
299         p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
300         p_vout->p_sys->b_double_rate = true;
301     }
302     else if( !strcmp( psz_method, "x" ) )
303     {
304         p_vout->p_sys->i_mode = DEINTERLACE_X;
305         p_vout->p_sys->b_double_rate = false;
306     }
307     else
308     {
309         msg_Err( p_vout, "no valid deinterlace mode provided, "
310                  "using \"discard\"" );
311     }
312
313     msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
314 }
315
316 /*****************************************************************************
317  * Init: initialize Deinterlace video thread output method
318  *****************************************************************************/
319 static int Init( vout_thread_t *p_vout )
320 {
321     int i_index;
322     picture_t *p_pic;
323
324     I_OUTPUTPICTURES = 0;
325
326     /* Initialize the output structure, full of directbuffers since we want
327      * the decoder to output directly to our structures. */
328     switch( p_vout->render.i_chroma )
329     {
330         case VLC_FOURCC('I','4','2','0'):
331         case VLC_FOURCC('I','Y','U','V'):
332         case VLC_FOURCC('Y','V','1','2'):
333         case VLC_FOURCC('I','4','2','2'):
334             p_vout->output.i_chroma = p_vout->render.i_chroma;
335             p_vout->output.i_width  = p_vout->render.i_width;
336             p_vout->output.i_height = p_vout->render.i_height;
337             p_vout->output.i_aspect = p_vout->render.i_aspect;
338             p_vout->fmt_out = p_vout->fmt_in;
339             break;
340
341         default:
342             return VLC_EGENERIC; /* unknown chroma */
343             break;
344     }
345
346     /* Try to open the real video output */
347     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
348
349     if( p_vout->p_sys->p_vout == NULL )
350     {
351         /* Everything failed */
352         msg_Err( p_vout, "cannot open vout, aborting" );
353
354         return VLC_EGENERIC;
355     }
356
357     var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
358
359     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
360
361     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
362
363     ADD_PARENT_CALLBACKS( SendEventsToChild );
364
365     return VLC_SUCCESS;
366 }
367
368 /*****************************************************************************
369  * SpawnRealVout: spawn the real video output.
370  *****************************************************************************/
371 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout )
372 {
373     vout_thread_t *p_real_vout = NULL;
374     video_format_t fmt;
375     memset( &fmt, 0, sizeof( video_format_t ) );
376
377     msg_Dbg( p_vout, "spawning the real video output" );
378
379     fmt = p_vout->fmt_out;
380
381     switch( p_vout->render.i_chroma )
382     {
383     case VLC_FOURCC('I','4','2','0'):
384     case VLC_FOURCC('I','Y','U','V'):
385     case VLC_FOURCC('Y','V','1','2'):
386         switch( p_vout->p_sys->i_mode )
387         {
388         case DEINTERLACE_MEAN:
389         case DEINTERLACE_DISCARD:
390             fmt.i_height /= 2; fmt.i_visible_height /= 2; fmt.i_y_offset /= 2;
391             fmt.i_sar_den *= 2;
392             p_real_vout = vout_Create( p_vout, &fmt );
393             break;
394
395         case DEINTERLACE_BOB:
396         case DEINTERLACE_BLEND:
397         case DEINTERLACE_LINEAR:
398         case DEINTERLACE_X:
399             p_real_vout = vout_Create( p_vout, &fmt );
400             break;
401         }
402         break;
403
404     case VLC_FOURCC('I','4','2','2'):
405         fmt.i_chroma = VLC_FOURCC('I','4','2','0');
406         p_real_vout = vout_Create( p_vout, &fmt );
407         break;
408
409     default:
410         break;
411     }
412
413     return p_real_vout;
414 }
415
416 /*****************************************************************************
417  * End: terminate Deinterlace video thread output method
418  *****************************************************************************/
419 static void End( vout_thread_t *p_vout )
420 {
421     int i_index;
422
423     DEL_PARENT_CALLBACKS( SendEventsToChild );
424
425     if( p_vout->p_sys->p_vout )
426         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
427
428     /* Free the fake output buffers we allocated */
429     for( i_index = I_OUTPUTPICTURES ; i_index ; )
430     {
431         i_index--;
432         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
433     }
434
435     if( p_vout->p_sys->p_vout )
436         vout_CloseAndRelease( p_vout->p_sys->p_vout );
437 }
438
439 /*****************************************************************************
440  * Destroy: destroy Deinterlace video thread output method
441  *****************************************************************************
442  * Terminate an output method created by DeinterlaceCreateOutputMethod
443  *****************************************************************************/
444 static void Destroy( vlc_object_t *p_this )
445 {
446     vout_thread_t *p_vout = (vout_thread_t *)p_this;
447     vlc_mutex_destroy( &p_vout->p_sys->filter_lock );
448     free( p_vout->p_sys );
449 }
450
451 /*****************************************************************************
452  * Render: displays previously rendered output
453  *****************************************************************************
454  * This function send the currently rendered image to Deinterlace image,
455  * waits until it is displayed and switch the two rendering buffers, preparing
456  * next frame.
457  *****************************************************************************/
458 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
459 {
460     vout_sys_t *p_sys = p_vout->p_sys;
461     picture_t *pp_outpic[2];
462
463     p_vout->fmt_out.i_x_offset = p_sys->p_vout->fmt_in.i_x_offset =
464         p_vout->fmt_in.i_x_offset;
465     p_vout->fmt_out.i_y_offset = p_sys->p_vout->fmt_in.i_y_offset =
466         p_vout->fmt_in.i_y_offset;
467     p_vout->fmt_out.i_visible_width = p_sys->p_vout->fmt_in.i_visible_width =
468         p_vout->fmt_in.i_visible_width;
469     p_vout->fmt_out.i_visible_height = p_sys->p_vout->fmt_in.i_visible_height =
470         p_vout->fmt_in.i_visible_height;
471     if( p_vout->p_sys->i_mode == DEINTERLACE_MEAN ||
472         p_vout->p_sys->i_mode == DEINTERLACE_DISCARD )
473     {
474         p_vout->fmt_out.i_y_offset /= 2; p_sys->p_vout->fmt_in.i_y_offset /= 2;
475         p_vout->fmt_out.i_visible_height /= 2;
476         p_sys->p_vout->fmt_in.i_visible_height /= 2;
477     }
478  
479     pp_outpic[0] = pp_outpic[1] = NULL;
480
481     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
482
483     /* Get a new picture */
484     while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
485                                                 0, 0, 0 ) )
486               == NULL )
487     {
488         if( !vlc_object_alive (p_vout) || p_vout->b_error )
489         {
490             vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
491             return;
492         }
493         msleep( VOUT_OUTMEM_SLEEP );
494     }
495
496     vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
497
498     /* If we are using double rate, get an additional new picture */
499     if( p_vout->p_sys->b_double_rate )
500     {
501         while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
502                                                  0, 0, 0 ) )
503                   == NULL )
504         {
505             if( !vlc_object_alive (p_vout) || p_vout->b_error )
506             {
507                 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
508                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
509                 return;
510             }
511             msleep( VOUT_OUTMEM_SLEEP );
512         }
513
514         /* 20ms is a bit arbitrary, but it's only for the first image we get */
515         if( !p_vout->p_sys->last_date )
516         {
517             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
518                               p_pic->date + 20000 );
519         }
520         else
521         {
522             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
523                       (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
524         }
525         p_vout->p_sys->last_date = p_pic->date;
526     }
527
528     switch( p_vout->p_sys->i_mode )
529     {
530         case DEINTERLACE_DISCARD:
531             RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
532             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
533             break;
534
535         case DEINTERLACE_BOB:
536             RenderBob( p_vout, pp_outpic[0], p_pic, p_pic->b_top_field_first ? 0 : 1 );
537             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
538             RenderBob( p_vout, pp_outpic[1], p_pic, p_pic->b_top_field_first ? 1 : 0 );
539             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
540             break;
541
542         case DEINTERLACE_LINEAR:
543             RenderLinear( p_vout, pp_outpic[0], p_pic, p_pic->b_top_field_first ? 0 : 1 );
544             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
545             RenderLinear( p_vout, pp_outpic[1], p_pic, p_pic->b_top_field_first ? 1 : 0 );
546             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
547             break;
548
549         case DEINTERLACE_MEAN:
550             RenderMean( p_vout, pp_outpic[0], p_pic );
551             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
552             break;
553
554         case DEINTERLACE_BLEND:
555             RenderBlend( p_vout, pp_outpic[0], p_pic );
556             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
557             break;
558
559         case DEINTERLACE_X:
560             RenderX( pp_outpic[0], p_pic );
561             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
562             break;
563     }
564     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
565 }
566
567 /*****************************************************************************
568  * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
569  *****************************************************************************/
570 static void RenderDiscard( vout_thread_t *p_vout,
571                            picture_t *p_outpic, picture_t *p_pic, int i_field )
572 {
573     int i_plane;
574
575     /* Copy image and skip lines */
576     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
577     {
578         uint8_t *p_in, *p_out_end, *p_out;
579         int i_increment;
580
581         p_in = p_pic->p[i_plane].p_pixels
582                    + i_field * p_pic->p[i_plane].i_pitch;
583
584         p_out = p_outpic->p[i_plane].p_pixels;
585         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
586                              * p_outpic->p[i_plane].i_visible_lines;
587
588         switch( p_vout->render.i_chroma )
589         {
590         case VLC_FOURCC('I','4','2','0'):
591         case VLC_FOURCC('I','Y','U','V'):
592         case VLC_FOURCC('Y','V','1','2'):
593
594             for( ; p_out < p_out_end ; )
595             {
596                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
597
598                 p_out += p_outpic->p[i_plane].i_pitch;
599                 p_in += 2 * p_pic->p[i_plane].i_pitch;
600             }
601             break;
602
603         case VLC_FOURCC('I','4','2','2'):
604
605             i_increment = 2 * p_pic->p[i_plane].i_pitch;
606
607             if( i_plane == Y_PLANE )
608             {
609                 for( ; p_out < p_out_end ; )
610                 {
611                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
612                     p_out += p_outpic->p[i_plane].i_pitch;
613                     vlc_memcpy( p_out, p_in, 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                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
623                     p_out += p_outpic->p[i_plane].i_pitch;
624                     p_in += i_increment;
625                 }
626             }
627             break;
628
629         default:
630             break;
631         }
632     }
633 }
634
635 /*****************************************************************************
636  * RenderBob: renders a BOB picture - simple copy
637  *****************************************************************************/
638 static void RenderBob( vout_thread_t *p_vout,
639                        picture_t *p_outpic, picture_t *p_pic, int i_field )
640 {
641     int i_plane;
642
643     /* Copy image and skip lines */
644     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
645     {
646         uint8_t *p_in, *p_out_end, *p_out;
647
648         p_in = p_pic->p[i_plane].p_pixels;
649         p_out = p_outpic->p[i_plane].p_pixels;
650         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
651                              * p_outpic->p[i_plane].i_visible_lines;
652
653         switch( p_vout->render.i_chroma )
654         {
655             case VLC_FOURCC('I','4','2','0'):
656             case VLC_FOURCC('I','Y','U','V'):
657             case VLC_FOURCC('Y','V','1','2'):
658                 /* For BOTTOM field we need to add the first line */
659                 if( i_field == 1 )
660                 {
661                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
662                     p_in += p_pic->p[i_plane].i_pitch;
663                     p_out += p_outpic->p[i_plane].i_pitch;
664                 }
665
666                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
667
668                 for( ; p_out < p_out_end ; )
669                 {
670                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
671
672                     p_out += p_outpic->p[i_plane].i_pitch;
673
674                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
675
676                     p_in += 2 * p_pic->p[i_plane].i_pitch;
677                     p_out += p_outpic->p[i_plane].i_pitch;
678                 }
679
680                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
681
682                 /* For TOP field we need to add the last line */
683                 if( i_field == 0 )
684                 {
685                     p_in += p_pic->p[i_plane].i_pitch;
686                     p_out += p_outpic->p[i_plane].i_pitch;
687                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
688                 }
689                 break;
690
691             case VLC_FOURCC('I','4','2','2'):
692                 /* For BOTTOM field we need to add the first line */
693                 if( i_field == 1 )
694                 {
695                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
696                     p_in += p_pic->p[i_plane].i_pitch;
697                     p_out += p_outpic->p[i_plane].i_pitch;
698                 }
699
700                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
701
702                 if( i_plane == Y_PLANE )
703                 {
704                     for( ; p_out < p_out_end ; )
705                     {
706                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
707
708                         p_out += p_outpic->p[i_plane].i_pitch;
709
710                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
711
712                         p_in += 2 * p_pic->p[i_plane].i_pitch;
713                         p_out += p_outpic->p[i_plane].i_pitch;
714                     }
715                 }
716                 else
717                 {
718                     for( ; p_out < p_out_end ; )
719                     {
720                         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
721
722                         p_out += p_outpic->p[i_plane].i_pitch;
723                         p_in += 2 * p_pic->p[i_plane].i_pitch;
724                     }
725                 }
726
727                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
728
729                 /* For TOP field we need to add the last line */
730                 if( i_field == 0 )
731                 {
732                     p_in += p_pic->p[i_plane].i_pitch;
733                     p_out += p_outpic->p[i_plane].i_pitch;
734                     vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
735                 }
736                 break;
737         }
738     }
739 }
740
741 #define Merge p_vout->p_sys->pf_merge
742 #define EndMerge if(p_vout->p_sys->pf_end_merge) p_vout->p_sys->pf_end_merge
743
744 /*****************************************************************************
745  * RenderLinear: BOB with linear interpolation
746  *****************************************************************************/
747 static void RenderLinear( vout_thread_t *p_vout,
748                           picture_t *p_outpic, picture_t *p_pic, int i_field )
749 {
750     int i_plane;
751
752     /* Copy image and skip lines */
753     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
754     {
755         uint8_t *p_in, *p_out_end, *p_out;
756
757         p_in = p_pic->p[i_plane].p_pixels;
758         p_out = p_outpic->p[i_plane].p_pixels;
759         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
760                              * p_outpic->p[i_plane].i_visible_lines;
761
762         /* For BOTTOM field we need to add the first line */
763         if( i_field == 1 )
764         {
765             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
766             p_in += p_pic->p[i_plane].i_pitch;
767             p_out += p_outpic->p[i_plane].i_pitch;
768         }
769
770         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
771
772         for( ; p_out < p_out_end ; )
773         {
774             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
775
776             p_out += p_outpic->p[i_plane].i_pitch;
777
778             Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
779                    p_pic->p[i_plane].i_pitch );
780
781             p_in += 2 * p_pic->p[i_plane].i_pitch;
782             p_out += p_outpic->p[i_plane].i_pitch;
783         }
784
785         vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
786
787         /* For TOP field we need to add the last line */
788         if( i_field == 0 )
789         {
790             p_in += p_pic->p[i_plane].i_pitch;
791             p_out += p_outpic->p[i_plane].i_pitch;
792             vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
793         }
794     }
795     EndMerge();
796 }
797
798 static void RenderMean( vout_thread_t *p_vout,
799                         picture_t *p_outpic, picture_t *p_pic )
800 {
801     int i_plane;
802
803     /* Copy image and skip lines */
804     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
805     {
806         uint8_t *p_in, *p_out_end, *p_out;
807
808         p_in = p_pic->p[i_plane].p_pixels;
809
810         p_out = p_outpic->p[i_plane].p_pixels;
811         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
812                              * p_outpic->p[i_plane].i_visible_lines;
813
814         /* All lines: mean value */
815         for( ; p_out < p_out_end ; )
816         {
817             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
818                    p_pic->p[i_plane].i_pitch );
819
820             p_out += p_outpic->p[i_plane].i_pitch;
821             p_in += 2 * p_pic->p[i_plane].i_pitch;
822         }
823     }
824     EndMerge();
825 }
826
827 static void RenderBlend( vout_thread_t *p_vout,
828                          picture_t *p_outpic, picture_t *p_pic )
829 {
830     int i_plane;
831
832     /* Copy image and skip lines */
833     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
834     {
835         uint8_t *p_in, *p_out_end, *p_out;
836
837         p_in = p_pic->p[i_plane].p_pixels;
838
839         p_out = p_outpic->p[i_plane].p_pixels;
840         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
841                              * p_outpic->p[i_plane].i_visible_lines;
842
843         switch( p_vout->render.i_chroma )
844         {
845             case VLC_FOURCC('I','4','2','0'):
846             case VLC_FOURCC('I','Y','U','V'):
847             case VLC_FOURCC('Y','V','1','2'):
848                 /* First line: simple copy */
849                 vlc_memcpy( p_out, p_in, p_pic->p[i_plane].i_pitch );
850                 p_out += p_outpic->p[i_plane].i_pitch;
851
852                 /* Remaining lines: mean value */
853                 for( ; p_out < p_out_end ; )
854                 {
855                     Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
856                            p_pic->p[i_plane].i_pitch );
857
858                     p_out += p_outpic->p[i_plane].i_pitch;
859                     p_in += p_pic->p[i_plane].i_pitch;
860                 }
861                 break;
862
863             case VLC_FOURCC('I','4','2','2'):
864                 /* First li