[FFmpeg-devel] [PATCH v4 1/4] libavcodec/vc2enc: Split out common functions between software and hardware encoders
Andreas Rheinhardt
andreas.rheinhardt at outlook.com
Mon May 19 18:56:55 EEST 2025
IndecisiveTurtle:
> From: IndecisiveTurtle <geoster3d at gmail.com>
>
> ---
> libavcodec/Makefile | 2 +-
> libavcodec/vc2enc.c | 679 ++-----------------------------------
> libavcodec/vc2enc_common.c | 571 +++++++++++++++++++++++++++++++
> libavcodec/vc2enc_common.h | 178 ++++++++++
> 4 files changed, 772 insertions(+), 658 deletions(-)
> create mode 100644 libavcodec/vc2enc_common.c
> create mode 100644 libavcodec/vc2enc_common.h
>
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 77734dff24..bdf0d6742e 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -771,7 +771,7 @@ OBJS-$(CONFIG_VC1_CUVID_DECODER) += cuviddec.o
> OBJS-$(CONFIG_VC1_MMAL_DECODER) += mmaldec.o
> OBJS-$(CONFIG_VC1_QSV_DECODER) += qsvdec.o
> OBJS-$(CONFIG_VC1_V4L2M2M_DECODER) += v4l2_m2m_dec.o
> -OBJS-$(CONFIG_VC2_ENCODER) += vc2enc.o vc2enc_dwt.o diractab.o
> +OBJS-$(CONFIG_VC2_ENCODER) += vc2enc.o vc2enc_dwt.o vc2enc_common.o diractab.o
Seems like this should be split into two lines
> OBJS-$(CONFIG_VCR1_DECODER) += vcr1.o
> OBJS-$(CONFIG_VMDAUDIO_DECODER) += vmdaudio.o
> OBJS-$(CONFIG_VMDVIDEO_DECODER) += vmdvideo.o
> diff --git a/libavcodec/vc2enc.c b/libavcodec/vc2enc.c
> index 99ca95c40a..939bafa195 100644
> --- a/libavcodec/vc2enc.c
> +++ b/libavcodec/vc2enc.c
> @@ -30,505 +30,11 @@
> #include "put_bits.h"
> #include "version.h"
>
> -#include "vc2enc_dwt.h"
> -#include "diractab.h"
> -
> -/* The limited size resolution of each slice forces us to do this */
> -#define SSIZE_ROUND(b) (FFALIGN((b), s->size_scaler) + 4 + s->prefix_bytes)
> +#include "vc2enc_common.h"
>
> /* Decides the cutoff point in # of slices to distribute the leftover bytes */
> #define SLICE_REDIST_TOTAL 150
>
> -typedef struct VC2BaseVideoFormat {
> - enum AVPixelFormat pix_fmt;
> - AVRational time_base;
> - int width, height;
> - uint8_t interlaced, level;
> - char name[13];
> -} VC2BaseVideoFormat;
> -
> -static const VC2BaseVideoFormat base_video_fmts[] = {
> - { 0 }, /* Custom format, here just to make indexing equal to base_vf */
> - { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 176, 120, 0, 1, "QSIF525" },
> - { AV_PIX_FMT_YUV420P, { 2, 25 }, 176, 144, 0, 1, "QCIF" },
> - { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 352, 240, 0, 1, "SIF525" },
> - { AV_PIX_FMT_YUV420P, { 2, 25 }, 352, 288, 0, 1, "CIF" },
> - { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 704, 480, 0, 1, "4SIF525" },
> - { AV_PIX_FMT_YUV420P, { 2, 25 }, 704, 576, 0, 1, "4CIF" },
> -
> - { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 720, 480, 1, 2, "SD480I-60" },
> - { AV_PIX_FMT_YUV422P10, { 1, 25 }, 720, 576, 1, 2, "SD576I-50" },
> -
> - { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 1280, 720, 0, 3, "HD720P-60" },
> - { AV_PIX_FMT_YUV422P10, { 1, 50 }, 1280, 720, 0, 3, "HD720P-50" },
> - { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 1920, 1080, 1, 3, "HD1080I-60" },
> - { AV_PIX_FMT_YUV422P10, { 1, 25 }, 1920, 1080, 1, 3, "HD1080I-50" },
> - { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 1920, 1080, 0, 3, "HD1080P-60" },
> - { AV_PIX_FMT_YUV422P10, { 1, 50 }, 1920, 1080, 0, 3, "HD1080P-50" },
> -
> - { AV_PIX_FMT_YUV444P12, { 1, 24 }, 2048, 1080, 0, 4, "DC2K" },
> - { AV_PIX_FMT_YUV444P12, { 1, 24 }, 4096, 2160, 0, 5, "DC4K" },
> -
> - { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 3840, 2160, 0, 6, "UHDTV 4K-60" },
> - { AV_PIX_FMT_YUV422P10, { 1, 50 }, 3840, 2160, 0, 6, "UHDTV 4K-50" },
> -
> - { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 7680, 4320, 0, 7, "UHDTV 8K-60" },
> - { AV_PIX_FMT_YUV422P10, { 1, 50 }, 7680, 4320, 0, 7, "UHDTV 8K-50" },
> -
> - { AV_PIX_FMT_YUV422P10, { 1001, 24000 }, 1920, 1080, 0, 3, "HD1080P-24" },
> - { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 720, 486, 1, 2, "SD Pro486" },
> -};
> -static const int base_video_fmts_len = FF_ARRAY_ELEMS(base_video_fmts);
> -
> -enum VC2_QM {
> - VC2_QM_DEF = 0,
> - VC2_QM_COL,
> - VC2_QM_FLAT,
> -
> - VC2_QM_NB
> -};
> -
> -typedef struct SubBand {
> - dwtcoef *buf;
> - ptrdiff_t stride;
> - int width;
> - int height;
> -} SubBand;
> -
> -typedef struct Plane {
> - SubBand band[MAX_DWT_LEVELS][4];
> - dwtcoef *coef_buf;
> - int width;
> - int height;
> - int dwt_width;
> - int dwt_height;
> - ptrdiff_t coef_stride;
> -} Plane;
> -
> -typedef struct SliceArgs {
> - const struct VC2EncContext *ctx;
> - union {
> - int cache[DIRAC_MAX_QUANT_INDEX];
> - uint8_t *buf;
> - };
> - int x;
> - int y;
> - int quant_idx;
> - int bits_ceil;
> - int bits_floor;
> - int bytes;
> -} SliceArgs;
> -
> -typedef struct TransformArgs {
> - const struct VC2EncContext *ctx;
> - Plane *plane;
> - const void *idata;
> - ptrdiff_t istride;
> - int field;
> - VC2TransformContext t;
> -} TransformArgs;
> -
> -typedef struct VC2EncContext {
> - AVClass *av_class;
> - PutBitContext pb;
> - Plane plane[3];
> - AVCodecContext *avctx;
> - DiracVersionInfo ver;
> -
> - SliceArgs *slice_args;
> - TransformArgs transform_args[3];
> -
> - /* For conversion from unsigned pixel values to signed */
> - int diff_offset;
> - int bpp;
> - int bpp_idx;
> -
> - /* Picture number */
> - uint32_t picture_number;
> -
> - /* Base video format */
> - int base_vf;
> - int level;
> - int profile;
> -
> - /* Quantization matrix */
> - uint8_t quant[MAX_DWT_LEVELS][4];
> - int custom_quant_matrix;
> -
> - /* Division LUT */
> - uint32_t qmagic_lut[116][2];
> -
> - int num_x; /* #slices horizontally */
> - int num_y; /* #slices vertically */
> - int prefix_bytes;
> - int size_scaler;
> - int chroma_x_shift;
> - int chroma_y_shift;
> -
> - /* Rate control stuff */
> - int frame_max_bytes;
> - int slice_max_bytes;
> - int slice_min_bytes;
> - int q_ceil;
> - int q_avg;
> -
> - /* Options */
> - double tolerance;
> - int wavelet_idx;
> - int wavelet_depth;
> - int strict_compliance;
> - int slice_height;
> - int slice_width;
> - int interlaced;
> - enum VC2_QM quant_matrix;
> -
> - /* Parse code state */
> - uint32_t next_parse_offset;
> - enum DiracParseCodes last_parse_code;
> -} VC2EncContext;
> -
> -/// x_k x_{k-1} ... x_0 -> 0 x_k 0 x_{k - 1} ... 0 x_0
> -static uint16_t interleaved_ue_golomb_tab[256];
> -/// 1 x_{k-1} ... x_0 -> 0 0 0 x_{k - 1} ... 0 x_0
> -static uint16_t top_interleaved_ue_golomb_tab[256];
> -/// 1 x_{k-1} ... x_0 -> 2 * k
> -static uint8_t golomb_len_tab[256];
> -
> -static av_cold void vc2_init_static_data(void)
> -{
> - interleaved_ue_golomb_tab[1] = 1;
> - for (unsigned i = 2; i < 256; ++i) {
> - golomb_len_tab[i] = golomb_len_tab[i >> 1] + 2;
> - interleaved_ue_golomb_tab[i] = (interleaved_ue_golomb_tab[i >> 1] << 2) | (i & 1);
> - top_interleaved_ue_golomb_tab[i] = interleaved_ue_golomb_tab[i] ^ (1 << golomb_len_tab[i]);
> - }
> -}
> -
> -static av_always_inline void put_vc2_ue_uint_inline(PutBitContext *pb, uint32_t val)
> -{
> - uint64_t pbits = 1;
> - int bits = 1;
> -
> - ++val;
> -
> - while (val >> 8) {
> - pbits |= (uint64_t)interleaved_ue_golomb_tab[val & 0xff] << bits;
> - val >>= 8;
> - bits += 16;
> - }
> - pbits |= (uint64_t)top_interleaved_ue_golomb_tab[val] << bits;
> - bits += golomb_len_tab[val];
> -
> - put_bits63(pb, bits, pbits);
> -}
> -
> -static av_noinline void put_vc2_ue_uint(PutBitContext *pb, uint32_t val)
> -{
> - put_vc2_ue_uint_inline(pb, val);
> -}
> -
> -static av_always_inline int count_vc2_ue_uint(uint32_t val)
> -{
> - return 2 * av_log2(val + 1) + 1;
> -}
> -
> -/* VC-2 10.4 - parse_info() */
> -static void encode_parse_info(VC2EncContext *s, enum DiracParseCodes pcode)
> -{
> - uint32_t cur_pos, dist;
> -
> - align_put_bits(&s->pb);
> -
> - cur_pos = put_bytes_count(&s->pb, 0);
> -
> - /* Magic string */
> - ff_put_string(&s->pb, "BBCD", 0);
> -
> - /* Parse code */
> - put_bits(&s->pb, 8, pcode);
> -
> - /* Next parse offset */
> - dist = cur_pos - s->next_parse_offset;
> - AV_WB32(s->pb.buf + s->next_parse_offset + 5, dist);
> - s->next_parse_offset = cur_pos;
> - put_bits32(&s->pb, pcode == DIRAC_PCODE_END_SEQ ? 13 : 0);
> -
> - /* Last parse offset */
> - put_bits32(&s->pb, s->last_parse_code == DIRAC_PCODE_END_SEQ ? 13 : dist);
> -
> - s->last_parse_code = pcode;
> -}
> -
> -/* VC-2 11.1 - parse_parameters()
> - * The level dictates what the decoder should expect in terms of resolution
> - * and allows it to quickly reject whatever it can't support. Remember,
> - * this codec kinda targets cheapo FPGAs without much memory. Unfortunately
> - * it also limits us greatly in our choice of formats, hence the flag to disable
> - * strict_compliance */
> -static void encode_parse_params(VC2EncContext *s)
> -{
> - put_vc2_ue_uint(&s->pb, s->ver.major); /* VC-2 demands this to be 2 */
> - put_vc2_ue_uint(&s->pb, s->ver.minor); /* ^^ and this to be 0 */
> - put_vc2_ue_uint(&s->pb, s->profile); /* 3 to signal HQ profile */
> - put_vc2_ue_uint(&s->pb, s->level); /* 3 - 1080/720, 6 - 4K */
> -}
> -
> -/* VC-2 11.3 - frame_size() */
> -static void encode_frame_size(VC2EncContext *s)
> -{
> - put_bits(&s->pb, 1, !s->strict_compliance);
> - if (!s->strict_compliance) {
> - AVCodecContext *avctx = s->avctx;
> - put_vc2_ue_uint(&s->pb, avctx->width);
> - put_vc2_ue_uint(&s->pb, avctx->height);
> - }
> -}
> -
> -/* VC-2 11.3.3 - color_diff_sampling_format() */
> -static void encode_sample_fmt(VC2EncContext *s)
> -{
> - put_bits(&s->pb, 1, !s->strict_compliance);
> - if (!s->strict_compliance) {
> - int idx;
> - if (s->chroma_x_shift == 1 && s->chroma_y_shift == 0)
> - idx = 1; /* 422 */
> - else if (s->chroma_x_shift == 1 && s->chroma_y_shift == 1)
> - idx = 2; /* 420 */
> - else
> - idx = 0; /* 444 */
> - put_vc2_ue_uint(&s->pb, idx);
> - }
> -}
> -
> -/* VC-2 11.3.4 - scan_format() */
> -static void encode_scan_format(VC2EncContext *s)
> -{
> - put_bits(&s->pb, 1, !s->strict_compliance);
> - if (!s->strict_compliance)
> - put_vc2_ue_uint(&s->pb, s->interlaced);
> -}
> -
> -/* VC-2 11.3.5 - frame_rate() */
> -static void encode_frame_rate(VC2EncContext *s)
> -{
> - put_bits(&s->pb, 1, !s->strict_compliance);
> - if (!s->strict_compliance) {
> - AVCodecContext *avctx = s->avctx;
> - put_vc2_ue_uint(&s->pb, 0);
> - put_vc2_ue_uint(&s->pb, avctx->time_base.den);
> - put_vc2_ue_uint(&s->pb, avctx->time_base.num);
> - }
> -}
> -
> -/* VC-2 11.3.6 - aspect_ratio() */
> -static void encode_aspect_ratio(VC2EncContext *s)
> -{
> - put_bits(&s->pb, 1, !s->strict_compliance);
> - if (!s->strict_compliance) {
> - AVCodecContext *avctx = s->avctx;
> - put_vc2_ue_uint(&s->pb, 0);
> - put_vc2_ue_uint(&s->pb, avctx->sample_aspect_ratio.num);
> - put_vc2_ue_uint(&s->pb, avctx->sample_aspect_ratio.den);
> - }
> -}
> -
> -/* VC-2 11.3.7 - clean_area() */
> -static void encode_clean_area(VC2EncContext *s)
> -{
> - put_bits(&s->pb, 1, 0);
> -}
> -
> -/* VC-2 11.3.8 - signal_range() */
> -static void encode_signal_range(VC2EncContext *s)
> -{
> - put_bits(&s->pb, 1, !s->strict_compliance);
> - if (!s->strict_compliance)
> - put_vc2_ue_uint(&s->pb, s->bpp_idx);
> -}
> -
> -/* VC-2 11.3.9 - color_spec() */
> -static void encode_color_spec(VC2EncContext *s)
> -{
> - AVCodecContext *avctx = s->avctx;
> - put_bits(&s->pb, 1, !s->strict_compliance);
> - if (!s->strict_compliance) {
> - int val;
> - put_vc2_ue_uint(&s->pb, 0);
> -
> - /* primaries */
> - put_bits(&s->pb, 1, 1);
> - if (avctx->color_primaries == AVCOL_PRI_BT470BG)
> - val = 2;
> - else if (avctx->color_primaries == AVCOL_PRI_SMPTE170M)
> - val = 1;
> - else if (avctx->color_primaries == AVCOL_PRI_SMPTE240M)
> - val = 1;
> - else
> - val = 0;
> - put_vc2_ue_uint(&s->pb, val);
> -
> - /* color matrix */
> - put_bits(&s->pb, 1, 1);
> - if (avctx->colorspace == AVCOL_SPC_RGB)
> - val = 3;
> - else if (avctx->colorspace == AVCOL_SPC_YCOCG)
> - val = 2;
> - else if (avctx->colorspace == AVCOL_SPC_BT470BG)
> - val = 1;
> - else
> - val = 0;
> - put_vc2_ue_uint(&s->pb, val);
> -
> - /* transfer function */
> - put_bits(&s->pb, 1, 1);
> - if (avctx->color_trc == AVCOL_TRC_LINEAR)
> - val = 2;
> - else if (avctx->color_trc == AVCOL_TRC_BT1361_ECG)
> - val = 1;
> - else
> - val = 0;
> - put_vc2_ue_uint(&s->pb, val);
> - }
> -}
> -
> -/* VC-2 11.3 - source_parameters() */
> -static void encode_source_params(VC2EncContext *s)
> -{
> - encode_frame_size(s);
> - encode_sample_fmt(s);
> - encode_scan_format(s);
> - encode_frame_rate(s);
> - encode_aspect_ratio(s);
> - encode_clean_area(s);
> - encode_signal_range(s);
> - encode_color_spec(s);
> -}
> -
> -/* VC-2 11 - sequence_header() */
> -static void encode_seq_header(VC2EncContext *s)
> -{
> - align_put_bits(&s->pb);
> - encode_parse_params(s);
> - put_vc2_ue_uint(&s->pb, s->base_vf);
> - encode_source_params(s);
> - put_vc2_ue_uint(&s->pb, s->interlaced); /* Frames or fields coding */
> -}
> -
> -/* VC-2 12.1 - picture_header() */
> -static void encode_picture_header(VC2EncContext *s)
> -{
> - align_put_bits(&s->pb);
> - put_bits32(&s->pb, s->picture_number++);
> -}
> -
> -/* VC-2 12.3.4.1 - slice_parameters() */
> -static void encode_slice_params(VC2EncContext *s)
> -{
> - put_vc2_ue_uint(&s->pb, s->num_x);
> - put_vc2_ue_uint(&s->pb, s->num_y);
> - put_vc2_ue_uint(&s->pb, s->prefix_bytes);
> - put_vc2_ue_uint(&s->pb, s->size_scaler);
> -}
> -
> -/* 1st idx = LL, second - vertical, third - horizontal, fourth - total */
> -static const uint8_t vc2_qm_col_tab[][4] = {
> - {20, 9, 15, 4},
> - { 0, 6, 6, 4},
> - { 0, 3, 3, 5},
> - { 0, 3, 5, 1},
> - { 0, 11, 10, 11}
> -};
> -
> -static const uint8_t vc2_qm_flat_tab[][4] = {
> - { 0, 0, 0, 0},
> - { 0, 0, 0, 0},
> - { 0, 0, 0, 0},
> - { 0, 0, 0, 0},
> - { 0, 0, 0, 0}
> -};
> -
> -static void init_quant_matrix(VC2EncContext *s)
> -{
> - int level, orientation;
> -
> - if (s->wavelet_depth <= 4 && s->quant_matrix == VC2_QM_DEF) {
> - s->custom_quant_matrix = 0;
> - for (level = 0; level < s->wavelet_depth; level++) {
> - s->quant[level][0] = ff_dirac_default_qmat[s->wavelet_idx][level][0];
> - s->quant[level][1] = ff_dirac_default_qmat[s->wavelet_idx][level][1];
> - s->quant[level][2] = ff_dirac_default_qmat[s->wavelet_idx][level][2];
> - s->quant[level][3] = ff_dirac_default_qmat[s->wavelet_idx][level][3];
> - }
> - return;
> - }
> -
> - s->custom_quant_matrix = 1;
> -
> - if (s->quant_matrix == VC2_QM_DEF) {
> - for (level = 0; level < s->wavelet_depth; level++) {
> - for (orientation = 0; orientation < 4; orientation++) {
> - if (level <= 3)
> - s->quant[level][orientation] = ff_dirac_default_qmat[s->wavelet_idx][level][orientation];
> - else
> - s->quant[level][orientation] = vc2_qm_col_tab[level][orientation];
> - }
> - }
> - } else if (s->quant_matrix == VC2_QM_COL) {
> - for (level = 0; level < s->wavelet_depth; level++) {
> - for (orientation = 0; orientation < 4; orientation++) {
> - s->quant[level][orientation] = vc2_qm_col_tab[level][orientation];
> - }
> - }
> - } else {
> - for (level = 0; level < s->wavelet_depth; level++) {
> - for (orientation = 0; orientation < 4; orientation++) {
> - s->quant[level][orientation] = vc2_qm_flat_tab[level][orientation];
> - }
> - }
> - }
> -}
> -
> -/* VC-2 12.3.4.2 - quant_matrix() */
> -static void encode_quant_matrix(VC2EncContext *s)
> -{
> - int level;
> - put_bits(&s->pb, 1, s->custom_quant_matrix);
> - if (s->custom_quant_matrix) {
> - put_vc2_ue_uint(&s->pb, s->quant[0][0]);
> - for (level = 0; level < s->wavelet_depth; level++) {
> - put_vc2_ue_uint(&s->pb, s->quant[level][1]);
> - put_vc2_ue_uint(&s->pb, s->quant[level][2]);
> - put_vc2_ue_uint(&s->pb, s->quant[level][3]);
> - }
> - }
> -}
> -
> -/* VC-2 12.3 - transform_parameters() */
> -static void encode_transform_params(VC2EncContext *s)
> -{
> - put_vc2_ue_uint(&s->pb, s->wavelet_idx);
> - put_vc2_ue_uint(&s->pb, s->wavelet_depth);
> -
> - encode_slice_params(s);
> - encode_quant_matrix(s);
> -}
> -
> -/* VC-2 12.2 - wavelet_transform() */
> -static void encode_wavelet_transform(VC2EncContext *s)
> -{
> - encode_transform_params(s);
> - align_put_bits(&s->pb);
> -}
> -
> -/* VC-2 12 - picture_parse() */
> -static void encode_picture_start(VC2EncContext *s)
> -{
> - align_put_bits(&s->pb);
> - encode_picture_header(s);
> - align_put_bits(&s->pb);
> - encode_wavelet_transform(s);
> -}
> -
> #define QUANT(c, mul, add, shift) (((mul) * (c) + (add)) >> (shift))
>
> /* VC-2 13.5.5.2 - slice_band() */
> @@ -558,6 +64,11 @@ static void encode_subband(const VC2EncContext *s, PutBitContext *pb,
> }
> }
>
> +static inline int count_vc2_ue_uint(uint32_t val)
> +{
> + return 2 * av_log2(val + 1) + 1;
> +}
> +
> static int count_hq_slice(SliceArgs *slice, int quant_idx)
> {
> int x, y;
> @@ -657,7 +168,7 @@ static int calc_slice_sizes(VC2EncContext *s)
> SliceArgs *enc_args = s->slice_args;
> SliceArgs *top_loc[SLICE_REDIST_TOTAL] = {NULL};
>
> - init_quant_matrix(s);
> + ff_vc2_init_quant_matrix(s, s->quant);
>
> for (slice_y = 0; slice_y < s->num_y; slice_y++) {
> for (slice_x = 0; slice_x < s->num_x; slice_x++) {
> @@ -782,7 +293,7 @@ static int encode_hq_slice(AVCodecContext *avctx, void *arg)
> }
>
> /* VC-2 13.5.1 - low_delay_transform_data() */
> -static int encode_slices(VC2EncContext *s)
> +static void encode_slices(VC2EncContext *s)
> {
> uint8_t *buf;
> int slice_x, slice_y, skip = 0;
> @@ -803,8 +314,6 @@ static int encode_slices(VC2EncContext *s)
> sizeof(SliceArgs));
>
> skip_put_bytes(&s->pb, skip);
> -
> - return 0;
> }
>
> /*
> @@ -902,7 +411,7 @@ static int dwt_plane(AVCodecContext *avctx, void *arg)
> }
>
> static int encode_frame(VC2EncContext *s, AVPacket *avpkt, const AVFrame *frame,
> - const char *aux_data, const int header_size, int field)
> + const int header_size, int field)
> {
> int i, ret;
> int64_t max_frame_bytes;
> @@ -929,25 +438,8 @@ static int encode_frame(VC2EncContext *s, AVPacket *avpkt, const AVFrame *frame,
> init_put_bits(&s->pb, avpkt->data, avpkt->size);
> }
>
> - /* Sequence header */
> - encode_parse_info(s, DIRAC_PCODE_SEQ_HEADER);
> - encode_seq_header(s);
> -
> - /* Encoder version */
> - if (aux_data) {
> - encode_parse_info(s, DIRAC_PCODE_AUX);
> - ff_put_string(&s->pb, aux_data, 1);
> - }
> -
> - /* Picture header */
> - encode_parse_info(s, DIRAC_PCODE_PICTURE_HQ);
> - encode_picture_start(s);
> -
> - /* Encode slices */
> - encode_slices(s);
> -
> - /* End sequence */
> - encode_parse_info(s, DIRAC_PCODE_END_SEQ);
> + /* Encode frame */
> + ff_vc2_encode_frame(s, encode_slices);
>
> return 0;
> }
> @@ -956,45 +448,20 @@ static av_cold int vc2_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
> const AVFrame *frame, int *got_packet)
> {
> int ret = 0;
> - int slice_ceil, sig_size = 256;
> VC2EncContext *s = avctx->priv_data;
> const int bitexact = avctx->flags & AV_CODEC_FLAG_BITEXACT;
> - const char *aux_data = bitexact ? "Lavc" : LIBAVCODEC_IDENT;
> const int aux_data_size = bitexact ? sizeof("Lavc") : sizeof(LIBAVCODEC_IDENT);
> const int header_size = 100 + aux_data_size;
> - int64_t r_bitrate = avctx->bit_rate >> (s->interlaced);
> -
> - s->avctx = avctx;
> - s->size_scaler = 2;
> - s->prefix_bytes = 0;
> - s->last_parse_code = 0;
> - s->next_parse_offset = 0;
> -
> - /* Rate control */
> - s->frame_max_bytes = (av_rescale(r_bitrate, s->avctx->time_base.num,
> - s->avctx->time_base.den) >> 3) - header_size;
> - s->slice_max_bytes = slice_ceil = av_rescale(s->frame_max_bytes, 1, s->num_x*s->num_y);
> -
> - /* Find an appropriate size scaler */
> - while (sig_size > 255) {
> - int r_size = SSIZE_ROUND(s->slice_max_bytes);
> - if (r_size > slice_ceil) {
> - s->slice_max_bytes -= r_size - slice_ceil;
> - r_size = SSIZE_ROUND(s->slice_max_bytes);
> - }
> - sig_size = r_size/s->size_scaler; /* Signalled slize size */
> - s->size_scaler <<= 1;
> - }
>
> - s->slice_min_bytes = s->slice_max_bytes - s->slice_max_bytes*(s->tolerance/100.0f);
> - if (s->slice_min_bytes < 0 || s->slice_max_bytes > INT_MAX >> 3)
> - return AVERROR(EINVAL);
> + ret = ff_vc2_frame_init_properties(avctx, s);
> + if (ret)
> + return ret;
>
> - ret = encode_frame(s, avpkt, frame, aux_data, header_size, s->interlaced);
> + ret = encode_frame(s, avpkt, frame, header_size, s->interlaced);
> if (ret)
> return ret;
> if (s->interlaced) {
> - ret = encode_frame(s, avpkt, frame, aux_data, header_size, 2);
> + ret = encode_frame(s, avpkt, frame, header_size, 2);
> if (ret)
> return ret;
> }
> @@ -1026,83 +493,13 @@ static av_cold int vc2_encode_end(AVCodecContext *avctx)
>
> static av_cold int vc2_encode_init(AVCodecContext *avctx)
> {
> - static AVOnce init_static_once = AV_ONCE_INIT;
> Plane *p;
> SubBand *b;
> - int i, level, o, shift;
> + int ret, i, level, o, shift;
> const AVPixFmtDescriptor *pixdesc;
> int depth;
> VC2EncContext *s = avctx->priv_data;
>
> - s->picture_number = 0;
> -
> - /* Total allowed quantization range */
> - s->q_ceil = DIRAC_MAX_QUANT_INDEX;
> -
> - s->ver.major = 2;
> - s->ver.minor = 0;
> - s->profile = 3;
> - s->level = 3;
> -
> - s->base_vf = -1;
> - s->strict_compliance = 1;
> -
> - s->q_avg = 0;
> - s->slice_max_bytes = 0;
> - s->slice_min_bytes = 0;
> -
> - /* Mark unknown as progressive */
> - s->interlaced = !((avctx->field_order == AV_FIELD_UNKNOWN) ||
> - (avctx->field_order == AV_FIELD_PROGRESSIVE));
> -
> - for (i = 0; i < base_video_fmts_len; i++) {
> - const VC2BaseVideoFormat *fmt = &base_video_fmts[i];
> - if (avctx->pix_fmt != fmt->pix_fmt)
> - continue;
> - if (avctx->time_base.num != fmt->time_base.num)
> - continue;
> - if (avctx->time_base.den != fmt->time_base.den)
> - continue;
> - if (avctx->width != fmt->width)
> - continue;
> - if (avctx->height != fmt->height)
> - continue;
> - if (s->interlaced != fmt->interlaced)
> - continue;
> - s->base_vf = i;
> - s->level = base_video_fmts[i].level;
> - break;
> - }
> -
> - if (s->interlaced)
> - av_log(avctx, AV_LOG_WARNING, "Interlacing enabled!\n");
> -
> - if ((s->slice_width & (s->slice_width - 1)) ||
> - (s->slice_height & (s->slice_height - 1))) {
> - av_log(avctx, AV_LOG_ERROR, "Slice size is not a power of two!\n");
> - return AVERROR(EINVAL);
> - }
> -
> - if ((s->slice_width > avctx->width) ||
> - (s->slice_height > avctx->height)) {
> - av_log(avctx, AV_LOG_ERROR, "Slice size is bigger than the image!\n");
> - return AVERROR(EINVAL);
> - }
> -
> - if (s->base_vf <= 0) {
> - if (avctx->strict_std_compliance < FF_COMPLIANCE_STRICT) {
> - s->strict_compliance = s->base_vf = 0;
> - av_log(avctx, AV_LOG_WARNING, "Format does not strictly comply with VC2 specs\n");
> - } else {
> - av_log(avctx, AV_LOG_WARNING, "Given format does not strictly comply with "
> - "the specifications, decrease strictness to use it.\n");
> - return AVERROR(EINVAL);
> - }
> - } else {
> - av_log(avctx, AV_LOG_INFO, "Selected base video format = %i (%s)\n",
> - s->base_vf, base_video_fmts[s->base_vf].name);
> - }
> -
> pixdesc = av_pix_fmt_desc_get(avctx->pix_fmt);
> /* Chroma subsampling */
> s->chroma_x_shift = pixdesc->log2_chroma_w;
> @@ -1110,47 +507,21 @@ static av_cold int vc2_encode_init(AVCodecContext *avctx)
>
> /* Bit depth and color range index */
> depth = pixdesc->comp[0].depth;
> - if (depth == 8 && avctx->color_range == AVCOL_RANGE_JPEG) {
> - s->bpp = 1;
> - s->bpp_idx = 1;
> - s->diff_offset = 128;
> - } else if (depth == 8 && (avctx->color_range == AVCOL_RANGE_MPEG ||
> - avctx->color_range == AVCOL_RANGE_UNSPECIFIED)) {
> - s->bpp = 1;
> - s->bpp_idx = 2;
> - s->diff_offset = 128;
> - } else if (depth == 10) {
> - s->bpp = 2;
> - s->bpp_idx = 3;
> - s->diff_offset = 512;
> - } else {
> - s->bpp = 2;
> - s->bpp_idx = 4;
> - s->diff_offset = 2048;
> - }
> +
> + /* Context initialization */
> + ret = ff_vc2_encode_init(avctx, depth);
> + if (ret < 0)
> + return ret;
>
> /* Planes initialization */
> for (i = 0; i < 3; i++) {
> - int w, h;
> p = &s->plane[i];
> - p->width = avctx->width >> (i ? s->chroma_x_shift : 0);
> - p->height = avctx->height >> (i ? s->chroma_y_shift : 0);
> - if (s->interlaced)
> - p->height >>= 1;
> - p->dwt_width = w = FFALIGN(p->width, (1 << s->wavelet_depth));
> - p->dwt_height = h = FFALIGN(p->height, (1 << s->wavelet_depth));
> - p->coef_stride = FFALIGN(p->dwt_width, 32);
> p->coef_buf = av_mallocz(p->coef_stride*p->dwt_height*sizeof(dwtcoef));
> if (!p->coef_buf)
> return AVERROR(ENOMEM);
> for (level = s->wavelet_depth-1; level >= 0; level--) {
> - w = w >> 1;
> - h = h >> 1;
> for (o = 0; o < 4; o++) {
> b = &p->band[level][o];
> - b->width = w;
> - b->height = h;
> - b->stride = p->coef_stride;
> shift = (o > 1)*b->height*b->stride + (o & 1)*b->width;
> b->buf = p->coef_buf + shift;
> }
> @@ -1164,10 +535,6 @@ static av_cold int vc2_encode_init(AVCodecContext *avctx)
> return AVERROR(ENOMEM);
> }
>
> - /* Slices */
> - s->num_x = s->plane[0].dwt_width/s->slice_width;
> - s->num_y = s->plane[0].dwt_height/s->slice_height;
> -
> s->slice_args = av_calloc(s->num_x*s->num_y, sizeof(SliceArgs));
> if (!s->slice_args)
> return AVERROR(ENOMEM);
> @@ -1189,8 +556,6 @@ static av_cold int vc2_encode_init(AVCodecContext *avctx)
> }
> }
>
> - ff_thread_once(&init_static_once, vc2_init_static_data);
> -
> return 0;
> }
>
> diff --git a/libavcodec/vc2enc_common.c b/libavcodec/vc2enc_common.c
> new file mode 100644
> index 0000000000..bd27fd3c40
> --- /dev/null
> +++ b/libavcodec/vc2enc_common.c
> @@ -0,0 +1,571 @@
> +/*
> +* Copyright (C) 2016 Open Broadcast Systems Ltd.
> +* Author 2016 Rostislav Pehlivanov <atomnuker at gmail.com>
> +*
> +* 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/pixdesc.h"
> +#include "libavutil/thread.h"
> +#include "vc2enc_common.h"
> +#include "version.h"
> +
> +typedef struct VC2BaseVideoFormat {
> + enum AVPixelFormat pix_fmt;
> + AVRational time_base;
> + int width, height;
> + uint8_t interlaced, level;
> + char name[13];
> +} VC2BaseVideoFormat;
> +
> +static const VC2BaseVideoFormat base_video_fmts[] = {
> + { 0 }, /* Custom format, here just to make indexing equal to base_vf */
> + { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 176, 120, 0, 1, "QSIF525" },
> + { AV_PIX_FMT_YUV420P, { 2, 25 }, 176, 144, 0, 1, "QCIF" },
> + { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 352, 240, 0, 1, "SIF525" },
> + { AV_PIX_FMT_YUV420P, { 2, 25 }, 352, 288, 0, 1, "CIF" },
> + { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 704, 480, 0, 1, "4SIF525" },
> + { AV_PIX_FMT_YUV420P, { 2, 25 }, 704, 576, 0, 1, "4CIF" },
> +
> + { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 720, 480, 1, 2, "SD480I-60" },
> + { AV_PIX_FMT_YUV422P10, { 1, 25 }, 720, 576, 1, 2, "SD576I-50" },
> +
> + { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 1280, 720, 0, 3, "HD720P-60" },
> + { AV_PIX_FMT_YUV422P10, { 1, 50 }, 1280, 720, 0, 3, "HD720P-50" },
> + { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 1920, 1080, 1, 3, "HD1080I-60" },
> + { AV_PIX_FMT_YUV422P10, { 1, 25 }, 1920, 1080, 1, 3, "HD1080I-50" },
> + { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 1920, 1080, 0, 3, "HD1080P-60" },
> + { AV_PIX_FMT_YUV422P10, { 1, 50 }, 1920, 1080, 0, 3, "HD1080P-50" },
> +
> + { AV_PIX_FMT_YUV444P12, { 1, 24 }, 2048, 1080, 0, 4, "DC2K" },
> + { AV_PIX_FMT_YUV444P12, { 1, 24 }, 4096, 2160, 0, 5, "DC4K" },
> +
> + { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 3840, 2160, 0, 6, "UHDTV 4K-60" },
> + { AV_PIX_FMT_YUV422P10, { 1, 50 }, 3840, 2160, 0, 6, "UHDTV 4K-50" },
> +
> + { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 7680, 4320, 0, 7, "UHDTV 8K-60" },
> + { AV_PIX_FMT_YUV422P10, { 1, 50 }, 7680, 4320, 0, 7, "UHDTV 8K-50" },
> +
> + { AV_PIX_FMT_YUV422P10, { 1001, 24000 }, 1920, 1080, 0, 3, "HD1080P-24" },
> + { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 720, 486, 1, 2, "SD Pro486" },
> +};
> +static const int base_video_fmts_len = FF_ARRAY_ELEMS(base_video_fmts);
> +
> +/// x_k x_{k-1} ... x_0 -> 0 x_k 0 x_{k - 1} ... 0 x_0
> +uint16_t interleaved_ue_golomb_tab[256];
> +/// 1 x_{k-1} ... x_0 -> 0 0 0 x_{k - 1} ... 0 x_0
> +uint16_t top_interleaved_ue_golomb_tab[256];
> +/// 1 x_{k-1} ... x_0 -> 2 * k
> +uint8_t golomb_len_tab[256];
> +
> +static av_cold void vc2_init_static_data(void)
> +{
> + interleaved_ue_golomb_tab[1] = 1;
> + for (unsigned i = 2; i < 256; ++i) {
> + golomb_len_tab[i] = golomb_len_tab[i >> 1] + 2;
> + interleaved_ue_golomb_tab[i] = (interleaved_ue_golomb_tab[i >> 1] << 2) | (i & 1);
> + top_interleaved_ue_golomb_tab[i] = interleaved_ue_golomb_tab[i] ^ (1 << golomb_len_tab[i]);
> + }
> +}
> +
> +static void put_vc2_ue_uint(PutBitContext *pb, uint32_t val)
> +{
> + put_vc2_ue_uint_inline(pb, val);
> +}
> +
> +/* VC-2 10.4 - parse_info() */
> +static void encode_parse_info(VC2EncContext *s, enum DiracParseCodes pcode)
> +{
> + uint32_t cur_pos, dist;
> +
> + align_put_bits(&s->pb);
> +
> + cur_pos = put_bytes_count(&s->pb, 0);
> +
> + /* Magic string */
> + ff_put_string(&s->pb, "BBCD", 0);
> +
> + /* Parse code */
> + put_bits(&s->pb, 8, pcode);
> +
> + /* Next parse offset */
> + dist = cur_pos - s->next_parse_offset;
> + AV_WB32(s->pb.buf + s->next_parse_offset + 5, dist);
> + s->next_parse_offset = cur_pos;
> + put_bits32(&s->pb, pcode == DIRAC_PCODE_END_SEQ ? 13 : 0);
> +
> + cur_pos = put_bytes_count(&s->pb, 0);
> +
> + /* Last parse offset */
> + put_bits32(&s->pb, s->last_parse_code == DIRAC_PCODE_END_SEQ ? 13 : dist);
> +
> + s->last_parse_code = pcode;
> +}
> +
> +/* VC-2 11.1 - parse_parameters()
> +* The level dictates what the decoder should expect in terms of resolution
> +* and allows it to quickly reject whatever it can't support. Remember,
> +* this codec kinda targets cheapo FPGAs without much memory. Unfortunately
> +* it also limits us greatly in our choice of formats, hence the flag to disable
> +* strict_compliance */
> +static void encode_parse_params(VC2EncContext *s)
> +{
> + put_vc2_ue_uint(&s->pb, s->ver.major); /* VC-2 demands this to be 2 */
> + put_vc2_ue_uint(&s->pb, s->ver.minor); /* ^^ and this to be 0 */
> + put_vc2_ue_uint(&s->pb, s->profile); /* 3 to signal HQ profile */
> + put_vc2_ue_uint(&s->pb, s->level); /* 3 - 1080/720, 6 - 4K */
> +}
> +
> +/* VC-2 11.3 - frame_size() */
> +static void encode_frame_size(VC2EncContext *s)
> +{
> + put_bits(&s->pb, 1, !s->strict_compliance);
> + if (!s->strict_compliance) {
> + AVCodecContext *avctx = s->avctx;
> + put_vc2_ue_uint(&s->pb, avctx->width);
> + put_vc2_ue_uint(&s->pb, avctx->height);
> + }
> +}
> +
> +/* VC-2 11.3.3 - color_diff_sampling_format() */
> +static void encode_sample_fmt(VC2EncContext *s)
> +{
> + put_bits(&s->pb, 1, !s->strict_compliance);
> + if (!s->strict_compliance) {
> + int idx;
> + if (s->chroma_x_shift == 1 && s->chroma_y_shift == 0)
> + idx = 1; /* 422 */
> + else if (s->chroma_x_shift == 1 && s->chroma_y_shift == 1)
> + idx = 2; /* 420 */
> + else
> + idx = 0; /* 444 */
> + put_vc2_ue_uint(&s->pb, idx);
> + }
> +}
> +
> +/* VC-2 11.3.4 - scan_format() */
> +static void encode_scan_format(VC2EncContext *s)
> +{
> + put_bits(&s->pb, 1, !s->strict_compliance);
> + if (!s->strict_compliance)
> + put_vc2_ue_uint(&s->pb, s->interlaced);
> +}
> +
> +/* VC-2 11.3.5 - frame_rate() */
> +static void encode_frame_rate(VC2EncContext *s)
> +{
> + put_bits(&s->pb, 1, !s->strict_compliance);
> + if (!s->strict_compliance) {
> + AVCodecContext *avctx = s->avctx;
> + put_vc2_ue_uint(&s->pb, 0);
> + put_vc2_ue_uint(&s->pb, avctx->time_base.den);
> + put_vc2_ue_uint(&s->pb, avctx->time_base.num);
> + }
> +}
> +
> +/* VC-2 11.3.6 - aspect_ratio() */
> +static void encode_aspect_ratio(VC2EncContext *s)
> +{
> + put_bits(&s->pb, 1, !s->strict_compliance);
> + if (!s->strict_compliance) {
> + AVCodecContext *avctx = s->avctx;
> + put_vc2_ue_uint(&s->pb, 0);
> + put_vc2_ue_uint(&s->pb, avctx->sample_aspect_ratio.num);
> + put_vc2_ue_uint(&s->pb, avctx->sample_aspect_ratio.den);
> + }
> +}
> +
> +/* VC-2 11.3.7 - clean_area() */
> +static void encode_clean_area(VC2EncContext *s)
> +{
> + put_bits(&s->pb, 1, 0);
> +}
> +
> +/* VC-2 11.3.8 - signal_range() */
> +static void encode_signal_range(VC2EncContext *s)
> +{
> + put_bits(&s->pb, 1, !s->strict_compliance);
> + if (!s->strict_compliance)
> + put_vc2_ue_uint(&s->pb, s->bpp_idx);
> +}
> +
> +/* VC-2 11.3.9 - color_spec() */
> +static void encode_color_spec(VC2EncContext *s)
> +{
> + AVCodecContext *avctx = s->avctx;
> + put_bits(&s->pb, 1, !s->strict_compliance);
> + if (!s->strict_compliance) {
> + int val;
> + put_vc2_ue_uint(&s->pb, 0);
> +
> + /* primaries */
> + put_bits(&s->pb, 1, 1);
> + if (avctx->color_primaries == AVCOL_PRI_BT470BG)
> + val = 2;
> + else if (avctx->color_primaries == AVCOL_PRI_SMPTE170M)
> + val = 1;
> + else if (avctx->color_primaries == AVCOL_PRI_SMPTE240M)
> + val = 1;
> + else
> + val = 0;
> + put_vc2_ue_uint(&s->pb, val);
> +
> + /* color matrix */
> + put_bits(&s->pb, 1, 1);
> + if (avctx->colorspace == AVCOL_SPC_RGB)
> + val = 3;
> + else if (avctx->colorspace == AVCOL_SPC_YCOCG)
> + val = 2;
> + else if (avctx->colorspace == AVCOL_SPC_BT470BG)
> + val = 1;
> + else
> + val = 0;
> + put_vc2_ue_uint(&s->pb, val);
> +
> + /* transfer function */
> + put_bits(&s->pb, 1, 1);
> + if (avctx->color_trc == AVCOL_TRC_LINEAR)
> + val = 2;
> + else if (avctx->color_trc == AVCOL_TRC_BT1361_ECG)
> + val = 1;
> + else
> + val = 0;
> + put_vc2_ue_uint(&s->pb, val);
> + }
> +}
> +
> +/* VC-2 11.3 - source_parameters() */
> +static void encode_source_params(VC2EncContext *s)
> +{
> + encode_frame_size(s);
> + encode_sample_fmt(s);
> + encode_scan_format(s);
> + encode_frame_rate(s);
> + encode_aspect_ratio(s);
> + encode_clean_area(s);
> + encode_signal_range(s);
> + encode_color_spec(s);
> +}
> +
> +/* VC-2 11 - sequence_header() */
> +static void encode_seq_header(VC2EncContext *s)
> +{
> + align_put_bits(&s->pb);
> + encode_parse_params(s);
> + put_vc2_ue_uint(&s->pb, s->base_vf);
> + encode_source_params(s);
> + put_vc2_ue_uint(&s->pb, s->interlaced); /* Frames or fields coding */
> +}
> +
> +/* VC-2 12.1 - picture_header() */
> +static void encode_picture_header(VC2EncContext *s)
> +{
> + align_put_bits(&s->pb);
> + put_bits32(&s->pb, s->picture_number++);
> +}
> +
> +/* VC-2 12.3.4.1 - slice_parameters() */
> +static void encode_slice_params(VC2EncContext *s)
> +{
> + put_vc2_ue_uint(&s->pb, s->num_x);
> + put_vc2_ue_uint(&s->pb, s->num_y);
> + put_vc2_ue_uint(&s->pb, s->prefix_bytes);
> + put_vc2_ue_uint(&s->pb, s->size_scaler);
> +}
> +
> +/* 1st idx = LL, second - vertical, third - horizontal, fourth - total */
> +static const uint8_t vc2_qm_col_tab[][4] = {
> + {20, 9, 15, 4},
> + { 0, 6, 6, 4},
> + { 0, 3, 3, 5},
> + { 0, 3, 5, 1},
> + { 0, 11, 10, 11}
> +};
> +
> +static const uint8_t vc2_qm_flat_tab[][4] = {
> + { 0, 0, 0, 0},
> + { 0, 0, 0, 0},
> + { 0, 0, 0, 0},
> + { 0, 0, 0, 0},
> + { 0, 0, 0, 0}
> +};
> +
> +void ff_vc2_init_quant_matrix(VC2EncContext *s, uint8_t quant[MAX_DWT_LEVELS][4])
> +{
> + int level, orientation;
> +
> + if (s->wavelet_depth <= 4 && s->quant_matrix == VC2_QM_DEF) {
> + s->custom_quant_matrix = 0;
> + for (level = 0; level < s->wavelet_depth; level++) {
> + quant[level][0] = ff_dirac_default_qmat[s->wavelet_idx][level][0];
> + quant[level][1] = ff_dirac_default_qmat[s->wavelet_idx][level][1];
> + quant[level][2] = ff_dirac_default_qmat[s->wavelet_idx][level][2];
> + quant[level][3] = ff_dirac_default_qmat[s->wavelet_idx][level][3];
> + }
> + return;
> + }
> +
> + s->custom_quant_matrix = 1;
> +
> + if (s->quant_matrix == VC2_QM_DEF) {
> + for (level = 0; level < s->wavelet_depth; level++) {
> + for (orientation = 0; orientation < 4; orientation++) {
> + if (level <= 3)
> + quant[level][orientation] = ff_dirac_default_qmat[s->wavelet_idx][level][orientation];
> + else
> + quant[level][orientation] = vc2_qm_col_tab[level][orientation];
> + }
> + }
> + } else if (s->quant_matrix == VC2_QM_COL) {
> + for (level = 0; level < s->wavelet_depth; level++) {
> + for (orientation = 0; orientation < 4; orientation++) {
> + quant[level][orientation] = vc2_qm_col_tab[level][orientation];
> + }
> + }
> + } else {
> + for (level = 0; level < s->wavelet_depth; level++) {
> + for (orientation = 0; orientation < 4; orientation++) {
> + quant[level][orientation] = vc2_qm_flat_tab[level][orientation];
> + }
> + }
> + }
> +}
> +
> +/* VC-2 12.3.4.2 - quant_matrix() */
> +static void encode_quant_matrix(VC2EncContext *s)
> +{
> + int level;
> + put_bits(&s->pb, 1, s->custom_quant_matrix);
> + if (s->custom_quant_matrix) {
> + put_vc2_ue_uint(&s->pb, s->quant[0][0]);
> + for (level = 0; level < s->wavelet_depth; level++) {
> + put_vc2_ue_uint(&s->pb, s->quant[level][1]);
> + put_vc2_ue_uint(&s->pb, s->quant[level][2]);
> + put_vc2_ue_uint(&s->pb, s->quant[level][3]);
> + }
> + }
> +}
> +
> +/* VC-2 12.3 - transform_parameters() */
> +static void encode_transform_params(VC2EncContext *s)
> +{
> + put_vc2_ue_uint(&s->pb, s->wavelet_idx);
> + put_vc2_ue_uint(&s->pb, s->wavelet_depth);
> +
> + encode_slice_params(s);
> + encode_quant_matrix(s);
> +}
> +
> +/* VC-2 12.2 - wavelet_transform() */
> +static void encode_wavelet_transform(VC2EncContext *s)
> +{
> + encode_transform_params(s);
> + align_put_bits(&s->pb);
> +}
> +
> +/* VC-2 12 - picture_parse() */
> +static void encode_picture_start(VC2EncContext *s)
> +{
> + align_put_bits(&s->pb);
> + encode_picture_header(s);
> + align_put_bits(&s->pb);
> + encode_wavelet_transform(s);
> +}
> +
> +int ff_vc2_encode_init(AVCodecContext *avctx, int depth)
> +{
> + static AVOnce init_static_once = AV_ONCE_INIT;
> + int i, level, o;
> + SubBand *b;
> + Plane *p;
> + VC2EncContext *s = avctx->priv_data;
> +
> + s->picture_number = 0;
> +
> + /* Total allowed quantization range */
> + s->q_ceil = DIRAC_MAX_QUANT_INDEX;
> +
> + s->ver.major = 2;
> + s->ver.minor = 0;
> + s->profile = 3;
> + s->level = 3;
> +
> + s->base_vf = -1;
> + s->strict_compliance = 1;
> +
> + s->q_avg = 0;
> + s->slice_max_bytes = 0;
> + s->slice_min_bytes = 0;
> +
> + /* Mark unknown as progressive */
> + s->interlaced = !((avctx->field_order == AV_FIELD_UNKNOWN) ||
> + (avctx->field_order == AV_FIELD_PROGRESSIVE));
> +
> + for (i = 0; i < base_video_fmts_len; i++) {
> + const VC2BaseVideoFormat *fmt = &base_video_fmts[i];
> + if (avctx->pix_fmt != fmt->pix_fmt || avctx->time_base.num != fmt->time_base.num ||
> + avctx->time_base.den != fmt->time_base.den || avctx->width != fmt->width ||
> + avctx->height != fmt->height || s->interlaced != fmt->interlaced)
> + continue;
> + s->base_vf = i;
> + s->level = base_video_fmts[i].level;
> + break;
> + }
> +
> + if (s->interlaced)
> + av_log(avctx, AV_LOG_WARNING, "Interlacing enabled!\n");
> +
> + if ((s->slice_width & (s->slice_width - 1)) ||
> + (s->slice_height & (s->slice_height - 1))) {
> + av_log(avctx, AV_LOG_ERROR, "Slice size is not a power of two!\n");
> + return AVERROR(EINVAL);
> + }
> +
> + if ((s->slice_width > avctx->width) ||
> + (s->slice_height > avctx->height)) {
> + av_log(avctx, AV_LOG_ERROR, "Slice size is bigger than the image!\n");
> + return AVERROR(EINVAL);
> + }
> +
> + if (s->base_vf <= 0) {
> + if (avctx->strict_std_compliance < FF_COMPLIANCE_STRICT) {
> + s->strict_compliance = s->base_vf = 0;
> + av_log(avctx, AV_LOG_WARNING, "Format does not strictly comply with VC2 specs\n");
> + } else {
> + av_log(avctx, AV_LOG_WARNING, "Given format does not strictly comply with "
> + "the specifications, decrease strictness to use it.\n");
> + return AVERROR(EINVAL);
> + }
> + } else {
> + av_log(avctx, AV_LOG_INFO, "Selected base video format = %i (%s)\n",
> + s->base_vf, base_video_fmts[s->base_vf].name);
> + }
> +
> + /* Bit depth and color range index */
> + if (depth == 8 && avctx->color_range == AVCOL_RANGE_JPEG) {
> + s->bpp = 1;
> + s->bpp_idx = 1;
> + s->diff_offset = 128;
> + } else if (depth == 8 && (avctx->color_range == AVCOL_RANGE_MPEG ||
> + avctx->color_range == AVCOL_RANGE_UNSPECIFIED)) {
> + s->bpp = 1;
> + s->bpp_idx = 2;
> + s->diff_offset = 128;
> + } else if (depth == 10) {
> + s->bpp = 2;
> + s->bpp_idx = 3;
> + s->diff_offset = 512;
> + } else {
> + s->bpp = 2;
> + s->bpp_idx = 4;
> + s->diff_offset = 2048;
> + }
> +
> + /* Planes initialization */
> + for (i = 0; i < 3; i++) {
> + int w, h;
> + p = &s->plane[i];
> + p->width = avctx->width >> (i ? s->chroma_x_shift : 0);
> + p->height = avctx->height >> (i ? s->chroma_y_shift : 0);
> + if (s->interlaced)
> + p->height >>= 1;
> + p->dwt_width = w = FFALIGN(p->width, (1 << s->wavelet_depth));
> + p->dwt_height = h = FFALIGN(p->height, (1 << s->wavelet_depth));
> + p->coef_stride = FFALIGN(p->dwt_width, 32);
> + for (level = s->wavelet_depth-1; level >= 0; level--) {
> + w = w >> 1;
> + h = h >> 1;
> + for (o = 0; o < 4; o++) {
> + b = &p->band[level][o];
> + b->width = w;
> + b->height = h;
> + b->stride = p->coef_stride;
> + }
> + }
> + }
> +
> + /* Slices */
> + s->num_x = s->plane[0].dwt_width/s->slice_width;
> + s->num_y = s->plane[0].dwt_height/s->slice_height;
> +
> + ff_thread_once(&init_static_once, vc2_init_static_data);
> +
> + return 0;
> +}
> +
> +int ff_vc2_frame_init_properties(AVCodecContext *avctx, VC2EncContext *s)
> +{
> + int slice_ceil, sig_size = 256;
> + const int bitexact = avctx->flags & AV_CODEC_FLAG_BITEXACT;
> + const int aux_data_size = bitexact ? sizeof("Lavc") : sizeof(LIBAVCODEC_IDENT);
> + const int header_size = 100 + aux_data_size;
> + int64_t r_bitrate = avctx->bit_rate >> (s->interlaced);
> +
> + s->avctx = avctx;
> + s->size_scaler = 2;
> + s->prefix_bytes = 0;
> + s->last_parse_code = 0;
> + s->next_parse_offset = 0;
> +
> + /* Rate control */
> + s->frame_max_bytes = (av_rescale(r_bitrate, s->avctx->time_base.num,
> + s->avctx->time_base.den) >> 3) - header_size;
> + s->slice_max_bytes = slice_ceil = av_rescale(s->frame_max_bytes, 1, s->num_x * s->num_y);
> +
> + /* Find an appropriate size scaler */
> + while (sig_size > 255) {
> + int r_size = SSIZE_ROUND(s->slice_max_bytes);
> + if (r_size > slice_ceil) {
> + s->slice_max_bytes -= r_size - slice_ceil;
> + r_size = SSIZE_ROUND(s->slice_max_bytes);
> + }
> + sig_size = r_size/s->size_scaler; /* Signalled slize size */
> + s->size_scaler <<= 1;
> + }
> +
> + s->slice_min_bytes = s->slice_max_bytes - s->slice_max_bytes*(s->tolerance/100.0f);
> + if (s->slice_min_bytes < 0 || s->slice_max_bytes > INT_MAX >> 3)
> + return AVERROR(EINVAL);
> +
> + return 0;
> +}
> +
> +void ff_vc2_encode_frame(VC2EncContext *s, void(*encode_slices)(VC2EncContext*))
> +{
> + const int bitexact = s->avctx->flags & AV_CODEC_FLAG_BITEXACT;
> + const char *aux_data = bitexact ? "Lavc" : LIBAVCODEC_IDENT;
> +
> + /* Sequence header */
> + encode_parse_info(s, DIRAC_PCODE_SEQ_HEADER);
> + encode_seq_header(s);
> +
> + /* Encoder version */
> + if (aux_data) {
> + encode_parse_info(s, DIRAC_PCODE_AUX);
> + ff_put_string(&s->pb, aux_data, 1);
> + }
> +
> + /* Picture header */
> + encode_parse_info(s, DIRAC_PCODE_PICTURE_HQ);
> + encode_picture_start(s);
> +
> + /* Encode slices */
> + encode_slices(s);
I know that I told you to factor out writing the actual frame, but I was
not aware at the time that encode_slices() will differ between the
encoders. Better add two functions: ff_vc2_write_frame_header(), doing
everything before encode_slices() here and one
ff_vc2_write_sequence_end() for the end-of-sequence below.
> +
> + /* End sequence */
> + encode_parse_info(s, DIRAC_PCODE_END_SEQ);
> +}
> \ No newline at end of file
> diff --git a/libavcodec/vc2enc_common.h b/libavcodec/vc2enc_common.h
> new file mode 100644
> index 0000000000..0466869943
> --- /dev/null
> +++ b/libavcodec/vc2enc_common.h
> @@ -0,0 +1,178 @@
> +/*
> +* Copyright (C) 2016 Open Broadcast Systems Ltd.
> +* Author 2016 Rostislav Pehlivanov <atomnuker at gmail.com>
> +*
> +* 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_VC2ENC_COMMON_H
> +#define AVCODEC_VC2ENC_COMMON_H
> +
> +#include "avcodec.h"
> +#include "dirac.h"
> +#include "put_bits.h"
> +#include "libavutil/attributes_internal.h"
> +
> +#include "vc2enc_dwt.h"
> +#include "diractab.h"
> +
> +/* The limited size resolution of each slice forces us to do this */
> +#define SSIZE_ROUND(b) (FFALIGN((b), s->size_scaler) + 4 + s->prefix_bytes)
> +
> +FF_VISIBILITY_PUSH_HIDDEN
> +
> +enum VC2_QM {
> + VC2_QM_DEF = 0,
> + VC2_QM_COL,
> + VC2_QM_FLAT,
> +
> + VC2_QM_NB
> +};
> +
> +typedef struct SubBand {
> + dwtcoef *buf;
> + ptrdiff_t stride;
> + int width;
> + int height;
> + int shift;
> +} SubBand;
> +
> +typedef struct Plane {
> + SubBand band[MAX_DWT_LEVELS][4];
> + dwtcoef *coef_buf;
> + int width;
> + int height;
> + int dwt_width;
> + int dwt_height;
> + ptrdiff_t coef_stride;
> +} Plane;
> +
> +typedef struct SliceArgs {
> + const struct VC2EncContext *ctx;
> + union {
> + int cache[DIRAC_MAX_QUANT_INDEX];
> + uint8_t *buf;
> + };
> + int x;
> + int y;
> + int quant_idx;
> + int bits_ceil;
> + int bits_floor;
> + int bytes;
> +} SliceArgs;
This structure is only used by the software encoder; use a "struct
SliceArgs *" below and keep this structure in vc2enc.c. This will allow
to avoid the diractab inclusion (if I am not mistaken).
> +
> +typedef struct TransformArgs {
> + const struct VC2EncContext *ctx;
> + Plane *plane;
> + const void *idata;
> + ptrdiff_t istride;
> + int field;
> + VC2TransformContext t;
> +} TransformArgs;
> +
> +typedef struct VC2EncContext {
> + AVClass *av_class;
> + PutBitContext pb;
> + Plane plane[3];
> + AVCodecContext *avctx;
> + DiracVersionInfo ver;
> +
> + SliceArgs *slice_args;
> + TransformArgs transform_args[3];
> +
> + /* For conversion from unsigned pixel values to signed */
> + int diff_offset;
> + int bpp;
> + int bpp_idx;
> +
> + /* Picture number */
> + uint32_t picture_number;
> +
> + /* Base video format */
> + int base_vf;
> + int level;
> + int profile;
> +
> + /* Quantization matrix */
> + uint8_t quant[MAX_DWT_LEVELS][4];
> + int custom_quant_matrix;
> +
> + /* Division LUT */
> + uint32_t qmagic_lut[116][2];
> +
> + int num_x; /* #slices horizontally */
> + int num_y; /* #slices vertically */
> + int prefix_bytes;
> + int size_scaler;
> + int chroma_x_shift;
> + int chroma_y_shift;
> +
> + /* Rate control stuff */
> + int frame_max_bytes;
> + int slice_max_bytes;
> + int slice_min_bytes;
> + int q_ceil;
> + int q_avg;
> +
> + /* Options */
> + double tolerance;
> + int wavelet_idx;
> + int wavelet_depth;
> + int strict_compliance;
> + int slice_height;
> + int slice_width;
> + int interlaced;
> + enum VC2_QM quant_matrix;
> +
> + /* Parse code state */
> + uint32_t next_parse_offset;
> + enum DiracParseCodes last_parse_code;
> +} VC2EncContext;
> +
> +extern uint16_t interleaved_ue_golomb_tab[256];
> +extern uint16_t top_interleaved_ue_golomb_tab[256];
> +extern uint8_t golomb_len_tab[256];
Missing ff_ prefix.
> +
> +static inline void put_vc2_ue_uint_inline(PutBitContext *pb, uint32_t val)
> +{
> + uint64_t pbits = 1;
> + int bits = 1;
> +
> + ++val;
> +
> + while (val >> 8) {
> + pbits |= (uint64_t)interleaved_ue_golomb_tab[val & 0xff] << bits;
> + val >>= 8;
> + bits += 16;
> + }
> + pbits |= (uint64_t)top_interleaved_ue_golomb_tab[val] << bits;
> + bits += golomb_len_tab[val];
> +
> + put_bits63(pb, bits, pbits);
> +}
> +
> +int ff_vc2_encode_init(AVCodecContext *avctx, int depth);
> +
> +int ff_vc2_frame_init_properties(AVCodecContext *avctx, VC2EncContext *s);
> +
> +void ff_vc2_init_quant_matrix(VC2EncContext *s, uint8_t quant[MAX_DWT_LEVELS][4]);
> +
> +void ff_vc2_encode_frame(VC2EncContext *s, void(*encode_slices)(VC2EncContext*));
> +
> +FF_VISIBILITY_POP_HIDDEN
> +
> +#endif
More information about the ffmpeg-devel
mailing list