summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gst/isomp4/qtdemux.c569
-rw-r--r--gst/isomp4/qtdemux.h4
2 files changed, 439 insertions, 134 deletions
diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c
index 10fe9ba08..47cafe2b5 100644
--- a/gst/isomp4/qtdemux.c
+++ b/gst/isomp4/qtdemux.c
@@ -390,7 +390,9 @@ enum QtDemuxState
QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
QTDEMUX_STATE_HEADER, /* Parsing the header */
QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
- QTDEMUX_STATE_BUFFER_MDAT /* Buffering the mdat atom */
+ QTDEMUX_STATE_BUFFER_MDAT, /* Buffering the mdat atom (push-based) */
+ QTDEMUX_STATE_MFRO_SEARCH, /* Searching for mfro (push-based) */
+ QTDEMUX_STATE_MFRA_SEARCH, /* Searching for mfra (push-based) */
};
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
@@ -496,6 +498,7 @@ static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
static void qtdemux_do_allocation (GstQTDemux * qtdemux,
QtDemuxStream * stream);
+static void qtdemux_parse_mfra (GstQTDemux * qtdemux, GNode * mfra_node);
static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
@@ -742,13 +745,18 @@ done:
static gboolean
gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
{
- gboolean res = TRUE;
+ gboolean res = FALSE;
*duration = GST_CLOCK_TIME_NONE;
+ GST_DEBUG_OBJECT (qtdemux,
+ "Querying duration : %" G_GUINT64_FORMAT " %" G_GUINT32_FORMAT,
+ qtdemux->duration, qtdemux->timescale);
+
if (qtdemux->duration != 0) {
if (qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
*duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
+ res = TRUE;
}
}
return res;
@@ -1178,6 +1186,248 @@ gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
str->discont = TRUE;
}
+static const QtDemuxRandomAccessEntry *
+gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
+ GstClockTime pos, gboolean after)
+{
+ QtDemuxRandomAccessEntry *entries = stream->ra_entries;
+ guint n_entries = stream->n_ra_entries;
+ guint i;
+
+ GST_DEBUG_OBJECT (qtdemux, "stream %p pos:%" GST_TIME_FORMAT " after:%d",
+ stream, GST_TIME_ARGS (pos), after);
+
+ /* we assume the table is sorted */
+ for (i = 0; i < n_entries; ++i) {
+ GST_DEBUG_OBJECT (qtdemux, "Entry %d ts:%" GST_TIME_FORMAT,
+ i, GST_TIME_ARGS (entries[i].ts));
+ if (entries[i].ts > pos)
+ break;
+ }
+
+ /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
+ * probably okay to assume that the index lists the very first fragment */
+ if (i == 0)
+ return &entries[0];
+
+ if (after)
+ return &entries[i];
+ else
+ return &entries[i - 1];
+}
+
+/* Find the optimal position in a fragmented file for a given
+ * position.
+ *
+ * will set moof_offset and return the corrected time position
+ *
+ * PUSH-BASED
+ */
+
+static GstClockTime
+gst_qtdemux_find_fragment_position_push (GstQTDemux * qtdemux,
+ GstClockTime position)
+{
+ const QtDemuxRandomAccessEntry *best_entry = NULL;
+ guint i;
+
+ GST_DEBUG_OBJECT (qtdemux, "position : %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (position));
+
+ GST_OBJECT_LOCK (qtdemux);
+
+ g_assert (qtdemux->n_streams > 0);
+
+ for (i = 0; i < qtdemux->n_streams; i++) {
+ const QtDemuxRandomAccessEntry *entry;
+ QtDemuxStream *stream;
+ gboolean is_audio_or_video;
+
+ stream = qtdemux->streams[i];
+
+ if (stream->ra_entries == NULL)
+ continue;
+
+ if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
+ is_audio_or_video = TRUE;
+ else
+ is_audio_or_video = FALSE;
+
+ entry =
+ gst_qtdemux_stream_seek_fragment (qtdemux, stream,
+ position, !is_audio_or_video);
+
+ GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
+ "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
+
+ /* decide position to jump to just based on audio/video tracks, not subs */
+ if (!is_audio_or_video)
+ continue;
+
+ if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
+ best_entry = entry;
+ }
+
+ if (best_entry == NULL)
+ return GST_CLOCK_TIME_NONE;
+
+ /* Set best entry on all streams */
+ for (i = 0; i < qtdemux->n_streams; i++) {
+ QtDemuxStream *stream;
+ stream = qtdemux->streams[i];
+ stream->pending_seek = best_entry;
+ }
+
+ GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
+ "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (qtdemux->streams[0]->time_position),
+ best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
+
+ qtdemux->moof_offset = best_entry->moof_offset;
+
+ GST_OBJECT_UNLOCK (qtdemux);
+ return best_entry->ts;
+}
+
+/* Called when the new BYTE segment has been received */
+static gboolean
+gst_qtdemux_do_fragmented_seek_push (GstQTDemux * qtdemux,
+ GstClockTime position)
+{
+ const QtDemuxRandomAccessEntry *best_entry = NULL;
+ guint i;
+
+ GST_OBJECT_LOCK (qtdemux);
+
+ g_assert (qtdemux->n_streams > 0);
+
+ for (i = 0; i < qtdemux->n_streams; i++) {
+ const QtDemuxRandomAccessEntry *entry;
+ QtDemuxStream *stream;
+ gboolean is_audio_or_video;
+
+ stream = qtdemux->streams[i];
+
+ g_free (stream->samples);
+ stream->samples = NULL;
+ stream->n_samples = 0;
+ stream->stbl_index = -1; /* no samples have yet been parsed */
+ stream->sample_index = -1;
+
+ if (stream->ra_entries == NULL)
+ continue;
+
+ if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
+ is_audio_or_video = TRUE;
+ else
+ is_audio_or_video = FALSE;
+
+ entry =
+ gst_qtdemux_stream_seek_fragment (qtdemux, stream,
+ position, !is_audio_or_video);
+
+ GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
+ "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
+
+ stream->pending_seek = entry;
+
+ /* decide position to jump to just based on audio/video tracks, not subs */
+ if (!is_audio_or_video)
+ continue;
+
+ if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
+ best_entry = entry;
+ }
+
+ if (best_entry == NULL)
+ return FALSE;
+
+ GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
+ "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (qtdemux->streams[0]->time_position),
+ best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
+
+ /* Set best entry on all streams */
+ for (i = 0; i < qtdemux->n_streams; i++) {
+ QtDemuxStream *stream;
+ stream = qtdemux->streams[i];
+ stream->pending_seek = best_entry;
+ }
+
+ qtdemux->moof_offset = best_entry->moof_offset;
+
+ GST_OBJECT_UNLOCK (qtdemux);
+ return TRUE;
+}
+
+/* Called *after* time_position has been figured out and flushing
+ * has been done */
+static gboolean
+gst_qtdemux_do_fragmented_seek_pull (GstQTDemux * qtdemux)
+{
+ const QtDemuxRandomAccessEntry *best_entry = NULL;
+ guint i;
+
+ GST_OBJECT_LOCK (qtdemux);
+
+ g_assert (qtdemux->n_streams > 0);
+
+ for (i = 0; i < qtdemux->n_streams; i++) {
+ const QtDemuxRandomAccessEntry *entry;
+ QtDemuxStream *stream;
+ gboolean is_audio_or_video;
+
+ stream = qtdemux->streams[i];
+
+ g_free (stream->samples);
+ stream->samples = NULL;
+ stream->n_samples = 0;
+ stream->stbl_index = -1; /* no samples have yet been parsed */
+ stream->sample_index = -1;
+
+ if (stream->ra_entries == NULL)
+ continue;
+
+ if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
+ is_audio_or_video = TRUE;
+ else
+ is_audio_or_video = FALSE;
+
+ entry =
+ gst_qtdemux_stream_seek_fragment (qtdemux, stream,
+ stream->time_position, !is_audio_or_video);
+
+ GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
+ "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
+
+ stream->pending_seek = entry;
+
+ /* decide position to jump to just based on audio/video tracks, not subs */
+ if (!is_audio_or_video)
+ continue;
+
+ if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
+ best_entry = entry;
+ }
+
+ if (best_entry == NULL)
+ return FALSE;
+
+ GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
+ "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (qtdemux->streams[0]->time_position),
+ best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
+
+ qtdemux->moof_offset = best_entry->moof_offset;
+
+ qtdemux_add_fragmented_samples (qtdemux);
+
+ GST_OBJECT_UNLOCK (qtdemux);
+ return TRUE;
+}
+
+
+
static void
gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
gboolean use_sparse, gint64 * key_time, gint64 * key_offset)
@@ -1321,11 +1571,23 @@ gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
original_stop = stop;
stop = -1;
- /* find reasonable corresponding BYTE position,
- * also try to mind about keyframes, since we can not go back a bit for them
- * later on */
- gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, &key_cur, &byte_cur);
-
+ /* If working in fragmented, search in random access entries */
+ if (qtdemux->fragmented) {
+ cur = gst_qtdemux_find_fragment_position_push (qtdemux, cur);
+ if (!GST_CLOCK_TIME_IS_VALID (cur))
+ goto fragment_fail;
+ /* seek to the moof_offset */
+
+ byte_cur = qtdemux->moof_offset;
+ /* qtdemux->state = QTDEMUX_STATE_INITIAL; */
+ qtdemux->doing_fragment_seek = TRUE;
+ } else {
+
+ /* find reasonable corresponding BYTE position,
+ * also try to mind about keyframes, since we can not go back a bit for them
+ * later on */
+ gst_qtdemux_adjust_seek (qtdemux, cur, FALSE, &key_cur, &byte_cur);
+ }
if (byte_cur == -1)
goto abort_seek;
@@ -1353,6 +1615,9 @@ gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
stop_type, stop);
gst_event_set_seqnum (event, seqnum);
res = gst_pad_push_event (qtdemux->sinkpad, event);
+ if (qtdemux->fragmented) {
+ qtdemux->neededbytes = 16;
+ }
return res;
@@ -1373,6 +1638,12 @@ no_format:
GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
return FALSE;
}
+fragment_fail:
+ {
+ GST_WARNING_OBJECT (qtdemux,
+ "Could not find fragment position, seek aborted");
+ return FALSE;
+ }
}
/* perform the seek.
@@ -1611,7 +1882,8 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstObject * parent,
res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
} else {
GST_DEBUG_OBJECT (qtdemux,
- "ignoring seek in push mode in current state");
+ "ignoring seek in push mode in current state (fragmented:%d)",
+ qtdemux->fragmented);
res = FALSE;
}
gst_event_unref (event);
@@ -1961,7 +2233,8 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
"not in time format");
/* chain will send initial newsegment after pads have been added */
- if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
+ if (!demux->doing_fragment_seek && (demux->state != QTDEMUX_STATE_MOVIE
+ || !demux->n_streams)) {
GST_DEBUG_OBJECT (demux, "still starting, eating event");
goto exit;
}
@@ -1987,6 +2260,12 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
"segment %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
GST_TIME_ARGS (segment.start), GST_TIME_ARGS (segment.stop));
GST_OBJECT_UNLOCK (demux);
+ if (demux->doing_fragment_seek) {
+ /* Finally reset the streams before the new data comes in */
+ gst_qtdemux_do_fragmented_seek_push (demux, segment.start);
+ demux->doing_fragment_seek = FALSE;
+ demux->state = QTDEMUX_STATE_INITIAL;
+ }
}
/* we only expect a BYTE segment, e.g. following a seek */
@@ -2057,7 +2336,7 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
demux->neededbytes = demux->todrop + stream->samples[idx].size;
} else {
/* set up for EOS */
- if (demux->upstream_newsegment) {
+ if (demux->upstream_newsegment || demux->state != QTDEMUX_STATE_MOVIE) {
demux->neededbytes = 16;
} else {
demux->neededbytes = -1;
@@ -2271,6 +2550,18 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
return result;
}
+static gboolean
+qtdemux_parse_mfro (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
+ guint32 * mfra_size)
+{
+ if (length >= 16) {
+ *mfra_size = GST_READ_UINT32_BE (buffer + 12);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
static void
qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
{
@@ -2681,8 +2972,9 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
goto index_too_big;
GST_DEBUG_OBJECT (qtdemux, "allocating n_samples %u * %u (%.2f MB)",
- stream->n_samples, (guint) sizeof (QtDemuxSample),
- stream->n_samples * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
+ stream->n_samples + samples_count, (guint) sizeof (QtDemuxSample),
+ (stream->n_samples +
+ samples_count) * sizeof (QtDemuxSample) / (1024.0 * 1024.0));
/* create a new array of samples if it's the first sample parsed */
if (stream->n_samples == 0)
@@ -3035,6 +3327,7 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
}
g_node_destroy (moof_node);
+
return TRUE;
missing_tfhd:
@@ -3145,11 +3438,13 @@ qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
#endif
}
+ GST_DEBUG_OBJECT (qtdemux, "Final time seen is %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time));
check_update_duration (qtdemux, time);
return TRUE;
-/* ERRORS */
+ /* ERRORS */
unknown_trackid:
{
GST_WARNING_OBJECT (qtdemux, "Couldn't find stream for track %u", track_id);
@@ -3167,6 +3462,19 @@ no_samples:
}
}
+static void
+qtdemux_parse_mfra (GstQTDemux * qtdemux, GNode * mfra_node)
+{
+ GNode *tfra_node;
+ tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
+
+ while (tfra_node) {
+ qtdemux_parse_tfra (qtdemux, tfra_node);
+ /* iterate all siblings */
+ tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
+ }
+}
+
static gboolean
qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
{
@@ -3175,7 +3483,7 @@ qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
GstBuffer *mfro = NULL, *mfra = NULL;
GstFlowReturn flow;
gboolean ret = FALSE;
- GNode *mfra_node, *tfra_node;
+ GNode *mfra_node;
guint64 mfra_offset = 0;
guint32 fourcc, mfra_size;
gint64 len;
@@ -3217,14 +3525,8 @@ qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux)
mfra_node = g_node_new ((guint8 *) mfra_map.data);
qtdemux_parse_node (qtdemux, mfra_node, mfra_map.data, mfra_map.size);
+ qtdemux_parse_mfra (qtdemux, mfra_node);
- tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
-
- while (tfra_node) {
- qtdemux_parse_tfra (qtdemux, tfra_node);
- /* iterate all siblings */
- tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
- }
g_node_destroy (mfra_node);
GST_INFO_OBJECT (qtdemux, "parsed movie fragment random access box (mfra)");
@@ -3244,7 +3546,7 @@ exit:
}
return ret;
-/* ERRORS */
+ /* ERRORS */
size_query_failed:
{
GST_WARNING_OBJECT (qtdemux, "could not query upstream size");
@@ -3813,8 +4115,8 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
/* find keyframe of the target index */
kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);
-/* *INDENT-OFF* */
-/* indent does stupid stuff with stream->samples[].timestamp */
+ /* *INDENT-OFF* */
+ /* indent does stupid stuff with stream->samples[].timestamp */
/* if we move forwards, we don't have to go back to the previous
* keyframe since we already sent that. We can also just jump to
@@ -3841,7 +4143,7 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
gst_qtdemux_move_stream (qtdemux, stream, kf_index);
}
-/* *INDENT-ON* */
+ /* *INDENT-ON* */
return TRUE;
}
@@ -3907,18 +4209,20 @@ gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
if (!qtdemux->fragmented)
goto eos;
- GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
- do {
- GstFlowReturn flow;
+ if (qtdemux->pullbased) {
+ GST_INFO_OBJECT (qtdemux, "out of samples, trying to add more");
+ do {
+ GstFlowReturn flow;
- GST_OBJECT_LOCK (qtdemux);
- flow = qtdemux_add_fragmented_samples (qtdemux);
- GST_OBJECT_UNLOCK (qtdemux);
+ GST_OBJECT_LOCK (qtdemux);
+ flow = qtdemux_add_fragmented_samples (qtdemux);
+ GST_OBJECT_UNLOCK (qtdemux);
- if (flow != GST_FLOW_OK)
- goto eos;
+ if (flow != GST_FLOW_OK)
+ goto eos;
+ }
+ while (stream->sample_index >= stream->n_samples);
}
- while (stream->sample_index >= stream->n_samples);
}
if (!qtdemux_parse_samples (qtdemux, stream, stream->sample_index)) {
@@ -4424,96 +4728,6 @@ exit:
return ret;
}
-static const QtDemuxRandomAccessEntry *
-gst_qtdemux_stream_seek_fragment (GstQTDemux * qtdemux, QtDemuxStream * stream,
- GstClockTime pos, gboolean after)
-{
- QtDemuxRandomAccessEntry *entries = stream->ra_entries;
- guint n_entries = stream->n_ra_entries;
- guint i;
-
- /* we assume the table is sorted */
- for (i = 0; i < n_entries; ++i) {
- if (entries[i].ts > pos)
- break;
- }
-
- /* FIXME: maybe save first moof_offset somewhere instead, but for now it's
- * probably okay to assume that the index lists the very first fragment */
- if (i == 0)
- return &entries[0];
-
- if (after)
- return &entries[i];
- else
- return &entries[i - 1];
-}
-
-static gboolean
-gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
-{
- const QtDemuxRandomAccessEntry *best_entry = NULL;
- guint i;
-
- GST_OBJECT_LOCK (qtdemux);
-
- g_assert (qtdemux->n_streams > 0);
-
- for (i = 0; i < qtdemux->n_streams; i++) {
- const QtDemuxRandomAccessEntry *entry;
- QtDemuxStream *stream;
- gboolean is_audio_or_video;
-
- stream = qtdemux->streams[i];
-
- g_free (stream->samples);
- stream->samples = NULL;
- stream->n_samples = 0;
- stream->stbl_index = -1; /* no samples have yet been parsed */
- stream->sample_index = -1;
-
- if (stream->ra_entries == NULL)
- continue;
-
- if (stream->subtype == FOURCC_vide || stream->subtype == FOURCC_soun)
- is_audio_or_video = TRUE;
- else
- is_audio_or_video = FALSE;
-
- entry =
- gst_qtdemux_stream_seek_fragment (qtdemux, stream,
- stream->time_position, !is_audio_or_video);
-
- GST_INFO_OBJECT (stream->pad, "%" GST_TIME_FORMAT " at offset "
- "%" G_GUINT64_FORMAT, GST_TIME_ARGS (entry->ts), entry->moof_offset);
-
- stream->pending_seek = entry;
-
- /* decide position to jump to just based on audio/video tracks, not subs */
- if (!is_audio_or_video)
- continue;
-
- if (best_entry == NULL || entry->moof_offset < best_entry->moof_offset)
- best_entry = entry;
- }
-
- if (best_entry == NULL) {
- GST_OBJECT_UNLOCK (qtdemux);
- return FALSE;
- }
-
- GST_INFO_OBJECT (qtdemux, "seek to %" GST_TIME_FORMAT ", best fragment "
- "moof offset: %" G_GUINT64_FORMAT ", ts %" GST_TIME_FORMAT,
- GST_TIME_ARGS (qtdemux->streams[0]->time_position),
- best_entry->moof_offset, GST_TIME_ARGS (best_entry->ts));
-
- qtdemux->moof_offset = best_entry->moof_offset;
-
- qtdemux_add_fragmented_samples (qtdemux);
-
- GST_OBJECT_UNLOCK (qtdemux);
- return TRUE;
-}
static GstFlowReturn
gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
@@ -4537,7 +4751,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
if (qtdemux->fragmented_seek_pending) {
GST_INFO_OBJECT (qtdemux, "pending fragmented seek");
- gst_qtdemux_do_fragmented_seek (qtdemux);
+ gst_qtdemux_do_fragmented_seek_pull (qtdemux);
GST_INFO_OBJECT (qtdemux, "fragmented seek done!");
qtdemux->fragmented_seek_pending = FALSE;
}
@@ -5155,13 +5369,14 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
case QTDEMUX_STATE_HEADER:{
const guint8 *data;
guint32 fourcc;
+ guint64 size;
GST_DEBUG_OBJECT (demux, "In header");
data = gst_adapter_map (demux->adapter, demux->neededbytes);
/* parse the header */
- extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
+ extract_initial_length_and_fourcc (data, demux->neededbytes, &size,
&fourcc);
if (fourcc == FOURCC_moov) {
/* in usual fragmented setup we could try to scan for more
@@ -5210,7 +5425,26 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
g_node_destroy (demux->moov_node);
demux->moov_node = NULL;
- GST_DEBUG_OBJECT (demux, "Finished parsing the header");
+ GST_DEBUG_OBJECT (demux,
+ "Finished parsing the header (currently at offset %"
+ G_GUINT64_FORMAT ")", demux->offset + size);
+
+ /* If fragmented, look for the mfro */
+ if (demux->fragmented && demux->upstream_seekable) {
+ demux->mdatoffset = demux->offset + size;
+ GST_DEBUG_OBJECT (demux, "Looking for potential mfro/mfra");
+ /* Let's check the last 16 bytes for the mfro atom */
+ if (qtdemux_seek_offset (demux, demux->upstream_size - 16)) {
+ demux->offset = demux->upstream_size - 16;
+ demux->neededbytes = 16;
+ demux->state = QTDEMUX_STATE_MFRO_SEARCH;
+ goto done;
+ }
+ GST_DEBUG_OBJECT (demux, "Seeking failed, resuming playback");
+ demux->offset = demux->mdatoffset;
+ demux->neededbytes = 16;
+ demux->state = QTDEMUX_STATE_INITIAL;
+ }
}
} else if (fourcc == FOURCC_moof) {
if ((demux->got_moov || demux->media_caps) && demux->fragmented) {
@@ -5357,6 +5591,66 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
break;
}
+ case QTDEMUX_STATE_MFRO_SEARCH:
+ {
+ const guint8 *data;
+ guint32 fourcc;
+ guint64 size;
+
+ data = gst_adapter_map (demux->adapter, demux->neededbytes);
+
+ /* get fourcc/length, set neededbytes */
+ extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
+ &size, &fourcc);
+ if (fourcc == FOURCC_mfro) {
+ guint32 mfra_size;
+ GST_DEBUG_OBJECT (demux, "Parsing [mfro]");
+ /* Extract mfra location from mfro and seek to there */
+ if (qtdemux_parse_mfro (demux, data, demux->neededbytes, &mfra_size)
+ && qtdemux_seek_offset (demux, demux->upstream_size - mfra_size)) {
+ demux->offset = demux->upstream_size - mfra_size;
+ demux->neededbytes = mfra_size;
+ demux->state = QTDEMUX_STATE_MFRA_SEARCH;
+ goto done;
+ }
+ }
+ gst_adapter_unmap (demux->adapter);
+ data = NULL;
+ GST_DEBUG_OBJECT (demux,
+ "Did not find 'mfro' at end of file, resuming normal playback");
+ demux->offset = demux->mdatoffset;
+ demux->neededbytes = 16;
+ demux->state = QTDEMUX_STATE_INITIAL;
+ break;
+ }
+ case QTDEMUX_STATE_MFRA_SEARCH:
+ {
+ const guint8 *data;
+ guint32 fourcc;
+ guint64 size;
+
+ data = gst_adapter_map (demux->adapter, demux->neededbytes);
+
+ /* get fourcc/length, set neededbytes */
+ extract_initial_length_and_fourcc ((guint8 *) data, demux->neededbytes,
+ &size, &fourcc);
+ if (fourcc == FOURCC_mfra) {
+ GNode *mfra_node;
+ GST_DEBUG_OBJECT (demux, "Found mfra");
+ mfra_node = g_node_new ((guint8 *) data);
+ qtdemux_parse_node (demux, mfra_node, data, demux->neededbytes);
+ qtdemux_node_dump (demux, mfra_node);
+ qtdemux_parse_mfra (demux, mfra_node);
+ GST_DEBUG_OBJECT (demux, "Resume parsing");
+ }
+ gst_adapter_unmap (demux->adapter);
+ data = NULL;
+ qtdemux_seek_offset (demux, demux->mdatoffset);
+ demux->offset = demux->mdatoffset;
+ demux->neededbytes = 16;
+ demux->state = QTDEMUX_STATE_INITIAL;
+ break;
+ }
case QTDEMUX_STATE_MOVIE:{
GstBuffer *outbuf;
QtDemuxStream *stream = NULL;
@@ -5369,8 +5663,9 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
"BEGIN // in MOVIE for offset %" G_GUINT64_FORMAT, demux->offset);
if (demux->fragmented) {
- GST_DEBUG_OBJECT (demux, "mdat remaining %" G_GUINT64_FORMAT,
- demux->mdatleft);
+ GST_DEBUG_OBJECT (demux,
+ "mdat remaining %" G_GUINT64_FORMAT " needed %u", demux->mdatleft,
+ demux->neededbytes);
if (G_LIKELY (demux->todrop < demux->mdatleft)) {
/* if needed data starts within this atom,
* then it should not exceed this atom */
@@ -5464,6 +5759,10 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
duration = QTSAMPLE_DUR_DTS (stream, sample, dts);
keyframe = QTSAMPLE_KEYFRAME (stream, sample);
+ GST_DEBUG_OBJECT (demux,
+ "Checking pts (%" GST_TIME_FORMAT ") against segment.stop (%"
+ GST_TIME_FORMAT ")", GST_TIME_ARGS (pts),
+ GST_TIME_ARGS (demux->segment.stop));
/* check for segment end */
if (G_UNLIKELY (demux->segment.stop != -1
&& demux->segment.stop <= pts && stream->on_keyframe)) {
@@ -7134,11 +7433,13 @@ done:
/* if index has been completely parsed, free data that is no-longer needed */
if (n + 1 == stream->n_samples) {
gst_qtdemux_stbl_free (stream);
- GST_DEBUG_OBJECT (qtdemux,
- "parsed all available samples; checking for more");
- while (n + 1 == stream->n_samples)
- if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
- break;
+ if (qtdemux->pullbased) {
+ GST_DEBUG_OBJECT (qtdemux,
+ "parsed all available samples; checking for more");
+ while (n + 1 == stream->n_samples)
+ if (qtdemux_add_fragmented_samples (qtdemux) != GST_FLOW_OK)
+ break;
+ }
}
GST_OBJECT_UNLOCK (qtdemux);
diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h
index a562e6263..c0f207cbe 100644
--- a/gst/isomp4/qtdemux.h
+++ b/gst/isomp4/qtdemux.h
@@ -128,6 +128,10 @@ struct _GstQTDemux {
gint64 push_seek_start;
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 */
+ gboolean doing_fragment_seek;
#if 0
/* gst index support */
GstIndex *element_index;