summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmpdemux/demux_mkv.c298
1 files changed, 148 insertions, 150 deletions
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);
}
}