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 $
7 * Authors: Samuel Hocevar <sam@zoy.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
34 #include "filter_common.h"
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
42 /*****************************************************************************
43 * Capabilities defined in the other files.
44 *****************************************************************************/
45 static void vout_getfunctions( function_list_t * p_function_list );
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 );
52 static void Merge ( void *, const void *, const void *, size_t );
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\"")
60 static char *mode_list[] = { "discard", "blend", "mean", "bob", "linear", NULL };
63 ADD_CATEGORY_HINT( N_("Miscellaneous"), NULL )
64 ADD_STRING_FROM_LIST ( "deinterlace-mode", "discard", mode_list, NULL, \
65 MODE_TEXT, MODE_LONGTEXT )
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" )
77 vout_getfunctions( &p_module->p_functions->vout );
80 MODULE_DEACTIVATE_START
81 MODULE_DEACTIVATE_STOP
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 *****************************************************************************/
91 int i_mode; /* Deinterlace mode */
92 vlc_bool_t b_double_rate; /* Shall we double the framerate? */
97 vout_thread_t *p_vout;
100 /*****************************************************************************
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 * );
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 )
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;
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 )
135 /* Allocate structure */
136 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
137 if( p_vout->p_sys == NULL )
139 msg_Err( p_vout, "out of memory" );
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;
147 /* Look what method was requested */
148 psz_method = config_GetPsz( p_vout, "deinterlace-mode" );
150 if( psz_method == NULL )
152 msg_Err( p_vout, "configuration variable %s empty",
153 "deinterlace-mode" );
154 msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
158 if( !strcmp( psz_method, "discard" ) )
160 p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
162 else if( !strcmp( psz_method, "mean" ) )
164 p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
166 else if( !strcmp( psz_method, "blend" )
167 || !strcmp( psz_method, "average" )
168 || !strcmp( psz_method, "combine-fields" ) )
170 p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
172 else if( !strcmp( psz_method, "bob" )
173 || !strcmp( psz_method, "progressive-scan" ) )
175 p_vout->p_sys->i_mode = DEINTERLACE_BOB;
176 p_vout->p_sys->b_double_rate = 1;
178 else if( !strcmp( psz_method, "linear" ) )
180 p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
181 p_vout->p_sys->b_double_rate = 1;
185 msg_Err( p_vout, "no valid deinterlace mode provided, "
186 "using \"discard\"" );
195 /*****************************************************************************
196 * vout_Init: initialize Deinterlace video thread output method
197 *****************************************************************************/
198 static int vout_Init( vout_thread_t *p_vout )
203 I_OUTPUTPICTURES = 0;
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 )
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;
220 return 0; /* unknown chroma */
224 /* Try to open the real video output, with half the height our images */
225 msg_Dbg( p_vout, "spawning the real video output" );
227 switch( p_vout->render.i_chroma )
232 switch( p_vout->p_sys->i_mode )
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 );
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 );
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 );
264 /* Everything failed */
265 if( p_vout->p_sys->p_vout == NULL )
267 msg_Err( p_vout, "cannot open vout, aborting" );
272 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
277 /*****************************************************************************
278 * vout_End: terminate Deinterlace video thread output method
279 *****************************************************************************/
280 static void vout_End( vout_thread_t *p_vout )
284 /* Free the fake output buffers we allocated */
285 for( i_index = I_OUTPUTPICTURES ; i_index ; )
288 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
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 )
299 vout_DestroyThread( p_vout->p_sys->p_vout );
301 free( p_vout->p_sys );
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 )
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
321 *****************************************************************************/
322 static void vout_Render ( vout_thread_t *p_vout, picture_t *p_pic )
324 picture_t *pp_outpic[2];
326 /* Get a new picture */
327 while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
331 if( p_vout->b_die || p_vout->b_error )
335 msleep( VOUT_OUTMEM_SLEEP );
338 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
340 /* If we are using double rate, get an additional new picture */
341 if( p_vout->p_sys->b_double_rate )
343 while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
347 if( p_vout->b_die || p_vout->b_error )
349 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
352 msleep( VOUT_OUTMEM_SLEEP );
355 /* 20ms is a bit arbitrary, but it's only for the first image we get */
356 if( !p_vout->p_sys->last_date )
358 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
359 p_pic->date + 20000 );
363 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
364 (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
366 p_vout->p_sys->last_date = p_pic->date;
369 switch( p_vout->p_sys->i_mode )
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] );
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] );
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] );
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] );
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] );
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 )
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 )
420 /* Copy image and skip lines */
421 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
423 u8 *p_in, *p_out_end, *p_out;
426 p_in = p_pic->p[i_plane].p_pixels
427 + i_field * p_pic->p[i_plane].i_pitch;
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;
433 switch( p_vout->render.i_chroma )
439 for( ; p_out < p_out_end ; )
441 p_vout->p_vlc->pf_memcpy( p_out, p_in,
442 p_pic->p[i_plane].i_pitch );
444 p_out += p_pic->p[i_plane].i_pitch;
445 p_in += 2 * p_pic->p[i_plane].i_pitch;
451 i_increment = 2 * p_pic->p[i_plane].i_pitch;
453 if( i_plane == Y_PLANE )
455 for( ; p_out < p_out_end ; )
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;
468 for( ; p_out < p_out_end ; )
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;
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 )
492 /* Copy image and skip lines */
493 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
495 u8 *p_in, *p_out_end, *p_out;
497 p_in = p_pic->p[i_plane].p_pixels
498 + i_field * p_pic->p[i_plane].i_pitch;
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;
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;
512 p_out_end -= p_outpic->p[i_plane].i_pitch;
514 for( ; p_out < p_out_end ; )
516 p_vout->p_vlc->pf_memcpy( p_out, p_in,
517 p_pic->p[i_plane].i_pitch );
519 p_out += p_pic->p[i_plane].i_pitch;
521 Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
522 p_pic->p[i_plane].i_pitch );
524 p_in += 2 * p_pic->p[i_plane].i_pitch;
525 p_out += p_pic->p[i_plane].i_pitch;
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 );
539 static void RenderMean( vout_thread_t *p_vout,
540 picture_t *p_outpic, picture_t *p_pic )
544 /* Copy image and skip lines */
545 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
547 u8 *p_in, *p_out_end, *p_out;
549 p_in = p_pic->p[i_plane].p_pixels;
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;
555 /* All lines: mean value */
556 for( ; p_out < p_out_end ; )
558 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
559 p_pic->p[i_plane].i_pitch );
561 p_out += p_pic->p[i_plane].i_pitch;
562 p_in += 2 * p_pic->p[i_plane].i_pitch;
567 static void RenderBlend( vout_thread_t *p_vout,
568 picture_t *p_outpic, picture_t *p_pic )
572 /* Copy image and skip lines */
573 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
575 u8 *p_in, *p_out_end, *p_out;
577 p_in = p_pic->p[i_plane].p_pixels;
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;
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;
588 /* Remaining lines: mean value */
589 for( ; p_out < p_out_end ; )
591 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
592 p_pic->p[i_plane].i_pitch );
594 p_out += p_pic->p[i_plane].i_pitch;
595 p_in += p_pic->p[i_plane].i_pitch;
600 static void Merge( void *p_dest, const void *p_s1,
601 const void *p_s2, size_t i_bytes )
603 u8* p_end = (u8*)p_dest + i_bytes - 8;
605 while( (u8*)p_dest < p_end )
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;
619 while( (u8*)p_dest < p_end )
621 *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;