baseparse: reverse playback; mind keyframes for fragment boundary

This commit is contained in:
Mark Nauwelaerts 2011-01-14 15:16:04 +01:00 committed by Tim-Philipp Müller
parent 39da316386
commit fa16a9a0ef

View file

@ -285,6 +285,7 @@ struct _GstBaseParsePrivate
/* reverse playback */ /* reverse playback */
GSList *buffers_pending; GSList *buffers_pending;
GSList *buffers_queued; GSList *buffers_queued;
GSList *buffers_send;
GstClockTime last_ts; GstClockTime last_ts;
gint64 last_offset; gint64 last_offset;
}; };
@ -394,6 +395,9 @@ gst_base_parse_clear_queues (GstBaseParse * parse)
NULL); NULL);
g_slist_free (parse->priv->buffers_pending); g_slist_free (parse->priv->buffers_pending);
parse->priv->buffers_pending = NULL; parse->priv->buffers_pending = NULL;
g_slist_foreach (parse->priv->buffers_send, (GFunc) gst_buffer_unref, NULL);
g_slist_free (parse->priv->buffers_send);
parse->priv->buffers_send = NULL;
} }
static void static void
@ -1816,6 +1820,50 @@ gst_base_parse_drain (GstBaseParse * parse)
parse->priv->drain = FALSE; parse->priv->drain = FALSE;
} }
/**
* gst_base_parse_process_fragment:
* @parse: #GstBaseParse.
*
* Sends buffers collected in send_buffers downstream, and ensures that list
* is empty at the end (errors or not).
*/
static GstFlowReturn
gst_base_parse_send_buffers (GstBaseParse * parse)
{
GSList *send = NULL;
GstBuffer *buf;
GstFlowReturn ret = GST_FLOW_OK;
send = parse->priv->buffers_send;
/* send buffers */
while (send) {
buf = GST_BUFFER_CAST (send->data);
GST_LOG_OBJECT (parse, "pushing buffer %p, timestamp %"
GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
", offset %" G_GINT64_FORMAT, buf,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf));
/* iterate output queue an push downstream */
ret = gst_pad_push (parse->srcpad, buf);
send = g_slist_delete_link (send, send);
/* clear any leftover if error */
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
while (send) {
buf = GST_BUFFER_CAST (send->data);
gst_buffer_unref (buf);
send = g_slist_delete_link (send, send);
}
}
}
parse->priv->buffers_send = send;
return ret;
}
/** /**
* gst_base_parse_process_fragment: * gst_base_parse_process_fragment:
* @parse: #GstBaseParse. * @parse: #GstBaseParse.
@ -1831,7 +1879,7 @@ gst_base_parse_process_fragment (GstBaseParse * parse, gboolean push_only)
{ {
GstBuffer *buf; GstBuffer *buf;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GSList *send = NULL; gboolean seen_key = FALSE, seen_delta = FALSE;
if (push_only) if (push_only)
goto push; goto push;
@ -1861,6 +1909,11 @@ gst_base_parse_process_fragment (GstBaseParse * parse, gboolean push_only)
gst_base_parse_drain (parse); gst_base_parse_drain (parse);
push: push:
if (parse->priv->buffers_send) {
buf = GST_BUFFER_CAST (parse->priv->buffers_send->data);
seen_key |= !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
}
/* add metadata (if needed to queued buffers */ /* add metadata (if needed to queued buffers */
GST_LOG_OBJECT (parse, "last timestamp: %" GST_TIME_FORMAT, GST_LOG_OBJECT (parse, "last timestamp: %" GST_TIME_FORMAT,
GST_TIME_ARGS (parse->priv->last_ts)); GST_TIME_ARGS (parse->priv->last_ts));
@ -1885,38 +1938,39 @@ push:
GST_WARNING_OBJECT (parse, "could not determine time for buffer"); GST_WARNING_OBJECT (parse, "could not determine time for buffer");
} }
parse->priv->last_ts = GST_BUFFER_TIMESTAMP (buf);
/* reverse order for ascending sending */ /* reverse order for ascending sending */
send = g_slist_prepend (send, buf); /* send downstream at keyframe not preceded by a keyframe
* (e.g. that should identify start of collection of IDR nals) */
if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
if (seen_key) {
ret = gst_base_parse_send_buffers (parse);
/* if a problem, throw all to sending */
if (ret != GST_FLOW_OK) {
parse->priv->buffers_send =
g_slist_reverse (parse->priv->buffers_queued);
parse->priv->buffers_queued = NULL;
break;
}
seen_key = FALSE;
}
} else {
seen_delta = TRUE;
}
seen_key |= !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
parse->priv->buffers_send =
g_slist_prepend (parse->priv->buffers_send, buf);
parse->priv->buffers_queued = parse->priv->buffers_queued =
g_slist_delete_link (parse->priv->buffers_queued, g_slist_delete_link (parse->priv->buffers_queued,
parse->priv->buffers_queued); parse->priv->buffers_queued);
} }
/* send buffers */ /* audio may have all marked as keyframe, so arrange to send here */
while (send) { if (!seen_delta)
buf = GST_BUFFER_CAST (send->data); ret = gst_base_parse_send_buffers (parse);
GST_LOG_OBJECT (parse, "pushing buffer %p, timestamp %"
GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
", offset %" G_GINT64_FORMAT, buf,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf));
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (parse->priv->last_ts)))
parse->priv->last_ts = GST_BUFFER_TIMESTAMP (buf);
/* iterate output queue an push downstream */
ret = gst_pad_push (parse->srcpad, buf);
send = g_slist_delete_link (send, send);
/* clear any leftover if error */
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
while (send) {
buf = GST_BUFFER_CAST (send->data);
gst_buffer_unref (buf);
send = g_slist_delete_link (send, send);
}
}
}
/* any trailing unused no longer usable (ideally none) */ /* any trailing unused no longer usable (ideally none) */
if (G_UNLIKELY (gst_adapter_available (parse->adapter))) { if (G_UNLIKELY (gst_adapter_available (parse->adapter))) {
@ -3494,7 +3548,7 @@ done:
/* ERRORS */ /* ERRORS */
negative_rate: negative_rate:
{ {
GST_DEBUG_OBJECT (parse, "negative playback rates are not supported yet."); GST_DEBUG_OBJECT (parse, "negative playback rates delegated upstream.");
res = FALSE; res = FALSE;
goto done; goto done;
} }