tagdemux: Properly implement seeking if tagdemux is driving the pipeline

https://bugzilla.gnome.org/show_bug.cgi?id=705062
This commit is contained in:
Sebastian Dröge 2013-07-29 11:40:57 +02:00
parent 31763d3c20
commit bb870b8b7b

View file

@ -136,6 +136,8 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_STATIC_CAPS ("ANY")
);
static void gst_tag_demux_element_loop (GstTagDemux * demux);
static void gst_tag_demux_dispose (GObject * object);
static GstFlowReturn gst_tag_demux_chain (GstPad * pad, GstObject * parent,
@ -778,18 +780,132 @@ gst_tag_demux_get_upstream_size (GstTagDemux * tagdemux)
}
static gboolean
gst_tag_demux_srcpad_event (GstPad * pad, GstObject * parent, GstEvent * event)
gst_tag_demux_seek_pull (GstTagDemux * tagdemux, GstEvent * event)
{
GstSeekFlags flags;
GstSeekType start_type, stop_type;
GstFormat format;
gboolean flush;
gdouble rate;
gint64 start, stop;
GstSegment seeksegment = { 0, };
gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
&stop_type, &stop);
/* we can only seek on bytes */
if (format != GST_FORMAT_BYTES) {
GST_DEBUG_OBJECT (tagdemux, "Can only seek on BYTES");
return FALSE;
}
if (tagdemux->priv->state != GST_TAG_DEMUX_STREAMING) {
GST_DEBUG_OBJECT (tagdemux, "Can only seek if streaming already");
return FALSE;
}
switch (start_type) {
case GST_SEEK_TYPE_SET:
if (start == -1)
start = 0;
start += tagdemux->priv->strip_start;
break;
case GST_SEEK_TYPE_END:
/* Adjust the seek to be relative to the start of any end tag
* (note: 10 bytes before end is represented by stop=-10) */
if (start > 0)
start = 0;
start -= tagdemux->priv->strip_end;
break;
case GST_SEEK_TYPE_NONE:
default:
break;
}
switch (stop_type) {
case GST_SEEK_TYPE_SET:
if (stop != -1) {
/* -1 means the end of the file, pass it upstream intact */
stop += tagdemux->priv->strip_start;
}
break;
case GST_SEEK_TYPE_END:
/* Adjust the seek to be relative to the start of any end tag
* (note: 10 bytes before end is represented by stop=-10) */
if (stop > 0)
stop = 0;
stop -= tagdemux->priv->strip_end;
break;
case GST_SEEK_TYPE_NONE:
default:
break;
}
/* copy segment, we need this because we still need the old
* segment when we close the current segment. */
memcpy (&seeksegment, &tagdemux->priv->segment, sizeof (GstSegment));
GST_DEBUG_OBJECT (tagdemux, "configuring seek");
gst_segment_do_seek (&seeksegment, rate, format, flags,
start_type, start, stop_type, stop, NULL);
flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
GST_DEBUG_OBJECT (tagdemux, "New segment %" GST_SEGMENT_FORMAT, &seeksegment);
if (flush) {
GST_DEBUG_OBJECT (tagdemux, "Starting flush");
gst_pad_push_event (tagdemux->priv->sinkpad, gst_event_new_flush_start ());
gst_pad_push_event (tagdemux->priv->srcpad, gst_event_new_flush_start ());
} else {
GST_DEBUG_OBJECT (tagdemux, "Non-flushing seek, pausing task");
gst_pad_pause_task (tagdemux->priv->sinkpad);
}
/* now grab the stream lock so that streaming cannot continue, for
* non flushing seeks when the element is in PAUSED this could block
* forever. */
GST_DEBUG_OBJECT (tagdemux, "Waiting for streaming to stop");
GST_PAD_STREAM_LOCK (tagdemux->priv->sinkpad);
if (flush) {
GST_DEBUG_OBJECT (tagdemux, "Stopping flush");
gst_pad_push_event (tagdemux->priv->sinkpad,
gst_event_new_flush_stop (TRUE));
gst_pad_push_event (tagdemux->priv->srcpad,
gst_event_new_flush_stop (TRUE));
}
/* now update the real segment info */
GST_DEBUG_OBJECT (tagdemux, "Committing new seek segment");
memcpy (&tagdemux->priv->segment, &seeksegment, sizeof (GstSegment));
tagdemux->priv->offset = tagdemux->priv->segment.start;
/* notify start of new segment */
if (tagdemux->priv->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
GstMessage *msg;
msg = gst_message_new_segment_start (GST_OBJECT (tagdemux),
GST_FORMAT_BYTES, tagdemux->priv->segment.start);
gst_element_post_message (GST_ELEMENT (tagdemux), msg);
}
tagdemux->priv->need_newseg = TRUE;
/* restart our task since it might have been stopped when we did the
* flush. */
gst_pad_start_task (tagdemux->priv->sinkpad,
(GstTaskFunction) gst_tag_demux_element_loop, tagdemux, NULL);
/* streaming can continue now */
GST_PAD_STREAM_UNLOCK (tagdemux->priv->sinkpad);
return TRUE;
}
static gboolean
gst_tag_demux_seek_push (GstTagDemux * tagdemux, GstEvent * event)
{
GstTagDemux *tagdemux;
gboolean res = FALSE;
tagdemux = GST_TAG_DEMUX (parent);
/* Handle SEEK events, with adjusted byte offsets and sizes. */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
{
gdouble rate;
GstFormat format;
GstSeekType start_type, stop_type;
@ -843,6 +959,27 @@ gst_tag_demux_srcpad_event (GstPad * pad, GstObject * parent, GstEvent * event)
start_type, start, stop_type, stop);
res = gst_pad_push_event (tagdemux->priv->sinkpad, upstream);
}
return res;
}
static gboolean
gst_tag_demux_srcpad_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstTagDemux *tagdemux;
gboolean res = FALSE;
tagdemux = GST_TAG_DEMUX (parent);
/* Handle SEEK events, with adjusted byte offsets and sizes. */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
{
if (GST_PAD_MODE (tagdemux->priv->sinkpad) == GST_PAD_MODE_PUSH)
res = gst_tag_demux_seek_push (tagdemux, event);
else
res = gst_tag_demux_seek_pull (tagdemux, event);
break;
}
default:
@ -1202,6 +1339,7 @@ skip_typefinding:
/* set it again, in case we skipped typefinding */
demux->priv->state = GST_TAG_DEMUX_STREAMING;
demux->priv->offset += demux->priv->strip_start;
return ret;
@ -1274,7 +1412,11 @@ gst_tag_demux_element_loop (GstTagDemux * demux)
if (ret != GST_FLOW_OK)
break;
GST_BUFFER_OFFSET (outbuf) =
demux->priv->offset - demux->priv->strip_start;
demux->priv->offset += gst_buffer_get_size (outbuf);
GST_BUFFER_OFFSET_END (outbuf) =
demux->priv->offset - demux->priv->strip_start;
ret = gst_pad_push (demux->priv->srcpad, outbuf);
break;
@ -1336,14 +1478,18 @@ static gboolean
gst_tag_demux_sink_activate_mode (GstPad * pad, GstObject * parent,
GstPadMode mode, gboolean active)
{
GstTagDemux *demux = GST_TAG_DEMUX (parent);
gboolean res;
switch (mode) {
case GST_PAD_MODE_PULL:
if (active)
if (active) {
demux->priv->need_newseg = TRUE;
demux->priv->offset = 0;
res = TRUE;
else
} else {
res = gst_pad_stop_task (pad);
}
break;
default:
res = TRUE;