* deinterlace.c : deinterlacer plugin for vlc
*****************************************************************************
* Copyright (C) 2000, 2001 VideoLAN
- * $Id: deinterlace.c,v 1.14 2002/06/01 18:04:48 sam Exp $
+ * $Id: deinterlace.c,v 1.15 2002/06/09 16:52:58 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_BLEND 2
+#define DEINTERLACE_BOB 3
+#define DEINTERLACE_LINEAR 4
/*****************************************************************************
* 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 RenderBlend ( vout_thread_t *, picture_t *, picture_t * );
+static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
+
+static void MergeLinear ( void *, const void *, const void *, size_t );
+static void MergeBlend ( void *, 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 'bob' and 'blend'") )
+ADD_STRING ( "deinterlace-mode", "discard", NULL, N_("Deinterlace mode"),
+ N_("one of \"discard\", \"blend\", \"bob\" or \"linear\"") )
MODULE_CONFIG_STOP
MODULE_INIT_START
*****************************************************************************/
struct vout_sys_s
{
- int i_mode;
+ int i_mode; /* Deinterlace mode */
+ vlc_bool_t b_double_rate; /* Shall we double the framerate? */
+
+ mtime_t last_date;
+ mtime_t next_date;
+
vout_thread_t *p_vout;
- mtime_t last_date;
};
/*****************************************************************************
if( p_vout->p_sys == NULL )
{
msg_Err( p_vout, "out of memory" );
- return( 1 );
+ 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;
+
/* Look what method was requested */
psz_method = config_GetPsz( p_vout, "deinterlace-mode" );
{
msg_Err( p_vout, "configuration variable %s empty",
"deinterlace-mode" );
- msg_Err( p_vout, "no valid deinterlace mode provided, using 'bob'" );
- p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
+ msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
}
else
{
- if( !strcmp( psz_method, "bob" ) )
+ if( !strcmp( psz_method, "discard" ) )
+ {
+ p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
+ }
+ else if( !strcmp( psz_method, "blend" )
+ || !strcmp( psz_method, "average" )
+ || !strcmp( psz_method, "combine-fields" ) )
{
- p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
+ p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
}
- else if( !strcmp( psz_method, "blend" ) )
+ else if( !strcmp( psz_method, "bob" )
+ || !strcmp( psz_method, "progressive-scan" ) )
{
- p_vout->p_sys->i_mode = DEINTERLACE_MODE_BLEND;
+ 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
{
msg_Err( p_vout, "no valid deinterlace mode provided, "
- "using 'bob'" );
- p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
+ "using \"discard\"" );
}
- }
- free( psz_method );
+ free( psz_method );
+ }
- return( 0 );
+ return 0;
}
/*****************************************************************************
break;
default:
- return( 0 ); /* unknown chroma */
+ return 0; /* unknown chroma */
break;
}
case FOURCC_YV12:
switch( p_vout->p_sys->i_mode )
{
- case DEINTERLACE_MODE_BOB:
+ case DEINTERLACE_DISCARD:
+ case DEINTERLACE_BOB:
p_vout->p_sys->p_vout =
vout_CreateThread( p_vout,
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( p_vout,
p_vout->output.i_width, p_vout->output.i_height,
{
msg_Err( p_vout, "cannot open vout, aborting" );
- return( 0 );
+ return 0;
}
- p_vout->p_sys->last_date = 0;
-
ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
- return( 0 );
+ return 0;
}
/*****************************************************************************
*****************************************************************************/
static int vout_Manage( vout_thread_t *p_vout )
{
- return( 0 );
+ return 0;
}
/*****************************************************************************
*****************************************************************************/
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];
- p_vout->p_sys->last_date = p_pic->date;
+ /* 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 );
+ }
- for( i_field = 0 ; i_field < 2 ; i_field++ )
+ vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
+
+ /* 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 )
+ {
+ vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
+ p_pic->date + 20000 );
+ }
+ else
{
- u8 *p_in, *p_out_end, *p_out;
- int i_increment;
+ 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;
+ }
- p_in = p_pic->p[i_plane].p_pixels
- + i_field * p_pic->p[i_plane].i_pitch;
+ 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;
- 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;
+ 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;
- switch( p_vout->render.i_chroma )
- {
- case FOURCC_I420:
- case FOURCC_IYUV:
- case FOURCC_YV12:
+ 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;
- switch( p_vout->p_sys->i_mode )
- {
- case DEINTERLACE_MODE_BOB:
- for( ; p_out < p_out_end ; )
- {
- p_vout->p_vlc->pf_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_field == 0 )
- {
- p_vout->p_vlc->pf_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;
- }
-
- p_out_end -= p_outpic->p[i_plane].i_pitch;
-
- for( ; p_out < p_out_end ; )
- {
- p_vout->p_vlc->pf_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;
- }
+ case DEINTERLACE_BLEND:
+ RenderBlend( p_vout, pp_outpic[0], p_pic );
+ vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
+ break;
+ }
+}
-#if 0
- if( i_field == 0 )
- {
- p_in -= 2 * p_pic->p[i_plane].i_pitch;
- p_vout->p_vlc->pf_memcpy( p_out, p_in,
- p_pic->p[i_plane].i_pitch );
- }
-#endif
+/*****************************************************************************
+ * vout_Display: displays previously rendered output
+ *****************************************************************************
+ * This function does nothing, since all the rendering was already done.
+ *****************************************************************************/
+static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
+{
+ ;
+}
- break;
- }
- break;
+/*****************************************************************************
+ * 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_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 )
+ {
+ case FOURCC_I420:
+ case FOURCC_IYUV:
+ case FOURCC_YV12:
+
+ for( ; p_out < p_out_end ; )
+ {
+ p_vout->p_vlc->pf_memcpy( p_out, p_in,
+ p_pic->p[i_plane].i_pitch );
- case FOURCC_I422:
+ p_out += p_pic->p[i_plane].i_pitch;
+ p_in += 2 * p_pic->p[i_plane].i_pitch;
+ }
+ break;
+
+ 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 ; )
- {
- p_vout->p_vlc->pf_memcpy( p_out, p_in,
- p_pic->p[i_plane].i_pitch );
- p_out += p_pic->p[i_plane].i_pitch;
- p_vout->p_vlc->pf_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;
- }
+ p_vout->p_vlc->pf_memcpy( p_out, p_in,
+ p_pic->p[i_plane].i_pitch );
+ p_out += p_pic->p[i_plane].i_pitch;
+ p_vout->p_vlc->pf_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 ; )
- {
- p_vout->p_vlc->pf_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;
- }
+ p_vout->p_vlc->pf_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
+ + 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;
+
+ if( i_field == 0 )
+ {
+ p_vout->p_vlc->pf_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;
+ }
+
+ p_out_end -= p_outpic->p[i_plane].i_pitch;
+
+ for( ; p_out < p_out_end ; )
+ {
+ p_vout->p_vlc->pf_memcpy( p_out, p_in,
+ p_pic->p[i_plane].i_pitch );
+
+ p_out += p_pic->p[i_plane].i_pitch;
+
+ MergeLinear( 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;
+ }
+
+#if 0
+ if( i_field == 0 )
+ {
+ p_in -= 2 * p_pic->p[i_plane].i_pitch;
+ p_vout->p_vlc->pf_memcpy( p_out, p_in,
+ p_pic->p[i_plane].i_pitch );
+ }
+#endif
+ }
}
-static void *memblend( void *p_dest, const void *p_s1,
- const void *p_s2, size_t i_bytes )
+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;
+
+ for( ; p_out < p_out_end ; )
+ {
+ MergeBlend( p_out, p_out + p_outpic->p[i_plane].i_pitch,
+ p_in, p_in + p_pic->p[i_plane].i_pitch,
+ p_pic->p[i_plane].i_pitch );
+
+ p_out += 2 * p_pic->p[i_plane].i_pitch;
+ p_in += 2 * p_pic->p[i_plane].i_pitch;
+ }
+ }
+}
+
+static void MergeLinear( 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;
}
+}
+
+static void MergeBlend( void *p_dest1, void *p_dest2,
+ const void *p_s1, const void *p_s2, size_t i_bytes )
+{
+ u8* p_end1 = (u8*)p_dest1 + i_bytes - 8;
+ u8 i;
+
+ while( (u8*)p_dest1 < p_end1 )
+ {
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ }
- return p_dest;
+ p_end1 += 8;
+
+ while( (u8*)p_dest1 < p_end1 )
+ {
+ i = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
+ *(u8*)p_dest1++ = *(u8*)p_dest2++ = i;
+ }
}