deinterlace: Implement field history flushing

In a number of cases it is necessary to flush the field history by
performing 'degraded' deinterlacing - that is, using the user-chosen
method for as many fields as possible, then using vfir for as long as
there are >= 2 fields remaining in the history, then using linear for
the last field.

This should avoid losing fields being kept for history for example at
EOS.

This may address part of #633294
This commit is contained in:
Robert Swain 2010-11-05 17:00:15 +01:00 committed by Tim-Philipp Müller
parent 5a56274cba
commit 9be159b32c
2 changed files with 74 additions and 42 deletions

View file

@ -66,12 +66,6 @@ enum
PROP_LAST
};
#define GST_TYPE_DEINTERLACE_METHODS (gst_deinterlace_methods_get_type ())
static GType
gst_deinterlace_methods_get_type (void)
{
static GType deinterlace_methods_type = 0;
static const GEnumValue methods_types[] = {
{GST_DEINTERLACE_TOMSMOCOMP, "Motion Adaptive: Motion Search",
"tomsmocomp"},
@ -91,6 +85,13 @@ gst_deinterlace_methods_get_type (void)
{0, NULL, NULL},
};
#define GST_TYPE_DEINTERLACE_METHODS (gst_deinterlace_methods_get_type ())
static GType
gst_deinterlace_methods_get_type (void)
{
static GType deinterlace_methods_type = 0;
if (!deinterlace_methods_type) {
deinterlace_methods_type =
g_enum_register_static ("GstDeinterlaceMethods", methods_types);
@ -209,6 +210,8 @@ static gboolean gst_deinterlace_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_deinterlace_src_query (GstPad * pad, GstQuery * query);
static const GstQueryType *gst_deinterlace_src_query_types (GstPad * pad);
static GstFlowReturn gst_deinterlace_output_frame (GstDeinterlace * self,
gboolean flushing);
static void gst_deinterlace_reset (GstDeinterlace * self);
static void gst_deinterlace_update_qos (GstDeinterlace * self,
gdouble proportion, GstClockTimeDiff diff, GstClockTime time);
@ -575,7 +578,8 @@ gst_deinterlace_init (GstDeinterlace * self, GstDeinterlaceClass * klass)
gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
self->mode = DEFAULT_MODE;
gst_deinterlace_set_method (self, DEFAULT_METHOD);
self->user_set_method_id = DEFAULT_METHOD;
gst_deinterlace_set_method (self, self->user_set_method_id);
self->fields = DEFAULT_FIELDS;
self->field_layout = DEFAULT_FIELD_LAYOUT;
@ -585,11 +589,13 @@ gst_deinterlace_init (GstDeinterlace * self, GstDeinterlaceClass * klass)
}
static void
gst_deinterlace_reset_history (GstDeinterlace * self)
gst_deinterlace_reset_history (GstDeinterlace * self, gboolean drop_all)
{
gint i;
GST_DEBUG_OBJECT (self, "Resetting history");
if (drop_all) {
GST_DEBUG_OBJECT (self, "Resetting history (count %d)",
self->history_count);
for (i = 0; i < self->history_count; i++) {
if (self->field_history[i].buf) {
@ -597,14 +603,20 @@ gst_deinterlace_reset_history (GstDeinterlace * self)
self->field_history[i].buf = NULL;
}
}
} else {
GST_DEBUG_OBJECT (self, "Flushing history (count %d)", self->history_count);
while (self->history_count > 0)
gst_deinterlace_output_frame (self, TRUE);
}
memset (self->field_history, 0,
GST_DEINTERLACE_MAX_FIELD_HISTORY * sizeof (GstDeinterlaceField));
self->history_count = 0;
if (self->last_buffer)
if (!self->still_frame_mode && self->last_buffer) {
gst_buffer_unref (self->last_buffer);
self->last_buffer = NULL;
}
}
static void
gst_deinterlace_update_passthrough (GstDeinterlace * self)
@ -648,7 +660,7 @@ gst_deinterlace_reset (GstDeinterlace * self)
gst_caps_unref (self->request_caps);
self->request_caps = NULL;
gst_deinterlace_reset_history (self);
gst_deinterlace_reset_history (self, TRUE);
gst_deinterlace_reset_qos (self);
}
@ -679,7 +691,8 @@ gst_deinterlace_set_property (GObject * object, guint prop_id,
break;
}
case PROP_METHOD:
gst_deinterlace_set_method (self, g_value_get_enum (value));
self->user_set_method_id = g_value_get_enum (value);
gst_deinterlace_set_method (self, self->user_set_method_id);
break;
case PROP_FIELDS:{
gint new_fields;
@ -718,7 +731,7 @@ gst_deinterlace_get_property (GObject * object, guint prop_id,
g_value_set_enum (value, self->mode);
break;
case PROP_METHOD:
g_value_set_enum (value, self->method_id);
g_value_set_enum (value, self->user_set_method_id);
break;
case PROP_FIELDS:
g_value_set_enum (value, self->fields);
@ -941,7 +954,7 @@ gst_deinterlace_do_qos (GstDeinterlace * self, GstClockTime timestamp)
}
static GstFlowReturn
gst_deinterlace_output_frame (GstDeinterlace * self)
gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing)
{
GstClockTime timestamp;
GstFlowReturn ret = GST_FLOW_OK;
@ -949,14 +962,29 @@ gst_deinterlace_output_frame (GstDeinterlace * self)
gint cur_field_idx = 0;
GstBuffer *buf, *outbuf;
gst_deinterlace_set_method (self, self->user_set_method_id);
fields_required = gst_deinterlace_method_get_fields_required (self->method);
/* Not enough fields in the history */
if (self->history_count < fields_required) {
if (flushing) {
/* FIXME: if there are any methods implemented that output different
* dimensions (e.g. half height) that require more than one field of
* history, it is desirable to degrade to something that outputs
* half-height also */
gst_deinterlace_set_method (self,
self->history_count >= 2 ?
GST_DEINTERLACE_VFIR : GST_DEINTERLACE_LINEAR);
fields_required =
gst_deinterlace_method_get_fields_required (self->method);
GST_DEBUG_OBJECT (self, "Flushing field(s) using %s method",
methods_types[self->method_id].value_nick);
} else {
/* Not enough fields in the history */
GST_DEBUG_OBJECT (self, "Need more fields (have %d, need %d)",
self->history_count, fields_required);
return GST_FLOW_OK;
}
}
while (self->history_count >= fields_required) {
if (self->fields == GST_DEINTERLACE_ALL)
@ -1149,13 +1177,13 @@ gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
GST_DEBUG_OBJECT (self, "DISCONT buffer, resetting history");
gst_deinterlace_reset_history (self);
gst_deinterlace_reset_history (self, FALSE);
}
gst_deinterlace_push_history (self, buf);
buf = NULL;
return gst_deinterlace_output_frame (self);
return gst_deinterlace_output_frame (self, FALSE);
}
static gint
@ -1396,6 +1424,8 @@ gst_deinterlace_setcaps (GstPad * pad, GstCaps * caps)
gst_caps_set_simple (othercaps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
}
gst_deinterlace_reset_history (self, FALSE);
if (!gst_pad_set_caps (otherpad, othercaps))
goto caps_not_accepted;
@ -1473,7 +1503,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
}
gst_deinterlace_reset_qos (self);
gst_deinterlace_reset_history (self);
gst_deinterlace_reset_history (self, FALSE);
res = gst_pad_push_event (self->srcpad, event);
break;
}
@ -1489,6 +1519,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
GST_DEBUG_OBJECT (self, "Handling still frame");
self->still_frame_mode = TRUE;
gst_deinterlace_reset_history (self, FALSE);
if (self->last_buffer) {
ret =
gst_pad_push (self->srcpad, gst_buffer_ref (self->last_buffer));
@ -1505,7 +1536,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
}
/* fall through */
case GST_EVENT_EOS:
gst_deinterlace_reset_history (self);
gst_deinterlace_reset_history (self, FALSE);
/* fall through */
default:
@ -1519,7 +1550,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
}
gst_deinterlace_reset_qos (self);
res = gst_pad_push_event (self->srcpad, event);
gst_deinterlace_reset_history (self);
gst_deinterlace_reset_history (self, TRUE);
break;
}

View file

@ -92,7 +92,8 @@ struct _GstDeinterlace
GstDeinterlaceFields fields;
GstDeinterlaceMethods method_id;
GstDeinterlaceMethods method_id; /* current state (differs when flushing) */
GstDeinterlaceMethods user_set_method_id; /* property value */
GstDeinterlaceMethod *method;
GstVideoFormat format;