lavc/cbs: APV support

This commit is contained in:
Mark Thompson
2025-04-19 18:44:14 +01:00
parent 0c79a091e4
commit 821717c3fe
9 changed files with 1363 additions and 0 deletions

1
configure vendored
View File

@ -2562,6 +2562,7 @@ CONFIG_EXTRA="
bswapdsp
cabac
cbs
cbs_apv
cbs_av1
cbs_h264
cbs_h265

View File

@ -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

89
libavcodec/apv.h Normal file
View File

@ -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 */

View File

@ -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

455
libavcodec/cbs_apv.c Normal file
View File

@ -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,
};

207
libavcodec/cbs_apv.h Normal file
View File

@ -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 */

View File

@ -0,0 +1,599 @@
/*
* 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, &current->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,
&current->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, &current->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, &current->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, &current->pbu_header));
CHECK(FUNC(frame_header)(ctx, rw, &current->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, &current->tile[t], t));
}
CHECK(FUNC(filler)(ctx, rw, &current->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, &current->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,
&current->itu_t_t35,
current->payload_size));
break;
case APV_METADATA_MDCV:
CHECK(FUNC(metadata_mdcv)(ctx, rw, &current->mdcv));
break;
case APV_METADATA_CLL:
CHECK(FUNC(metadata_cll)(ctx, rw, &current->cll));
break;
case APV_METADATA_FILLER:
CHECK(FUNC(metadata_filler)(ctx, rw,
&current->filler,
current->payload_size));
break;
case APV_METADATA_USER_DEFINED:
CHECK(FUNC(metadata_user_defined)(ctx, rw,
&current->user_defined,
current->payload_size));
break;
default:
CHECK(FUNC(metadata_undefined)(ctx, rw,
&current->undefined,
current->payload_size));
}
return 0;
}
static int FUNC(metadata)(CodedBitstreamContext *ctx, RWContext *rw,
APVRawMetadata *current)
{
int err;
#ifdef READ
uint32_t metadata_bytes_left;
#else
PutBitContext metadata_start_state;
uint32_t metadata_start_position;
int trace;
#endif
HEADER("Metadata");
CHECK(FUNC(pbu_header)(ctx, rw, &current->pbu_header));
#ifdef READ
ub(32, metadata_size);
metadata_bytes_left = current->metadata_size;
for (int p = 0; p < CBS_APV_MAX_METADATA_PAYLOADS; p++) {
APVRawMetadataPayload *pl = &current->payloads[p];
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 = &current->payloads[p];
uint32_t payload_start_position;
uint32_t tmp;
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);
payload_start_position = put_bits_count(rw);
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 - 4;
ctx->trace_enable = trace;
}
}
#endif
CHECK(FUNC(filler)(ctx, rw, &current->filler));
return 0;
}

View File

@ -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);

View File

@ -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