#include "qtpalette.h"
#define EBML_UNKNOWN_LENGTH UINT64_MAX /* EBML unknown length, in uint64_t */
+#define NEEDS_CHECKING 2 /* Indicates that some error checks
+ * still need to be performed */
typedef enum {
EBML_NONE,
EBML_BIN,
EBML_NEST,
EBML_LEVEL1,
- EBML_PASS,
EBML_STOP,
EBML_SINT,
EBML_TYPE_COUNT
uint64_t length;
} MatroskaLevel;
+typedef struct MatroskaBlock {
+ uint64_t duration;
+ int64_t reference;
+ uint64_t non_simple;
+ EbmlBin bin;
+ uint64_t additional_id;
+ EbmlBin additional;
+ int64_t discard_padding;
+} MatroskaBlock;
+
typedef struct MatroskaCluster {
+ MatroskaBlock block;
uint64_t timecode;
- EbmlList blocks;
+ int64_t pos;
} MatroskaCluster;
typedef struct MatroskaLevel1Element {
- uint64_t id;
uint64_t pos;
+ uint32_t id;
int parsed;
} MatroskaLevel1Element;
/* EBML stuff */
int num_levels;
MatroskaLevel levels[EBML_MAX_DEPTH];
- int level_up;
uint32_t current_id;
+ int64_t resync_pos;
uint64_t time_scale;
double duration;
MatroskaLevel1Element level1_elems[64];
int num_level1_elems;
- int current_cluster_num_blocks;
- int64_t current_cluster_pos;
MatroskaCluster current_cluster;
- /* File has SSA subtitles which prevent incremental cluster parsing. */
- int contains_ssa;
-
/* WebM DASH Manifest live flag */
int is_live;
int bandwidth;
} MatroskaDemuxContext;
-typedef struct MatroskaBlock {
- uint64_t duration;
- int64_t reference;
- uint64_t non_simple;
- EbmlBin bin;
- uint64_t additional_id;
- EbmlBin additional;
- int64_t discard_padding;
-} MatroskaBlock;
-
static const EbmlSyntax ebml_header[] = {
{ EBML_ID_EBMLREADVERSION, EBML_UINT, 0, offsetof(Ebml, version), { .u = EBML_VERSION } },
{ EBML_ID_EBMLMAXSIZELENGTH, EBML_UINT, 0, offsetof(Ebml, max_size), { .u = 8 } },
static const EbmlSyntax matroska_blockgroup[] = {
{ MATROSKA_ID_BLOCK, EBML_BIN, 0, offsetof(MatroskaBlock, bin) },
{ MATROSKA_ID_BLOCKADDITIONS, EBML_NEST, 0, 0, { .n = matroska_blockadditions} },
- { MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock, bin) },
{ MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock, duration) },
{ MATROSKA_ID_DISCARDPADDING, EBML_SINT, 0, offsetof(MatroskaBlock, discard_padding) },
{ MATROSKA_ID_BLOCKREFERENCE, EBML_SINT, 0, offsetof(MatroskaBlock, reference), { .i = INT64_MIN } },
{ 0 }
};
-static const EbmlSyntax matroska_cluster[] = {
- { MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },
- { MATROSKA_ID_BLOCKGROUP, EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
- { MATROSKA_ID_SIMPLEBLOCK, EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
- { MATROSKA_ID_CLUSTERPOSITION, EBML_NONE },
- { MATROSKA_ID_CLUSTERPREVSIZE, EBML_NONE },
- { 0 }
-};
-
-static const EbmlSyntax matroska_clusters[] = {
- { MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, { .n = matroska_cluster } },
- { MATROSKA_ID_INFO, EBML_NONE },
- { MATROSKA_ID_CUES, EBML_NONE },
- { MATROSKA_ID_TAGS, EBML_NONE },
- { MATROSKA_ID_SEEKHEAD, EBML_NONE },
- { 0 }
-};
-
-static const EbmlSyntax matroska_cluster_incremental_parsing[] = {
- { MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },
- { MATROSKA_ID_BLOCKGROUP, EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
- { MATROSKA_ID_SIMPLEBLOCK, EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
+static const EbmlSyntax matroska_cluster_parsing[] = {
+ { MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },
+ { MATROSKA_ID_BLOCKGROUP, EBML_NEST, 0, 0, { .n = matroska_blockgroup } },
+ { MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock, bin) },
{ MATROSKA_ID_CLUSTERPOSITION, EBML_NONE },
{ MATROSKA_ID_CLUSTERPREVSIZE, EBML_NONE },
{ MATROSKA_ID_INFO, EBML_NONE },
{ 0 }
};
-static const EbmlSyntax matroska_cluster_incremental[] = {
+static const EbmlSyntax matroska_cluster[] = {
{ MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },
{ MATROSKA_ID_BLOCKGROUP, EBML_STOP },
{ MATROSKA_ID_SIMPLEBLOCK, EBML_STOP },
{ 0 }
};
-static const EbmlSyntax matroska_clusters_incremental[] = {
- { MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, { .n = matroska_cluster_incremental } },
+static const EbmlSyntax matroska_clusters[] = {
+ { MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, { .n = matroska_cluster } },
{ MATROSKA_ID_INFO, EBML_NONE },
{ MATROSKA_ID_CUES, EBML_NONE },
{ MATROSKA_ID_TAGS, EBML_NONE },
static int matroska_read_close(AVFormatContext *s);
+/*
+ * This function prepares the status for parsing of level 1 elements.
+ */
+static int matroska_reset_status(MatroskaDemuxContext *matroska,
+ uint32_t id, int64_t position)
+{
+ if (position >= 0) {
+ int err = avio_seek(matroska->ctx->pb, position, SEEK_SET);
+ if (err < 0)
+ return err;
+ }
+
+ matroska->current_id = id;
+ matroska->num_levels = 1;
+ matroska->current_cluster.pos = 0;
+ matroska->resync_pos = avio_tell(matroska->ctx->pb);
+ if (id)
+ matroska->resync_pos -= (av_log2(id) + 7) / 8;
+
+ return 0;
+}
+
static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos)
{
AVIOContext *pb = matroska->ctx->pb;
- int64_t ret;
uint32_t id;
- matroska->current_id = 0;
- matroska->num_levels = 0;
- /* seek to next position to resync from */
- if ((ret = avio_seek(pb, last_pos + 1, SEEK_SET)) < 0) {
- matroska->done = 1;
- return ret;
+ /* Try to seek to the last position to resync from. If this doesn't work,
+ * we resync from the earliest position available: The start of the buffer. */
+ if (last_pos < avio_tell(pb) && avio_seek(pb, last_pos + 1, SEEK_SET) < 0) {
+ av_log(matroska->ctx, AV_LOG_WARNING,
+ "Seek to desired resync point failed. Seeking to "
+ "earliest point available instead.\n");
+ avio_seek(pb, FFMAX(avio_tell(pb) + (pb->buffer - pb->buf_ptr),
+ last_pos + 1), SEEK_SET);
}
id = avio_rb32(pb);
id == MATROSKA_ID_CUES || id == MATROSKA_ID_TAGS ||
id == MATROSKA_ID_SEEKHEAD || id == MATROSKA_ID_ATTACHMENTS ||
id == MATROSKA_ID_CLUSTER || id == MATROSKA_ID_CHAPTERS) {
- matroska->current_id = id;
+ /* Prepare the context for parsing of a level 1 element. */
+ matroska_reset_status(matroska, id, -1);
+ /* Given that we are here means that an error has occured,
+ * so treat the segment as unknown length in order not to
+ * discard valid data that happens to be beyond the designated
+ * end of the segment. */
+ matroska->levels[0].length = EBML_UNKNOWN_LENGTH;
return 0;
}
id = (id << 8) | avio_r8(pb);
}
matroska->done = 1;
- return AVERROR_EOF;
+ return pb->error ? pb->error : AVERROR_EOF;
}
/*
* Returns: number of bytes read, < 0 on error
*/
static int ebml_read_num(MatroskaDemuxContext *matroska, AVIOContext *pb,
- int max_size, uint64_t *number)
+ int max_size, uint64_t *number, int eof_forbidden)
{
- int read = 1, n = 1;
- uint64_t total = 0;
+ int read, n = 1;
+ uint64_t total;
+ int64_t pos;
- /* The first byte tells us the length in bytes - avio_r8() can normally
- * return 0, but since that's not a valid first ebmlID byte, we can
- * use it safely here to catch EOS. */
- if (!(total = avio_r8(pb))) {
- /* we might encounter EOS here */
- if (!avio_feof(pb)) {
- int64_t pos = avio_tell(pb);
- av_log(matroska->ctx, AV_LOG_ERROR,
- "Read error at pos. %"PRIu64" (0x%"PRIx64")\n",
- pos, pos);
- return pb->error ? pb->error : AVERROR(EIO);
- }
- return AVERROR_EOF;
- }
+ /* The first byte tells us the length in bytes - except when it is zero. */
+ total = avio_r8(pb);
+ if (pb->eof_reached)
+ goto err;
/* get the length of the EBML number */
read = 8 - ff_log2_tab[total];
- if (read > max_size) {
- int64_t pos = avio_tell(pb) - 1;
- av_log(matroska->ctx, AV_LOG_ERROR,
- "Invalid EBML number size tag 0x%02x at pos %"PRIu64" (0x%"PRIx64")\n",
- (uint8_t) total, pos, pos);
+
+ if (!total || read > max_size) {
+ pos = avio_tell(pb) - 1;
+ if (!total) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "0x00 at pos %"PRId64" (0x%"PRIx64") invalid as first byte "
+ "of an EBML number\n", pos, pos);
+ } else {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Length %d indicated by an EBML number's first byte 0x%02x "
+ "at pos %"PRId64" (0x%"PRIx64") exceeds max length %d.\n",
+ read, (uint8_t) total, pos, pos, max_size);
+ }
return AVERROR_INVALIDDATA;
}
while (n++ < read)
total = (total << 8) | avio_r8(pb);
+ if (pb->eof_reached) {
+ eof_forbidden = 1;
+ goto err;
+ }
+
*number = total;
return read;
+
+err:
+ pos = avio_tell(pb);
+ if (pb->error) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Read error at pos. %"PRIu64" (0x%"PRIx64")\n",
+ pos, pos);
+ return pb->error;
+ }
+ if (eof_forbidden) {
+ av_log(matroska->ctx, AV_LOG_ERROR, "File ended prematurely "
+ "at pos. %"PRIu64" (0x%"PRIx64")\n", pos, pos);
+ return AVERROR(EIO);
+ }
+ return AVERROR_EOF;
}
/**
static int ebml_read_length(MatroskaDemuxContext *matroska, AVIOContext *pb,
uint64_t *number)
{
- int res = ebml_read_num(matroska, pb, 8, number);
+ int res = ebml_read_num(matroska, pb, 8, number, 1);
if (res > 0 && *number + 1 == 1ULL << (7 * res))
*number = EBML_UNKNOWN_LENGTH;
return res;
/*
* Read the next element as an unsigned int.
- * 0 is success, < 0 is failure.
+ * Returns NEEDS_CHECKING.
*/
static int ebml_read_uint(AVIOContext *pb, int size, uint64_t *num)
{
int n = 0;
- if (size > 8)
- return AVERROR_INVALIDDATA;
-
/* big-endian ordering; build up number */
*num = 0;
while (n++ < size)
*num = (*num << 8) | avio_r8(pb);
- return 0;
+ return NEEDS_CHECKING;
}
/*
* Read the next element as a signed int.
- * 0 is success, < 0 is failure.
+ * Returns NEEDS_CHECKING.
*/
static int ebml_read_sint(AVIOContext *pb, int size, int64_t *num)
{
int n = 1;
- if (size > 8)
- return AVERROR_INVALIDDATA;
-
if (size == 0) {
*num = 0;
} else {
*num = ((uint64_t)*num << 8) | avio_r8(pb);
}
- return 0;
+ return NEEDS_CHECKING;
}
/*
* Read the next element as a float.
- * 0 is success, < 0 is failure.
+ * Returns NEEDS_CHECKING or < 0 on obvious failure.
*/
static int ebml_read_float(AVIOContext *pb, int size, double *num)
{
else
return AVERROR_INVALIDDATA;
- return 0;
+ return NEEDS_CHECKING;
}
/*
* Read the next element as an ASCII string.
- * 0 is success, < 0 is failure.
+ * 0 is success, < 0 or NEEDS_CHECKING is failure.
*/
static int ebml_read_ascii(AVIOContext *pb, int size, char **str)
{
char *res;
+ int ret;
/* EBML strings are usually not 0-terminated, so we allocate one
* byte more, read the string and NULL-terminate it ourselves. */
if (!(res = av_malloc(size + 1)))
return AVERROR(ENOMEM);
- if (avio_read(pb, (uint8_t *) res, size) != size) {
+ if ((ret = avio_read(pb, (uint8_t *) res, size)) != size) {
av_free(res);
- return AVERROR(EIO);
+ return ret < 0 ? ret : NEEDS_CHECKING;
}
(res)[size] = '\0';
av_free(*str);
/*
* Read the next element as binary data.
- * 0 is success, < 0 is failure.
+ * 0 is success, < 0 or NEEDS_CHECKING is failure.
*/
static int ebml_read_binary(AVIOContext *pb, int length, EbmlBin *bin)
{
bin->data = bin->buf->data;
bin->size = length;
bin->pos = avio_tell(pb);
- if (avio_read(pb, bin->data, length) != length) {
+ if ((ret = avio_read(pb, bin->data, length)) != length) {
av_buffer_unref(&bin->buf);
bin->data = NULL;
bin->size = 0;
- return AVERROR(EIO);
+ return ret < 0 ? ret : NEEDS_CHECKING;
}
return 0;
{
AVIOContext pb;
ffio_init_context(&pb, data, size, 0, NULL, NULL, NULL, NULL);
- return ebml_read_num(matroska, &pb, FFMIN(size, 8), num);
+ return ebml_read_num(matroska, &pb, FFMIN(size, 8), num, 1);
}
/*
return res;
}
-static int ebml_parse_elem(MatroskaDemuxContext *matroska,
- EbmlSyntax *syntax, void *data);
+static int ebml_parse(MatroskaDemuxContext *matroska,
+ EbmlSyntax *syntax, void *data);
-static int ebml_parse_id(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
- uint32_t id, void *data)
+static EbmlSyntax *ebml_parse_id(EbmlSyntax *syntax, uint32_t id)
{
int i;
for (i = 0; syntax[i].id; i++)
if (id == syntax[i].id)
break;
- if (!syntax[i].id && id == MATROSKA_ID_CLUSTER &&
- matroska->num_levels > 0 &&
- matroska->levels[matroska->num_levels - 1].length == EBML_UNKNOWN_LENGTH)
- return 0; // we reached the end of an unknown size cluster
- if (!syntax[i].id && id != EBML_ID_VOID && id != EBML_ID_CRC32) {
- av_log(matroska->ctx, AV_LOG_DEBUG, "Unknown entry 0x%"PRIX32"\n", id);
- }
- return ebml_parse_elem(matroska, &syntax[i], data);
-}
-static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
- void *data)
-{
- if (!matroska->current_id) {
- uint64_t id;
- int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id);
- if (res < 0) {
- // in live mode, finish parsing if EOF is reached.
- return (matroska->is_live && matroska->ctx->pb->eof_reached &&
- res == AVERROR_EOF) ? 1 : res;
- }
- matroska->current_id = id | 1 << 7 * res;
- }
- return ebml_parse_id(matroska, syntax, matroska->current_id, data);
+ return &syntax[i];
}
static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
return elem;
}
-static int ebml_parse_elem(MatroskaDemuxContext *matroska,
- EbmlSyntax *syntax, void *data)
+static int ebml_parse(MatroskaDemuxContext *matroska,
+ EbmlSyntax *syntax, void *data)
{
static const uint64_t max_lengths[EBML_TYPE_COUNT] = {
[EBML_UINT] = 8,
+ [EBML_SINT] = 8,
[EBML_FLOAT] = 8,
// max. 16 MB for strings
[EBML_STR] = 0x1000000,
// no limits for anything else
};
AVIOContext *pb = matroska->ctx->pb;
- uint32_t id = syntax->id;
+ uint32_t id;
uint64_t length;
- int res;
+ int64_t pos = avio_tell(pb);
+ int res, update_pos = 1;
void *newelem;
MatroskaLevel1Element *level1_elem;
+ if (!matroska->current_id) {
+ uint64_t id;
+ res = ebml_read_num(matroska, pb, 4, &id, 0);
+ if (res < 0) {
+ // in live mode, finish parsing if EOF is reached.
+ return (matroska->is_live && pb->eof_reached &&
+ res == AVERROR_EOF) ? 1 : res;
+ }
+ matroska->current_id = id | 1 << 7 * res;
+ } else
+ pos -= (av_log2(matroska->current_id) + 7) / 8;
+
+ id = matroska->current_id;
+
+ syntax = ebml_parse_id(syntax, id);
+ if (!syntax->id && id == MATROSKA_ID_CLUSTER &&
+ matroska->num_levels > 0 &&
+ matroska->levels[matroska->num_levels - 1].length == EBML_UNKNOWN_LENGTH)
+ return 0; // we reached the end of an unknown size cluster
+ if (!syntax->id && id != EBML_ID_VOID && id != EBML_ID_CRC32) {
+ av_log(matroska->ctx, AV_LOG_DEBUG, "Unknown entry 0x%"PRIX32"\n", id);
+ update_pos = 0;
+ }
+
data = (char *) data + syntax->data_offset;
if (syntax->list_elem_size) {
EbmlList *list = data;
list->nb_elem++;
}
- if (syntax->type != EBML_PASS && syntax->type != EBML_STOP) {
+ if (syntax->type != EBML_STOP) {
matroska->current_id = 0;
if ((res = ebml_read_length(matroska, pb, &length)) < 0)
return res;
MatroskaLevel *level = &matroska->levels[matroska->num_levels - 1];
AVIOContext *pb = matroska->ctx->pb;
int64_t pos = avio_tell(pb);
- if (level->length != EBML_UNKNOWN_LENGTH &&
- (pos + length) > (level->start + level->length)) {
+
+ if (length != EBML_UNKNOWN_LENGTH &&
+ level->length != EBML_UNKNOWN_LENGTH) {
+ uint64_t elem_end = pos + length,
+ level_end = level->start + level->length;
+
+ if (level_end < elem_end) {
+ av_log(matroska->ctx, AV_LOG_ERROR,
+ "Element at 0x%"PRIx64" ending at 0x%"PRIx64" exceeds "
+ "containing master element ending at 0x%"PRIx64"\n",
+ pos, elem_end, level_end);
+ return AVERROR_INVALIDDATA;
+ }
+ } else if (level->length != EBML_UNKNOWN_LENGTH) {
+ av_log(matroska->ctx, AV_LOG_ERROR, "Unknown-sized element "
+ "at 0x%"PRIx64" inside parent with finite size\n", pos);
+ return AVERROR_INVALIDDATA;
+ } else if (length == EBML_UNKNOWN_LENGTH && id != MATROSKA_ID_CLUSTER) {
+ // According to the specifications only clusters and segments
+ // are allowed to be unknown-sized.
av_log(matroska->ctx, AV_LOG_ERROR,
- "Invalid length 0x%"PRIx64" > 0x%"PRIx64" in parent\n",
- length, level->start + level->length);
+ "Found unknown-sized element other than a cluster at "
+ "0x%"PRIx64". Dropping the invalid element.\n", pos);
return AVERROR_INVALIDDATA;
}
}
+
+ if (update_pos) {
+ // We have found an element that is allowed at this place
+ // in the hierarchy and it passed all checks, so treat the beginning
+ // of the element as the "last known good" position.
+ matroska->resync_pos = pos;
+ }
}
switch (syntax->type) {
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);
case EBML_STOP:
return 1;
default:
- if (ffio_limit(pb, length) != length)
- return AVERROR(EIO);
- return avio_skip(pb, length) < 0 ? AVERROR(EIO) : 0;
- }
- if (res == AVERROR_INVALIDDATA)
- av_log(matroska->ctx, AV_LOG_ERROR, "Invalid element\n");
- else if (res == AVERROR(EIO))
- av_log(matroska->ctx, AV_LOG_ERROR, "Read error\n");
+ if (length) {
+ int64_t res2;
+ if (ffio_limit(pb, length) != length) {
+ // ffio_limit emits its own error message,
+ // so we don't have to.
+ return AVERROR(EIO);
+ }
+ if ((res2 = avio_skip(pb, length - 1)) >= 0) {
+ // avio_skip might take us past EOF. We check for this
+ // by skipping only length - 1 bytes, reading a byte and
+ // checking the error flags. This is done in order to check
+ // that the element has been properly skipped even when
+ // no filesize (that ffio_limit relies on) is available.
+ avio_r8(pb);
+ res = NEEDS_CHECKING;
+ } else
+ res = res2;
+ } else
+ res = 0;
+ }
+ if (res) {
+ if (res == NEEDS_CHECKING) {
+ if (pb->eof_reached) {
+ if (pb->error)
+ res = pb->error;
+ else
+ res = AVERROR_EOF;
+ } else
+ res = 0;
+ }
+
+ if (res == AVERROR_INVALIDDATA)
+ av_log(matroska->ctx, AV_LOG_ERROR, "Invalid element\n");
+ else if (res == AVERROR(EIO))
+ av_log(matroska->ctx, AV_LOG_ERROR, "Read error\n");
+ else if (res == AVERROR_EOF) {
+ av_log(matroska->ctx, AV_LOG_ERROR, "File ended prematurely\n");
+ res = AVERROR(EIO);
+ }
+ }
return res;
}
/*
* Autodetecting...
*/
-static int matroska_probe(AVProbeData *p)
+static int matroska_probe(const AVProbeData *p)
{
uint64_t total = 0;
int len_mask = 0x80, size = 1, n = 1, i;
static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska,
uint64_t pos)
{
- uint32_t level_up = matroska->level_up;
uint32_t saved_id = matroska->current_id;
int64_t before_pos = avio_tell(matroska->ctx->pb);
MatroskaLevel level;
matroska->current_id = 0;
ret = ebml_parse(matroska, matroska_segment, matroska);
-
- /* remove dummy level */
- while (matroska->num_levels) {
- uint64_t length = matroska->levels[--matroska->num_levels].length;
- if (length == EBML_UNKNOWN_LENGTH)
- break;
- }
}
}
- /* seek back */
- avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
- matroska->level_up = level_up;
- matroska->current_id = saved_id;
+ /* Seek back - notice that in all instances where this is used it is safe
+ * to set the level to 1 and unset the position of the current cluster. */
+ matroska_reset_status(matroska, saved_id, before_pos);
return ret;
}
}
} else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) {
st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
- if (st->codecpar->codec_id == AV_CODEC_ID_ASS)
- matroska->contains_ssa = 1;
}
}
pos = avio_tell(matroska->ctx->pb);
res = ebml_parse(matroska, matroska_segment, matroska);
}
+ /* Set data_offset as it might be needed later by seek_frame_generic. */
+ if (matroska->current_id == MATROSKA_ID_CLUSTER)
+ s->internal->data_offset = avio_tell(matroska->ctx->pb) - 4;
matroska_execute_seekhead(matroska);
if (!matroska->time_scale)
attachments[j].stream = st;
if (st->codecpar->codec_id != AV_CODEC_ID_NONE) {
+ AVPacket *pkt = &st->attached_pic;
+
st->disposition |= AV_DISPOSITION_ATTACHED_PIC;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- av_init_packet(&st->attached_pic);
- if ((res = av_new_packet(&st->attached_pic, attachments[j].bin.size)) < 0)
- return res;
- memcpy(st->attached_pic.data, attachments[j].bin.data, attachments[j].bin.size);
- st->attached_pic.stream_index = st->index;
- st->attached_pic.flags |= AV_PKT_FLAG_KEY;
+ av_init_packet(pkt);
+ pkt->buf = av_buffer_ref(attachments[j].bin.buf);
+ if (!pkt->buf)
+ return AVERROR(ENOMEM);
+ pkt->data = attachments[j].bin.data;
+ pkt->size = attachments[j].bin.size;
+ pkt->stream_index = st->index;
+ pkt->flags |= AV_PKT_FLAG_KEY;
} else {
st->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT;
if (ff_alloc_extradata(st->codecpar, attachments[j].bin.size))
if (!type) {
*laces = 1;
- *lace_buf = av_mallocz(sizeof(int));
+ *lace_buf = av_malloc(sizeof(**lace_buf));
if (!*lace_buf)
return AVERROR(ENOMEM);
*laces = *data + 1;
data += 1;
size -= 1;
- lace_size = av_mallocz(*laces * sizeof(int));
+ lace_size = av_malloc_array(*laces, sizeof(*lace_size));
if (!lace_size)
return AVERROR(ENOMEM);
uint8_t temp;
uint32_t total = 0;
for (n = 0; res == 0 && n < *laces - 1; n++) {
+ lace_size[n] = 0;
+
while (1) {
if (size <= total) {
res = AVERROR_INVALIDDATA;
int trust_default_duration = 1;
if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) {
- av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n");
return n;
}
data += n;
track = matroska_find_track_by_num(matroska, num);
if (!track || !track->stream) {
av_log(matroska->ctx, AV_LOG_INFO,
- "Invalid stream %"PRIu64" or size %u\n", num, size);
+ "Invalid stream %"PRIu64"\n", num);
return AVERROR_INVALIDDATA;
} else if (size <= 3)
return 0;
return res;
}
-static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska)
+static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
{
- EbmlList *blocks_list;
- MatroskaBlock *blocks;
- int i, res;
+ MatroskaCluster *cluster = &matroska->current_cluster;
+ MatroskaBlock *block = &cluster->block;
+ int res;
res = ebml_parse(matroska,
- matroska_cluster_incremental_parsing,
- &matroska->current_cluster);
+ matroska_cluster_parsing,
+ cluster);
if (res == 1) {
/* New Cluster */
- if (matroska->current_cluster_pos)
+ if (cluster->pos)
ebml_level_end(matroska);
- ebml_free(matroska_cluster, &matroska->current_cluster);
- memset(&matroska->current_cluster, 0, sizeof(MatroskaCluster));
- matroska->current_cluster_num_blocks = 0;
- matroska->current_cluster_pos = avio_tell(matroska->ctx->pb);
+ cluster->pos = avio_tell(matroska->ctx->pb);
/* sizeof the ID which was already read */
if (matroska->current_id)
- matroska->current_cluster_pos -= 4;
+ cluster->pos -= 4;
res = ebml_parse(matroska,
- matroska_clusters_incremental,
- &matroska->current_cluster);
+ matroska_clusters,
+ cluster);
/* Try parsing the block again. */
if (res == 1)
res = ebml_parse(matroska,
- matroska_cluster_incremental_parsing,
- &matroska->current_cluster);
- }
-
- if (!res &&
- matroska->current_cluster_num_blocks <
- matroska->current_cluster.blocks.nb_elem) {
- blocks_list = &matroska->current_cluster.blocks;
- blocks = blocks_list->elem;
-
- matroska->current_cluster_num_blocks = blocks_list->nb_elem;
- i = blocks_list->nb_elem - 1;
- if (blocks[i].bin.size > 0 && blocks[i].bin.data) {
- int is_keyframe = blocks[i].non_simple ? blocks[i].reference == INT64_MIN : -1;
- uint8_t* additional = blocks[i].additional.size > 0 ?
- blocks[i].additional.data : NULL;
- if (!blocks[i].non_simple)
- blocks[i].duration = 0;
- res = matroska_parse_block(matroska, blocks[i].bin.buf, blocks[i].bin.data,
- blocks[i].bin.size, blocks[i].bin.pos,
- matroska->current_cluster.timecode,
- blocks[i].duration, is_keyframe,
- additional, blocks[i].additional_id,
- blocks[i].additional.size,
- matroska->current_cluster_pos,
- blocks[i].discard_padding);
- }
+ matroska_cluster_parsing,
+ cluster);
}
- return res;
-}
+ if (!res && block->bin.size > 0) {
+ int is_keyframe = block->non_simple ? block->reference == INT64_MIN : -1;
+ uint8_t* additional = block->additional.size > 0 ?
+ block->additional.data : NULL;
-static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
-{
- MatroskaCluster cluster = { 0 };
- EbmlList *blocks_list;
- MatroskaBlock *blocks;
- int i, res;
- int64_t pos;
+ res = matroska_parse_block(matroska, block->bin.buf, block->bin.data,
+ block->bin.size, block->bin.pos,
+ matroska->current_cluster.timecode,
+ block->duration, is_keyframe,
+ additional, block->additional_id,
+ block->additional.size,
+ cluster->pos,
+ block->discard_padding);
+ }
+
+ ebml_free(matroska_blockgroup, block);
+ memset(block, 0, sizeof(*block));
- if (!matroska->contains_ssa)
- return matroska_parse_cluster_incremental(matroska);
- pos = avio_tell(matroska->ctx->pb);
- if (matroska->current_id)
- pos -= 4; /* sizeof the ID which was already read */
- res = ebml_parse(matroska, matroska_clusters, &cluster);
- blocks_list = &cluster.blocks;
- blocks = blocks_list->elem;
- for (i = 0; i < blocks_list->nb_elem; i++)
- if (blocks[i].bin.size > 0 && blocks[i].bin.data) {
- int is_keyframe = blocks[i].non_simple ? blocks[i].reference == INT64_MIN : -1;
- res = matroska_parse_block(matroska, blocks[i].bin.buf, blocks[i].bin.data,
- blocks[i].bin.size, blocks[i].bin.pos,
- cluster.timecode, blocks[i].duration,
- is_keyframe, NULL, 0, 0, pos,
- blocks[i].discard_padding);
- }
- ebml_free(matroska_cluster, &cluster);
return res;
}
MatroskaDemuxContext *matroska = s->priv_data;
int ret = 0;
+ if (matroska->resync_pos == -1) {
+ // This can only happen if generic seeking has been used.
+ matroska->resync_pos = avio_tell(s->pb);
+ }
+
while (matroska_deliver_packet(matroska, pkt)) {
- int64_t pos = avio_tell(matroska->ctx->pb);
if (matroska->done)
return (ret < 0) ? ret : AVERROR_EOF;
if (matroska_parse_cluster(matroska) < 0)
- ret = matroska_resync(matroska, pos);
+ ret = matroska_resync(matroska, matroska->resync_pos);
}
return 0;
MatroskaDemuxContext *matroska = s->priv_data;
MatroskaTrack *tracks = NULL;
AVStream *st = s->streams[stream_index];
- int i, index, index_min;
+ int i, index;
/* Parse the CUES now since we need the index data to seek. */
if (matroska->cues_parsing_deferred > 0) {
timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);
if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
- avio_seek(s->pb, st->index_entries[st->nb_index_entries - 1].pos,
- SEEK_SET);
- matroska->current_id = 0;
+ matroska_reset_status(matroska, 0, st->index_entries[st->nb_index_entries - 1].pos);
while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
matroska_clear_queue(matroska);
if (matroska_parse_cluster(matroska) < 0)
if (index < 0 || (matroska->cues_parsing_deferred < 0 && index == st->nb_index_entries - 1))
goto err;
- index_min = index;
tracks = matroska->tracks.elem;
for (i = 0; i < matroska->tracks.nb_elem; i++) {
tracks[i].audio.pkt_cnt = 0;
tracks[i].end_timecode = 0;
}
- avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET);
- matroska->current_id = 0;
+ /* We seek to a level 1 element, so set the appropriate status. */
+ matroska_reset_status(matroska, 0, st->index_entries[index].pos);
if (flags & AVSEEK_FLAG_ANY) {
st->skip_to_keyframe = 0;
matroska->skip_to_timecode = timestamp;
}
matroska->skip_to_keyframe = 1;
matroska->done = 0;
- matroska->num_levels = 0;
ff_update_cur_dts(s, st, st->index_entries[index].timestamp);
return 0;
err:
// slightly hackish but allows proper fallback to
// the generic seeking code.
+ matroska_reset_status(matroska, 0, -1);
+ matroska->resync_pos = -1;
matroska_clear_queue(matroska);
- matroska->current_id = 0;
st->skip_to_keyframe =
matroska->skip_to_keyframe = 0;
matroska->done = 0;
- matroska->num_levels = 0;
return -1;
}
for (n = 0; n < matroska->tracks.nb_elem; n++)
if (tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO)
av_freep(&tracks[n].audio.buf);
- ebml_free(matroska_cluster, &matroska->current_cluster);
ebml_free(matroska_segment, matroska);
return 0;
static int webm_clusters_start_with_keyframe(AVFormatContext *s)
{
MatroskaDemuxContext *matroska = s->priv_data;
+ uint32_t id = matroska->current_id;
int64_t cluster_pos, before_pos;
int index, rv = 1;
if (s->streams[0]->nb_index_entries <= 0) return 0;
cluster_pos = s->streams[0]->index_entries[index].pos;
before_pos = avio_tell(s->pb);
while (1) {
- int64_t cluster_id = 0, cluster_length = 0;
+ uint64_t cluster_id, cluster_length;
+ int read;
AVPacket *pkt;
avio_seek(s->pb, cluster_pos, SEEK_SET);
// read cluster id and length
- ebml_read_num(matroska, matroska->ctx->pb, 4, &cluster_id);
- ebml_read_length(matroska, matroska->ctx->pb, &cluster_length);
- if (cluster_id != 0xF43B675) { // done with all clusters
+ read = ebml_read_num(matroska, matroska->ctx->pb, 4, &cluster_id, 1);
+ if (read < 0 || cluster_id != 0xF43B675) // done with all clusters
break;
- }
- avio_seek(s->pb, cluster_pos, SEEK_SET);
- matroska->current_id = 0;
+ read = ebml_read_length(matroska, matroska->ctx->pb, &cluster_length);
+ if (read < 0)
+ break;
+
+ matroska_reset_status(matroska, 0, cluster_pos);
matroska_clear_queue(matroska);
if (matroska_parse_cluster(matroska) < 0 ||
!matroska->queue) {
break;
}
pkt = &matroska->queue->pkt;
- cluster_pos += cluster_length + 12; // 12 is the offset of the cluster id and length.
+ // 4 + read is the length of the cluster id and the cluster length field.
+ cluster_pos += 4 + read + cluster_length;
if (!(pkt->flags & AV_PKT_FLAG_KEY)) {
rv = 0;
break;
}
}
- avio_seek(s->pb, before_pos, SEEK_SET);
+
+ /* Restore the status after matroska_read_header: */
+ matroska_reset_status(matroska, id, before_pos);
+
return rv;
}
cues_start = seekhead[i].pos + matroska->segment_start;
if (avio_seek(matroska->ctx->pb, cues_start, SEEK_SET) == cues_start) {
// cues_end is computed as cues_start + cues_length + length of the
- // Cues element ID + EBML length of the Cues element. cues_end is
- // inclusive and the above sum is reduced by 1.
- uint64_t cues_length = 0, cues_id = 0, bytes_read = 0;
- bytes_read += ebml_read_num(matroska, matroska->ctx->pb, 4, &cues_id);
- bytes_read += ebml_read_length(matroska, matroska->ctx->pb, &cues_length);
- cues_end = cues_start + cues_length + bytes_read - 1;
+ // Cues element ID (i.e. 4) + EBML length of the Cues element.
+ // cues_end is inclusive and the above sum is reduced by 1.
+ uint64_t cues_length, cues_id;
+ int bytes_read;
+ bytes_read = ebml_read_num (matroska, matroska->ctx->pb, 4, &cues_id, 1);
+ if (bytes_read < 0 || cues_id != (MATROSKA_ID_CUES & 0xfffffff))
+ return bytes_read < 0 ? bytes_read : AVERROR_INVALIDDATA;
+ bytes_read = ebml_read_length(matroska, matroska->ctx->pb, &cues_length);
+ if (bytes_read < 0)
+ return bytes_read;
+ cues_end = cues_start + 4 + bytes_read + cues_length - 1;
}
avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
if (cues_start == -1 || cues_end == -1) return -1;