summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Swain <robert.swain@collabora.co.uk>2010-11-05 17:00:15 +0100
committerTim-Philipp Müller <tim.muller@collabora.co.uk>2010-11-12 10:40:51 +0000
commit9be159b32c9f14634a975b2df250b61702958194 (patch)
treeaa50e23f0b47d2a274a9e9acb032a5b8a34e819d
parent5a56274cba13df5831fe15746792be433fc1baf3 (diff)
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
-rw-r--r--gst/deinterlace/gstdeinterlace.c113
-rw-r--r--gst/deinterlace/gstdeinterlace.h3
2 files changed, 74 insertions, 42 deletions
diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c
index 87380419b..bca90ec05 100644
--- a/gst/deinterlace/gstdeinterlace.c
+++ b/gst/deinterlace/gstdeinterlace.c
@@ -66,31 +66,32 @@ enum
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 ())
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"},
- {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) {
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,25 +589,33 @@ 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) {
- gst_buffer_unref (self->field_history[i].buf);
- self->field_history[i].buf = NULL;
+ for (i = 0; i < self->history_count; i++) {
+ if (self->field_history[i].buf) {
+ gst_buffer_unref (self->field_history[i].buf);
+ 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;
+ self->last_buffer = NULL;
+ }
}
static void
@@ -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,13 +962,28 @@ 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) {
- GST_DEBUG_OBJECT (self, "Need more fields (have %d, need %d)",
- self->history_count, fields_required);
- return GST_FLOW_OK;
+ 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) {
@@ -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;
}
diff --git a/gst/deinterlace/gstdeinterlace.h b/gst/deinterlace/gstdeinterlace.h
index a457c84c5..6641da208 100644
--- a/gst/deinterlace/gstdeinterlace.h
+++ b/gst/deinterlace/gstdeinterlace.h
@@ -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;