diff mbox series

[FFmpeg-devel,v3] avcodec/cbs_vp8: Add support for VP8 codec bitstream READ methods

Message ID DM6PR11MB2681AF1C7AC1423E1400B455B14B9@DM6PR11MB2681.namprd11.prod.outlook.com
State New
Headers show
Series [FFmpeg-devel,v3] avcodec/cbs_vp8: Add support for VP8 codec bitstream READ methods | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Dai, Jianhui J May 30, 2023, 7:59 a.m. UTC
This commit adds VP8 into cbs supported codec list, and enables the
`trace_headers` bitstream filters to support VP8, besides existing AV1,
H.264, H.265 and VP9. It can be useful to debug VP8 stream issues.

Only the READ methods `read_unit` and `split_fragment` are implemented,
the WRITE methods `write_unit` and `assemble_fragment` return
`AVERROR_PATCHWELCOME` error code. It is because the CBS VP8 WRITE is
unlikely used by any applications at the moment. The WRITE methods can
be added later if there are real requirments.

TESTS: ffmpeg -i fate-suite/vp8/frame_size_change.webm -vcodec copy
-bsf:v trace_headers -f null -

Signed-off-by: Jianhui Dai <jianhui.j.dai@intel.com>
---
 configure                            |   4 +-
 libavcodec/Makefile                  |   1 +
 libavcodec/cbs.c                     |   6 +
 libavcodec/cbs_internal.h            |   1 +
 libavcodec/cbs_vp8.c                 | 360 +++++++++++++++++++++++++++
 libavcodec/cbs_vp8.h                 | 112 +++++++++
 libavcodec/cbs_vp8_syntax_template.c | 224 +++++++++++++++++
 7 files changed, 707 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/cbs_vp8.c
 create mode 100644 libavcodec/cbs_vp8.h
 create mode 100644 libavcodec/cbs_vp8_syntax_template.c

Comments

