mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 21:28:45 +00:00
deinterlace: Implement basic QoS
This change is based on Tim's QoS implementation for jpegdec.
This commit is contained in:
parent
049722b680
commit
d0a5c0f3e3
2 changed files with 158 additions and 19 deletions
|
@ -394,6 +394,11 @@ 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 void gst_deinterlace_reset (GstDeinterlace * self);
|
static void gst_deinterlace_reset (GstDeinterlace * self);
|
||||||
|
static void gst_deinterlace_update_qos (GstDeinterlace * self,
|
||||||
|
gdouble proportion, GstClockTime diff, GstClockTime time);
|
||||||
|
static void gst_deinterlace_reset_qos (GstDeinterlace * self);
|
||||||
|
static void gst_deinterlace_read_qos (GstDeinterlace * self,
|
||||||
|
gdouble * proportion, GstClockTime * time);
|
||||||
|
|
||||||
static void gst_deinterlace_child_proxy_interface_init (gpointer g_iface,
|
static void gst_deinterlace_child_proxy_interface_init (gpointer g_iface,
|
||||||
gpointer iface_data);
|
gpointer iface_data);
|
||||||
|
@ -725,7 +730,11 @@ gst_deinterlace_reset (GstDeinterlace * self)
|
||||||
self->field_height = 0;
|
self->field_height = 0;
|
||||||
self->field_stride = 0;
|
self->field_stride = 0;
|
||||||
|
|
||||||
|
gst_segment_init (&self->segment, GST_FORMAT_TIME);
|
||||||
|
|
||||||
gst_deinterlace_reset_history (self);
|
gst_deinterlace_reset_history (self);
|
||||||
|
|
||||||
|
gst_deinterlace_reset_qos (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -927,6 +936,79 @@ gst_deinterlace_push_history (GstDeinterlace * self, GstBuffer * buffer)
|
||||||
self->last_buffer = buffer;
|
self->last_buffer = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_deinterlace_update_qos (GstDeinterlace * self, gdouble proportion,
|
||||||
|
GstClockTime diff, GstClockTime timestamp)
|
||||||
|
{
|
||||||
|
GST_OBJECT_LOCK (self);
|
||||||
|
self->proportion = proportion;
|
||||||
|
if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
|
||||||
|
if (G_UNLIKELY (diff > 0))
|
||||||
|
self->earliest_time = timestamp + 2 * diff + self->field_duration;
|
||||||
|
else
|
||||||
|
self->earliest_time = timestamp + diff;
|
||||||
|
} else {
|
||||||
|
self->earliest_time = GST_CLOCK_TIME_NONE;
|
||||||
|
}
|
||||||
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_deinterlace_reset_qos (GstDeinterlace * self)
|
||||||
|
{
|
||||||
|
gst_deinterlace_update_qos (self, 0.5, 0, GST_CLOCK_TIME_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_deinterlace_read_qos (GstDeinterlace * self, gdouble * proportion,
|
||||||
|
GstClockTime * time)
|
||||||
|
{
|
||||||
|
GST_OBJECT_LOCK (self);
|
||||||
|
*proportion = self->proportion;
|
||||||
|
*time = self->earliest_time;
|
||||||
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform qos calculations before processing the next frame. Returns TRUE if
|
||||||
|
* the frame should be processed, FALSE if the frame can be dropped entirely */
|
||||||
|
static gboolean
|
||||||
|
gst_deinterlace_do_qos (GstDeinterlace * self, GstClockTime timestamp)
|
||||||
|
{
|
||||||
|
GstClockTime qostime, earliest_time;
|
||||||
|
gdouble proportion;
|
||||||
|
|
||||||
|
/* no timestamp, can't do QoS => process frame */
|
||||||
|
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
|
||||||
|
GST_LOG_OBJECT (self, "invalid timestamp, can't do QoS, process frame");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get latest QoS observation values */
|
||||||
|
gst_deinterlace_read_qos (self, &proportion, &earliest_time);
|
||||||
|
|
||||||
|
/* skip qos if we have no observation (yet) => process frame */
|
||||||
|
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
|
||||||
|
GST_LOG_OBJECT (self, "no observation yet, process frame");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* qos is done on running time */
|
||||||
|
qostime = gst_segment_to_running_time (&self->segment, GST_FORMAT_TIME,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
|
/* see how our next timestamp relates to the latest qos timestamp */
|
||||||
|
GST_LOG_OBJECT (self, "qostime %" GST_TIME_FORMAT ", earliest %"
|
||||||
|
GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
|
||||||
|
|
||||||
|
if (qostime != GST_CLOCK_TIME_NONE && qostime <= earliest_time) {
|
||||||
|
GST_DEBUG_OBJECT (self, "we are late, drop frame");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (self, "process frame");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
|
gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
|
@ -983,9 +1065,6 @@ gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
|
||||||
if (ret != GST_FLOW_OK)
|
if (ret != GST_FLOW_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* do magic calculus */
|
|
||||||
gst_deinterlace_method_deinterlace_frame (self->method, self, outbuf);
|
|
||||||
|
|
||||||
g_assert (self->history_count - 1 -
|
g_assert (self->history_count - 1 -
|
||||||
gst_deinterlace_method_get_latency (self->method) >= 0);
|
gst_deinterlace_method_get_latency (self->method) >= 0);
|
||||||
buf =
|
buf =
|
||||||
|
@ -993,18 +1072,29 @@ gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
|
||||||
gst_deinterlace_method_get_latency (self->method)].buf;
|
gst_deinterlace_method_get_latency (self->method)].buf;
|
||||||
timestamp = GST_BUFFER_TIMESTAMP (buf);
|
timestamp = GST_BUFFER_TIMESTAMP (buf);
|
||||||
|
|
||||||
gst_buffer_unref (gst_deinterlace_pop_history (self));
|
|
||||||
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
|
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
|
||||||
if (self->fields == GST_DEINTERLACE_ALL)
|
if (self->fields == GST_DEINTERLACE_ALL)
|
||||||
GST_BUFFER_DURATION (outbuf) = self->field_duration;
|
GST_BUFFER_DURATION (outbuf) = self->field_duration;
|
||||||
else
|
else
|
||||||
GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
|
GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
|
||||||
|
|
||||||
ret = gst_pad_push (self->srcpad, outbuf);
|
/* Check if we need to drop the frame because of QoS */
|
||||||
outbuf = NULL;
|
if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
|
||||||
if (ret != GST_FLOW_OK)
|
gst_buffer_unref (gst_deinterlace_pop_history (self));
|
||||||
return ret;
|
gst_buffer_unref (outbuf);
|
||||||
|
outbuf = NULL;
|
||||||
|
ret = GST_FLOW_OK;
|
||||||
|
} else {
|
||||||
|
/* do magic calculus */
|
||||||
|
gst_deinterlace_method_deinterlace_frame (self->method, self, outbuf);
|
||||||
|
|
||||||
|
gst_buffer_unref (gst_deinterlace_pop_history (self));
|
||||||
|
|
||||||
|
ret = gst_pad_push (self->srcpad, outbuf);
|
||||||
|
outbuf = NULL;
|
||||||
|
if (ret != GST_FLOW_OK)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* no calculation done: remove excess field */
|
/* no calculation done: remove excess field */
|
||||||
else if (self->field_history[cur_field_idx].flags ==
|
else if (self->field_history[cur_field_idx].flags ==
|
||||||
|
@ -1030,9 +1120,6 @@ gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
|
||||||
if (ret != GST_FLOW_OK)
|
if (ret != GST_FLOW_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* do magic calculus */
|
|
||||||
gst_deinterlace_method_deinterlace_frame (self->method, self, outbuf);
|
|
||||||
|
|
||||||
g_assert (self->history_count - 1 -
|
g_assert (self->history_count - 1 -
|
||||||
gst_deinterlace_method_get_latency (self->method) >= 0);
|
gst_deinterlace_method_get_latency (self->method) >= 0);
|
||||||
buf =
|
buf =
|
||||||
|
@ -1040,19 +1127,29 @@ gst_deinterlace_chain (GstPad * pad, GstBuffer * buf)
|
||||||
gst_deinterlace_method_get_latency (self->method)].buf;
|
gst_deinterlace_method_get_latency (self->method)].buf;
|
||||||
timestamp = GST_BUFFER_TIMESTAMP (buf);
|
timestamp = GST_BUFFER_TIMESTAMP (buf);
|
||||||
|
|
||||||
gst_buffer_unref (gst_deinterlace_pop_history (self));
|
|
||||||
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
|
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
|
||||||
if (self->fields == GST_DEINTERLACE_ALL)
|
if (self->fields == GST_DEINTERLACE_ALL)
|
||||||
GST_BUFFER_DURATION (outbuf) = self->field_duration;
|
GST_BUFFER_DURATION (outbuf) = self->field_duration;
|
||||||
else
|
else
|
||||||
GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
|
GST_BUFFER_DURATION (outbuf) = 2 * self->field_duration;
|
||||||
|
|
||||||
ret = gst_pad_push (self->srcpad, outbuf);
|
/* Check if we need to drop the frame because of QoS */
|
||||||
outbuf = NULL;
|
if (!gst_deinterlace_do_qos (self, GST_BUFFER_TIMESTAMP (buf))) {
|
||||||
|
gst_buffer_unref (gst_deinterlace_pop_history (self));
|
||||||
|
gst_buffer_unref (outbuf);
|
||||||
|
outbuf = NULL;
|
||||||
|
ret = GST_FLOW_OK;
|
||||||
|
} else {
|
||||||
|
/* do magic calculus */
|
||||||
|
gst_deinterlace_method_deinterlace_frame (self->method, self, outbuf);
|
||||||
|
|
||||||
if (ret != GST_FLOW_OK)
|
gst_buffer_unref (gst_deinterlace_pop_history (self));
|
||||||
return ret;
|
|
||||||
|
ret = gst_pad_push (self->srcpad, outbuf);
|
||||||
|
outbuf = NULL;
|
||||||
|
if (ret != GST_FLOW_OK)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* no calculation done: remove excess field */
|
/* no calculation done: remove excess field */
|
||||||
else if (self->field_history[cur_field_idx].flags ==
|
else if (self->field_history[cur_field_idx].flags ==
|
||||||
|
@ -1357,6 +1454,31 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
|
||||||
GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
|
GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_NEWSEGMENT:
|
||||||
|
{
|
||||||
|
GstFormat fmt;
|
||||||
|
gboolean is_update;
|
||||||
|
gint64 start, end, base;
|
||||||
|
gdouble rate;
|
||||||
|
|
||||||
|
gst_event_parse_new_segment (event, &is_update, &rate, &fmt, &start,
|
||||||
|
&end, &base);
|
||||||
|
if (fmt == GST_FORMAT_TIME) {
|
||||||
|
GST_DEBUG_OBJECT (pad,
|
||||||
|
"Got NEWSEGMENT event in GST_FORMAT_TIME, passing on (%"
|
||||||
|
GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", GST_TIME_ARGS (start),
|
||||||
|
GST_TIME_ARGS (end));
|
||||||
|
gst_segment_set_newsegment (&self->segment, is_update, rate, fmt, start,
|
||||||
|
end, base);
|
||||||
|
} else {
|
||||||
|
gst_segment_init (&self->segment, GST_FORMAT_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_deinterlace_reset_qos (self);
|
||||||
|
gst_deinterlace_reset_history (self);
|
||||||
|
res = gst_pad_push_event (self->srcpad, event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case GST_EVENT_CUSTOM_DOWNSTREAM:{
|
case GST_EVENT_CUSTOM_DOWNSTREAM:{
|
||||||
const GstStructure *s = gst_event_get_structure (event);
|
const GstStructure *s = gst_event_get_structure (event);
|
||||||
|
|
||||||
|
@ -1393,7 +1515,6 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
|
||||||
}
|
}
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case GST_EVENT_EOS:
|
case GST_EVENT_EOS:
|
||||||
case GST_EVENT_NEWSEGMENT:
|
|
||||||
gst_deinterlace_reset_history (self);
|
gst_deinterlace_reset_history (self);
|
||||||
|
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
@ -1406,6 +1527,7 @@ gst_deinterlace_sink_event (GstPad * pad, GstEvent * event)
|
||||||
GST_DEBUG_OBJECT (self, "Ending still frames");
|
GST_DEBUG_OBJECT (self, "Ending still frames");
|
||||||
self->still_frame_mode = FALSE;
|
self->still_frame_mode = FALSE;
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
break;
|
break;
|
||||||
|
@ -1485,6 +1607,16 @@ gst_deinterlace_src_event (GstPad * pad, GstEvent * event)
|
||||||
GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
|
GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_QOS:{
|
||||||
|
GstClockTimeDiff diff;
|
||||||
|
GstClockTime timestamp;
|
||||||
|
gdouble proportion;
|
||||||
|
|
||||||
|
gst_event_parse_qos (event, &proportion, &diff, ×tamp);
|
||||||
|
|
||||||
|
gst_deinterlace_update_qos (self, proportion, diff, timestamp);
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
default:
|
default:
|
||||||
res = gst_pad_push_event (self->sinkpad, event);
|
res = gst_pad_push_event (self->sinkpad, event);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -254,6 +254,13 @@ struct _GstDeinterlace
|
||||||
need not match the pixel width
|
need not match the pixel width
|
||||||
*/
|
*/
|
||||||
guint field_stride;
|
guint field_stride;
|
||||||
|
|
||||||
|
/* Current segment */
|
||||||
|
GstSegment segment;
|
||||||
|
|
||||||
|
/* QoS stuff */
|
||||||
|
gdouble proportion;
|
||||||
|
GstClockTime earliest_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstDeinterlaceClass
|
struct _GstDeinterlaceClass
|
||||||
|
|
Loading…
Reference in a new issue