mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
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>
This commit is contained in:
parent
1381081cdb
commit
8336eb6f85
@ -212,6 +212,11 @@ typedef struct {
|
|||||||
uint64_t length;
|
uint64_t length;
|
||||||
} MatroskaLevel;
|
} MatroskaLevel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t timecode;
|
||||||
|
EbmlList blocks;
|
||||||
|
} MatroskaCluster;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
AVFormatContext *ctx;
|
AVFormatContext *ctx;
|
||||||
|
|
||||||
@ -247,6 +252,13 @@ typedef struct {
|
|||||||
|
|
||||||
/* File has a CUES element, but we defer parsing until it is needed. */
|
/* File has a CUES element, but we defer parsing until it is needed. */
|
||||||
int cues_parsing_deferred;
|
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;
|
} MatroskaDemuxContext;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -256,11 +268,6 @@ typedef struct {
|
|||||||
EbmlBin bin;
|
EbmlBin bin;
|
||||||
} MatroskaBlock;
|
} MatroskaBlock;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint64_t timecode;
|
|
||||||
EbmlList blocks;
|
|
||||||
} MatroskaCluster;
|
|
||||||
|
|
||||||
static EbmlSyntax ebml_header[] = {
|
static EbmlSyntax ebml_header[] = {
|
||||||
{ EBML_ID_EBMLREADVERSION, EBML_UINT, 0, offsetof(Ebml,version), {.u=EBML_VERSION} },
|
{ 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_EBMLMAXSIZELENGTH, EBML_UINT, 0, offsetof(Ebml,max_size), {.u=8} },
|
||||||
@ -514,6 +521,38 @@ static EbmlSyntax matroska_clusters[] = {
|
|||||||
{ 0 }
|
{ 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 const char *const matroska_doctypes[] = { "matroska", "webm" };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1563,6 +1602,8 @@ static int matroska_read_header(AVFormatContext *s)
|
|||||||
st->need_parsing = AVSTREAM_PARSE_HEADERS;
|
st->need_parsing = AVSTREAM_PARSE_HEADERS;
|
||||||
} else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) {
|
} else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) {
|
||||||
st->codec->codec_type = AVMEDIA_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;
|
matroska->packets = newpackets;
|
||||||
} else {
|
} else {
|
||||||
av_freep(&matroska->packets);
|
av_freep(&matroska->packets);
|
||||||
|
matroska->prev_pkt = NULL;
|
||||||
}
|
}
|
||||||
matroska->num_packets--;
|
matroska->num_packets--;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1929,13 +1971,71 @@ end:
|
|||||||
return res;
|
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)
|
static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
|
||||||
{
|
{
|
||||||
MatroskaCluster cluster = { 0 };
|
MatroskaCluster cluster = { 0 };
|
||||||
EbmlList *blocks_list;
|
EbmlList *blocks_list;
|
||||||
MatroskaBlock *blocks;
|
MatroskaBlock *blocks;
|
||||||
int i, res;
|
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;
|
matroska->prev_pkt = NULL;
|
||||||
if (matroska->current_id)
|
if (matroska->current_id)
|
||||||
pos -= 4; /* sizeof the ID which was already read */
|
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++)
|
for (n=0; n < matroska->tracks.nb_elem; n++)
|
||||||
if (tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO)
|
if (tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO)
|
||||||
av_free(tracks[n].audio.buf);
|
av_free(tracks[n].audio.buf);
|
||||||
|
ebml_free(matroska_cluster, &matroska->current_cluster);
|
||||||
ebml_free(matroska_segment, matroska);
|
ebml_free(matroska_segment, matroska);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -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: 0 st: 1 flags:0 ts: 1.307000
|
||||||
ret:-EOF
|
ret:-EOF
|
||||||
ret: 0 st: 1 flags:1 ts: 0.201000
|
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: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 dts: 0.000000 pts: 0.000000 pos: 512 size: 208
|
||||||
ret: 0 st:-1 flags:1 ts: 1.989173
|
ret: 0 st:-1 flags:1 ts: 1.989173
|
||||||
|
Loading…
Reference in New Issue
Block a user