* ./plugins/filter/deinterlace.c: fixed the green bottom line in the
[vlc.git] / modules / video_filter / deinterlace / deinterlace.c
1 /*****************************************************************************
2  * deinterlace.c : deinterlacer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: deinterlace.c,v 1.3 2002/10/11 21:17:29 sam Exp $
6  *
7  * Authors: Samuel 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 RenderBob    ( vout_thread_t *, picture_t *, picture_t *, int );
53 static void RenderMean   ( vout_thread_t *, picture_t *, picture_t * );
54 static void RenderBlend  ( vout_thread_t *, picture_t *, picture_t * );
55 static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
56
57 static void Merge        ( void *, const void *, const void *, size_t );
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 #define MODE_TEXT N_("deinterlace mode")
63 #define MODE_LONGTEXT N_("One of \"discard\", \"blend\", \"mean\", \"bob\" or \"linear\"")
64
65 static char *mode_list[] = { "discard", "blend", "mean", "bob", "linear", NULL };
66
67 vlc_module_begin();
68     add_category_hint( N_("Miscellaneous"), NULL );
69     add_string_from_list( "deinterlace-mode", "discard", mode_list, NULL,
70                           MODE_TEXT, MODE_LONGTEXT );
71     set_description( _("deinterlacing module") );
72     set_capability( "video filter", 0 );
73     add_shortcut( "deinterlace" );
74     set_callbacks( Create, Destroy );
75 vlc_module_end();
76
77 /*****************************************************************************
78  * vout_sys_t: Deinterlace video output method descriptor
79  *****************************************************************************
80  * This structure is part of the video output thread descriptor.
81  * It describes the Deinterlace specific properties of an output thread.
82  *****************************************************************************/
83 struct vout_sys_t
84 {
85     int        i_mode;        /* Deinterlace mode */
86     vlc_bool_t b_double_rate; /* Shall we double the framerate? */
87
88     mtime_t    last_date;
89     mtime_t    next_date;
90
91     vout_thread_t *p_vout;
92 };
93
94 /*****************************************************************************
95  * Create: allocates Deinterlace video thread output method
96  *****************************************************************************
97  * This function allocates and initializes a Deinterlace vout method.
98  *****************************************************************************/
99 static int Create( vlc_object_t *p_this )
100 {   
101     vout_thread_t *p_vout = (vout_thread_t *)p_this;
102     char *psz_method;
103
104     /* Allocate structure */
105     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
106     if( p_vout->p_sys == NULL )
107     {
108         msg_Err( p_vout, "out of memory" );
109         return 1;
110     }
111
112     p_vout->pf_init = Init;
113     p_vout->pf_end = End;
114     p_vout->pf_manage = NULL;
115     p_vout->pf_render = Render;
116     p_vout->pf_display = NULL;
117
118     p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
119     p_vout->p_sys->b_double_rate = 0;
120     p_vout->p_sys->last_date = 0;
121
122     /* Look what method was requested */
123     psz_method = config_GetPsz( p_vout, "deinterlace-mode" );
124
125     if( psz_method == NULL )
126     {
127         msg_Err( p_vout, "configuration variable %s empty",
128                          "deinterlace-mode" );
129         msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
130     }
131     else
132     {
133         if( !strcmp( psz_method, "discard" ) )
134         {
135             p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
136         }
137         else if( !strcmp( psz_method, "mean" ) )
138         {
139             p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
140         }
141         else if( !strcmp( psz_method, "blend" )
142                   || !strcmp( psz_method, "average" )
143                   || !strcmp( psz_method, "combine-fields" ) )
144         {
145             p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
146         }
147         else if( !strcmp( psz_method, "bob" )
148                   || !strcmp( psz_method, "progressive-scan" ) )
149         {
150             p_vout->p_sys->i_mode = DEINTERLACE_BOB;
151             p_vout->p_sys->b_double_rate = 1;
152         }
153         else if( !strcmp( psz_method, "linear" ) )
154         {
155             p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
156             p_vout->p_sys->b_double_rate = 1;
157         }
158         else
159         {
160             msg_Err( p_vout, "no valid deinterlace mode provided, "
161                              "using \"discard\"" );
162         }
163
164         free( psz_method );
165     }
166
167     return 0;
168 }
169
170 /*****************************************************************************
171  * Init: initialize Deinterlace video thread output method
172  *****************************************************************************/
173 static int Init( vout_thread_t *p_vout )
174 {
175     int i_index;
176     picture_t *p_pic;
177     
178     I_OUTPUTPICTURES = 0;
179
180     /* Initialize the output structure, full of directbuffers since we want
181      * the decoder to output directly to our structures. */
182     switch( p_vout->render.i_chroma )
183     {
184         case VLC_FOURCC('I','4','2','0'):
185         case VLC_FOURCC('I','Y','U','V'):
186         case VLC_FOURCC('Y','V','1','2'):
187         case VLC_FOURCC('I','4','2','2'):
188             p_vout->output.i_chroma = p_vout->render.i_chroma;
189             p_vout->output.i_width  = p_vout->render.i_width;
190             p_vout->output.i_height = p_vout->render.i_height;
191             p_vout->output.i_aspect = p_vout->render.i_aspect;
192             break;
193
194         default:
195             return 0; /* unknown chroma */
196             break;
197     }
198
199     /* Try to open the real video output, with half the height our images */
200     msg_Dbg( p_vout, "spawning the real video output" );
201
202     switch( p_vout->render.i_chroma )
203     {
204     case VLC_FOURCC('I','4','2','0'):
205     case VLC_FOURCC('I','Y','U','V'):
206     case VLC_FOURCC('Y','V','1','2'):
207         switch( p_vout->p_sys->i_mode )
208         {
209         case DEINTERLACE_BOB:
210         case DEINTERLACE_MEAN:
211         case DEINTERLACE_DISCARD:
212             p_vout->p_sys->p_vout =
213                 vout_CreateThread( p_vout,
214                        p_vout->output.i_width, p_vout->output.i_height / 2,
215                        p_vout->output.i_chroma, p_vout->output.i_aspect );
216             break;
217
218         case DEINTERLACE_BLEND:
219         case DEINTERLACE_LINEAR:
220             p_vout->p_sys->p_vout =
221                 vout_CreateThread( p_vout,
222                        p_vout->output.i_width, p_vout->output.i_height,
223                        p_vout->output.i_chroma, p_vout->output.i_aspect );
224             break;
225         }
226         break;
227
228     case VLC_FOURCC('I','4','2','2'):
229         p_vout->p_sys->p_vout =
230             vout_CreateThread( p_vout,
231                        p_vout->output.i_width, p_vout->output.i_height,
232                        VLC_FOURCC('I','4','2','0'), p_vout->output.i_aspect );
233         break;
234
235     default:
236         break;
237     }
238
239     /* Everything failed */
240     if( p_vout->p_sys->p_vout == NULL )
241     {
242         msg_Err( p_vout, "cannot open vout, aborting" );
243
244         return 0;
245     }
246  
247     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
248
249     return 0;
250 }
251
252 /*****************************************************************************
253  * End: terminate Deinterlace video thread output method
254  *****************************************************************************/
255 static void End( vout_thread_t *p_vout )
256 {
257     int i_index;
258
259     /* Free the fake output buffers we allocated */
260     for( i_index = I_OUTPUTPICTURES ; i_index ; )
261     {
262         i_index--;
263         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
264     }
265 }
266
267 /*****************************************************************************
268  * Destroy: destroy Deinterlace video thread output method
269  *****************************************************************************
270  * Terminate an output method created by DeinterlaceCreateOutputMethod
271  *****************************************************************************/
272 static void Destroy( vlc_object_t *p_this )
273 {
274     vout_thread_t *p_vout = (vout_thread_t *)p_this;
275
276     vout_DestroyThread( p_vout->p_sys->p_vout );
277
278     free( p_vout->p_sys );
279 }
280
281 /*****************************************************************************
282  * Render: displays previously rendered output
283  *****************************************************************************
284  * This function send the currently rendered image to Deinterlace image,
285  * waits until it is displayed and switch the two rendering buffers, preparing
286  * next frame.
287  *****************************************************************************/
288 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
289 {
290     picture_t *pp_outpic[2];
291
292     /* Get a new picture */
293     while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
294                                              0, 0, 0 ) )
295               == NULL )
296     {
297         if( p_vout->b_die || p_vout->b_error )
298         {
299             return;
300         }
301         msleep( VOUT_OUTMEM_SLEEP );
302     }
303
304     vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
305
306     /* If we are using double rate, get an additional new picture */
307     if( p_vout->p_sys->b_double_rate )
308     {
309         while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
310                                                  0, 0, 0 ) )
311                   == NULL )
312         {
313             if( p_vout->b_die || p_vout->b_error )
314             {
315                 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
316                 return;
317             }
318             msleep( VOUT_OUTMEM_SLEEP );
319         }   
320
321         /* 20ms is a bit arbitrary, but it's only for the first image we get */
322         if( !p_vout->p_sys->last_date )
323         {
324             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
325                               p_pic->date + 20000 );
326         }
327         else
328         {
329             vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
330                       (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
331         }
332         p_vout->p_sys->last_date = p_pic->date;
333     }
334
335     switch( p_vout->p_sys->i_mode )
336     {
337         case DEINTERLACE_DISCARD:
338             RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
339             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
340             break;
341
342         case DEINTERLACE_BOB:
343             RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
344             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
345             RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
346             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
347             break;
348
349         case DEINTERLACE_LINEAR:
350             RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
351             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
352             RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
353             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
354             break;
355
356         case DEINTERLACE_MEAN:
357             RenderMean( p_vout, pp_outpic[0], p_pic );
358             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
359             break;
360
361         case DEINTERLACE_BLEND:
362             RenderBlend( p_vout, pp_outpic[0], p_pic );
363             vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
364             break;
365     }
366 }
367
368 /*****************************************************************************
369  * RenderBob: renders a bob picture
370  *****************************************************************************/
371 static void RenderBob( vout_thread_t *p_vout,
372                        picture_t *p_outpic, picture_t *p_pic, int i_field )
373 {
374     int i_plane;
375
376     /* Copy image and skip lines */
377     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
378     {
379         u8 *p_in, *p_out_end, *p_out;
380         int i_increment;
381
382         p_in = p_pic->p[i_plane].p_pixels
383                    + i_field * p_pic->p[i_plane].i_pitch;
384
385         p_out = p_outpic->p[i_plane].p_pixels;
386         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
387                              * p_outpic->p[i_plane].i_lines;
388
389         switch( p_vout->render.i_chroma )
390         {
391         case VLC_FOURCC('I','4','2','0'):
392         case VLC_FOURCC('I','Y','U','V'):
393         case VLC_FOURCC('Y','V','1','2'):
394
395             for( ; p_out < p_out_end ; )
396             {
397                 p_vout->p_vlc->pf_memcpy( p_out, p_in,
398                                           p_pic->p[i_plane].i_pitch );
399
400                 p_out += p_pic->p[i_plane].i_pitch;
401                 p_in += 2 * p_pic->p[i_plane].i_pitch;
402             }
403             break;
404
405         case VLC_FOURCC('I','4','2','2'):
406
407             i_increment = 2 * p_pic->p[i_plane].i_pitch;
408
409             if( i_plane == Y_PLANE )
410             {
411                 for( ; p_out < p_out_end ; )
412                 {
413                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
414                                               p_pic->p[i_plane].i_pitch );
415                     p_out += p_pic->p[i_plane].i_pitch;
416                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
417                                               p_pic->p[i_plane].i_pitch );
418                     p_out += p_pic->p[i_plane].i_pitch;
419                     p_in += i_increment;
420                 }
421             }
422             else
423             {
424                 for( ; p_out < p_out_end ; )
425                 {
426                     p_vout->p_vlc->pf_memcpy( p_out, p_in,
427                                               p_pic->p[i_plane].i_pitch );
428                     p_out += p_pic->p[i_plane].i_pitch;
429                     p_in += i_increment;
430                 }
431             }
432             break;
433
434         default:
435             break;
436         }
437     }
438 }
439
440 /*****************************************************************************
441  * RenderLinear: displays previously rendered output
442  *****************************************************************************/
443 static void RenderLinear( vout_thread_t *p_vout,
444                           picture_t *p_outpic, picture_t *p_pic, int i_field )
445 {
446     int i_plane;
447
448     /* Copy image and skip lines */
449     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
450     {
451         u8 *p_in, *p_out_end, *p_out;
452
453         p_in = p_pic->p[i_plane].p_pixels;
454         p_out = p_outpic->p[i_plane].p_pixels;
455         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
456                              * p_outpic->p[i_plane].i_lines;
457
458         /* For BOTTOM field we need to add the first line */
459         if( i_field == 1 )
460         {
461             p_vout->p_vlc->pf_memcpy( p_out, p_in,
462                                       p_pic->p[i_plane].i_pitch );
463             p_in += p_pic->p[i_plane].i_pitch;
464             p_out += p_pic->p[i_plane].i_pitch;
465         }
466
467         p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
468
469         for( ; p_out < p_out_end ; )
470         {
471             p_vout->p_vlc->pf_memcpy( p_out, p_in,
472                                       p_pic->p[i_plane].i_pitch );
473
474             p_out += p_pic->p[i_plane].i_pitch;
475
476             Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
477                    p_pic->p[i_plane].i_pitch );
478
479             p_in += 2 * p_pic->p[i_plane].i_pitch;
480             p_out += p_pic->p[i_plane].i_pitch;
481         }
482
483         p_vout->p_vlc->pf_memcpy( p_out, p_in,
484                                   p_pic->p[i_plane].i_pitch );
485
486         /* For TOP field we need to add the last line */
487         if( i_field == 0 )
488         {
489             p_in += 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         }
494     }
495 }
496
497 static void RenderMean( vout_thread_t *p_vout,
498                         picture_t *p_outpic, picture_t *p_pic )
499 {
500     int i_plane;
501
502     /* Copy image and skip lines */
503     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
504     {
505         u8 *p_in, *p_out_end, *p_out;
506
507         p_in = p_pic->p[i_plane].p_pixels;
508
509         p_out = p_outpic->p[i_plane].p_pixels;
510         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
511                              * p_outpic->p[i_plane].i_lines;
512
513         /* All lines: mean value */
514         for( ; p_out < p_out_end ; )
515         {
516             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
517                    p_pic->p[i_plane].i_pitch );
518
519             p_out += p_pic->p[i_plane].i_pitch;
520             p_in += 2 * p_pic->p[i_plane].i_pitch;
521         }
522     }
523 }
524
525 static void RenderBlend( vout_thread_t *p_vout,
526                          picture_t *p_outpic, picture_t *p_pic )
527 {
528     int i_plane;
529
530     /* Copy image and skip lines */
531     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
532     {
533         u8 *p_in, *p_out_end, *p_out;
534
535         p_in = p_pic->p[i_plane].p_pixels;
536
537         p_out = p_outpic->p[i_plane].p_pixels;
538         p_out_end = p_out + p_outpic->p[i_plane].i_pitch
539                              * p_outpic->p[i_plane].i_lines;
540
541         /* First line: simple copy */
542         p_vout->p_vlc->pf_memcpy( p_out, p_in,
543                                   p_pic->p[i_plane].i_pitch );
544         p_out += p_pic->p[i_plane].i_pitch;
545
546         /* Remaining lines: mean value */
547         for( ; p_out < p_out_end ; )
548         {
549             Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
550                    p_pic->p[i_plane].i_pitch );
551
552             p_out += p_pic->p[i_plane].i_pitch;
553             p_in += p_pic->p[i_plane].i_pitch;
554         }
555     }
556 }
557
558 static void Merge( void *p_dest, const void *p_s1,
559                    const void *p_s2, size_t i_bytes )
560 {
561     u8* p_end = (u8*)p_dest + i_bytes - 8;
562
563     while( (u8*)p_dest < p_end )
564     {
565         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
566         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
567         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
568         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
569         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
570         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
571         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
572         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
573     }
574
575     p_end += 8;
576
577     while( (u8*)p_dest < p_end )
578     {
579         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
580     }
581 }