]> git.sesse.net Git - vlc/blob - modules/video_filter/deinterlace/deinterlace.c
aa58eb00b54722ad4245f22782dfcf19d9e310c4
[vlc] / modules / video_filter / deinterlace / deinterlace.c
1 /*****************************************************************************
2  * deinterlace.c : deinterlacer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001, 2002, 2003 VideoLAN
5  * $Id: deinterlace.c,v 1.19 2004/01/23 15:36:23 titer Exp $
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <errno.h>
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <vlc/vlc.h>
32 #include <vlc/vout.h>
33
34 #include "../filter_common.h"
35
36 #define DEINTERLACE_DISCARD 1
37 #define DEINTERLACE_MEAN    2
38 #define DEINTERLACE_BLEND   3
39 #define DEINTERLACE_BOB     4
40 #define DEINTERLACE_LINEAR  5
41
42 /*****************************************************************************
43  * Local protypes
44  *****************************************************************************/
45 static int  Create    ( vlc_object_t * );
46 static void Destroy   ( vlc_object_t * );
47
48 static int  Init      ( vout_thread_t * );
49 static void End       ( vout_thread_t * );
50 static void Render    ( vout_thread_t *, picture_t * );
51
52 static void RenderDiscard( vout_thread_t *, picture_t *, picture_t *, int );
53 static void RenderBob    ( vout_thread_t *, picture_t *, picture_t *, int );
54 static void RenderMean   ( vout_thread_t *, picture_t *, picture_t * );
55 static void RenderBlend  ( vout_thread_t *, picture_t *, picture_t * );
56 static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
57
58 static void MergeGeneric ( void *, const void *, const void *, size_t );
59 static void MergeAltivec ( void *, const void *, const void *, size_t );
60
61 static int  SendEvents   ( vlc_object_t *, char const *,
62                            vlc_value_t, vlc_value_t, void * );
63
64 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method );
65 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout );
66
67 /*****************************************************************************
68  * Callback prototypes
69  *****************************************************************************/
70 static int FilterCallback ( vlc_object_t *, char const *,
71                             vlc_value_t, vlc_value_t, void * );
72
73 /*****************************************************************************
74  * Module descriptor
75  *****************************************************************************/
76 #define MODE_TEXT N_("Deinterlace mode")
77 #define MODE_LONGTEXT N_("You can choose the default deinterlace mode")
78
79 static char *mode_list[] = { "discard", "blend", "mean", "bob", "linear" };
80 static char *mode_list_text[] = { N_("discard"), N_("Blend"), N_("Mean"),
81                                   N_("Bob"), N_("Linear") };
82
83 vlc_module_begin();
84     add_category_hint( N_("Deinterlace"), NULL, VLC_FALSE );
85     add_string( "deinterlace-mode", "discard", NULL, MODE_TEXT,
86                 MODE_LONGTEXT, VLC_FALSE );
87         change_string_list( mode_list, mode_list_text, 0 );
88     set_description( _("video deinterlacing filter") );
89     set_capability( "video filter", 0 );
90     add_shortcut( "deinterlace" );
91     set_callbacks( Create, Destroy );
92 vlc_module_end();
93
94 /*****************************************************************************
95  * vout_sys_t: Deinterlace video output method descriptor
96  *****************************************************************************
97  * This structure is part of the video output thread descriptor.
98  * It describes the Deinterlace specific properties of an output thread.
99  *****************************************************************************/
100 struct vout_sys_t
101 {
102     int        i_mode;        /* Deinterlace mode */
103     vlc_bool_t b_double_rate; /* Shall we double the framerate? */
104
105     mtime_t    last_date;
106     mtime_t    next_date;
107
108     vout_thread_t *p_vout;
109
110     vlc_mutex_t filter_lock;
111
112     void (*pf_merge) ( void *, const void *, const void *, size_t );
113 };
114
115 /*****************************************************************************
116  * Create: allocates Deinterlace video thread output method
117  *****************************************************************************
118  * This function allocates and initializes a Deinterlace vout method.
119  *****************************************************************************/
120 static int Create( vlc_object_t *p_this )
121 {
122     vout_thread_t *p_vout = (vout_thread_t *)p_this;
123     vlc_value_t val;
124
125     /* Allocate structure */
126     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
127     if( p_vout->p_sys == NULL )
128     {
129         msg_Err( p_vout, "out of memory" );
130         return VLC_ENOMEM;
131     }
132
133     p_vout->pf_init = Init;
134     p_vout->pf_end = End;
135     p_vout->pf_manage = NULL;
136     p_vout->pf_render = Render;
137     p_vout->pf_display = NULL;
138
139     p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
140     p_vout->p_sys->b_double_rate = 0;
141     p_vout->p_sys->last_date = 0;
142     vlc_mutex_init( p_vout, &p_vout->p_sys->filter_lock );
143
144     if( p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_ALTIVEC )
145     {
146         p_vout->p_sys->pf_merge = MergeAltivec;
147     }
148     else
149     {
150         p_vout->p_sys->pf_merge = MergeGeneric;
151     }
152
153     /* Look what method was requested */
154     var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING );
155     var_Change( p_vout, "deinterlace-mode", VLC_VAR_INHERITVALUE, &val, NULL );
156
157     if( val.psz_string == NULL )
158     {
159         msg_Err( p_vout, "configuration variable deinterlace-mode empty" );
160         msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
161
162         val.psz_string = strdup( "discard" );
163     }
164
165     msg_Dbg( p_vout, "using %s deinterlace mode", val.psz_string );
166
167     SetFilterMethod( p_vout, val.psz_string );
168
169     free( val.psz_string );
170
171     var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
172
173     return VLC_SUCCESS;
174 }
175
176 /*****************************************************************************
177  * SetFilterMethod: setup the deinterlace method to use.
178  *****************************************************************************/
179 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method )
180 {
181     if( !strcmp( psz_method, "discard" ) )
182     {
183         p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
184         p_vout->p_sys->b_double_rate = 0;
185     }
186     else if( !strcmp( psz_method, "mean" ) )
187     {
188         p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
189         p_vout->p_sys->b_double_rate = 0;
190     }
191     else if( !strcmp( psz_method, "blend" )
192              || !strcmp( psz_method, "average" )
193              || !strcmp( psz_method, "combine-fields" ) )
194     {
195         p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
196         p_vout->p_sys->b_double_rate = 0;
197     }
198     else if( !strcmp( psz_method, "bob" )
199              || !strcmp( psz_method, "progressive-scan" ) )
200     {
201         p_vout->p_sys->i_mode = DEINTERLACE_BOB;
202         p_vout->p_sys->b_double_rate = 1;
203     }
204     else if( !strcmp( psz_method, "linear" ) )
205     {
206         p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
207         p_vout->p_sys->b_double_rate = 1;
208     }
209     else
210     {
211         msg_Err( p_vout, "no valid deinterlace mode provided, "
212                  "using \"discard\"" );
213     }
214
215     msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
216 }
217
218 /*****************************************************************************
219  * Init: initialize Deinterlace video thread output method
220  *****************************************************************************/
221 static int Init( vout_thread_t *p_vout )
222 {
223     int i_index;
224     picture_t *p_pic;
225
226     I_OUTPUTPICTURES = 0;
227
228     /* Initialize the output structure, full of directbuffers since we want
229      * the decoder to output directly to our structures. */
230     switch( p_vout->render.i_chroma )
231     {
232         case VLC_FOURCC('I','4','2','0'):
233         case VLC_FOURCC('I','Y','U','V'):
234         case VLC_FOURCC('Y','V','1','2'):
235         case VLC_FOURCC('I','4','2','2'):
236             p_vout->output.i_chroma = p_vout->render.i_chroma;
237             p_vout->output.i_width  = p_vout->render.i_width;
238             p_vout->output.i_height = p_vout->render.i_height;
239             p_vout->output.i_aspect = p_vout->render.i_aspect;
240             break;
241
242         default:
243             return VLC_EGENERIC; /* unknown chroma */
244             break;
245     }
246
247     /* Try to open the real video output */
248     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
249
250     if( p_vout->p_sys->p_vout == NULL )
251     {
252         /* Everything failed */
253         msg_Err( p_vout, "cannot open vout, aborting" );
254
255         return VLC_EGENERIC;
256     }
257
258     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
259
260     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
261
262     ADD_PARENT_CALLBACKS( SendEventsToChild );
263
264     return VLC_SUCCESS;
265 }
266
267 /*****************************************************************************
268  * SpawnRealVout: spawn the real video output.
269  *****************************************************************************/
270 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout )
271 {
272     vout_thread_t *p_real_vout = NULL;
273
274     msg_Dbg( p_vout, "spawning the real video output" );
275
276     switch( p_vout->render.i_chroma )
277     {
278     case VLC_FOURCC('I','4','2','0'):
279     case VLC_FOURCC('I','Y','U','V'):
280     case VLC_FOURCC('Y','V','1','2'):
281         switch( p_vout->p_sys->i_mode )
282         {
283         case DEINTERLACE_MEAN:
284         case DEINTERLACE_DISCARD:
285             p_real_vout =
286                 vout_Create( p_vout,
287                        p_vout->output.i_width, p_vout->output.i_height / 2,
288                        p_vout->output.i_chroma, p_vout->output.i_aspect );
289             break;
290
291         case DEINTERLACE_BOB:
292         case DEINTERLACE_BLEND:
293         case DEINTERLACE_LINEAR:
294             p_real_vout =
295                 vout_Create( p_vout,
296                        p_vout->output.i_width, p_vout->output.i_height,
297                        p_vout->output.i_chroma, p_vout->output.i_aspect );
298             break;
299         }
300         break;
301
302     case VLC_FOURCC('I','4','2','2'):
303         p_real_vout =
304             vout_Create( p_vout,
305                        p_vout->output.i_width, p_vout->output.i_height,
306                        VLC_FOURCC('I','4','2','0'), p_vout->output.i_aspect );
307         break;
308
309     default:
310         break;
311     }
312
313     return p_real_vout;
314 }
315
316 /*****************************************************************************
317  * End: terminate Deinterlace video thread output method
318  *****************************************************************************/
319 static void End( vout_thread_t *p_vout )
320 {
321     int i_index;
322
323     /* Free the fake output buffers we allocated */
324     for( i_index = I_OUTPUTPICTURES ; i_index ; )
325     {
326         i_index--;
327         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
328     }
329 }
330
331 /*****************************************************************************
332  * Destroy: destroy Deinterlace video thread output method
333  *****************************************************************************
334  * Terminate an output method created by DeinterlaceCreateOutputMethod
335  *****************************************************************************/
336 static void Destroy( vlc_object_t *p_this )
337 {
338     vout_thread_t *p_vout = (vout_thread_t *)p_this;
339
340     DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
341
342     vlc_object_detach( p_vout->p_sys->p_vout );
343     vout_Destroy( p_vout->p_sys->p_vout );
344
345     DEL_PARENT_CALLBACKS( SendEventsToChild );
346
347     free( p_vout->p_sys );
348 }
349
350 /*****************************************************************************
351  * Render: displays previously rendered output
352  *****************************************************************************
353  * This function send the currently rendered image to Deinterlace image,
354  * waits until it is displayed and switch the two rendering buffers, preparing
355  * next frame.
356  *****************************************************************************/
357 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
358 {
359     picture_t *pp_outpic[2];
360
361     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
362
363     /* Get a new picture */
364     while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
365                                              0, 0, 0 ) )
366               == NULL )
367     {
368         if( p_vout->b_die || p_vout->b_error )
369         {
370             vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
371             return;
372         }
373         msleep( VOUT_OUTMEM_SLEEP );
374      }
375
376     vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
377
378     /* If we are using double rate, get an additional new picture */
379     if( p_vout->p_sys->b_double_rate )
380     {
381         while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
382                                                  0, 0, 0 ) )
383                   == NULL )
384         {
385             if( p_vout->b_die || p_vout->b_error )
386             {
387                 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
388                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
389                 return;
390             }
391             msleep( VOUT_OUTMEM_SLEEP );
392         }
393
394         /* 20ms is a bit arbitrary, but it's only for the first image we get */
395         if( !p_vout->p_sys->last_date )
396         {
397             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
398                               p_pic->date + 20000 );
399         }
400         else
401         {
402             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
403                       (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
404         }
405         p_vout->p_sys->last_date = p_pic->date;
406     }
407
408     switch( p_vout->p_sys->i_mode )
409     {
410         case DEINTERLACE_DISCARD:
411             RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
412             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
413             break;
414
415         case DEINTERLACE_BOB:
416             RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
417             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
418             RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
419             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
420             break;
421
422         case DEINTERLACE_LINEAR:
423             RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
424             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
425             RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
426             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
427             break;
428
429         case DEINTERLACE_MEAN:
430             RenderMean( p_vout, pp_outpic[0], p_pic );
431             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
432             break;
433
434         case DEINTERLACE_BLEND:
435             RenderBlend( p_vout, pp_outpic[0], p_pic );
436             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
437             break;
438     }
439
440     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
441 }
442
443 /*****************************************************************************
444  * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
445  *****************************************************************************/
446 static void RenderDiscard( vout_thread_t *p_vout,
447                            picture_t *p_outpic, picture_t *p_pic, int i_field )
448 {
449     int i_plane;
450
451     /* Copy image and skip lines */
452     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
453     {
454         uint8_t *p_in, *p_out_end, *p_out;
455         int i_increment;
456
457         p_in = p_pic->p[i_plane].p_pixels
458                    + i_field * p_pic->p[i_plane].i_pitch;
459
460         p_out = p_outpic->p[i_plane].p_pixels;
461         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
462                              * p_outpic->p[i_plane].i_lines;
463
464         switch( p_vout->render.i_chroma )
465         {
466         case VLC_FOURCC('I','4','2','0'):
467         case VLC_FOURCC('I','Y','U','V'):
468         case VLC_FOURCC('Y','V','1','2'):
469
470             for( ; p_out < p_out_end ; )
471             {
472                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
473                                           p_pic->p[i_plane].i_pitch );
474
475                 p_out += p_pic->p[i_plane].i_pitch;
476                 p_in += 2 * p_pic->p[i_plane].i_pitch;
477             }
478             break;
479
480         case VLC_FOURCC('I','4','2','2'):
481
482             i_increment = 2 * p_pic->p[i_plane].i_pitch;
483
484             if( i_plane == Y_PLANE )
485             {
486                 for( ; p_out < p_out_end ; )
487                 {
488                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
489                                               p_pic->p[i_plane].i_pitch );
490                     p_out += p_pic->p[i_plane].i_pitch;
491                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
492                                               p_pic->p[i_plane].i_pitch );
493                     p_out += p_pic->p[i_plane].i_pitch;
494                     p_in += i_increment;
495                 }
496             }
497             else
498             {
499                 for( ; p_out < p_out_end ; )
500                 {
501                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
502                                               p_pic->p[i_plane].i_pitch );
503                     p_out += p_pic->p[i_plane].i_pitch;
504                     p_in += i_increment;
505                 }
506             }
507             break;
508
509         default:
510             break;
511         }
512     }
513 }
514
515 /*****************************************************************************
516  * RenderBob: renders a BOB picture - simple copy
517  *****************************************************************************/
518 static void RenderBob( vout_thread_t *p_vout,
519                        picture_t *p_outpic, picture_t *p_pic, int i_field )
520 {
521     int i_plane;
522
523     /* Copy image and skip lines */
524     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
525     {
526         uint8_t *p_in, *p_out_end, *p_out;
527
528         p_in = p_pic->p[i_plane].p_pixels;
529         p_out = p_outpic->p[i_plane].p_pixels;
530         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
531                              * p_outpic->p[i_plane].i_lines;
532
533         switch( p_vout->render.i_chroma )
534         {
535             case VLC_FOURCC('I','4','2','0'):
536             case VLC_FOURCC('I','Y','U','V'):
537             case VLC_FOURCC('Y','V','1','2'):
538                 /* For BOTTOM field we need to add the first line */
539                 if( i_field == 1 )
540                 {
541                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
542                                               p_pic->p[i_plane].i_pitch );
543                     p_in += p_pic->p[i_plane].i_pitch;
544                     p_out += p_pic->p[i_plane].i_pitch;
545                 }
546
547                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
548
549                 for( ; p_out < p_out_end ; )
550                 {
551                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
552                                               p_pic->p[i_plane].i_pitch );
553
554                     p_out += p_pic->p[i_plane].i_pitch;
555
556                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
557                                               p_pic->p[i_plane].i_pitch );
558
559                     p_in += 2 * p_pic->p[i_plane].i_pitch;
560                     p_out += p_pic->p[i_plane].i_pitch;
561                 }
562
563                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
564                                           p_pic->p[i_plane].i_pitch );
565
566                 /* For TOP field we need to add the last line */
567                 if( i_field == 0 )
568                 {
569                     p_in += p_pic->p[i_plane].i_pitch;
570                     p_out += p_pic->p[i_plane].i_pitch;
571                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
572                                               p_pic->p[i_plane].i_pitch );
573                 }
574                 break;
575
576             case VLC_FOURCC('I','4','2','2'):
577                 /* For BOTTOM field we need to add the first line */
578                 if( i_field == 1 )
579                 {
580                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
581                                               p_pic->p[i_plane].i_pitch );
582                     p_in += p_pic->p[i_plane].i_pitch;
583                     p_out += p_pic->p[i_plane].i_pitch;
584                 }
585
586                 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
587
588                 if( i_plane == Y_PLANE )
589                 {
590                     for( ; p_out < p_out_end ; )
591                     {
592                         p_vout->p_vlc->pf_memcpy( p_out, p_in,
593                                                   p_pic->p[i_plane].i_pitch );
594
595                         p_out += p_pic->p[i_plane].i_pitch;
596
597                         p_vout->p_vlc->pf_memcpy( p_out, p_in,
598                                                   p_pic->p[i_plane].i_pitch );
599
600                         p_in += 2 * p_pic->p[i_plane].i_pitch;
601                         p_out += p_pic->p[i_plane].i_pitch;
602                     }
603                 }
604                 else
605                 {
606                     for( ; p_out < p_out_end ; )
607                     {
608                         p_vout->p_vlc->pf_memcpy( p_out, p_in,
609                                                   p_pic->p[i_plane].i_pitch );
610
611                         p_out += p_pic->p[i_plane].i_pitch;
612                         p_in += 2 * p_pic->p[i_plane].i_pitch;
613                     }
614                 }
615
616                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
617                                           p_pic->p[i_plane].i_pitch );
618
619                 /* For TOP field we need to add the last line */
620                 if( i_field == 0 )
621                 {
622                     p_in += p_pic->p[i_plane].i_pitch;
623                     p_out += p_pic->p[i_plane].i_pitch;
624                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
625                                               p_pic->p[i_plane].i_pitch );
626                 }
627                 break;
628         }
629     }
630 }
631
632 #define Merge p_vout->p_sys->pf_merge
633
634 /*****************************************************************************
635  * RenderLinear: BOB with linear interpolation
636  *****************************************************************************/
637 static void RenderLinear( vout_thread_t *p_vout,
638                           picture_t *p_outpic, picture_t *p_pic, int i_field )
639 {
640     int i_plane;
641
642     /* Copy image and skip lines */
643     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
644     {
645         uint8_t *p_in, *p_out_end, *p_out;
646
647         p_in = p_pic->p[i_plane].p_pixels;
648         p_out = p_outpic->p[i_plane].p_pixels;
649         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
650                              * p_outpic->p[i_plane].i_lines;
651
652         /* For BOTTOM field we need to add the first line */
653         if( i_field == 1 )
654         {
655             p_vout->p_vlc->pf_memcpy( p_out, p_in,
656                                       p_pic->p[i_plane].i_pitch );
657             p_in += p_pic->p[i_plane].i_pitch;
658             p_out += p_pic->p[i_plane].i_pitch;
659         }
660
661         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
662
663         for( ; p_out < p_out_end ; )
664         {
665             p_vout->p_vlc->pf_memcpy( p_out, p_in,
666                                       p_pic->p[i_plane].i_pitch );
667
668             p_out += p_pic->p[i_plane].i_pitch;
669
670             Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
671                    p_pic->p[i_plane].i_pitch );
672
673             p_in += 2 * p_pic->p[i_plane].i_pitch;
674             p_out += p_pic->p[i_plane].i_pitch;
675         }
676
677         p_vout->p_vlc->pf_memcpy( p_out, p_in,
678                                   p_pic->p[i_plane].i_pitch );
679
680         /* For TOP field we need to add the last line */
681         if( i_field == 0 )
682         {
683             p_in += p_pic->p[i_plane].i_pitch;
684             p_out += p_pic->p[i_plane].i_pitch;
685             p_vout->p_vlc->pf_memcpy( p_out, p_in,
686                                       p_pic->p[i_plane].i_pitch );
687         }
688     }
689 }
690
691 static void RenderMean( vout_thread_t *p_vout,
692                         picture_t *p_outpic, picture_t *p_pic )
693 {
694     int i_plane;
695
696     /* Copy image and skip lines */
697     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
698     {
699         uint8_t *p_in, *p_out_end, *p_out;
700
701         p_in = p_pic->p[i_plane].p_pixels;
702
703         p_out = p_outpic->p[i_plane].p_pixels;
704         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
705                              * p_outpic->p[i_plane].i_lines;
706
707         /* All lines: mean value */
708         for( ; p_out < p_out_end ; )
709         {
710             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
711                    p_pic->p[i_plane].i_pitch );
712
713             p_out += p_pic->p[i_plane].i_pitch;
714             p_in += 2 * p_pic->p[i_plane].i_pitch;
715         }
716     }
717 }
718
719 static void RenderBlend( vout_thread_t *p_vout,
720                          picture_t *p_outpic, picture_t *p_pic )
721 {
722     int i_plane;
723
724     /* Copy image and skip lines */
725     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
726     {
727         uint8_t *p_in, *p_out_end, *p_out;
728
729         p_in = p_pic->p[i_plane].p_pixels;
730
731         p_out = p_outpic->p[i_plane].p_pixels;
732         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
733                              * p_outpic->p[i_plane].i_lines;
734
735         switch( p_vout->render.i_chroma )
736         {
737             case VLC_FOURCC('I','4','2','0'):
738             case VLC_FOURCC('I','Y','U','V'):
739             case VLC_FOURCC('Y','V','1','2'):
740                 /* First line: simple copy */
741                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
742                                           p_pic->p[i_plane].i_pitch );
743                 p_out += p_pic->p[i_plane].i_pitch;
744
745                 /* Remaining lines: mean value */
746                 for( ; p_out < p_out_end ; )
747                 {
748                    Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
749                           p_pic->p[i_plane].i_pitch );
750
751                     p_out += p_pic->p[i_plane].i_pitch;
752                     p_in += p_pic->p[i_plane].i_pitch;
753                 }
754                 break;
755
756             case VLC_FOURCC('I','4','2','2'):
757                 /* First line: simple copy */
758                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
759                                           p_pic->p[i_plane].i_pitch );
760                 p_out += p_pic->p[i_plane].i_pitch;
761
762                 /* Remaining lines: mean value */
763                 if( i_plane == Y_PLANE )
764                 {
765                     for( ; p_out < p_out_end ; )
766                     {
767                         Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
768                                p_pic->p[i_plane].i_pitch );
769
770                         p_out += p_pic->p[i_plane].i_pitch;
771                         p_in += p_pic->p[i_plane].i_pitch;
772                     }
773                 }
774
775                 else
776                 {
777                     for( ; p_out < p_out_end ; )
778                     {
779                         Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
780                                p_pic->p[i_plane].i_pitch );
781
782                         p_out += p_pic->p[i_plane].i_pitch;
783                         p_in += 2*p_pic->p[i_plane].i_pitch;
784                     }
785                 }
786                 break;
787         }
788     }
789 }
790
791 #undef Merge
792
793 static void MergeGeneric( void *_p_dest, const void *_p_s1,
794                           const void *_p_s2, size_t i_bytes )
795 {
796     uint8_t* p_dest = (uint8_t*)_p_dest;
797     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
798     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
799     uint8_t* p_end = p_dest + i_bytes - 8;
800
801     while( p_dest < p_end )
802     {
803         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
804         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
805         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
806         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
807         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
808         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
809         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
810         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
811     }
812
813     p_end += 8;
814
815     while( p_dest < p_end )
816     {
817         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
818     }
819 }
820
821 static void MergeAltivec( void *_p_dest, const void *_p_s1,
822                           const void *_p_s2, size_t i_bytes )
823 {
824 #ifdef CAN_COMPILE_C_ALTIVEC
825     uint8_t *p_dest = (uint8_t*)_p_dest;
826     const uint8_t *p_s1 = (const uint8_t *)_p_s1;
827     const uint8_t *p_s2 = (const uint8_t *)_p_s2;
828     uint8_t *p_end = p_dest + i_bytes - 16;
829
830     if( ( (int)p_s1 & 0xF ) | ( (int)p_s2 & 0xF ) |
831         ( (int)p_dest & 0xF ) )
832     {
833         /* TODO Handle non 16-bytes aligned planes */
834         MergeGeneric( _p_dest, _p_s1, _p_s2, i_bytes );
835         return;
836     }
837
838     while( p_dest < p_end )
839     {
840         vec_st( vec_avg( vec_ld( 0, p_s1 ), vec_ld( 0, p_s2 ) ),
841                 0, p_dest );
842         p_s1   += 16;
843         p_s2   += 16;
844         p_dest += 16;
845     }
846
847     p_end += 16;
848
849     while( p_dest < p_end )
850     {
851         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
852     }
853 #endif
854 }
855
856 /*****************************************************************************
857  * SendEvents: forward mouse and keyboard events to the parent p_vout
858  *****************************************************************************/
859 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
860                        vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
861 {
862     vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
863     vlc_value_t sentval = newval;
864
865     if( !strcmp( psz_var, "mouse-y" ) )
866     {
867         switch( p_vout->p_sys->i_mode )
868         {
869             case DEINTERLACE_MEAN:
870             case DEINTERLACE_DISCARD:
871                 sentval.i_int *= 2;
872                 break;
873         }
874     }
875
876     var_Set( p_vout, psz_var, sentval );
877
878     return VLC_SUCCESS;
879 }
880
881 /*****************************************************************************
882  * FilterCallback: called when changing the deinterlace method on the fly.
883  *****************************************************************************/
884 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
885                            vlc_value_t oldval, vlc_value_t newval,
886                            void *p_data )
887 {
888     vout_thread_t * p_vout = (vout_thread_t *)p_this;
889     int i_old_mode = p_vout->p_sys->i_mode;
890
891     msg_Dbg( p_vout, "using %s deinterlace mode", newval.psz_string );
892
893     vlc_mutex_lock( &p_vout->p_sys->filter_lock );
894
895     SetFilterMethod( p_vout, newval.psz_string );
896
897     switch( p_vout->render.i_chroma )
898     {
899     case VLC_FOURCC('I','4','2','2'):
900         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
901         return VLC_SUCCESS;
902         break;
903
904     case VLC_FOURCC('I','4','2','0'):
905     case VLC_FOURCC('I','Y','U','V'):
906     case VLC_FOURCC('Y','V','1','2'):
907         switch( p_vout->p_sys->i_mode )
908         {
909         case DEINTERLACE_MEAN:
910         case DEINTERLACE_DISCARD:
911             if( ( i_old_mode == DEINTERLACE_MEAN )
912                 || ( i_old_mode == DEINTERLACE_DISCARD ) )
913             {
914                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
915                 return VLC_SUCCESS;
916             }
917             break;
918
919         case DEINTERLACE_BOB:
920         case DEINTERLACE_BLEND:
921         case DEINTERLACE_LINEAR:
922             if( ( i_old_mode == DEINTERLACE_BOB )
923                 || ( i_old_mode == DEINTERLACE_BLEND )
924                 || ( i_old_mode == DEINTERLACE_LINEAR ) )
925             {
926                 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
927                 return VLC_SUCCESS;
928             }
929             break;
930         }
931         break;
932
933     default:
934         break;
935     }
936
937     /* We need to kill the old vout */
938
939     DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
940
941     vlc_object_detach( p_vout->p_sys->p_vout );
942     vout_Destroy( p_vout->p_sys->p_vout );
943
944     /* Try to open a new video output */
945     p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
946
947     if( p_vout->p_sys->p_vout == NULL )
948     {
949         /* Everything failed */
950         msg_Err( p_vout, "cannot open vout, aborting" );
951
952         vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
953         return VLC_EGENERIC;
954     }
955
956     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
957
958     vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
959     return VLC_SUCCESS;
960 }
961
962 /*****************************************************************************
963  * SendEventsToChild: forward events to the child/children vout
964  *****************************************************************************/
965 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
966                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
967 {
968     vout_thread_t *p_vout = (vout_thread_t *)p_this;
969     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
970     return VLC_SUCCESS;
971 }