From 00f23053b1f27c6857a2153b39e1344d2d6e9eaf Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Tue, 3 May 2016 13:59:54 -0300 Subject: [PATCH] qtdemux: improve edts segment handling after seeks in push mode Properly handle edts segments for push-based operation seeking. We only support edts that a single segment that has media at the end, being preceeded by any number of gap segments. This also allows the qt segment rate to be respected after seeks https://bugzilla.gnome.org/show_bug.cgi?id=765669 --- gst/isomp4/qtdemux.c | 88 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index b70457864c..83d3bf35d9 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -530,6 +530,8 @@ static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux, static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux, QtDemuxStream * stream, gint seg_idx, GstClockTime offset, GstClockTime * _start, GstClockTime * _stop); +static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux, + QtDemuxStream * stream, gint segment_index, GstClockTime pos); static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux); static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration); @@ -2021,6 +2023,44 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard) } } + +/* Maps the @segment to the qt edts internal segments and pushes + * the correspnding segment event. + * + * If it ends up being at a empty segment, a gap will be pushed and the next + * edts segment will be activated in sequence. + * + * To be used in push-mode only */ +static void +gst_qtdemux_map_and_push_segments (GstQTDemux * qtdemux, GstSegment * segment) +{ + gint n, i; + + for (n = 0; n < qtdemux->n_streams; n++) { + QtDemuxStream *stream = qtdemux->streams[n]; + + stream->time_position = segment->start; + + /* in push mode we should be guaranteed that we will have empty segments + * at the beginning and then one segment after, other scenarios are not + * supported and are discarded when parsing the edts */ + for (i = 0; i < stream->n_segments; i++) { + if (stream->segments[i].stop_time > segment->start) { + gst_qtdemux_activate_segment (qtdemux, stream, i, + stream->time_position); + if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) { + /* push the empty segment and move to the next one */ + gst_qtdemux_send_gap_for_segment (qtdemux, stream, i, + stream->time_position); + continue; + } + + g_assert (i == stream->n_segments - 1); + } + } + } +} + static gboolean gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent, GstEvent * event) @@ -2116,11 +2156,19 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent, gst_segment_copy_into (&segment, &demux->segment); GST_DEBUG_OBJECT (demux, "Pushing newseg %" GST_SEGMENT_FORMAT, &segment); - segment_event = gst_event_new_segment (&segment); - gst_event_set_seqnum (segment_event, gst_event_get_seqnum (event)); /* erase any previously set segment */ gst_event_replace (&demux->pending_newsegment, NULL); - gst_qtdemux_push_event (demux, segment_event); + + /* For pull mode, segment activation will be handled in the looping task + * For push mode, need to do it here */ + if (demux->pullbased) { + segment_event = gst_event_new_segment (&segment); + gst_event_set_seqnum (segment_event, gst_event_get_seqnum (event)); + gst_qtdemux_push_event (demux, segment_event); + } else { + /* map segment to internal qt segments and push on each stream */ + gst_qtdemux_map_and_push_segments (demux, &segment); + } /* clear leftover in current segment, if any */ gst_adapter_clear (demux->adapter); @@ -5920,6 +5968,25 @@ gst_qtdemux_check_send_pending_segment (GstQTDemux * demux) } } +static void +gst_qtdemux_send_gap_for_segment (GstQTDemux * demux, + QtDemuxStream * stream, gint segment_index, GstClockTime pos) +{ + GstClockTime ts, dur; + GstEvent *gap; + + ts = pos; + dur = + stream->segments[segment_index].duration - (pos - + stream->segments[segment_index].time); + gap = gst_event_new_gap (ts, dur); + stream->time_position += dur; + + GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty " + "segment: %" GST_PTR_FORMAT, gap); + gst_pad_push_event (stream->pad, gap); +} + static void gst_qtdemux_stream_send_initial_gap_segments (GstQTDemux * demux, QtDemuxStream * stream) @@ -5932,19 +5999,8 @@ gst_qtdemux_stream_send_initial_gap_segments (GstQTDemux * demux, gst_qtdemux_activate_segment (demux, stream, i, stream->time_position); if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) { - GstClockTime ts, dur; - GstEvent *gap; - - ts = stream->time_position; - dur = - stream->segments[i].duration - (stream->time_position - - stream->segments[i].time); - gap = gst_event_new_gap (ts, dur); - stream->time_position += dur; - - GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty " - "segment: %" GST_PTR_FORMAT, gap); - gst_pad_push_event (stream->pad, gap); + gst_qtdemux_send_gap_for_segment (demux, stream, i, + stream->time_position); } else { /* Only support empty segment at the beginning followed by * one non-empty segment, this was checked when parsing the