Commit 6267b320 authored by Steve Lhomme's avatar Steve Lhomme
Browse files

deinterlace: implement draining of extra pictures

Each filter using the common deinterlacing code now calls DoDrain (similar to
DoDeinterlacing) to do its draining.

We no longer return pictures chained using vlc_picture_chain_AppendChain().

DoDraining calls DoDeinterlacing internally with a NULL input picture. To
create secondary pictures, the state to create the same pictures that were
appended before is kept in the filter p_sys. The prev_pic is the original
picture used to create the primary and secondary pictures. The picture is
released when there is no more pictures to produce from this picture or during
a Flush.

Now DoDeinterlacing() only outputs one picture at a time. By default it outputs
the current_field == 0 picture, then extra pictures up to max_fields.
parent d9c7e949
......@@ -174,6 +174,17 @@ static picture_t *Deinterlace(filter_t *p_filter, picture_t *p_pic)
return res;
}
static picture_t *Drain(filter_t *p_filter)
{
filter_sys_t *p_sys = p_filter->p_sys;
d3d11_device_lock( p_sys->d3d_dev );
picture_t *res = DoDraining( p_filter, &p_sys->context );
d3d11_device_unlock( p_sys->d3d_dev );
return res;
}
static const struct filter_mode_t *GetFilterMode(const char *mode)
{
if ( mode == NULL || !strcmp( mode, "auto" ) )
......@@ -244,6 +255,7 @@ static void D3D11CloseDeinterlace(filter_t *filter)
static const struct vlc_filter_operations filter_ops = {
.filter_video = Deinterlace, .flush = Flush, .close = D3D11CloseDeinterlace,
.drain_video = Drain,
};
int D3D11OpenDeinterlace(filter_t *filter)
......
......@@ -268,6 +268,12 @@ static picture_t *Deinterlace(filter_t *p_filter, picture_t *p_pic)
return DoDeinterlacing( p_filter, &p_sys->context, p_pic );
}
static picture_t *Drain(filter_t *p_filter)
{
filter_sys_t *p_sys = p_filter->p_sys;
return DoDraining( p_filter, &p_sys->context );
}
static const struct filter_mode_t *GetFilterMode(const char *mode)
{
if ( mode == NULL || !strcmp( mode, "auto" ) )
......@@ -337,6 +343,7 @@ static void D3D9CloseDeinterlace(filter_t *filter)
static const struct vlc_filter_operations filter_ops = {
.filter_video = Deinterlace, .flush = Flush, .close = D3D9CloseDeinterlace,
.drain_video = Drain,
};
int D3D9OpenDeinterlace(filter_t *filter)
......
......@@ -49,6 +49,10 @@ void InitDeinterlacingContext( struct deinterlace_ctx *p_context )
cannot have offset) */
for( int i = 0; i < HISTORY_SIZE; i++ )
p_context->pp_history[i] = NULL;
p_context->prev_pic = NULL;
p_context->current_field = 0;
p_context->max_fields = 0;
}
void FlushDeinterlacing(struct deinterlace_ctx *p_context)
......@@ -67,6 +71,11 @@ void FlushDeinterlacing(struct deinterlace_ctx *p_context)
picture_Release( p_context->pp_history[i] );
p_context->pp_history[i] = NULL;
}
if ( p_context->prev_pic )
{
picture_Release( p_context->prev_pic );
p_context->prev_pic = NULL;
}
}
vlc_tick_t GetFieldDuration(const struct deinterlace_ctx *p_context,
......@@ -127,7 +136,6 @@ void GetDeinterlacingOutput( const struct deinterlace_ctx *p_context,
picture_t *DoDeinterlacing( filter_t *p_filter,
struct deinterlace_ctx *p_context, picture_t *p_pic )
{
picture_t *p_dst[DEINTERLACE_DST_SIZE];
int i_double_rate_alloc_end;
/* Remember the frame offset that we should use for this frame.
The value in p_sys will be updated to reflect the correct value
......@@ -137,20 +145,32 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
bool b_top_field_first;
/* Request output picture */
p_dst[0] = AllocPicture( p_filter );
if( p_dst[0] == NULL )
if ( p_context->current_field == p_context->max_fields )
{
picture_Release( p_pic );
return NULL;
if( p_pic == NULL )
return NULL; // there's nothing else to drain
}
if ( p_context->current_field != 0 )
{
assert(p_pic == NULL); // we are draining the output
p_pic = p_context->prev_pic;
}
picture_CopyProperties( p_dst[0], p_pic );
/* Any unused p_dst pointers must be NULL, because they are used to
check how many output frames we have. */
for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i )
p_dst[i] = NULL;
/* Request output picture */
picture_t *p_dst = AllocPicture( p_filter );
if( p_dst == NULL )
{
if ( p_context->current_field != 0 )
msg_Err( p_filter, "Framerate doubler: could not allocate "\
"output frame %d", p_context->current_field+1 );
goto exit;
}
picture_CopyProperties( p_dst, p_pic );
p_dst->b_progressive = true;
p_dst->i_nb_fields = 2;
if ( p_context->current_field == 0 )
{
/* Update the input frame history, if the currently active algorithm
needs it. */
if( p_context->settings.b_use_frame_history )
......@@ -173,6 +193,7 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
p_context->meta[METADATA_SIZE-1].pi_date = p_pic->date;
p_context->meta[METADATA_SIZE-1].pi_nb_fields = p_pic->i_nb_fields;
p_context->meta[METADATA_SIZE-1].pb_top_field_first = p_pic->b_top_field_first;
}
/* Remember the frame offset that we should use for this frame.
The value in p_sys will be updated to reflect the correct value
......@@ -206,6 +227,8 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
i_nb_fields = p_pic->i_nb_fields; /* unused */
}
if ( p_context->current_field == 0 )
{
/* For framerate doublers, determine field duration and allocate
output frames. */
i_double_rate_alloc_end = 0; /* One past last for allocated output
......@@ -229,57 +252,39 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
i_nb_fields, DEINTERLACE_DST_SIZE );
i_double_rate_alloc_end = DEINTERLACE_DST_SIZE;
}
/* Allocate output frames. */
for( int i = 1; i < i_double_rate_alloc_end ; ++i )
{
p_dst[i] = AllocPicture( p_filter );
if( p_dst[i] )
{
vlc_picture_chain_AppendChain( p_dst[i-1], p_dst[i] );
picture_CopyProperties( p_dst[i], p_pic );
}
else
{
msg_Err( p_filter, "Framerate doubler: could not allocate "\
"output frame %d", i+1 );
i_double_rate_alloc_end = i; /* Inform the PTS logic about the
correct end position. */
break; /* If this happens, the rest of the allocations
aren't likely to work, either... */
}
}
/* Now we have allocated *up to* the correct number of frames;
normally, exactly the correct number. Upon alloc failure,
we may have succeeded in allocating *some* output frames,
but fewer than were desired. In such a case, as many will
be rendered as were successfully allocated.
Note that now p_dst[i] != NULL
for 0 <= i < i_double_rate_alloc_end. */
}
assert( p_context->settings.b_double_rate || p_dst[1] == NULL );
assert( i_nb_fields > 2 || p_dst[2] == NULL );
assert( p_context->settings.b_double_rate || i_double_rate_alloc_end == 0 );
assert( i_nb_fields > 2 || i_double_rate_alloc_end <= 2 );
p_context->max_fields = i_double_rate_alloc_end;
}
/* Render */
if ( !p_context->settings.b_double_rate )
{
if ( p_context->pf_render_single_pic( p_filter, p_dst[0], p_pic ) )
if ( p_context->pf_render_single_pic( p_filter, p_dst, p_pic ) )
goto drop;
}
else
{
/* Note: RenderIVTC will automatically drop the duplicate frames
produced by IVTC. This is part of normal operation. */
if ( p_context->pf_render_ordered( p_filter, p_dst[0], p_pic,
0, !b_top_field_first ) )
goto drop;
if ( p_dst[1] )
p_context->pf_render_ordered( p_filter, p_dst[1], p_pic,
1, b_top_field_first );
if ( p_dst[2] )
p_context->pf_render_ordered( p_filter, p_dst[2], p_pic,
2, !b_top_field_first );
if ( p_context->current_field == 0 )
{
assert(p_context->prev_pic == NULL);
p_context->prev_pic = p_pic;
if ( p_context->pf_render_ordered( p_filter, p_dst, p_pic,
0, !b_top_field_first ) )
goto drop;
}
else
{
if ( p_context->current_field == 1 )
p_context->pf_render_ordered( p_filter, p_dst, p_pic,
1, b_top_field_first );
else
p_context->pf_render_ordered( p_filter, p_dst, p_pic,
2, !b_top_field_first );
}
}
if ( p_context->settings.b_custom_pts )
......@@ -313,43 +318,33 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
b_double_rate = false), this effectively does nothing.
This is needed to correct the timestamp
when i_frame_offset > 0. */
p_dst[0]->date = i_base_pts;
p_dst->date = i_base_pts;
if( p_context->settings.b_double_rate )
if( p_context->current_field != 0 )
{
vlc_tick_t i_field_dur = GetFieldDuration( p_context, &p_filter->fmt_out.video, p_pic );
/* Processing all actually allocated output frames. */
for( int i = 1; i < i_double_rate_alloc_end; ++i )
/* XXX it's not really good especially for the first picture, but
* I don't think that delaying by one frame is worth it */
if( i_base_pts != VLC_TICK_INVALID )
{
/* XXX it's not really good especially for the first picture, but
* I don't think that delaying by one frame is worth it */
if( i_base_pts != VLC_TICK_INVALID )
p_dst[i]->date = i_base_pts + i * i_field_dur;
else
p_dst[i]->date = VLC_TICK_INVALID;
vlc_tick_t i_field_dur = GetFieldDuration( p_context, &p_filter->fmt_out.video, p_pic );
p_dst->date += p_context->current_field * i_field_dur;
}
}
}
for( int i = 0; i < DEINTERLACE_DST_SIZE; ++i )
if ( ++p_context->current_field >= p_context->max_fields )
{
if( p_dst[i] )
{
p_dst[i]->b_progressive = true;
p_dst[i]->i_nb_fields = 2;
}
picture_Release( p_pic );
p_context->prev_pic = NULL;
p_context->current_field = 0;
p_context->max_fields = 0;
}
picture_Release( p_pic );
return p_dst[0];
return p_dst;
drop:
picture_Release( p_dst[0] );
for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i )
{
if( p_dst[i] )
picture_Release( p_dst[i] );
}
picture_Release( p_dst );
exit:
#ifndef NDEBUG
picture_Release( p_pic );
return NULL;
......@@ -357,3 +352,8 @@ drop:
return p_pic;
#endif
}
picture_t *DoDraining( filter_t *p_filter, struct deinterlace_ctx *p_context )
{
return DoDeinterlacing( p_filter, p_context, NULL );
}
......@@ -88,6 +88,10 @@ struct deinterlace_ctx
int order, int i_field);
int (*pf_render_single_pic)(filter_t *, picture_t *p_dst, picture_t *p_pic);
};
int current_field;
int max_fields;
picture_t *prev_pic;
};
#define DEINTERLACE_DST_SIZE 3
......@@ -116,6 +120,7 @@ void GetDeinterlacingOutput( const struct deinterlace_ctx *,
* @return The deinterlaced picture or NULL if it failed
*/
picture_t *DoDeinterlacing( filter_t *, struct deinterlace_ctx *, picture_t * );
picture_t *DoDraining( filter_t *, struct deinterlace_ctx * );
/**
* @brief Flush the deinterlacer context
......
......@@ -444,6 +444,12 @@ picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
return DoDeinterlacing( p_filter, &p_sys->context, p_pic );
}
static picture_t *Drain(filter_t *p_filter )
{
filter_sys_t *p_sys = p_filter->p_sys;
return DoDraining( p_filter, &p_sys->context );
}
/*****************************************************************************
* Flush
*****************************************************************************/
......@@ -486,6 +492,7 @@ static void Close( filter_t *p_filter )
static const struct vlc_filter_operations filter_ops = {
.filter_video = Deinterlace,
.drain_video = Drain,
.flush = Flush,
.video_mouse = Mouse,
.close = Close,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment