aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDale Curtis <dalecurtis@chromium.org>2012-04-19 11:12:24 -0700
committerLuca Barbato <lu_zero@gentoo.org>2012-04-22 17:23:50 -0700
commit8336eb6f85e4b94b9c198b16bd0ac4178f4dba86 (patch)
treeb7bf84ad49f9b3f700127a9e8514efd595a760ac
parent1381081cdb5999892d69f2ff010cf9371bfc7978 (diff)
downloadffmpeg-8336eb6f85e4b94b9c198b16bd0ac4178f4dba86.tar.gz
matroska: Add incremental parsing of clusters.
Reduces the amount of upfront data required for cluster parsing thus decreasing latency on seek and startup. The change in the seek-lavf_mkv FATE test is due to incremental parsing no longer reading as much data as the old parser and thus not having that additional data to generate index entries based on keyframes. Index entries are added correctly as the file is parsed. All FATE tests pass and Chrome has been using this patch for ~6 months without issue. Currently incremental parsing is not supported for files with SSA tracks since they require merging packets between clusters. In this case the code falls back to non-incremental parsing. Signed-off-by: Aaron Colwell <acolwell@chromium.org> Signed-off-by: Dale Curtis <dalecurtis@chromium.org> Signed-off-by: Luca Barbato <lu_zero@gentoo.org>
-rw-r--r--libavformat/matroskadec.c113
-rw-r--r--tests/ref/seek/lavf_mkv2
2 files changed, 108 insertions, 7 deletions
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index 99e0dac0ca..100b97f5dd 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -213,6 +213,11 @@ typedef struct {
} MatroskaLevel;
typedef struct {
+ uint64_t timecode;
+ EbmlList blocks;
+} MatroskaCluster;
+
+typedef struct {
AVFormatContext *ctx;
/* EBML stuff */
@@ -247,6 +252,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 {
@@ -256,11 +268,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} },
@@ -514,6 +521,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" };
/*
@@ -1563,6 +1602,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;
}
}
@@ -1634,6 +1675,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;
@@ -1929,13 +1971,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 = AV_NOPTS_VALUE;
+ 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 */
@@ -2040,6 +2140,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;
diff --git a/tests/ref/seek/lavf_mkv b/tests/ref/seek/lavf_mkv
index 0f76f344c5..e51e9c9c3b 100644
--- a/tests/ref/seek/lavf_mkv
+++ b/tests/ref/seek/lavf_mkv
@@ -34,7 +34,7 @@ ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292150 size: 27834
ret: 0 st: 1 flags:0 ts: 1.307000
ret:-EOF
ret: 0 st: 1 flags:1 ts: 0.201000
-ret: 0 st: 1 flags:1 dts: 0.198000 pts: 0.198000 pos: 512 size: 208
+ret: 0 st: 1 flags:1 dts: 0.015000 pts: 0.015000 pos: 512 size: 208
ret: 0 st:-1 flags:0 ts:-0.904994
ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 512 size: 208
ret: 0 st:-1 flags:1 ts: 1.989173