--- configure | 1 + libavcodec/Makefile | 1 + libavcodec/apv.h | 89 ++++ libavcodec/cbs.c | 6 + libavcodec/cbs_apv.c | 455 ++++++++++++++++++++ libavcodec/cbs_apv.h | 207 ++++++++++ libavcodec/cbs_apv_syntax_template.c | 596 +++++++++++++++++++++++++++ libavcodec/cbs_internal.h | 4 + libavformat/cbs.h | 1 + 9 files changed, 1360 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..14ca27bf31 --- /dev/null +++ b/libavcodec/apv.h @@ -0,0 +1,89 @@ +/* + * 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 + +// Signature value in APV bitstreams (section 5.3.1). +#define APV_SIGNATURE MKBETAG('a', 'P', 'v', '1') + +// 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 398d286a92..c0f6c62d20 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..2c4a1daaf0 --- /dev/null +++ b/libavcodec/cbs_apv.c @@ -0,0 +1,455 @@ +/* + * 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; + uint32_t signature; + int err, trace; + + if (header) { + // Ignore extradata fragments. + return 0; + } + + if (frag->data_size < 4) { + // Too small to be a valid fragment. + return AVERROR_INVALIDDATA; + } + + // Don't include parsing here in trace output. + trace = ctx->trace_enable; + ctx->trace_enable = 0; + + signature = AV_RB32(data); + if (signature != APV_SIGNATURE) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid APV access unit: bad signature %08x.\n", + signature); + err = AVERROR_INVALIDDATA; + goto fail; + } + data += 4; + size -= 4; + + 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 < 8) { + 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: + case APV_PBU_NON_PRIMARY_FRAME: + case APV_PBU_PREVIEW_FRAME: + case APV_PBU_DEPTH_FRAME: + case APV_PBU_ALPHA_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: + case APV_PBU_NON_PRIMARY_FRAME: + case APV_PBU_PREVIEW_FRAME: + case APV_PBU_DEPTH_FRAME: + case APV_PBU_ALPHA_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 = 4, 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); + + AV_WB32(frag->data, APV_SIGNATURE); + pos = 4; + 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 void cbs_apv_free_metadata(AVRefStructOpaque unused, void *content) +{ + APVRawMetadata *md = content; + av_assert0(md->pbu_header.pbu_type == APV_PBU_METADATA); + + for (int i = 0; i < md->metadata_count; i++) { + APVRawMetadataPayload *pl = &md->payloads[i]; + + switch (pl->payload_type) { + case APV_METADATA_MDCV: + case APV_METADATA_CLL: + case APV_METADATA_FILLER: + break; + case APV_METADATA_ITU_T_T35: + av_buffer_unref(&pl->itu_t_t35.data_ref); + break; + case APV_METADATA_USER_DEFINED: + av_buffer_unref(&pl->user_defined.data_ref); + break; + default: + av_buffer_unref(&pl->undefined.data_ref); + } + } +} + +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_COMPLEX(APV_PBU_METADATA, APVRawMetadata, + &cbs_apv_free_metadata), + + CBS_UNIT_TYPE_POD(APV_PBU_ACCESS_UNIT_INFORMATION, APVRawAUInfo), + CBS_UNIT_TYPE_POD(APV_PBU_FILLER, APVRawFiller), + + 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..cbaeb45acb --- /dev/null +++ b/libavcodec/cbs_apv.h @@ -0,0 +1,207 @@ +/* + * 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; + + 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..7864dfca9d --- /dev/null +++ b/libavcodec/cbs_apv_syntax_template.c @@ -0,0 +1,596 @@ +/* + * 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); + ub(1, full_range_flag); + } else { + infer(color_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + infer(full_range_flag, 0); + } + + priv->bit_depth = current->frame_info.bit_depth_minus8 + 8; + priv->num_comp = cbs_apv_get_num_comp(current); + + 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; + + current->metadata_count = p + 1; + 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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".