From 86a1aec2c044f1e92409d5e2d5bab1673a5f27fc Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 18 Feb 2010 12:42:31 +0100 Subject: [PATCH] flvdemux: incrementally build index in pull mode Scan for needed part upon a seek as opposed to doing a complete scan at startup, which may take some time depending on file and/or platform. Also accept index metadata in pull mode and peek for some metadata at the end of the file when deemed appropriate. --- gst/flv/gstflvdemux.c | 108 +++++++++++++++++++++++++++++++++++++++++++++----- gst/flv/gstflvdemux.h | 3 ++ gst/flv/gstflvparse.c | 16 +++++--- gst/flv/gstflvparse.h | 3 +- 4 files changed, 113 insertions(+), 17 deletions(-) diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c index 1bb523d..70a00b8 100644 --- a/gst/flv/gstflvdemux.c +++ b/gst/flv/gstflvdemux.c @@ -119,6 +119,9 @@ gst_flv_demux_cleanup (GstFLVDemux * demux) demux->indexed = FALSE; demux->file_size = 0; + demux->index_max_pos = 0; + demux->index_max_time = 0; + demux->audio_start = demux->video_start = GST_CLOCK_TIME_NONE; demux->no_more_pads = FALSE; @@ -583,35 +586,105 @@ gst_flv_demux_push_src_event (GstFLVDemux * demux, GstEvent * event) } static void -gst_flv_demux_create_index (GstFLVDemux * demux) +gst_flv_demux_create_index (GstFLVDemux * demux, gint64 pos, GstClockTime ts) { gint64 size; GstFormat fmt = GST_FORMAT_BYTES; size_t tag_size; guint64 old_offset; GstBuffer *buffer; + GstClockTime tag_time; if (G_UNLIKELY (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &size) || fmt != GST_FORMAT_BYTES)) return; + GST_DEBUG_OBJECT (demux, "building index at %" G_GINT64_FORMAT + " looking for time %" GST_TIME_FORMAT, pos, GST_TIME_ARGS (ts)); + old_offset = demux->offset; + demux->offset = pos; while (gst_flv_demux_pull_range (demux, demux->sinkpad, demux->offset, 12, &buffer) == GST_FLOW_OK) { - if (G_UNLIKELY (gst_flv_parse_tag_timestamp (demux, buffer, - &tag_size) == GST_CLOCK_TIME_NONE)) { - gst_buffer_unref (buffer); - break; - } + tag_time = gst_flv_parse_tag_timestamp (demux, TRUE, buffer, &tag_size); gst_buffer_unref (buffer); + + if (G_UNLIKELY (tag_time == GST_CLOCK_TIME_NONE || tag_time > ts)) + goto exit; + demux->offset += tag_size; } + /* file ran out, so mark we have complete index */ + demux->indexed = TRUE; + +exit: demux->offset = old_offset; } +static gint64 +gst_flv_demux_get_metadata (GstFLVDemux * demux) +{ + gint64 ret, offset; + GstFormat fmt = GST_FORMAT_BYTES; + size_t tag_size, size; + GstBuffer *buffer = NULL; + + if (G_UNLIKELY (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &offset) + || fmt != GST_FORMAT_BYTES)) + goto exit; + + ret = offset; + GST_DEBUG_OBJECT (demux, "upstream size: %" G_GINT64_FORMAT, offset); + if (G_UNLIKELY (offset < 4)) + goto exit; + + offset -= 4; + if (GST_FLOW_OK != gst_flv_demux_pull_range (demux, demux->sinkpad, offset, + 4, &buffer)) + goto exit; + + tag_size = GST_READ_UINT32_BE (GST_BUFFER_DATA (buffer)); + GST_DEBUG_OBJECT (demux, "last tag size: %d", tag_size); + gst_buffer_unref (buffer); + buffer = NULL; + + offset -= tag_size; + if (GST_FLOW_OK != gst_flv_demux_pull_range (demux, demux->sinkpad, offset, + 12, &buffer)) + goto exit; + + /* a consistency check */ + size = GST_READ_UINT24_BE (GST_BUFFER_DATA (buffer) + 1); + if (size != tag_size - 11) { + GST_DEBUG_OBJECT (demux, "tag size %d, expected %d, ", + "corrupt or truncated file", size, tag_size - 11); + goto exit; + } + + /* try to update duration with timestamp in any case */ + gst_flv_parse_tag_timestamp (demux, FALSE, buffer, &size); + + /* maybe get some more metadata */ + if (GST_BUFFER_DATA (buffer)[0] == 18) { + gst_buffer_unref (buffer); + buffer = NULL; + GST_DEBUG_OBJECT (demux, "script tag, pulling it to parse"); + offset += 4; + if (GST_FLOW_OK == gst_flv_demux_pull_range (demux, demux->sinkpad, offset, + tag_size, &buffer)) + gst_flv_parse_tag_script (demux, buffer); + } + +exit: + if (buffer) + gst_buffer_unref (buffer); + + return ret; +} + static void gst_flv_demux_loop (GstPad * pad) { @@ -625,15 +698,21 @@ gst_flv_demux_loop (GstPad * pad) switch (demux->state) { case FLV_STATE_TAG_TYPE: ret = gst_flv_demux_pull_tag (pad, demux); + /* if we have seen real data, we probably passed a possible metadata + * header located at start. So if we do not yet have an index, + * try to pick up metadata (index, duration) at the end */ + if (G_UNLIKELY (!demux->file_size && !demux->indexed && + (demux->has_video || demux->has_audio))) + demux->file_size = gst_flv_demux_get_metadata (demux); break; case FLV_STATE_DONE: ret = GST_FLOW_UNEXPECTED; break; default: ret = gst_flv_demux_pull_header (pad, demux); - if (ret == GST_FLOW_OK) - gst_flv_demux_create_index (demux); - + /* index scans start after header */ + demux->index_max_pos = demux->offset; + break; } /* pause if something went wrong */ @@ -663,7 +742,7 @@ gst_flv_demux_loop (GstPad * pad) default: ret = gst_flv_demux_pull_header (pad, demux); if (ret == GST_FLOW_OK) - gst_flv_demux_create_index (demux); + gst_flv_demux_create_index (demux, demux->offset, G_MAXINT64); } /* pause if something went wrong */ @@ -978,6 +1057,15 @@ gst_flv_demux_handle_seek_pull (GstFLVDemux * demux, GstEvent * event) if (flush || seeksegment.last_stop != demux->segment.last_stop) { /* Do the actual seeking */ + /* index is reliable if it is complete or we do not go to far ahead */ + if (!demux->indexed && + seeksegment.last_stop > demux->index_max_time + 10 * GST_SECOND) { + /* scan and build index from current maximum offset to desired time */ + /* NOTE this will _pull_range from seeking thread, but should be ok ... */ + gst_flv_demux_create_index (demux, demux->index_max_pos, + seeksegment.last_stop); + } + /* now index should be as reliable as it can be for current purpose */ demux->offset = gst_flv_demux_find_offset (demux, &seeksegment); /* Tell all the stream we moved to a different position (discont) */ diff --git a/gst/flv/gstflvdemux.h b/gst/flv/gstflvdemux.h index 4c82e5b..8027c6b 100644 --- a/gst/flv/gstflvdemux.h +++ b/gst/flv/gstflvdemux.h @@ -125,6 +125,9 @@ struct _GstFLVDemux gboolean indexed; /* TRUE if index is completely built */ gint64 file_size; GstEvent *seek_event; + + GstClockTime index_max_time; + gint64 index_max_pos; }; struct _GstFLVDemuxClass diff --git a/gst/flv/gstflvparse.c b/gst/flv/gstflvparse.c index a02660b..eb2180b 100644 --- a/gst/flv/gstflvparse.c +++ b/gst/flv/gstflvparse.c @@ -44,6 +44,11 @@ gst_flv_parse_add_index_entry (GstFLVDemux * demux, GstClockTime ts, gst_index_add_associationv (demux->index, demux->index_id, (keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE, 2, (const GstIndexAssociation *) &associations); + + if (pos > demux->index_max_pos) + demux->index_max_pos = pos; + if (ts > demux->index_max_time) + demux->index_max_time = ts; } static gchar * @@ -440,11 +445,10 @@ gst_flv_parse_tag_script (GstFLVDemux * demux, GstBuffer * buffer) g_free (function_name); - if (demux->index && demux->times && demux->filepositions - && !demux->random_access) { + if (demux->index && demux->times && demux->filepositions) { guint num; - /* If an index was found and we're in push mode, insert associations */ + /* If an index was found, insert associations */ num = MIN (demux->times->len, demux->filepositions->len); for (i = 0; i < num; i++) { guint64 time, fileposition; @@ -1182,8 +1186,8 @@ beach: } GstClockTime -gst_flv_parse_tag_timestamp (GstFLVDemux * demux, GstBuffer * buffer, - size_t * tag_size) +gst_flv_parse_tag_timestamp (GstFLVDemux * demux, gboolean index, + GstBuffer * buffer, size_t * tag_size) { guint32 pts = 0, pts_ext = 0; guint32 tag_data_size; @@ -1239,7 +1243,7 @@ gst_flv_parse_tag_timestamp (GstFLVDemux * demux, GstBuffer * buffer, ret = pts * GST_MSECOND; GST_LOG_OBJECT (demux, "pts: %" GST_TIME_FORMAT, GST_TIME_ARGS (ret)); - if (demux->index && !demux->indexed && (type == 9 || (type == 8 + if (index && demux->index && !demux->indexed && (type == 9 || (type == 8 && !demux->has_video))) { gst_flv_parse_add_index_entry (demux, ret, demux->offset, keyframe); } diff --git a/gst/flv/gstflvparse.h b/gst/flv/gstflvparse.h index 203d30d..0875380 100644 --- a/gst/flv/gstflvparse.h +++ b/gst/flv/gstflvparse.h @@ -36,7 +36,8 @@ GstFlowReturn gst_flv_parse_tag_type (GstFLVDemux * demux, GstBuffer *buffer); GstFlowReturn gst_flv_parse_header (GstFLVDemux * demux, GstBuffer *buffer); -GstClockTime gst_flv_parse_tag_timestamp (GstFLVDemux *demux, GstBuffer *buffer, size_t *tag_data_size); +GstClockTime gst_flv_parse_tag_timestamp (GstFLVDemux *demux, gboolean index, + GstBuffer *buffer, size_t *tag_data_size); G_END_DECLS #endif /* __FLV_PARSE_H__ */ -- 2.7.4