[FFmpeg-devel] [PATCH v10 4/6] lavc: Implement Dolby Vision RPU parsing
Andreas Rheinhardt
andreas.rheinhardt at outlook.com
Tue Jan 4 11:13:03 EET 2022
Niklas Haas:
> From: Niklas Haas <git at haasn.dev>
>
> Based on a mixture of guesswork, partial documentation in patents, and
> reverse engineering of real-world samples. Confirmed working for all the
> samples I've thrown at it.
>
> Contains some annoying machinery to persist these values in between
> frames, which is needed in theory even though I've never actually seen a
> sample that relies on it in practice. May or may not work.
>
> Since the distinction matters greatly for parsing the color matrix
> values, this includes a small helper function to guess the right profile
> from the RPU itself in case the user has forgotten to forward the dovi
> configuration record to the decoder. (Which in practice, only ffmpeg.c
> and ffplay do..)
>
> Notable omissions / deviations:
> - CRC32 verification. This is based on the MPEG2 CRC32 type, which is
> similar to IEEE CRC32 but apparently different in subtle enough ways
> that I could not get it to pass verification no matter what parameters
> I fed to av_crc. It's possible the code needs some changes.
> - Linear interpolation support. Nothing documents this (beyond its
> existence) and no samples use it, so impossible to implement.
> - All of the extension metadata blocks, but these contain values that
> seem largely congruent with ST2094, HDR10, or other existing forms of
> side data, so I will defer parsing/attaching them to a future commit.
> - The patent describes a mechanism for predicting coefficients from
> previous RPUs, but the bit for the flag whether to use the
> prediction deltas or signal entirely new coefficients does not seem to
> be present in actual RPUs, so we ignore this subsystem entirely.
> - In the patent's spec, the NLQ subsystem also loops over
> num_nlq_pivots, but even in the patent the number is hard-coded to one
> iteration rather than signalled. So we only store one set of coefs.
>
> Heavily influenced by https://github.com/quietvoid/dovi_tool
> Documentation drawn from US Patent 10,701,399 B2 and ETSI GS CCM 001
>
> Signed-off-by: Niklas Haas <git at haasn.dev>
> ---
> configure | 2 +
> libavcodec/Makefile | 1 +
> libavcodec/dovi_rpu.c | 449 ++++++++++++++++++++++++++++++++++++++++++
> libavcodec/dovi_rpu.h | 87 ++++++++
> 4 files changed, 539 insertions(+)
> create mode 100644 libavcodec/dovi_rpu.c
> create mode 100644 libavcodec/dovi_rpu.h
>
> diff --git a/configure b/configure
> index 6ad70b9f7b..8303e1329e 100755
> --- a/configure
> +++ b/configure
> @@ -2434,6 +2434,7 @@ CONFIG_EXTRA="
> cbs_vp9
> dirac_parse
> dnn
> + dovi_rpu
> dvprofile
> exif
> faandct
> @@ -2706,6 +2707,7 @@ cbs_mpeg2_select="cbs"
> cbs_vp9_select="cbs"
> dct_select="rdft"
> dirac_parse_select="golomb"
> +dovi_rpu_select="golomb"
> dnn_suggest="libtensorflow libopenvino"
> dnn_deps="avformat swscale"
> error_resilience_select="me_cmp"
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 9577062eec..ceecdf05e1 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -77,6 +77,7 @@ OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
> OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
> OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o
> OBJS-$(CONFIG_DCT) += dct.o dct32_fixed.o dct32_float.o
> +OBJS-$(CONFIG_DOVI_RPU) += dovi_rpu.o
> OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o
> OBJS-$(CONFIG_EXIF) += exif.o tiff_common.o
> OBJS-$(CONFIG_FAANDCT) += faandct.o
> diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c
> new file mode 100644
> index 0000000000..a87562c8a3
> --- /dev/null
> +++ b/libavcodec/dovi_rpu.c
> @@ -0,0 +1,449 @@
> +/*
> + * Dolby Vision RPU decoder
> + *
> + * Copyright (C) 2021 Jan Ekström
> + * Copyright (C) 2021 Niklas Haas
> + *
> + * 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/buffer.h"
> +
> +#include "dovi_rpu.h"
> +#include "golomb.h"
> +#include "get_bits.h"
> +
> +enum {
> + RPU_COEFF_FIXED = 0,
> + RPU_COEFF_FLOAT = 1,
> +};
> +
> +/**
> + * Private contents of vdr_ref.
> + */
> +typedef struct DOVIVdrRef {
> + AVDOVIDataMapping mapping;
> + AVDOVIColorMetadata color;
> +} DOVIVdrRef;
> +
> +void ff_dovi_ctx_unref(DOVIContext *s)
> +{
> + for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr_ref); i++)
> + av_buffer_unref(&s->vdr_ref[i]);
> +
> + *s = (DOVIContext) {
> + .logctx = s->logctx,
> + };
This could be simplified to ff_dovi_ctx_flush(); s->dv_profile = 0;
> +}
> +
> +void ff_dovi_ctx_flush(DOVIContext *s)
> +{
> + for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr_ref); i++)
> + av_buffer_unref(&s->vdr_ref[i]);
> +
> + *s = (DOVIContext) {
> + .logctx = s->logctx,
> + .dv_profile = s->dv_profile,
> + };
> +}
> +
> +int ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0)
> +{
> + int ret;
> + s->logctx = s0->logctx;
> + s->mapping = s0->mapping;
> + s->color = s0->color;
> + s->dv_profile = s0->dv_profile;
> + for (int i = 0; i < DOVI_MAX_DM_ID; i++) {
> + if ((ret = av_buffer_replace(&s->vdr_ref[i], s0->vdr_ref[i])) < 0)
> + goto fail;
> + }
> +
> + return 0;
> +
> +fail:
> + ff_dovi_ctx_unref(s);
> + return ret;
> +}
> +
> +void ff_dovi_update_cfg(DOVIContext *s, const AVDOVIDecoderConfigurationRecord *cfg)
> +{
> + if (!cfg)
> + return;
> +
> + s->dv_profile = cfg->dv_profile;
> +}
> +
> +int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
> +{
> + AVFrameSideData *sd;
> + AVBufferRef *buf;
> + AVDOVIMetadata *dovi;
> + size_t dovi_size;
> +
> + if (!s->mapping || !s->color)
> + return 0; /* incomplete dovi metadata */
> +
> + dovi = av_dovi_metadata_alloc(&dovi_size);
> + if (!dovi)
> + return AVERROR(ENOMEM);
> +
> + buf = av_buffer_create((uint8_t *) dovi, dovi_size, NULL, NULL, 0);
> + if (!buf) {
> + av_free(dovi);
> + return AVERROR(ENOMEM);
> + }
> +
> + sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_METADATA, buf);
> + if (!sd) {
> + av_buffer_unref(&buf);
> + return AVERROR(ENOMEM);
> + }
> +
> + /* Copy only the parts of these structs known to us at compiler-time. */
> +#define COPY(t, a, b, last) memcpy(a, b, offsetof(t, last) + sizeof((b)->last))
> + COPY(AVDOVIRpuDataHeader, av_dovi_get_header(dovi), &s->header, disable_residual_flag);
> + COPY(AVDOVIDataMapping, av_dovi_get_mapping(dovi), s->mapping, nlq[2].linear_deadzone_threshold);
You should be able to use nlg as last element (without
[2].linear_deadzone_threshold).
(Obviously the changes suggested by these comments could be applied at
any time.)
> + COPY(AVDOVIColorMetadata, av_dovi_get_color(dovi), s->color, source_diagonal);
> + return 0;
> +}
> +
> +static int guess_profile(const AVDOVIRpuDataHeader *hdr)
> +{
> + switch (hdr->vdr_rpu_profile) {
> + case 0:
> + if (hdr->bl_video_full_range_flag)
> + return 5;
> + break;
> + case 1:
> + if (hdr->el_spatial_resampling_filter_flag && !hdr->disable_residual_flag) {
> + if (hdr->vdr_bit_depth == 12) {
> + return 7;
> + } else {
> + return 4;
> + }
> + } else {
> + return 8;
> + }
> + }
> +
> + return 0; /* unknown */
> +}
> +
> +static inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
> +{
> + uint64_t ipart;
> + union { uint32_t u32; float f32; } fpart;
> +
> + switch (hdr->coef_data_type) {
> + case RPU_COEFF_FIXED:
> + ipart = get_ue_golomb_long(gb);
> + fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
> + return (ipart << hdr->coef_log2_denom) + fpart.u32;
> +
> + case RPU_COEFF_FLOAT:
> + fpart.u32 = get_bits_long(gb, 32);
> + return fpart.f32 * (1 << hdr->coef_log2_denom);
> + }
> +
> + return 0; /* unreachable */
> +}
> +
> +static inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
> +{
> + int64_t ipart;
> + union { uint32_t u32; float f32; } fpart;
> +
> + switch (hdr->coef_data_type) {
> + case RPU_COEFF_FIXED:
> + ipart = get_se_golomb_long(gb);
> + fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
> + return (ipart << hdr->coef_log2_denom) + fpart.u32;
> +
> + case RPU_COEFF_FLOAT:
> + fpart.u32 = get_bits_long(gb, 32);
> + return fpart.f32 * (1 << hdr->coef_log2_denom);
> + }
> +
> + return 0; /* unreachable */
> +}
> +
> +#define VALIDATE(VAR, MIN, MAX) \
> + do { \
> + if (VAR < MIN || VAR > MAX) { \
> + av_log(s->logctx, AV_LOG_ERROR, "RPU validation failed: " \
> + #MIN" <= "#VAR" = %d <= "#MAX"\n", (int) VAR); \
> + goto fail; \
> + } \
> + } while (0)
> +
> +int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size)
> +{
> + AVDOVIRpuDataHeader *hdr = &s->header;
> + GetBitContext *gb = &(GetBitContext){0};
> + DOVIVdrRef *vdr;
> + int ret;
> +
> + uint8_t nal_prefix;
> + uint8_t rpu_type;
> + uint8_t vdr_seq_info_present;
> + uint8_t vdr_dm_metadata_present;
> + uint8_t use_prev_vdr_rpu;
> + uint8_t use_nlq;
> + uint8_t profile;
> + if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0)
> + return ret;
> +
> + /* RPU header, common values */
> + nal_prefix = get_bits(gb, 8);
> + VALIDATE(nal_prefix, 25, 25);
> + rpu_type = get_bits(gb, 6);
> + if (rpu_type != 2) {
> + av_log(s->logctx, AV_LOG_WARNING, "Unrecognized RPU type "
> + "%"PRIu8", ignoring\n", rpu_type);
> + return 0;
> + }
> +
> + hdr->rpu_type = rpu_type;
> + hdr->rpu_format = get_bits(gb, 11);
> +
> + /* Values specific to RPU type 2 */
> + hdr->vdr_rpu_profile = get_bits(gb, 4);
> + hdr->vdr_rpu_level = get_bits(gb, 4);
> +
> + vdr_seq_info_present = get_bits1(gb);
> + if (vdr_seq_info_present) {
> + hdr->chroma_resampling_explicit_filter_flag = get_bits1(gb);
> + hdr->coef_data_type = get_bits(gb, 2);
> + VALIDATE(hdr->coef_data_type, RPU_COEFF_FIXED, RPU_COEFF_FLOAT);
> + switch (hdr->coef_data_type) {
> + case RPU_COEFF_FIXED:
> + hdr->coef_log2_denom = get_ue_golomb(gb);
> + VALIDATE(hdr->coef_log2_denom, 13, 32);
> + break;
> + case RPU_COEFF_FLOAT:
> + hdr->coef_log2_denom = 32; /* arbitrary, choose maximum precision */
> + break;
> + }
> +
> + hdr->vdr_rpu_normalized_idc = get_bits(gb, 2);
> + hdr->bl_video_full_range_flag = get_bits1(gb);
> +
> + if ((hdr->rpu_format & 0x700) == 0) {
> + int bl_bit_depth_minus8 = get_ue_golomb_31(gb);
> + int el_bit_depth_minus8 = get_ue_golomb_31(gb);
> + int vdr_bit_depth_minus8 = get_ue_golomb_31(gb);
> + VALIDATE(bl_bit_depth_minus8, 0, 8);
> + VALIDATE(el_bit_depth_minus8, 0, 8);
> + VALIDATE(vdr_bit_depth_minus8, 0, 8);
> + hdr->bl_bit_depth = bl_bit_depth_minus8 + 8;
> + hdr->el_bit_depth = el_bit_depth_minus8 + 8;
> + hdr->vdr_bit_depth = vdr_bit_depth_minus8 + 8;
> + hdr->spatial_resampling_filter_flag = get_bits1(gb);
> + skip_bits(gb, 3); /* reserved_zero_3bits */
> + hdr->el_spatial_resampling_filter_flag = get_bits1(gb);
> + hdr->disable_residual_flag = get_bits1(gb);
> + }
> + }
> +
> + if (!hdr->bl_bit_depth) {
> + av_log(s->logctx, AV_LOG_ERROR, "Missing RPU VDR sequence info?\n");
> + goto fail;
> + }
> +
> + vdr_dm_metadata_present = get_bits1(gb);
> + use_prev_vdr_rpu = get_bits1(gb);
> + use_nlq = (hdr->rpu_format & 0x700) == 0 && !hdr->disable_residual_flag;
> +
> + profile = s->dv_profile ? s->dv_profile : guess_profile(hdr);
> + if (profile == 5 && use_nlq) {
> + av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
> + goto fail;
> + }
> +
> + if (use_prev_vdr_rpu) {
> + int prev_vdr_rpu_id = get_ue_golomb_31(gb);
> + VALIDATE(prev_vdr_rpu_id, 0, DOVI_MAX_DM_ID);
> + if (!s->vdr_ref[prev_vdr_rpu_id]) {
> + av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU ID: %u\n",
> + prev_vdr_rpu_id);
> + goto fail;
> + }
> + vdr = (DOVIVdrRef *) s->vdr_ref[prev_vdr_rpu_id]->data;
> + s->mapping = &vdr->mapping;
> + } else {
> + int vdr_rpu_id = get_ue_golomb_31(gb);
> + VALIDATE(vdr_rpu_id, 0, DOVI_MAX_DM_ID);
> + if (!s->vdr_ref[vdr_rpu_id]) {
> + s->vdr_ref[vdr_rpu_id] = av_buffer_allocz(sizeof(DOVIVdrRef));
> + if (!s->vdr_ref[vdr_rpu_id])
> + return AVERROR(ENOMEM);
> + }
> +
> + vdr = (DOVIVdrRef *) s->vdr_ref[vdr_rpu_id]->data;
> + s->mapping = &vdr->mapping;
> +
> + vdr->mapping.vdr_rpu_id = vdr_rpu_id;
> + vdr->mapping.mapping_color_space = get_ue_golomb_31(gb);
> + vdr->mapping.mapping_chroma_format_idc = get_ue_golomb_31(gb);
> +
> + for (int c = 0; c < 3; c++) {
> + AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c];
> + int num_pivots_minus_2 = get_ue_golomb_31(gb);
> + int pivot = 0;
> +
> + VALIDATE(num_pivots_minus_2, 0, AV_DOVI_MAX_PIECES - 1);
> + curve->num_pivots = num_pivots_minus_2 + 2;
> + for (int i = 0; i < curve->num_pivots; i++) {
> + pivot += get_bits(gb, hdr->bl_bit_depth);
> + curve->pivots[i] = av_clip_uint16(pivot);
> + }
> + }
> +
> + if (use_nlq) {
> + vdr->mapping.nlq_method_idc = get_bits(gb, 3);
> + /**
> + * The patent mentions another legal value, NLQ_MU_LAW, but it's
> + * not documented anywhere how to parse or apply that type of NLQ.
> + */
> + VALIDATE(vdr->mapping.nlq_method_idc, 0, AV_DOVI_NLQ_LINEAR_DZ);
> + } else {
> + vdr->mapping.nlq_method_idc = AV_DOVI_NLQ_NONE;
> + }
> +
> + vdr->mapping.num_x_partitions = get_ue_golomb_long(gb) + 1;
> + vdr->mapping.num_y_partitions = get_ue_golomb_long(gb) + 1;
> + /* End of rpu_data_header(), start of vdr_rpu_data_payload() */
> +
> + for (int c = 0; c < 3; c++) {
> + AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c];
> + for (int i = 0; i < curve->num_pivots - 1; i++) {
> + int mapping_idc = get_ue_golomb_31(gb);
> + VALIDATE(mapping_idc, 0, 1);
> + curve->mapping_idc[i] = mapping_idc;
> + switch (mapping_idc) {
> + case AV_DOVI_MAPPING_POLYNOMIAL: {
> + int poly_order_minus1 = get_ue_golomb_31(gb);
> + VALIDATE(poly_order_minus1, 0, 1);
> + curve->poly_order[i] = poly_order_minus1 + 1;
> + if (poly_order_minus1 == 0) {
> + int linear_interp_flag = get_bits1(gb);
> + if (linear_interp_flag) {
> + /* lack of documentation/samples */
> + avpriv_request_sample(s->logctx, "Dolby Vision "
> + "linear interpolation");
> + ff_dovi_ctx_unref(s);
> + return AVERROR_PATCHWELCOME;
> + }
> + }
> + for (int k = 0; k <= curve->poly_order[i]; k++)
> + curve->poly_coef[i][k] = get_se_coef(gb, hdr);
> + break;
> + }
> + case AV_DOVI_MAPPING_MMR: {
> + int mmr_order_minus1 = get_bits(gb, 2);
> + VALIDATE(mmr_order_minus1, 0, 2);
> + curve->mmr_order[i] = mmr_order_minus1 + 1;
> + curve->mmr_constant[i] = get_se_coef(gb, hdr);
> + for (int j = 0; j < curve->mmr_order[i]; j++) {
> + for (int k = 0; k < 7; k++)
> + curve->mmr_coef[i][j][k] = get_se_coef(gb, hdr);
> + }
> + break;
> + }
> + }
> + }
> + }
> +
> + if (use_nlq) {
> + for (int c = 0; c < 3; c++) {
> + AVDOVINLQParams *nlq = &vdr->mapping.nlq[c];
> + nlq->nlq_offset = get_bits(gb, hdr->el_bit_depth);
> + nlq->vdr_in_max = get_ue_coef(gb, hdr);
> + switch (vdr->mapping.nlq_method_idc) {
> + case AV_DOVI_NLQ_LINEAR_DZ:
> + nlq->linear_deadzone_slope = get_ue_coef(gb, hdr);
> + nlq->linear_deadzone_threshold = get_ue_coef(gb, hdr);
> + break;
> + }
> + }
> + }
> + }
> +
> + if (vdr_dm_metadata_present) {
> + AVDOVIColorMetadata *color;
> + int affected_dm_id = get_ue_golomb_31(gb);
> + int current_dm_id = get_ue_golomb_31(gb);
> + VALIDATE(affected_dm_id, 0, DOVI_MAX_DM_ID);
> + VALIDATE(current_dm_id, 0, DOVI_MAX_DM_ID);
> + if (!s->vdr_ref[affected_dm_id]) {
> + s->vdr_ref[affected_dm_id] = av_buffer_allocz(sizeof(DOVIVdrRef));
> + if (!s->vdr_ref[affected_dm_id])
> + return AVERROR(ENOMEM);
> + }
> +
> + if (!s->vdr_ref[current_dm_id]) {
> + av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU DM ID: %u\n",
> + current_dm_id);
> + goto fail;
> + }
> +
> + /* Update current pointer based on current_dm_id */
> + vdr = (DOVIVdrRef *) s->vdr_ref[current_dm_id]->data;
> + s->color = &vdr->color;
> +
> + /* Update values of affected_dm_id */
> + vdr = (DOVIVdrRef *) s->vdr_ref[affected_dm_id]->data;
> + color = &vdr->color;
> + color->dm_metadata_id = affected_dm_id;
> + color->scene_refresh_flag = get_ue_golomb_31(gb);
> + for (int i = 0; i < 9; i++)
> + color->ycc_to_rgb_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 13);
> + for (int i = 0; i < 3; i++) {
> + int denom = profile == 4 ? (1 << 30) : (1 << 28);
> + unsigned offset = get_bits_long(gb, 32);
> + if (offset > INT_MAX) {
> + /* Ensure the result fits inside AVRational */
> + offset >>= 1;
> + denom >>= 1;
> + }
> + color->ycc_to_rgb_offset[i] = av_make_q(offset, denom);
> + }
> + for (int i = 0; i < 9; i++)
> + color->rgb_to_lms_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 14);
> +
> + color->signal_eotf = get_bits(gb, 16);
> + color->signal_eotf_param0 = get_bits(gb, 16);
> + color->signal_eotf_param1 = get_bits(gb, 16);
> + color->signal_eotf_param2 = get_bits_long(gb, 32);
> + color->signal_bit_depth = get_bits(gb, 5);
> + VALIDATE(color->signal_bit_depth, 8, 16);
> + color->signal_color_space = get_bits(gb, 2);
> + color->signal_chroma_format = get_bits(gb, 2);
> + color->signal_full_range_flag = get_bits(gb, 2);
> + color->source_min_pq = get_bits(gb, 12);
> + color->source_max_pq = get_bits(gb, 12);
> + color->source_diagonal = get_bits(gb, 10);
> + }
> +
> + /* FIXME: verify CRC32, requires implementation of AV_CRC_32_MPEG_2 */
> + return 0;
> +
> +fail:
> + ff_dovi_ctx_unref(s); /* don't leak potentially invalid state */
> + return AVERROR(EINVAL);
> +}
> diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h
> new file mode 100644
> index 0000000000..f6ca5bbbc5
> --- /dev/null
> +++ b/libavcodec/dovi_rpu.h
> @@ -0,0 +1,87 @@
> +/*
> + * Dolby Vision RPU decoder
> + *
> + * Copyright (C) 2021 Jan Ekström
> + * Copyright (C) 2021 Niklas Haas
> + *
> + * 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_DOVI_RPU_H
> +#define AVCODEC_DOVI_RPU_H
> +
> +#include "libavutil/dovi_meta.h"
> +#include "libavutil/frame.h"
> +
> +#define DOVI_MAX_DM_ID 15
> +typedef struct DOVIContext {
> + void *logctx;
> +
> + /**
> + * Currently active RPU data header, updates on every dovi_rpu_parse().
> + */
> + AVDOVIRpuDataHeader header;
> +
> + /**
> + * Currently active data mappings, or NULL. Points into memory owned by the
> + * corresponding rpu/vdr_ref, which becomes invalid on the next call to
> + * dovi_rpu_parse.
> + */
> + const AVDOVIDataMapping *mapping;
> + const AVDOVIColorMetadata *color;
> +
> + /**
> + * Private fields internal to dovi_rpu.c
> + */
> + AVBufferRef *vdr_ref[DOVI_MAX_DM_ID+1];
> + uint8_t dv_profile;
> +
> +} DOVIContext;
> +
> +int ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0);
> +
> +/**
> + * Completely reset a DOVIContext, preserving only logctx.
> + */
> +void ff_dovi_ctx_unref(DOVIContext *s);
> +
> +/**
> + * Partially reset the internal state. Resets per-frame state while preserving
> + * fields parsed from the configuration record.
> + */
> +void ff_dovi_ctx_flush(DOVIContext *s);
> +
> +/**
> + * Read the contents of an AVDOVIDecoderConfigurationRecord (usually provided
> + * by stream side data) and update internal state accordingly.
> + */
> +void ff_dovi_update_cfg(DOVIContext *s, const AVDOVIDecoderConfigurationRecord *cfg);
> +
> +/**
> + * Parse the contents of a Dovi RPU NAL and update the parsed values in the
> + * DOVIContext struct.
> + *
> + * Returns 0 or an error code.
> + */
> +int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size);
> +
> +/**
> + * Attach the decoded AVDOVIMetadata as side data to an AVFrame.
> + */
> +int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame);
> +
> +#endif /* AVCODEC_DOVI_RPU_H */
>
More information about the ffmpeg-devel
mailing list