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,31 +66,32 @@ enum
PROP_LAST PROP_LAST
}; };
static const GEnumValue methods_types[] = {
{GST_DEINTERLACE_TOMSMOCOMP, "Motion Adaptive: Motion Search",
"tomsmocomp"},
{GST_DEINTERLACE_GREEDY_H, "Motion Adaptive: Advanced Detection",
"greedyh"},
{GST_DEINTERLACE_GREEDY_L, "Motion Adaptive: Simple Detection", "greedyl"},
{GST_DEINTERLACE_VFIR, "Blur Vertical", "vfir"},
{GST_DEINTERLACE_LINEAR, "Television: Full resolution", "linear"},
{GST_DEINTERLACE_LINEAR_BLEND, "Blur: Temporal (Do Not Use)",
"linearblend"},
{GST_DEINTERLACE_SCALER_BOB, "Double lines", "scalerbob"},
{GST_DEINTERLACE_WEAVE, "Weave (Do Not Use)", "weave"},
{GST_DEINTERLACE_WEAVE_TFF, "Progressive: Top Field First (Do Not Use)",
"weavetff"},
{GST_DEINTERLACE_WEAVE_BFF, "Progressive: Bottom Field First (Do Not Use)",
"weavebff"},
{0, NULL, NULL},
};
#define GST_TYPE_DEINTERLACE_METHODS (gst_deinterlace_methods_get_type ()) #define GST_TYPE_DEINTERLACE_METHODS (gst_deinterlace_methods_get_type ())
static GType static GType
gst_deinterlace_methods_get_type (void) gst_deinterlace_methods_get_type (void)
{ {
static GType deinterlace_methods_type = 0; static GType deinterlace_methods_type = 0;
static const GEnumValue methods_types[] = {
{GST_DEINTERLACE_TOMSMOCOMP, "Motion Adaptive: Motion Search",
"tomsmocomp"},
{GST_DEINTERLACE_GREEDY_H, "Motion Adaptive: Advanced Detection",
"greedyh"},
{GST_DEINTERLACE_GREEDY_L, "Motion Adaptive: Simple Detection", "greedyl"},
{GST_DEINTERLACE_VFIR, "Blur Vertical", "vfir"},
{GST_DEINTERLACE_LINEAR, "Television: Full resolution", "linear"},
{GST_DEINTERLACE_LINEAR_BLEND, "Blur: Temporal (Do Not Use)",
"linearblend"},
{GST_DEINTERLACE_SCALER_BOB, "Double lines", "scalerbob"},
{GST_DEINTERLACE_WEAVE, "Weave (Do Not Use)", "weave"},
{GST_DEINTERLACE_WEAVE_TFF, "Progressive: Top Field First (Do Not Use)",
"weavetff"},
{GST_DEINTERLACE_WEAVE_BFF, "Progressive: Bottom Field First (Do Not Use)",
"weavebff"},
{0, NULL, NULL},
};
if (!deinterlace_methods_type) { if (!deinterlace_methods_type) {
deinterlace_methods_type = deinterlace_methods_type =
g_enum_register_static ("GstDeinterlaceMethods", methods_types); 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 gboolean gst_deinterlace_src_query (GstPad * pad, GstQuery * query);
static const GstQueryType *gst_deinterlace_src_query_types (GstPad * pad); 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_reset (GstDeinterlace * self);
static void gst_deinterlace_update_qos (GstDeinterlace * self, static void gst_deinterlace_update_qos (GstDeinterlace * self,
gdouble proportion, GstClockTimeDiff diff, GstClockTime time); 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); gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
self->mode = DEFAULT_MODE; 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->fields = DEFAULT_FIELDS;
self->field_layout = DEFAULT_FIELD_LAYOUT; self->field_layout = DEFAULT_FIELD_LAYOUT;
@ -585,25 +589,33 @@ gst_deinterlace_init (GstDeinterlace * self, GstDeinterlaceClass * klass)
} }
static void static void
gst_deinterlace_reset_history (GstDeinterlace * self) gst_deinterlace_reset_history (GstDeinterlace * self, gboolean drop_all)
{ {
gint i; 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++) { for (i = 0; i < self->history_count; i++) {
if (self->field_history[i].buf) { if (self->field_history[i].buf) {
gst_buffer_unref (self->field_history[i].buf); gst_buffer_unref (self->field_history[i].buf);
self->field_history[i].buf = NULL; 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, memset (self->field_history, 0,
GST_DEINTERLACE_MAX_FIELD_HISTORY * sizeof (GstDeinterlaceField)); GST_DEINTERLACE_MAX_FIELD_HISTORY * sizeof (GstDeinterlaceField));
self->history_count = 0; self->history_count = 0;
if (self->last_buffer) if (!self->still_frame_mode && self->last_buffer) {
gst_buffer_unref (self->last_buffer); gst_buffer_unref (self->last_buffer);
self->last_buffer = NULL; self->last_buffer = NULL;
}
} }
static void static void
@ -648,7 +660,7 @@ gst_deinterlace_reset (GstDeinterlace * self)
gst_caps_unref (self->request_caps); gst_caps_unref (self->request_caps);
self->request_caps = NULL; self->request_caps = NULL;
gst_deinterlace_reset_history (self); gst_deinterlace_reset_history (self, TRUE);
gst_deinterlace_reset_qos (self); gst_deinterlace_reset_qos (self);
} }
@ -679,7 +691,8 @@ gst_deinterlace_set_property (GObject * object, guint prop_id,
break; break;
} }
case PROP_METHOD: 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; break;
case PROP_FIELDS:{ case PROP_FIELDS:{
gint new_fields; gint new_fields;
@ -718,7 +731,7 @@ gst_deinterlace_get_property (GObject * object, guint prop_id,
g_value_set_enum (value, self->mode); g_value_set_enum (value, self->mode);
break; break;
case PROP_METHOD: case PROP_METHOD:
g_value_set_enum (value, self->method_id); g_value_set_enum (value, self->user_set_method_id);
break; break;
case PROP_FIELDS: case PROP_FIELDS:
g_value_set_enum (value, self->fields); g_value_set_enum (value, self->fields);
@ -941,7 +954,7 @@ gst_deinterlace_do_qos (GstDeinterlace * self, GstClockTime timestamp)
} }
static GstFlowReturn static GstFlowReturn
gst_deinterlace_output_frame (GstDeinterlace * self) gst_deinterlace_output_frame (GstDeinterlace * self, gboolean flushing)
{ {
GstClockTime timestamp; GstClockTime timestamp;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
@ -949,13 +962,28 @@ gst_deinterlace_output_frame (GstDeinterlace * self)
gint cur_field_idx = 0; gint cur_field_idx = 0;
GstBuffer *buf, *outbuf; GstBuffer *buf, *outbuf;
gst_deinterlace_set_method (self, self->user_set_method_id);
fields_required = gst_deinterlace_method_get_fields_required (self->method); fields_required = gst_deinterlace_method_get_fields_required (self->method);
/* Not enough fields in the history */
if (self->history_count < fields_required) { if (self->history_count < fields_required) {
GST_DEBUG_OBJECT (self, "Need more fields (have %d, need %d)", if (flushing) {
self->history_count, fields_required); /* FIXME: if there are any methods implemented that output different
return GST_FLOW_OK; * 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) { while (self->history_count >= fields_required) {
@ -1149,13 +1177,13 @@ gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) { if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
GST_DEBUG_OBJECT (self, "DISCONT buffer, resetting history"); 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); gst_deinterlace_push_history (self, buf);
buf = NULL; buf = NULL;
return gst_deinterlace_output_frame (self); return gst_deinterlace_output_frame (self, FALSE);
} }
static gint 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_caps_set_simple (othercaps, "interlaced", G_TYPE_BOOLEAN, FALSE, NULL);
} }
gst_deinterlace_reset_history (self, FALSE);
if (!gst_pad_set_caps (otherpad, othercaps)) if (!gst_pad_set_caps (otherpad, othercaps))
goto caps_not_accepted; goto caps_not_accepted;
@ -1473,7 +1503,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
} }
gst_deinterlace_reset_qos (self); gst_deinterlace_reset_qos (self);
gst_deinterlace_reset_history (self); gst_deinterlace_reset_history (self, FALSE);
res = gst_pad_push_event (self->srcpad, event); res = gst_pad_push_event (self->srcpad, event);
break; break;
} }
@ -1489,6 +1519,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
GST_DEBUG_OBJECT (self, "Handling still frame"); GST_DEBUG_OBJECT (self, "Handling still frame");
self->still_frame_mode = TRUE; self->still_frame_mode = TRUE;
gst_deinterlace_reset_history (self, FALSE);
if (self->last_buffer) { if (self->last_buffer) {
ret = ret =
gst_pad_push (self->srcpad, gst_buffer_ref (self->last_buffer)); 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 */ /* fall through */
case GST_EVENT_EOS: case GST_EVENT_EOS:
gst_deinterlace_reset_history (self); gst_deinterlace_reset_history (self, FALSE);
/* fall through */ /* fall through */
default: default:
@ -1519,7 +1550,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
} }
gst_deinterlace_reset_qos (self); gst_deinterlace_reset_qos (self);
res = gst_pad_push_event (self->srcpad, event); res = gst_pad_push_event (self->srcpad, event);
gst_deinterlace_reset_history (self); gst_deinterlace_reset_history (self, TRUE);
break; break;
} }

View file

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