Dai, Jianhui J June 8, 2023, 3:18 a.m. UTC | #1
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Dai,
> Jianhui J
> Sent: Tuesday, May 30, 2023 4:00 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: [FFmpeg-devel] [PATCH v3] avcodec/cbs_vp8: Add support for VP8
> codec bitstream READ methods
> 
> This commit adds VP8 into cbs supported codec list, and enables the
> `trace_headers` bitstream filters to support VP8, besides existing AV1, H.264,
> H.265 and VP9. It can be useful to debug VP8 stream issues.
> 
> Only the READ methods `read_unit` and `split_fragment` are implemented, the
> WRITE methods `write_unit` and `assemble_fragment` return
> `AVERROR_PATCHWELCOME` error code. It is because the CBS VP8 WRITE is
> unlikely used by any applications at the moment. The WRITE methods can be
> added later if there are real requirments.
> 
> TESTS: ffmpeg -i fate-suite/vp8/frame_size_change.webm -vcodec copy -bsf:v
> trace_headers -f null -
> 
> Signed-off-by: Jianhui Dai <jianhui.j.dai@intel.com>
> ---
>  configure                            |   4 +-
>  libavcodec/Makefile                  |   1 +
>  libavcodec/cbs.c                     |   6 +
>  libavcodec/cbs_internal.h            |   1 +
>  libavcodec/cbs_vp8.c                 | 360 +++++++++++++++++++++++++++
>  libavcodec/cbs_vp8.h                 | 112 +++++++++
>  libavcodec/cbs_vp8_syntax_template.c | 224 +++++++++++++++++
>  7 files changed, 707 insertions(+), 1 deletion(-)  create mode 100644
> libavcodec/cbs_vp8.c  create mode 100644 libavcodec/cbs_vp8.h  create mode
> 100644 libavcodec/cbs_vp8_syntax_template.c
> 
> diff --git a/configure b/configure
> index bb7be67676..b8960d2639 100755
> --- a/configure
> +++ b/configure
> @@ -2432,6 +2432,7 @@ CONFIG_EXTRA="
>      cbs_h265
>      cbs_jpeg
>      cbs_mpeg2
> +    cbs_vp8
>      cbs_vp9
>      deflate_wrapper
>      dirac_parse
> @@ -2713,6 +2714,7 @@ cbs_h264_select="cbs"
>  cbs_h265_select="cbs"
>  cbs_jpeg_select="cbs"
>  cbs_mpeg2_select="cbs"
> +cbs_vp8_select="cbs"
>  cbs_vp9_select="cbs"
>  dct_select="rdft"
>  deflate_wrapper_deps="zlib"
> @@ -3284,7 +3286,7 @@ h264_redundant_pps_bsf_select="cbs_h264"
>  hevc_metadata_bsf_select="cbs_h265"
>  mjpeg2jpeg_bsf_select="jpegtables"
>  mpeg2_metadata_bsf_select="cbs_mpeg2"
> -trace_headers_bsf_select="cbs"
> +trace_headers_bsf_select="cbs cbs_vp8"
>  vp9_metadata_bsf_select="cbs_vp9"
> 
>  # external libraries
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile index
> 3cf4444b7e..1c4f0da1d2 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -78,6 +78,7 @@ OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o
> cbs_sei.o h2645_parse.o
>  OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o cbs_sei.o h2645_parse.o
>  OBJS-$(CONFIG_CBS_JPEG)                += cbs_jpeg.o
>  OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
> +OBJS-$(CONFIG_CBS_VP8)                 += cbs_vp8.o vpx_rac.o
>  OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
>  OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
>  OBJS-$(CONFIG_DCT)                     += dct.o dct32_fixed.o dct32_float.o
> diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index 504197e06d..c77110abb1
> 100644
> --- a/libavcodec/cbs.c
> +++ b/libavcodec/cbs.c
> @@ -46,6 +46,9 @@ static const CodedBitstreamType *const cbs_type_table[]
> = {  #if CONFIG_CBS_MPEG2
>      &ff_cbs_type_mpeg2,
>  #endif
> +#if CONFIG_CBS_VP8
> +    &ff_cbs_type_vp8,
> +#endif
>  #if CONFIG_CBS_VP9
>      &ff_cbs_type_vp9,
>  #endif
> @@ -67,6 +70,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {  #if
> CONFIG_CBS_MPEG2
>      AV_CODEC_ID_MPEG2VIDEO,
>  #endif
> +#if CONFIG_CBS_VP8
> +    AV_CODEC_ID_VP8,
> +#endif
>  #if CONFIG_CBS_VP9
>      AV_CODEC_ID_VP9,
>  #endif
> diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index
> e585c77934..beaf8505d1 100644
> --- a/libavcodec/cbs_internal.h
> +++ b/libavcodec/cbs_internal.h
> @@ -247,6 +247,7 @@ extern const CodedBitstreamType ff_cbs_type_h264;
> extern const CodedBitstreamType ff_cbs_type_h265;  extern const
> CodedBitstreamType ff_cbs_type_jpeg;  extern const CodedBitstreamType
> ff_cbs_type_mpeg2;
> +extern const CodedBitstreamType ff_cbs_type_vp8;
>  extern const CodedBitstreamType ff_cbs_type_vp9;
> 
> 
> diff --git a/libavcodec/cbs_vp8.c b/libavcodec/cbs_vp8.c new file mode 100644
> index 0000000000..a890590cd9
> --- /dev/null
> +++ b/libavcodec/cbs_vp8.c
> @@ -0,0 +1,360 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> +02110-1301 USA  */
> +
> +#include "libavutil/avassert.h"
> +
> +#include "cbs.h"
> +#include "cbs_internal.h"
> +#include "cbs_vp8.h"
> +
> +#include "vp8data.h"
> +
> +// Wrap of VPXRangeCoder to support size check.
> +typedef struct CBSVP8RangeCoder {
> +    VPXRangeCoder c;
> +    const uint8_t *buffer;
> +    int buffer_size;
> +} CBSVP8RangeCoder;
> +
> +static int cbs_vp8_read_unsigned(CodedBitstreamContext *ctx, GetBitContext
> *gbc,
> +                                 int width, const char *name,
> +                                 const int *subscripts, uint32_t
> +*write_to) {
> +    int pos;
> +    uint32_t value;
> +
> +    av_assert0(width > 0 && width <= 24);
> +
> +    if (get_bits_left(gbc) < width) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value: bitstream ended.\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos   = get_bits_count(gbc);
> +    value = get_bits_le(gbc, width);
> +
> +    if (ctx->trace_enable) {
> +        int i;
> +        char bits[33];
> +        for (i = 0; i < width; i++)
> +            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';
> +        bits[i] = 0;
> +
> +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> +    }
> +
> +    *write_to = value;
> +    return 0;
> +}
> +
> +static int cbs_vp8_init_range_decoder(CBSVP8RangeCoder *rac, const uint8_t
> *buf,
> +                                      int buf_size) {
> +    int err;
> +
> +    err = ff_vpx_init_range_decoder(&rac->c, buf, buf_size);
> +    if (err < 0)
> +        return err;
> +
> +    rac->buffer      = buf;
> +    rac->buffer_size = buf_size;
> +    return 0;
> +}
> +
> +static av_unused int cbs_vp8_rac_get(CBSVP8RangeCoder *rac, int width)
> +{
> +    int value = 0;
> +
> +    while (width--)
> +        value = (value << 1) | vpx_rac_get_prob(&rac->c, 128);
> +
> +    return value;
> +}
> +
> +static int cbs_vp8_rac_get_prob_branchy(CBSVP8RangeCoder *rac, int
> +prob) {
> +    return vpx_rac_get_prob_branchy(&rac->c, prob); }
> +
> +static int cbs_vp8_rac_get_bits_count(CBSVP8RangeCoder *rac) {
> +    int bits_count = (rac->c.buffer - rac->buffer) * 8;
> +    bits_count += rac->c.bits;
> +    return bits_count;
> +}
> +
> +static int cbs_vp8_rac_read_unsigned(CodedBitstreamContext *ctx,
> +                                     CBSVP8RangeCoder *rac, int width,
> +                                     const char *name, const int *subscripts,
> +                                     uint32_t *write_to) {
> +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> +    uint32_t value;
> +    int pos;
> +
> +    av_assert0(width >= 0 && width <= 8);
> +
> +    if (vpx_rac_is_end(&rac->c)) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> +               "Invalid value at "
> +               "%s: bitstream ended.\n",
> +               name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos = cbs_vp8_rac_get_bits_count(rac);
> +    if (pos > rac->buffer_size * 8) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> +               "Invalid value at "
> +               "%s: bitstream ended.\n",
> +               name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos += vp8->uncompressed_header_size * 8;
> +
> +    value = cbs_vp8_rac_get(rac, width);
> +
> +    if (ctx->trace_enable) {
> +        char bits[33] = "-";
> +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> +    }
> +
> +    *write_to = value;
> +    return 0;
> +}
> +
> +static int cbs_vp8_rac_read_signed(CodedBitstreamContext *ctx,
> +                                   CBSVP8RangeCoder *rac, int width,
> +                                   const char *name, const int *subscripts,
> +                                   int32_t *write_to) {
> +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> +    int32_t value;
> +    int pos;
> +
> +    av_assert0(width >= 0 && width <= 8);
> +
> +    if (vpx_rac_is_end(&rac->c)) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> +               "Invalid value at "
> +               "%s: bitstream ended.\n",
> +               name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos = cbs_vp8_rac_get_bits_count(rac);
> +    if (pos > rac->buffer_size * 8) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> +               "Invalid value at "
> +               "%s: bitstream ended.\n",
> +               name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos += vp8->uncompressed_header_size * 8;
> +
> +    value = cbs_vp8_rac_get(rac, width);
> +    if (cbs_vp8_rac_get(rac, 1))
> +        value = -value;
> +
> +    if (ctx->trace_enable) {
> +        char bits[33] = "-";
> +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> +    }
> +
> +    *write_to = value;
> +    return 0;
> +}
> +
> +#define HEADER(name) \
> +    do { \
> +        ff_cbs_trace_header(ctx, name); \
> +    } while (0)
> +
> +#define CHECK(call) \
> +    do { \
> +        int err = (call); \
> +        if (err < 0) \
> +            return err; \
> +    } while (0)
> +
> +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name
> #define
> +FUNC_VP8(rw, name) FUNC_NAME(rw, vp8, name) #define FUNC(name)
> +FUNC_VP8(READWRITE, name)
> +
> +#define SUBSCRIPTS(subs, ...) \
> +    (subs > 0 ? ((int[subs + 1]){subs, __VA_ARGS__}) : NULL)
> +
> +#define f(width, name) xf(width, name, 0)
> +
> +#define rac_f(width, name) rac_unsigned_subs(width, name, 0) #define
> +rac_s(width, name) rac_signed_subs(width, name, 0) #define
> +rac_fs(width, name, subs, ...) \
> +    rac_unsigned_subs(width, name, subs, __VA_ARGS__) #define
> +rac_ss(width, name, subs, ...) \
> +    rac_signed_subs(width, name, subs, __VA_ARGS__)
> +
> +#define READ
> +#define READWRITE read
> +#define RWContext GetBitContext
> +
> +#define xf(width, name, subs, ...) \
> +    do { \
> +        uint32_t value; \
> +        CHECK(cbs_vp8_read_unsigned(ctx, rw, width, #name, \
> +                                    SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> +        current->name = value; \
> +    } while (0)
> +
> +#define fixed(width, name, value) \
> +    do { \
> +        av_unused uint32_t fixed_value; \
> +        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, 0, &fixed_value, \
> +                                   value, value)); \
> +    } while (0)
> +
> +#define rac_unsigned_subs(width, name, subs, ...) \
> +    do { \
> +        uint32_t value; \
> +        CHECK(cbs_vp8_rac_read_unsigned( \
> +            ctx, rac, width, #name, SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> +        current->name = value; \
> +    } while (0)
> +
> +#define rac_signed_subs(width, name, subs, ...) \
> +    do { \
> +        int32_t value; \
> +        CHECK(cbs_vp8_rac_read_signed(ctx, rac, width, #name, \
> +                                      SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> +        current->name = value; \
> +    } while (0)
> +
> +#include "cbs_vp8_syntax_template.c"
> +
> +static int cbs_vp8_split_fragment(CodedBitstreamContext *ctx,
> +                                  CodedBitstreamFragment *frag, int
> +header) {
> +    int err;
> +
> +    if (frag->data_size == 0)
> +        return AVERROR_INVALIDDATA;
> +
> +    err = ff_cbs_append_unit_data(frag, 0, frag->data, frag->data_size,
> +                                  frag->data_ref);
> +    if (err < 0)
> +        return err;
> +
> +    return 0;
> +}
> +
> +static int cbs_vp8_read_unit(CodedBitstreamContext *ctx,
> +                             CodedBitstreamUnit *unit) {
> +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> +    VP8RawFrame *frame;
> +    GetBitContext gbc;
> +    CBSVP8RangeCoder rac;
> +    int err, pos;
> +
> +    err = ff_cbs_alloc_unit_content(ctx, unit);
> +    if (err < 0)
> +        return err;
> +    frame = unit->content;
> +
> +    // Create GetBitContext for uncompressed header.
> +    err = init_get_bits8_le(&gbc, unit->data, 8 * unit->data_size);
> +    if (err < 0)
> +        return err;
> +
> +    err = cbs_vp8_read_uncompressed_header(ctx, &gbc, frame);
> +    if (err < 0)
> +        return err;
> +
> +    pos = get_bits_count(&gbc);
> +    av_assert0(pos % 8 == 0);
> +    pos /= 8;
> +    av_assert0(pos <= unit->data_size);
> +
> +    if (pos == unit->data_size)
> +        return AVERROR_INVALIDDATA;
> +
> +    vp8->uncompressed_header_size = pos;
> +
> +    // Create range decoder for compressed header.
> +    err = cbs_vp8_init_range_decoder(
> +        &rac, unit->data + pos, frame->header.first_partition_length_in_bytes);
> +    if (err < 0)
> +        return err;
> +
> +    err = cbs_vp8_read_compressed_header(ctx, &rac, frame);
> +    if (err < 0)
> +        return err;
> +
> +    pos = cbs_vp8_rac_get_bits_count(&rac);
> +    if (pos > rac.buffer_size * 8)
> +        return AVERROR_INVALIDDATA;
> +
> +    frame->data_ref = av_buffer_ref(unit->data_ref);
> +    if (!frame->data_ref)
> +        return AVERROR(ENOMEM);
> +
> +    frame->data      = unit->data + pos;
> +    frame->data_size = unit->data_size - pos;
> +
> +    return 0;
> +}
> +
> +static int cbs_vp8_write_unit(CodedBitstreamContext *ctx,
> +                              CodedBitstreamUnit *unit, PutBitContext
> +*pbc) {
> +    return AVERROR_PATCHWELCOME;
> +}
> +
> +static int cbs_vp8_assemble_fragment(CodedBitstreamContext *ctx,
> +                                     CodedBitstreamFragment *frag) {
> +    return AVERROR_PATCHWELCOME;
> +}
> +
> +static void cbs_vp8_flush(CodedBitstreamContext *ctx) {
> +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> +
> +    vp8->uncompressed_header_size = 0;
> +}
> +
> +static const CodedBitstreamUnitTypeDescriptor cbs_vp8_unit_types[] = {
> +    CBS_UNIT_TYPE_INTERNAL_REF(0, VP8RawFrame, data),
> +    CBS_UNIT_TYPE_END_OF_LIST};
> +
> +const CodedBitstreamType ff_cbs_type_vp8 = {
> +    .codec_id          = AV_CODEC_ID_VP8,
> +
> +    .priv_data_size    = sizeof(CodedBitstreamVP8Context),
> +
> +    .unit_types        = cbs_vp8_unit_types,
> +
> +    .split_fragment    = &cbs_vp8_split_fragment,
> +    .read_unit         = &cbs_vp8_read_unit,
> +    .write_unit        = &cbs_vp8_write_unit,
> +
> +    .flush             = &cbs_vp8_flush,
> +
> +    .assemble_fragment = &cbs_vp8_assemble_fragment, };
> diff --git a/libavcodec/cbs_vp8.h b/libavcodec/cbs_vp8.h new file mode 100644
> index 0000000000..0bfa465ff7
> --- /dev/null
> +++ b/libavcodec/cbs_vp8.h
> @@ -0,0 +1,112 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> +02110-1301 USA  */
> +
> +#ifndef AVCODEC_CBS_VP8_H
> +#define AVCODEC_CBS_VP8_H
> +
> +#include <stddef.h>
> +#include <stdint.h>
> +
> +#include "cbs.h"
> +#include "vpx_rac.h"
> +
> +enum {
> +    VP8_START_CODE_0 = 0x9D,
> +    VP8_START_CODE_1 = 0x01,
> +    VP8_START_CODE_2 = 0x2A,
> +};
> +
> +enum {
> +    VP8_KEY_FRAME     = 0,
> +    VP8_NON_KEY_FRAME = 1,
> +};
> +
> +typedef struct VP8RawFrameHeader {
> +    // frame tag
> +    uint8_t frame_type;
> +    uint8_t profile;
> +    uint8_t show_frame;
> +    uint32_t first_partition_length_in_bytes;
> +
> +    uint16_t width;
> +    uint8_t horizontal_scale;
> +    uint16_t height;
> +    uint8_t vertical_scale;
> +
> +    // frame header
> +    uint8_t color_space;
> +    uint8_t clamping_type;
> +
> +    // segmentation
> +    uint8_t segmentation_enabled;
> +    uint8_t update_segment_map;
> +    uint8_t update_segment_feature_data;
> +    uint8_t segment_feature_mode;
> +    int8_t segment_update_qp[4];
> +    int8_t segment_update_loop_filter_level[4];
> +    uint8_t segment_update_probs[3];
> +
> +    // loop filter
> +    uint8_t loop_filter_type;
> +    uint8_t loop_filter_level;
> +    uint8_t loop_filter_sharpness;
> +    uint8_t mode_ref_lf_delta_enabled;
> +    int8_t ref_lf_deltas[4];
> +    int8_t mode_lf_deltas[4];
> +
> +    uint8_t log2_token_partitions;
> +
> +    // qp
> +    uint8_t base_qindex;
> +    int8_t y1dc_delta_q;
> +    int8_t y2dc_delta_q;
> +    int8_t y2ac_delta_q;
> +    int8_t uvdc_delta_q;
> +    int8_t uvac_delta_q;
> +
> +    // ref
> +    uint8_t refresh_golden_frame;
> +    uint8_t refresh_alternate_frame;
> +    uint8_t ref_frame_sign_bias_golden;
> +    uint8_t ref_frame_sign_bias_alternate;
> +    uint8_t refresh_last_frame;
> +
> +    // token probs
> +    uint8_t refresh_entropy_probs;
> +
> +    uint8_t mb_no_skip_coeff;
> +    uint8_t prob_skip_false;
> +
> +    uint8_t prob_intra;
> +    uint8_t prob_last;
> +    uint8_t prob_golden;
> +} VP8RawFrameHeader;
> +
> +typedef struct VP8RawFrame {
> +    VP8RawFrameHeader header;
> +
> +    uint8_t *data;
> +    AVBufferRef *data_ref;
> +    size_t data_size;
> +} VP8RawFrame;
> +
> +typedef struct CodedBitstreamVP8Context {
> +    uint32_t uncompressed_header_size;
> +} CodedBitstreamVP8Context;
> +
> +#endif /* AVCODEC_CBS_VP8_H */
> diff --git a/libavcodec/cbs_vp8_syntax_template.c
> b/libavcodec/cbs_vp8_syntax_template.c
> new file mode 100644
> index 0000000000..5ab034a164
> --- /dev/null
> +++ b/libavcodec/cbs_vp8_syntax_template.c
> @@ -0,0 +1,224 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> +02110-1301 USA  */
> +
> +static int FUNC(update_segmentation)(CodedBitstreamContext *ctx,
> +                                     CBSVP8RangeCoder *rac,
> +                                     VP8RawFrameHeader *current) {
> +    rac_f(1, update_segment_map);
> +    rac_f(1, update_segment_feature_data);
> +
> +    if (current->update_segment_feature_data) {
> +        rac_f(1, segment_feature_mode);
> +        // quantizer
> +        for (int i = 0; i < 4; i++)
> +            if (cbs_vp8_rac_get(rac, 1))
> +                rac_ss(7, segment_update_qp[i], 1, i);
> +        // loop filter
> +        for (int i = 0; i < 4; i++)
> +            if (cbs_vp8_rac_get(rac, 1))
> +                rac_ss(6, segment_update_loop_filter_level[i], 1, i);
> +    }
> +
> +    if (current->update_segment_map) {
> +        for (int i = 0; i < 3; i++)
> +            if (cbs_vp8_rac_get(rac, 1))
> +                rac_fs(8, segment_update_probs[i], 1, i);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(mode_ref_lf_deltas)(CodedBitstreamContext *ctx,
> +                                    CBSVP8RangeCoder *rac,
> +                                    VP8RawFrameHeader *current) {
> +    rac_f(1, mode_ref_lf_delta_enabled);
> +    if (current->mode_ref_lf_delta_enabled) {
> +        if (cbs_vp8_rac_get(rac, 1)) {
> +            // ref_lf_deltas
> +            for (int i = 0; i < 4; i++)
> +                if (cbs_vp8_rac_get(rac, 1))
> +                    rac_ss(6, ref_lf_deltas[i], 1, i);
> +            // mode_lf_deltas
> +            for (int i = 0; i < 4; i++)
> +                if (cbs_vp8_rac_get(rac, 1))
> +                    rac_ss(6, mode_lf_deltas[i], 1, i);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(quantization_params)(CodedBitstreamContext *ctx,
> +                                     CBSVP8RangeCoder *rac,
> +                                     VP8RawFrameHeader *current) {
> +    rac_f(7, base_qindex);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, y1dc_delta_q);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, y2dc_delta_q);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, y2ac_delta_q);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, uvdc_delta_q);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, uvac_delta_q);
> +
> +    return 0;
> +}
> +
> +static int FUNC(update_token_probs)(CodedBitstreamContext *ctx,
> +                                    CBSVP8RangeCoder *rac,
> +                                    VP8RawFrameHeader *current) {
> +    for (int i = 0; i < 4; ++i) {
> +        for (int j = 0; j < 8; ++j) {
> +            for (int k = 0; k < 3; ++k) {
> +                for (int l = 0; l < 11; ++l)
> +                    if (cbs_vp8_rac_get_prob_branchy(
> +                            rac, vp8_token_update_probs[i][j][k][l]))
> +                        cbs_vp8_rac_get(rac, 8);
> +            }
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(update_mv_probs)(CodedBitstreamContext *ctx,
> +                                 CBSVP8RangeCoder *rac,
> +                                 VP8RawFrameHeader *current) {
> +    for (int i = 0; i < 2; ++i) {
> +        for (int j = 0; j < 19; ++j)
> +            if (cbs_vp8_rac_get(rac, 1))
> +                cbs_vp8_rac_get(rac, 7);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(frame_tag)(CodedBitstreamContext *ctx, RWContext *rw,
> +                           VP8RawFrameHeader *current) {
> +    f(1, frame_type);
> +    f(3, profile);
> +    f(1, show_frame);
> +    f(19, first_partition_length_in_bytes);
> +
> +    if (current->frame_type == VP8_KEY_FRAME) {
> +        fixed(8, start_code_0, VP8_START_CODE_0);
> +        fixed(8, start_code_1, VP8_START_CODE_1);
> +        fixed(8, start_code_2, VP8_START_CODE_2);
> +
> +        f(14, width);
> +        f(2, horizontal_scale);
> +        f(14, height);
> +        f(2, vertical_scale);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(frame_header)(CodedBitstreamContext *ctx,
> CBSVP8RangeCoder *rac,
> +                              VP8RawFrameHeader *current) {
> +    if (current->frame_type == VP8_KEY_FRAME) {
> +        rac_f(1, color_space);
> +        rac_f(1, clamping_type);
> +    }
> +
> +    rac_f(1, segmentation_enabled);
> +    if (current->segmentation_enabled)
> +        CHECK(FUNC(update_segmentation)(ctx, rac, current));
> +
> +    rac_f(1, loop_filter_type);
> +    rac_f(6, loop_filter_level);
> +    rac_f(3, loop_filter_sharpness);
> +
> +    CHECK(FUNC(mode_ref_lf_deltas)(ctx, rac, current));
> +
> +    rac_f(2, log2_token_partitions);
> +
> +    CHECK(FUNC(quantization_params)(ctx, rac, current));
> +
> +    if (current->frame_type != VP8_KEY_FRAME) {
> +        rac_f(1, refresh_golden_frame);
> +        rac_f(1, refresh_alternate_frame);
> +        if (!current->refresh_golden_frame)
> +            cbs_vp8_rac_get(rac, 2);
> +        if (!current->refresh_alternate_frame)
> +            cbs_vp8_rac_get(rac, 2);
> +        rac_f(1, ref_frame_sign_bias_golden);
> +        rac_f(1, ref_frame_sign_bias_alternate);
> +    }
> +    rac_f(1, refresh_entropy_probs);
> +    if (current->frame_type != VP8_KEY_FRAME)
> +        rac_f(1, refresh_last_frame);
> +
> +    CHECK(FUNC(update_token_probs)(ctx, rac, current));
> +
> +    rac_f(1, mb_no_skip_coeff);
> +    if (current->mb_no_skip_coeff)
> +        rac_f(8, prob_skip_false);
> +
> +    if (current->frame_type != VP8_KEY_FRAME) {
> +        rac_f(8, prob_intra);
> +        rac_f(8, prob_last);
> +        rac_f(8, prob_golden);
> +
> +        // intra_16x16_prob
> +        if (cbs_vp8_rac_get(rac, 1))
> +            for (int i = 0; i < 4; i++)
> +                cbs_vp8_rac_get(rac, 8);
> +
> +        // intra_chroma_prob
> +        if (cbs_vp8_rac_get(rac, 1))
> +            for (int i = 0; i < 4; i++)
> +                cbs_vp8_rac_get(rac, 8);
> +
> +        CHECK(FUNC(update_mv_probs)(ctx, rac, current));
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx,
> RWContext *rw,
> +                                     VP8RawFrame *current) {
> +    HEADER("Frame");
> +
> +    CHECK(FUNC(frame_tag)(ctx, rw, &current->header));
> +
> +    return 0;
> +}
> +
> +static int FUNC(compressed_header)(CodedBitstreamContext *ctx,
> +                                   CBSVP8RangeCoder *rac, VP8RawFrame
> +*current) {
> +    CHECK(FUNC(frame_header)(ctx, rac, &current->header));
> +
> +    return 0;
> +}
> --
> 2.25.1
> 

Friendly ping reviews to take a look.

> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org
> with subject "unsubscribe".
Dai, Jianhui J June 20, 2023, 1:42 a.m. UTC | #2
> -----Original Message-----
> From: Dai, Jianhui J
> Sent: Thursday, June 8, 2023 11:18 AM
> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> Subject: RE: [PATCH v3] avcodec/cbs_vp8: Add support for VP8 codec bitstream
> READ methods
> 
> 
> 
> > -----Original Message-----
> > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Dai,
> > Jianhui J
> > Sent: Tuesday, May 30, 2023 4:00 PM
> > To: ffmpeg-devel@ffmpeg.org
> > Subject: [FFmpeg-devel] [PATCH v3] avcodec/cbs_vp8: Add support for
> > VP8 codec bitstream READ methods
> >
> > This commit adds VP8 into cbs supported codec list, and enables the
> > `trace_headers` bitstream filters to support VP8, besides existing
> > AV1, H.264,
> > H.265 and VP9. It can be useful to debug VP8 stream issues.
> >
> > Only the READ methods `read_unit` and `split_fragment` are
> > implemented, the WRITE methods `write_unit` and `assemble_fragment`
> > return `AVERROR_PATCHWELCOME` error code. It is because the CBS VP8
> > WRITE is unlikely used by any applications at the moment. The WRITE
> > methods can be added later if there are real requirments.
> >
> > TESTS: ffmpeg -i fate-suite/vp8/frame_size_change.webm -vcodec copy
> > -bsf:v trace_headers -f null -
> >
> > Signed-off-by: Jianhui Dai <jianhui.j.dai@intel.com>
> > ---
> >  configure                            |   4 +-
> >  libavcodec/Makefile                  |   1 +
> >  libavcodec/cbs.c                     |   6 +
> >  libavcodec/cbs_internal.h            |   1 +
> >  libavcodec/cbs_vp8.c                 | 360 +++++++++++++++++++++++++++
> >  libavcodec/cbs_vp8.h                 | 112 +++++++++
> >  libavcodec/cbs_vp8_syntax_template.c | 224 +++++++++++++++++
> >  7 files changed, 707 insertions(+), 1 deletion(-)  create mode 100644
> > libavcodec/cbs_vp8.c  create mode 100644 libavcodec/cbs_vp8.h  create
> > mode
> > 100644 libavcodec/cbs_vp8_syntax_template.c
> >
> > diff --git a/configure b/configure
> > index bb7be67676..b8960d2639 100755
> > --- a/configure
> > +++ b/configure
> > @@ -2432,6 +2432,7 @@ CONFIG_EXTRA="
> >      cbs_h265
> >      cbs_jpeg
> >      cbs_mpeg2
> > +    cbs_vp8
> >      cbs_vp9
> >      deflate_wrapper
> >      dirac_parse
> > @@ -2713,6 +2714,7 @@ cbs_h264_select="cbs"
> >  cbs_h265_select="cbs"
> >  cbs_jpeg_select="cbs"
> >  cbs_mpeg2_select="cbs"
> > +cbs_vp8_select="cbs"
> >  cbs_vp9_select="cbs"
> >  dct_select="rdft"
> >  deflate_wrapper_deps="zlib"
> > @@ -3284,7 +3286,7 @@ h264_redundant_pps_bsf_select="cbs_h264"
> >  hevc_metadata_bsf_select="cbs_h265"
> >  mjpeg2jpeg_bsf_select="jpegtables"
> >  mpeg2_metadata_bsf_select="cbs_mpeg2"
> > -trace_headers_bsf_select="cbs"
> > +trace_headers_bsf_select="cbs cbs_vp8"
> >  vp9_metadata_bsf_select="cbs_vp9"
> >
> >  # external libraries
> > diff --git a/libavcodec/Makefile b/libavcodec/Makefile index
> > 3cf4444b7e..1c4f0da1d2 100644
> > --- a/libavcodec/Makefile
> > +++ b/libavcodec/Makefile
> > @@ -78,6 +78,7 @@ OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o
> > cbs_sei.o h2645_parse.o
> >  OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o cbs_sei.o h2645_parse.o
> >  OBJS-$(CONFIG_CBS_JPEG)                += cbs_jpeg.o
> >  OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
> > +OBJS-$(CONFIG_CBS_VP8)                 += cbs_vp8.o vpx_rac.o
> >  OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
> >  OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
> >  OBJS-$(CONFIG_DCT)                     += dct.o dct32_fixed.o dct32_float.o
> > diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index
> > 504197e06d..c77110abb1
> > 100644
> > --- a/libavcodec/cbs.c
> > +++ b/libavcodec/cbs.c
> > @@ -46,6 +46,9 @@ static const CodedBitstreamType *const
> > cbs_type_table[] = {  #if CONFIG_CBS_MPEG2
> >      &ff_cbs_type_mpeg2,
> >  #endif
> > +#if CONFIG_CBS_VP8
> > +    &ff_cbs_type_vp8,
> > +#endif
> >  #if CONFIG_CBS_VP9
> >      &ff_cbs_type_vp9,
> >  #endif
> > @@ -67,6 +70,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {  #if
> > CONFIG_CBS_MPEG2
> >      AV_CODEC_ID_MPEG2VIDEO,
> >  #endif
> > +#if CONFIG_CBS_VP8
> > +    AV_CODEC_ID_VP8,
> > +#endif
> >  #if CONFIG_CBS_VP9
> >      AV_CODEC_ID_VP9,
> >  #endif
> > diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
> > index
> > e585c77934..beaf8505d1 100644
> > --- a/libavcodec/cbs_internal.h
> > +++ b/libavcodec/cbs_internal.h
> > @@ -247,6 +247,7 @@ extern const CodedBitstreamType ff_cbs_type_h264;
> > extern const CodedBitstreamType ff_cbs_type_h265;  extern const
> > CodedBitstreamType ff_cbs_type_jpeg;  extern const CodedBitstreamType
> > ff_cbs_type_mpeg2;
> > +extern const CodedBitstreamType ff_cbs_type_vp8;
> >  extern const CodedBitstreamType ff_cbs_type_vp9;
> >
> >
> > diff --git a/libavcodec/cbs_vp8.c b/libavcodec/cbs_vp8.c new file mode
> > 100644 index 0000000000..a890590cd9
> > --- /dev/null
> > +++ b/libavcodec/cbs_vp8.c
> > @@ -0,0 +1,360 @@
> > +/*
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > +02110-1301 USA  */
> > +
> > +#include "libavutil/avassert.h"
> > +
> > +#include "cbs.h"
> > +#include "cbs_internal.h"
> > +#include "cbs_vp8.h"
> > +
> > +#include "vp8data.h"
> > +
> > +// Wrap of VPXRangeCoder to support size check.
> > +typedef struct CBSVP8RangeCoder {
> > +    VPXRangeCoder c;
> > +    const uint8_t *buffer;
> > +    int buffer_size;
> > +} CBSVP8RangeCoder;
> > +
> > +static int cbs_vp8_read_unsigned(CodedBitstreamContext *ctx,
> > +GetBitContext
> > *gbc,
> > +                                 int width, const char *name,
> > +                                 const int *subscripts, uint32_t
> > +*write_to) {
> > +    int pos;
> > +    uint32_t value;
> > +
> > +    av_assert0(width > 0 && width <= 24);
> > +
> > +    if (get_bits_left(gbc) < width) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value: bitstream
> ended.\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos   = get_bits_count(gbc);
> > +    value = get_bits_le(gbc, width);
> > +
> > +    if (ctx->trace_enable) {
> > +        int i;
> > +        char bits[33];
> > +        for (i = 0; i < width; i++)
> > +            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';
> > +        bits[i] = 0;
> > +
> > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > +    }
> > +
> > +    *write_to = value;
> > +    return 0;
> > +}
> > +
> > +static int cbs_vp8_init_range_decoder(CBSVP8RangeCoder *rac, const
> > +uint8_t
> > *buf,
> > +                                      int buf_size) {
> > +    int err;
> > +
> > +    err = ff_vpx_init_range_decoder(&rac->c, buf, buf_size);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    rac->buffer      = buf;
> > +    rac->buffer_size = buf_size;
> > +    return 0;
> > +}
> > +
> > +static av_unused int cbs_vp8_rac_get(CBSVP8RangeCoder *rac, int
> > +width) {
> > +    int value = 0;
> > +
> > +    while (width--)
> > +        value = (value << 1) | vpx_rac_get_prob(&rac->c, 128);
> > +
> > +    return value;
> > +}
> > +
> > +static int cbs_vp8_rac_get_prob_branchy(CBSVP8RangeCoder *rac, int
> > +prob) {
> > +    return vpx_rac_get_prob_branchy(&rac->c, prob); }
> > +
> > +static int cbs_vp8_rac_get_bits_count(CBSVP8RangeCoder *rac) {
> > +    int bits_count = (rac->c.buffer - rac->buffer) * 8;
> > +    bits_count += rac->c.bits;
> > +    return bits_count;
> > +}
> > +
> > +static int cbs_vp8_rac_read_unsigned(CodedBitstreamContext *ctx,
> > +                                     CBSVP8RangeCoder *rac, int width,
> > +                                     const char *name, const int *subscripts,
> > +                                     uint32_t *write_to) {
> > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > +    uint32_t value;
> > +    int pos;
> > +
> > +    av_assert0(width >= 0 && width <= 8);
> > +
> > +    if (vpx_rac_is_end(&rac->c)) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > +               "Invalid value at "
> > +               "%s: bitstream ended.\n",
> > +               name);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos = cbs_vp8_rac_get_bits_count(rac);
> > +    if (pos > rac->buffer_size * 8) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > +               "Invalid value at "
> > +               "%s: bitstream ended.\n",
> > +               name);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos += vp8->uncompressed_header_size * 8;
> > +
> > +    value = cbs_vp8_rac_get(rac, width);
> > +
> > +    if (ctx->trace_enable) {
> > +        char bits[33] = "-";
> > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > +    }
> > +
> > +    *write_to = value;
> > +    return 0;
> > +}
> > +
> > +static int cbs_vp8_rac_read_signed(CodedBitstreamContext *ctx,
> > +                                   CBSVP8RangeCoder *rac, int width,
> > +                                   const char *name, const int *subscripts,
> > +                                   int32_t *write_to) {
> > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > +    int32_t value;
> > +    int pos;
> > +
> > +    av_assert0(width >= 0 && width <= 8);
> > +
> > +    if (vpx_rac_is_end(&rac->c)) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > +               "Invalid value at "
> > +               "%s: bitstream ended.\n",
> > +               name);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos = cbs_vp8_rac_get_bits_count(rac);
> > +    if (pos > rac->buffer_size * 8) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > +               "Invalid value at "
> > +               "%s: bitstream ended.\n",
> > +               name);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos += vp8->uncompressed_header_size * 8;
> > +
> > +    value = cbs_vp8_rac_get(rac, width);
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        value = -value;
> > +
> > +    if (ctx->trace_enable) {
> > +        char bits[33] = "-";
> > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > +    }
> > +
> > +    *write_to = value;
> > +    return 0;
> > +}
> > +
> > +#define HEADER(name) \
> > +    do { \
> > +        ff_cbs_trace_header(ctx, name); \
> > +    } while (0)
> > +
> > +#define CHECK(call) \
> > +    do { \
> > +        int err = (call); \
> > +        if (err < 0) \
> > +            return err; \
> > +    } while (0)
> > +
> > +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name
> > #define
> > +FUNC_VP8(rw, name) FUNC_NAME(rw, vp8, name) #define FUNC(name)
> > +FUNC_VP8(READWRITE, name)
> > +
> > +#define SUBSCRIPTS(subs, ...) \
> > +    (subs > 0 ? ((int[subs + 1]){subs, __VA_ARGS__}) : NULL)
> > +
> > +#define f(width, name) xf(width, name, 0)
> > +
> > +#define rac_f(width, name) rac_unsigned_subs(width, name, 0) #define
> > +rac_s(width, name) rac_signed_subs(width, name, 0) #define
> > +rac_fs(width, name, subs, ...) \
> > +    rac_unsigned_subs(width, name, subs, __VA_ARGS__) #define
> > +rac_ss(width, name, subs, ...) \
> > +    rac_signed_subs(width, name, subs, __VA_ARGS__)
> > +
> > +#define READ
> > +#define READWRITE read
> > +#define RWContext GetBitContext
> > +
> > +#define xf(width, name, subs, ...) \
> > +    do { \
> > +        uint32_t value; \
> > +        CHECK(cbs_vp8_read_unsigned(ctx, rw, width, #name, \
> > +                                    SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> > +        current->name = value; \
> > +    } while (0)
> > +
> > +#define fixed(width, name, value) \
> > +    do { \
> > +        av_unused uint32_t fixed_value; \
> > +        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, 0, &fixed_value, \
> > +                                   value, value)); \
> > +    } while (0)
> > +
> > +#define rac_unsigned_subs(width, name, subs, ...) \
> > +    do { \
> > +        uint32_t value; \
> > +        CHECK(cbs_vp8_rac_read_unsigned( \
> > +            ctx, rac, width, #name, SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> > +        current->name = value; \
> > +    } while (0)
> > +
> > +#define rac_signed_subs(width, name, subs, ...) \
> > +    do { \
> > +        int32_t value; \
> > +        CHECK(cbs_vp8_rac_read_signed(ctx, rac, width, #name, \
> > +                                      SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> > +        current->name = value; \
> > +    } while (0)
> > +
> > +#include "cbs_vp8_syntax_template.c"
> > +
> > +static int cbs_vp8_split_fragment(CodedBitstreamContext *ctx,
> > +                                  CodedBitstreamFragment *frag, int
> > +header) {
> > +    int err;
> > +
> > +    if (frag->data_size == 0)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    err = ff_cbs_append_unit_data(frag, 0, frag->data, frag->data_size,
> > +                                  frag->data_ref);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    return 0;
> > +}
> > +
> > +static int cbs_vp8_read_unit(CodedBitstreamContext *ctx,
> > +                             CodedBitstreamUnit *unit) {
> > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > +    VP8RawFrame *frame;
> > +    GetBitContext gbc;
> > +    CBSVP8RangeCoder rac;
> > +    int err, pos;
> > +
> > +    err = ff_cbs_alloc_unit_content(ctx, unit);
> > +    if (err < 0)
> > +        return err;
> > +    frame = unit->content;
> > +
> > +    // Create GetBitContext for uncompressed header.
> > +    err = init_get_bits8_le(&gbc, unit->data, 8 * unit->data_size);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    err = cbs_vp8_read_uncompressed_header(ctx, &gbc, frame);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    pos = get_bits_count(&gbc);
> > +    av_assert0(pos % 8 == 0);
> > +    pos /= 8;
> > +    av_assert0(pos <= unit->data_size);
> > +
> > +    if (pos == unit->data_size)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    vp8->uncompressed_header_size = pos;
> > +
> > +    // Create range decoder for compressed header.
> > +    err = cbs_vp8_init_range_decoder(
> > +        &rac, unit->data + pos, frame->header.first_partition_length_in_bytes);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    err = cbs_vp8_read_compressed_header(ctx, &rac, frame);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    pos = cbs_vp8_rac_get_bits_count(&rac);
> > +    if (pos > rac.buffer_size * 8)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    frame->data_ref = av_buffer_ref(unit->data_ref);
> > +    if (!frame->data_ref)
> > +        return AVERROR(ENOMEM);
> > +
> > +    frame->data      = unit->data + pos;
> > +    frame->data_size = unit->data_size - pos;
> > +
> > +    return 0;
> > +}
> > +
> > +static int cbs_vp8_write_unit(CodedBitstreamContext *ctx,
> > +                              CodedBitstreamUnit *unit, PutBitContext
> > +*pbc) {
> > +    return AVERROR_PATCHWELCOME;
> > +}
> > +
> > +static int cbs_vp8_assemble_fragment(CodedBitstreamContext *ctx,
> > +                                     CodedBitstreamFragment *frag) {
> > +    return AVERROR_PATCHWELCOME;
> > +}
> > +
> > +static void cbs_vp8_flush(CodedBitstreamContext *ctx) {
> > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > +
> > +    vp8->uncompressed_header_size = 0; }
> > +
> > +static const CodedBitstreamUnitTypeDescriptor cbs_vp8_unit_types[] = {
> > +    CBS_UNIT_TYPE_INTERNAL_REF(0, VP8RawFrame, data),
> > +    CBS_UNIT_TYPE_END_OF_LIST};
> > +
> > +const CodedBitstreamType ff_cbs_type_vp8 = {
> > +    .codec_id          = AV_CODEC_ID_VP8,
> > +
> > +    .priv_data_size    = sizeof(CodedBitstreamVP8Context),
> > +
> > +    .unit_types        = cbs_vp8_unit_types,
> > +
> > +    .split_fragment    = &cbs_vp8_split_fragment,
> > +    .read_unit         = &cbs_vp8_read_unit,
> > +    .write_unit        = &cbs_vp8_write_unit,
> > +
> > +    .flush             = &cbs_vp8_flush,
> > +
> > +    .assemble_fragment = &cbs_vp8_assemble_fragment, };
> > diff --git a/libavcodec/cbs_vp8.h b/libavcodec/cbs_vp8.h new file mode
> > 100644 index 0000000000..0bfa465ff7
> > --- /dev/null
> > +++ b/libavcodec/cbs_vp8.h
> > @@ -0,0 +1,112 @@
> > +/*
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > +02110-1301 USA  */
> > +
> > +#ifndef AVCODEC_CBS_VP8_H
> > +#define AVCODEC_CBS_VP8_H
> > +
> > +#include <stddef.h>
> > +#include <stdint.h>
> > +
> > +#include "cbs.h"
> > +#include "vpx_rac.h"
> > +
> > +enum {
> > +    VP8_START_CODE_0 = 0x9D,
> > +    VP8_START_CODE_1 = 0x01,
> > +    VP8_START_CODE_2 = 0x2A,
> > +};
> > +
> > +enum {
> > +    VP8_KEY_FRAME     = 0,
> > +    VP8_NON_KEY_FRAME = 1,
> > +};
> > +
> > +typedef struct VP8RawFrameHeader {
> > +    // frame tag
> > +    uint8_t frame_type;
> > +    uint8_t profile;
> > +    uint8_t show_frame;
> > +    uint32_t first_partition_length_in_bytes;
> > +
> > +    uint16_t width;
> > +    uint8_t horizontal_scale;
> > +    uint16_t height;
> > +    uint8_t vertical_scale;
> > +
> > +    // frame header
> > +    uint8_t color_space;
> > +    uint8_t clamping_type;
> > +
> > +    // segmentation
> > +    uint8_t segmentation_enabled;
> > +    uint8_t update_segment_map;
> > +    uint8_t update_segment_feature_data;
> > +    uint8_t segment_feature_mode;
> > +    int8_t segment_update_qp[4];
> > +    int8_t segment_update_loop_filter_level[4];
> > +    uint8_t segment_update_probs[3];
> > +
> > +    // loop filter
> > +    uint8_t loop_filter_type;
> > +    uint8_t loop_filter_level;
> > +    uint8_t loop_filter_sharpness;
> > +    uint8_t mode_ref_lf_delta_enabled;
> > +    int8_t ref_lf_deltas[4];
> > +    int8_t mode_lf_deltas[4];
> > +
> > +    uint8_t log2_token_partitions;
> > +
> > +    // qp
> > +    uint8_t base_qindex;
> > +    int8_t y1dc_delta_q;
> > +    int8_t y2dc_delta_q;
> > +    int8_t y2ac_delta_q;
> > +    int8_t uvdc_delta_q;
> > +    int8_t uvac_delta_q;
> > +
> > +    // ref
> > +    uint8_t refresh_golden_frame;
> > +    uint8_t refresh_alternate_frame;
> > +    uint8_t ref_frame_sign_bias_golden;
> > +    uint8_t ref_frame_sign_bias_alternate;
> > +    uint8_t refresh_last_frame;
> > +
> > +    // token probs
> > +    uint8_t refresh_entropy_probs;
> > +
> > +    uint8_t mb_no_skip_coeff;
> > +    uint8_t prob_skip_false;
> > +
> > +    uint8_t prob_intra;
> > +    uint8_t prob_last;
> > +    uint8_t prob_golden;
> > +} VP8RawFrameHeader;
> > +
> > +typedef struct VP8RawFrame {
> > +    VP8RawFrameHeader header;
> > +
> > +    uint8_t *data;
> > +    AVBufferRef *data_ref;
> > +    size_t data_size;
> > +} VP8RawFrame;
> > +
> > +typedef struct CodedBitstreamVP8Context {
> > +    uint32_t uncompressed_header_size; } CodedBitstreamVP8Context;
> > +
> > +#endif /* AVCODEC_CBS_VP8_H */
> > diff --git a/libavcodec/cbs_vp8_syntax_template.c
> > b/libavcodec/cbs_vp8_syntax_template.c
> > new file mode 100644
> > index 0000000000..5ab034a164
> > --- /dev/null
> > +++ b/libavcodec/cbs_vp8_syntax_template.c
> > @@ -0,0 +1,224 @@
> > +/*
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > +02110-1301 USA  */
> > +
> > +static int FUNC(update_segmentation)(CodedBitstreamContext *ctx,
> > +                                     CBSVP8RangeCoder *rac,
> > +                                     VP8RawFrameHeader *current) {
> > +    rac_f(1, update_segment_map);
> > +    rac_f(1, update_segment_feature_data);
> > +
> > +    if (current->update_segment_feature_data) {
> > +        rac_f(1, segment_feature_mode);
> > +        // quantizer
> > +        for (int i = 0; i < 4; i++)
> > +            if (cbs_vp8_rac_get(rac, 1))
> > +                rac_ss(7, segment_update_qp[i], 1, i);
> > +        // loop filter
> > +        for (int i = 0; i < 4; i++)
> > +            if (cbs_vp8_rac_get(rac, 1))
> > +                rac_ss(6, segment_update_loop_filter_level[i], 1, i);
> > +    }
> > +
> > +    if (current->update_segment_map) {
> > +        for (int i = 0; i < 3; i++)
> > +            if (cbs_vp8_rac_get(rac, 1))
> > +                rac_fs(8, segment_update_probs[i], 1, i);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(mode_ref_lf_deltas)(CodedBitstreamContext *ctx,
> > +                                    CBSVP8RangeCoder *rac,
> > +                                    VP8RawFrameHeader *current) {
> > +    rac_f(1, mode_ref_lf_delta_enabled);
> > +    if (current->mode_ref_lf_delta_enabled) {
> > +        if (cbs_vp8_rac_get(rac, 1)) {
> > +            // ref_lf_deltas
> > +            for (int i = 0; i < 4; i++)
> > +                if (cbs_vp8_rac_get(rac, 1))
> > +                    rac_ss(6, ref_lf_deltas[i], 1, i);
> > +            // mode_lf_deltas
> > +            for (int i = 0; i < 4; i++)
> > +                if (cbs_vp8_rac_get(rac, 1))
> > +                    rac_ss(6, mode_lf_deltas[i], 1, i);
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(quantization_params)(CodedBitstreamContext *ctx,
> > +                                     CBSVP8RangeCoder *rac,
> > +                                     VP8RawFrameHeader *current) {
> > +    rac_f(7, base_qindex);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, y1dc_delta_q);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, y2dc_delta_q);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, y2ac_delta_q);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, uvdc_delta_q);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, uvac_delta_q);
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(update_token_probs)(CodedBitstreamContext *ctx,
> > +                                    CBSVP8RangeCoder *rac,
> > +                                    VP8RawFrameHeader *current) {
> > +    for (int i = 0; i < 4; ++i) {
> > +        for (int j = 0; j < 8; ++j) {
> > +            for (int k = 0; k < 3; ++k) {
> > +                for (int l = 0; l < 11; ++l)
> > +                    if (cbs_vp8_rac_get_prob_branchy(
> > +                            rac, vp8_token_update_probs[i][j][k][l]))
> > +                        cbs_vp8_rac_get(rac, 8);
> > +            }
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(update_mv_probs)(CodedBitstreamContext *ctx,
> > +                                 CBSVP8RangeCoder *rac,
> > +                                 VP8RawFrameHeader *current) {
> > +    for (int i = 0; i < 2; ++i) {
> > +        for (int j = 0; j < 19; ++j)
> > +            if (cbs_vp8_rac_get(rac, 1))
> > +                cbs_vp8_rac_get(rac, 7);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(frame_tag)(CodedBitstreamContext *ctx, RWContext *rw,
> > +                           VP8RawFrameHeader *current) {
> > +    f(1, frame_type);
> > +    f(3, profile);
> > +    f(1, show_frame);
> > +    f(19, first_partition_length_in_bytes);
> > +
> > +    if (current->frame_type == VP8_KEY_FRAME) {
> > +        fixed(8, start_code_0, VP8_START_CODE_0);
> > +        fixed(8, start_code_1, VP8_START_CODE_1);
> > +        fixed(8, start_code_2, VP8_START_CODE_2);
> > +
> > +        f(14, width);
> > +        f(2, horizontal_scale);
> > +        f(14, height);
> > +        f(2, vertical_scale);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(frame_header)(CodedBitstreamContext *ctx,
> > CBSVP8RangeCoder *rac,
> > +                              VP8RawFrameHeader *current) {
> > +    if (current->frame_type == VP8_KEY_FRAME) {
> > +        rac_f(1, color_space);
> > +        rac_f(1, clamping_type);
> > +    }
> > +
> > +    rac_f(1, segmentation_enabled);
> > +    if (current->segmentation_enabled)
> > +        CHECK(FUNC(update_segmentation)(ctx, rac, current));
> > +
> > +    rac_f(1, loop_filter_type);
> > +    rac_f(6, loop_filter_level);
> > +    rac_f(3, loop_filter_sharpness);
> > +
> > +    CHECK(FUNC(mode_ref_lf_deltas)(ctx, rac, current));
> > +
> > +    rac_f(2, log2_token_partitions);
> > +
> > +    CHECK(FUNC(quantization_params)(ctx, rac, current));
> > +
> > +    if (current->frame_type != VP8_KEY_FRAME) {
> > +        rac_f(1, refresh_golden_frame);
> > +        rac_f(1, refresh_alternate_frame);
> > +        if (!current->refresh_golden_frame)
> > +            cbs_vp8_rac_get(rac, 2);
> > +        if (!current->refresh_alternate_frame)
> > +            cbs_vp8_rac_get(rac, 2);
> > +        rac_f(1, ref_frame_sign_bias_golden);
> > +        rac_f(1, ref_frame_sign_bias_alternate);
> > +    }
> > +    rac_f(1, refresh_entropy_probs);
> > +    if (current->frame_type != VP8_KEY_FRAME)
> > +        rac_f(1, refresh_last_frame);
> > +
> > +    CHECK(FUNC(update_token_probs)(ctx, rac, current));
> > +
> > +    rac_f(1, mb_no_skip_coeff);
> > +    if (current->mb_no_skip_coeff)
> > +        rac_f(8, prob_skip_false);
> > +
> > +    if (current->frame_type != VP8_KEY_FRAME) {
> > +        rac_f(8, prob_intra);
> > +        rac_f(8, prob_last);
> > +        rac_f(8, prob_golden);
> > +
> > +        // intra_16x16_prob
> > +        if (cbs_vp8_rac_get(rac, 1))
> > +            for (int i = 0; i < 4; i++)
> > +                cbs_vp8_rac_get(rac, 8);
> > +
> > +        // intra_chroma_prob
> > +        if (cbs_vp8_rac_get(rac, 1))
> > +            for (int i = 0; i < 4; i++)
> > +                cbs_vp8_rac_get(rac, 8);
> > +
> > +        CHECK(FUNC(update_mv_probs)(ctx, rac, current));
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx,
> > RWContext *rw,
> > +                                     VP8RawFrame *current) {
> > +    HEADER("Frame");
> > +
> > +    CHECK(FUNC(frame_tag)(ctx, rw, &current->header));
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(compressed_header)(CodedBitstreamContext *ctx,
> > +                                   CBSVP8RangeCoder *rac, VP8RawFrame
> > +*current) {
> > +    CHECK(FUNC(frame_header)(ctx, rac, &current->header));
> > +
> > +    return 0;
> > +}
> > --
> > 2.25.1
> >
> 
> Friendly ping reviews to take a look.

Ping reviewer to submit, if no additional comments. Thanks,

> 
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Dai, Jianhui J July 31, 2023, 2:14 a.m. UTC | #3
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Dai,
> Jianhui J
> Sent: Tuesday, June 20, 2023 9:42 AM
> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] [PATCH v3] avcodec/cbs_vp8: Add support for VP8
> codec bitstream READ methods
> 
> 
> 
> > -----Original Message-----
> > From: Dai, Jianhui J
> > Sent: Thursday, June 8, 2023 11:18 AM
> > To: FFmpeg development discussions and patches
> > <ffmpeg-devel@ffmpeg.org>
> > Subject: RE: [PATCH v3] avcodec/cbs_vp8: Add support for VP8 codec
> > bitstream READ methods
> >
> >
> >
> > > -----Original Message-----
> > > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > > Dai, Jianhui J
> > > Sent: Tuesday, May 30, 2023 4:00 PM
> > > To: ffmpeg-devel@ffmpeg.org
> > > Subject: [FFmpeg-devel] [PATCH v3] avcodec/cbs_vp8: Add support for
> > > VP8 codec bitstream READ methods
> > >
> > > This commit adds VP8 into cbs supported codec list, and enables the
> > > `trace_headers` bitstream filters to support VP8, besides existing
> > > AV1, H.264,
> > > H.265 and VP9. It can be useful to debug VP8 stream issues.
> > >
> > > Only the READ methods `read_unit` and `split_fragment` are
> > > implemented, the WRITE methods `write_unit` and `assemble_fragment`
> > > return `AVERROR_PATCHWELCOME` error code. It is because the CBS VP8
> > > WRITE is unlikely used by any applications at the moment. The WRITE
> > > methods can be added later if there are real requirments.
> > >
> > > TESTS: ffmpeg -i fate-suite/vp8/frame_size_change.webm -vcodec copy
> > > -bsf:v trace_headers -f null -
> > >
> > > Signed-off-by: Jianhui Dai <jianhui.j.dai@intel.com>
> > > ---
> > >  configure                            |   4 +-
> > >  libavcodec/Makefile                  |   1 +
> > >  libavcodec/cbs.c                     |   6 +
> > >  libavcodec/cbs_internal.h            |   1 +
> > >  libavcodec/cbs_vp8.c                 | 360 +++++++++++++++++++++++++++
> > >  libavcodec/cbs_vp8.h                 | 112 +++++++++
> > >  libavcodec/cbs_vp8_syntax_template.c | 224 +++++++++++++++++
> > >  7 files changed, 707 insertions(+), 1 deletion(-)  create mode
> > > 100644 libavcodec/cbs_vp8.c  create mode 100644 libavcodec/cbs_vp8.h
> > > create mode
> > > 100644 libavcodec/cbs_vp8_syntax_template.c
> > >
> > > diff --git a/configure b/configure
> > > index bb7be67676..b8960d2639 100755
> > > --- a/configure
> > > +++ b/configure
> > > @@ -2432,6 +2432,7 @@ CONFIG_EXTRA="
> > >      cbs_h265
> > >      cbs_jpeg
> > >      cbs_mpeg2
> > > +    cbs_vp8
> > >      cbs_vp9
> > >      deflate_wrapper
> > >      dirac_parse
> > > @@ -2713,6 +2714,7 @@ cbs_h264_select="cbs"
> > >  cbs_h265_select="cbs"
> > >  cbs_jpeg_select="cbs"
> > >  cbs_mpeg2_select="cbs"
> > > +cbs_vp8_select="cbs"
> > >  cbs_vp9_select="cbs"
> > >  dct_select="rdft"
> > >  deflate_wrapper_deps="zlib"
> > > @@ -3284,7 +3286,7 @@ h264_redundant_pps_bsf_select="cbs_h264"
> > >  hevc_metadata_bsf_select="cbs_h265"
> > >  mjpeg2jpeg_bsf_select="jpegtables"
> > >  mpeg2_metadata_bsf_select="cbs_mpeg2"
> > > -trace_headers_bsf_select="cbs"
> > > +trace_headers_bsf_select="cbs cbs_vp8"
> > >  vp9_metadata_bsf_select="cbs_vp9"
> > >
> > >  # external libraries
> > > diff --git a/libavcodec/Makefile b/libavcodec/Makefile index
> > > 3cf4444b7e..1c4f0da1d2 100644
> > > --- a/libavcodec/Makefile
> > > +++ b/libavcodec/Makefile
> > > @@ -78,6 +78,7 @@ OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o
> > > cbs_sei.o h2645_parse.o
> > >  OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o cbs_sei.o
> h2645_parse.o
> > >  OBJS-$(CONFIG_CBS_JPEG)                += cbs_jpeg.o
> > >  OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
> > > +OBJS-$(CONFIG_CBS_VP8)                 += cbs_vp8.o vpx_rac.o
> > >  OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
> > >  OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
> > >  OBJS-$(CONFIG_DCT)                     += dct.o dct32_fixed.o dct32_float.o
> > > diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index
> > > 504197e06d..c77110abb1
> > > 100644
> > > --- a/libavcodec/cbs.c
> > > +++ b/libavcodec/cbs.c
> > > @@ -46,6 +46,9 @@ static const CodedBitstreamType *const
> > > cbs_type_table[] = {  #if CONFIG_CBS_MPEG2
> > >      &ff_cbs_type_mpeg2,
> > >  #endif
> > > +#if CONFIG_CBS_VP8
> > > +    &ff_cbs_type_vp8,
> > > +#endif
> > >  #if CONFIG_CBS_VP9
> > >      &ff_cbs_type_vp9,
> > >  #endif
> > > @@ -67,6 +70,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {
> > > #if
> > > CONFIG_CBS_MPEG2
> > >      AV_CODEC_ID_MPEG2VIDEO,
> > >  #endif
> > > +#if CONFIG_CBS_VP8
> > > +    AV_CODEC_ID_VP8,
> > > +#endif
> > >  #if CONFIG_CBS_VP9
> > >      AV_CODEC_ID_VP9,
> > >  #endif
> > > diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
> > > index
> > > e585c77934..beaf8505d1 100644
> > > --- a/libavcodec/cbs_internal.h
> > > +++ b/libavcodec/cbs_internal.h
> > > @@ -247,6 +247,7 @@ extern const CodedBitstreamType
> > > ff_cbs_type_h264; extern const CodedBitstreamType ff_cbs_type_h265;
> > > extern const CodedBitstreamType ff_cbs_type_jpeg;  extern const
> > > CodedBitstreamType ff_cbs_type_mpeg2;
> > > +extern const CodedBitstreamType ff_cbs_type_vp8;
> > >  extern const CodedBitstreamType ff_cbs_type_vp9;
> > >
> > >
> > > diff --git a/libavcodec/cbs_vp8.c b/libavcodec/cbs_vp8.c new file
> > > mode
> > > 100644 index 0000000000..a890590cd9
> > > --- /dev/null
> > > +++ b/libavcodec/cbs_vp8.c
> > > @@ -0,0 +1,360 @@
> > > +/*
> > > + * This file is part of FFmpeg.
> > > + *
> > > + * FFmpeg is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU Lesser General Public
> > > + * License as published by the Free Software Foundation; either
> > > + * version 2.1 of the License, or (at your option) any later version.
> > > + *
> > > + * FFmpeg is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > +GNU
> > > + * Lesser General Public License for more details.
> > > + *
> > > + * You should have received a copy of the GNU Lesser General Public
> > > + * License along with FFmpeg; if not, write to the Free Software
> > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > > +02110-1301 USA  */
> > > +
> > > +#include "libavutil/avassert.h"
> > > +
> > > +#include "cbs.h"
> > > +#include "cbs_internal.h"
> > > +#include "cbs_vp8.h"
> > > +
> > > +#include "vp8data.h"
> > > +
> > > +// Wrap of VPXRangeCoder to support size check.
> > > +typedef struct CBSVP8RangeCoder {
> > > +    VPXRangeCoder c;
> > > +    const uint8_t *buffer;
> > > +    int buffer_size;
> > > +} CBSVP8RangeCoder;
> > > +
> > > +static int cbs_vp8_read_unsigned(CodedBitstreamContext *ctx,
> > > +GetBitContext
> > > *gbc,
> > > +                                 int width, const char *name,
> > > +                                 const int *subscripts, uint32_t
> > > +*write_to) {
> > > +    int pos;
> > > +    uint32_t value;
> > > +
> > > +    av_assert0(width > 0 && width <= 24);
> > > +
> > > +    if (get_bits_left(gbc) < width) {
> > > +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value:
> > > + bitstream
> > ended.\n");
> > > +        return AVERROR_INVALIDDATA;
> > > +    }
> > > +
> > > +    pos   = get_bits_count(gbc);
> > > +    value = get_bits_le(gbc, width);
> > > +
> > > +    if (ctx->trace_enable) {
> > > +        int i;
> > > +        char bits[33];
> > > +        for (i = 0; i < width; i++)
> > > +            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';
> > > +        bits[i] = 0;
> > > +
> > > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > > +    }
> > > +
> > > +    *write_to = value;
> > > +    return 0;
> > > +}
> > > +
> > > +static int cbs_vp8_init_range_decoder(CBSVP8RangeCoder *rac, const
> > > +uint8_t
> > > *buf,
> > > +                                      int buf_size) {
> > > +    int err;
> > > +
> > > +    err = ff_vpx_init_range_decoder(&rac->c, buf, buf_size);
> > > +    if (err < 0)
> > > +        return err;
> > > +
> > > +    rac->buffer      = buf;
> > > +    rac->buffer_size = buf_size;
> > > +    return 0;
> > > +}
> > > +
> > > +static av_unused int cbs_vp8_rac_get(CBSVP8RangeCoder *rac, int
> > > +width) {
> > > +    int value = 0;
> > > +
> > > +    while (width--)
> > > +        value = (value << 1) | vpx_rac_get_prob(&rac->c, 128);
> > > +
> > > +    return value;
> > > +}
> > > +
> > > +static int cbs_vp8_rac_get_prob_branchy(CBSVP8RangeCoder *rac, int
> > > +prob) {
> > > +    return vpx_rac_get_prob_branchy(&rac->c, prob); }
> > > +
> > > +static int cbs_vp8_rac_get_bits_count(CBSVP8RangeCoder *rac) {
> > > +    int bits_count = (rac->c.buffer - rac->buffer) * 8;
> > > +    bits_count += rac->c.bits;
> > > +    return bits_count;
> > > +}
> > > +
> > > +static int cbs_vp8_rac_read_unsigned(CodedBitstreamContext *ctx,
> > > +                                     CBSVP8RangeCoder *rac, int width,
> > > +                                     const char *name, const int *subscripts,
> > > +                                     uint32_t *write_to) {
> > > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > > +    uint32_t value;
> > > +    int pos;
> > > +
> > > +    av_assert0(width >= 0 && width <= 8);
> > > +
> > > +    if (vpx_rac_is_end(&rac->c)) {
> > > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > > +               "Invalid value at "
> > > +               "%s: bitstream ended.\n",
> > > +               name);
> > > +        return AVERROR_INVALIDDATA;
> > > +    }
> > > +
> > > +    pos = cbs_vp8_rac_get_bits_count(rac);
> > > +    if (pos > rac->buffer_size * 8) {
> > > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > > +               "Invalid value at "
> > > +               "%s: bitstream ended.\n",
> > > +               name);
> > > +        return AVERROR_INVALIDDATA;
> > > +    }
> > > +
> > > +    pos += vp8->uncompressed_header_size * 8;
> > > +
> > > +    value = cbs_vp8_rac_get(rac, width);
> > > +
> > > +    if (ctx->trace_enable) {
> > > +        char bits[33] = "-";
> > > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > > +    }
> > > +
> > > +    *write_to = value;
> > > +    return 0;
> > > +}
> > > +
> > > +static int cbs_vp8_rac_read_signed(CodedBitstreamContext *ctx,
> > > +                                   CBSVP8RangeCoder *rac, int width,
> > > +                                   const char *name, const int *subscripts,
> > > +                                   int32_t *write_to) {
> > > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > > +    int32_t value;
> > > +    int pos;
> > > +
> > > +    av_assert0(width >= 0 && width <= 8);
> > > +
> > > +    if (vpx_rac_is_end(&rac->c)) {
> > > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > > +               "Invalid value at "
> > > +               "%s: bitstream ended.\n",
> > > +               name);
> > > +        return AVERROR_INVALIDDATA;
> > > +    }
> > > +
> > > +    pos = cbs_vp8_rac_get_bits_count(rac);
> > > +    if (pos > rac->buffer_size * 8) {
> > > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > > +               "Invalid value at "
> > > +               "%s: bitstream ended.\n",
> > > +               name);
> > > +        return AVERROR_INVALIDDATA;
> > > +    }
> > > +
> > > +    pos += vp8->uncompressed_header_size * 8;
> > > +
> > > +    value = cbs_vp8_rac_get(rac, width);
> > > +    if (cbs_vp8_rac_get(rac, 1))
> > > +        value = -value;
> > > +
> > > +    if (ctx->trace_enable) {
> > > +        char bits[33] = "-";
> > > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > > +    }
> > > +
> > > +    *write_to = value;
> > > +    return 0;
> > > +}
> > > +
> > > +#define HEADER(name) \
> > > +    do { \
> > > +        ff_cbs_trace_header(ctx, name); \
> > > +    } while (0)
> > > +
> > > +#define CHECK(call) \
> > > +    do { \
> > > +        int err = (call); \
> > > +        if (err < 0) \
> > > +            return err; \
> > > +    } while (0)
> > > +
> > > +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name
> > > #define
> > > +FUNC_VP8(rw, name) FUNC_NAME(rw, vp8, name) #define FUNC(name)
> > > +FUNC_VP8(READWRITE, name)
> > > +
> > > +#define SUBSCRIPTS(subs, ...) \
> > > +    (subs > 0 ? ((int[subs + 1]){subs, __VA_ARGS__}) : NULL)
> > > +
> > > +#define f(width, name) xf(width, name, 0)
> > > +
> > > +#define rac_f(width, name) rac_unsigned_subs(width, name, 0)
> > > +#define rac_s(width, name) rac_signed_subs(width, name, 0) #define
> > > +rac_fs(width, name, subs, ...) \
> > > +    rac_unsigned_subs(width, name, subs, __VA_ARGS__) #define
> > > +rac_ss(width, name, subs, ...) \
> > > +    rac_signed_subs(width, name, subs, __VA_ARGS__)
> > > +
> > > +#define READ
> > > +#define READWRITE read
> > > +#define RWContext GetBitContext
> > > +
> > > +#define xf(width, name, subs, ...) \
> > > +    do { \
> > > +        uint32_t value; \
> > > +        CHECK(cbs_vp8_read_unsigned(ctx, rw, width, #name, \
> > > +                                    SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> > > +        current->name = value; \
> > > +    } while (0)
> > > +
> > > +#define fixed(width, name, value) \
> > > +    do { \
> > > +        av_unused uint32_t fixed_value; \
> > > +        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, 0, &fixed_value, \
> > > +                                   value, value)); \
> > > +    } while (0)
> > > +
> > > +#define rac_unsigned_subs(width, name, subs, ...) \
> > > +    do { \
> > > +        uint32_t value; \
> > > +        CHECK(cbs_vp8_rac_read_unsigned( \
> > > +            ctx, rac, width, #name, SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> > > +        current->name = value; \
> > > +    } while (0)
> > > +
> > > +#define rac_signed_subs(width, name, subs, ...) \
> > > +    do { \
> > > +        int32_t value; \
> > > +        CHECK(cbs_vp8_rac_read_signed(ctx, rac, width, #name, \
> > > +                                      SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> > > +        current->name = value; \
> > > +    } while (0)
> > > +
> > > +#include "cbs_vp8_syntax_template.c"
> > > +
> > > +static int cbs_vp8_split_fragment(CodedBitstreamContext *ctx,
> > > +                                  CodedBitstreamFragment *frag, int
> > > +header) {
> > > +    int err;
> > > +
> > > +    if (frag->data_size == 0)
> > > +        return AVERROR_INVALIDDATA;
> > > +
> > > +    err = ff_cbs_append_unit_data(frag, 0, frag->data, frag->data_size,
> > > +                                  frag->data_ref);
> > > +    if (err < 0)
> > > +        return err;
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int cbs_vp8_read_unit(CodedBitstreamContext *ctx,
> > > +                             CodedBitstreamUnit *unit) {
> > > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > > +    VP8RawFrame *frame;
> > > +    GetBitContext gbc;
> > > +    CBSVP8RangeCoder rac;
> > > +    int err, pos;
> > > +
> > > +    err = ff_cbs_alloc_unit_content(ctx, unit);
> > > +    if (err < 0)
> > > +        return err;
> > > +    frame = unit->content;
> > > +
> > > +    // Create GetBitContext for uncompressed header.
> > > +    err = init_get_bits8_le(&gbc, unit->data, 8 * unit->data_size);
> > > +    if (err < 0)
> > > +        return err;
> > > +
> > > +    err = cbs_vp8_read_uncompressed_header(ctx, &gbc, frame);
> > > +    if (err < 0)
> > > +        return err;
> > > +
> > > +    pos = get_bits_count(&gbc);
> > > +    av_assert0(pos % 8 == 0);
> > > +    pos /= 8;
> > > +    av_assert0(pos <= unit->data_size);
> > > +
> > > +    if (pos == unit->data_size)
> > > +        return AVERROR_INVALIDDATA;
> > > +
> > > +    vp8->uncompressed_header_size = pos;
> > > +
> > > +    // Create range decoder for compressed header.
> > > +    err = cbs_vp8_init_range_decoder(
> > > +        &rac, unit->data + pos, frame->header.first_partition_length_in_bytes);
> > > +    if (err < 0)
> > > +        return err;
> > > +
> > > +    err = cbs_vp8_read_compressed_header(ctx, &rac, frame);
> > > +    if (err < 0)
> > > +        return err;
> > > +
> > > +    pos = cbs_vp8_rac_get_bits_count(&rac);
> > > +    if (pos > rac.buffer_size * 8)
> > > +        return AVERROR_INVALIDDATA;
> > > +
> > > +    frame->data_ref = av_buffer_ref(unit->data_ref);
> > > +    if (!frame->data_ref)
> > > +        return AVERROR(ENOMEM);
> > > +
> > > +    frame->data      = unit->data + pos;
> > > +    frame->data_size = unit->data_size - pos;
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int cbs_vp8_write_unit(CodedBitstreamContext *ctx,
> > > +                              CodedBitstreamUnit *unit,
> > > +PutBitContext
> > > +*pbc) {
> > > +    return AVERROR_PATCHWELCOME;
> > > +}
> > > +
> > > +static int cbs_vp8_assemble_fragment(CodedBitstreamContext *ctx,
> > > +                                     CodedBitstreamFragment *frag) {
> > > +    return AVERROR_PATCHWELCOME;
> > > +}
> > > +
> > > +static void cbs_vp8_flush(CodedBitstreamContext *ctx) {
> > > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > > +
> > > +    vp8->uncompressed_header_size = 0; }
> > > +
> > > +static const CodedBitstreamUnitTypeDescriptor cbs_vp8_unit_types[] = {
> > > +    CBS_UNIT_TYPE_INTERNAL_REF(0, VP8RawFrame, data),
> > > +    CBS_UNIT_TYPE_END_OF_LIST};
> > > +
> > > +const CodedBitstreamType ff_cbs_type_vp8 = {
> > > +    .codec_id          = AV_CODEC_ID_VP8,
> > > +
> > > +    .priv_data_size    = sizeof(CodedBitstreamVP8Context),
> > > +
> > > +    .unit_types        = cbs_vp8_unit_types,
> > > +
> > > +    .split_fragment    = &cbs_vp8_split_fragment,
> > > +    .read_unit         = &cbs_vp8_read_unit,
> > > +    .write_unit        = &cbs_vp8_write_unit,
> > > +
> > > +    .flush             = &cbs_vp8_flush,
> > > +
> > > +    .assemble_fragment = &cbs_vp8_assemble_fragment, };
> > > diff --git a/libavcodec/cbs_vp8.h b/libavcodec/cbs_vp8.h new file
> > > mode
> > > 100644 index 0000000000..0bfa465ff7
> > > --- /dev/null
> > > +++ b/libavcodec/cbs_vp8.h
> > > @@ -0,0 +1,112 @@
> > > +/*
> > > + * This file is part of FFmpeg.
> > > + *
> > > + * FFmpeg is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU Lesser General Public
> > > + * License as published by the Free Software Foundation; either
> > > + * version 2.1 of the License, or (at your option) any later version.
> > > + *
> > > + * FFmpeg is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > +GNU
> > > + * Lesser General Public License for more details.
> > > + *
> > > + * You should have received a copy of the GNU Lesser General Public
> > > + * License along with FFmpeg; if not, write to the Free Software
> > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > > +02110-1301 USA  */
> > > +
> > > +#ifndef AVCODEC_CBS_VP8_H
> > > +#define AVCODEC_CBS_VP8_H
> > > +
> > > +#include <stddef.h>
> > > +#include <stdint.h>
> > > +
> > > +#include "cbs.h"
> > > +#include "vpx_rac.h"
> > > +
> > > +enum {
> > > +    VP8_START_CODE_0 = 0x9D,
> > > +    VP8_START_CODE_1 = 0x01,
> > > +    VP8_START_CODE_2 = 0x2A,
> > > +};
> > > +
> > > +enum {
> > > +    VP8_KEY_FRAME     = 0,
> > > +    VP8_NON_KEY_FRAME = 1,
> > > +};
> > > +
> > > +typedef struct VP8RawFrameHeader {
> > > +    // frame tag
> > > +    uint8_t frame_type;
> > > +    uint8_t profile;
> > > +    uint8_t show_frame;
> > > +    uint32_t first_partition_length_in_bytes;
> > > +
> > > +    uint16_t width;
> > > +    uint8_t horizontal_scale;
> > > +    uint16_t height;
> > > +    uint8_t vertical_scale;
> > > +
> > > +    // frame header
> > > +    uint8_t color_space;
> > > +    uint8_t clamping_type;
> > > +
> > > +    // segmentation
> > > +    uint8_t segmentation_enabled;
> > > +    uint8_t update_segment_map;
> > > +    uint8_t update_segment_feature_data;
> > > +    uint8_t segment_feature_mode;
> > > +    int8_t segment_update_qp[4];
> > > +    int8_t segment_update_loop_filter_level[4];
> > > +    uint8_t segment_update_probs[3];
> > > +
> > > +    // loop filter
> > > +    uint8_t loop_filter_type;
> > > +    uint8_t loop_filter_level;
> > > +    uint8_t loop_filter_sharpness;
> > > +    uint8_t mode_ref_lf_delta_enabled;
> > > +    int8_t ref_lf_deltas[4];
> > > +    int8_t mode_lf_deltas[4];
> > > +
> > > +    uint8_t log2_token_partitions;
> > > +
> > > +    // qp
> > > +    uint8_t base_qindex;
> > > +    int8_t y1dc_delta_q;
> > > +    int8_t y2dc_delta_q;
> > > +    int8_t y2ac_delta_q;
> > > +    int8_t uvdc_delta_q;
> > > +    int8_t uvac_delta_q;
> > > +
> > > +    // ref
> > > +    uint8_t refresh_golden_frame;
> > > +    uint8_t refresh_alternate_frame;
> > > +    uint8_t ref_frame_sign_bias_golden;
> > > +    uint8_t ref_frame_sign_bias_alternate;
> > > +    uint8_t refresh_last_frame;
> > > +
> > > +    // token probs
> > > +    uint8_t refresh_entropy_probs;
> > > +
> > > +    uint8_t mb_no_skip_coeff;
> > > +    uint8_t prob_skip_false;
> > > +
> > > +    uint8_t prob_intra;
> > > +    uint8_t prob_last;
> > > +    uint8_t prob_golden;
> > > +} VP8RawFrameHeader;
> > > +
> > > +typedef struct VP8RawFrame {
> > > +    VP8RawFrameHeader header;
> > > +
> > > +    uint8_t *data;
> > > +    AVBufferRef *data_ref;
> > > +    size_t data_size;
> > > +} VP8RawFrame;
> > > +
> > > +typedef struct CodedBitstreamVP8Context {
> > > +    uint32_t uncompressed_header_size; } CodedBitstreamVP8Context;
> > > +
> > > +#endif /* AVCODEC_CBS_VP8_H */
> > > diff --git a/libavcodec/cbs_vp8_syntax_template.c
> > > b/libavcodec/cbs_vp8_syntax_template.c
> > > new file mode 100644
> > > index 0000000000..5ab034a164
> > > --- /dev/null
> > > +++ b/libavcodec/cbs_vp8_syntax_template.c
> > > @@ -0,0 +1,224 @@
> > > +/*
> > > + * This file is part of FFmpeg.
> > > + *
> > > + * FFmpeg is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU Lesser General Public
> > > + * License as published by the Free Software Foundation; either
> > > + * version 2.1 of the License, or (at your option) any later version.
> > > + *
> > > + * FFmpeg is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > +GNU
> > > + * Lesser General Public License for more details.
> > > + *
> > > + * You should have received a copy of the GNU Lesser General Public
> > > + * License along with FFmpeg; if not, write to the Free Software
> > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > > +02110-1301 USA  */
> > > +
> > > +static int FUNC(update_segmentation)(CodedBitstreamContext *ctx,
> > > +                                     CBSVP8RangeCoder *rac,
> > > +                                     VP8RawFrameHeader *current) {
> > > +    rac_f(1, update_segment_map);
> > > +    rac_f(1, update_segment_feature_data);
> > > +
> > > +    if (current->update_segment_feature_data) {
> > > +        rac_f(1, segment_feature_mode);
> > > +        // quantizer
> > > +        for (int i = 0; i < 4; i++)
> > > +            if (cbs_vp8_rac_get(rac, 1))
> > > +                rac_ss(7, segment_update_qp[i], 1, i);
> > > +        // loop filter
> > > +        for (int i = 0; i < 4; i++)
> > > +            if (cbs_vp8_rac_get(rac, 1))
> > > +                rac_ss(6, segment_update_loop_filter_level[i], 1, i);
> > > +    }
> > > +
> > > +    if (current->update_segment_map) {
> > > +        for (int i = 0; i < 3; i++)
> > > +            if (cbs_vp8_rac_get(rac, 1))
> > > +                rac_fs(8, segment_update_probs[i], 1, i);
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int FUNC(mode_ref_lf_deltas)(CodedBitstreamContext *ctx,
> > > +                                    CBSVP8RangeCoder *rac,
> > > +                                    VP8RawFrameHeader *current) {
> > > +    rac_f(1, mode_ref_lf_delta_enabled);
> > > +    if (current->mode_ref_lf_delta_enabled) {
> > > +        if (cbs_vp8_rac_get(rac, 1)) {
> > > +            // ref_lf_deltas
> > > +            for (int i = 0; i < 4; i++)
> > > +                if (cbs_vp8_rac_get(rac, 1))
> > > +                    rac_ss(6, ref_lf_deltas[i], 1, i);
> > > +            // mode_lf_deltas
> > > +            for (int i = 0; i < 4; i++)
> > > +                if (cbs_vp8_rac_get(rac, 1))
> > > +                    rac_ss(6, mode_lf_deltas[i], 1, i);
> > > +        }
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int FUNC(quantization_params)(CodedBitstreamContext *ctx,
> > > +                                     CBSVP8RangeCoder *rac,
> > > +                                     VP8RawFrameHeader *current) {
> > > +    rac_f(7, base_qindex);
> > > +
> > > +    if (cbs_vp8_rac_get(rac, 1))
> > > +        rac_s(4, y1dc_delta_q);
> > > +
> > > +    if (cbs_vp8_rac_get(rac, 1))
> > > +        rac_s(4, y2dc_delta_q);
> > > +
> > > +    if (cbs_vp8_rac_get(rac, 1))
> > > +        rac_s(4, y2ac_delta_q);
> > > +
> > > +    if (cbs_vp8_rac_get(rac, 1))
> > > +        rac_s(4, uvdc_delta_q);
> > > +
> > > +    if (cbs_vp8_rac_get(rac, 1))
> > > +        rac_s(4, uvac_delta_q);
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int FUNC(update_token_probs)(CodedBitstreamContext *ctx,
> > > +                                    CBSVP8RangeCoder *rac,
> > > +                                    VP8RawFrameHeader *current) {
> > > +    for (int i = 0; i < 4; ++i) {
> > > +        for (int j = 0; j < 8; ++j) {
> > > +            for (int k = 0; k < 3; ++k) {
> > > +                for (int l = 0; l < 11; ++l)
> > > +                    if (cbs_vp8_rac_get_prob_branchy(
> > > +                            rac, vp8_token_update_probs[i][j][k][l]))
> > > +                        cbs_vp8_rac_get(rac, 8);
> > > +            }
> > > +        }
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int FUNC(update_mv_probs)(CodedBitstreamContext *ctx,
> > > +                                 CBSVP8RangeCoder *rac,
> > > +                                 VP8RawFrameHeader *current) {
> > > +    for (int i = 0; i < 2; ++i) {
> > > +        for (int j = 0; j < 19; ++j)
> > > +            if (cbs_vp8_rac_get(rac, 1))
> > > +                cbs_vp8_rac_get(rac, 7);
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int FUNC(frame_tag)(CodedBitstreamContext *ctx, RWContext *rw,
> > > +                           VP8RawFrameHeader *current) {
> > > +    f(1, frame_type);
> > > +    f(3, profile);
> > > +    f(1, show_frame);
> > > +    f(19, first_partition_length_in_bytes);
> > > +
> > > +    if (current->frame_type == VP8_KEY_FRAME) {
> > > +        fixed(8, start_code_0, VP8_START_CODE_0);
> > > +        fixed(8, start_code_1, VP8_START_CODE_1);
> > > +        fixed(8, start_code_2, VP8_START_CODE_2);
> > > +
> > > +        f(14, width);
> > > +        f(2, horizontal_scale);
> > > +        f(14, height);
> > > +        f(2, vertical_scale);
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int FUNC(frame_header)(CodedBitstreamContext *ctx,
> > > CBSVP8RangeCoder *rac,
> > > +                              VP8RawFrameHeader *current) {
> > > +    if (current->frame_type == VP8_KEY_FRAME) {
> > > +        rac_f(1, color_space);
> > > +        rac_f(1, clamping_type);
> > > +    }
> > > +
> > > +    rac_f(1, segmentation_enabled);
> > > +    if (current->segmentation_enabled)
> > > +        CHECK(FUNC(update_segmentation)(ctx, rac, current));
> > > +
> > > +    rac_f(1, loop_filter_type);
> > > +    rac_f(6, loop_filter_level);
> > > +    rac_f(3, loop_filter_sharpness);
> > > +
> > > +    CHECK(FUNC(mode_ref_lf_deltas)(ctx, rac, current));
> > > +
> > > +    rac_f(2, log2_token_partitions);
> > > +
> > > +    CHECK(FUNC(quantization_params)(ctx, rac, current));
> > > +
> > > +    if (current->frame_type != VP8_KEY_FRAME) {
> > > +        rac_f(1, refresh_golden_frame);
> > > +        rac_f(1, refresh_alternate_frame);
> > > +        if (!current->refresh_golden_frame)
> > > +            cbs_vp8_rac_get(rac, 2);
> > > +        if (!current->refresh_alternate_frame)
> > > +            cbs_vp8_rac_get(rac, 2);
> > > +        rac_f(1, ref_frame_sign_bias_golden);
> > > +        rac_f(1, ref_frame_sign_bias_alternate);
> > > +    }
> > > +    rac_f(1, refresh_entropy_probs);
> > > +    if (current->frame_type != VP8_KEY_FRAME)
> > > +        rac_f(1, refresh_last_frame);
> > > +
> > > +    CHECK(FUNC(update_token_probs)(ctx, rac, current));
> > > +
> > > +    rac_f(1, mb_no_skip_coeff);
> > > +    if (current->mb_no_skip_coeff)
> > > +        rac_f(8, prob_skip_false);
> > > +
> > > +    if (current->frame_type != VP8_KEY_FRAME) {
> > > +        rac_f(8, prob_intra);
> > > +        rac_f(8, prob_last);
> > > +        rac_f(8, prob_golden);
> > > +
> > > +        // intra_16x16_prob
> > > +        if (cbs_vp8_rac_get(rac, 1))
> > > +            for (int i = 0; i < 4; i++)
> > > +                cbs_vp8_rac_get(rac, 8);
> > > +
> > > +        // intra_chroma_prob
> > > +        if (cbs_vp8_rac_get(rac, 1))
> > > +            for (int i = 0; i < 4; i++)
> > > +                cbs_vp8_rac_get(rac, 8);
> > > +
> > > +        CHECK(FUNC(update_mv_probs)(ctx, rac, current));
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx,
> > > RWContext *rw,
> > > +                                     VP8RawFrame *current) {
> > > +    HEADER("Frame");
> > > +
> > > +    CHECK(FUNC(frame_tag)(ctx, rw, &current->header));
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int FUNC(compressed_header)(CodedBitstreamContext *ctx,
> > > +                                   CBSVP8RangeCoder *rac,
> > > +VP8RawFrame
> > > +*current) {
> > > +    CHECK(FUNC(frame_header)(ctx, rac, &current->header));
> > > +
> > > +    return 0;
> > > +}
> > > --
> > > 2.25.1
> > >
> >
> > Friendly ping reviews to take a look.
> 
> Ping reviewer to submit, if no additional comments. Thanks,

Ping reviewers, thanks.

> 
> >
> > > _______________________________________________
> > > ffmpeg-devel mailing list
> > > ffmpeg-devel@ffmpeg.org
> > > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >
> > > To unsubscribe, visit link above, or email
> > > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with
> subject "unsubscribe".
Xiang, Haihao Aug. 10, 2023, 5:25 a.m. UTC | #4
On Ma, 2023-07-31 at 02:14 +0000, Dai, Jianhui J wrote:
> 
> 
> > -----Original Message-----
> > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Dai,
> > Jianhui J
> > Sent: Tuesday, June 20, 2023 9:42 AM
> > To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> > Subject: Re: [FFmpeg-devel] [PATCH v3] avcodec/cbs_vp8: Add support for VP8
> > codec bitstream READ methods
> > 
> > 
> > 
> > > -----Original Message-----
> > > From: Dai, Jianhui J
> > > Sent: Thursday, June 8, 2023 11:18 AM
> > > To: FFmpeg development discussions and patches
> > > <ffmpeg-devel@ffmpeg.org>
> > > Subject: RE: [PATCH v3] avcodec/cbs_vp8: Add support for VP8 codec
> > > bitstream READ methods
> > > 
> > > 
> > > 
> > > > -----Original Message-----
> > > > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > > > Dai, Jianhui J
> > > > Sent: Tuesday, May 30, 2023 4:00 PM
> > > > To: ffmpeg-devel@ffmpeg.org
> > > > Subject: [FFmpeg-devel] [PATCH v3] avcodec/cbs_vp8: Add support for
> > > > VP8 codec bitstream READ methods
> > > > 
> > > > This commit adds VP8 into cbs supported codec list, and enables the
> > > > `trace_headers` bitstream filters to support VP8, besides existing
> > > > AV1, H.264,
> > > > H.265 and VP9. It can be useful to debug VP8 stream issues.
> > > > 
> > > > Only the READ methods `read_unit` and `split_fragment` are
> > > > implemented, the WRITE methods `write_unit` and `assemble_fragment`
> > > > return `AVERROR_PATCHWELCOME` error code. It is because the CBS VP8
> > > > WRITE is unlikely used by any applications at the moment. The WRITE
> > > > methods can be added later if there are real requirments.
> > > > 
> > > > TESTS: ffmpeg -i fate-suite/vp8/frame_size_change.webm -vcodec copy
> > > > -bsf:v trace_headers -f null -
> > > > 
> > > > Signed-off-by: Jianhui Dai <jianhui.j.dai@intel.com>
> > > > ---
> > > >  configure                            |   4 +-
> > > >  libavcodec/Makefile                  |   1 +
> > > >  libavcodec/cbs.c                     |   6 +
> > > >  libavcodec/cbs_internal.h            |   1 +
> > > >  libavcodec/cbs_vp8.c                 | 360 +++++++++++++++++++++++++++
> > > >  libavcodec/cbs_vp8.h                 | 112 +++++++++
> > > >  libavcodec/cbs_vp8_syntax_template.c | 224 +++++++++++++++++
> > > >  7 files changed, 707 insertions(+), 1 deletion(-)  create mode
> > > > 100644 libavcodec/cbs_vp8.c  create mode 100644 libavcodec/cbs_vp8.h
> > > > create mode
> > > > 100644 libavcodec/cbs_vp8_syntax_template.c
> > > > 
> > > > diff --git a/configure b/configure
> > > > index bb7be67676..b8960d2639 100755
> > > > --- a/configure
> > > > +++ b/configure
> > > > @@ -2432,6 +2432,7 @@ CONFIG_EXTRA="
> > > >      cbs_h265
> > > >      cbs_jpeg
> > > >      cbs_mpeg2
> > > > +    cbs_vp8
> > > >      cbs_vp9
> > > >      deflate_wrapper
> > > >      dirac_parse
> > > > @@ -2713,6 +2714,7 @@ cbs_h264_select="cbs"
> > > >  cbs_h265_select="cbs"
> > > >  cbs_jpeg_select="cbs"
> > > >  cbs_mpeg2_select="cbs"
> > > > +cbs_vp8_select="cbs"
> > > >  cbs_vp9_select="cbs"
> > > >  dct_select="rdft"
> > > >  deflate_wrapper_deps="zlib"
> > > > @@ -3284,7 +3286,7 @@ h264_redundant_pps_bsf_select="cbs_h264"
> > > >  hevc_metadata_bsf_select="cbs_h265"
> > > >  mjpeg2jpeg_bsf_select="jpegtables"
> > > >  mpeg2_metadata_bsf_select="cbs_mpeg2"
> > > > -trace_headers_bsf_select="cbs"
> > > > +trace_headers_bsf_select="cbs cbs_vp8"
> > > >  vp9_metadata_bsf_select="cbs_vp9"
> > > > 
> > > >  # external libraries
> > > > diff --git a/libavcodec/Makefile b/libavcodec/Makefile index
> > > > 3cf4444b7e..1c4f0da1d2 100644
> > > > --- a/libavcodec/Makefile
> > > > +++ b/libavcodec/Makefile
> > > > @@ -78,6 +78,7 @@ OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o
> > > > cbs_sei.o h2645_parse.o
> > > >  OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o cbs_sei.o
> > h2645_parse.o
> > > >  OBJS-$(CONFIG_CBS_JPEG)                += cbs_jpeg.o
> > > >  OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
> > > > +OBJS-$(CONFIG_CBS_VP8)                 += cbs_vp8.o vpx_rac.o
> > > >  OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
> > > >  OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
> > > >  OBJS-$(CONFIG_DCT)                     += dct.o dct32_fixed.o
> > > > dct32_float.o
> > > > diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index
> > > > 504197e06d..c77110abb1
> > > > 100644
> > > > --- a/libavcodec/cbs.c
> > > > +++ b/libavcodec/cbs.c
> > > > @@ -46,6 +46,9 @@ static const CodedBitstreamType *const
> > > > cbs_type_table[] = {  #if CONFIG_CBS_MPEG2
> > > >      &ff_cbs_type_mpeg2,
> > > >  #endif
> > > > +#if CONFIG_CBS_VP8
> > > > +    &ff_cbs_type_vp8,
> > > > +#endif
> > > >  #if CONFIG_CBS_VP9
> > > >      &ff_cbs_type_vp9,
> > > >  #endif
> > > > @@ -67,6 +70,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {
> > > > #if
> > > > CONFIG_CBS_MPEG2
> > > >      AV_CODEC_ID_MPEG2VIDEO,
> > > >  #endif
> > > > +#if CONFIG_CBS_VP8
> > > > +    AV_CODEC_ID_VP8,
> > > > +#endif
> > > >  #if CONFIG_CBS_VP9
> > > >      AV_CODEC_ID_VP9,
> > > >  #endif
> > > > diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
> > > > index
> > > > e585c77934..beaf8505d1 100644
> > > > --- a/libavcodec/cbs_internal.h
> > > > +++ b/libavcodec/cbs_internal.h
> > > > @@ -247,6 +247,7 @@ extern const CodedBitstreamType
> > > > ff_cbs_type_h264; extern const CodedBitstreamType ff_cbs_type_h265;
> > > > extern const CodedBitstreamType ff_cbs_type_jpeg;  extern const
> > > > CodedBitstreamType ff_cbs_type_mpeg2;
> > > > +extern const CodedBitstreamType ff_cbs_type_vp8;
> > > >  extern const CodedBitstreamType ff_cbs_type_vp9;
> > > > 
> > > > 
> > > > diff --git a/libavcodec/cbs_vp8.c b/libavcodec/cbs_vp8.c new file
> > > > mode
> > > > 100644 index 0000000000..a890590cd9
> > > > --- /dev/null
> > > > +++ b/libavcodec/cbs_vp8.c
> > > > @@ -0,0 +1,360 @@
> > > > +/*
> > > > + * This file is part of FFmpeg.
> > > > + *
> > > > + * FFmpeg is free software; you can redistribute it and/or
> > > > + * modify it under the terms of the GNU Lesser General Public
> > > > + * License as published by the Free Software Foundation; either
> > > > + * version 2.1 of the License, or (at your option) any later version.
> > > > + *
> > > > + * FFmpeg is distributed in the hope that it will be useful,
> > > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > > +GNU
> > > > + * Lesser General Public License for more details.
> > > > + *
> > > > + * You should have received a copy of the GNU Lesser General Public
> > > > + * License along with FFmpeg; if not, write to the Free Software
> > > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > > > +02110-1301 USA  */
> > > > +
> > > > +#include "libavutil/avassert.h"
> > > > +
> > > > +#include "cbs.h"
> > > > +#include "cbs_internal.h"
> > > > +#include "cbs_vp8.h"
> > > > +
> > > > +#include "vp8data.h"
> > > > +
> > > > +// Wrap of VPXRangeCoder to support size check.
> > > > +typedef struct CBSVP8RangeCoder {
> > > > +    VPXRangeCoder c;
> > > > +    const uint8_t *buffer;
> > > > +    int buffer_size;
> > > > +} CBSVP8RangeCoder;
> > > > +
> > > > +static int cbs_vp8_read_unsigned(CodedBitstreamContext *ctx,
> > > > +GetBitContext
> > > > *gbc,
> > > > +                                 int width, const char *name,
> > > > +                                 const int *subscripts, uint32_t
> > > > +*write_to) {
> > > > +    int pos;
> > > > +    uint32_t value;
> > > > +
> > > > +    av_assert0(width > 0 && width <= 24);
> > > > +
> > > > +    if (get_bits_left(gbc) < width) {
> > > > +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value:
> > > > + bitstream
> > > ended.\n");
> > > > +        return AVERROR_INVALIDDATA;
> > > > +    }
> > > > +
> > > > +    pos   = get_bits_count(gbc);
> > > > +    value = get_bits_le(gbc, width);
> > > > +
> > > > +    if (ctx->trace_enable) {
> > > > +        int i;
> > > > +        char bits[33];
> > > > +        for (i = 0; i < width; i++)
> > > > +            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';
> > > > +        bits[i] = 0;
> > > > +
> > > > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits,
> > > > value);
> > > > +    }
> > > > +
> > > > +    *write_to = value;
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int cbs_vp8_init_range_decoder(CBSVP8RangeCoder *rac, const
> > > > +uint8_t
> > > > *buf,
> > > > +                                      int buf_size) {
> > > > +    int err;
> > > > +
> > > > +    err = ff_vpx_init_range_decoder(&rac->c, buf, buf_size);
> > > > +    if (err < 0)
> > > > +        return err;
> > > > +
> > > > +    rac->buffer      = buf;
> > > > +    rac->buffer_size = buf_size;
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static av_unused int cbs_vp8_rac_get(CBSVP8RangeCoder *rac, int
> > > > +width) {
> > > > +    int value = 0;
> > > > +
> > > > +    while (width--)
> > > > +        value = (value << 1) | vpx_rac_get_prob(&rac->c, 128);
> > > > +
> > > > +    return value;
> > > > +}
> > > > +
> > > > +static int cbs_vp8_rac_get_prob_branchy(CBSVP8RangeCoder *rac, int
> > > > +prob) {
> > > > +    return vpx_rac_get_prob_branchy(&rac->c, prob); }
> > > > +
> > > > +static int cbs_vp8_rac_get_bits_count(CBSVP8RangeCoder *rac) {
> > > > +    int bits_count = (rac->c.buffer - rac->buffer) * 8;
> > > > +    bits_count += rac->c.bits;
> > > > +    return bits_count;
> > > > +}
> > > > +
> > > > +static int cbs_vp8_rac_read_unsigned(CodedBitstreamContext *ctx,
> > > > +                                     CBSVP8RangeCoder *rac, int width,
> > > > +                                     const char *name, const int
> > > > *subscripts,
> > > > +                                     uint32_t *write_to) {
> > > > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > > > +    uint32_t value;
> > > > +    int pos;
> > > > +
> > > > +    av_assert0(width >= 0 && width <= 8);
> > > > +
> > > > +    if (vpx_rac_is_end(&rac->c)) {
> > > > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > > > +               "Invalid value at "
> > > > +               "%s: bitstream ended.\n",
> > > > +               name);
> > > > +        return AVERROR_INVALIDDATA;
> > > > +    }
> > > > +
> > > > +    pos = cbs_vp8_rac_get_bits_count(rac);
> > > > +    if (pos > rac->buffer_size * 8) {
> > > > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > > > +               "Invalid value at "
> > > > +               "%s: bitstream ended.\n",
> > > > +               name);
> > > > +        return AVERROR_INVALIDDATA;
> > > > +    }
> > > > +
> > > > +    pos += vp8->uncompressed_header_size * 8;
> > > > +
> > > > +    value = cbs_vp8_rac_get(rac, width);
> > > > +
> > > > +    if (ctx->trace_enable) {
> > > > +        char bits[33] = "-";
> > > > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits,
> > > > value);
> > > > +    }
> > > > +
> > > > +    *write_to = value;
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int cbs_vp8_rac_read_signed(CodedBitstreamContext *ctx,
> > > > +                                   CBSVP8RangeCoder *rac, int width,
> > > > +                                   const char *name, const int
> > > > *subscripts,
> > > > +                                   int32_t *write_to) {
> > > > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > > > +    int32_t value;
> > > > +    int pos;
> > > > +
> > > > +    av_assert0(width >= 0 && width <= 8);
> > > > +
> > > > +    if (vpx_rac_is_end(&rac->c)) {
> > > > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > > > +               "Invalid value at "
> > > > +               "%s: bitstream ended.\n",
> > > > +               name);
> > > > +        return AVERROR_INVALIDDATA;
> > > > +    }
> > > > +
> > > > +    pos = cbs_vp8_rac_get_bits_count(rac);
> > > > +    if (pos > rac->buffer_size * 8) {
> > > > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > > > +               "Invalid value at "
> > > > +               "%s: bitstream ended.\n",
> > > > +               name);
> > > > +        return AVERROR_INVALIDDATA;
> > > > +    }
> > > > +
> > > > +    pos += vp8->uncompressed_header_size * 8;
> > > > +
> > > > +    value = cbs_vp8_rac_get(rac, width);
> > > > +    if (cbs_vp8_rac_get(rac, 1))
> > > > +        value = -value;
> > > > +
> > > > +    if (ctx->trace_enable) {
> > > > +        char bits[33] = "-";
> > > > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits,
> > > > value);
> > > > +    }
> > > > +
> > > > +    *write_to = value;
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +#define HEADER(name) \
> > > > +    do { \
> > > > +        ff_cbs_trace_header(ctx, name); \
> > > > +    } while (0)
> > > > +
> > > > +#define CHECK(call) \
> > > > +    do { \
> > > > +        int err = (call); \
> > > > +        if (err < 0) \
> > > > +            return err; \
> > > > +    } while (0)
> > > > +
> > > > +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name
> > > > #define
> > > > +FUNC_VP8(rw, name) FUNC_NAME(rw, vp8, name) #define FUNC(name)
> > > > +FUNC_VP8(READWRITE, name)
> > > > +
> > > > +#define SUBSCRIPTS(subs, ...) \
> > > > +    (subs > 0 ? ((int[subs + 1]){subs, __VA_ARGS__}) : NULL)
> > > > +
> > > > +#define f(width, name) xf(width, name, 0)
> > > > +
> > > > +#define rac_f(width, name) rac_unsigned_subs(width, name, 0)
> > > > +#define rac_s(width, name) rac_signed_subs(width, name, 0) #define
> > > > +rac_fs(width, name, subs, ...) \
> > > > +    rac_unsigned_subs(width, name, subs, __VA_ARGS__) #define
> > > > +rac_ss(width, name, subs, ...) \
> > > > +    rac_signed_subs(width, name, subs, __VA_ARGS__)
> > > > +
> > > > +#define READ
> > > > +#define READWRITE read
> > > > +#define RWContext GetBitContext
> > > > +
> > > > +#define xf(width, name, subs, ...) \
> > > > +    do { \
> > > > +        uint32_t value; \
> > > > +        CHECK(cbs_vp8_read_unsigned(ctx, rw, width, #name, \
> > > > +                                    SUBSCRIPTS(subs, __VA_ARGS__),
> > > > &value)); \
> > > > +        current->name = value; \
> > > > +    } while (0)
> > > > +
> > > > +#define fixed(width, name, value) \
> > > > +    do { \
> > > > +        av_unused uint32_t fixed_value; \
> > > > +        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, 0,
> > > > &fixed_value, \
> > > > +                                   value, value)); \
> > > > +    } while (0)
> > > > +
> > > > +#define rac_unsigned_subs(width, name, subs, ...) \
> > > > +    do { \
> > > > +        uint32_t value; \
> > > > +        CHECK(cbs_vp8_rac_read_unsigned( \
> > > > +            ctx, rac, width, #name, SUBSCRIPTS(subs, __VA_ARGS__),
> > > > &value)); \
> > > > +        current->name = value; \
> > > > +    } while (0)
> > > > +
> > > > +#define rac_signed_subs(width, name, subs, ...) \
> > > > +    do { \
> > > > +        int32_t value; \
> > > > +        CHECK(cbs_vp8_rac_read_signed(ctx, rac, width, #name, \
> > > > +                                      SUBSCRIPTS(subs, __VA_ARGS__),
> > > > &value)); \
> > > > +        current->name = value; \
> > > > +    } while (0)
> > > > +
> > > > +#include "cbs_vp8_syntax_template.c"
> > > > +
> > > > +static int cbs_vp8_split_fragment(CodedBitstreamContext *ctx,
> > > > +                                  CodedBitstreamFragment *frag, int
> > > > +header) {
> > > > +    int err;
> > > > +
> > > > +    if (frag->data_size == 0)
> > > > +        return AVERROR_INVALIDDATA;
> > > > +
> > > > +    err = ff_cbs_append_unit_data(frag, 0, frag->data, frag->data_size,
> > > > +                                  frag->data_ref);
> > > > +    if (err < 0)
> > > > +        return err;
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int cbs_vp8_read_unit(CodedBitstreamContext *ctx,
> > > > +                             CodedBitstreamUnit *unit) {
> > > > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > > > +    VP8RawFrame *frame;
> > > > +    GetBitContext gbc;
> > > > +    CBSVP8RangeCoder rac;
> > > > +    int err, pos;
> > > > +
> > > > +    err = ff_cbs_alloc_unit_content(ctx, unit);
> > > > +    if (err < 0)
> > > > +        return err;
> > > > +    frame = unit->content;
> > > > +
> > > > +    // Create GetBitContext for uncompressed header.
> > > > +    err = init_get_bits8_le(&gbc, unit->data, 8 * unit->data_size);
> > > > +    if (err < 0)
> > > > +        return err;
> > > > +
> > > > +    err = cbs_vp8_read_uncompressed_header(ctx, &gbc, frame);
> > > > +    if (err < 0)
> > > > +        return err;
> > > > +
> > > > +    pos = get_bits_count(&gbc);
> > > > +    av_assert0(pos % 8 == 0);
> > > > +    pos /= 8;
> > > > +    av_assert0(pos <= unit->data_size);
> > > > +
> > > > +    if (pos == unit->data_size)
> > > > +        return AVERROR_INVALIDDATA;
> > > > +
> > > > +    vp8->uncompressed_header_size = pos;
> > > > +
> > > > +    // Create range decoder for compressed header.
> > > > +    err = cbs_vp8_init_range_decoder(
> > > > +        &rac, unit->data + pos, frame-
> > > > >header.first_partition_length_in_bytes);
> > > > +    if (err < 0)
> > > > +        return err;
> > > > +
> > > > +    err = cbs_vp8_read_compressed_header(ctx, &rac, frame);
> > > > +    if (err < 0)
> > > > +        return err;
> > > > +
> > > > +    pos = cbs_vp8_rac_get_bits_count(&rac);
> > > > +    if (pos > rac.buffer_size * 8)
> > > > +        return AVERROR_INVALIDDATA;
> > > > +
> > > > +    frame->data_ref = av_buffer_ref(unit->data_ref);
> > > > +    if (!frame->data_ref)
> > > > +        return AVERROR(ENOMEM);
> > > > +
> > > > +    frame->data      = unit->data + pos;
> > > > +    frame->data_size = unit->data_size - pos;
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int cbs_vp8_write_unit(CodedBitstreamContext *ctx,
> > > > +                              CodedBitstreamUnit *unit,
> > > > +PutBitContext
> > > > +*pbc) {
> > > > +    return AVERROR_PATCHWELCOME;
> > > > +}
> > > > +
> > > > +static int cbs_vp8_assemble_fragment(CodedBitstreamContext *ctx,
> > > > +                                     CodedBitstreamFragment *frag) {
> > > > +    return AVERROR_PATCHWELCOME;
> > > > +}
> > > > +
> > > > +static void cbs_vp8_flush(CodedBitstreamContext *ctx) {
> > > > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > > > +
> > > > +    vp8->uncompressed_header_size = 0; }
> > > > +
> > > > +static const CodedBitstreamUnitTypeDescriptor cbs_vp8_unit_types[] = {
> > > > +    CBS_UNIT_TYPE_INTERNAL_REF(0, VP8RawFrame, data),
> > > > +    CBS_UNIT_TYPE_END_OF_LIST};
> > > > +
> > > > +const CodedBitstreamType ff_cbs_type_vp8 = {
> > > > +    .codec_id          = AV_CODEC_ID_VP8,
> > > > +
> > > > +    .priv_data_size    = sizeof(CodedBitstreamVP8Context),
> > > > +
> > > > +    .unit_types        = cbs_vp8_unit_types,
> > > > +
> > > > +    .split_fragment    = &cbs_vp8_split_fragment,
> > > > +    .read_unit         = &cbs_vp8_read_unit,
> > > > +    .write_unit        = &cbs_vp8_write_unit,
> > > > +
> > > > +    .flush             = &cbs_vp8_flush,
> > > > +
> > > > +    .assemble_fragment = &cbs_vp8_assemble_fragment, };
> > > > diff --git a/libavcodec/cbs_vp8.h b/libavcodec/cbs_vp8.h new file
> > > > mode
> > > > 100644 index 0000000000..0bfa465ff7
> > > > --- /dev/null
> > > > +++ b/libavcodec/cbs_vp8.h
> > > > @@ -0,0 +1,112 @@
> > > > +/*
> > > > + * This file is part of FFmpeg.
> > > > + *
> > > > + * FFmpeg is free software; you can redistribute it and/or
> > > > + * modify it under the terms of the GNU Lesser General Public
> > > > + * License as published by the Free Software Foundation; either
> > > > + * version 2.1 of the License, or (at your option) any later version.
> > > > + *
> > > > + * FFmpeg is distributed in the hope that it will be useful,
> > > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > > +GNU
> > > > + * Lesser General Public License for more details.
> > > > + *
> > > > + * You should have received a copy of the GNU Lesser General Public
> > > > + * License along with FFmpeg; if not, write to the Free Software
> > > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > > > +02110-1301 USA  */
> > > > +
> > > > +#ifndef AVCODEC_CBS_VP8_H
> > > > +#define AVCODEC_CBS_VP8_H
> > > > +
> > > > +#include <stddef.h>
> > > > +#include <stdint.h>
> > > > +
> > > > +#include "cbs.h"
> > > > +#include "vpx_rac.h"
> > > > +
> > > > +enum {
> > > > +    VP8_START_CODE_0 = 0x9D,
> > > > +    VP8_START_CODE_1 = 0x01,
> > > > +    VP8_START_CODE_2 = 0x2A,
> > > > +};
> > > > +
> > > > +enum {
> > > > +    VP8_KEY_FRAME     = 0,
> > > > +    VP8_NON_KEY_FRAME = 1,
> > > > +};
> > > > +
> > > > +typedef struct VP8RawFrameHeader {
> > > > +    // frame tag
> > > > +    uint8_t frame_type;
> > > > +    uint8_t profile;
> > > > +    uint8_t show_frame;
> > > > +    uint32_t first_partition_length_in_bytes;
> > > > +
> > > > +    uint16_t width;
> > > > +    uint8_t horizontal_scale;
> > > > +    uint16_t height;
> > > > +    uint8_t vertical_scale;
> > > > +
> > > > +    // frame header
> > > > +    uint8_t color_space;
> > > > +    uint8_t clamping_type;
> > > > +
> > > > +    // segmentation
> > > > +    uint8_t segmentation_enabled;
> > > > +    uint8_t update_segment_map;
> > > > +    uint8_t update_segment_feature_data;
> > > > +    uint8_t segment_feature_mode;
> > > > +    int8_t segment_update_qp[4];
> > > > +    int8_t segment_update_loop_filter_level[4];
> > > > +    uint8_t segment_update_probs[3];
> > > > +
> > > > +    // loop filter
> > > > +    uint8_t loop_filter_type;
> > > > +    uint8_t loop_filter_level;
> > > > +    uint8_t loop_filter_sharpness;
> > > > +    uint8_t mode_ref_lf_delta_enabled;
> > > > +    int8_t ref_lf_deltas[4];
> > > > +    int8_t mode_lf_deltas[4];
> > > > +
> > > > +    uint8_t log2_token_partitions;
> > > > +
> > > > +    // qp
> > > > +    uint8_t base_qindex;
> > > > +    int8_t y1dc_delta_q;
> > > > +    int8_t y2dc_delta_q;
> > > > +    int8_t y2ac_delta_q;
> > > > +    int8_t uvdc_delta_q;
> > > > +    int8_t uvac_delta_q;
> > > > +
> > > > +    // ref
> > > > +    uint8_t refresh_golden_frame;
> > > > +    uint8_t refresh_alternate_frame;
> > > > +    uint8_t ref_frame_sign_bias_golden;
> > > > +    uint8_t ref_frame_sign_bias_alternate;
> > > > +    uint8_t refresh_last_frame;
> > > > +
> > > > +    // token probs
> > > > +    uint8_t refresh_entropy_probs;
> > > > +
> > > > +    uint8_t mb_no_skip_coeff;
> > > > +    uint8_t prob_skip_false;
> > > > +
> > > > +    uint8_t prob_intra;
> > > > +    uint8_t prob_last;
> > > > +    uint8_t prob_golden;
> > > > +} VP8RawFrameHeader;
> > > > +
> > > > +typedef struct VP8RawFrame {
> > > > +    VP8RawFrameHeader header;
> > > > +
> > > > +    uint8_t *data;
> > > > +    AVBufferRef *data_ref;
> > > > +    size_t data_size;
> > > > +} VP8RawFrame;
> > > > +
> > > > +typedef struct CodedBitstreamVP8Context {
> > > > +    uint32_t uncompressed_header_size; } CodedBitstreamVP8Context;
> > > > +
> > > > +#endif /* AVCODEC_CBS_VP8_H */
> > > > diff --git a/libavcodec/cbs_vp8_syntax_template.c
> > > > b/libavcodec/cbs_vp8_syntax_template.c
> > > > new file mode 100644
> > > > index 0000000000..5ab034a164
> > > > --- /dev/null
> > > > +++ b/libavcodec/cbs_vp8_syntax_template.c
> > > > @@ -0,0 +1,224 @@
> > > > +/*
> > > > + * This file is part of FFmpeg.
> > > > + *
> > > > + * FFmpeg is free software; you can redistribute it and/or
> > > > + * modify it under the terms of the GNU Lesser General Public
> > > > + * License as published by the Free Software Foundation; either
> > > > + * version 2.1 of the License, or (at your option) any later version.
> > > > + *
> > > > + * FFmpeg is distributed in the hope that it will be useful,
> > > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > > +GNU
> > > > + * Lesser General Public License for more details.
> > > > + *
> > > > + * You should have received a copy of the GNU Lesser General Public
> > > > + * License along with FFmpeg; if not, write to the Free Software
> > > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > > > +02110-1301 USA  */
> > > > +
> > > > +static int FUNC(update_segmentation)(CodedBitstreamContext *ctx,
> > > > +                                     CBSVP8RangeCoder *rac,
> > > > +                                     VP8RawFrameHeader *current) {
> > > > +    rac_f(1, update_segment_map);
> > > > +    rac_f(1, update_segment_feature_data);
> > > > +
> > > > +    if (current->update_segment_feature_data) {
> > > > +        rac_f(1, segment_feature_mode);
> > > > +        // quantizer
> > > > +        for (int i = 0; i < 4; i++)
> > > > +            if (cbs_vp8_rac_get(rac, 1))
> > > > +                rac_ss(7, segment_update_qp[i], 1, i);
> > > > +        // loop filter
> > > > +        for (int i = 0; i < 4; i++)
> > > > +            if (cbs_vp8_rac_get(rac, 1))
> > > > +                rac_ss(6, segment_update_loop_filter_level[i], 1, i);
> > > > +    }
> > > > +
> > > > +    if (current->update_segment_map) {
> > > > +        for (int i = 0; i < 3; i++)
> > > > +            if (cbs_vp8_rac_get(rac, 1))
> > > > +                rac_fs(8, segment_update_probs[i], 1, i);
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int FUNC(mode_ref_lf_deltas)(CodedBitstreamContext *ctx,
> > > > +                                    CBSVP8RangeCoder *rac,
> > > > +                                    VP8RawFrameHeader *current) {
> > > > +    rac_f(1, mode_ref_lf_delta_enabled);
> > > > +    if (current->mode_ref_lf_delta_enabled) {
> > > > +        if (cbs_vp8_rac_get(rac, 1)) {
> > > > +            // ref_lf_deltas
> > > > +            for (int i = 0; i < 4; i++)
> > > > +                if (cbs_vp8_rac_get(rac, 1))
> > > > +                    rac_ss(6, ref_lf_deltas[i], 1, i);
> > > > +            // mode_lf_deltas
> > > > +            for (int i = 0; i < 4; i++)
> > > > +                if (cbs_vp8_rac_get(rac, 1))
> > > > +                    rac_ss(6, mode_lf_deltas[i], 1, i);
> > > > +        }
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int FUNC(quantization_params)(CodedBitstreamContext *ctx,
> > > > +                                     CBSVP8RangeCoder *rac,
> > > > +                                     VP8RawFrameHeader *current) {
> > > > +    rac_f(7, base_qindex);
> > > > +
> > > > +    if (cbs_vp8_rac_get(rac, 1))
> > > > +        rac_s(4, y1dc_delta_q);
> > > > +
> > > > +    if (cbs_vp8_rac_get(rac, 1))
> > > > +        rac_s(4, y2dc_delta_q);
> > > > +
> > > > +    if (cbs_vp8_rac_get(rac, 1))
> > > > +        rac_s(4, y2ac_delta_q);
> > > > +
> > > > +    if (cbs_vp8_rac_get(rac, 1))
> > > > +        rac_s(4, uvdc_delta_q);
> > > > +
> > > > +    if (cbs_vp8_rac_get(rac, 1))
> > > > +        rac_s(4, uvac_delta_q);
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int FUNC(update_token_probs)(CodedBitstreamContext *ctx,
> > > > +                                    CBSVP8RangeCoder *rac,
> > > > +                                    VP8RawFrameHeader *current) {
> > > > +    for (int i = 0; i < 4; ++i) {
> > > > +        for (int j = 0; j < 8; ++j) {
> > > > +            for (int k = 0; k < 3; ++k) {
> > > > +                for (int l = 0; l < 11; ++l)
> > > > +                    if (cbs_vp8_rac_get_prob_branchy(
> > > > +                            rac, vp8_token_update_probs[i][j][k][l]))
> > > > +                        cbs_vp8_rac_get(rac, 8);
> > > > +            }
> > > > +        }
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int FUNC(update_mv_probs)(CodedBitstreamContext *ctx,
> > > > +                                 CBSVP8RangeCoder *rac,
> > > > +                                 VP8RawFrameHeader *current) {
> > > > +    for (int i = 0; i < 2; ++i) {
> > > > +        for (int j = 0; j < 19; ++j)
> > > > +            if (cbs_vp8_rac_get(rac, 1))
> > > > +                cbs_vp8_rac_get(rac, 7);
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int FUNC(frame_tag)(CodedBitstreamContext *ctx, RWContext *rw,
> > > > +                           VP8RawFrameHeader *current) {
> > > > +    f(1, frame_type);
> > > > +    f(3, profile);
> > > > +    f(1, show_frame);
> > > > +    f(19, first_partition_length_in_bytes);
> > > > +
> > > > +    if (current->frame_type == VP8_KEY_FRAME) {
> > > > +        fixed(8, start_code_0, VP8_START_CODE_0);
> > > > +        fixed(8, start_code_1, VP8_START_CODE_1);
> > > > +        fixed(8, start_code_2, VP8_START_CODE_2);
> > > > +
> > > > +        f(14, width);
> > > > +        f(2, horizontal_scale);
> > > > +        f(14, height);
> > > > +        f(2, vertical_scale);
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int FUNC(frame_header)(CodedBitstreamContext *ctx,
> > > > CBSVP8RangeCoder *rac,
> > > > +                              VP8RawFrameHeader *current) {
> > > > +    if (current->frame_type == VP8_KEY_FRAME) {
> > > > +        rac_f(1, color_space);
> > > > +        rac_f(1, clamping_type);
> > > > +    }
> > > > +
> > > > +    rac_f(1, segmentation_enabled);
> > > > +    if (current->segmentation_enabled)
> > > > +        CHECK(FUNC(update_segmentation)(ctx, rac, current));
> > > > +
> > > > +    rac_f(1, loop_filter_type);
> > > > +    rac_f(6, loop_filter_level);
> > > > +    rac_f(3, loop_filter_sharpness);
> > > > +
> > > > +    CHECK(FUNC(mode_ref_lf_deltas)(ctx, rac, current));
> > > > +
> > > > +    rac_f(2, log2_token_partitions);
> > > > +
> > > > +    CHECK(FUNC(quantization_params)(ctx, rac, current));
> > > > +
> > > > +    if (current->frame_type != VP8_KEY_FRAME) {
> > > > +        rac_f(1, refresh_golden_frame);
> > > > +        rac_f(1, refresh_alternate_frame);
> > > > +        if (!current->refresh_golden_frame)
> > > > +            cbs_vp8_rac_get(rac, 2);
> > > > +        if (!current->refresh_alternate_frame)
> > > > +            cbs_vp8_rac_get(rac, 2);
> > > > +        rac_f(1, ref_frame_sign_bias_golden);
> > > > +        rac_f(1, ref_frame_sign_bias_alternate);
> > > > +    }
> > > > +    rac_f(1, refresh_entropy_probs);
> > > > +    if (current->frame_type != VP8_KEY_FRAME)
> > > > +        rac_f(1, refresh_last_frame);
> > > > +
> > > > +    CHECK(FUNC(update_token_probs)(ctx, rac, current));
> > > > +
> > > > +    rac_f(1, mb_no_skip_coeff);
> > > > +    if (current->mb_no_skip_coeff)
> > > > +        rac_f(8, prob_skip_false);
> > > > +
> > > > +    if (current->frame_type != VP8_KEY_FRAME) {
> > > > +        rac_f(8, prob_intra);
> > > > +        rac_f(8, prob_last);
> > > > +        rac_f(8, prob_golden);
> > > > +
> > > > +        // intra_16x16_prob
> > > > +        if (cbs_vp8_rac_get(rac, 1))
> > > > +            for (int i = 0; i < 4; i++)
> > > > +                cbs_vp8_rac_get(rac, 8);
> > > > +
> > > > +        // intra_chroma_prob
> > > > +        if (cbs_vp8_rac_get(rac, 1))
> > > > +            for (int i = 0; i < 4; i++)
> > > > +                cbs_vp8_rac_get(rac, 8);
> > > > +
> > > > +        CHECK(FUNC(update_mv_probs)(ctx, rac, current));
> > > > +    }
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx,
> > > > RWContext *rw,
> > > > +                                     VP8RawFrame *current) {
> > > > +    HEADER("Frame");
> > > > +
> > > > +    CHECK(FUNC(frame_tag)(ctx, rw, &current->header));
> > > > +
> > > > +    return 0;
> > > > +}
> > > > +
> > > > +static int FUNC(compressed_header)(CodedBitstreamContext *ctx,
> > > > +                                   CBSVP8RangeCoder *rac,
> > > > +VP8RawFrame
> > > > +*current) {
> > > > +    CHECK(FUNC(frame_header)(ctx, rac, &current->header));
> > > > +
> > > > +    return 0;
> > > > +}
> > > > --
> > > > 2.25.1
> > > > 
> > > 
> > > Friendly ping reviews to take a look.
> > 
> > Ping reviewer to submit, if no additional comments. Thanks,
> 
> Ping reviewers, thanks.

LGTM, I'll push this in a few days if there are no comments

Thanks
Haihao

> 
>
Andreas Rheinhardt Aug. 11, 2023, 9:41 p.m. UTC | #5
Dai, Jianhui J:
> This commit adds VP8 into cbs supported codec list, and enables the
> `trace_headers` bitstream filters to support VP8, besides existing AV1,
> H.264, H.265 and VP9. It can be useful to debug VP8 stream issues.
> 
> Only the READ methods `read_unit` and `split_fragment` are implemented,
> the WRITE methods `write_unit` and `assemble_fragment` return
> `AVERROR_PATCHWELCOME` error code. It is because the CBS VP8 WRITE is
> unlikely used by any applications at the moment. The WRITE methods can
> be added later if there are real requirments.
> 
> TESTS: ffmpeg -i fate-suite/vp8/frame_size_change.webm -vcodec copy
> -bsf:v trace_headers -f null -
> 
> Signed-off-by: Jianhui Dai <jianhui.j.dai@intel.com>
> ---
>  configure                            |   4 +-
>  libavcodec/Makefile                  |   1 +
>  libavcodec/cbs.c                     |   6 +
>  libavcodec/cbs_internal.h            |   1 +
>  libavcodec/cbs_vp8.c                 | 360 +++++++++++++++++++++++++++
>  libavcodec/cbs_vp8.h                 | 112 +++++++++
>  libavcodec/cbs_vp8_syntax_template.c | 224 +++++++++++++++++
>  7 files changed, 707 insertions(+), 1 deletion(-)
>  create mode 100644 libavcodec/cbs_vp8.c
>  create mode 100644 libavcodec/cbs_vp8.h
>  create mode 100644 libavcodec/cbs_vp8_syntax_template.c
> 
> diff --git a/configure b/configure
> index bb7be67676..b8960d2639 100755
> --- a/configure
> +++ b/configure
> @@ -2432,6 +2432,7 @@ CONFIG_EXTRA="
>      cbs_h265
>      cbs_jpeg
>      cbs_mpeg2
> +    cbs_vp8
>      cbs_vp9
>      deflate_wrapper
>      dirac_parse
> @@ -2713,6 +2714,7 @@ cbs_h264_select="cbs"
>  cbs_h265_select="cbs"
>  cbs_jpeg_select="cbs"
>  cbs_mpeg2_select="cbs"
> +cbs_vp8_select="cbs"
>  cbs_vp9_select="cbs"
>  dct_select="rdft"
>  deflate_wrapper_deps="zlib"
> @@ -3284,7 +3286,7 @@ h264_redundant_pps_bsf_select="cbs_h264"
>  hevc_metadata_bsf_select="cbs_h265"
>  mjpeg2jpeg_bsf_select="jpegtables"
>  mpeg2_metadata_bsf_select="cbs_mpeg2"
> -trace_headers_bsf_select="cbs"
> +trace_headers_bsf_select="cbs cbs_vp8"
>  vp9_metadata_bsf_select="cbs_vp9"
>  
>  # external libraries
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 3cf4444b7e..1c4f0da1d2 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -78,6 +78,7 @@ OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o cbs_sei.o h2645_parse.o
>  OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o cbs_sei.o h2645_parse.o
>  OBJS-$(CONFIG_CBS_JPEG)                += cbs_jpeg.o
>  OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
> +OBJS-$(CONFIG_CBS_VP8)                 += cbs_vp8.o vpx_rac.o
>  OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
>  OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
>  OBJS-$(CONFIG_DCT)                     += dct.o dct32_fixed.o dct32_float.o
> diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
> index 504197e06d..c77110abb1 100644
> --- a/libavcodec/cbs.c
> +++ b/libavcodec/cbs.c
> @@ -46,6 +46,9 @@ static const CodedBitstreamType *const cbs_type_table[] = {
>  #if CONFIG_CBS_MPEG2
>      &ff_cbs_type_mpeg2,
>  #endif
> +#if CONFIG_CBS_VP8
> +    &ff_cbs_type_vp8,
> +#endif
>  #if CONFIG_CBS_VP9
>      &ff_cbs_type_vp9,
>  #endif
> @@ -67,6 +70,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {
>  #if CONFIG_CBS_MPEG2
>      AV_CODEC_ID_MPEG2VIDEO,
>  #endif
> +#if CONFIG_CBS_VP8
> +    AV_CODEC_ID_VP8,
> +#endif
>  #if CONFIG_CBS_VP9
>      AV_CODEC_ID_VP9,
>  #endif
> diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
> index e585c77934..beaf8505d1 100644
> --- a/libavcodec/cbs_internal.h
> +++ b/libavcodec/cbs_internal.h
> @@ -247,6 +247,7 @@ extern const CodedBitstreamType ff_cbs_type_h264;
>  extern const CodedBitstreamType ff_cbs_type_h265;
>  extern const CodedBitstreamType ff_cbs_type_jpeg;
>  extern const CodedBitstreamType ff_cbs_type_mpeg2;
> +extern const CodedBitstreamType ff_cbs_type_vp8;
>  extern const CodedBitstreamType ff_cbs_type_vp9;
>  
>  
> diff --git a/libavcodec/cbs_vp8.c b/libavcodec/cbs_vp8.c
> new file mode 100644
> index 0000000000..a890590cd9
> --- /dev/null
> +++ b/libavcodec/cbs_vp8.c
> @@ -0,0 +1,360 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "libavutil/avassert.h"
> +
> +#include "cbs.h"
> +#include "cbs_internal.h"
> +#include "cbs_vp8.h"
> +
> +#include "vp8data.h"

This header is currently only included at one place, namely vp8.c and
therefore it uses static tables. You are only using it for
vp8_token_update_probs and the way you are doing it will duplicate this
table in the binary.

> +
> +// Wrap of VPXRangeCoder to support size check.
> +typedef struct CBSVP8RangeCoder {
> +    VPXRangeCoder c;

You are still using the vpx_rac.h API although it is not really suitable
for CBS at all (see below). Also, you are not directly including the
header for it.

> +    const uint8_t *buffer;
> +    int buffer_size;
> +} CBSVP8RangeCoder;
> +
> +static int cbs_vp8_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc,
> +                                 int width, const char *name,
> +                                 const int *subscripts, uint32_t *write_to)
> +{
> +    int pos;
> +    uint32_t value;
> +
> +    av_assert0(width > 0 && width <= 24);
> +
> +    if (get_bits_left(gbc) < width) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value: bitstream ended.\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos   = get_bits_count(gbc);
> +    value = get_bits_le(gbc, width);
> +
> +    if (ctx->trace_enable) {
> +        int i;
> +        char bits[33];
> +        for (i = 0; i < width; i++)
> +            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';
> +        bits[i] = 0;
> +
> +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> +    }
> +
> +    *write_to = value;
> +    return 0;
> +}
> +
> +static int cbs_vp8_init_range_decoder(CBSVP8RangeCoder *rac, const uint8_t *buf,
> +                                      int buf_size)
> +{
> +    int err;
> +
> +    err = ff_vpx_init_range_decoder(&rac->c, buf, buf_size);
> +    if (err < 0)
> +        return err;
> +
> +    rac->buffer      = buf;
> +    rac->buffer_size = buf_size;
> +    return 0;
> +}
> +
> +static av_unused int cbs_vp8_rac_get(CBSVP8RangeCoder *rac, int width)

Why av_unused?

> +{
> +    int value = 0;
> +
> +    while (width--)
> +        value = (value << 1) | vpx_rac_get_prob(&rac->c, 128);

vpx_rac_renorm() seems to perform bounds checking, but it also seems to
be able to overread by one byte (it always reads two bytes even when
only one byte is available) and there is no check for overreads. E.g.
all overreads after prob_golden in frame_header are completely unchecked.

I also do not even know whether the arch-specific variants of these
functions behave completely the same as the C versions.

I recommend you to implement your own versions of these functions, only
based upon the spec and not upon vpx_rac.h.

> +
> +    return value;
> +}
> +
> +static int cbs_vp8_rac_get_prob_branchy(CBSVP8RangeCoder *rac, int prob)
> +{
> +    return vpx_rac_get_prob_branchy(&rac->c, prob);
> +}
> +
> +static int cbs_vp8_rac_get_bits_count(CBSVP8RangeCoder *rac)
> +{
> +    int bits_count = (rac->c.buffer - rac->buffer) * 8;
> +    bits_count += rac->c.bits;
> +    return bits_count;
> +}
> +
> +static int cbs_vp8_rac_read_unsigned(CodedBitstreamContext *ctx,
> +                                     CBSVP8RangeCoder *rac, int width,
> +                                     const char *name, const int *subscripts,
> +                                     uint32_t *write_to)
> +{
> +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> +    uint32_t value;
> +    int pos;
> +
> +    av_assert0(width >= 0 && width <= 8);
> +
> +    if (vpx_rac_is_end(&rac->c)) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> +               "Invalid value at "
> +               "%s: bitstream ended.\n",
> +               name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos = cbs_vp8_rac_get_bits_count(rac);
> +    if (pos > rac->buffer_size * 8) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> +               "Invalid value at "
> +               "%s: bitstream ended.\n",
> +               name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos += vp8->uncompressed_header_size * 8;
> +
> +    value = cbs_vp8_rac_get(rac, width);
> +
> +    if (ctx->trace_enable) {
> +        char bits[33] = "-";
> +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> +    }
> +
> +    *write_to = value;
> +    return 0;
> +}
> +
> +static int cbs_vp8_rac_read_signed(CodedBitstreamContext *ctx,
> +                                   CBSVP8RangeCoder *rac, int width,
> +                                   const char *name, const int *subscripts,
> +                                   int32_t *write_to)
> +{
> +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> +    int32_t value;
> +    int pos;
> +
> +    av_assert0(width >= 0 && width <= 8);
> +
> +    if (vpx_rac_is_end(&rac->c)) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> +               "Invalid value at "
> +               "%s: bitstream ended.\n",
> +               name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos = cbs_vp8_rac_get_bits_count(rac);
> +    if (pos > rac->buffer_size * 8) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> +               "Invalid value at "
> +               "%s: bitstream ended.\n",
> +               name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    pos += vp8->uncompressed_header_size * 8;
> +
> +    value = cbs_vp8_rac_get(rac, width);
> +    if (cbs_vp8_rac_get(rac, 1))
> +        value = -value;
> +
> +    if (ctx->trace_enable) {
> +        char bits[33] = "-";
> +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> +    }
> +
> +    *write_to = value;
> +    return 0;
> +}
> +
> +#define HEADER(name) \
> +    do { \
> +        ff_cbs_trace_header(ctx, name); \
> +    } while (0)
> +
> +#define CHECK(call) \
> +    do { \
> +        int err = (call); \
> +        if (err < 0) \
> +            return err; \
> +    } while (0)
> +
> +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name
> +#define FUNC_VP8(rw, name) FUNC_NAME(rw, vp8, name)
> +#define FUNC(name) FUNC_VP8(READWRITE, name)
> +
> +#define SUBSCRIPTS(subs, ...) \
> +    (subs > 0 ? ((int[subs + 1]){subs, __VA_ARGS__}) : NULL)
> +
> +#define f(width, name) xf(width, name, 0)
> +
> +#define rac_f(width, name) rac_unsigned_subs(width, name, 0)
> +#define rac_s(width, name) rac_signed_subs(width, name, 0)
> +#define rac_fs(width, name, subs, ...) \
> +    rac_unsigned_subs(width, name, subs, __VA_ARGS__)
> +#define rac_ss(width, name, subs, ...) \
> +    rac_signed_subs(width, name, subs, __VA_ARGS__)
> +
> +#define READ
> +#define READWRITE read
> +#define RWContext GetBitContext
> +
> +#define xf(width, name, subs, ...) \
> +    do { \
> +        uint32_t value; \
> +        CHECK(cbs_vp8_read_unsigned(ctx, rw, width, #name, \
> +                                    SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> +        current->name = value; \
> +    } while (0)
> +
> +#define fixed(width, name, value) \
> +    do { \
> +        av_unused uint32_t fixed_value; \
> +        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, 0, &fixed_value, \
> +                                   value, value)); \
> +    } while (0)
> +
> +#define rac_unsigned_subs(width, name, subs, ...) \
> +    do { \
> +        uint32_t value; \
> +        CHECK(cbs_vp8_rac_read_unsigned( \
> +            ctx, rac, width, #name, SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> +        current->name = value; \
> +    } while (0)
> +
> +#define rac_signed_subs(width, name, subs, ...) \
> +    do { \
> +        int32_t value; \
> +        CHECK(cbs_vp8_rac_read_signed(ctx, rac, width, #name, \
> +                                      SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> +        current->name = value; \
> +    } while (0)
> +
> +#include "cbs_vp8_syntax_template.c"
> +
> +static int cbs_vp8_split_fragment(CodedBitstreamContext *ctx,
> +                                  CodedBitstreamFragment *frag, int header)
> +{
> +    int err;
> +
> +    if (frag->data_size == 0)
> +        return AVERROR_INVALIDDATA;
> +
> +    err = ff_cbs_append_unit_data(frag, 0, frag->data, frag->data_size,
> +                                  frag->data_ref);
> +    if (err < 0)
> +        return err;
> +
> +    return 0;
> +}
> +
> +static int cbs_vp8_read_unit(CodedBitstreamContext *ctx,
> +                             CodedBitstreamUnit *unit)
> +{
> +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> +    VP8RawFrame *frame;
> +    GetBitContext gbc;
> +    CBSVP8RangeCoder rac;
> +    int err, pos;
> +
> +    err = ff_cbs_alloc_unit_content(ctx, unit);
> +    if (err < 0)
> +        return err;
> +    frame = unit->content;
> +
> +    // Create GetBitContext for uncompressed header.
> +    err = init_get_bits8_le(&gbc, unit->data, 8 * unit->data_size);
> +    if (err < 0)
> +        return err;
> +
> +    err = cbs_vp8_read_uncompressed_header(ctx, &gbc, frame);
> +    if (err < 0)
> +        return err;
> +
> +    pos = get_bits_count(&gbc);
> +    av_assert0(pos % 8 == 0);
> +    pos /= 8;
> +    av_assert0(pos <= unit->data_size);
> +
> +    if (pos == unit->data_size)
> +        return AVERROR_INVALIDDATA;
> +
> +    vp8->uncompressed_header_size = pos;
> +
> +    // Create range decoder for compressed header.
> +    err = cbs_vp8_init_range_decoder(
> +        &rac, unit->data + pos, frame->header.first_partition_length_in_bytes);
> +    if (err < 0)
> +        return err;
> +
> +    err = cbs_vp8_read_compressed_header(ctx, &rac, frame);
> +    if (err < 0)
> +        return err;
> +
> +    pos = cbs_vp8_rac_get_bits_count(&rac);
> +    if (pos > rac.buffer_size * 8)
> +        return AVERROR_INVALIDDATA;
> +
> +    frame->data_ref = av_buffer_ref(unit->data_ref);
> +    if (!frame->data_ref)
> +        return AVERROR(ENOMEM);
> +
> +    frame->data      = unit->data + pos;
> +    frame->data_size = unit->data_size - pos;
> +
> +    return 0;
> +}
> +
> +static int cbs_vp8_write_unit(CodedBitstreamContext *ctx,
> +                              CodedBitstreamUnit *unit, PutBitContext *pbc)
> +{
> +    return AVERROR_PATCHWELCOME;
> +}
> +
> +static int cbs_vp8_assemble_fragment(CodedBitstreamContext *ctx,
> +                                     CodedBitstreamFragment *frag)
> +{
> +    return AVERROR_PATCHWELCOME;
> +}
> +
> +static void cbs_vp8_flush(CodedBitstreamContext *ctx)
> +{
> +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> +
> +    vp8->uncompressed_header_size = 0;

Why do you reset this at all here? It is reset in every read_unit before
it is read, i.e. its effective lifetime is a part of every read_unit
call. It could actually be passed to every function; it need not be in
the context (and therefore the context need not exist). But even if it
is in the context, it need not be reset.

> +}
> +
> +static const CodedBitstreamUnitTypeDescriptor cbs_vp8_unit_types[] = {
> +    CBS_UNIT_TYPE_INTERNAL_REF(0, VP8RawFrame, data),
> +    CBS_UNIT_TYPE_END_OF_LIST};
> +
> +const CodedBitstreamType ff_cbs_type_vp8 = {
> +    .codec_id          = AV_CODEC_ID_VP8,
> +
> +    .priv_data_size    = sizeof(CodedBitstreamVP8Context),
> +
> +    .unit_types        = cbs_vp8_unit_types,
> +
> +    .split_fragment    = &cbs_vp8_split_fragment,
> +    .read_unit         = &cbs_vp8_read_unit,
> +    .write_unit        = &cbs_vp8_write_unit,
> +
> +    .flush             = &cbs_vp8_flush,
> +
> +    .assemble_fragment = &cbs_vp8_assemble_fragment,
> +};
> diff --git a/libavcodec/cbs_vp8.h b/libavcodec/cbs_vp8.h
> new file mode 100644
> index 0000000000..0bfa465ff7
> --- /dev/null
> +++ b/libavcodec/cbs_vp8.h
> @@ -0,0 +1,112 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVCODEC_CBS_VP8_H
> +#define AVCODEC_CBS_VP8_H
> +
> +#include <stddef.h>
> +#include <stdint.h>
> +
> +#include "cbs.h"
> +#include "vpx_rac.h"
> +
> +enum {
> +    VP8_START_CODE_0 = 0x9D,
> +    VP8_START_CODE_1 = 0x01,
> +    VP8_START_CODE_2 = 0x2A,
> +};
> +
> +enum {
> +    VP8_KEY_FRAME     = 0,
> +    VP8_NON_KEY_FRAME = 1,
> +};
> +
> +typedef struct VP8RawFrameHeader {
> +    // frame tag
> +    uint8_t frame_type;
> +    uint8_t profile;
> +    uint8_t show_frame;
> +    uint32_t first_partition_length_in_bytes;
> +
> +    uint16_t width;
> +    uint8_t horizontal_scale;
> +    uint16_t height;
> +    uint8_t vertical_scale;
> +
> +    // frame header
> +    uint8_t color_space;
> +    uint8_t clamping_type;
> +
> +    // segmentation
> +    uint8_t segmentation_enabled;
> +    uint8_t update_segment_map;
> +    uint8_t update_segment_feature_data;
> +    uint8_t segment_feature_mode;
> +    int8_t segment_update_qp[4];
> +    int8_t segment_update_loop_filter_level[4];
> +    uint8_t segment_update_probs[3];
> +
> +    // loop filter
> +    uint8_t loop_filter_type;
> +    uint8_t loop_filter_level;
> +    uint8_t loop_filter_sharpness;
> +    uint8_t mode_ref_lf_delta_enabled;
> +    int8_t ref_lf_deltas[4];
> +    int8_t mode_lf_deltas[4];
> +
> +    uint8_t log2_token_partitions;
> +
> +    // qp
> +    uint8_t base_qindex;
> +    int8_t y1dc_delta_q;
> +    int8_t y2dc_delta_q;
> +    int8_t y2ac_delta_q;
> +    int8_t uvdc_delta_q;
> +    int8_t uvac_delta_q;
> +
> +    // ref
> +    uint8_t refresh_golden_frame;
> +    uint8_t refresh_alternate_frame;
> +    uint8_t ref_frame_sign_bias_golden;
> +    uint8_t ref_frame_sign_bias_alternate;
> +    uint8_t refresh_last_frame;
> +
> +    // token probs
> +    uint8_t refresh_entropy_probs;
> +
> +    uint8_t mb_no_skip_coeff;
> +    uint8_t prob_skip_false;
> +
> +    uint8_t prob_intra;
> +    uint8_t prob_last;
> +    uint8_t prob_golden;
> +} VP8RawFrameHeader;
> +
> +typedef struct VP8RawFrame {
> +    VP8RawFrameHeader header;
> +
> +    uint8_t *data;
> +    AVBufferRef *data_ref;
> +    size_t data_size;
> +} VP8RawFrame;
> +
> +typedef struct CodedBitstreamVP8Context {
> +    uint32_t uncompressed_header_size;
> +} CodedBitstreamVP8Context;
> +
> +#endif /* AVCODEC_CBS_VP8_H */
> diff --git a/libavcodec/cbs_vp8_syntax_template.c b/libavcodec/cbs_vp8_syntax_template.c
> new file mode 100644
> index 0000000000..5ab034a164
> --- /dev/null
> +++ b/libavcodec/cbs_vp8_syntax_template.c
> @@ -0,0 +1,224 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +static int FUNC(update_segmentation)(CodedBitstreamContext *ctx,
> +                                     CBSVP8RangeCoder *rac,
> +                                     VP8RawFrameHeader *current)
> +{
> +    rac_f(1, update_segment_map);
> +    rac_f(1, update_segment_feature_data);
> +
> +    if (current->update_segment_feature_data) {
> +        rac_f(1, segment_feature_mode);
> +        // quantizer
> +        for (int i = 0; i < 4; i++)
> +            if (cbs_vp8_rac_get(rac, 1))

CBS syntax templates are supposed to be for both reading and writing;
even though the latter is unimplemented here, does not mean that we
should adopt naming that only makes sense for reading. Your naming
should reflect the syntax element and not whether it is reading or writing.

> +                rac_ss(7, segment_update_qp[i], 1, i);
> +        // loop filter
> +        for (int i = 0; i < 4; i++)
> +            if (cbs_vp8_rac_get(rac, 1))
> +                rac_ss(6, segment_update_loop_filter_level[i], 1, i);
> +    }
> +
> +    if (current->update_segment_map) {
> +        for (int i = 0; i < 3; i++)
> +            if (cbs_vp8_rac_get(rac, 1))
> +                rac_fs(8, segment_update_probs[i], 1, i);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(mode_ref_lf_deltas)(CodedBitstreamContext *ctx,
> +                                    CBSVP8RangeCoder *rac,
> +                                    VP8RawFrameHeader *current)
> +{
> +    rac_f(1, mode_ref_lf_delta_enabled);
> +    if (current->mode_ref_lf_delta_enabled) {
> +        if (cbs_vp8_rac_get(rac, 1)) {
> +            // ref_lf_deltas
> +            for (int i = 0; i < 4; i++)
> +                if (cbs_vp8_rac_get(rac, 1))
> +                    rac_ss(6, ref_lf_deltas[i], 1, i);
> +            // mode_lf_deltas
> +            for (int i = 0; i < 4; i++)
> +                if (cbs_vp8_rac_get(rac, 1))
> +                    rac_ss(6, mode_lf_deltas[i], 1, i);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(quantization_params)(CodedBitstreamContext *ctx,
> +                                     CBSVP8RangeCoder *rac,
> +                                     VP8RawFrameHeader *current)
> +{
> +    rac_f(7, base_qindex);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, y1dc_delta_q);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, y2dc_delta_q);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, y2ac_delta_q);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, uvdc_delta_q);
> +
> +    if (cbs_vp8_rac_get(rac, 1))
> +        rac_s(4, uvac_delta_q);
> +
> +    return 0;
> +}
> +
> +static int FUNC(update_token_probs)(CodedBitstreamContext *ctx,
> +                                    CBSVP8RangeCoder *rac,
> +                                    VP8RawFrameHeader *current)
> +{
> +    for (int i = 0; i < 4; ++i) {
> +        for (int j = 0; j < 8; ++j) {
> +            for (int k = 0; k < 3; ++k) {
> +                for (int l = 0; l < 11; ++l)
> +                    if (cbs_vp8_rac_get_prob_branchy(
> +                            rac, vp8_token_update_probs[i][j][k][l]))
> +                        cbs_vp8_rac_get(rac, 8);
> +            }
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(update_mv_probs)(CodedBitstreamContext *ctx,
> +                                 CBSVP8RangeCoder *rac,
> +                                 VP8RawFrameHeader *current)
> +{
> +    for (int i = 0; i < 2; ++i) {
> +        for (int j = 0; j < 19; ++j)
> +            if (cbs_vp8_rac_get(rac, 1))
> +                cbs_vp8_rac_get(rac, 7);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(frame_tag)(CodedBitstreamContext *ctx, RWContext *rw,
> +                           VP8RawFrameHeader *current)
> +{
> +    f(1, frame_type);
> +    f(3, profile);
> +    f(1, show_frame);
> +    f(19, first_partition_length_in_bytes);
> +
> +    if (current->frame_type == VP8_KEY_FRAME) {
> +        fixed(8, start_code_0, VP8_START_CODE_0);
> +        fixed(8, start_code_1, VP8_START_CODE_1);
> +        fixed(8, start_code_2, VP8_START_CODE_2);
> +
> +        f(14, width);
> +        f(2, horizontal_scale);
> +        f(14, height);
> +        f(2, vertical_scale);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(frame_header)(CodedBitstreamContext *ctx, CBSVP8RangeCoder *rac,
> +                              VP8RawFrameHeader *current)
> +{
> +    if (current->frame_type == VP8_KEY_FRAME) {
> +        rac_f(1, color_space);
> +        rac_f(1, clamping_type);
> +    }
> +
> +    rac_f(1, segmentation_enabled);
> +    if (current->segmentation_enabled)
> +        CHECK(FUNC(update_segmentation)(ctx, rac, current));
> +
> +    rac_f(1, loop_filter_type);
> +    rac_f(6, loop_filter_level);
> +    rac_f(3, loop_filter_sharpness);
> +
> +    CHECK(FUNC(mode_ref_lf_deltas)(ctx, rac, current));
> +
> +    rac_f(2, log2_token_partitions);
> +
> +    CHECK(FUNC(quantization_params)(ctx, rac, current));
> +
> +    if (current->frame_type != VP8_KEY_FRAME) {
> +        rac_f(1, refresh_golden_frame);
> +        rac_f(1, refresh_alternate_frame);
> +        if (!current->refresh_golden_frame)
> +            cbs_vp8_rac_get(rac, 2);
> +        if (!current->refresh_alternate_frame)
> +            cbs_vp8_rac_get(rac, 2);
> +        rac_f(1, ref_frame_sign_bias_golden);
> +        rac_f(1, ref_frame_sign_bias_alternate);
> +    }
> +    rac_f(1, refresh_entropy_probs);
> +    if (current->frame_type != VP8_KEY_FRAME)
> +        rac_f(1, refresh_last_frame);
> +
> +    CHECK(FUNC(update_token_probs)(ctx, rac, current));
> +
> +    rac_f(1, mb_no_skip_coeff);
> +    if (current->mb_no_skip_coeff)
> +        rac_f(8, prob_skip_false);
> +
> +    if (current->frame_type != VP8_KEY_FRAME) {
> +        rac_f(8, prob_intra);
> +        rac_f(8, prob_last);
> +        rac_f(8, prob_golden);
> +
> +        // intra_16x16_prob
> +        if (cbs_vp8_rac_get(rac, 1))
> +            for (int i = 0; i < 4; i++)
> +                cbs_vp8_rac_get(rac, 8);
> +
> +        // intra_chroma_prob
> +        if (cbs_vp8_rac_get(rac, 1))
> +            for (int i = 0; i < 4; i++)
> +                cbs_vp8_rac_get(rac, 8);
> +
> +        CHECK(FUNC(update_mv_probs)(ctx, rac, current));
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw,
> +                                     VP8RawFrame *current)
> +{
> +    HEADER("Frame");
> +
> +    CHECK(FUNC(frame_tag)(ctx, rw, &current->header));
> +
> +    return 0;
> +}
> +
> +static int FUNC(compressed_header)(CodedBitstreamContext *ctx,
> +                                   CBSVP8RangeCoder *rac, VP8RawFrame *current)
> +{
> +    CHECK(FUNC(frame_header)(ctx, rac, &current->header));
> +
> +    return 0;
> +}
Dai, Jianhui J Aug. 15, 2023, 1:59 a.m. UTC | #6
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Andreas Rheinhardt
> Sent: Saturday, August 12, 2023 5:41 AM
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH v3] avcodec/cbs_vp8: Add support for
> VP8 codec bitstream READ methods
> 
> Dai, Jianhui J:
> > This commit adds VP8 into cbs supported codec list, and enables the
> > `trace_headers` bitstream filters to support VP8, besides existing
> > AV1, H.264, H.265 and VP9. It can be useful to debug VP8 stream issues.
> >
> > Only the READ methods `read_unit` and `split_fragment` are
> > implemented, the WRITE methods `write_unit` and `assemble_fragment`
> > return `AVERROR_PATCHWELCOME` error code. It is because the CBS VP8
> > WRITE is unlikely used by any applications at the moment. The WRITE
> > methods can be added later if there are real requirments.
> >
> > TESTS: ffmpeg -i fate-suite/vp8/frame_size_change.webm -vcodec copy
> > -bsf:v trace_headers -f null -
> >
> > Signed-off-by: Jianhui Dai <jianhui.j.dai@intel.com>
> > ---
> >  configure                            |   4 +-
> >  libavcodec/Makefile                  |   1 +
> >  libavcodec/cbs.c                     |   6 +
> >  libavcodec/cbs_internal.h            |   1 +
> >  libavcodec/cbs_vp8.c                 | 360 +++++++++++++++++++++++++++
> >  libavcodec/cbs_vp8.h                 | 112 +++++++++
> >  libavcodec/cbs_vp8_syntax_template.c | 224 +++++++++++++++++
> >  7 files changed, 707 insertions(+), 1 deletion(-)  create mode 100644
> > libavcodec/cbs_vp8.c  create mode 100644 libavcodec/cbs_vp8.h  create
> > mode 100644 libavcodec/cbs_vp8_syntax_template.c
> >
> > diff --git a/configure b/configure
> > index bb7be67676..b8960d2639 100755
> > --- a/configure
> > +++ b/configure
> > @@ -2432,6 +2432,7 @@ CONFIG_EXTRA="
> >      cbs_h265
> >      cbs_jpeg
> >      cbs_mpeg2
> > +    cbs_vp8
> >      cbs_vp9
> >      deflate_wrapper
> >      dirac_parse
> > @@ -2713,6 +2714,7 @@ cbs_h264_select="cbs"
> >  cbs_h265_select="cbs"
> >  cbs_jpeg_select="cbs"
> >  cbs_mpeg2_select="cbs"
> > +cbs_vp8_select="cbs"
> >  cbs_vp9_select="cbs"
> >  dct_select="rdft"
> >  deflate_wrapper_deps="zlib"
> > @@ -3284,7 +3286,7 @@ h264_redundant_pps_bsf_select="cbs_h264"
> >  hevc_metadata_bsf_select="cbs_h265"
> >  mjpeg2jpeg_bsf_select="jpegtables"
> >  mpeg2_metadata_bsf_select="cbs_mpeg2"
> > -trace_headers_bsf_select="cbs"
> > +trace_headers_bsf_select="cbs cbs_vp8"
> >  vp9_metadata_bsf_select="cbs_vp9"
> >
> >  # external libraries
> > diff --git a/libavcodec/Makefile b/libavcodec/Makefile index
> > 3cf4444b7e..1c4f0da1d2 100644
> > --- a/libavcodec/Makefile
> > +++ b/libavcodec/Makefile
> > @@ -78,6 +78,7 @@ OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o
> cbs_sei.o h2645_parse.o
> >  OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o cbs_sei.o
> h2645_parse.o
> >  OBJS-$(CONFIG_CBS_JPEG)                += cbs_jpeg.o
> >  OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
> > +OBJS-$(CONFIG_CBS_VP8)                 += cbs_vp8.o vpx_rac.o
> >  OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
> >  OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
> >  OBJS-$(CONFIG_DCT)                     += dct.o dct32_fixed.o dct32_float.o
> > diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index
> > 504197e06d..c77110abb1 100644
> > --- a/libavcodec/cbs.c
> > +++ b/libavcodec/cbs.c
> > @@ -46,6 +46,9 @@ static const CodedBitstreamType *const
> > cbs_type_table[] = {  #if CONFIG_CBS_MPEG2
> >      &ff_cbs_type_mpeg2,
> >  #endif
> > +#if CONFIG_CBS_VP8
> > +    &ff_cbs_type_vp8,
> > +#endif
> >  #if CONFIG_CBS_VP9
> >      &ff_cbs_type_vp9,
> >  #endif
> > @@ -67,6 +70,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {  #if
> > CONFIG_CBS_MPEG2
> >      AV_CODEC_ID_MPEG2VIDEO,
> >  #endif
> > +#if CONFIG_CBS_VP8
> > +    AV_CODEC_ID_VP8,
> > +#endif
> >  #if CONFIG_CBS_VP9
> >      AV_CODEC_ID_VP9,
> >  #endif
> > diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
> > index e585c77934..beaf8505d1 100644
> > --- a/libavcodec/cbs_internal.h
> > +++ b/libavcodec/cbs_internal.h
> > @@ -247,6 +247,7 @@ extern const CodedBitstreamType
> ff_cbs_type_h264;
> > extern const CodedBitstreamType ff_cbs_type_h265;  extern const
> > CodedBitstreamType ff_cbs_type_jpeg;  extern const CodedBitstreamType
> > ff_cbs_type_mpeg2;
> > +extern const CodedBitstreamType ff_cbs_type_vp8;
> >  extern const CodedBitstreamType ff_cbs_type_vp9;
> >
> >
> > diff --git a/libavcodec/cbs_vp8.c b/libavcodec/cbs_vp8.c new file mode
> > 100644 index 0000000000..a890590cd9
> > --- /dev/null
> > +++ b/libavcodec/cbs_vp8.c
> > @@ -0,0 +1,360 @@
> > +/*
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > +02110-1301 USA  */
> > +
> > +#include "libavutil/avassert.h"
> > +
> > +#include "cbs.h"
> > +#include "cbs_internal.h"
> > +#include "cbs_vp8.h"
> > +
> > +#include "vp8data.h"
> 
> This header is currently only included at one place, namely vp8.c and
> therefore it uses static tables. You are only using it for
> vp8_token_update_probs and the way you are doing it will duplicate this
> table in the binary.
> 
> > +
> > +// Wrap of VPXRangeCoder to support size check.
> > +typedef struct CBSVP8RangeCoder {
> > +    VPXRangeCoder c;
> 
> You are still using the vpx_rac.h API although it is not really suitable for CBS
> at all (see below). Also, you are not directly including the header for it.
> 
> > +    const uint8_t *buffer;
> > +    int buffer_size;
> > +} CBSVP8RangeCoder;
> > +
> > +static int cbs_vp8_read_unsigned(CodedBitstreamContext *ctx,
> GetBitContext *gbc,
> > +                                 int width, const char *name,
> > +                                 const int *subscripts, uint32_t
> > +*write_to) {
> > +    int pos;
> > +    uint32_t value;
> > +
> > +    av_assert0(width > 0 && width <= 24);
> > +
> > +    if (get_bits_left(gbc) < width) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value: bitstream
> ended.\n");
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos   = get_bits_count(gbc);
> > +    value = get_bits_le(gbc, width);
> > +
> > +    if (ctx->trace_enable) {
> > +        int i;
> > +        char bits[33];
> > +        for (i = 0; i < width; i++)
> > +            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';
> > +        bits[i] = 0;
> > +
> > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > +    }
> > +
> > +    *write_to = value;
> > +    return 0;
> > +}
> > +
> > +static int cbs_vp8_init_range_decoder(CBSVP8RangeCoder *rac, const
> uint8_t *buf,
> > +                                      int buf_size) {
> > +    int err;
> > +
> > +    err = ff_vpx_init_range_decoder(&rac->c, buf, buf_size);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    rac->buffer      = buf;
> > +    rac->buffer_size = buf_size;
> > +    return 0;
> > +}
> > +
> > +static av_unused int cbs_vp8_rac_get(CBSVP8RangeCoder *rac, int
> > +width)
> 
> Why av_unused?
> 
> > +{
> > +    int value = 0;
> > +
> > +    while (width--)
> > +        value = (value << 1) | vpx_rac_get_prob(&rac->c, 128);
> 
> vpx_rac_renorm() seems to perform bounds checking, but it also seems to be
> able to overread by one byte (it always reads two bytes even when only one
> byte is available) and there is no check for overreads. E.g.
> all overreads after prob_golden in frame_header are completely unchecked.
> 
> I also do not even know whether the arch-specific variants of these functions
> behave completely the same as the C versions.
> 
> I recommend you to implement your own versions of these functions, only
> based upon the spec and not upon vpx_rac.h.
> 
> > +
> > +    return value;
> > +}
> > +
> > +static int cbs_vp8_rac_get_prob_branchy(CBSVP8RangeCoder *rac, int
> > +prob) {
> > +    return vpx_rac_get_prob_branchy(&rac->c, prob); }
> > +
> > +static int cbs_vp8_rac_get_bits_count(CBSVP8RangeCoder *rac) {
> > +    int bits_count = (rac->c.buffer - rac->buffer) * 8;
> > +    bits_count += rac->c.bits;
> > +    return bits_count;
> > +}
> > +
> > +static int cbs_vp8_rac_read_unsigned(CodedBitstreamContext *ctx,
> > +                                     CBSVP8RangeCoder *rac, int width,
> > +                                     const char *name, const int *subscripts,
> > +                                     uint32_t *write_to) {
> > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > +    uint32_t value;
> > +    int pos;
> > +
> > +    av_assert0(width >= 0 && width <= 8);
> > +
> > +    if (vpx_rac_is_end(&rac->c)) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > +               "Invalid value at "
> > +               "%s: bitstream ended.\n",
> > +               name);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos = cbs_vp8_rac_get_bits_count(rac);
> > +    if (pos > rac->buffer_size * 8) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > +               "Invalid value at "
> > +               "%s: bitstream ended.\n",
> > +               name);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos += vp8->uncompressed_header_size * 8;
> > +
> > +    value = cbs_vp8_rac_get(rac, width);
> > +
> > +    if (ctx->trace_enable) {
> > +        char bits[33] = "-";
> > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > +    }
> > +
> > +    *write_to = value;
> > +    return 0;
> > +}
> > +
> > +static int cbs_vp8_rac_read_signed(CodedBitstreamContext *ctx,
> > +                                   CBSVP8RangeCoder *rac, int width,
> > +                                   const char *name, const int *subscripts,
> > +                                   int32_t *write_to) {
> > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > +    int32_t value;
> > +    int pos;
> > +
> > +    av_assert0(width >= 0 && width <= 8);
> > +
> > +    if (vpx_rac_is_end(&rac->c)) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > +               "Invalid value at "
> > +               "%s: bitstream ended.\n",
> > +               name);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos = cbs_vp8_rac_get_bits_count(rac);
> > +    if (pos > rac->buffer_size * 8) {
> > +        av_log(ctx->log_ctx, AV_LOG_ERROR,
> > +               "Invalid value at "
> > +               "%s: bitstream ended.\n",
> > +               name);
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    pos += vp8->uncompressed_header_size * 8;
> > +
> > +    value = cbs_vp8_rac_get(rac, width);
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        value = -value;
> > +
> > +    if (ctx->trace_enable) {
> > +        char bits[33] = "-";
> > +        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
> > +    }
> > +
> > +    *write_to = value;
> > +    return 0;
> > +}
> > +
> > +#define HEADER(name) \
> > +    do { \
> > +        ff_cbs_trace_header(ctx, name); \
> > +    } while (0)
> > +
> > +#define CHECK(call) \
> > +    do { \
> > +        int err = (call); \
> > +        if (err < 0) \
> > +            return err; \
> > +    } while (0)
> > +
> > +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name
> > +#define FUNC_VP8(rw, name) FUNC_NAME(rw, vp8, name) #define
> > +FUNC(name) FUNC_VP8(READWRITE, name)
> > +
> > +#define SUBSCRIPTS(subs, ...) \
> > +    (subs > 0 ? ((int[subs + 1]){subs, __VA_ARGS__}) : NULL)
> > +
> > +#define f(width, name) xf(width, name, 0)
> > +
> > +#define rac_f(width, name) rac_unsigned_subs(width, name, 0) #define
> > +rac_s(width, name) rac_signed_subs(width, name, 0) #define
> > +rac_fs(width, name, subs, ...) \
> > +    rac_unsigned_subs(width, name, subs, __VA_ARGS__) #define
> > +rac_ss(width, name, subs, ...) \
> > +    rac_signed_subs(width, name, subs, __VA_ARGS__)
> > +
> > +#define READ
> > +#define READWRITE read
> > +#define RWContext GetBitContext
> > +
> > +#define xf(width, name, subs, ...) \
> > +    do { \
> > +        uint32_t value; \
> > +        CHECK(cbs_vp8_read_unsigned(ctx, rw, width, #name, \
> > +                                    SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> > +        current->name = value; \
> > +    } while (0)
> > +
> > +#define fixed(width, name, value) \
> > +    do { \
> > +        av_unused uint32_t fixed_value; \
> > +        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, 0, &fixed_value, \
> > +                                   value, value)); \
> > +    } while (0)
> > +
> > +#define rac_unsigned_subs(width, name, subs, ...) \
> > +    do { \
> > +        uint32_t value; \
> > +        CHECK(cbs_vp8_rac_read_unsigned( \
> > +            ctx, rac, width, #name, SUBSCRIPTS(subs, __VA_ARGS__), &value));
> \
> > +        current->name = value; \
> > +    } while (0)
> > +
> > +#define rac_signed_subs(width, name, subs, ...) \
> > +    do { \
> > +        int32_t value; \
> > +        CHECK(cbs_vp8_rac_read_signed(ctx, rac, width, #name, \
> > +                                      SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
> > +        current->name = value; \
> > +    } while (0)
> > +
> > +#include "cbs_vp8_syntax_template.c"
> > +
> > +static int cbs_vp8_split_fragment(CodedBitstreamContext *ctx,
> > +                                  CodedBitstreamFragment *frag, int
> > +header) {
> > +    int err;
> > +
> > +    if (frag->data_size == 0)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    err = ff_cbs_append_unit_data(frag, 0, frag->data, frag->data_size,
> > +                                  frag->data_ref);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    return 0;
> > +}
> > +
> > +static int cbs_vp8_read_unit(CodedBitstreamContext *ctx,
> > +                             CodedBitstreamUnit *unit) {
> > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > +    VP8RawFrame *frame;
> > +    GetBitContext gbc;
> > +    CBSVP8RangeCoder rac;
> > +    int err, pos;
> > +
> > +    err = ff_cbs_alloc_unit_content(ctx, unit);
> > +    if (err < 0)
> > +        return err;
> > +    frame = unit->content;
> > +
> > +    // Create GetBitContext for uncompressed header.
> > +    err = init_get_bits8_le(&gbc, unit->data, 8 * unit->data_size);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    err = cbs_vp8_read_uncompressed_header(ctx, &gbc, frame);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    pos = get_bits_count(&gbc);
> > +    av_assert0(pos % 8 == 0);
> > +    pos /= 8;
> > +    av_assert0(pos <= unit->data_size);
> > +
> > +    if (pos == unit->data_size)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    vp8->uncompressed_header_size = pos;
> > +
> > +    // Create range decoder for compressed header.
> > +    err = cbs_vp8_init_range_decoder(
> > +        &rac, unit->data + pos, frame-
> >header.first_partition_length_in_bytes);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    err = cbs_vp8_read_compressed_header(ctx, &rac, frame);
> > +    if (err < 0)
> > +        return err;
> > +
> > +    pos = cbs_vp8_rac_get_bits_count(&rac);
> > +    if (pos > rac.buffer_size * 8)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    frame->data_ref = av_buffer_ref(unit->data_ref);
> > +    if (!frame->data_ref)
> > +        return AVERROR(ENOMEM);
> > +
> > +    frame->data      = unit->data + pos;
> > +    frame->data_size = unit->data_size - pos;
> > +
> > +    return 0;
> > +}
> > +
> > +static int cbs_vp8_write_unit(CodedBitstreamContext *ctx,
> > +                              CodedBitstreamUnit *unit, PutBitContext
> > +*pbc) {
> > +    return AVERROR_PATCHWELCOME;
> > +}
> > +
> > +static int cbs_vp8_assemble_fragment(CodedBitstreamContext *ctx,
> > +                                     CodedBitstreamFragment *frag) {
> > +    return AVERROR_PATCHWELCOME;
> > +}
> > +
> > +static void cbs_vp8_flush(CodedBitstreamContext *ctx) {
> > +    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
> > +
> > +    vp8->uncompressed_header_size = 0;
> 
> Why do you reset this at all here? It is reset in every read_unit before it is
> read, i.e. its effective lifetime is a part of every read_unit call. It could actually
> be passed to every function; it need not be in the context (and therefore the
> context need not exist). But even if it is in the context, it need not be reset.
> 
> > +}
> > +
> > +static const CodedBitstreamUnitTypeDescriptor cbs_vp8_unit_types[] = {
> > +    CBS_UNIT_TYPE_INTERNAL_REF(0, VP8RawFrame, data),
> > +    CBS_UNIT_TYPE_END_OF_LIST};
> > +
> > +const CodedBitstreamType ff_cbs_type_vp8 = {
> > +    .codec_id          = AV_CODEC_ID_VP8,
> > +
> > +    .priv_data_size    = sizeof(CodedBitstreamVP8Context),
> > +
> > +    .unit_types        = cbs_vp8_unit_types,
> > +
> > +    .split_fragment    = &cbs_vp8_split_fragment,
> > +    .read_unit         = &cbs_vp8_read_unit,
> > +    .write_unit        = &cbs_vp8_write_unit,
> > +
> > +    .flush             = &cbs_vp8_flush,
> > +
> > +    .assemble_fragment = &cbs_vp8_assemble_fragment, };
> > diff --git a/libavcodec/cbs_vp8.h b/libavcodec/cbs_vp8.h new file mode
> > 100644 index 0000000000..0bfa465ff7
> > --- /dev/null
> > +++ b/libavcodec/cbs_vp8.h
> > @@ -0,0 +1,112 @@
> > +/*
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > +02110-1301 USA  */
> > +
> > +#ifndef AVCODEC_CBS_VP8_H
> > +#define AVCODEC_CBS_VP8_H
> > +
> > +#include <stddef.h>
> > +#include <stdint.h>
> > +
> > +#include "cbs.h"
> > +#include "vpx_rac.h"
> > +
> > +enum {
> > +    VP8_START_CODE_0 = 0x9D,
> > +    VP8_START_CODE_1 = 0x01,
> > +    VP8_START_CODE_2 = 0x2A,
> > +};
> > +
> > +enum {
> > +    VP8_KEY_FRAME     = 0,
> > +    VP8_NON_KEY_FRAME = 1,
> > +};
> > +
> > +typedef struct VP8RawFrameHeader {
> > +    // frame tag
> > +    uint8_t frame_type;
> > +    uint8_t profile;
> > +    uint8_t show_frame;
> > +    uint32_t first_partition_length_in_bytes;
> > +
> > +    uint16_t width;
> > +    uint8_t horizontal_scale;
> > +    uint16_t height;
> > +    uint8_t vertical_scale;
> > +
> > +    // frame header
> > +    uint8_t color_space;
> > +    uint8_t clamping_type;
> > +
> > +    // segmentation
> > +    uint8_t segmentation_enabled;
> > +    uint8_t update_segment_map;
> > +    uint8_t update_segment_feature_data;
> > +    uint8_t segment_feature_mode;
> > +    int8_t segment_update_qp[4];
> > +    int8_t segment_update_loop_filter_level[4];
> > +    uint8_t segment_update_probs[3];
> > +
> > +    // loop filter
> > +    uint8_t loop_filter_type;
> > +    uint8_t loop_filter_level;
> > +    uint8_t loop_filter_sharpness;
> > +    uint8_t mode_ref_lf_delta_enabled;
> > +    int8_t ref_lf_deltas[4];
> > +    int8_t mode_lf_deltas[4];
> > +
> > +    uint8_t log2_token_partitions;
> > +
> > +    // qp
> > +    uint8_t base_qindex;
> > +    int8_t y1dc_delta_q;
> > +    int8_t y2dc_delta_q;
> > +    int8_t y2ac_delta_q;
> > +    int8_t uvdc_delta_q;
> > +    int8_t uvac_delta_q;
> > +
> > +    // ref
> > +    uint8_t refresh_golden_frame;
> > +    uint8_t refresh_alternate_frame;
> > +    uint8_t ref_frame_sign_bias_golden;
> > +    uint8_t ref_frame_sign_bias_alternate;
> > +    uint8_t refresh_last_frame;
> > +
> > +    // token probs
> > +    uint8_t refresh_entropy_probs;
> > +
> > +    uint8_t mb_no_skip_coeff;
> > +    uint8_t prob_skip_false;
> > +
> > +    uint8_t prob_intra;
> > +    uint8_t prob_last;
> > +    uint8_t prob_golden;
> > +} VP8RawFrameHeader;
> > +
> > +typedef struct VP8RawFrame {
> > +    VP8RawFrameHeader header;
> > +
> > +    uint8_t *data;
> > +    AVBufferRef *data_ref;
> > +    size_t data_size;
> > +} VP8RawFrame;
> > +
> > +typedef struct CodedBitstreamVP8Context {
> > +    uint32_t uncompressed_header_size; } CodedBitstreamVP8Context;
> > +
> > +#endif /* AVCODEC_CBS_VP8_H */
> > diff --git a/libavcodec/cbs_vp8_syntax_template.c
> > b/libavcodec/cbs_vp8_syntax_template.c
> > new file mode 100644
> > index 0000000000..5ab034a164
> > --- /dev/null
> > +++ b/libavcodec/cbs_vp8_syntax_template.c
> > @@ -0,0 +1,224 @@
> > +/*
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > +02110-1301 USA  */
> > +
> > +static int FUNC(update_segmentation)(CodedBitstreamContext *ctx,
> > +                                     CBSVP8RangeCoder *rac,
> > +                                     VP8RawFrameHeader *current) {
> > +    rac_f(1, update_segment_map);
> > +    rac_f(1, update_segment_feature_data);
> > +
> > +    if (current->update_segment_feature_data) {
> > +        rac_f(1, segment_feature_mode);
> > +        // quantizer
> > +        for (int i = 0; i < 4; i++)
> > +            if (cbs_vp8_rac_get(rac, 1))
> 
> CBS syntax templates are supposed to be for both reading and writing; even
> though the latter is unimplemented here, does not mean that we should
> adopt naming that only makes sense for reading. Your naming should reflect
> the syntax element and not whether it is reading or writing.
> 
> > +                rac_ss(7, segment_update_qp[i], 1, i);
> > +        // loop filter
> > +        for (int i = 0; i < 4; i++)
> > +            if (cbs_vp8_rac_get(rac, 1))
> > +                rac_ss(6, segment_update_loop_filter_level[i], 1, i);
> > +    }
> > +
> > +    if (current->update_segment_map) {
> > +        for (int i = 0; i < 3; i++)
> > +            if (cbs_vp8_rac_get(rac, 1))
> > +                rac_fs(8, segment_update_probs[i], 1, i);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(mode_ref_lf_deltas)(CodedBitstreamContext *ctx,
> > +                                    CBSVP8RangeCoder *rac,
> > +                                    VP8RawFrameHeader *current) {
> > +    rac_f(1, mode_ref_lf_delta_enabled);
> > +    if (current->mode_ref_lf_delta_enabled) {
> > +        if (cbs_vp8_rac_get(rac, 1)) {
> > +            // ref_lf_deltas
> > +            for (int i = 0; i < 4; i++)
> > +                if (cbs_vp8_rac_get(rac, 1))
> > +                    rac_ss(6, ref_lf_deltas[i], 1, i);
> > +            // mode_lf_deltas
> > +            for (int i = 0; i < 4; i++)
> > +                if (cbs_vp8_rac_get(rac, 1))
> > +                    rac_ss(6, mode_lf_deltas[i], 1, i);
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(quantization_params)(CodedBitstreamContext *ctx,
> > +                                     CBSVP8RangeCoder *rac,
> > +                                     VP8RawFrameHeader *current) {
> > +    rac_f(7, base_qindex);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, y1dc_delta_q);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, y2dc_delta_q);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, y2ac_delta_q);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, uvdc_delta_q);
> > +
> > +    if (cbs_vp8_rac_get(rac, 1))
> > +        rac_s(4, uvac_delta_q);
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(update_token_probs)(CodedBitstreamContext *ctx,
> > +                                    CBSVP8RangeCoder *rac,
> > +                                    VP8RawFrameHeader *current) {
> > +    for (int i = 0; i < 4; ++i) {
> > +        for (int j = 0; j < 8; ++j) {
> > +            for (int k = 0; k < 3; ++k) {
> > +                for (int l = 0; l < 11; ++l)
> > +                    if (cbs_vp8_rac_get_prob_branchy(
> > +                            rac, vp8_token_update_probs[i][j][k][l]))
> > +                        cbs_vp8_rac_get(rac, 8);
> > +            }
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(update_mv_probs)(CodedBitstreamContext *ctx,
> > +                                 CBSVP8RangeCoder *rac,
> > +                                 VP8RawFrameHeader *current) {
> > +    for (int i = 0; i < 2; ++i) {
> > +        for (int j = 0; j < 19; ++j)
> > +            if (cbs_vp8_rac_get(rac, 1))
> > +                cbs_vp8_rac_get(rac, 7);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(frame_tag)(CodedBitstreamContext *ctx, RWContext *rw,
> > +                           VP8RawFrameHeader *current) {
> > +    f(1, frame_type);
> > +    f(3, profile);
> > +    f(1, show_frame);
> > +    f(19, first_partition_length_in_bytes);
> > +
> > +    if (current->frame_type == VP8_KEY_FRAME) {
> > +        fixed(8, start_code_0, VP8_START_CODE_0);
> > +        fixed(8, start_code_1, VP8_START_CODE_1);
> > +        fixed(8, start_code_2, VP8_START_CODE_2);
> > +
> > +        f(14, width);
> > +        f(2, horizontal_scale);
> > +        f(14, height);
> > +        f(2, vertical_scale);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(frame_header)(CodedBitstreamContext *ctx,
> CBSVP8RangeCoder *rac,
> > +                              VP8RawFrameHeader *current) {
> > +    if (current->frame_type == VP8_KEY_FRAME) {
> > +        rac_f(1, color_space);
> > +        rac_f(1, clamping_type);
> > +    }
> > +
> > +    rac_f(1, segmentation_enabled);
> > +    if (current->segmentation_enabled)
> > +        CHECK(FUNC(update_segmentation)(ctx, rac, current));
> > +
> > +    rac_f(1, loop_filter_type);
> > +    rac_f(6, loop_filter_level);
> > +    rac_f(3, loop_filter_sharpness);
> > +
> > +    CHECK(FUNC(mode_ref_lf_deltas)(ctx, rac, current));
> > +
> > +    rac_f(2, log2_token_partitions);
> > +
> > +    CHECK(FUNC(quantization_params)(ctx, rac, current));
> > +
> > +    if (current->frame_type != VP8_KEY_FRAME) {
> > +        rac_f(1, refresh_golden_frame);
> > +        rac_f(1, refresh_alternate_frame);
> > +        if (!current->refresh_golden_frame)
> > +            cbs_vp8_rac_get(rac, 2);
> > +        if (!current->refresh_alternate_frame)
> > +            cbs_vp8_rac_get(rac, 2);
> > +        rac_f(1, ref_frame_sign_bias_golden);
> > +        rac_f(1, ref_frame_sign_bias_alternate);
> > +    }
> > +    rac_f(1, refresh_entropy_probs);
> > +    if (current->frame_type != VP8_KEY_FRAME)
> > +        rac_f(1, refresh_last_frame);
> > +
> > +    CHECK(FUNC(update_token_probs)(ctx, rac, current));
> > +
> > +    rac_f(1, mb_no_skip_coeff);
> > +    if (current->mb_no_skip_coeff)
> > +        rac_f(8, prob_skip_false);
> > +
> > +    if (current->frame_type != VP8_KEY_FRAME) {
> > +        rac_f(8, prob_intra);
> > +        rac_f(8, prob_last);
> > +        rac_f(8, prob_golden);
> > +
> > +        // intra_16x16_prob
> > +        if (cbs_vp8_rac_get(rac, 1))
> > +            for (int i = 0; i < 4; i++)
> > +                cbs_vp8_rac_get(rac, 8);
> > +
> > +        // intra_chroma_prob
> > +        if (cbs_vp8_rac_get(rac, 1))
> > +            for (int i = 0; i < 4; i++)
> > +                cbs_vp8_rac_get(rac, 8);
> > +
> > +        CHECK(FUNC(update_mv_probs)(ctx, rac, current));
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx,
> RWContext *rw,
> > +                                     VP8RawFrame *current) {
> > +    HEADER("Frame");
> > +
> > +    CHECK(FUNC(frame_tag)(ctx, rw, &current->header));
> > +
> > +    return 0;
> > +}
> > +
> > +static int FUNC(compressed_header)(CodedBitstreamContext *ctx,
> > +                                   CBSVP8RangeCoder *rac, VP8RawFrame
> > +*current) {
> > +    CHECK(FUNC(frame_header)(ctx, rac, &current->header));
> > +
> > +    return 0;
> > +}
> 

Thank for the comments. Will update a new patchset to resolve all.

> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org
> with subject "unsubscribe".
diff mbox series

Patch

diff --git a/configure b/configure
index bb7be67676..b8960d2639 100755
--- a/configure
+++ b/configure
@@ -2432,6 +2432,7 @@  CONFIG_EXTRA="
     cbs_h265
     cbs_jpeg
     cbs_mpeg2
+    cbs_vp8
     cbs_vp9
     deflate_wrapper
     dirac_parse
@@ -2713,6 +2714,7 @@  cbs_h264_select="cbs"
 cbs_h265_select="cbs"
 cbs_jpeg_select="cbs"
 cbs_mpeg2_select="cbs"
+cbs_vp8_select="cbs"
 cbs_vp9_select="cbs"
 dct_select="rdft"
 deflate_wrapper_deps="zlib"
@@ -3284,7 +3286,7 @@  h264_redundant_pps_bsf_select="cbs_h264"
 hevc_metadata_bsf_select="cbs_h265"
 mjpeg2jpeg_bsf_select="jpegtables"
 mpeg2_metadata_bsf_select="cbs_mpeg2"
-trace_headers_bsf_select="cbs"
+trace_headers_bsf_select="cbs cbs_vp8"
 vp9_metadata_bsf_select="cbs_vp9"
 
 # external libraries
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3cf4444b7e..1c4f0da1d2 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -78,6 +78,7 @@  OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o cbs_sei.o h2645_parse.o
 OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o cbs_sei.o h2645_parse.o
 OBJS-$(CONFIG_CBS_JPEG)                += cbs_jpeg.o
 OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
+OBJS-$(CONFIG_CBS_VP8)                 += cbs_vp8.o vpx_rac.o
 OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
 OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
 OBJS-$(CONFIG_DCT)                     += dct.o dct32_fixed.o dct32_float.o
diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index 504197e06d..c77110abb1 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -46,6 +46,9 @@  static const CodedBitstreamType *const cbs_type_table[] = {
 #if CONFIG_CBS_MPEG2
     &ff_cbs_type_mpeg2,
 #endif
+#if CONFIG_CBS_VP8
+    &ff_cbs_type_vp8,
+#endif
 #if CONFIG_CBS_VP9
     &ff_cbs_type_vp9,
 #endif
@@ -67,6 +70,9 @@  const enum AVCodecID ff_cbs_all_codec_ids[] = {
 #if CONFIG_CBS_MPEG2
     AV_CODEC_ID_MPEG2VIDEO,
 #endif
+#if CONFIG_CBS_VP8
+    AV_CODEC_ID_VP8,
+#endif
 #if CONFIG_CBS_VP9
     AV_CODEC_ID_VP9,
 #endif
diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
index e585c77934..beaf8505d1 100644
--- a/libavcodec/cbs_internal.h
+++ b/libavcodec/cbs_internal.h
@@ -247,6 +247,7 @@  extern const CodedBitstreamType ff_cbs_type_h264;
 extern const CodedBitstreamType ff_cbs_type_h265;
 extern const CodedBitstreamType ff_cbs_type_jpeg;
 extern const CodedBitstreamType ff_cbs_type_mpeg2;
+extern const CodedBitstreamType ff_cbs_type_vp8;
 extern const CodedBitstreamType ff_cbs_type_vp9;
 
 
diff --git a/libavcodec/cbs_vp8.c b/libavcodec/cbs_vp8.c
new file mode 100644
index 0000000000..a890590cd9
--- /dev/null
+++ b/libavcodec/cbs_vp8.c
@@ -0,0 +1,360 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avassert.h"
+
+#include "cbs.h"
+#include "cbs_internal.h"
+#include "cbs_vp8.h"
+
+#include "vp8data.h"
+
+// Wrap of VPXRangeCoder to support size check.
+typedef struct CBSVP8RangeCoder {
+    VPXRangeCoder c;
+    const uint8_t *buffer;
+    int buffer_size;
+} CBSVP8RangeCoder;
+
+static int cbs_vp8_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc,
+                                 int width, const char *name,
+                                 const int *subscripts, uint32_t *write_to)
+{
+    int pos;
+    uint32_t value;
+
+    av_assert0(width > 0 && width <= 24);
+
+    if (get_bits_left(gbc) < width) {
+        av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value: bitstream ended.\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    pos   = get_bits_count(gbc);
+    value = get_bits_le(gbc, width);
+
+    if (ctx->trace_enable) {
+        int i;
+        char bits[33];
+        for (i = 0; i < width; i++)
+            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';
+        bits[i] = 0;
+
+        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
+    }
+
+    *write_to = value;
+    return 0;
+}
+
+static int cbs_vp8_init_range_decoder(CBSVP8RangeCoder *rac, const uint8_t *buf,
+                                      int buf_size)
+{
+    int err;
+
+    err = ff_vpx_init_range_decoder(&rac->c, buf, buf_size);
+    if (err < 0)
+        return err;
+
+    rac->buffer      = buf;
+    rac->buffer_size = buf_size;
+    return 0;
+}
+
+static av_unused int cbs_vp8_rac_get(CBSVP8RangeCoder *rac, int width)
+{
+    int value = 0;
+
+    while (width--)
+        value = (value << 1) | vpx_rac_get_prob(&rac->c, 128);
+
+    return value;
+}
+
+static int cbs_vp8_rac_get_prob_branchy(CBSVP8RangeCoder *rac, int prob)
+{
+    return vpx_rac_get_prob_branchy(&rac->c, prob);
+}
+
+static int cbs_vp8_rac_get_bits_count(CBSVP8RangeCoder *rac)
+{
+    int bits_count = (rac->c.buffer - rac->buffer) * 8;
+    bits_count += rac->c.bits;
+    return bits_count;
+}
+
+static int cbs_vp8_rac_read_unsigned(CodedBitstreamContext *ctx,
+                                     CBSVP8RangeCoder *rac, int width,
+                                     const char *name, const int *subscripts,
+                                     uint32_t *write_to)
+{
+    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
+    uint32_t value;
+    int pos;
+
+    av_assert0(width >= 0 && width <= 8);
+
+    if (vpx_rac_is_end(&rac->c)) {
+        av_log(ctx->log_ctx, AV_LOG_ERROR,
+               "Invalid value at "
+               "%s: bitstream ended.\n",
+               name);
+        return AVERROR_INVALIDDATA;
+    }
+
+    pos = cbs_vp8_rac_get_bits_count(rac);
+    if (pos > rac->buffer_size * 8) {
+        av_log(ctx->log_ctx, AV_LOG_ERROR,
+               "Invalid value at "
+               "%s: bitstream ended.\n",
+               name);
+        return AVERROR_INVALIDDATA;
+    }
+
+    pos += vp8->uncompressed_header_size * 8;
+
+    value = cbs_vp8_rac_get(rac, width);
+
+    if (ctx->trace_enable) {
+        char bits[33] = "-";
+        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
+    }
+
+    *write_to = value;
+    return 0;
+}
+
+static int cbs_vp8_rac_read_signed(CodedBitstreamContext *ctx,
+                                   CBSVP8RangeCoder *rac, int width,
+                                   const char *name, const int *subscripts,
+                                   int32_t *write_to)
+{
+    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
+    int32_t value;
+    int pos;
+
+    av_assert0(width >= 0 && width <= 8);
+
+    if (vpx_rac_is_end(&rac->c)) {
+        av_log(ctx->log_ctx, AV_LOG_ERROR,
+               "Invalid value at "
+               "%s: bitstream ended.\n",
+               name);
+        return AVERROR_INVALIDDATA;
+    }
+
+    pos = cbs_vp8_rac_get_bits_count(rac);
+    if (pos > rac->buffer_size * 8) {
+        av_log(ctx->log_ctx, AV_LOG_ERROR,
+               "Invalid value at "
+               "%s: bitstream ended.\n",
+               name);
+        return AVERROR_INVALIDDATA;
+    }
+
+    pos += vp8->uncompressed_header_size * 8;
+
+    value = cbs_vp8_rac_get(rac, width);
+    if (cbs_vp8_rac_get(rac, 1))
+        value = -value;
+
+    if (ctx->trace_enable) {
+        char bits[33] = "-";
+        ff_cbs_trace_syntax_element(ctx, pos, name, subscripts, bits, value);
+    }
+
+    *write_to = value;
+    return 0;
+}
+
+#define HEADER(name) \
+    do { \
+        ff_cbs_trace_header(ctx, name); \
+    } while (0)
+
+#define CHECK(call) \
+    do { \
+        int err = (call); \
+        if (err < 0) \
+            return err; \
+    } while (0)
+
+#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name
+#define FUNC_VP8(rw, name) FUNC_NAME(rw, vp8, name)
+#define FUNC(name) FUNC_VP8(READWRITE, name)
+
+#define SUBSCRIPTS(subs, ...) \
+    (subs > 0 ? ((int[subs + 1]){subs, __VA_ARGS__}) : NULL)
+
+#define f(width, name) xf(width, name, 0)
+
+#define rac_f(width, name) rac_unsigned_subs(width, name, 0)
+#define rac_s(width, name) rac_signed_subs(width, name, 0)
+#define rac_fs(width, name, subs, ...) \
+    rac_unsigned_subs(width, name, subs, __VA_ARGS__)
+#define rac_ss(width, name, subs, ...) \
+    rac_signed_subs(width, name, subs, __VA_ARGS__)
+
+#define READ
+#define READWRITE read
+#define RWContext GetBitContext
+
+#define xf(width, name, subs, ...) \
+    do { \
+        uint32_t value; \
+        CHECK(cbs_vp8_read_unsigned(ctx, rw, width, #name, \
+                                    SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
+        current->name = value; \
+    } while (0)
+
+#define fixed(width, name, value) \
+    do { \
+        av_unused uint32_t fixed_value; \
+        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, 0, &fixed_value, \
+                                   value, value)); \
+    } while (0)
+
+#define rac_unsigned_subs(width, name, subs, ...) \
+    do { \
+        uint32_t value; \
+        CHECK(cbs_vp8_rac_read_unsigned( \
+            ctx, rac, width, #name, SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
+        current->name = value; \
+    } while (0)
+
+#define rac_signed_subs(width, name, subs, ...) \
+    do { \
+        int32_t value; \
+        CHECK(cbs_vp8_rac_read_signed(ctx, rac, width, #name, \
+                                      SUBSCRIPTS(subs, __VA_ARGS__), &value)); \
+        current->name = value; \
+    } while (0)
+
+#include "cbs_vp8_syntax_template.c"
+
+static int cbs_vp8_split_fragment(CodedBitstreamContext *ctx,
+                                  CodedBitstreamFragment *frag, int header)
+{
+    int err;
+
+    if (frag->data_size == 0)
+        return AVERROR_INVALIDDATA;
+
+    err = ff_cbs_append_unit_data(frag, 0, frag->data, frag->data_size,
+                                  frag->data_ref);
+    if (err < 0)
+        return err;
+
+    return 0;
+}
+
+static int cbs_vp8_read_unit(CodedBitstreamContext *ctx,
+                             CodedBitstreamUnit *unit)
+{
+    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
+    VP8RawFrame *frame;
+    GetBitContext gbc;
+    CBSVP8RangeCoder rac;
+    int err, pos;
+
+    err = ff_cbs_alloc_unit_content(ctx, unit);
+    if (err < 0)
+        return err;
+    frame = unit->content;
+
+    // Create GetBitContext for uncompressed header.
+    err = init_get_bits8_le(&gbc, unit->data, 8 * unit->data_size);
+    if (err < 0)
+        return err;
+
+    err = cbs_vp8_read_uncompressed_header(ctx, &gbc, frame);
+    if (err < 0)
+        return err;
+
+    pos = get_bits_count(&gbc);
+    av_assert0(pos % 8 == 0);
+    pos /= 8;
+    av_assert0(pos <= unit->data_size);
+
+    if (pos == unit->data_size)
+        return AVERROR_INVALIDDATA;
+
+    vp8->uncompressed_header_size = pos;
+
+    // Create range decoder for compressed header.
+    err = cbs_vp8_init_range_decoder(
+        &rac, unit->data + pos, frame->header.first_partition_length_in_bytes);
+    if (err < 0)
+        return err;
+
+    err = cbs_vp8_read_compressed_header(ctx, &rac, frame);
+    if (err < 0)
+        return err;
+
+    pos = cbs_vp8_rac_get_bits_count(&rac);
+    if (pos > rac.buffer_size * 8)
+        return AVERROR_INVALIDDATA;
+
+    frame->data_ref = av_buffer_ref(unit->data_ref);
+    if (!frame->data_ref)
+        return AVERROR(ENOMEM);
+
+    frame->data      = unit->data + pos;
+    frame->data_size = unit->data_size - pos;
+
+    return 0;
+}
+
+static int cbs_vp8_write_unit(CodedBitstreamContext *ctx,
+                              CodedBitstreamUnit *unit, PutBitContext *pbc)
+{
+    return AVERROR_PATCHWELCOME;
+}
+
+static int cbs_vp8_assemble_fragment(CodedBitstreamContext *ctx,
+                                     CodedBitstreamFragment *frag)
+{
+    return AVERROR_PATCHWELCOME;
+}
+
+static void cbs_vp8_flush(CodedBitstreamContext *ctx)
+{
+    CodedBitstreamVP8Context *vp8 = ctx->priv_data;
+
+    vp8->uncompressed_header_size = 0;
+}
+
+static const CodedBitstreamUnitTypeDescriptor cbs_vp8_unit_types[] = {
+    CBS_UNIT_TYPE_INTERNAL_REF(0, VP8RawFrame, data),
+    CBS_UNIT_TYPE_END_OF_LIST};
+
+const CodedBitstreamType ff_cbs_type_vp8 = {
+    .codec_id          = AV_CODEC_ID_VP8,
+
+    .priv_data_size    = sizeof(CodedBitstreamVP8Context),
+
+    .unit_types        = cbs_vp8_unit_types,
+
+    .split_fragment    = &cbs_vp8_split_fragment,
+    .read_unit         = &cbs_vp8_read_unit,
+    .write_unit        = &cbs_vp8_write_unit,
+
+    .flush             = &cbs_vp8_flush,
+
+    .assemble_fragment = &cbs_vp8_assemble_fragment,
+};
diff --git a/libavcodec/cbs_vp8.h b/libavcodec/cbs_vp8.h
new file mode 100644
index 0000000000..0bfa465ff7
--- /dev/null
+++ b/libavcodec/cbs_vp8.h
@@ -0,0 +1,112 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_CBS_VP8_H
+#define AVCODEC_CBS_VP8_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "cbs.h"
+#include "vpx_rac.h"
+
+enum {
+    VP8_START_CODE_0 = 0x9D,
+    VP8_START_CODE_1 = 0x01,
+    VP8_START_CODE_2 = 0x2A,
+};
+
+enum {
+    VP8_KEY_FRAME     = 0,
+    VP8_NON_KEY_FRAME = 1,
+};
+
+typedef struct VP8RawFrameHeader {
+    // frame tag
+    uint8_t frame_type;
+    uint8_t profile;
+    uint8_t show_frame;
+    uint32_t first_partition_length_in_bytes;
+
+    uint16_t width;
+    uint8_t horizontal_scale;
+    uint16_t height;
+    uint8_t vertical_scale;
+
+    // frame header
+    uint8_t color_space;
+    uint8_t clamping_type;
+
+    // segmentation
+    uint8_t segmentation_enabled;
+    uint8_t update_segment_map;
+    uint8_t update_segment_feature_data;
+    uint8_t segment_feature_mode;
+    int8_t segment_update_qp[4];
+    int8_t segment_update_loop_filter_level[4];
+    uint8_t segment_update_probs[3];
+
+    // loop filter
+    uint8_t loop_filter_type;
+    uint8_t loop_filter_level;
+    uint8_t loop_filter_sharpness;
+    uint8_t mode_ref_lf_delta_enabled;
+    int8_t ref_lf_deltas[4];
+    int8_t mode_lf_deltas[4];
+
+    uint8_t log2_token_partitions;
+
+    // qp
+    uint8_t base_qindex;
+    int8_t y1dc_delta_q;
+    int8_t y2dc_delta_q;
+    int8_t y2ac_delta_q;
+    int8_t uvdc_delta_q;
+    int8_t uvac_delta_q;
+
+    // ref
+    uint8_t refresh_golden_frame;
+    uint8_t refresh_alternate_frame;
+    uint8_t ref_frame_sign_bias_golden;
+    uint8_t ref_frame_sign_bias_alternate;
+    uint8_t refresh_last_frame;
+
+    // token probs
+    uint8_t refresh_entropy_probs;
+
+    uint8_t mb_no_skip_coeff;
+    uint8_t prob_skip_false;
+
+    uint8_t prob_intra;
+    uint8_t prob_last;
+    uint8_t prob_golden;
+} VP8RawFrameHeader;
+
+typedef struct VP8RawFrame {
+    VP8RawFrameHeader header;
+
+    uint8_t *data;
+    AVBufferRef *data_ref;
+    size_t data_size;
+} VP8RawFrame;
+
+typedef struct CodedBitstreamVP8Context {
+    uint32_t uncompressed_header_size;
+} CodedBitstreamVP8Context;
+
+#endif /* AVCODEC_CBS_VP8_H */
diff --git a/libavcodec/cbs_vp8_syntax_template.c b/libavcodec/cbs_vp8_syntax_template.c
new file mode 100644
index 0000000000..5ab034a164
--- /dev/null
+++ b/libavcodec/cbs_vp8_syntax_template.c
@@ -0,0 +1,224 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+static int FUNC(update_segmentation)(CodedBitstreamContext *ctx,
+                                     CBSVP8RangeCoder *rac,
+                                     VP8RawFrameHeader *current)
+{
+    rac_f(1, update_segment_map);
+    rac_f(1, update_segment_feature_data);
+
+    if (current->update_segment_feature_data) {
+        rac_f(1, segment_feature_mode);
+        // quantizer
+        for (int i = 0; i < 4; i++)
+            if (cbs_vp8_rac_get(rac, 1))
+                rac_ss(7, segment_update_qp[i], 1, i);
+        // loop filter
+        for (int i = 0; i < 4; i++)
+            if (cbs_vp8_rac_get(rac, 1))
+                rac_ss(6, segment_update_loop_filter_level[i], 1, i);
+    }
+
+    if (current->update_segment_map) {
+        for (int i = 0; i < 3; i++)
+            if (cbs_vp8_rac_get(rac, 1))
+                rac_fs(8, segment_update_probs[i], 1, i);
+    }
+
+    return 0;
+}
+
+static int FUNC(mode_ref_lf_deltas)(CodedBitstreamContext *ctx,
+                                    CBSVP8RangeCoder *rac,
+                                    VP8RawFrameHeader *current)
+{
+    rac_f(1, mode_ref_lf_delta_enabled);
+    if (current->mode_ref_lf_delta_enabled) {
+        if (cbs_vp8_rac_get(rac, 1)) {
+            // ref_lf_deltas
+            for (int i = 0; i < 4; i++)
+                if (cbs_vp8_rac_get(rac, 1))
+                    rac_ss(6, ref_lf_deltas[i], 1, i);
+            // mode_lf_deltas
+            for (int i = 0; i < 4; i++)
+                if (cbs_vp8_rac_get(rac, 1))
+                    rac_ss(6, mode_lf_deltas[i], 1, i);
+        }
+    }
+
+    return 0;
+}
+
+static int FUNC(quantization_params)(CodedBitstreamContext *ctx,
+                                     CBSVP8RangeCoder *rac,
+                                     VP8RawFrameHeader *current)
+{
+    rac_f(7, base_qindex);
+
+    if (cbs_vp8_rac_get(rac, 1))
+        rac_s(4, y1dc_delta_q);
+
+    if (cbs_vp8_rac_get(rac, 1))
+        rac_s(4, y2dc_delta_q);
+
+    if (cbs_vp8_rac_get(rac, 1))
+        rac_s(4, y2ac_delta_q);
+
+    if (cbs_vp8_rac_get(rac, 1))
+        rac_s(4, uvdc_delta_q);
+
+    if (cbs_vp8_rac_get(rac, 1))
+        rac_s(4, uvac_delta_q);
+
+    return 0;
+}
+
+static int FUNC(update_token_probs)(CodedBitstreamContext *ctx,
+                                    CBSVP8RangeCoder *rac,
+                                    VP8RawFrameHeader *current)
+{
+    for (int i = 0; i < 4; ++i) {
+        for (int j = 0; j < 8; ++j) {
+            for (int k = 0; k < 3; ++k) {
+                for (int l = 0; l < 11; ++l)
+                    if (cbs_vp8_rac_get_prob_branchy(
+                            rac, vp8_token_update_probs[i][j][k][l]))
+                        cbs_vp8_rac_get(rac, 8);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int FUNC(update_mv_probs)(CodedBitstreamContext *ctx,
+                                 CBSVP8RangeCoder *rac,
+                                 VP8RawFrameHeader *current)
+{
+    for (int i = 0; i < 2; ++i) {
+        for (int j = 0; j < 19; ++j)
+            if (cbs_vp8_rac_get(rac, 1))
+                cbs_vp8_rac_get(rac, 7);
+    }
+
+    return 0;
+}
+
+static int FUNC(frame_tag)(CodedBitstreamContext *ctx, RWContext *rw,
+                           VP8RawFrameHeader *current)
+{
+    f(1, frame_type);
+    f(3, profile);
+    f(1, show_frame);
+    f(19, first_partition_length_in_bytes);
+
+    if (current->frame_type == VP8_KEY_FRAME) {
+        fixed(8, start_code_0, VP8_START_CODE_0);
+        fixed(8, start_code_1, VP8_START_CODE_1);
+        fixed(8, start_code_2, VP8_START_CODE_2);
+
+        f(14, width);
+        f(2, horizontal_scale);
+        f(14, height);
+        f(2, vertical_scale);
+    }
+
+    return 0;
+}
+
+static int FUNC(frame_header)(CodedBitstreamContext *ctx, CBSVP8RangeCoder *rac,
+                              VP8RawFrameHeader *current)
+{
+    if (current->frame_type == VP8_KEY_FRAME) {
+        rac_f(1, color_space);
+        rac_f(1, clamping_type);
+    }
+
+    rac_f(1, segmentation_enabled);
+    if (current->segmentation_enabled)
+        CHECK(FUNC(update_segmentation)(ctx, rac, current));
+
+    rac_f(1, loop_filter_type);
+    rac_f(6, loop_filter_level);
+    rac_f(3, loop_filter_sharpness);
+
+    CHECK(FUNC(mode_ref_lf_deltas)(ctx, rac, current));
+
+    rac_f(2, log2_token_partitions);
+
+    CHECK(FUNC(quantization_params)(ctx, rac, current));
+
+    if (current->frame_type != VP8_KEY_FRAME) {
+        rac_f(1, refresh_golden_frame);
+        rac_f(1, refresh_alternate_frame);
+        if (!current->refresh_golden_frame)
+            cbs_vp8_rac_get(rac, 2);
+        if (!current->refresh_alternate_frame)
+            cbs_vp8_rac_get(rac, 2);
+        rac_f(1, ref_frame_sign_bias_golden);
+        rac_f(1, ref_frame_sign_bias_alternate);
+    }
+    rac_f(1, refresh_entropy_probs);
+    if (current->frame_type != VP8_KEY_FRAME)
+        rac_f(1, refresh_last_frame);
+
+    CHECK(FUNC(update_token_probs)(ctx, rac, current));
+
+    rac_f(1, mb_no_skip_coeff);
+    if (current->mb_no_skip_coeff)
+        rac_f(8, prob_skip_false);
+
+    if (current->frame_type != VP8_KEY_FRAME) {
+        rac_f(8, prob_intra);
+        rac_f(8, prob_last);
+        rac_f(8, prob_golden);
+
+        // intra_16x16_prob
+        if (cbs_vp8_rac_get(rac, 1))
+            for (int i = 0; i < 4; i++)
+                cbs_vp8_rac_get(rac, 8);
+
+        // intra_chroma_prob
+        if (cbs_vp8_rac_get(rac, 1))
+            for (int i = 0; i < 4; i++)
+                cbs_vp8_rac_get(rac, 8);
+
+        CHECK(FUNC(update_mv_probs)(ctx, rac, current));
+    }
+
+    return 0;
+}
+
+static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw,
+                                     VP8RawFrame *current)
+{
+    HEADER("Frame");
+
+    CHECK(FUNC(frame_tag)(ctx, rw, &current->header));
+
+    return 0;
+}
+
+static int FUNC(compressed_header)(CodedBitstreamContext *ctx,
+                                   CBSVP8RangeCoder *rac, VP8RawFrame *current)
+{
+    CHECK(FUNC(frame_header)(ctx, rac, &current->header));
+
+    return 0;
+}