matroskadec: use generic parser to parse clusters

Originally committed as revision 14573 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
Aurelien Jacobs 2008-08-05 00:41:05 +00:00
parent f06a488647
commit 209472b45d

View File

@ -216,6 +216,17 @@ typedef struct MatroskaDemuxContext {
AVStream *skip_to_stream;
} MatroskaDemuxContext;
typedef struct {
uint64_t duration;
int64_t reference;
EbmlBin bin;
} MatroskaBlock;
typedef struct {
uint64_t timecode;
EbmlList blocks;
} MatroskaCluster;
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
static EbmlSyntax ebml_header[] = {
@ -426,6 +437,28 @@ static EbmlSyntax matroska_segments[] = {
{ 0 }
};
static EbmlSyntax matroska_blockgroup[] = {
{ MATROSKA_ID_BLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) },
{ MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) },
{ MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock,duration), {.u=AV_NOPTS_VALUE} },
{ MATROSKA_ID_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock,reference) },
{ EBML_ID_VOID, EBML_NONE },
{ 0 }
};
static 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} },
{ EBML_ID_VOID, EBML_NONE },
{ 0 }
};
static EbmlSyntax matroska_clusters[] = {
{ MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, {.n=matroska_cluster} },
{ 0 }
};
/*
* The first few functions handle EBML file parsing. The rest
* is the document interpretation. Matroska really just is a
@ -1675,7 +1708,6 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size,
int res = 0;
AVStream *st;
AVPacket *pkt;
uint8_t *origdata = data;
int16_t block_time;
uint32_t *lace_size = NULL;
int n, flags, laces = 0;
@ -1684,7 +1716,6 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size,
/* first byte(s): tracknum */
if ((n = matroska_ebmlnum_uint(data, size, &num)) < 0) {
av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n");
av_free(origdata);
return res;
}
data += n;
@ -1695,12 +1726,10 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size,
if (size <= 3 || !track || !track->stream) {
av_log(matroska->ctx, AV_LOG_INFO,
"Invalid stream %"PRIu64" or size %u\n", num, size);
av_free(origdata);
return res;
}
st = track->stream;
if (st->discard >= AVDISCARD_ALL) {
av_free(origdata);
return res;
}
if (duration == AV_NOPTS_VALUE)
@ -1716,7 +1745,6 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size,
if (matroska->skip_to_keyframe) {
if (!is_keyframe || st != matroska->skip_to_stream) {
av_free(origdata);
return res;
}
matroska->skip_to_keyframe = 0;
@ -1885,151 +1913,25 @@ matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int size,
}
av_free(lace_size);
av_free(origdata);
return res;
}
static int
matroska_parse_blockgroup (MatroskaDemuxContext *matroska,
uint64_t cluster_time)
{
int res = 0;
uint32_t id;
int is_keyframe = PKT_FLAG_KEY, last_num_packets = matroska->num_packets;
uint64_t duration = AV_NOPTS_VALUE;
uint8_t *data;
int size = 0;
int64_t pos = 0;
av_log(matroska->ctx, AV_LOG_DEBUG, "parsing blockgroup...\n");
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
break;
}
switch (id) {
/* one block inside the group. Note, block parsing is one
* of the harder things, so this code is a bit complicated.
* See http://www.matroska.org/ for documentation. */
case MATROSKA_ID_BLOCK: {
pos = url_ftell(matroska->ctx->pb);
res = ebml_read_binary(matroska, &id, &data, &size);
break;
}
case MATROSKA_ID_BLOCKDURATION: {
if ((res = ebml_read_uint(matroska, &id, &duration)) < 0)
break;
break;
}
case MATROSKA_ID_BLOCKREFERENCE: {
int64_t num;
/* We've found a reference, so not even the first frame in
* the lace is a key frame. */
is_keyframe = 0;
if (last_num_packets != matroska->num_packets)
matroska->packets[last_num_packets]->flags = 0;
if ((res = ebml_read_sint(matroska, &id, &num)) < 0)
break;
break;
}
default:
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown entry 0x%x in blockgroup data\n", id);
/* fall-through */
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
break;
}
if (matroska->level_up) {
matroska->level_up--;
break;
}
}
if (res)
return res;
if (size > 0)
res = matroska_parse_block(matroska, data, size, pos, cluster_time,
duration, is_keyframe);
return res;
}
static int
matroska_parse_cluster (MatroskaDemuxContext *matroska)
{
int res = 0;
uint32_t id;
uint64_t cluster_time = 0;
uint8_t *data;
int64_t pos;
int size;
av_log(matroska->ctx, AV_LOG_DEBUG,
"parsing cluster at %"PRId64"\n", url_ftell(matroska->ctx->pb));
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
res = AVERROR(EIO);
break;
} else if (matroska->level_up) {
matroska->level_up--;
break;
}
switch (id) {
/* cluster timecode */
case MATROSKA_ID_CLUSTERTIMECODE: {
uint64_t num;
if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
break;
cluster_time = num;
break;
}
/* a group of blocks inside a cluster */
case MATROSKA_ID_BLOCKGROUP:
if ((res = ebml_read_master(matroska, &id)) < 0)
break;
res = matroska_parse_blockgroup(matroska, cluster_time);
break;
case MATROSKA_ID_SIMPLEBLOCK:
pos = url_ftell(matroska->ctx->pb);
res = ebml_read_binary(matroska, &id, &data, &size);
if (res == 0)
res = matroska_parse_block(matroska, data, size, pos,
cluster_time, AV_NOPTS_VALUE,
-1);
break;
default:
av_log(matroska->ctx, AV_LOG_INFO,
"Unknown entry 0x%x in cluster data\n", id);
/* fall-through */
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
break;
}
if (matroska->level_up) {
matroska->level_up--;
break;
}
}
MatroskaCluster cluster = { 0 };
EbmlList *blocks_list;
MatroskaBlock *blocks;
int i, res = ebml_parse(matroska, matroska_clusters, &cluster, 0, 1);
blocks_list = &cluster.blocks;
blocks = blocks_list->elem;
for (i=0; !res && i<blocks_list->nb_elem; i++)
if (blocks[i].bin.size > 0)
res=matroska_parse_block(matroska,
blocks[i].bin.data, blocks[i].bin.size,
blocks[i].bin.pos, cluster.timecode,
blocks[i].duration, !blocks[i].reference);
ebml_free(matroska_cluster, &cluster);
return res;
}
@ -2038,8 +1940,6 @@ matroska_read_packet (AVFormatContext *s,
AVPacket *pkt)
{
MatroskaDemuxContext *matroska = s->priv_data;
int res;
uint32_t id;
/* Read stream until we have a packet queued. */
while (matroska_deliver_packet(matroska, pkt)) {
@ -2048,36 +1948,7 @@ matroska_read_packet (AVFormatContext *s,
if (matroska->done)
return AVERROR(EIO);
res = 0;
while (res == 0) {
if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
return AVERROR(EIO);
} else if (matroska->level_up) {
matroska->level_up--;
break;
}
switch (id) {
case MATROSKA_ID_CLUSTER:
if ((res = ebml_read_master(matroska, &id)) < 0)
break;
if ((res = matroska_parse_cluster(matroska)) == 0)
res = 1; /* Parsed one cluster, let's get out. */
break;
default:
case EBML_ID_VOID:
res = ebml_read_skip(matroska);
break;
}
if (matroska->level_up) {
matroska->level_up--;
break;
}
}
if (res == -1)
if (matroska_parse_cluster(matroska) < 0)
matroska->done = 1;
}