diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c index 203523ff62..fe925f15ec 100644 --- a/libmpdemux/demux_mkv.c +++ b/libmpdemux/demux_mkv.c @@ -175,10 +175,13 @@ typedef struct mkv_demuxer { mkv_index_t *indexes; int num_indexes; - off_t *parsed_cues; - int parsed_cues_num; - off_t *parsed_seekhead; - int parsed_seekhead_num; + off_t *parsed_pos; + int num_parsed_pos; + bool parsed_info; + bool parsed_tracks; + bool parsed_tags; + bool parsed_chapters; + bool parsed_attachments; struct cluster_pos { uint64_t filepos; @@ -211,6 +214,29 @@ static void *grow_array(void *array, int nelem, size_t elsize) return array; } +static bool is_parsed_header(struct mkv_demuxer *mkv_d, off_t pos) +{ + int low = 0; + int high = mkv_d->num_parsed_pos; + while (high > low + 1) { + int mid = high + low >> 1; + if (mkv_d->parsed_pos[mid] > pos) + high = mid; + else + low = mid; + } + if (mkv_d->num_parsed_pos && mkv_d->parsed_pos[low] == pos) + return true; + if (!(mkv_d->num_parsed_pos & 31)) + mkv_d->parsed_pos = talloc_realloc(mkv_d, mkv_d->parsed_pos, off_t, + mkv_d->num_parsed_pos + 32); + mkv_d->num_parsed_pos++; + for (int i = mkv_d->num_parsed_pos - 1; i > low; i--) + mkv_d->parsed_pos[i] = mkv_d->parsed_pos[i - 1]; + mkv_d->parsed_pos[low] = pos; + return false; +} + static mkv_track_t *demux_mkv_find_track_by_num(mkv_demuxer_t *d, int n, int type) { @@ -930,23 +956,12 @@ static int demux_mkv_read_cues(demuxer_t *demuxer) mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; stream_t *s = demuxer->stream; uint64_t length, l, time, track, pos; - off_t off; int i, il; if (index_mode == 0 || index_mode == 2) { ebml_read_skip(s, NULL); return 0; } - off = stream_tell(s); - for (i = 0; i < mkv_d->parsed_cues_num; i++) - if (mkv_d->parsed_cues[i] == off) { - ebml_read_skip(s, NULL); - return 0; - } - mkv_d->parsed_cues = - realloc(mkv_d->parsed_cues, (mkv_d->parsed_cues_num + 1) - * sizeof(off_t)); - mkv_d->parsed_cues[mkv_d->parsed_cues_num++] = off; mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing cues ] -----------\n"); length = ebml_read_length(s, NULL); @@ -1147,11 +1162,6 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer) int i; uint32_t id; - if (demuxer->chapters) { - ebml_read_skip(s, NULL); - return 0; - } - mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing chapters ] ---------\n"); length = ebml_read_length(s, NULL); @@ -1356,119 +1366,140 @@ static int demux_mkv_read_attachments(demuxer_t *demuxer) return 0; } +static int read_header_element(struct demuxer *demuxer, uint32_t id, + off_t at_filepos); + static int demux_mkv_read_seekhead(demuxer_t *demuxer) { - mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; - stream_t *s = demuxer->stream; - uint64_t length, l, seek_pos, saved_pos, num; - uint32_t seek_id; - int i, il, res = 0; - off_t off; - - off = stream_tell(s); - for (i = 0; i < mkv_d->parsed_seekhead_num; i++) - if (mkv_d->parsed_seekhead[i] == off) { - ebml_read_skip(s, NULL); - return 0; - } - mkv_d->parsed_seekhead = realloc(mkv_d->parsed_seekhead, - (mkv_d->parsed_seekhead_num + 1) * sizeof(off_t)); - mkv_d->parsed_seekhead[mkv_d->parsed_seekhead_num++] = off; + struct mkv_demuxer *mkv_d = demuxer->priv; + struct stream *s = demuxer->stream; + int res = 0; + struct ebml_seek_head seekhead = {}; + struct ebml_parse_ctx parse_ctx = {}; mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing seek head ] ---------\n"); - length = ebml_read_length(s, NULL); - /* off now holds the position of the next element after the seek head. */ - off = stream_tell(s) + length; - while (length > 0 && !res) { - - seek_id = 0; - seek_pos = EBML_UINT_INVALID; - - switch (ebml_read_id(s, &il)) { - case MATROSKA_ID_SEEK:; - uint64_t len = ebml_read_length(s, &i); - l = len + i; - - while (len > 0) { - uint64_t l; - int il; - - switch (ebml_read_id(s, &il)) { - case MATROSKA_ID_SEEKID: - num = ebml_read_uint(s, &l); - if (num != EBML_UINT_INVALID) - seek_id = num; - break; - - case MATROSKA_ID_SEEKPOSITION: - seek_pos = ebml_read_uint(s, &l); - break; - - default: - ebml_read_skip(s, &l); - break; - } - len -= l + il; - } - - break; - - default: - ebml_read_skip(s, &l); - break; - } - length -= l + il; - - if (seek_id == 0 || seek_id == MATROSKA_ID_CLUSTER - || seek_pos == EBML_UINT_INVALID - || ((mkv_d->segment_start + seek_pos) >= - (uint64_t) demuxer->movi_end)) - continue; - - saved_pos = stream_tell(s); - if (!stream_seek(s, mkv_d->segment_start + seek_pos)) - res = 1; - else { - if (ebml_read_id(s, &il) != seek_id) - res = 1; - else - switch (seek_id) { - case MATROSKA_ID_CUES: - if (demux_mkv_read_cues(demuxer)) - res = 1; - break; - - case MATROSKA_ID_TAGS: - if (demux_mkv_read_tags(demuxer)) - res = 1; - break; - - case MATROSKA_ID_SEEKHEAD: - if (demux_mkv_read_seekhead(demuxer)) - res = 1; - break; - - case MATROSKA_ID_CHAPTERS: - if (demux_mkv_read_chapters(demuxer)) - res = 1; - break; - } - } - - stream_seek(s, saved_pos); + if (ebml_read_element(s, &parse_ctx, &seekhead, &ebml_seek_head_desc) < 0) { + res = 1; + goto out; } - if (res) { - /* If there was an error then try to skip this seek head. */ - if (stream_seek(s, off)) - res = 0; - } else if (length > 0) - stream_seek(s, stream_tell(s) + length); + /* off now holds the position of the next element after the seek head. */ + off_t off = stream_tell(s); + for (int i = 0; i < seekhead.n_seek; i++) { + struct ebml_seek *seek = &seekhead.seek[i]; + if (seek->n_seek_id != 1 || seek->n_seek_position != 1) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Invalid SeekHead entry\n"); + continue; + } + uint64_t pos = seek->seek_position + mkv_d->segment_start; + if (pos >= demuxer->movi_end) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] SeekHead position beyond " + "end of file - incomplete file?\n"); + continue; + } + read_header_element(demuxer, seek->seek_id, pos); + } + if (!stream_seek(s, off)) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Couldn't seek back after " + "SeekHead??\n"); + res = 1; + } + out: mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing seek head ] ---------\n"); + talloc_free(parse_ctx.talloc_ctx); return res; } +static bool seek_pos_id(struct stream *s, off_t pos, uint32_t id) +{ + if (!stream_seek(s, pos)) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Failed to seek in file\n"); + return false; + } + if (ebml_read_id(s, NULL) != id) { + mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Expected element not found\n"); + return false; + } + return true; +} + +static int read_header_element(struct demuxer *demuxer, uint32_t id, + off_t at_filepos) +{ + struct mkv_demuxer *mkv_d = demuxer->priv; + stream_t *s = demuxer->stream; + off_t pos = stream_tell(s) - 4; + + switch(id) { + case MATROSKA_ID_INFO: + if (mkv_d->parsed_info) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment information...\n"); + mkv_d->parsed_info = true; + return demux_mkv_read_info(demuxer) ? -1 : 1; + + case MATROSKA_ID_TRACKS: + if (mkv_d->parsed_tracks) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_tracks = true; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment tracks...\n"); + return demux_mkv_read_tracks(demuxer) ? -1 : 1; + + case MATROSKA_ID_CUES: + if (is_parsed_header(mkv_d, pos)) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + return demux_mkv_read_cues(demuxer) ? -1 : 1; + + case MATROSKA_ID_TAGS: + if (mkv_d->parsed_tags) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_tags = true; + return demux_mkv_read_tags(demuxer) ? -1 : 1; + + case MATROSKA_ID_SEEKHEAD: + if (is_parsed_header(mkv_d, pos)) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + return demux_mkv_read_seekhead(demuxer) ? -1 : 1; + + case MATROSKA_ID_CHAPTERS: + if (mkv_d->parsed_chapters) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_chapters = true; + return demux_mkv_read_chapters(demuxer) ? -1 : 1; + + case MATROSKA_ID_ATTACHMENTS: + if (mkv_d->parsed_attachments) + break; + if (at_filepos && !seek_pos_id(s, at_filepos, id)) + return -1; + mkv_d->parsed_attachments = true; + return demux_mkv_read_attachments(demuxer) ? -1 : 1; + + default: + if (!at_filepos) + ebml_read_skip(s, NULL); + return 0; + } + if (!at_filepos) + ebml_read_skip(s, NULL); + return 1; +} + + + static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track, int vid); static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track, @@ -2063,45 +2094,14 @@ static int demux_mkv_open(demuxer_t *demuxer) mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] + a segment...\n"); - mkv_d = calloc(1, sizeof(mkv_demuxer_t)); + mkv_d = talloc_zero(demuxer, struct mkv_demuxer); demuxer->priv = mkv_d; mkv_d->tc_scale = 1000000; mkv_d->segment_start = stream_tell(s); - mkv_d->parsed_cues = malloc(sizeof(off_t)); - mkv_d->parsed_seekhead = malloc(sizeof(off_t)); while (!cont) { - switch (ebml_read_id(s, NULL)) { - case MATROSKA_ID_INFO: - mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment information...\n"); - cont = demux_mkv_read_info(demuxer); - break; - - case MATROSKA_ID_TRACKS: - mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ segment tracks...\n"); - cont = demux_mkv_read_tracks(demuxer); - break; - - case MATROSKA_ID_CUES: - cont = demux_mkv_read_cues(demuxer); - break; - - case MATROSKA_ID_TAGS: - cont = demux_mkv_read_tags(demuxer); - break; - - case MATROSKA_ID_SEEKHEAD: - cont = demux_mkv_read_seekhead(demuxer); - break; - - case MATROSKA_ID_CHAPTERS: - cont = demux_mkv_read_chapters(demuxer); - break; - - case MATROSKA_ID_ATTACHMENTS: - cont = demux_mkv_read_attachments(demuxer); - break; - + uint32_t id = ebml_read_id(s, NULL); + switch (id) { case MATROSKA_ID_CLUSTER: mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ found cluster, headers are " @@ -2111,7 +2111,8 @@ static int demux_mkv_open(demuxer_t *demuxer) break; default: - cont = 1; + cont = read_header_element(demuxer, id, 0) < 1; + break; case EBML_ID_VOID: ebml_read_skip(s, NULL); break; @@ -2221,9 +2222,6 @@ static void demux_close_mkv(demuxer_t *demuxer) } free(mkv_d->indexes); free(mkv_d->cluster_positions); - free(mkv_d->parsed_cues); - free(mkv_d->parsed_seekhead); - free(mkv_d); } }