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