mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 21:28:45 +00:00
qtdemux: do not ignore empty segments
Make sure empty segments are used and pushed with a gap event to represent its data (or lack of it) Each QtSegment is mapped into a GstSegment with the corresponding media range. For empty QtSegments a gap event is pushed instead of GstBuffers and it advances to the next QtSegment. To make this work with seeks, need to keep track of the starting 'base' to make sure it remains consistently increasing when pushing new segment events. For example: if a seek makes qtdemux start from 5s, the first segment will have a base=0. When the next segment is activated, its base time will be QtSegment.time - qtdemux.segment_base so that it doesn't include the first 5s that weren't played and shouldn't be accounted on the running time This purposedly will remove the fix made for https://bugzilla.gnome.org/show_bug.cgi?id=700264, at this point it was decided to respect the gaps, even if they cause a delay on playback, because that's the way the file was crafted. https://bugzilla.gnome.org/show_bug.cgi?id=345830
This commit is contained in:
parent
8a143dfcbc
commit
90a5565229
2 changed files with 80 additions and 37 deletions
|
@ -199,6 +199,8 @@ struct _QtDemuxSegment
|
||||||
gdouble rate;
|
gdouble rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)
|
||||||
|
|
||||||
struct _QtDemuxStream
|
struct _QtDemuxStream
|
||||||
{
|
{
|
||||||
GstPad *pad;
|
GstPad *pad;
|
||||||
|
@ -1403,6 +1405,7 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment,
|
||||||
}
|
}
|
||||||
segment->position = desired_offset;
|
segment->position = desired_offset;
|
||||||
segment->time = desired_offset;
|
segment->time = desired_offset;
|
||||||
|
qtdemux->segment_base = desired_offset;
|
||||||
|
|
||||||
/* we stop at the end */
|
/* we stop at the end */
|
||||||
if (segment->stop == -1)
|
if (segment->stop == -1)
|
||||||
|
@ -1856,6 +1859,7 @@ gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard)
|
||||||
qtdemux->offset = 0;
|
qtdemux->offset = 0;
|
||||||
gst_adapter_clear (qtdemux->adapter);
|
gst_adapter_clear (qtdemux->adapter);
|
||||||
gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
|
gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
|
||||||
|
qtdemux->segment_base = 0;
|
||||||
|
|
||||||
if (hard) {
|
if (hard) {
|
||||||
for (n = 0; n < qtdemux->n_streams; n++) {
|
for (n = 0; n < qtdemux->n_streams; n++) {
|
||||||
|
@ -3460,7 +3464,10 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
stop =
|
stop =
|
||||||
MIN (segment->media_stop, stop - segment->time + segment->media_start);
|
MIN (segment->media_stop, stop - segment->time + segment->media_start);
|
||||||
|
|
||||||
if (qtdemux->segment.rate >= 0) {
|
if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
|
||||||
|
start = segment->time + seg_time;
|
||||||
|
time = offset;
|
||||||
|
} else if (qtdemux->segment.rate >= 0) {
|
||||||
start = MIN (segment->media_start + seg_time, stop);
|
start = MIN (segment->media_start + seg_time, stop);
|
||||||
time = offset;
|
time = offset;
|
||||||
} else {
|
} else {
|
||||||
|
@ -3493,6 +3500,9 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
stream->segment.stop = stop;
|
stream->segment.stop = stop;
|
||||||
stream->segment.time = time;
|
stream->segment.time = time;
|
||||||
stream->segment.position = start;
|
stream->segment.position = start;
|
||||||
|
stream->segment.base =
|
||||||
|
segment->time >
|
||||||
|
qtdemux->segment_base ? segment->time - qtdemux->segment_base : 0;
|
||||||
|
|
||||||
/* now prepare and send the segment */
|
/* now prepare and send the segment */
|
||||||
if (stream->pad) {
|
if (stream->pad) {
|
||||||
|
@ -3510,20 +3520,26 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
|
|
||||||
/* 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 (qtdemux->segment.rate >= 0) {
|
if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
|
||||||
index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
|
if (qtdemux->segment.rate >= 0) {
|
||||||
stream->to_sample = G_MAXUINT32;
|
index = gst_qtdemux_find_index_linear (qtdemux, stream, start);
|
||||||
GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
|
stream->to_sample = G_MAXUINT32;
|
||||||
", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
|
GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
|
||||||
GST_TIME_ARGS (gst_util_uint64_scale (stream->samples[index].timestamp,
|
", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (start), index,
|
||||||
GST_SECOND, stream->timescale)));
|
GST_TIME_ARGS (gst_util_uint64_scale (stream->
|
||||||
|
samples[index].timestamp, GST_SECOND, stream->timescale)));
|
||||||
|
} else {
|
||||||
|
index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
|
||||||
|
stream->to_sample = index;
|
||||||
|
GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
|
||||||
|
", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
|
||||||
|
GST_TIME_ARGS (gst_util_uint64_scale (stream->
|
||||||
|
samples[index].timestamp, GST_SECOND, stream->timescale)));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
index = gst_qtdemux_find_index_linear (qtdemux, stream, stop);
|
GST_DEBUG_OBJECT (qtdemux, "No need to look for keyframe, "
|
||||||
stream->to_sample = index;
|
"this is an empty segment");
|
||||||
GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
|
return TRUE;
|
||||||
", index: %u, pts %" GST_TIME_FORMAT, GST_TIME_ARGS (stop), index,
|
|
||||||
GST_TIME_ARGS (gst_util_uint64_scale (stream->samples[index].timestamp,
|
|
||||||
GST_SECOND, stream->timescale)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
|
/* gst_qtdemux_parse_sample () called from gst_qtdemux_find_index_linear ()
|
||||||
|
@ -3585,8 +3601,8 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
*/
|
*/
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
|
gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
|
||||||
QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * dts,
|
QtDemuxStream * stream, gboolean * empty, guint64 * offset, guint * size,
|
||||||
guint64 * pts, guint64 * duration, gboolean * keyframe)
|
guint64 * dts, guint64 * pts, guint64 * duration, gboolean * keyframe)
|
||||||
{
|
{
|
||||||
QtDemuxSample *sample;
|
QtDemuxSample *sample;
|
||||||
guint64 time_position;
|
guint64 time_position;
|
||||||
|
@ -3613,6 +3629,22 @@ gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
|
||||||
if (G_UNLIKELY (stream->segment_index != seg_idx))
|
if (G_UNLIKELY (stream->segment_index != seg_idx))
|
||||||
gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
|
gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (&stream->
|
||||||
|
segments[stream->segment_index]))) {
|
||||||
|
QtDemuxSegment *seg = &stream->segments[stream->segment_index];
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (qtdemux, "Empty segment activated,"
|
||||||
|
" prepare empty sample");
|
||||||
|
|
||||||
|
*empty = TRUE;
|
||||||
|
*pts = *dts = time_position;
|
||||||
|
*duration = seg->duration - (time_position - seg->time);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*empty = FALSE;
|
||||||
|
|
||||||
GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
|
GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
|
||||||
stream->sample_index, stream->n_samples);
|
stream->sample_index, stream->n_samples);
|
||||||
|
|
||||||
|
@ -3655,6 +3687,14 @@ gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
|
||||||
QtDemuxSample *sample;
|
QtDemuxSample *sample;
|
||||||
QtDemuxSegment *segment;
|
QtDemuxSegment *segment;
|
||||||
|
|
||||||
|
/* get current segment */
|
||||||
|
segment = &stream->segments[stream->segment_index];
|
||||||
|
|
||||||
|
if (G_UNLIKELY (QTSEGMENT_IS_EMPTY (segment))) {
|
||||||
|
GST_DEBUG_OBJECT (qtdemux, "Empty segment, no samples to advance");
|
||||||
|
goto next_segment;
|
||||||
|
}
|
||||||
|
|
||||||
if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
|
if (G_UNLIKELY (stream->sample_index >= stream->to_sample)) {
|
||||||
/* Mark the stream as EOS */
|
/* Mark the stream as EOS */
|
||||||
GST_DEBUG_OBJECT (qtdemux,
|
GST_DEBUG_OBJECT (qtdemux,
|
||||||
|
@ -3667,9 +3707,6 @@ gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
|
||||||
stream->sample_index++;
|
stream->sample_index++;
|
||||||
stream->offset_in_sample = 0;
|
stream->offset_in_sample = 0;
|
||||||
|
|
||||||
/* get current segment */
|
|
||||||
segment = &stream->segments[stream->segment_index];
|
|
||||||
|
|
||||||
/* reached the last sample, we need the next segment */
|
/* reached the last sample, we need the next segment */
|
||||||
if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
|
if (G_UNLIKELY (stream->sample_index >= stream->n_samples))
|
||||||
goto next_segment;
|
goto next_segment;
|
||||||
|
@ -4155,6 +4192,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
|
||||||
guint64 duration = 0;
|
guint64 duration = 0;
|
||||||
gboolean keyframe = FALSE;
|
gboolean keyframe = FALSE;
|
||||||
guint sample_size = 0;
|
guint sample_size = 0;
|
||||||
|
gboolean empty = 0;
|
||||||
guint size;
|
guint size;
|
||||||
gint index;
|
gint index;
|
||||||
gint i;
|
gint i;
|
||||||
|
@ -4218,16 +4256,23 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fetch info for the current sample of this stream */
|
/* fetch info for the current sample of this stream */
|
||||||
if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset,
|
if (G_UNLIKELY (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &empty,
|
||||||
&sample_size, &dts, &pts, &duration, &keyframe)))
|
&offset, &sample_size, &dts, &pts, &duration, &keyframe)))
|
||||||
goto eos_stream;
|
goto eos_stream;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (qtdemux,
|
GST_DEBUG_OBJECT (qtdemux,
|
||||||
"pushing from stream %d, offset %" G_GUINT64_FORMAT
|
"pushing from stream %d, empty %d offset %" G_GUINT64_FORMAT
|
||||||
", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
|
", size %d, dts=%" GST_TIME_FORMAT ", pts=%" GST_TIME_FORMAT
|
||||||
", duration %" GST_TIME_FORMAT, index, offset, sample_size,
|
", duration %" GST_TIME_FORMAT, index, empty, offset, sample_size,
|
||||||
GST_TIME_ARGS (dts), GST_TIME_ARGS (pts), GST_TIME_ARGS (duration));
|
GST_TIME_ARGS (dts), GST_TIME_ARGS (pts), GST_TIME_ARGS (duration));
|
||||||
|
|
||||||
|
if (G_UNLIKELY (empty)) {
|
||||||
|
/* empty segment, push a gap and move to the next one */
|
||||||
|
gst_pad_push_event (stream->pad, gst_event_new_gap (pts, duration));
|
||||||
|
stream->segment.position = pts + duration;
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
/* hmm, empty sample, skip and move to next sample */
|
/* hmm, empty sample, skip and move to next sample */
|
||||||
if (G_UNLIKELY (sample_size <= 0))
|
if (G_UNLIKELY (sample_size <= 0))
|
||||||
goto next;
|
goto next;
|
||||||
|
@ -6794,16 +6839,6 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
media_time = QT_UINT32 (buffer + 20 + i * 12);
|
media_time = QT_UINT32 (buffer + 20 + i * 12);
|
||||||
duration = QT_UINT32 (buffer + 16 + i * 12);
|
duration = QT_UINT32 (buffer + 16 + i * 12);
|
||||||
|
|
||||||
/* -1 media time is an empty segment, just ignore it */
|
|
||||||
if (media_time == G_MAXUINT32) {
|
|
||||||
if (i == 0) {
|
|
||||||
/* first empty segment specifies sample offset (if movie timescale) */
|
|
||||||
stream->elst_offset =
|
|
||||||
gst_util_uint64_scale (duration, GST_SECOND, qtdemux->timescale);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
segment = &stream->segments[count++];
|
segment = &stream->segments[count++];
|
||||||
|
|
||||||
/* time and duration expressed in global timescale */
|
/* time and duration expressed in global timescale */
|
||||||
|
@ -6814,9 +6849,14 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
segment->stop_time = stime;
|
segment->stop_time = stime;
|
||||||
segment->duration = stime - segment->time;
|
segment->duration = stime - segment->time;
|
||||||
/* media_time expressed in stream timescale */
|
/* media_time expressed in stream timescale */
|
||||||
segment->media_start =
|
if (media_time != G_MAXUINT32) {
|
||||||
gst_util_uint64_scale (media_time, GST_SECOND, stream->timescale);
|
segment->media_start =
|
||||||
segment->media_stop = segment->media_start + segment->duration;
|
gst_util_uint64_scale (media_time, GST_SECOND, stream->timescale);
|
||||||
|
segment->media_stop = segment->media_start + segment->duration;
|
||||||
|
} else {
|
||||||
|
segment->media_start = GST_CLOCK_TIME_NONE;
|
||||||
|
segment->media_stop = GST_CLOCK_TIME_NONE;
|
||||||
|
}
|
||||||
rate_int = GST_READ_UINT32_BE (buffer + 24 + i * 12);
|
rate_int = GST_READ_UINT32_BE (buffer + 24 + i * 12);
|
||||||
|
|
||||||
if (rate_int <= 1) {
|
if (rate_int <= 1) {
|
||||||
|
@ -6835,7 +6875,7 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
|
||||||
GST_TIME_ARGS (segment->duration),
|
GST_TIME_ARGS (segment->duration),
|
||||||
GST_TIME_ARGS (segment->media_start), segment->rate, rate_int);
|
GST_TIME_ARGS (segment->media_start), segment->rate, rate_int);
|
||||||
}
|
}
|
||||||
GST_DEBUG_OBJECT (qtdemux, "found %d non-empty segments", count);
|
GST_DEBUG_OBJECT (qtdemux, "found %d segments", count);
|
||||||
stream->n_segments = count;
|
stream->n_segments = count;
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
|
|
@ -121,6 +121,9 @@ struct _GstQTDemux {
|
||||||
gint64 seek_offset;
|
gint64 seek_offset;
|
||||||
gint64 push_seek_start;
|
gint64 push_seek_start;
|
||||||
gint64 push_seek_stop;
|
gint64 push_seek_stop;
|
||||||
|
guint64 segment_base; /* The offset from which playback was started, needs to
|
||||||
|
* be subtracted from GstSegment.base to get a correct
|
||||||
|
* running time whenever a new QtSegment is activated */
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* gst index support */
|
/* gst index support */
|
||||||
|
|
Loading…
Reference in a new issue