summaryrefslogtreecommitdiff
path: root/libavformat/matroskadec.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat/matroskadec.c')
-rw-r--r--libavformat/matroskadec.c115
1 files changed, 109 insertions, 6 deletions
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index 41427fe5eb..355bcbb08c 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -224,6 +224,11 @@ typedef struct {
} MatroskaLevel;
typedef struct {
+ uint64_t timecode;
+ EbmlList blocks;
+} MatroskaCluster;
+
+typedef struct {
AVFormatContext *ctx;
/* EBML stuff */
@@ -259,6 +264,13 @@ typedef struct {
/* File has a CUES element, but we defer parsing until it is needed. */
int cues_parsing_deferred;
+
+ 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;
} MatroskaDemuxContext;
typedef struct {
@@ -268,11 +280,6 @@ typedef struct {
EbmlBin bin;
} MatroskaBlock;
-typedef struct {
- uint64_t timecode;
- EbmlList blocks;
-} MatroskaCluster;
-
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} },
@@ -543,6 +550,38 @@ static EbmlSyntax matroska_clusters[] = {
{ 0 }
};
+static 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} },
+ { MATROSKA_ID_CLUSTERPOSITION,EBML_NONE },
+ { MATROSKA_ID_CLUSTERPREVSIZE,EBML_NONE },
+ { MATROSKA_ID_INFO, EBML_NONE },
+ { MATROSKA_ID_CUES, EBML_NONE },
+ { MATROSKA_ID_TAGS, EBML_NONE },
+ { MATROSKA_ID_SEEKHEAD, EBML_NONE },
+ { MATROSKA_ID_CLUSTER, EBML_STOP },
+ { 0 }
+};
+
+static EbmlSyntax matroska_cluster_incremental[] = {
+ { MATROSKA_ID_CLUSTERTIMECODE,EBML_UINT,0, offsetof(MatroskaCluster,timecode) },
+ { MATROSKA_ID_BLOCKGROUP, EBML_STOP },
+ { MATROSKA_ID_SIMPLEBLOCK, EBML_STOP },
+ { MATROSKA_ID_CLUSTERPOSITION,EBML_NONE },
+ { MATROSKA_ID_CLUSTERPREVSIZE,EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_clusters_incremental[] = {
+ { MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, {.n=matroska_cluster_incremental} },
+ { MATROSKA_ID_INFO, EBML_NONE },
+ { MATROSKA_ID_CUES, EBML_NONE },
+ { MATROSKA_ID_TAGS, EBML_NONE },
+ { MATROSKA_ID_SEEKHEAD, EBML_NONE },
+ { 0 }
+};
+
static const char *const matroska_doctypes[] = { "matroska", "webm" };
static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos)
@@ -1693,6 +1732,8 @@ static int matroska_read_header(AVFormatContext *s)
st->need_parsing = AVSTREAM_PARSE_HEADERS;
} else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) {
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ if (st->codec->codec_id == CODEC_ID_SSA)
+ matroska->contains_ssa = 1;
}
}
@@ -1766,6 +1807,7 @@ static int matroska_deliver_packet(MatroskaDemuxContext *matroska,
matroska->packets = newpackets;
} else {
av_freep(&matroska->packets);
+ matroska->prev_pkt = NULL;
}
matroska->num_packets--;
return 0;
@@ -1822,6 +1864,7 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
st = track->stream;
if (st->discard >= AVDISCARD_ALL)
return res;
+ av_assert1(duration != AV_NOPTS_VALUE);
if (!duration)
duration = track->default_duration / matroska->time_scale;
@@ -2066,13 +2109,71 @@ end:
return res;
}
+static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska)
+{
+ EbmlList *blocks_list;
+ MatroskaBlock *blocks;
+ int i, res;
+ res = ebml_parse(matroska,
+ matroska_cluster_incremental_parsing,
+ &matroska->current_cluster);
+ if (res == 1) {
+ /* New Cluster */
+ if (matroska->current_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);
+ matroska->prev_pkt = NULL;
+ /* sizeof the ID which was already read */
+ if (matroska->current_id)
+ matroska->current_cluster_pos -= 4;
+ res = ebml_parse(matroska,
+ matroska_clusters_incremental,
+ &matroska->current_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 : -1;
+ if (!blocks[i].non_simple)
+ blocks[i].duration = 0;
+ res = matroska_parse_block(matroska,
+ blocks[i].bin.data, blocks[i].bin.size,
+ blocks[i].bin.pos,
+ matroska->current_cluster.timecode,
+ blocks[i].duration, is_keyframe,
+ matroska->current_cluster_pos);
+ }
+ }
+
+ if (res < 0) matroska->done = 1;
+ return res;
+}
+
static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
{
MatroskaCluster cluster = { 0 };
EbmlList *blocks_list;
MatroskaBlock *blocks;
int i, res;
- int64_t pos = avio_tell(matroska->ctx->pb);
+ int64_t pos;
+ if (!matroska->contains_ssa)
+ return matroska_parse_cluster_incremental(matroska);
+ pos = avio_tell(matroska->ctx->pb);
matroska->prev_pkt = NULL;
if (matroska->current_id)
pos -= 4; /* sizeof the ID which was already read */
@@ -2129,6 +2230,7 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
avio_seek(s->pb, st->index_entries[st->nb_index_entries-1].pos, SEEK_SET);
matroska->current_id = 0;
while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
+ matroska->prev_pkt = NULL;
matroska_clear_queue(matroska);
if (matroska_parse_cluster(matroska) < 0)
break;
@@ -2187,6 +2289,7 @@ static int matroska_read_close(AVFormatContext *s)
for (n=0; n < matroska->tracks.nb_elem; n++)
if (tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO)
av_free(tracks[n].audio.buf);
+ ebml_free(matroska_cluster, &matroska->current_cluster);
ebml_free(matroska_segment, matroska);
return 0;