[FFmpeg-devel] [PATCH 3/6] lavc/cbs: APV support
Mark Thompson
sw at jkqxz.net
Sat Apr 19 22:07:01 EEST 2025
---
configure | 1 +
libavcodec/Makefile | 1 +
libavcodec/apv.h | 86 ++++
libavcodec/cbs.c | 6 +
libavcodec/cbs_apv.c | 395 ++++++++++++++++++
libavcodec/cbs_apv.h | 209 ++++++++++
libavcodec/cbs_apv_syntax_template.c | 598 +++++++++++++++++++++++++++
libavcodec/cbs_internal.h | 4 +
libavformat/cbs.h | 1 +
9 files changed, 1301 insertions(+)
create mode 100644 libavcodec/apv.h
create mode 100644 libavcodec/cbs_apv.c
create mode 100644 libavcodec/cbs_apv.h
create mode 100644 libavcodec/cbs_apv_syntax_template.c
diff --git a/configure b/configure
index c94b8eac43..ca404d2797 100755
--- a/configure
+++ b/configure
@@ -2562,6 +2562,7 @@ CONFIG_EXTRA="
bswapdsp
cabac
cbs
+ cbs_apv
cbs_av1
cbs_h264
cbs_h265
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 7bd1dbec9a..a5f5c4e904 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -83,6 +83,7 @@ OBJS-$(CONFIG_BLOCKDSP) += blockdsp.o
OBJS-$(CONFIG_BSWAPDSP) += bswapdsp.o
OBJS-$(CONFIG_CABAC) += cabac.o
OBJS-$(CONFIG_CBS) += cbs.o cbs_bsf.o
+OBJS-$(CONFIG_CBS_APV) += cbs_apv.o
OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o
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
diff --git a/libavcodec/apv.h b/libavcodec/apv.h
new file mode 100644
index 0000000000..27e089ea22
--- /dev/null
+++ b/libavcodec/apv.h
@@ -0,0 +1,86 @@
+/*
+ * 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_APV_H
+#define AVCODEC_APV_H
+
+// PBU types (section 5.3.3).
+enum {
+ APV_PBU_PRIMARY_FRAME = 1,
+ APV_PBU_NON_PRIMARY_FRAME = 2,
+ APV_PBU_PREVIEW_FRAME = 25,
+ APV_PBU_DEPTH_FRAME = 26,
+ APV_PBU_ALPHA_FRAME = 27,
+ APV_PBU_ACCESS_UNIT_INFORMATION = 65,
+ APV_PBU_METADATA = 66,
+ APV_PBU_FILLER = 67,
+};
+
+// Format parameters (section 4.2).
+enum {
+ APV_MAX_NUM_COMP = 4,
+ APV_MB_WIDTH = 16,
+ APV_MB_HEIGHT = 16,
+ APV_TR_SIZE = 8,
+};
+
+// Chroma formats (section 4.2).
+enum {
+ APV_CHROMA_FORMAT_400 = 0,
+ APV_CHROMA_FORMAT_422 = 2,
+ APV_CHROMA_FORMAT_444 = 3,
+ APV_CHROMA_FORMAT_4444 = 4,
+};
+
+// Coefficient limits (section 5.3.15).
+enum {
+ APV_BLK_COEFFS = (APV_TR_SIZE * APV_TR_SIZE),
+ APV_MIN_TRANS_COEFF = -32768,
+ APV_MAX_TRANS_COEFF = 32767,
+};
+
+// Profiles (section 10.1.3).
+enum {
+ APV_PROFILE_422_10 = 33,
+ APV_PROFILE_422_12 = 44,
+ APV_PROFILE_444_10 = 55,
+ APV_PROFILE_444_12 = 66,
+ APV_PROFILE_4444_10 = 77,
+ APV_PROFILE_4444_12 = 88,
+ APV_PROFILE_400_10 = 99,
+};
+
+// General level limits for tiles (section 10.1.4.1).
+enum {
+ APV_MIN_TILE_WIDTH_IN_MBS = 16,
+ APV_MIN_TILE_HEIGHT_IN_MBS = 8,
+ APV_MAX_TILE_COLS = 20,
+ APV_MAX_TILE_ROWS = 20,
+ APV_MAX_TILE_COUNT = APV_MAX_TILE_COLS * APV_MAX_TILE_ROWS,
+};
+
+// Metadata types (section 10.3.1).
+enum {
+ APV_METADATA_ITU_T_T35 = 4,
+ APV_METADATA_MDCV = 5,
+ APV_METADATA_CLL = 6,
+ APV_METADATA_FILLER = 10,
+ APV_METADATA_USER_DEFINED = 170,
+};
+
+#endif /* AVCODEC_APV_H */
diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index ba1034a72e..9b485420d5 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -31,6 +31,9 @@
static const CodedBitstreamType *const cbs_type_table[] = {
+#if CBS_APV
+ &CBS_FUNC(type_apv),
+#endif
#if CBS_AV1
&CBS_FUNC(type_av1),
#endif
@@ -58,6 +61,9 @@ static const CodedBitstreamType *const cbs_type_table[] = {
};
const enum AVCodecID CBS_FUNC(all_codec_ids)[] = {
+#if CBS_APV
+ AV_CODEC_ID_APV,
+#endif
#if CBS_AV1
AV_CODEC_ID_AV1,
#endif
diff --git a/libavcodec/cbs_apv.c b/libavcodec/cbs_apv.c
new file mode 100644
index 0000000000..c37124168a
--- /dev/null
+++ b/libavcodec/cbs_apv.c
@@ -0,0 +1,395 @@
+/*
+ * 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/mem.h"
+#include "cbs.h"
+#include "cbs_internal.h"
+#include "cbs_apv.h"
+
+
+static int cbs_apv_get_num_comp(const APVRawFrameHeader *fh)
+{
+ switch (fh->frame_info.chroma_format_idc) {
+ case APV_CHROMA_FORMAT_400:
+ return 1;
+ case APV_CHROMA_FORMAT_422:
+ case APV_CHROMA_FORMAT_444:
+ return 3;
+ case APV_CHROMA_FORMAT_4444:
+ return 4;
+ default:
+ av_assert0(0 && "Invalid chroma_format_idc");
+ }
+}
+
+static void cbs_apv_derive_tile_info(APVDerivedTileInfo *ti,
+ const APVRawFrameHeader *fh)
+{
+ int frame_width_in_mbs = (fh->frame_info.frame_width + 15) / 16;
+ int frame_height_in_mbs = (fh->frame_info.frame_height + 15) / 16;
+ int start_mb, i;
+
+ start_mb = 0;
+ for (i = 0; start_mb < frame_width_in_mbs; i++) {
+ ti->col_starts[i] = start_mb * APV_MB_WIDTH;
+ start_mb += fh->tile_info.tile_width_in_mbs;
+ }
+ av_assert0(i <= APV_MAX_TILE_COLS);
+ ti->col_starts[i] = frame_width_in_mbs * APV_MB_WIDTH;
+ ti->tile_cols = i;
+
+ start_mb = 0;
+ for (i = 0; start_mb < frame_height_in_mbs; i++) {
+ av_assert0(i < APV_MAX_TILE_ROWS);
+ ti->row_starts[i] = start_mb * APV_MB_HEIGHT;
+ start_mb += fh->tile_info.tile_height_in_mbs;
+ }
+ av_assert0(i <= APV_MAX_TILE_ROWS);
+ ti->row_starts[i] = frame_height_in_mbs * APV_MB_HEIGHT;
+ ti->tile_rows = i;
+
+ ti->num_tiles = ti->tile_cols * ti->tile_rows;
+}
+
+
+#define HEADER(name) do { \
+ ff_cbs_trace_header(ctx, name); \
+ } while (0)
+
+#define CHECK(call) do { \
+ err = (call); \
+ if (err < 0) \
+ return err; \
+ } while (0)
+
+#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)
+
+
+#define u(width, name, range_min, range_max) \
+ xu(width, name, current->name, range_min, range_max, 0, )
+#define ub(width, name) \
+ xu(width, name, current->name, 0, MAX_UINT_BITS(width), 0, )
+#define us(width, name, range_min, range_max, subs, ...) \
+ xu(width, name, current->name, range_min, range_max, subs, __VA_ARGS__)
+#define ubs(width, name, subs, ...) \
+ xu(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__)
+
+#define fixed(width, name, value) do { \
+ av_unused uint32_t fixed_value = value; \
+ xu(width, name, fixed_value, value, value, 0, ); \
+ } while (0)
+
+
+#define READ
+#define READWRITE read
+#define RWContext GetBitContext
+#define FUNC(name) cbs_apv_read_ ## name
+
+#define xu(width, name, var, range_min, range_max, subs, ...) do { \
+ uint32_t value; \
+ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
+ SUBSCRIPTS(subs, __VA_ARGS__), \
+ &value, range_min, range_max)); \
+ var = value; \
+ } while (0)
+
+#define infer(name, value) do { \
+ current->name = value; \
+ } while (0)
+
+#define byte_alignment(rw) (get_bits_count(rw) % 8)
+
+#include "cbs_apv_syntax_template.c"
+
+#undef READ
+#undef READWRITE
+#undef RWContext
+#undef FUNC
+#undef xu
+#undef infer
+#undef byte_alignment
+
+#define WRITE
+#define READWRITE write
+#define RWContext PutBitContext
+#define FUNC(name) cbs_apv_write_ ## name
+
+#define xu(width, name, var, range_min, range_max, subs, ...) do { \
+ uint32_t value = var; \
+ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
+ SUBSCRIPTS(subs, __VA_ARGS__), \
+ value, range_min, range_max)); \
+ } while (0)
+
+#define infer(name, value) do { \
+ if (current->name != (value)) { \
+ av_log(ctx->log_ctx, AV_LOG_ERROR, \
+ "%s does not match inferred value: " \
+ "%"PRId64", but should be %"PRId64".\n", \
+ #name, (int64_t)current->name, (int64_t)(value)); \
+ return AVERROR_INVALIDDATA; \
+ } \
+ } while (0)
+
+#define byte_alignment(rw) (put_bits_count(rw) % 8)
+
+#include "cbs_apv_syntax_template.c"
+
+#undef WRITE
+#undef READWRITE
+#undef RWContext
+#undef FUNC
+#undef xu
+#undef infer
+#undef byte_alignment
+
+
+static int cbs_apv_split_fragment(CodedBitstreamContext *ctx,
+ CodedBitstreamFragment *frag,
+ int header)
+{
+ uint8_t *data = frag->data;
+ size_t size = frag->data_size;
+ int err, trace;
+
+ // Don't include parsing here in trace output.
+ trace = ctx->trace_enable;
+ ctx->trace_enable = 0;
+
+ while (size > 0) {
+ GetBitContext gbc;
+ uint32_t pbu_size;
+ APVRawPBUHeader pbu_header;
+
+ if (size < 8) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
+ "fragment too short (%"SIZE_SPECIFIER" bytes).\n",
+ size);
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ pbu_size = AV_RB32(data);
+ if (pbu_size < 4 ) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
+ "pbu_size too small (%"PRIu32" bytes).\n",
+ pbu_size);
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ data += 4;
+ size -= 4;
+
+ if (pbu_size > size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid PBU: "
+ "pbu_size too large (%"PRIu32" bytes).\n",
+ pbu_size);
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ init_get_bits(&gbc, data, 8 * pbu_size);
+
+ err = cbs_apv_read_pbu_header(ctx, &gbc, &pbu_header);
+ if (err < 0)
+ return err;
+
+ // Could select/skip frames based on type/group_id here.
+
+ err = ff_cbs_append_unit_data(frag, pbu_header.pbu_type,
+ data, pbu_size, frag->data_ref);
+ if (err < 0)
+ return err;
+
+ data += pbu_size;
+ size -= pbu_size;
+ }
+
+ err = 0;
+fail:
+ ctx->trace_enable = trace;
+ return err;
+}
+
+static int cbs_apv_read_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ GetBitContext gbc;
+ int err;
+
+ err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
+ if (err < 0)
+ return err;
+
+ err = ff_cbs_alloc_unit_content(ctx, unit);
+ if (err < 0)
+ return err;
+
+ switch (unit->type) {
+ case APV_PBU_PRIMARY_FRAME:
+ {
+ APVRawFrame *frame = unit->content;
+
+ err = cbs_apv_read_frame(ctx, &gbc, frame);
+ if (err < 0)
+ return err;
+
+ // Each tile inside the frame has pointers into the unit
+ // data buffer; make a single reference here for all of
+ // them together.
+ frame->tile_data_ref = av_buffer_ref(unit->data_ref);
+ if (!frame->tile_data_ref)
+ return AVERROR(ENOMEM);
+ }
+ break;
+ case APV_PBU_ACCESS_UNIT_INFORMATION:
+ {
+ err = cbs_apv_read_au_info(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+ }
+ break;
+ case APV_PBU_METADATA:
+ {
+ err = cbs_apv_read_metadata(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+ }
+ break;
+ case APV_PBU_FILLER:
+ {
+ err = cbs_apv_read_filler(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+ }
+ break;
+ default:
+ return AVERROR(ENOSYS);
+ }
+
+ return 0;
+}
+
+static int cbs_apv_write_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit,
+ PutBitContext *pbc)
+{
+ int err;
+
+ switch (unit->type) {
+ case APV_PBU_PRIMARY_FRAME:
+ {
+ APVRawFrame *frame = unit->content;
+
+ err = cbs_apv_write_frame(ctx, pbc, frame);
+ if (err < 0)
+ return err;
+ }
+ break;
+ case APV_PBU_ACCESS_UNIT_INFORMATION:
+ {
+ err = cbs_apv_write_au_info(ctx, pbc, unit->content);
+ if (err < 0)
+ return err;
+ }
+ break;
+ case APV_PBU_METADATA:
+ {
+ err = cbs_apv_write_metadata(ctx, pbc, unit->content);
+ if (err < 0)
+ return err;
+ }
+ break;
+ case APV_PBU_FILLER:
+ {
+ err = cbs_apv_write_filler(ctx, pbc, unit->content);
+ if (err < 0)
+ return err;
+ }
+ break;
+ default:
+ return AVERROR(ENOSYS);
+ }
+
+ return 0;
+}
+
+static int cbs_apv_assemble_fragment(CodedBitstreamContext *ctx,
+ CodedBitstreamFragment *frag)
+{
+ size_t size = 0, pos;
+
+ for (int i = 0; i < frag->nb_units; i++)
+ size += frag->units[i].data_size + 4;
+
+ frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!frag->data_ref)
+ return AVERROR(ENOMEM);
+ frag->data = frag->data_ref->data;
+ memset(frag->data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
+ pos = 0;
+ for (int i = 0; i < frag->nb_units; i++) {
+ AV_WB32(frag->data + pos, frag->units[i].data_size);
+ pos += 4;
+
+ memcpy(frag->data + pos, frag->units[i].data,
+ frag->units[i].data_size);
+ pos += frag->units[i].data_size;
+ }
+ av_assert0(pos == size);
+ frag->data_size = size;
+
+ return 0;
+}
+
+
+static const CodedBitstreamUnitTypeDescriptor cbs_apv_unit_types[] = {
+ {
+ .nb_unit_types = CBS_UNIT_TYPE_RANGE,
+ .unit_type.range = {
+ .start = APV_PBU_PRIMARY_FRAME,
+ .end = APV_PBU_ALPHA_FRAME,
+ },
+ .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS,
+ .content_size = sizeof(APVRawFrame),
+ .type.ref = {
+ .nb_offsets = 1,
+ .offsets = { offsetof(APVRawFrame, tile_data_ref) -
+ sizeof(void*) },
+ },
+ },
+
+ CBS_UNIT_TYPE_POD(APV_PBU_METADATA, APVRawMetadata),
+
+ CBS_UNIT_TYPE_END_OF_LIST
+};
+
+const CodedBitstreamType ff_cbs_type_apv = {
+ .codec_id = AV_CODEC_ID_APV,
+
+ .priv_data_size = sizeof(CodedBitstreamAPVContext),
+
+ .unit_types = cbs_apv_unit_types,
+
+ .split_fragment = &cbs_apv_split_fragment,
+ .read_unit = &cbs_apv_read_unit,
+ .write_unit = &cbs_apv_write_unit,
+ .assemble_fragment = &cbs_apv_assemble_fragment,
+};
diff --git a/libavcodec/cbs_apv.h b/libavcodec/cbs_apv.h
new file mode 100644
index 0000000000..7a7f35626b
--- /dev/null
+++ b/libavcodec/cbs_apv.h
@@ -0,0 +1,209 @@
+/*
+ * 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_APV_H
+#define AVCODEC_CBS_APV_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "libavutil/buffer.h"
+#include "apv.h"
+
+// Arbitrary limits to avoid large structures.
+#define CBS_APV_MAX_AU_FRAMES 8
+#define CBS_APV_MAX_METADATA_PAYLOADS 8
+
+
+typedef struct APVRawPBUHeader {
+ uint8_t pbu_type;
+ uint16_t group_id;
+ uint8_t reserved_zero_8bits;
+} APVRawPBUHeader;
+
+typedef struct APVRawFiller {
+ size_t filler_size;
+} APVRawFiller;
+
+typedef struct APVRawFrameInfo {
+ uint8_t profile_idc;
+ uint8_t level_idc;
+ uint8_t band_idc;
+ uint8_t reserved_zero_5bits;
+ uint32_t frame_width;
+ uint32_t frame_height;
+ uint8_t chroma_format_idc;
+ uint8_t bit_depth_minus8;
+ uint8_t capture_time_distance;
+ uint8_t reserved_zero_8bits;
+} APVRawFrameInfo;
+
+typedef struct APVRawQuantizationMatrix {
+ uint8_t q_matrix[APV_MAX_NUM_COMP][APV_TR_SIZE][APV_TR_SIZE];
+} APVRawQuantizationMatrix;
+
+typedef struct APVRawTileInfo {
+ uint32_t tile_width_in_mbs;
+ uint32_t tile_height_in_mbs;
+ uint8_t tile_size_present_in_fh_flag;
+ uint32_t tile_size_in_fh[APV_MAX_TILE_COUNT];
+} APVRawTileInfo;
+
+typedef struct APVRawFrameHeader {
+ APVRawFrameInfo frame_info;
+ uint8_t reserved_zero_8bits;
+
+ uint8_t color_description_present_flag;
+ uint8_t color_primaries;
+ uint8_t transfer_characteristics;
+ uint8_t matrix_coefficients;
+ uint8_t full_range_flag;
+
+ uint8_t use_q_matrix;
+ APVRawQuantizationMatrix quantization_matrix;
+
+ APVRawTileInfo tile_info;
+
+ uint8_t reserved_zero_8bits_2;
+} APVRawFrameHeader;
+
+typedef struct APVRawTileHeader {
+ uint16_t tile_header_size;
+ uint16_t tile_index;
+ uint32_t tile_data_size[APV_MAX_NUM_COMP];
+ uint8_t tile_qp [APV_MAX_NUM_COMP];
+ uint8_t reserved_zero_8bits;
+} APVRawTileHeader;
+
+typedef struct APVRawTile {
+ APVRawTileHeader tile_header;
+
+ uint8_t *tile_data[APV_MAX_NUM_COMP];
+ uint8_t *tile_dummy_byte;
+ uint32_t tile_dummy_byte_size;
+} APVRawTile;
+
+typedef struct APVRawFrame {
+ APVRawPBUHeader pbu_header;
+ APVRawFrameHeader frame_header;
+ uint32_t tile_size[APV_MAX_TILE_COUNT];
+ APVRawTile tile [APV_MAX_TILE_COUNT];
+ APVRawFiller filler;
+
+ AVBufferRef *tile_data_ref;
+} APVRawFrame;
+
+typedef struct APVRawAUInfo {
+ uint16_t num_frames;
+
+ uint8_t pbu_type [CBS_APV_MAX_AU_FRAMES];
+ uint8_t group_id [CBS_APV_MAX_AU_FRAMES];
+ uint8_t reserved_zero_8bits[CBS_APV_MAX_AU_FRAMES];
+ APVRawFrameInfo frame_info [CBS_APV_MAX_AU_FRAMES];
+
+ uint8_t reserved_zero_8bits_2;
+
+ APVRawFiller filler;
+} APVRawAUInfo;
+
+typedef struct APVRawMetadataITUTT35 {
+ uint8_t itu_t_t35_country_code;
+ uint8_t itu_t_t35_country_code_extension;
+
+ uint8_t *data;
+ AVBufferRef *data_ref;
+ size_t data_size;
+} APVRawMetadataITUTT35;
+
+typedef struct APVRawMetadataMDCV {
+ uint16_t primary_chromaticity_x[3];
+ uint16_t primary_chromaticity_y[3];
+ uint16_t white_point_chromaticity_x;
+ uint16_t white_point_chromaticity_y;
+ uint32_t max_mastering_luminance;
+ uint32_t min_mastering_luminance;
+} APVRawMetadataMDCV;
+
+typedef struct APVRawMetadataCLL {
+ uint16_t max_cll;
+ uint16_t max_fall;
+} APVRawMetadataCLL;
+
+typedef struct APVRawMetadataFiller {
+ uint32_t payload_size;
+} APVRawMetadataFiller;
+
+typedef struct APVRawMetadataUserDefined {
+ uint8_t uuid[16];
+
+ uint8_t *data;
+ AVBufferRef *data_ref;
+ size_t data_size;
+} APVRawMetadataUserDefined;
+
+typedef struct APVRawMetadataUndefined {
+ uint8_t *data;
+ AVBufferRef *data_ref;
+ size_t data_size;
+} APVRawMetadataUndefined;
+
+typedef struct APVRawMetadataPayload {
+ uint32_t payload_type;
+ uint32_t payload_size;
+ union {
+ APVRawMetadataITUTT35 itu_t_t35;
+ APVRawMetadataMDCV mdcv;
+ APVRawMetadataCLL cll;
+ APVRawMetadataFiller filler;
+ APVRawMetadataUserDefined user_defined;
+ APVRawMetadataUndefined undefined;
+ };
+} APVRawMetadataPayload;
+
+typedef struct APVRawMetadata {
+ APVRawPBUHeader pbu_header;
+
+ uint32_t metadata_size;
+ uint32_t metadata_count;
+
+ APVRawMetadataPayload payloads[CBS_APV_MAX_METADATA_PAYLOADS];
+
+ APVRawFiller filler;
+} APVRawMetadata;
+
+
+typedef struct APVDerivedTileInfo {
+ uint8_t tile_cols;
+ uint8_t tile_rows;
+ uint16_t num_tiles;
+ // The spec uses an extra element on the end of these arrays
+ // not corresponding to any tile.
+ uint16_t col_starts[APV_MAX_TILE_COLS + 1];
+ uint16_t row_starts[APV_MAX_TILE_ROWS + 1];
+} APVDerivedTileInfo;
+
+typedef struct CodedBitstreamAPVContext {
+ int bit_depth;
+ int num_comp;
+ int sub_width_c;
+ int sub_height_c;
+
+ APVDerivedTileInfo tile_info;
+} CodedBitstreamAPVContext;
+
+#endif /* AVCODEC_CBS_APV_H */
diff --git a/libavcodec/cbs_apv_syntax_template.c b/libavcodec/cbs_apv_syntax_template.c
new file mode 100644
index 0000000000..190e900a9b
--- /dev/null
+++ b/libavcodec/cbs_apv_syntax_template.c
@@ -0,0 +1,598 @@
+/*
+ * 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(pbu_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawPBUHeader *current)
+{
+ int err;
+
+ ub(8, pbu_type);
+ ub(16, group_id);
+ u(8, reserved_zero_8bits, 0, 0);
+
+ return 0;
+}
+
+static int FUNC(byte_alignment)(CodedBitstreamContext *ctx, RWContext *rw)
+{
+ int err;
+
+ while (byte_alignment(rw) != 0)
+ fixed(1, alignment_bit_equal_to_zero, 0);
+
+ return 0;
+}
+
+static int FUNC(filler)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawFiller *current)
+{
+ int err;
+
+#ifdef READ
+ current->filler_size = 0;
+ while (show_bits(rw, 8) == 0xff) {
+ fixed(8, ff_byte, 0xff);
+ ++current->filler_size;
+ }
+#else
+ {
+ uint32_t i;
+ for (i = 0; i < current->filler_size; i++)
+ fixed(8, ff_byte, 0xff);
+ }
+#endif
+
+ return 0;
+}
+
+static int FUNC(frame_info)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawFrameInfo *current)
+{
+ int err;
+
+ ub(8, profile_idc);
+ ub(8, level_idc);
+ ub(3, band_idc);
+
+ u(5, reserved_zero_5bits, 0, 0);
+
+ ub(24, frame_width);
+ ub(24, frame_height);
+
+ u(4, chroma_format_idc, 0, 4);
+ if (current->chroma_format_idc == 1) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR,
+ "chroma_format_idc 1 for 4:2:0 is not allowed in APV.\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ u(4, bit_depth_minus8, 2, 8);
+
+ ub(8, capture_time_distance);
+
+ u(8, reserved_zero_8bits, 0, 0);
+
+ return 0;
+}
+
+static int FUNC(quantization_matrix)(CodedBitstreamContext *ctx,
+ RWContext *rw,
+ APVRawQuantizationMatrix *current)
+{
+ const CodedBitstreamAPVContext *priv = ctx->priv_data;
+ int err;
+
+ for (int c = 0; c < priv->num_comp; c++) {
+ for (int y = 0; y < 8; y++) {
+ for (int x = 0; x < 8 ; x++) {
+ us(8, q_matrix[c][x][y], 1, 255, 3, c, x, y);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawTileInfo *current,
+ const APVRawFrameHeader *fh)
+{
+ CodedBitstreamAPVContext *priv = ctx->priv_data;
+ int err;
+
+ u(20, tile_width_in_mbs,
+ APV_MIN_TILE_WIDTH_IN_MBS, MAX_UINT_BITS(20));
+ u(20, tile_height_in_mbs,
+ APV_MIN_TILE_HEIGHT_IN_MBS, MAX_UINT_BITS(20));
+
+ ub(1, tile_size_present_in_fh_flag);
+
+ cbs_apv_derive_tile_info(&priv->tile_info, fh);
+
+ if (current->tile_size_present_in_fh_flag) {
+ for (int t = 0; t < priv->tile_info.num_tiles; t++) {
+ us(32, tile_size_in_fh[t], 10, MAX_UINT_BITS(32), 1, t);
+ }
+ }
+
+ return 0;
+}
+
+static int FUNC(frame_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawFrameHeader *current)
+{
+ CodedBitstreamAPVContext *priv = ctx->priv_data;
+ int err;
+
+ CHECK(FUNC(frame_info)(ctx, rw, ¤t->frame_info));
+
+ u(8, reserved_zero_8bits, 0, 0);
+
+ ub(1, color_description_present_flag);
+ if (current->color_description_present_flag) {
+ ub(8, color_primaries);
+ ub(8, transfer_characteristics);
+ ub(8, matrix_coefficients);
+ } else {
+ infer(color_primaries, 2);
+ infer(transfer_characteristics, 2);
+ infer(matrix_coefficients, 2);
+ }
+
+ priv->bit_depth = current->frame_info.bit_depth_minus8 + 8;
+ priv->num_comp = cbs_apv_get_num_comp(current);
+ if (current->frame_info.chroma_format_idc == 2)
+ priv->sub_width_c = 2;
+ else
+ priv->sub_width_c = 1;
+ priv->sub_height_c = 1;
+
+ ub(1, use_q_matrix);
+ if (current->use_q_matrix) {
+ CHECK(FUNC(quantization_matrix)(ctx, rw,
+ ¤t->quantization_matrix));
+ } else {
+ for (int c = 0; c < priv->num_comp; c++) {
+ for (int y = 0; y < 8; y++) {
+ for (int x = 0; x < 8 ; x++) {
+ infer(quantization_matrix.q_matrix[c][y][x], 16);
+ }
+ }
+ }
+ }
+
+ CHECK(FUNC(tile_info)(ctx, rw, ¤t->tile_info, current));
+
+ u(8, reserved_zero_8bits_2, 0, 0);
+
+ CHECK(FUNC(byte_alignment)(ctx, rw));
+
+ return 0;
+}
+
+static int FUNC(tile_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawTileHeader *current, int tile_idx)
+{
+ const CodedBitstreamAPVContext *priv = ctx->priv_data;
+ uint16_t expected_tile_header_size;
+ uint8_t max_qp;
+ int err;
+
+ expected_tile_header_size = 4 + priv->num_comp * (4 + 1) + 1;
+
+ u(16, tile_header_size,
+ expected_tile_header_size, expected_tile_header_size);
+
+ u(16, tile_index, tile_idx, tile_idx);
+
+ for (int c = 0; c < priv->num_comp; c++) {
+ us(32, tile_data_size[c], 1, MAX_UINT_BITS(32), 1, c);
+ }
+
+ max_qp = 3 + priv->bit_depth * 6;
+ for (int c = 0; c < priv->num_comp; c++) {
+ us(8, tile_qp[c], 0, max_qp, 1, c);
+ }
+
+ u(8, reserved_zero_8bits, 0, 0);
+
+ return 0;
+}
+
+static int FUNC(tile)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawTile *current, int tile_idx)
+{
+ const CodedBitstreamAPVContext *priv = ctx->priv_data;
+ int err;
+
+ CHECK(FUNC(tile_header)(ctx, rw, ¤t->tile_header, tile_idx));
+
+ for (int c = 0; c < priv->num_comp; c++) {
+ uint32_t comp_size = current->tile_header.tile_data_size[c];
+#ifdef READ
+ int pos = get_bits_count(rw);
+ av_assert0(pos % 8 == 0);
+ current->tile_data[c] = (uint8_t*)align_get_bits(rw);
+ skip_bits_long(rw, 8 * comp_size);
+#else
+ if (put_bytes_left(rw, 0) < comp_size)
+ return AVERROR(ENOSPC);
+ ff_copy_bits(rw, current->tile_data[c], comp_size * 8);
+#endif
+ }
+
+ return 0;
+}
+
+static int FUNC(frame)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawFrame *current)
+{
+ const CodedBitstreamAPVContext *priv = ctx->priv_data;
+ int err;
+
+ HEADER("Frame");
+
+ CHECK(FUNC(pbu_header)(ctx, rw, ¤t->pbu_header));
+
+ CHECK(FUNC(frame_header)(ctx, rw, ¤t->frame_header));
+
+ for (int t = 0; t < priv->tile_info.num_tiles; t++) {
+ us(32, tile_size[t], 10, MAX_UINT_BITS(32), 1, t);
+
+ CHECK(FUNC(tile)(ctx, rw, ¤t->tile[t], t));
+ }
+
+ CHECK(FUNC(filler)(ctx, rw, ¤t->filler));
+
+ return 0;
+}
+
+static int FUNC(au_info)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawAUInfo *current)
+{
+ int err;
+
+ HEADER("Access Unit Information");
+
+ u(16, num_frames, 1, CBS_APV_MAX_AU_FRAMES);
+
+ for (int i = 0; i < current->num_frames; i++) {
+ ubs(8, pbu_type[i], 1, i);
+ ubs(8, group_id[i], 1, i);
+
+ us(8, reserved_zero_8bits[i], 0, 0, 1, i);
+
+ CHECK(FUNC(frame_info)(ctx, rw, ¤t->frame_info[i]));
+ }
+
+ u(8, reserved_zero_8bits_2, 0, 0);
+
+ return 0;
+}
+
+static int FUNC(metadata_itu_t_t35)(CodedBitstreamContext *ctx,
+ RWContext *rw,
+ APVRawMetadataITUTT35 *current,
+ size_t payload_size)
+{
+ int err;
+ size_t read_size = payload_size - 1;
+
+ HEADER("ITU-T T.35 Metadata");
+
+ ub(8, itu_t_t35_country_code);
+
+ if (current->itu_t_t35_country_code == 0xff) {
+ ub(8, itu_t_t35_country_code_extension);
+ --read_size;
+ }
+
+#ifdef READ
+ current->data_size = read_size;
+ current->data_ref = av_buffer_alloc(current->data_size);
+ if (!current->data_ref)
+ return AVERROR(ENOMEM);
+ current->data = current->data_ref->data;
+#else
+ if (current->data_size != read_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Write size mismatch: "
+ "payload %zu but expecting %zu\n",
+ current->data_size, read_size);
+ return AVERROR(EINVAL);
+ }
+#endif
+
+ for (size_t i = 0; i < current->data_size; i++) {
+ xu(8, itu_t_t35_payload[i],
+ current->data[i], 0x00, 0xff, 1, i);
+ }
+
+ return 0;
+}
+
+static int FUNC(metadata_mdcv)(CodedBitstreamContext *ctx,
+ RWContext *rw,
+ APVRawMetadataMDCV *current)
+{
+ int err, i;
+
+ HEADER("MDCV Metadata");
+
+ for (i = 0; i < 3; i++) {
+ ubs(16, primary_chromaticity_x[i], 1, i);
+ ubs(16, primary_chromaticity_y[i], 1, i);
+ }
+
+ ub(16, white_point_chromaticity_x);
+ ub(16, white_point_chromaticity_y);
+
+ ub(32, max_mastering_luminance);
+ ub(32, min_mastering_luminance);
+
+ return 0;
+}
+
+static int FUNC(metadata_cll)(CodedBitstreamContext *ctx,
+ RWContext *rw,
+ APVRawMetadataCLL *current)
+{
+ int err;
+
+ HEADER("CLL Metadata");
+
+ ub(16, max_cll);
+ ub(16, max_fall);
+
+ return 0;
+}
+
+static int FUNC(metadata_filler)(CodedBitstreamContext *ctx,
+ RWContext *rw,
+ APVRawMetadataFiller *current,
+ size_t payload_size)
+{
+ int err;
+
+ HEADER("Filler Metadata");
+
+ for (size_t i = 0; i < payload_size; i++)
+ fixed(8, ff_byte, 0xff);
+
+ return 0;
+}
+
+static int FUNC(metadata_user_defined)(CodedBitstreamContext *ctx,
+ RWContext *rw,
+ APVRawMetadataUserDefined *current,
+ size_t payload_size)
+{
+ int err;
+
+ HEADER("User-Defined Metadata");
+
+ for (int i = 0; i < 16; i++)
+ ubs(8, uuid[i], 1, i);
+
+#ifdef READ
+ current->data_size = payload_size - 16;
+ current->data_ref = av_buffer_alloc(current->data_size);
+ if (!current->data_ref)
+ return AVERROR(ENOMEM);
+ current->data = current->data_ref->data;
+#else
+ if (current->data_size != payload_size - 16) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Write size mismatch: "
+ "payload %zu but expecting %zu\n",
+ current->data_size, payload_size - 16);
+ return AVERROR(EINVAL);
+ }
+#endif
+
+ for (size_t i = 0; i < current->data_size; i++) {
+ xu(8, user_defined_data_payload[i],
+ current->data[i], 0x00, 0xff, 1, i);
+ }
+
+ return 0;
+}
+
+static int FUNC(metadata_undefined)(CodedBitstreamContext *ctx,
+ RWContext *rw,
+ APVRawMetadataUndefined *current,
+ size_t payload_size)
+{
+ int err;
+
+ HEADER("Undefined Metadata");
+
+#ifdef READ
+ current->data_size = payload_size;
+ current->data_ref = av_buffer_alloc(current->data_size);
+ if (!current->data_ref)
+ return AVERROR(ENOMEM);
+ current->data = current->data_ref->data;
+#else
+ if (current->data_size != payload_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Write size mismatch: "
+ "payload %zu but expecting %zu\n",
+ current->data_size, payload_size - 16);
+ return AVERROR(EINVAL);
+ }
+#endif
+
+ for (size_t i = 0; i < current->data_size; i++) {
+ xu(8, undefined_metadata_payload_byte[i],
+ current->data[i], 0x00, 0xff, 1, i);
+ }
+
+ return 0;
+}
+
+static int FUNC(metadata_payload)(CodedBitstreamContext *ctx,
+ RWContext *rw,
+ APVRawMetadataPayload *current)
+{
+ int err;
+
+ switch (current->payload_type) {
+ case APV_METADATA_ITU_T_T35:
+ CHECK(FUNC(metadata_itu_t_t35)(ctx, rw,
+ ¤t->itu_t_t35,
+ current->payload_size));
+ break;
+ case APV_METADATA_MDCV:
+ CHECK(FUNC(metadata_mdcv)(ctx, rw, ¤t->mdcv));
+ break;
+ case APV_METADATA_CLL:
+ CHECK(FUNC(metadata_cll)(ctx, rw, ¤t->cll));
+ break;
+ case APV_METADATA_FILLER:
+ CHECK(FUNC(metadata_filler)(ctx, rw,
+ ¤t->filler,
+ current->payload_size));
+ break;
+ case APV_METADATA_USER_DEFINED:
+ CHECK(FUNC(metadata_user_defined)(ctx, rw,
+ ¤t->user_defined,
+ current->payload_size));
+ break;
+ default:
+ CHECK(FUNC(metadata_undefined)(ctx, rw,
+ ¤t->undefined,
+ current->payload_size));
+ }
+
+ return 0;
+}
+
+static int FUNC(metadata)(CodedBitstreamContext *ctx, RWContext *rw,
+ APVRawMetadata *current)
+{
+ int err;
+
+#ifdef WRITE
+ PutBitContext metadata_start_state;
+ uint32_t metadata_start_position;
+ int trace;
+#endif
+
+ HEADER("Metadata");
+
+ CHECK(FUNC(pbu_header)(ctx, rw, ¤t->pbu_header));
+
+#ifdef READ
+ ub(32, metadata_size);
+
+ for (int p = 0; p < CBS_APV_MAX_METADATA_PAYLOADS; p++) {
+ APVRawMetadataPayload *pl = ¤t->payloads[p];
+ uint32_t metadata_bytes_left = current->metadata_size;
+ uint32_t tmp;
+
+ pl->payload_type = 0;
+ while (show_bits(rw, 8) == 0xff) {
+ fixed(8, ff_byte, 0xff);
+ pl->payload_type += 255;
+ --metadata_bytes_left;
+ }
+ xu(8, metadata_payload_type, tmp, 0, 254, 0);
+ pl->payload_type += tmp;
+ --metadata_bytes_left;
+
+ pl->payload_size = 0;
+ while (show_bits(rw, 8) == 0xff) {
+ fixed(8, ff_byte, 0xff);
+ pl->payload_size += 255;
+ --metadata_bytes_left;
+ }
+ xu(8, metadata_payload_size, tmp, 0, 254, 0);
+ pl->payload_size += tmp;
+ --metadata_bytes_left;
+
+ if (pl->payload_size > metadata_bytes_left) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid metadata: "
+ "payload_size larger than remaining metadata size "
+ "(%"PRIu32" bytes).\n", pl->payload_size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ CHECK(FUNC(metadata_payload)(ctx, rw, pl));
+
+ metadata_bytes_left -= pl->payload_size;
+
+ if (metadata_bytes_left == 0)
+ break;
+ }
+#else
+ // Two passes: the first write finds the size (with tracing
+ // disabled), the second write does the real write.
+
+ metadata_start_state = *rw;
+ metadata_start_position = put_bits_count(rw);
+
+ trace = ctx->trace_enable;
+ ctx->trace_enable = 0;
+
+ for (int pass = 1; pass <= 2; pass++) {
+ *rw = metadata_start_state;
+
+ ub(32, metadata_size);
+
+ for (int p = 0; p < current->metadata_count; p++) {
+ APVRawMetadataPayload *pl = ¤t->payloads[p];
+ uint32_t payload_start_position;
+ uint32_t tmp;
+
+ payload_start_position = put_bits_count(rw);
+
+ tmp = pl->payload_type;
+ while (tmp >= 255) {
+ fixed(8, ff_byte, 0xff);
+ tmp -= 255;
+ }
+ xu(8, metadata_payload_type, tmp, 0, 254, 0);
+
+ tmp = pl->payload_size;
+ while (tmp >= 255) {
+ fixed(8, ff_byte, 0xff);
+ tmp -= 255;
+ }
+ xu(8, metadata_payload_size, tmp, 0, 254, 0);
+
+ err = FUNC(metadata_payload)(ctx, rw, pl);
+ ctx->trace_enable = trace;
+ if (err < 0)
+ return err;
+
+ if (pass == 1) {
+ pl->payload_size = (put_bits_count(rw) -
+ payload_start_position) / 8;
+ }
+ }
+
+ if (pass == 1) {
+ current->metadata_size = (put_bits_count(rw) -
+ metadata_start_position) / 8;
+ ctx->trace_enable = trace;
+ }
+ }
+#endif
+
+ CHECK(FUNC(filler)(ctx, rw, ¤t->filler));
+
+ return 0;
+}
diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
index 1ed1f04c15..c3265924ba 100644
--- a/libavcodec/cbs_internal.h
+++ b/libavcodec/cbs_internal.h
@@ -42,6 +42,9 @@
#define CBS_TRACE 1
#endif
+#ifndef CBS_APV
+#define CBS_APV CONFIG_CBS_APV
+#endif
#ifndef CBS_AV1
#define CBS_AV1 CONFIG_CBS_AV1
#endif
@@ -383,6 +386,7 @@ int CBS_FUNC(write_signed)(CodedBitstreamContext *ctx, PutBitContext *pbc,
#define CBS_UNIT_TYPE_END_OF_LIST { .nb_unit_types = 0 }
+extern const CodedBitstreamType CBS_FUNC(type_apv);
extern const CodedBitstreamType CBS_FUNC(type_av1);
extern const CodedBitstreamType CBS_FUNC(type_h264);
extern const CodedBitstreamType CBS_FUNC(type_h265);
diff --git a/libavformat/cbs.h b/libavformat/cbs.h
index e4dc231001..0fab3a7457 100644
--- a/libavformat/cbs.h
+++ b/libavformat/cbs.h
@@ -22,6 +22,7 @@
#define CBS_PREFIX lavf_cbs
#define CBS_WRITE 0
#define CBS_TRACE 0
+#define CBS_APV 0
#define CBS_H264 0
#define CBS_H265 0
#define CBS_H266 0
--
2.47.2
More information about the ffmpeg-devel
mailing list