From f90e9a86d133a32d5b15b5ee3472f1a49cabc206 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 25 Jan 2010 12:37:42 +0200 Subject: demux_mkv: support reading all headers based on SeekHead Restructure the code reading toplevel header elements and rewrite the SeekHead parsing code using the new EBML parser. Now every type of header element is read anywhere in the file if there's a SeekHead entry pointing to it. The new SeekHead parsing code has more diagnostic output in case of errors. --- libmpdemux/demux_mkv.c | 298 ++++++++++++++++++++++++------------------------- 1 file changed, 148 insertions(+), 150 deletions(-) (limited to 'libmpdemux/demux_mkv.c') 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); + if (ebml_read_element(s, &parse_ctx, &seekhead, &ebml_seek_head_desc) < 0) { + res = 1; + goto out; + } /* 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; + 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; +} - case MATROSKA_ID_SEEKPOSITION: - seek_pos = ebml_read_uint(s, &l); - break; +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; +} - default: - ebml_read_skip(s, &l); - break; - } - len -= l + il; - } +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; - default: - ebml_read_skip(s, &l); + case MATROSKA_ID_TRACKS: + if (mkv_d->parsed_tracks) break; - } - length -= l + il; + 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; - 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; + 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; - 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 (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_TAGS: - if (demux_mkv_read_tags(demuxer)) - res = 1; - break; + 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_SEEKHEAD: - if (demux_mkv_read_seekhead(demuxer)) - res = 1; - break; + 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_CHAPTERS: - if (demux_mkv_read_chapters(demuxer)) - res = 1; - break; - } - } + 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; - stream_seek(s, saved_pos); + default: + if (!at_filepos) + ebml_read_skip(s, NULL); + return 0; } - 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); - mp_msg(MSGT_DEMUX, MSGL_V, - "[mkv] \\---- [ parsing seek head ] ---------\n"); - return res; + 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); } } -- cgit v1.2.3