diff mbox

[FFmpeg-devel,1/3] vp9_parser: Return stream properties

Message ID 20190319001822.5476-1-sw@jkqxz.net
State New
Headers show

Commit Message

Mark Thompson March 19, 2019, 12:18 a.m. UTC
Rewrites the parser entirely, using CBS for header parsing.  A new
entrypoint to the CBS code is added to avoid any copy overhead.
---
On 18/03/2019 03:22, Li, Zhong wrote:
> If you think that case can be resolved for VP9 parser, could you please provide more details?

Since all the infrastructure is already available it seemed easier to just do it than to try to explain it all needs to fit together.

- Mark


 libavcodec/cbs_vp9.c    |  62 ++++++++++++++++++++++
 libavcodec/cbs_vp9.h    |  12 +++++
 libavcodec/vp9_parser.c | 113 +++++++++++++++++++++++++++-------------
 3 files changed, 152 insertions(+), 35 deletions(-)

Comments

James Almer March 19, 2019, 12:40 a.m. UTC | #1
On 3/18/2019 9:18 PM, Mark Thompson wrote:
> Rewrites the parser entirely, using CBS for header parsing.  A new
> entrypoint to the CBS code is added to avoid any copy overhead.
> ---
> On 18/03/2019 03:22, Li, Zhong wrote:
>> If you think that case can be resolved for VP9 parser, could you please provide more details?
> 
> Since all the infrastructure is already available it seemed easier to just do it than to try to explain it all needs to fit together.
> 
> - Mark
> 
> 
>  libavcodec/cbs_vp9.c    |  62 ++++++++++++++++++++++
>  libavcodec/cbs_vp9.h    |  12 +++++
>  libavcodec/vp9_parser.c | 113 +++++++++++++++++++++++++++-------------
>  3 files changed, 152 insertions(+), 35 deletions(-)
> 
> diff --git a/libavcodec/cbs_vp9.c b/libavcodec/cbs_vp9.c
> index 0b5f137ed8..237416a06f 100644
> --- a/libavcodec/cbs_vp9.c
> +++ b/libavcodec/cbs_vp9.c
> @@ -690,3 +690,65 @@ const CodedBitstreamType ff_cbs_type_vp9 = {
>  
>      .close             = &cbs_vp9_close,
>  };
> +
> +int ff_cbs_vp9_parse_headers(CodedBitstreamContext *ctx,
> +                             VP9RawFrameHeader *header,
> +                             const uint8_t *data, size_t data_size)
> +{
> +    GetBitContext gbc;
> +    uint8_t superframe_header;
> +    int err;
> +
> +    if (data_size < 1)
> +        return AVERROR_INVALIDDATA;
> +
> +    superframe_header = data[data_size - 1];
> +    if ((superframe_header & 0xe0) == 0xc0) {
> +        VP9RawSuperframeIndex sfi;
> +        size_t index_size, pos;
> +        int i;
> +
> +        index_size = 2 + (((superframe_header & 0x18) >> 3) + 1) *
> +                          ((superframe_header & 0x07) + 1);
> +        if (index_size > data_size)
> +            return AVERROR_INVALIDDATA;
> +
> +        err = init_get_bits(&gbc, data + data_size - index_size,
> +                            8 * index_size);
> +        if (err < 0)
> +            return err;
> +
> +        err = cbs_vp9_read_superframe_index(ctx, &gbc, &sfi);
> +        if (err < 0)
> +            return err;
> +
> +        pos = 0;
> +        for (i = 0; i <= sfi.frames_in_superframe_minus_1; i++) {
> +            if (pos + sfi.frame_sizes[i] + index_size > data_size)
> +                return AVERROR_INVALIDDATA;
> +
> +            err = init_get_bits(&gbc, data + pos,
> +                                8 * sfi.frame_sizes[i]);
> +            if (err < 0)
> +                return err;
> +
> +            memset(header, 0, sizeof(*header));
> +            err = cbs_vp9_read_uncompressed_header(ctx, &gbc, header);
> +            if (err < 0)
> +                return err;
> +
> +            pos += sfi.frame_sizes[i];
> +        }
> +
> +    } else {
> +        err = init_get_bits(&gbc, data, 8 * data_size);
> +        if (err < 0)
> +            return err;
> +
> +        err = cbs_vp9_read_uncompressed_header(ctx, &gbc, header);
> +        if (err < 0)
> +            return err;
> +    }
> +
> +    return 0;
> +}
> diff --git a/libavcodec/cbs_vp9.h b/libavcodec/cbs_vp9.h
> index 4c9b2f880d..0b7e8dd71b 100644
> --- a/libavcodec/cbs_vp9.h
> +++ b/libavcodec/cbs_vp9.h
> @@ -214,4 +214,16 @@ typedef struct CodedBitstreamVP9Context {
>  } CodedBitstreamVP9Context;
>  
>  
> +/**
> + * Entrypoint for VP9 parser.
> + *
> + * Parses headers only in a VP9 frame, and does not require refcounting.
> + *
> + * The data may contain multiple frames in a superframe; all will be parsed
> + * but the returned information will be for the final frame.
> + */
> +int ff_cbs_vp9_parse_headers(CodedBitstreamContext *ctx,
> +                             VP9RawFrameHeader *header,
> +                             const uint8_t *data, size_t data_size);
> +
>  #endif /* AVCODEC_CBS_VP9_H */
> diff --git a/libavcodec/vp9_parser.c b/libavcodec/vp9_parser.c
> index c957a75667..5dd4d8d434 100644
> --- a/libavcodec/vp9_parser.c
> +++ b/libavcodec/vp9_parser.c
> @@ -1,8 +1,5 @@
>  /*
> - * VP9 compatible video decoder
> - *
> - * Copyright (C) 2013 Ronald S. Bultje <rsbultje gmail com>
> - * Copyright (C) 2013 Clément Bœsch <u pkh me>
> + * VP9 parser

You should add your copyright since you rewrote it from scratch.

>   *
>   * This file is part of FFmpeg.
>   *
> @@ -21,50 +18,96 @@
>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>   */
>  
> -#include "libavutil/intreadwrite.h"
> -#include "libavcodec/get_bits.h"
> +#include "libavutil/avassert.h"
> +#include "cbs.h"
> +#include "cbs_vp9.h"
>  #include "parser.h"
>  
> -static int parse(AVCodecParserContext *ctx,
> -                 AVCodecContext *avctx,
> -                 const uint8_t **out_data, int *out_size,
> -                 const uint8_t *data, int size)
> +typedef struct VP9ParserContext {
> +    CodedBitstreamContext *cbc;
> +    VP9RawFrameHeader frame_header;
> +} VP9ParserContext;
> +
> +static const enum AVPixelFormat vp9_pix_fmts[3][2][2] = {
> +    { // 8-bit.
> +        { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P },
> +        { AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P },
> +    },
> +    { // 10-bit.
> +        { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV440P10 },
> +        { AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10 },
> +    },
> +    { // 12-bit.
> +        { AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12 },
> +        { AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12 },
> +    },
> +};
> +
> +static int vp9_parser_parse(AVCodecParserContext *ctx,
> +                            AVCodecContext *avctx,
> +                            const uint8_t **out_data, int *out_size,
> +                            const uint8_t *data, int size)
>  {
> -    GetBitContext gb;
> -    int res, profile, keyframe;
> +    VP9ParserContext *s = ctx->priv_data;
> +    const CodedBitstreamVP9Context *vp9 = s->cbc->priv_data;
> +    const VP9RawFrameHeader *fh;
> +    int err;
>  
>      *out_data = data;
>      *out_size = size;

Maybe set a few sane default values here for pict_type and key_frame,
like h264 and av1 parsers do.

>  
> -    if (!size || (res = init_get_bits8(&gb, data, size)) < 0)
> -        return size; // parsers can't return errors
> -    get_bits(&gb, 2); // frame marker
> -    profile  = get_bits1(&gb);
> -    profile |= get_bits1(&gb) << 1;
> -    if (profile == 3) profile += get_bits1(&gb);
> -    if (profile > 3)
> -        return size;
> -
> -    avctx->profile = profile;
> -
> -    if (get_bits1(&gb)) {
> -        keyframe = 0;
> -    } else {
> -        keyframe  = !get_bits1(&gb);
> -    }
> +    if (!size)
> +        return 0;
>  
> -    if (!keyframe) {
> -        ctx->pict_type = AV_PICTURE_TYPE_P;
> -        ctx->key_frame = 0;
> -    } else {
> -        ctx->pict_type = AV_PICTURE_TYPE_I;
> -        ctx->key_frame = 1;
> +    s->cbc->log_ctx = avctx;

Adding a custom context like you suggested before may be a better idea
(I need to do the same for av1).

> +
> +    err = ff_cbs_vp9_parse_headers(s->cbc, &s->frame_header, data, size);
> +    if (err < 0) {
> +        av_log(avctx, AV_LOG_WARNING, "Failed to parse VP9 frame headers.\n");
> +        goto end;
>      }
> +    fh = &s->frame_header;
> +
> +    avctx->profile = vp9->profile;
> +    avctx->level   = FF_LEVEL_UNKNOWN;
> +
> +    ctx->width  = ctx->coded_width  = vp9->frame_width;
> +    ctx->height = ctx->coded_height = vp9->frame_height;
> +
> +    ctx->pict_type = fh->intra_only ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
> +    ctx->key_frame = !fh->frame_type;
> +
> +    ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
> +
> +    av_assert0(vp9->bit_depth == 8  ||
> +               vp9->bit_depth == 10 ||
> +               vp9->bit_depth == 12);
> +
> +    ctx->format = vp9_pix_fmts[(vp9->bit_depth - 8) / 2]
> +                              [vp9->subsampling_x][vp9->subsampling_y];
> +
> +end:
> +    s->cbc->log_ctx = NULL;
>  
>      return size;
>  }
>  
> +static av_cold int vp9_parser_init(AVCodecParserContext *ctx)
> +{
> +    VP9ParserContext *s = ctx->priv_data;
> +    return ff_cbs_init(&s->cbc, AV_CODEC_ID_VP9, NULL);
> +}
> +
> +static av_cold void vp9_parser_close(AVCodecParserContext *ctx)
> +{
> +    VP9ParserContext *s = ctx->priv_data;
> +    ff_cbs_close(&s->cbc);
> +}
> +
>  AVCodecParser ff_vp9_parser = {
>      .codec_ids      = { AV_CODEC_ID_VP9 },
> -    .parser_parse   = parse,
> +    .priv_data_size = sizeof(VP9ParserContext),
> +    .parser_init    = vp9_parser_init,
> +    .parser_close   = vp9_parser_close,
> +    .parser_parse   = vp9_parser_parse,
>  };
>
Zhong Li March 23, 2019, 3:14 p.m. UTC | #2
> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf

> Of Mark Thompson

> Sent: Tuesday, March 19, 2019 8:18 AM

> To: ffmpeg-devel@ffmpeg.org

> Subject: [FFmpeg-devel] [PATCH 1/3] vp9_parser: Return stream properties

> 

> Rewrites the parser entirely, using CBS for header parsing.  A new

> entrypoint to the CBS code is added to avoid any copy overhead.

> ---

> On 18/03/2019 03:22, Li, Zhong wrote:

> > If you think that case can be resolved for VP9 parser, could you please

> provide more details?

> 

> Since all the infrastructure is already available it seemed easier to just do it

> than to try to explain it all needs to fit together.

> 

> - Mark


Thank, it is quite helpful. Verified it with qsv vp9 decoder on KBL and it works.
(BTW, MJPEG parser should can reuse cbs code too.)

> 

> 

>  libavcodec/cbs_vp9.c    |  62 ++++++++++++++++++++++

>  libavcodec/cbs_vp9.h    |  12 +++++

>  libavcodec/vp9_parser.c | 113 +++++++++++++++++++++++++++-------------

>  3 files changed, 152 insertions(+), 35 deletions(-)

> 

> diff --git a/libavcodec/cbs_vp9.c b/libavcodec/cbs_vp9.c index

> 0b5f137ed8..237416a06f 100644

> --- a/libavcodec/cbs_vp9.c

> +++ b/libavcodec/cbs_vp9.c

> @@ -690,3 +690,65 @@ const CodedBitstreamType ff_cbs_type_vp9 = {

> 

>      .close             = &cbs_vp9_close,

>  };

> +

> +int ff_cbs_vp9_parse_headers(CodedBitstreamContext *ctx,

> +                             VP9RawFrameHeader *header,

> +                             const uint8_t *data, size_t data_size) {

> +    GetBitContext gbc;

> +    uint8_t superframe_header;

> +    int err;

> +

> +    if (data_size < 1)

> +        return AVERROR_INVALIDDATA;

> +

> +    superframe_header = data[data_size - 1];

> +    if ((superframe_header & 0xe0) == 0xc0) {


Define a macro should be helpful to read and maintain.

> +        VP9RawSuperframeIndex sfi;

> +        size_t index_size, pos;

> +        int i;

> +

> +        index_size = 2 + (((superframe_header & 0x18) >> 3) + 1) *

> +                          ((superframe_header & 0x07) + 1);

> +        if (index_size > data_size)

> +            return AVERROR_INVALIDDATA;

> +

> +        err = init_get_bits(&gbc, data + data_size - index_size,

> +                            8 * index_size);

> +        if (err < 0)

> +            return err;

> +

> +        err = cbs_vp9_read_superframe_index(ctx, &gbc, &sfi);

> +        if (err < 0)

> +            return err;

> +

> +        pos = 0;

> +        for (i = 0; i <= sfi.frames_in_superframe_minus_1; i++) {

> +            if (pos + sfi.frame_sizes[i] + index_size > data_size)

> +                return AVERROR_INVALIDDATA;

> +

> +            err = init_get_bits(&gbc, data + pos,

> +                                8 * sfi.frame_sizes[i]);

> +            if (err < 0)

> +                return err;

> +

> +            memset(header, 0, sizeof(*header));

> +            err = cbs_vp9_read_uncompressed_header(ctx, &gbc,

> header);

> +            if (err < 0)

> +                return err;

> +

> +            pos += sfi.frame_sizes[i];

> +        }

> +

> +    } else {

> +        err = init_get_bits(&gbc, data, 8 * data_size);

> +        if (err < 0)

> +            return err;

> +

> +        err = cbs_vp9_read_uncompressed_header(ctx, &gbc, header);

> +        if (err < 0)

> +            return err;

> +    }

> +

> +    return 0;

> +}

> diff --git a/libavcodec/cbs_vp9.h b/libavcodec/cbs_vp9.h index

> 4c9b2f880d..0b7e8dd71b 100644

> --- a/libavcodec/cbs_vp9.h

> +++ b/libavcodec/cbs_vp9.h

> @@ -214,4 +214,16 @@ typedef struct CodedBitstreamVP9Context {  }

> CodedBitstreamVP9Context;

> 

> 

> +/**

> + * Entrypoint for VP9 parser.

> + *

> + * Parses headers only in a VP9 frame, and does not require refcounting.

> + *

> + * The data may contain multiple frames in a superframe; all will be

> +parsed

> + * but the returned information will be for the final frame.

> + */

> +int ff_cbs_vp9_parse_headers(CodedBitstreamContext *ctx,

> +                             VP9RawFrameHeader *header,

> +                             const uint8_t *data, size_t data_size);

> +

>  #endif /* AVCODEC_CBS_VP9_H */

> diff --git a/libavcodec/vp9_parser.c b/libavcodec/vp9_parser.c index

> c957a75667..5dd4d8d434 100644

> --- a/libavcodec/vp9_parser.c

> +++ b/libavcodec/vp9_parser.c

> @@ -1,8 +1,5 @@

>  /*

> - * VP9 compatible video decoder

> - *

> - * Copyright (C) 2013 Ronald S. Bultje <rsbultje gmail com>

> - * Copyright (C) 2013 Clément Bœsch <u pkh me>

> + * VP9 parser

>   *

>   * This file is part of FFmpeg.

>   *

> @@ -21,50 +18,96 @@

>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301

> USA

>   */

> 

> -#include "libavutil/intreadwrite.h"

> -#include "libavcodec/get_bits.h"

> +#include "libavutil/avassert.h"

> +#include "cbs.h"

> +#include "cbs_vp9.h"

>  #include "parser.h"

> 

> -static int parse(AVCodecParserContext *ctx,

> -                 AVCodecContext *avctx,

> -                 const uint8_t **out_data, int *out_size,

> -                 const uint8_t *data, int size)

> +typedef struct VP9ParserContext {

> +    CodedBitstreamContext *cbc;

> +    VP9RawFrameHeader frame_header;

> +} VP9ParserContext;

> +

> +static const enum AVPixelFormat vp9_pix_fmts[3][2][2] = {

> +    { // 8-bit.

> +        { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P },

> +        { AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P },

> +    },

> +    { // 10-bit.

> +        { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV440P10 },

> +        { AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10 },

> +    },

> +    { // 12-bit.

> +        { AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12 },

> +        { AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12 },

> +    },

> +};

> +

> +static int vp9_parser_parse(AVCodecParserContext *ctx,

> +                            AVCodecContext *avctx,

> +                            const uint8_t **out_data, int *out_size,

> +                            const uint8_t *data, int size)

>  {

> -    GetBitContext gb;

> -    int res, profile, keyframe;

> +    VP9ParserContext *s = ctx->priv_data;

> +    const CodedBitstreamVP9Context *vp9 = s->cbc->priv_data;

> +    const VP9RawFrameHeader *fh;

> +    int err;

> 

>      *out_data = data;

>      *out_size = size;

> 

> -    if (!size || (res = init_get_bits8(&gb, data, size)) < 0)

> -        return size; // parsers can't return errors

> -    get_bits(&gb, 2); // frame marker

> -    profile  = get_bits1(&gb);

> -    profile |= get_bits1(&gb) << 1;

> -    if (profile == 3) profile += get_bits1(&gb);

> -    if (profile > 3)

> -        return size;

> -

> -    avctx->profile = profile;

> -

> -    if (get_bits1(&gb)) {

> -        keyframe = 0;

> -    } else {

> -        keyframe  = !get_bits1(&gb);

> -    }

> +    if (!size)

> +        return 0;

> 

> -    if (!keyframe) {

> -        ctx->pict_type = AV_PICTURE_TYPE_P;

> -        ctx->key_frame = 0;

> -    } else {

> -        ctx->pict_type = AV_PICTURE_TYPE_I;

> -        ctx->key_frame = 1;

> +    s->cbc->log_ctx = avctx;

> +

> +    err = ff_cbs_vp9_parse_headers(s->cbc, &s->frame_header, data,

> size);

> +    if (err < 0) {

> +        av_log(avctx, AV_LOG_WARNING, "Failed to parse VP9 frame

> headers.\n");

> +        goto end;

>      }

> +    fh = &s->frame_header;

> +

> +    avctx->profile = vp9->profile;

> +    avctx->level   = FF_LEVEL_UNKNOWN;

> +

> +    ctx->width  = ctx->coded_width  = vp9->frame_width;

> +    ctx->height = ctx->coded_height = vp9->frame_height;

> +

> +    ctx->pict_type = fh->intra_only ? AV_PICTURE_TYPE_I :

> AV_PICTURE_TYPE_P;


I found it is different as previous version, so I took a look at spec and cbs_vp9_syntax. 

I believe the intra_only is only usable for non-key frame, so it should be something like: 
   ctx->pict_type = is_key_frame ? AV_PICTURE_TYPE_I : (fh->intra_only ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P ); 

> +    ctx->key_frame = !fh->frame_type;

> +

> +    ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;

> +

> +    av_assert0(vp9->bit_depth == 8  ||

> +               vp9->bit_depth == 10 ||

> +               vp9->bit_depth == 12);

> +

> +    ctx->format = vp9_pix_fmts[(vp9->bit_depth - 8) / 2]

> +

> [vp9->subsampling_x][vp9->subsampling_y];

> +

> +end:

> +    s->cbc->log_ctx = NULL;

> 

>      return size;

>  }

> 

> +static av_cold int vp9_parser_init(AVCodecParserContext *ctx) {

> +    VP9ParserContext *s = ctx->priv_data;

> +    return ff_cbs_init(&s->cbc, AV_CODEC_ID_VP9, NULL); }

> +

> +static av_cold void vp9_parser_close(AVCodecParserContext *ctx) {

> +    VP9ParserContext *s = ctx->priv_data;

> +    ff_cbs_close(&s->cbc);

> +}

> +

>  AVCodecParser ff_vp9_parser = {

>      .codec_ids      = { AV_CODEC_ID_VP9 },

> -    .parser_parse   = parse,

> +    .priv_data_size = sizeof(VP9ParserContext),

> +    .parser_init    = vp9_parser_init,

> +    .parser_close   = vp9_parser_close,

> +    .parser_parse   = vp9_parser_parse,

>  };

> --

> 2.19.2


The reset LGTM.
Zhong Li March 28, 2019, 12:35 p.m. UTC | #3
> > +    ctx->width  = ctx->coded_width  = vp9->frame_width;

> > +    ctx->height = ctx->coded_height = vp9->frame_height;

> > +

> > +    ctx->pict_type = fh->intra_only ? AV_PICTURE_TYPE_I :

> > AV_PICTURE_TYPE_P;

> 

> I found it is different as previous version, so I took a look at spec and

> cbs_vp9_syntax.

> 

> I believe the intra_only is only usable for non-key frame, so it should be

> something like:

>    ctx->pict_type = is_key_frame ? AV_PICTURE_TYPE_I : (fh->intra_only ?

> AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P );


Any update? I am looking forward this patch can be applied with this fix.
diff mbox

Patch

diff --git a/libavcodec/cbs_vp9.c b/libavcodec/cbs_vp9.c
index 0b5f137ed8..237416a06f 100644
--- a/libavcodec/cbs_vp9.c
+++ b/libavcodec/cbs_vp9.c
@@ -690,3 +690,65 @@  const CodedBitstreamType ff_cbs_type_vp9 = {
 
     .close             = &cbs_vp9_close,
 };
+
+int ff_cbs_vp9_parse_headers(CodedBitstreamContext *ctx,
+                             VP9RawFrameHeader *header,
+                             const uint8_t *data, size_t data_size)
+{
+    GetBitContext gbc;
+    uint8_t superframe_header;
+    int err;
+
+    if (data_size < 1)
+        return AVERROR_INVALIDDATA;
+
+    superframe_header = data[data_size - 1];
+    if ((superframe_header & 0xe0) == 0xc0) {
+        VP9RawSuperframeIndex sfi;
+        size_t index_size, pos;
+        int i;
+
+        index_size = 2 + (((superframe_header & 0x18) >> 3) + 1) *
+                          ((superframe_header & 0x07) + 1);
+        if (index_size > data_size)
+            return AVERROR_INVALIDDATA;
+
+        err = init_get_bits(&gbc, data + data_size - index_size,
+                            8 * index_size);
+        if (err < 0)
+            return err;
+
+        err = cbs_vp9_read_superframe_index(ctx, &gbc, &sfi);
+        if (err < 0)
+            return err;
+
+        pos = 0;
+        for (i = 0; i <= sfi.frames_in_superframe_minus_1; i++) {
+            if (pos + sfi.frame_sizes[i] + index_size > data_size)
+                return AVERROR_INVALIDDATA;
+
+            err = init_get_bits(&gbc, data + pos,
+                                8 * sfi.frame_sizes[i]);
+            if (err < 0)
+                return err;
+
+            memset(header, 0, sizeof(*header));
+            err = cbs_vp9_read_uncompressed_header(ctx, &gbc, header);
+            if (err < 0)
+                return err;
+
+            pos += sfi.frame_sizes[i];
+        }
+
+    } else {
+        err = init_get_bits(&gbc, data, 8 * data_size);
+        if (err < 0)
+            return err;
+
+        err = cbs_vp9_read_uncompressed_header(ctx, &gbc, header);
+        if (err < 0)
+            return err;
+    }
+
+    return 0;
+}
diff --git a/libavcodec/cbs_vp9.h b/libavcodec/cbs_vp9.h
index 4c9b2f880d..0b7e8dd71b 100644
--- a/libavcodec/cbs_vp9.h
+++ b/libavcodec/cbs_vp9.h
@@ -214,4 +214,16 @@  typedef struct CodedBitstreamVP9Context {
 } CodedBitstreamVP9Context;
 
 
+/**
+ * Entrypoint for VP9 parser.
+ *
+ * Parses headers only in a VP9 frame, and does not require refcounting.
+ *
+ * The data may contain multiple frames in a superframe; all will be parsed
+ * but the returned information will be for the final frame.
+ */
+int ff_cbs_vp9_parse_headers(CodedBitstreamContext *ctx,
+                             VP9RawFrameHeader *header,
+                             const uint8_t *data, size_t data_size);
+
 #endif /* AVCODEC_CBS_VP9_H */
diff --git a/libavcodec/vp9_parser.c b/libavcodec/vp9_parser.c
index c957a75667..5dd4d8d434 100644
--- a/libavcodec/vp9_parser.c
+++ b/libavcodec/vp9_parser.c
@@ -1,8 +1,5 @@ 
 /*
- * VP9 compatible video decoder
- *
- * Copyright (C) 2013 Ronald S. Bultje <rsbultje gmail com>
- * Copyright (C) 2013 Clément Bœsch <u pkh me>
+ * VP9 parser
  *
  * This file is part of FFmpeg.
  *
@@ -21,50 +18,96 @@ 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "libavutil/intreadwrite.h"
-#include "libavcodec/get_bits.h"
+#include "libavutil/avassert.h"
+#include "cbs.h"
+#include "cbs_vp9.h"
 #include "parser.h"
 
-static int parse(AVCodecParserContext *ctx,
-                 AVCodecContext *avctx,
-                 const uint8_t **out_data, int *out_size,
-                 const uint8_t *data, int size)
+typedef struct VP9ParserContext {
+    CodedBitstreamContext *cbc;
+    VP9RawFrameHeader frame_header;
+} VP9ParserContext;
+
+static const enum AVPixelFormat vp9_pix_fmts[3][2][2] = {
+    { // 8-bit.
+        { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P },
+        { AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P },
+    },
+    { // 10-bit.
+        { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV440P10 },
+        { AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10 },
+    },
+    { // 12-bit.
+        { AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12 },
+        { AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12 },
+    },
+};
+
+static int vp9_parser_parse(AVCodecParserContext *ctx,
+                            AVCodecContext *avctx,
+                            const uint8_t **out_data, int *out_size,
+                            const uint8_t *data, int size)
 {
-    GetBitContext gb;
-    int res, profile, keyframe;
+    VP9ParserContext *s = ctx->priv_data;
+    const CodedBitstreamVP9Context *vp9 = s->cbc->priv_data;
+    const VP9RawFrameHeader *fh;
+    int err;
 
     *out_data = data;
     *out_size = size;
 
-    if (!size || (res = init_get_bits8(&gb, data, size)) < 0)
-        return size; // parsers can't return errors
-    get_bits(&gb, 2); // frame marker
-    profile  = get_bits1(&gb);
-    profile |= get_bits1(&gb) << 1;
-    if (profile == 3) profile += get_bits1(&gb);
-    if (profile > 3)
-        return size;
-
-    avctx->profile = profile;
-
-    if (get_bits1(&gb)) {
-        keyframe = 0;
-    } else {
-        keyframe  = !get_bits1(&gb);
-    }
+    if (!size)
+        return 0;
 
-    if (!keyframe) {
-        ctx->pict_type = AV_PICTURE_TYPE_P;
-        ctx->key_frame = 0;
-    } else {
-        ctx->pict_type = AV_PICTURE_TYPE_I;
-        ctx->key_frame = 1;
+    s->cbc->log_ctx = avctx;
+
+    err = ff_cbs_vp9_parse_headers(s->cbc, &s->frame_header, data, size);
+    if (err < 0) {
+        av_log(avctx, AV_LOG_WARNING, "Failed to parse VP9 frame headers.\n");
+        goto end;
     }
+    fh = &s->frame_header;
+
+    avctx->profile = vp9->profile;
+    avctx->level   = FF_LEVEL_UNKNOWN;
+
+    ctx->width  = ctx->coded_width  = vp9->frame_width;
+    ctx->height = ctx->coded_height = vp9->frame_height;
+
+    ctx->pict_type = fh->intra_only ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
+    ctx->key_frame = !fh->frame_type;
+
+    ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
+
+    av_assert0(vp9->bit_depth == 8  ||
+               vp9->bit_depth == 10 ||
+               vp9->bit_depth == 12);
+
+    ctx->format = vp9_pix_fmts[(vp9->bit_depth - 8) / 2]
+                              [vp9->subsampling_x][vp9->subsampling_y];
+
+end:
+    s->cbc->log_ctx = NULL;
 
     return size;
 }
 
+static av_cold int vp9_parser_init(AVCodecParserContext *ctx)
+{
+    VP9ParserContext *s = ctx->priv_data;
+    return ff_cbs_init(&s->cbc, AV_CODEC_ID_VP9, NULL);
+}
+
+static av_cold void vp9_parser_close(AVCodecParserContext *ctx)
+{
+    VP9ParserContext *s = ctx->priv_data;
+    ff_cbs_close(&s->cbc);
+}
+
 AVCodecParser ff_vp9_parser = {
     .codec_ids      = { AV_CODEC_ID_VP9 },
-    .parser_parse   = parse,
+    .priv_data_size = sizeof(VP9ParserContext),
+    .parser_init    = vp9_parser_init,
+    .parser_close   = vp9_parser_close,
+    .parser_parse   = vp9_parser_parse,
 };