mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
matroskadec: defer parsing of cues element until we seek.
This decreases startup latency. Signed-off-by: Ronald S. Bultje <rsbultje@gmail.com> Signed-off-by: Anton Khirnov <anton@khirnov.net>
This commit is contained in:
parent
84626b364b
commit
31ad14c21e
@ -244,6 +244,9 @@ typedef struct {
|
||||
/* What to skip before effectively reading a packet. */
|
||||
int skip_to_keyframe;
|
||||
uint64_t skip_to_timecode;
|
||||
|
||||
/* File has a CUES element, but we defer parsing until it is needed. */
|
||||
int cues_parsing_deferred;
|
||||
} MatroskaDemuxContext;
|
||||
|
||||
typedef struct {
|
||||
@ -1110,7 +1113,7 @@ static void matroska_convert_tags(AVFormatContext *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
|
||||
static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska, int idx)
|
||||
{
|
||||
EbmlList *seekhead_list = &matroska->seekhead;
|
||||
MatroskaSeekhead *seekhead = seekhead_list->elem;
|
||||
@ -1118,34 +1121,25 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
|
||||
int64_t before_pos = avio_tell(matroska->ctx->pb);
|
||||
uint32_t saved_id = matroska->current_id;
|
||||
MatroskaLevel level;
|
||||
int i;
|
||||
int64_t offset;
|
||||
int ret = 0;
|
||||
|
||||
// we should not do any seeking in the streaming case
|
||||
if (!matroska->ctx->pb->seekable ||
|
||||
(matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
|
||||
return;
|
||||
|
||||
for (i=0; i<seekhead_list->nb_elem; i++) {
|
||||
int64_t offset = seekhead[i].pos + matroska->segment_start;
|
||||
|
||||
if (seekhead[i].pos <= before_pos
|
||||
|| seekhead[i].id == MATROSKA_ID_SEEKHEAD
|
||||
|| seekhead[i].id == MATROSKA_ID_CLUSTER)
|
||||
continue;
|
||||
if (idx >= seekhead_list->nb_elem
|
||||
|| seekhead[idx].id == MATROSKA_ID_SEEKHEAD
|
||||
|| seekhead[idx].id == MATROSKA_ID_CLUSTER)
|
||||
return 0;
|
||||
|
||||
/* seek */
|
||||
if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) != offset)
|
||||
continue;
|
||||
|
||||
offset = seekhead[idx].pos + matroska->segment_start;
|
||||
if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) == offset) {
|
||||
/* We don't want to lose our seekhead level, so we add
|
||||
* a dummy. This is a crude hack. */
|
||||
if (matroska->num_levels == EBML_MAX_DEPTH) {
|
||||
av_log(matroska->ctx, AV_LOG_INFO,
|
||||
"Max EBML element depth (%d) reached, "
|
||||
"cannot parse further.\n", EBML_MAX_DEPTH);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = AVERROR_INVALIDDATA;
|
||||
} else {
|
||||
level.start = 0;
|
||||
level.length = (uint64_t)-1;
|
||||
matroska->levels[matroska->num_levels] = level;
|
||||
@ -1161,11 +1155,76 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/* seek back */
|
||||
avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
|
||||
matroska->level_up = level_up;
|
||||
matroska->current_id = saved_id;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
|
||||
{
|
||||
EbmlList *seekhead_list = &matroska->seekhead;
|
||||
MatroskaSeekhead *seekhead = seekhead_list->elem;
|
||||
int64_t before_pos = avio_tell(matroska->ctx->pb);
|
||||
int i;
|
||||
|
||||
// we should not do any seeking in the streaming case
|
||||
if (!matroska->ctx->pb->seekable ||
|
||||
(matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
|
||||
return;
|
||||
|
||||
for (i = 0; i < seekhead_list->nb_elem; i++) {
|
||||
if (seekhead[i].pos <= before_pos)
|
||||
continue;
|
||||
|
||||
// defer cues parsing until we actually need cue data.
|
||||
if (seekhead[i].id == MATROSKA_ID_CUES) {
|
||||
matroska->cues_parsing_deferred = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matroska_parse_seekhead_entry(matroska, i) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void matroska_parse_cues(MatroskaDemuxContext *matroska) {
|
||||
EbmlList *seekhead_list = &matroska->seekhead;
|
||||
MatroskaSeekhead *seekhead = seekhead_list->elem;
|
||||
EbmlList *index_list;
|
||||
MatroskaIndex *index;
|
||||
int index_scale = 1;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < seekhead_list->nb_elem; i++)
|
||||
if (seekhead[i].id != MATROSKA_ID_CUES)
|
||||
break;
|
||||
assert(i <= seekhead_list->nb_elem);
|
||||
|
||||
matroska_parse_seekhead_entry(matroska, i);
|
||||
|
||||
index_list = &matroska->index;
|
||||
index = index_list->elem;
|
||||
if (index_list->nb_elem
|
||||
&& index[0].time > 1E14/matroska->time_scale) {
|
||||
av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n");
|
||||
index_scale = matroska->time_scale;
|
||||
}
|
||||
for (i = 0; i < index_list->nb_elem; i++) {
|
||||
EbmlList *pos_list = &index[i].pos;
|
||||
MatroskaIndexPos *pos = pos_list->elem;
|
||||
for (j = 0; j < pos_list->nb_elem; j++) {
|
||||
MatroskaTrack *track = matroska_find_track_by_num(matroska, pos[j].track);
|
||||
if (track && track->stream)
|
||||
av_add_index_entry(track->stream,
|
||||
pos[j].pos + matroska->segment_start,
|
||||
index[i].time/index_scale, 0, 0,
|
||||
AVINDEX_KEYFRAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int matroska_aac_profile(char *codec_id)
|
||||
@ -1197,9 +1256,6 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
EbmlList *chapters_list = &matroska->chapters;
|
||||
MatroskaChapter *chapters;
|
||||
MatroskaTrack *tracks;
|
||||
EbmlList *index_list;
|
||||
MatroskaIndex *index;
|
||||
int index_scale = 1;
|
||||
uint64_t max_start = 0;
|
||||
Ebml ebml = { 0 };
|
||||
AVStream *st;
|
||||
@ -1529,27 +1585,6 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
||||
max_start = chapters[i].start;
|
||||
}
|
||||
|
||||
index_list = &matroska->index;
|
||||
index = index_list->elem;
|
||||
if (index_list->nb_elem
|
||||
&& index[0].time > 100000000000000/matroska->time_scale) {
|
||||
av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n");
|
||||
index_scale = matroska->time_scale;
|
||||
}
|
||||
for (i=0; i<index_list->nb_elem; i++) {
|
||||
EbmlList *pos_list = &index[i].pos;
|
||||
MatroskaIndexPos *pos = pos_list->elem;
|
||||
for (j=0; j<pos_list->nb_elem; j++) {
|
||||
MatroskaTrack *track = matroska_find_track_by_num(matroska,
|
||||
pos[j].track);
|
||||
if (track && track->stream)
|
||||
av_add_index_entry(track->stream,
|
||||
pos[j].pos + matroska->segment_start,
|
||||
index[i].time/index_scale, 0, 0,
|
||||
AVINDEX_KEYFRAME);
|
||||
}
|
||||
}
|
||||
|
||||
matroska_convert_tags(s);
|
||||
|
||||
return 0;
|
||||
@ -1898,6 +1933,12 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
|
||||
AVStream *st = s->streams[stream_index];
|
||||
int i, index, index_sub, index_min;
|
||||
|
||||
/* Parse the CUES now since we need the index data to seek. */
|
||||
if (matroska->cues_parsing_deferred) {
|
||||
matroska_parse_cues(matroska);
|
||||
matroska->cues_parsing_deferred = 0;
|
||||
}
|
||||
|
||||
if (!st->nb_index_entries)
|
||||
return 0;
|
||||
timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);
|
||||
|
Loading…
Reference in New Issue
Block a user