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

Mark Thompson sw at jkqxz.net
Tue Mar 19 02:18:20 EET 2019


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
  *
  * 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,
 };
-- 
2.19.2



More information about the ffmpeg-devel mailing list