|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|