* deinterlace.c : deinterlacer plugin for vlc
*****************************************************************************
* Copyright (C) 2000, 2001 VideoLAN
- * $Id: deinterlace.c,v 1.7 2002/03/11 07:23:09 gbazin Exp $
+ * $Id: deinterlace.c,v 1.12.2.2 2002/10/11 21:14:50 sam Exp $
*
* Authors: Samuel Hocevar <sam@zoy.org>
*
#include "filter_common.h"
-#define DEINTERLACE_MODE_BOB 1
-#define DEINTERLACE_MODE_BLEND 2
+#define DEINTERLACE_DISCARD 1
+#define DEINTERLACE_MEAN 2
+#define DEINTERLACE_BLEND 3
+#define DEINTERLACE_BOB 4
+#define DEINTERLACE_LINEAR 5
/*****************************************************************************
* Capabilities defined in the other files.
*****************************************************************************/
static void vout_getfunctions( function_list_t * p_function_list );
-static void *memblend( void *, const void *, const void *, size_t );
+static void RenderBob ( vout_thread_t *, picture_t *, picture_t *, int );
+static void RenderMean ( vout_thread_t *, picture_t *, picture_t * );
+static void RenderBlend ( vout_thread_t *, picture_t *, picture_t * );
+static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
+
+static void Merge ( void *, const void *, const void *, size_t );
/*****************************************************************************
* Build configuration tree.
*****************************************************************************/
MODULE_CONFIG_START
+ADD_CATEGORY_HINT( N_("Miscellaneous"), NULL )
+ADD_STRING ( "deinterlace-mode", "bob", NULL, N_("Deinterlace mode"),
+ N_("one of 'discard', 'blend', 'mean', 'bob' or 'linear'") )
MODULE_CONFIG_STOP
MODULE_INIT_START
- SET_DESCRIPTION( "deinterlacing module" )
+ SET_DESCRIPTION( _("deinterlacing module") )
/* Capability score set to 0 because we don't want to be spawned
* as a video output unless explicitly requested to */
ADD_CAPABILITY( VOUT, 0 )
*****************************************************************************/
typedef struct vout_sys_s
{
- int i_mode;
- struct vout_thread_s *p_vout;
- mtime_t last_date;
+ int i_mode; /* Deinterlace mode */
+ boolean_t b_double_rate; /* Shall we double the framerate? */
+
+ mtime_t last_date;
+ mtime_t next_date;
+
+ vout_thread_t *p_vout;
} vout_sys_t;
*****************************************************************************/
static int vout_Create( vout_thread_t *p_vout )
{
- char *psz_method, *psz_method_tmp;
+ char *psz_method;
/* Allocate structure */
p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
return( 1 );
}
- /* Look what method was requested */
- if( !(psz_method = psz_method_tmp
- = config_GetPszVariable( "filter" )) )
- {
- intf_ErrMsg( "vout error: configuration variable %s empty",
- "filter" );
- return( 1 );
- }
+ p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
+ p_vout->p_sys->b_double_rate = 0;
+ p_vout->p_sys->last_date = 0;
- while( *psz_method && *psz_method != ':' )
- {
- psz_method++;
- }
+ /* Look what method was requested */
+ psz_method = config_GetPszVariable( "deinterlace-mode" );
- if( !strcmp( psz_method, ":bob" ) )
- {
- p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
- }
- else if( !strcmp( psz_method, ":blend" ) )
+ if( psz_method == NULL )
{
- p_vout->p_sys->i_mode = DEINTERLACE_MODE_BLEND;
+ intf_ErrMsg( "vout error: configuration variable %s empty",
+ "deinterlace-mode" );
+ intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
+ "using deinterlace:discard" );
}
else
{
- intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
- "using deinterlace:bob" );
- p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
- }
+ if( !strcmp( psz_method, "discard" ) )
+ {
+ p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
+ }
+ else if( !strcmp( psz_method, "mean" ) )
+ {
+ p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
+ }
+ else if( !strcmp( psz_method, "blend" )
+ || !strcmp( psz_method, "average" )
+ || !strcmp( psz_method, "combine-fields" ) )
+ {
+ p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
+ }
+ else if( !strcmp( psz_method, "bob" )
+ || !strcmp( psz_method, "progressive-scan" ) )
+ {
+ p_vout->p_sys->i_mode = DEINTERLACE_BOB;
+ p_vout->p_sys->b_double_rate = 1;
+ }
+ else if( !strcmp( psz_method, "linear" ) )
+ {
+ p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
+ p_vout->p_sys->b_double_rate = 1;
+ }
+ else
+ {
+ intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
+ "using deinterlace:discard" );
+ }
- free( psz_method_tmp );
+ free( psz_method );
+ }
return( 0 );
}
case FOURCC_YV12:
switch( p_vout->p_sys->i_mode )
{
- case DEINTERLACE_MODE_BOB:
+ case DEINTERLACE_BOB:
+ case DEINTERLACE_MEAN:
+ case DEINTERLACE_DISCARD:
p_vout->p_sys->p_vout =
vout_CreateThread( NULL,
p_vout->output.i_width, p_vout->output.i_height / 2,
p_vout->output.i_chroma, p_vout->output.i_aspect );
break;
- case DEINTERLACE_MODE_BLEND:
+ case DEINTERLACE_BLEND:
+ case DEINTERLACE_LINEAR:
p_vout->p_sys->p_vout =
vout_CreateThread( NULL,
p_vout->output.i_width, p_vout->output.i_height,
return( 0 );
}
- p_vout->p_sys->last_date = 0;
-
ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
return( 0 );
for( i_index = I_OUTPUTPICTURES ; i_index ; )
{
i_index--;
- free( PP_OUTPUTPICTURE[ i_index ]->p_data );
+ free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
}
}
*****************************************************************************/
static void vout_Render ( vout_thread_t *p_vout, picture_t *p_pic )
{
- picture_t *p_outpic;
- int i_plane, i_field;
- /* 20ms is a bit arbitrary, but it's only for the first image we get */
- mtime_t new_date = p_vout->p_sys->last_date
- ? ( 3 * p_pic->date - p_vout->p_sys->last_date ) / 2
- : p_pic->date + 20000;
+ picture_t *pp_outpic[2];
+
+ /* Get a new picture */
+ while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
+ 0, 0, 0 ) )
+ == NULL )
+ {
+ if( p_vout->b_die || p_vout->b_error )
+ {
+ return;
+ }
+ msleep( VOUT_OUTMEM_SLEEP );
+ }
- p_vout->p_sys->last_date = p_pic->date;
+ vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
- for( i_field = 0 ; i_field < 2 ; i_field++ )
+ /* If we are using double rate, get an additional new picture */
+ if( p_vout->p_sys->b_double_rate )
{
- /* Get a structure from the video_output. */
- while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout,
- 0, 0, 0 ) )
+ while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
+ 0, 0, 0 ) )
== NULL )
{
if( p_vout->b_die || p_vout->b_error )
{
+ vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
return;
}
msleep( VOUT_OUTMEM_SLEEP );
- }
-
- vout_DatePicture( p_vout->p_sys->p_vout, p_outpic,
- p_pic->date + i_field ? new_date : p_pic->date );
+ }
- /* Copy image and skip lines */
- for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
+ /* 20ms is a bit arbitrary, but it's only for the first image we get */
+ if( !p_vout->p_sys->last_date )
{
- u8 *p_in, *p_out_end, *p_out;
- int i_increment;
+ vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
+ p_pic->date + 20000 );
+ }
+ else
+ {
+ vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
+ (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
+ }
+ p_vout->p_sys->last_date = p_pic->date;
+ }
+
+ switch( p_vout->p_sys->i_mode )
+ {
+ case DEINTERLACE_DISCARD:
+ RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
+ vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
+ break;
+
+ case DEINTERLACE_BOB:
+ RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
+ vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
+ RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
+ vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
+ break;
+
+ case DEINTERLACE_LINEAR:
+ RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
+ vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
+ RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
+ vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
+ break;
+
+ case DEINTERLACE_MEAN:
+ RenderMean( p_vout, pp_outpic[0], p_pic );
+ vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
+ break;
+
+ case DEINTERLACE_BLEND:
+ RenderBlend( p_vout, pp_outpic[0], p_pic );
+ vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
+ break;
+ }
+}
+
+/*****************************************************************************
+ * vout_Display: displays previously rendered output
+ *****************************************************************************
+ * This function send the currently rendered image to Invert image, waits
+ * until it is displayed and switch the two rendering buffers, preparing next
+ * frame.
+ *****************************************************************************/
+static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
+{
+ ;
+}
+
+/*****************************************************************************
+ * RenderBob: renders a bob picture
+ *****************************************************************************/
+static void RenderBob( vout_thread_t *p_vout,
+ picture_t *p_outpic, picture_t *p_pic, int i_field )
+{
+ int i_plane;
+
+ /* Copy image and skip lines */
+ for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
+ {
+ u8 *p_in, *p_out_end, *p_out;
+ int i_increment;
- p_in = p_pic->p[i_plane].p_pixels
- + i_field * p_pic->p[i_plane].i_pitch;
+ p_in = p_pic->p[i_plane].p_pixels
+ + i_field * p_pic->p[i_plane].i_pitch;
- p_out = p_outpic->p[i_plane].p_pixels;
- p_out_end = p_out + p_outpic->p[i_plane].i_pitch
- * p_outpic->p[i_plane].i_lines;
+ p_out = p_outpic->p[i_plane].p_pixels;
+ p_out_end = p_out + p_outpic->p[i_plane].i_pitch
+ * p_outpic->p[i_plane].i_lines;
- switch( p_vout->render.i_chroma )
+ switch( p_vout->render.i_chroma )
+ {
+ case FOURCC_I420:
+ case FOURCC_IYUV:
+ case FOURCC_YV12:
+
+ for( ; p_out < p_out_end ; )
{
- case FOURCC_I420:
- case FOURCC_IYUV:
- case FOURCC_YV12:
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
- switch( p_vout->p_sys->i_mode )
- {
- case DEINTERLACE_MODE_BOB:
- for( ; p_out < p_out_end ; )
- {
- FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
-
- p_out += p_pic->p[i_plane].i_pitch;
- p_in += 2 * p_pic->p[i_plane].i_pitch;
- }
- break;
-
- case DEINTERLACE_MODE_BLEND:
- if( i_plane != Y_PLANE )
- {
- for( ; p_out < p_out_end ; )
- {
- FAST_MEMCPY( p_out, p_in,
- p_pic->p[i_plane].i_pitch );
-
- p_out += p_pic->p[i_plane].i_pitch;
-
- FAST_MEMCPY( p_out, p_in,
- p_pic->p[i_plane].i_pitch );
-
- p_out += p_pic->p[i_plane].i_pitch;
- p_in += 2 * p_pic->p[i_plane].i_pitch;
- }
- break;
- }
-
- if( i_field == 0 )
- {
- FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
- p_in += 2 * p_pic->p[i_plane].i_pitch;
- p_out += p_pic->p[i_plane].i_pitch;
- }
-
- for( ; p_out < p_out_end ; )
- {
- FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
-
- p_out += p_pic->p[i_plane].i_pitch;
-
- memblend( p_out, p_in,
- p_in + 2 * p_pic->p[i_plane].i_pitch,
- p_pic->p[i_plane].i_pitch );
-
- p_in += 2 * p_pic->p[i_plane].i_pitch;
- p_out += p_pic->p[i_plane].i_pitch;
- }
- break;
- }
- break;
+ p_out += p_pic->p[i_plane].i_pitch;
+ p_in += 2 * p_pic->p[i_plane].i_pitch;
+ }
+ break;
- case FOURCC_I422:
+ case FOURCC_I422:
- i_increment = 2 * p_pic->p[i_plane].i_pitch;
+ i_increment = 2 * p_pic->p[i_plane].i_pitch;
- if( i_plane == Y_PLANE )
+ if( i_plane == Y_PLANE )
+ {
+ for( ; p_out < p_out_end ; )
{
- for( ; p_out < p_out_end ; )
- {
- FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
- p_out += p_pic->p[i_plane].i_pitch;
- FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
- p_out += p_pic->p[i_plane].i_pitch;
- p_in += i_increment;
- }
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
+ p_out += p_pic->p[i_plane].i_pitch;
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
+ p_out += p_pic->p[i_plane].i_pitch;
+ p_in += i_increment;
}
- else
+ }
+ else
+ {
+ for( ; p_out < p_out_end ; )
{
- for( ; p_out < p_out_end ; )
- {
- FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
- p_out += p_pic->p[i_plane].i_pitch;
- p_in += i_increment;
- }
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
+ p_out += p_pic->p[i_plane].i_pitch;
+ p_in += i_increment;
}
- break;
-
- default:
- break;
}
- }
+ break;
- vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
+ default:
+ break;
+ }
}
}
/*****************************************************************************
- * vout_Display: displays previously rendered output
- *****************************************************************************
- * This function send the currently rendered image to Invert image, waits
- * until it is displayed and switch the two rendering buffers, preparing next
- * frame.
+ * RenderLinear: displays previously rendered output
*****************************************************************************/
-static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
+static void RenderLinear( vout_thread_t *p_vout,
+ picture_t *p_outpic, picture_t *p_pic, int i_field )
{
- ;
+ int i_plane;
+
+ /* Copy image and skip lines */
+ for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
+ {
+ u8 *p_in, *p_out_end, *p_out;
+
+ p_in = p_pic->p[i_plane].p_pixels;
+ p_out = p_outpic->p[i_plane].p_pixels;
+ p_out_end = p_out + p_outpic->p[i_plane].i_pitch
+ * p_outpic->p[i_plane].i_lines;
+
+ /* For BOTTOM field we need to add the first line */
+ if( i_field == 1 )
+ {
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
+ p_in += p_pic->p[i_plane].i_pitch;
+ p_out += p_pic->p[i_plane].i_pitch;
+ }
+
+ p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
+
+ for( ; p_out < p_out_end ; )
+ {
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
+
+ p_out += p_pic->p[i_plane].i_pitch;
+
+ Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
+ p_pic->p[i_plane].i_pitch );
+
+ p_in += 2 * p_pic->p[i_plane].i_pitch;
+ p_out += p_pic->p[i_plane].i_pitch;
+ }
+
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
+
+ /* For TOP field we need to add the last line */
+ if( i_field == 0 )
+ {
+ p_in += p_pic->p[i_plane].i_pitch;
+ p_out += p_pic->p[i_plane].i_pitch;
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
+ }
+ }
+}
+
+static void RenderMean( vout_thread_t *p_vout,
+ picture_t *p_outpic, picture_t *p_pic )
+{
+ int i_plane;
+
+ /* Copy image and skip lines */
+ for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
+ {
+ u8 *p_in, *p_out_end, *p_out;
+
+ p_in = p_pic->p[i_plane].p_pixels;
+
+ p_out = p_outpic->p[i_plane].p_pixels;
+ p_out_end = p_out + p_outpic->p[i_plane].i_pitch
+ * p_outpic->p[i_plane].i_lines;
+
+ /* All lines: mean value */
+ for( ; p_out < p_out_end ; )
+ {
+ Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
+ p_pic->p[i_plane].i_pitch );
+
+ p_out += p_pic->p[i_plane].i_pitch;
+ p_in += 2 * p_pic->p[i_plane].i_pitch;
+ }
+ }
+}
+
+static void RenderBlend( vout_thread_t *p_vout,
+ picture_t *p_outpic, picture_t *p_pic )
+{
+ int i_plane;
+
+ /* Copy image and skip lines */
+ for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
+ {
+ u8 *p_in, *p_out_end, *p_out;
+
+ p_in = p_pic->p[i_plane].p_pixels;
+
+ p_out = p_outpic->p[i_plane].p_pixels;
+ p_out_end = p_out + p_outpic->p[i_plane].i_pitch
+ * p_outpic->p[i_plane].i_lines;
+
+ /* First line: simple copy */
+ FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
+ p_out += p_pic->p[i_plane].i_pitch;
+
+ /* Remaining lines: mean value */
+ for( ; p_out < p_out_end ; )
+ {
+ Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
+ p_pic->p[i_plane].i_pitch );
+
+ p_out += p_pic->p[i_plane].i_pitch;
+ p_in += p_pic->p[i_plane].i_pitch;
+ }
+ }
}
-static void *memblend( void *p_dest, const void *p_s1,
- const void *p_s2, size_t i_bytes )
+static void Merge( void *p_dest, const void *p_s1,
+ const void *p_s2, size_t i_bytes )
{
u8* p_end = (u8*)p_dest + i_bytes - 8;
{
*(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
}
-
- return p_dest;
}