[FFmpeg-devel] [PATCH] matroska: Add incremental parsing of clusters.

James Zern jzern at google.com
Tue Apr 17 20:52:18 CEST 2012


On Mon, Apr 16, 2012 at 17:54,  <dalecurtis at chromium.org> wrote:
> From: Dale Curtis <dalecurtis at chromium.org>
>
> 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.
>
I think this will break the case where packets need to be merged
(SSA), something Chrome doesn't deal with.
SSA could be special cased and matroska_parse_cluster called, though
there may be a cleaner way.

> Signed-off-by: Aaron Colwell <acolwell at chromium.org>
> Signed-off-by: Dale Curtis <dalecurtis at chromium.org>
> ---
>  libavformat/matroskadec.c |  105 ++++++++++++++++++++++++++++++++++++---------
>  tests/ref/seek/lavf_mkv   |    2 +-
>  2 files changed, 86 insertions(+), 21 deletions(-)
>
> diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
> index a484c50..41c88b4 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,10 @@ 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;
>  } MatroskaDemuxContext;
>
>  typedef struct {
> @@ -268,11 +277,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 +547,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)
> @@ -1759,6 +1795,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;
> @@ -2059,29 +2096,56 @@ end:
>     return res;
>  }
>
> -static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
> +static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska)
>  {
> -    MatroskaCluster cluster = { 0 };
>     EbmlList *blocks_list;
>     MatroskaBlock *blocks;
>     int i, res;
> -    int64_t pos = avio_tell(matroska->ctx->pb);
> -    matroska->prev_pkt = NULL;
> -    if (matroska->current_id)
> -        pos -= 4;  /* sizeof the ID which was already read */
> -    res = ebml_parse(matroska, matroska_clusters, &cluster);
> -    blocks_list = &cluster.blocks;
> -    blocks = blocks_list->elem;
> -    for (i=0; i<blocks_list->nb_elem; i++)
> +    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 agiain. */
> +        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;
>             res=matroska_parse_block(matroska,
>                                      blocks[i].bin.data, blocks[i].bin.size,
> -                                     blocks[i].bin.pos,  cluster.timecode,
> +                                     blocks[i].bin.pos,
> +                                     matroska->current_cluster.timecode,
>                                      blocks[i].duration, is_keyframe,
> -                                     pos);
> +                                     matroska->current_cluster_pos);
>         }
> -    ebml_free(matroska_cluster, &cluster);
> +    }
> +
> +    if (res < 0)  matroska->done = 1;
>     return res;
>  }
>
> @@ -2093,7 +2157,7 @@ static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt)
>         int64_t pos = avio_tell(matroska->ctx->pb);
>         if (matroska->done)
>             return AVERROR_EOF;
> -        if (matroska_parse_cluster(matroska) < 0)
> +        if (matroska_parse_cluster_incremental(matroska) < 0)
>             matroska_resync(matroska, pos);
>     }
>
> @@ -2123,7 +2187,7 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
>         matroska->current_id = 0;
>         while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
>             matroska_clear_queue(matroska);
> -            if (matroska_parse_cluster(matroska) < 0)
> +            if (matroska_parse_cluster_incremental(matroska) < 0)
>                 break;
>         }
>     }
> @@ -2180,6 +2244,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 4e7966b..278d2dc 100644
> --- a/tests/ref/seek/lavf_mkv
> +++ b/tests/ref/seek/lavf_mkv
> @@ -30,7 +30,7 @@ ret: 0         st: 0 flags:1  ts: 2.413000
>  ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292193 size: 27834
>  ret:-1         st: 1 flags:0  ts: 1.307000
>  ret: 0         st: 1 flags:1  ts: 0.201000
> -ret: 0         st: 1 flags:1 dts: 0.198000 pts: 0.198000 pos:    555 size:   208
> +ret: 0         st: 1 flags:1 dts: 0.015000 pts: 0.015000 pos:    555 size:   208
>  ret: 0         st:-1 flags:0  ts:-0.904994
>  ret: 0         st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos:    555 size:   208
>  ret: 0         st:-1 flags:1  ts: 1.989173
> --
> 1.7.7.3
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel


More information about the ffmpeg-devel mailing list