|
|
@@ -71,6 +71,7 @@ typedef enum { |
|
|
|
EBML_UTF8, |
|
|
|
EBML_BIN, |
|
|
|
EBML_NEST, |
|
|
|
EBML_LEVEL1, |
|
|
|
EBML_PASS, |
|
|
|
EBML_STOP, |
|
|
|
EBML_SINT, |
|
|
@@ -252,6 +253,12 @@ typedef struct { |
|
|
|
EbmlList blocks; |
|
|
|
} MatroskaCluster; |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
uint64_t id; |
|
|
|
uint64_t pos; |
|
|
|
int parsed; |
|
|
|
} MatroskaLevel1Element; |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
AVFormatContext *ctx; |
|
|
|
|
|
|
@@ -290,6 +297,10 @@ typedef struct { |
|
|
|
/* File has a CUES element, but we defer parsing until it is needed. */ |
|
|
|
int cues_parsing_deferred; |
|
|
|
|
|
|
|
/* Level1 elements and whether they were read yet */ |
|
|
|
MatroskaLevel1Element level1_elems[64]; |
|
|
|
int num_level1_elems; |
|
|
|
|
|
|
|
int current_cluster_num_blocks; |
|
|
|
int64_t current_cluster_pos; |
|
|
|
MatroskaCluster current_cluster; |
|
|
@@ -551,13 +562,13 @@ static EbmlSyntax matroska_seekhead[] = { |
|
|
|
}; |
|
|
|
|
|
|
|
static EbmlSyntax matroska_segment[] = { |
|
|
|
{ MATROSKA_ID_INFO, EBML_NEST, 0, 0, { .n = matroska_info } }, |
|
|
|
{ MATROSKA_ID_TRACKS, EBML_NEST, 0, 0, { .n = matroska_tracks } }, |
|
|
|
{ MATROSKA_ID_ATTACHMENTS, EBML_NEST, 0, 0, { .n = matroska_attachments } }, |
|
|
|
{ MATROSKA_ID_CHAPTERS, EBML_NEST, 0, 0, { .n = matroska_chapters } }, |
|
|
|
{ MATROSKA_ID_CUES, EBML_NEST, 0, 0, { .n = matroska_index } }, |
|
|
|
{ MATROSKA_ID_TAGS, EBML_NEST, 0, 0, { .n = matroska_tags } }, |
|
|
|
{ MATROSKA_ID_SEEKHEAD, EBML_NEST, 0, 0, { .n = matroska_seekhead } }, |
|
|
|
{ MATROSKA_ID_INFO, EBML_LEVEL1, 0, 0, { .n = matroska_info } }, |
|
|
|
{ MATROSKA_ID_TRACKS, EBML_LEVEL1, 0, 0, { .n = matroska_tracks } }, |
|
|
|
{ MATROSKA_ID_ATTACHMENTS, EBML_LEVEL1, 0, 0, { .n = matroska_attachments } }, |
|
|
|
{ MATROSKA_ID_CHAPTERS, EBML_LEVEL1, 0, 0, { .n = matroska_chapters } }, |
|
|
|
{ MATROSKA_ID_CUES, EBML_LEVEL1, 0, 0, { .n = matroska_index } }, |
|
|
|
{ MATROSKA_ID_TAGS, EBML_LEVEL1, 0, 0, { .n = matroska_tags } }, |
|
|
|
{ MATROSKA_ID_SEEKHEAD, EBML_LEVEL1, 0, 0, { .n = matroska_seekhead } }, |
|
|
|
{ MATROSKA_ID_CLUSTER, EBML_STOP }, |
|
|
|
{ 0 } |
|
|
|
}; |
|
|
@@ -976,6 +987,42 @@ static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax, |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Allocate and return the entry for the level1 element with the given ID. If |
|
|
|
* an entry already exists, return the existing entry. |
|
|
|
*/ |
|
|
|
static MatroskaLevel1Element *matroska_find_level1_elem(MatroskaDemuxContext *matroska, |
|
|
|
uint32_t id) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
MatroskaLevel1Element *elem; |
|
|
|
|
|
|
|
// Some files link to all clusters; useless. |
|
|
|
if (id == MATROSKA_ID_CLUSTER) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
// There can be multiple seekheads. |
|
|
|
if (id != MATROSKA_ID_SEEKHEAD) { |
|
|
|
for (i = 0; i < matroska->num_level1_elems; i++) { |
|
|
|
if (matroska->level1_elems[i].id == id) |
|
|
|
return &matroska->level1_elems[i]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Only a completely broken file would have more elements. |
|
|
|
// It also provides a low-effort way to escape from circular seekheads |
|
|
|
// (every iteration will add a level1 entry). |
|
|
|
if (matroska->num_level1_elems >= FF_ARRAY_ELEMS(matroska->level1_elems)) { |
|
|
|
av_log(matroska->ctx, AV_LOG_ERROR, "Too many level1 elements or circular seekheads.\n"); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
elem = &matroska->level1_elems[matroska->num_level1_elems++]; |
|
|
|
*elem = (MatroskaLevel1Element){.id = id}; |
|
|
|
|
|
|
|
return elem; |
|
|
|
} |
|
|
|
|
|
|
|
static int ebml_parse_elem(MatroskaDemuxContext *matroska, |
|
|
|
EbmlSyntax *syntax, void *data) |
|
|
|
{ |
|
|
@@ -994,6 +1041,7 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska, |
|
|
|
uint64_t length; |
|
|
|
int res; |
|
|
|
void *newelem; |
|
|
|
MatroskaLevel1Element *level1_elem; |
|
|
|
|
|
|
|
data = (char *) data + syntax->data_offset; |
|
|
|
if (syntax->list_elem_size) { |
|
|
@@ -1036,11 +1084,20 @@ static int ebml_parse_elem(MatroskaDemuxContext *matroska, |
|
|
|
case EBML_BIN: |
|
|
|
res = ebml_read_binary(pb, length, data); |
|
|
|
break; |
|
|
|
case EBML_LEVEL1: |
|
|
|
case EBML_NEST: |
|
|
|
if ((res = ebml_read_master(matroska, length)) < 0) |
|
|
|
return res; |
|
|
|
if (id == MATROSKA_ID_SEGMENT) |
|
|
|
matroska->segment_start = avio_tell(matroska->ctx->pb); |
|
|
|
if (id == MATROSKA_ID_CUES) |
|
|
|
matroska->cues_parsing_deferred = 0; |
|
|
|
if (syntax->type == EBML_LEVEL1 && |
|
|
|
(level1_elem = matroska_find_level1_elem(matroska, syntax->id))) { |
|
|
|
if (level1_elem->parsed) |
|
|
|
av_log(matroska->ctx, AV_LOG_ERROR, "Duplicate element\n"); |
|
|
|
level1_elem->parsed = 1; |
|
|
|
} |
|
|
|
return ebml_parse_nest(matroska, syntax->def.n, data); |
|
|
|
case EBML_PASS: |
|
|
|
return ebml_parse_id(matroska, syntax->def.n, id, data); |
|
|
@@ -1071,6 +1128,7 @@ static void ebml_free(EbmlSyntax *syntax, void *data) |
|
|
|
case EBML_BIN: |
|
|
|
av_freep(&((EbmlBin *) data_off)->data); |
|
|
|
break; |
|
|
|
case EBML_LEVEL1: |
|
|
|
case EBML_NEST: |
|
|
|
if (syntax[i].list_elem_size) { |
|
|
|
EbmlList *list = data_off; |
|
|
@@ -1356,24 +1414,17 @@ static void matroska_convert_tags(AVFormatContext *s) |
|
|
|
} |
|
|
|
|
|
|
|
static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska, |
|
|
|
int idx) |
|
|
|
uint64_t pos) |
|
|
|
{ |
|
|
|
EbmlList *seekhead_list = &matroska->seekhead; |
|
|
|
uint32_t level_up = matroska->level_up; |
|
|
|
uint32_t saved_id = matroska->current_id; |
|
|
|
MatroskaSeekhead *seekhead = seekhead_list->elem; |
|
|
|
int64_t before_pos = avio_tell(matroska->ctx->pb); |
|
|
|
MatroskaLevel level; |
|
|
|
int64_t offset; |
|
|
|
int ret = 0; |
|
|
|
|
|
|
|
if (idx >= seekhead_list->nb_elem || |
|
|
|
seekhead[idx].id == MATROSKA_ID_SEEKHEAD || |
|
|
|
seekhead[idx].id == MATROSKA_ID_CLUSTER) |
|
|
|
return 0; |
|
|
|
|
|
|
|
/* seek */ |
|
|
|
offset = seekhead[idx].pos + matroska->segment_start; |
|
|
|
offset = pos + matroska->segment_start; |
|
|
|
if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) == offset) { |
|
|
|
/* We don't want to lose our seekhead level, so we add |
|
|
|
* a dummy. This is a crude hack. */ |
|
|
@@ -1410,37 +1461,35 @@ static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska, |
|
|
|
static void matroska_execute_seekhead(MatroskaDemuxContext *matroska) |
|
|
|
{ |
|
|
|
EbmlList *seekhead_list = &matroska->seekhead; |
|
|
|
int64_t before_pos = avio_tell(matroska->ctx->pb); |
|
|
|
int i; |
|
|
|
int nb_elem; |
|
|
|
|
|
|
|
// we should not do any seeking in the streaming case |
|
|
|
if (!matroska->ctx->pb->seekable || |
|
|
|
(matroska->ctx->flags & AVFMT_FLAG_IGNIDX)) |
|
|
|
return; |
|
|
|
|
|
|
|
// do not read entries that are added while parsing seekhead entries |
|
|
|
nb_elem = seekhead_list->nb_elem; |
|
|
|
for (i = 0; i < seekhead_list->nb_elem; i++) { |
|
|
|
MatroskaSeekhead *seekheads = seekhead_list->elem; |
|
|
|
uint32_t id = seekheads[i].id; |
|
|
|
uint64_t pos = seekheads[i].pos; |
|
|
|
|
|
|
|
for (i = 0; i < nb_elem; i++) { |
|
|
|
MatroskaSeekhead *seekhead = seekhead_list->elem; |
|
|
|
if (seekhead[i].pos <= before_pos) |
|
|
|
MatroskaLevel1Element *elem = matroska_find_level1_elem(matroska, id); |
|
|
|
if (!elem || elem->parsed) |
|
|
|
continue; |
|
|
|
|
|
|
|
elem->pos = pos; |
|
|
|
|
|
|
|
// defer cues parsing until we actually need cue data. |
|
|
|
if (seekhead[i].id == MATROSKA_ID_CUES) { |
|
|
|
matroska->cues_parsing_deferred = 1; |
|
|
|
if (id == MATROSKA_ID_CUES) |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (matroska_parse_seekhead_entry(matroska, i) < 0) { |
|
|
|
if (matroska_parse_seekhead_entry(matroska, pos) < 0) { |
|
|
|
// mark index as broken |
|
|
|
matroska->cues_parsing_deferred = -1; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if (nb_elem != seekhead_list->nb_elem) { |
|
|
|
avpriv_request_sample(matroska->ctx, "recursive SeekHead elements"); |
|
|
|
|
|
|
|
elem->parsed = 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -1474,17 +1523,18 @@ static void matroska_add_index_entries(MatroskaDemuxContext *matroska) |
|
|
|
} |
|
|
|
|
|
|
|
static void matroska_parse_cues(MatroskaDemuxContext *matroska) { |
|
|
|
EbmlList *seekhead_list = &matroska->seekhead; |
|
|
|
MatroskaSeekhead *seekhead = seekhead_list->elem; |
|
|
|
int i; |
|
|
|
|
|
|
|
for (i = 0; i < seekhead_list->nb_elem; i++) |
|
|
|
if (seekhead[i].id == MATROSKA_ID_CUES) |
|
|
|
for (i = 0; i < matroska->num_level1_elems; i++) { |
|
|
|
MatroskaLevel1Element *elem = &matroska->level1_elems[i]; |
|
|
|
if (elem->id == MATROSKA_ID_CUES && !elem->parsed) { |
|
|
|
if (matroska_parse_seekhead_entry(matroska, elem->pos) < 0) |
|
|
|
matroska->cues_parsing_deferred = -1; |
|
|
|
elem->parsed = 1; |
|
|
|
break; |
|
|
|
av_assert1(i <= seekhead_list->nb_elem); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (matroska_parse_seekhead_entry(matroska, i) < 0) |
|
|
|
matroska->cues_parsing_deferred = -1; |
|
|
|
matroska_add_index_entries(matroska); |
|
|
|
} |
|
|
|
|
|
|
@@ -2014,6 +2064,7 @@ static int matroska_read_header(AVFormatContext *s) |
|
|
|
int i, j, res; |
|
|
|
|
|
|
|
matroska->ctx = s; |
|
|
|
matroska->cues_parsing_deferred = 1; |
|
|
|
|
|
|
|
/* First read the EBML header. */ |
|
|
|
if (ebml_parse(matroska, ebml_syntax, &ebml) || |
|
|
|