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. */
|
/* What to skip before effectively reading a packet. */
|
||||||
int skip_to_keyframe;
|
int skip_to_keyframe;
|
||||||
uint64_t skip_to_timecode;
|
uint64_t skip_to_timecode;
|
||||||
|
|
||||||
|
/* File has a CUES element, but we defer parsing until it is needed. */
|
||||||
|
int cues_parsing_deferred;
|
||||||
} MatroskaDemuxContext;
|
} MatroskaDemuxContext;
|
||||||
|
|
||||||
typedef struct {
|
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;
|
EbmlList *seekhead_list = &matroska->seekhead;
|
||||||
MatroskaSeekhead *seekhead = seekhead_list->elem;
|
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);
|
int64_t before_pos = avio_tell(matroska->ctx->pb);
|
||||||
uint32_t saved_id = matroska->current_id;
|
uint32_t saved_id = matroska->current_id;
|
||||||
MatroskaLevel level;
|
MatroskaLevel level;
|
||||||
int i;
|
int64_t offset;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
// we should not do any seeking in the streaming case
|
if (idx >= seekhead_list->nb_elem
|
||||||
if (!matroska->ctx->pb->seekable ||
|
|| seekhead[idx].id == MATROSKA_ID_SEEKHEAD
|
||||||
(matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
|
|| seekhead[idx].id == MATROSKA_ID_CLUSTER)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* seek */
|
/* seek */
|
||||||
if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) != offset)
|
offset = seekhead[idx].pos + matroska->segment_start;
|
||||||
continue;
|
if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) == offset) {
|
||||||
|
|
||||||
/* We don't want to lose our seekhead level, so we add
|
/* We don't want to lose our seekhead level, so we add
|
||||||
* a dummy. This is a crude hack. */
|
* a dummy. This is a crude hack. */
|
||||||
if (matroska->num_levels == EBML_MAX_DEPTH) {
|
if (matroska->num_levels == EBML_MAX_DEPTH) {
|
||||||
av_log(matroska->ctx, AV_LOG_INFO,
|
av_log(matroska->ctx, AV_LOG_INFO,
|
||||||
"Max EBML element depth (%d) reached, "
|
"Max EBML element depth (%d) reached, "
|
||||||
"cannot parse further.\n", EBML_MAX_DEPTH);
|
"cannot parse further.\n", EBML_MAX_DEPTH);
|
||||||
break;
|
ret = AVERROR_INVALIDDATA;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
level.start = 0;
|
level.start = 0;
|
||||||
level.length = (uint64_t)-1;
|
level.length = (uint64_t)-1;
|
||||||
matroska->levels[matroska->num_levels] = level;
|
matroska->levels[matroska->num_levels] = level;
|
||||||
@ -1161,11 +1155,76 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* seek back */
|
/* seek back */
|
||||||
avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
|
avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
|
||||||
matroska->level_up = level_up;
|
matroska->level_up = level_up;
|
||||||
matroska->current_id = saved_id;
|
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)
|
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;
|
EbmlList *chapters_list = &matroska->chapters;
|
||||||
MatroskaChapter *chapters;
|
MatroskaChapter *chapters;
|
||||||
MatroskaTrack *tracks;
|
MatroskaTrack *tracks;
|
||||||
EbmlList *index_list;
|
|
||||||
MatroskaIndex *index;
|
|
||||||
int index_scale = 1;
|
|
||||||
uint64_t max_start = 0;
|
uint64_t max_start = 0;
|
||||||
Ebml ebml = { 0 };
|
Ebml ebml = { 0 };
|
||||||
AVStream *st;
|
AVStream *st;
|
||||||
@ -1529,27 +1585,6 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
|||||||
max_start = chapters[i].start;
|
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);
|
matroska_convert_tags(s);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1898,6 +1933,12 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
|
|||||||
AVStream *st = s->streams[stream_index];
|
AVStream *st = s->streams[stream_index];
|
||||||
int i, index, index_sub, index_min;
|
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)
|
if (!st->nb_index_entries)
|
||||||
return 0;
|
return 0;
|
||||||
timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);
|
timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user