mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
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.
This commit is contained in:
parent
66fabd8bfd
commit
86a1aec2c0
4 changed files with 113 additions and 17 deletions
|
@ -119,6 +119,9 @@ gst_flv_demux_cleanup (GstFLVDemux * demux)
|
||||||
demux->indexed = FALSE;
|
demux->indexed = FALSE;
|
||||||
demux->file_size = 0;
|
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->audio_start = demux->video_start = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
demux->no_more_pads = FALSE;
|
demux->no_more_pads = FALSE;
|
||||||
|
@ -583,35 +586,105 @@ gst_flv_demux_push_src_event (GstFLVDemux * demux, GstEvent * event)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_flv_demux_create_index (GstFLVDemux * demux)
|
gst_flv_demux_create_index (GstFLVDemux * demux, gint64 pos, GstClockTime ts)
|
||||||
{
|
{
|
||||||
gint64 size;
|
gint64 size;
|
||||||
GstFormat fmt = GST_FORMAT_BYTES;
|
GstFormat fmt = GST_FORMAT_BYTES;
|
||||||
size_t tag_size;
|
size_t tag_size;
|
||||||
guint64 old_offset;
|
guint64 old_offset;
|
||||||
GstBuffer *buffer;
|
GstBuffer *buffer;
|
||||||
|
GstClockTime tag_time;
|
||||||
|
|
||||||
if (G_UNLIKELY (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &size) ||
|
if (G_UNLIKELY (!gst_pad_query_peer_duration (demux->sinkpad, &fmt, &size) ||
|
||||||
fmt != GST_FORMAT_BYTES))
|
fmt != GST_FORMAT_BYTES))
|
||||||
return;
|
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;
|
old_offset = demux->offset;
|
||||||
|
demux->offset = pos;
|
||||||
|
|
||||||
while (gst_flv_demux_pull_range (demux, demux->sinkpad, demux->offset, 12,
|
while (gst_flv_demux_pull_range (demux, demux->sinkpad, demux->offset, 12,
|
||||||
&buffer) == GST_FLOW_OK) {
|
&buffer) == GST_FLOW_OK) {
|
||||||
if (G_UNLIKELY (gst_flv_parse_tag_timestamp (demux, buffer,
|
tag_time = gst_flv_parse_tag_timestamp (demux, TRUE, buffer, &tag_size);
|
||||||
&tag_size) == GST_CLOCK_TIME_NONE)) {
|
|
||||||
gst_buffer_unref (buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_buffer_unref (buffer);
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (tag_time == GST_CLOCK_TIME_NONE || tag_time > ts))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
demux->offset += tag_size;
|
demux->offset += tag_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* file ran out, so mark we have complete index */
|
||||||
|
demux->indexed = TRUE;
|
||||||
|
|
||||||
|
exit:
|
||||||
demux->offset = old_offset;
|
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
|
static void
|
||||||
gst_flv_demux_loop (GstPad * pad)
|
gst_flv_demux_loop (GstPad * pad)
|
||||||
{
|
{
|
||||||
|
@ -625,15 +698,21 @@ gst_flv_demux_loop (GstPad * pad)
|
||||||
switch (demux->state) {
|
switch (demux->state) {
|
||||||
case FLV_STATE_TAG_TYPE:
|
case FLV_STATE_TAG_TYPE:
|
||||||
ret = gst_flv_demux_pull_tag (pad, demux);
|
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;
|
break;
|
||||||
case FLV_STATE_DONE:
|
case FLV_STATE_DONE:
|
||||||
ret = GST_FLOW_UNEXPECTED;
|
ret = GST_FLOW_UNEXPECTED;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = gst_flv_demux_pull_header (pad, demux);
|
ret = gst_flv_demux_pull_header (pad, demux);
|
||||||
if (ret == GST_FLOW_OK)
|
/* index scans start after header */
|
||||||
gst_flv_demux_create_index (demux);
|
demux->index_max_pos = demux->offset;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pause if something went wrong */
|
/* pause if something went wrong */
|
||||||
|
@ -663,7 +742,7 @@ gst_flv_demux_loop (GstPad * pad)
|
||||||
default:
|
default:
|
||||||
ret = gst_flv_demux_pull_header (pad, demux);
|
ret = gst_flv_demux_pull_header (pad, demux);
|
||||||
if (ret == GST_FLOW_OK)
|
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 */
|
/* 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) {
|
if (flush || seeksegment.last_stop != demux->segment.last_stop) {
|
||||||
/* Do the actual seeking */
|
/* 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);
|
demux->offset = gst_flv_demux_find_offset (demux, &seeksegment);
|
||||||
|
|
||||||
/* Tell all the stream we moved to a different position (discont) */
|
/* Tell all the stream we moved to a different position (discont) */
|
||||||
|
|
|
@ -125,6 +125,9 @@ struct _GstFLVDemux
|
||||||
gboolean indexed; /* TRUE if index is completely built */
|
gboolean indexed; /* TRUE if index is completely built */
|
||||||
gint64 file_size;
|
gint64 file_size;
|
||||||
GstEvent *seek_event;
|
GstEvent *seek_event;
|
||||||
|
|
||||||
|
GstClockTime index_max_time;
|
||||||
|
gint64 index_max_pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstFLVDemuxClass
|
struct _GstFLVDemuxClass
|
||||||
|
|
|
@ -44,6 +44,11 @@ gst_flv_parse_add_index_entry (GstFLVDemux * demux, GstClockTime ts,
|
||||||
gst_index_add_associationv (demux->index, demux->index_id,
|
gst_index_add_associationv (demux->index, demux->index_id,
|
||||||
(keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE,
|
(keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE,
|
||||||
2, (const GstIndexAssociation *) &associations);
|
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 *
|
static gchar *
|
||||||
|
@ -440,11 +445,10 @@ gst_flv_parse_tag_script (GstFLVDemux * demux, GstBuffer * buffer)
|
||||||
|
|
||||||
g_free (function_name);
|
g_free (function_name);
|
||||||
|
|
||||||
if (demux->index && demux->times && demux->filepositions
|
if (demux->index && demux->times && demux->filepositions) {
|
||||||
&& !demux->random_access) {
|
|
||||||
guint num;
|
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);
|
num = MIN (demux->times->len, demux->filepositions->len);
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
guint64 time, fileposition;
|
guint64 time, fileposition;
|
||||||
|
@ -1182,8 +1186,8 @@ beach:
|
||||||
}
|
}
|
||||||
|
|
||||||
GstClockTime
|
GstClockTime
|
||||||
gst_flv_parse_tag_timestamp (GstFLVDemux * demux, GstBuffer * buffer,
|
gst_flv_parse_tag_timestamp (GstFLVDemux * demux, gboolean index,
|
||||||
size_t * tag_size)
|
GstBuffer * buffer, size_t * tag_size)
|
||||||
{
|
{
|
||||||
guint32 pts = 0, pts_ext = 0;
|
guint32 pts = 0, pts_ext = 0;
|
||||||
guint32 tag_data_size;
|
guint32 tag_data_size;
|
||||||
|
@ -1239,7 +1243,7 @@ gst_flv_parse_tag_timestamp (GstFLVDemux * demux, GstBuffer * buffer,
|
||||||
ret = pts * GST_MSECOND;
|
ret = pts * GST_MSECOND;
|
||||||
GST_LOG_OBJECT (demux, "pts: %" GST_TIME_FORMAT, GST_TIME_ARGS (ret));
|
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))) {
|
&& !demux->has_video))) {
|
||||||
gst_flv_parse_add_index_entry (demux, ret, demux->offset, keyframe);
|
gst_flv_parse_add_index_entry (demux, ret, demux->offset, keyframe);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,8 @@ GstFlowReturn gst_flv_parse_tag_type (GstFLVDemux * demux, GstBuffer *buffer);
|
||||||
|
|
||||||
GstFlowReturn gst_flv_parse_header (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
|
G_END_DECLS
|
||||||
#endif /* __FLV_PARSE_H__ */
|
#endif /* __FLV_PARSE_H__ */
|
||||||
|
|
Loading…
Reference in a new issue