matroskadec: use generic ebml parser to parse ebml header

Originally committed as revision 14553 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
Aurelien Jacobs 2008-08-05 00:40:02 +00:00
parent 789ed100d7
commit 6351132435
1 changed files with 35 additions and 141 deletions

View File

@ -80,6 +80,14 @@ typedef struct {
int64_t pos;
} EbmlBin;
typedef struct {
uint64_t version;
uint64_t max_size;
uint64_t id_length;
char *doctype;
uint64_t doctype_version;
} Ebml;
typedef struct Track {
MatroskaTrackType type;
@ -203,6 +211,23 @@ typedef struct MatroskaDemuxContext {
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
static 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} },
{ EBML_ID_EBMLMAXIDLENGTH, EBML_UINT, 0, offsetof(Ebml,id_length), {.u=4} },
{ EBML_ID_DOCTYPE, EBML_STR, 0, offsetof(Ebml,doctype), {.s="(none)"} },
{ EBML_ID_DOCTYPEREADVERSION, EBML_UINT, 0, offsetof(Ebml,doctype_version), {.u=1} },
{ EBML_ID_EBMLVERSION, EBML_NONE },
{ EBML_ID_DOCTYPEVERSION, EBML_NONE },
{ EBML_ID_VOID, EBML_NONE },
{ 0 }
};
static EbmlSyntax ebml_syntax[] = {
{ EBML_ID_HEADER, EBML_NEST, 0, 0, {.n=ebml_header} },
{ 0 }
};
/*
* The first few functions handle EBML file parsing. The rest
* is the document interpretation. Matroska really just is a
@ -694,130 +719,6 @@ matroska_ebmlnum_sint (uint8_t *data,
return res;
}
/*
* Read an EBML header.
* 0 is success, < 0 is failure.
*/
static int
ebml_read_header (MatroskaDemuxContext *matroska,
char **doctype,
int *version)
{
uint32_t id;
int level_up, res = 0;
/* default init */
if (doctype)
*doctype = NULL;
if (version)
*version = 1;
if (!(id = ebml_peek_id(matroska, &level_up)) ||
level_up != 0 || id != EBML_ID_HEADER) {
av_log(matroska->ctx, AV_LOG_ERROR,
"This is not an EBML file (id=0x%x/0x%x)\n", id, EBML_ID_HEADER);
return AVERROR_INVALIDDATA;
}
if ((res = ebml_read_master(matroska, &id)) < 0)
return res;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &level_up)))
return AVERROR(EIO);
/* end-of-header */
if (level_up)
break;
switch (id) {
/* is our read version uptodate? */
case EBML_ID_EBMLREADVERSION: {
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
return res;
if (num > EBML_VERSION) {
av_log(matroska->ctx, AV_LOG_ERROR,
"EBML version %"PRIu64" (> %d) is not supported\n",
num, EBML_VERSION);
return AVERROR_INVALIDDATA;
}
break;
}
/* we only handle 8 byte lengths at max */
case EBML_ID_EBMLMAXSIZELENGTH: {
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
return res;
if (num > sizeof(uint64_t)) {
av_log(matroska->ctx, AV_LOG_ERROR,
"Integers of size %"PRIu64" (> %zd) not supported\n",
num, sizeof(uint64_t));
return AVERROR_INVALIDDATA;
}
break;
}
/* we handle 4 byte IDs at max */
case EBML_ID_EBMLMAXIDLENGTH: {
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
return res;
if (num > sizeof(uint32_t)) {
av_log(matroska->ctx, AV_LOG_ERROR,
"IDs of size %"PRIu64" (> %zu) not supported\n",
num, sizeof(uint32_t));
return AVERROR_INVALIDDATA;
}
break;
}
case EBML_ID_DOCTYPE: {
char *text;
if ((res = ebml_read_ascii(matroska, &id, &text)) < 0)
return res;
if (doctype) {
if (*doctype)
av_free(*doctype);
*doctype = text;
} else
av_free(text);
break;
}
case EBML_ID_DOCTYPEREADVERSION: {
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
return res;
if (version)
*version = num;
break;
}
default:
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown data type 0x%x in EBML header", id);
/* pass-through */
case EBML_ID_VOID:
/* we ignore these two, as they don't tell us anything we
* care about */
case EBML_ID_EBMLVERSION:
case EBML_ID_DOCTYPEVERSION:
res = ebml_read_skip (matroska);
break;
}
}
return 0;
}
static int
matroska_find_track_by_num (MatroskaDemuxContext *matroska,
@ -2500,31 +2401,24 @@ matroska_read_header (AVFormatContext *s,
AVFormatParameters *ap)
{
MatroskaDemuxContext *matroska = s->priv_data;
char *doctype;
int version, last_level, res = 0;
int last_level, res = 0;
Ebml ebml = { 0 };
uint32_t id;
matroska->ctx = s;
/* First read the EBML header. */
doctype = NULL;
if ((res = ebml_read_header(matroska, &doctype, &version)) < 0)
return res;
if ((doctype == NULL) || strcmp(doctype, "matroska")) {
if (ebml_parse(matroska, ebml_syntax, &ebml, 0, 1)
|| ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t)
|| ebml.id_length > sizeof(uint32_t) || strcmp(ebml.doctype, "matroska")
|| ebml.doctype_version > 2) {
av_log(matroska->ctx, AV_LOG_ERROR,
"Wrong EBML doctype ('%s' != 'matroska').\n",
doctype ? doctype : "(none)");
if (doctype)
av_free(doctype);
return AVERROR_NOFMT;
}
av_free(doctype);
if (version > 2) {
av_log(matroska->ctx, AV_LOG_ERROR,
"Matroska demuxer version 2 too old for file version %d\n",
version);
"EBML header using unsupported features\n"
"(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n",
ebml.version, ebml.doctype, ebml.doctype_version);
return AVERROR_NOFMT;
}
ebml_free(ebml_syntax, &ebml);
/* The next thing is a segment. */
while (1) {