qtdemux: respect qt segments in push-mode for empty starts

In push-mode it is hard to support qt segments overall but it is
possible to support when the file isn't heavily edited but just contain
a segment to indicate a gap at the beginning. This also allows properly
timestamping data that has negative DTS in push-mode.

It is relevant to support those for 2 scenarios:

1) fragmented streaming
2) HTTP playback of 'regular' mp4

https://bugzilla.gnome.org/show_bug.cgi?id=753484
This commit is contained in:
Thiago Santos 2015-08-10 11:23:45 -03:00
parent e686ec0c97
commit 142d8e2d23

View file

@ -4131,8 +4131,6 @@ eos:
* This will push out a NEWSEGMENT event with the right values and * This will push out a NEWSEGMENT event with the right values and
* position the stream index to the first decodable sample before * position the stream index to the first decodable sample before
* @offset. * @offset.
*
* PULL-BASED
*/ */
static gboolean static gboolean
gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream, gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
@ -4264,6 +4262,10 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
return TRUE; return TRUE;
} }
/* We don't need to look for a sample in push-based */
if (!qtdemux->pullbased)
return TRUE;
/* and move to the keyframe before the indicated media time of the /* and move to the keyframe before the indicated media time of the
* segment */ * segment */
if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) { if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
@ -5529,6 +5531,62 @@ gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
demux->todrop -= bytes; demux->todrop -= bytes;
} }
static void
gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
{
if (G_UNLIKELY (demux->pending_newsegment)) {
gint i;
gst_qtdemux_push_pending_newsegment (demux);
/* clear to send tags on all streams */
for (i = 0; i < demux->n_streams; i++) {
QtDemuxStream *stream;
stream = demux->streams[i];
gst_qtdemux_push_tags (demux, stream);
if (stream->sparse) {
GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
gst_pad_push_event (stream->pad,
gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
}
}
}
}
static void
gst_qtdemux_stream_send_initial_gap_segments (GstQTDemux * demux,
QtDemuxStream * stream)
{
gint i;
/* Push any initial gap segments before proceeding to the
* 'real' data */
for (i = 0; i < stream->n_segments; i++) {
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);
} else {
/* Only support empty segment at the beginning followed by
* one non-empty segment, this was checked when parsing the
* edts atom, arriving here is unexpected */
g_assert (i + 1 == stream->n_segments);
break;
}
}
}
static GstFlowReturn static GstFlowReturn
gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf) gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
{ {
@ -5698,6 +5756,8 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
extract_initial_length_and_fourcc (data, demux->neededbytes, NULL, extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
&fourcc); &fourcc);
if (fourcc == FOURCC_moov) { if (fourcc == FOURCC_moov) {
gint n;
/* in usual fragmented setup we could try to scan for more /* in usual fragmented setup we could try to scan for more
* and end up at the the moov (after mdat) again */ * and end up at the the moov (after mdat) again */
if (demux->got_moov && demux->n_streams > 0 && if (demux->got_moov && demux->n_streams > 0 &&
@ -5731,7 +5791,6 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
if (!demux->got_moov) if (!demux->got_moov)
qtdemux_expose_streams (demux); qtdemux_expose_streams (demux);
else { else {
gint n;
for (n = 0; n < demux->n_streams; n++) { for (n = 0; n < demux->n_streams; n++) {
QtDemuxStream *stream = demux->streams[n]; QtDemuxStream *stream = demux->streams[n];
@ -5741,6 +5800,11 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
} }
demux->got_moov = TRUE; demux->got_moov = TRUE;
gst_qtdemux_check_send_pending_segment (demux);
for (n = 0; n < demux->n_streams; n++) {
gst_qtdemux_stream_send_initial_gap_segments (demux,
demux->streams[n]);
}
g_node_destroy (demux->moov_node); g_node_destroy (demux->moov_node);
demux->moov_node = NULL; demux->moov_node = NULL;
@ -5966,20 +6030,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
/* first buffer? */ /* first buffer? */
/* initial newsegment sent here after having added pads, /* initial newsegment sent here after having added pads,
* possible others in sink_event */ * possible others in sink_event */
if (G_UNLIKELY (demux->pending_newsegment)) { gst_qtdemux_check_send_pending_segment (demux);
gst_qtdemux_push_pending_newsegment (demux);
/* clear to send tags on all streams */
for (i = 0; i < demux->n_streams; i++) {
stream = demux->streams[i];
gst_qtdemux_push_tags (demux, stream);
if (stream->sparse) {
GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
gst_pad_push_event (stream->pad,
gst_event_new_gap (stream->segment.position,
GST_CLOCK_TIME_NONE));
}
}
}
/* Figure out which stream this packet belongs to */ /* Figure out which stream this packet belongs to */
for (i = 0; i < demux->n_streams; i++) { for (i = 0; i < demux->n_streams; i++) {
@ -7876,6 +7927,10 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
GNode * trak) GNode * trak)
{ {
GNode *edts; GNode *edts;
/* accept edts if they contain gaps at start and there is only
* one media segment */
gboolean allow_pushbased_edts = TRUE;
gint media_segments_count = 0;
/* parse and prepare segment info from the edit list */ /* parse and prepare segment info from the edit list */
GST_DEBUG_OBJECT (qtdemux, "looking for edit list container"); GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
@ -7928,6 +7983,7 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
if (media_time != G_MAXUINT32) { if (media_time != G_MAXUINT32) {
segment->media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time); segment->media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
segment->media_stop = segment->media_start + segment->duration; segment->media_stop = segment->media_start + segment->duration;
media_segments_count++;
} else { } else {
segment->media_start = GST_CLOCK_TIME_NONE; segment->media_start = GST_CLOCK_TIME_NONE;
segment->media_stop = GST_CLOCK_TIME_NONE; segment->media_stop = GST_CLOCK_TIME_NONE;
@ -7965,12 +8021,14 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
} }
GST_DEBUG_OBJECT (qtdemux, "found %d segments", count); GST_DEBUG_OBJECT (qtdemux, "found %d segments", count);
stream->n_segments = count; stream->n_segments = count;
if (media_segments_count != 1)
allow_pushbased_edts = FALSE;
} }
done: done:
/* push based does not handle segments, so act accordingly here, /* push based does not handle segments, so act accordingly here,
* and warn if applicable */ * and warn if applicable */
if (!qtdemux->pullbased) { if (!qtdemux->pullbased && !allow_pushbased_edts) {
GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments"); GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
/* remove and use default one below, we stream like it anyway */ /* remove and use default one below, we stream like it anyway */
g_free (stream->segments); g_free (stream->segments);