demux_mkv: defer reading of seek index until first seek

Playing Youtube videos often requires an additional seek to the end of
the file. This flushes the stream cache. The reason for the seek is
reading the cues (seek index). This poses the question why Google is
muxing its files in such a way, since nothing in Matroska mandates that
cues are located at the end of the file, but we want to handle this
situation better anyway.

The seek index is not needed for normal playback, only for seeking.
This commit changes header parsing such that the index is not read on
initialization in order to avoid the additional stream-level seek.
Instead, read the index on the first demuxer-level seek, when the seek
index is actually needed.

If the cues are at the beginning of the file, they are read immediately
as part of the normal header reading process. This commit changes
behavior only if cues are outside of the header (i.e. not in the area
between EBML header and clusters), and linked by a SeekHead. Other
level 1 elements linked by the SeekHead might still cause seeks to the
end of the file, although that seems to be rare.
This commit is contained in:
wm4 2013-05-23 00:26:42 +02:00
parent 1139eae082
commit 5bdf9d01ca
1 changed files with 42 additions and 3 deletions

View File

@ -166,6 +166,7 @@ typedef struct mkv_demuxer {
mkv_index_t *indexes;
int num_indexes;
bool index_complete;
uint64_t deferred_cues;
int64_t *parsed_pos;
int num_parsed_pos;
@ -721,6 +722,9 @@ static int demux_mkv_read_cues(demuxer_t *demuxer)
struct ebml_parse_ctx parse_ctx = {};
if (ebml_read_element(s, &parse_ctx, &cues, &ebml_cues_desc) < 0)
return -1;
mkv_d->num_indexes = 0;
for (int i = 0; i < cues.n_cue_point; i++) {
struct ebml_cue_point *cuepoint = &cues.cue_point[i];
if (cuepoint->n_cue_time != 1 || !cuepoint->n_cue_track_positions) {
@ -748,6 +752,26 @@ static int demux_mkv_read_cues(demuxer_t *demuxer)
return 0;
}
static void read_deferred_cues(demuxer_t *demuxer)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
stream_t *s = demuxer->stream;
if (mkv_d->deferred_cues) {
int64_t pos = mkv_d->deferred_cues;
mkv_d->deferred_cues = 0;
if (!stream_seek(s, pos)) {
mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Failed to seek to cues\n");
return;
}
if (ebml_read_id(s, NULL) != MATROSKA_ID_CUES) {
mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Expected element not found\n");
return;
}
demux_mkv_read_cues(demuxer);
}
}
static int demux_mkv_read_chapters(struct demuxer *demuxer)
{
struct MPOpts *opts = demuxer->opts;
@ -1019,9 +1043,13 @@ static int read_header_element(struct demuxer *demuxer, uint32_t id,
case MATROSKA_ID_CUES:
if (mkv_d->parsed_cues)
break;
if (at_filepos && !seek_pos_id(s, at_filepos, id))
return -1;
if (at_filepos) {
// Read cues when they are needed, to avoid seeking on opening.
mkv_d->deferred_cues = at_filepos;
return 1;
}
mkv_d->parsed_cues = true;
mkv_d->deferred_cues = 0;
return demux_mkv_read_cues(demuxer);
case MATROSKA_ID_TAGS:
@ -2364,6 +2392,8 @@ static int create_index_until(struct demuxer *demuxer, uint64_t timecode)
struct mkv_demuxer *mkv_d = demuxer->priv;
struct stream *s = demuxer->stream;
read_deferred_cues(demuxer);
if (mkv_d->index_complete)
return 0;
@ -2462,6 +2492,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
float audio_delay, int flags)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
int64_t old_pos = stream_tell(demuxer->stream);
uint64_t v_tnum = -1;
uint64_t a_tnum = -1;
bool st_active[STREAM_TYPE_COUNT] = {0};
@ -2501,6 +2532,9 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
index = seek_with_cues(demuxer, -1, target_timecode, flags);
}
if (!index)
stream_seek(demuxer->stream, old_pos);
if (st_active[STREAM_VIDEO])
mkv_d->v_skip_to_keyframe = 1;
if (flags & SEEK_FORWARD)
@ -2519,8 +2553,11 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
mkv_index_t *index = NULL;
int i;
read_deferred_cues(demuxer);
if (!mkv_d->index_complete) { /* not implemented without index */
mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek unsupported flags\n");
stream_seek(s, old_pos);
return;
}
@ -2533,8 +2570,10 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
|| (mkv_d->indexes[i].filepos < index->filepos))))
index = &mkv_d->indexes[i];
if (!index)
if (!index) {
stream_seek(s, old_pos);
return;
}
mkv_d->cluster_end = 0;
stream_seek(s, index->filepos);