mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
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:
parent
e686ec0c97
commit
142d8e2d23
1 changed files with 76 additions and 18 deletions
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue