blob: cb51b05d7fdc9783dbdeffcc8f2f3f9f3b839766 [file] [log] [blame]
/*
* Copyright (c) 2021, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 3-Clause Clear License
* and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
* License was not distributed with this source code in the LICENSE file, you
* can obtain it at aomedia.org/license/software-license/bsd-3-c-c/. If the
* Alliance for Open Media Patent License 1.0 was not distributed with this
* source code in the PATENTS file, you can obtain it at
* aomedia.org/license/patent-license/.
*/
#include <assert.h>
#include "config/avm_config.h"
#include "av2/common/banding_metadata.h"
#include "config/avm_scale_rtcd.h"
#include "avm/avm_codec.h"
#include "avm_dsp/bitreader_buffer.h"
#include "avm_mem/avm_mem.h"
#include "avm_ports/mem_ops.h"
#include "av2/common/common.h"
#include "av2/common/obu_util.h"
#include "av2/common/level.h"
#include "av2/common/timing.h"
#include "av2/decoder/decoder.h"
#include "av2/decoder/decodeframe.h"
#include "av2/decoder/obu.h"
#include "av2/common/enums.h"
#include "av2/common/annexA.h"
#include "av2/decoder/annexF.h"
static uint32_t read_temporal_delimiter_obu() { return 0; }
// Returns a boolean that indicates success.
static int read_bitstream_level(AV2_LEVEL *seq_level_idx,
struct avm_read_bit_buffer *rb) {
*seq_level_idx = avm_rb_read_literal(rb, LEVEL_BITS);
if (!is_valid_seq_level_idx(*seq_level_idx)) return 0;
return 1;
}
static void av2_read_tlayer_dependency_info(SequenceHeader *const seq,
struct avm_read_bit_buffer *rb) {
const int max_mlayer_id = seq->max_mlayer_id;
const int max_tlayer_id = seq->max_tlayer_id;
const int multi_tlayer_flag = seq->multi_tlayer_dependency_map_present_flag;
for (int curr_mlayer_id = 0; curr_mlayer_id <= max_mlayer_id;
curr_mlayer_id++) {
for (int curr_tlayer_id = 1; curr_tlayer_id <= max_tlayer_id;
curr_tlayer_id++) {
for (int ref_tlayer_id = curr_tlayer_id; ref_tlayer_id >= 0;
ref_tlayer_id--) {
if (multi_tlayer_flag > 0 || curr_mlayer_id == 0) {
seq->tlayer_dependency_map[curr_mlayer_id][curr_tlayer_id]
[ref_tlayer_id] = avm_rb_read_bit(rb);
} else {
seq->tlayer_dependency_map[curr_mlayer_id][curr_tlayer_id]
[ref_tlayer_id] =
seq->tlayer_dependency_map[0][curr_tlayer_id][ref_tlayer_id];
}
}
}
}
}
static void av2_read_mlayer_dependency_info(SequenceHeader *const seq,
struct avm_read_bit_buffer *rb) {
const int max_mlayer_id = seq->max_mlayer_id;
for (int curr_mlayer_id = 1; curr_mlayer_id <= max_mlayer_id;
curr_mlayer_id++) {
for (int ref_mlayer_id = curr_mlayer_id; ref_mlayer_id >= 0;
ref_mlayer_id--) {
seq->mlayer_dependency_map[curr_mlayer_id][ref_mlayer_id] =
avm_rb_read_bit(rb);
}
}
}
// This function validates the conformance window params
static void av2_validate_seq_conformance_window(
const struct SequenceHeader *seq_params,
struct avm_internal_error_info *error_info) {
const struct CropWindow *conf = &seq_params->conf;
if (!conf->conf_win_enabled_flag) return;
if (conf->conf_win_left_offset >= seq_params->max_frame_width) {
avm_internal_error(
error_info, AVM_CODEC_UNSUP_BITSTREAM,
"Conformance window left offset %d exceeds max width %d\n",
conf->conf_win_left_offset, seq_params->max_frame_width);
}
if (conf->conf_win_right_offset >= seq_params->max_frame_width) {
avm_internal_error(
error_info, AVM_CODEC_UNSUP_BITSTREAM,
"Conformance window right offset %d exceeds max width %d\n",
conf->conf_win_right_offset, seq_params->max_frame_width);
}
if (conf->conf_win_top_offset >= seq_params->max_frame_height) {
avm_internal_error(
error_info, AVM_CODEC_UNSUP_BITSTREAM,
"Conformance window top offset %d exceeds max height %d\n",
conf->conf_win_top_offset, seq_params->max_frame_height);
}
if (conf->conf_win_bottom_offset >= seq_params->max_frame_height) {
avm_internal_error(
error_info, AVM_CODEC_UNSUP_BITSTREAM,
"Conformance window bottom offset %d exceeds max height %d\n",
conf->conf_win_bottom_offset, seq_params->max_frame_height);
}
}
// Returns whether two sequence headers are consistent with each other.
// Note that the 'op_params' field is not compared per Section 7.5 in the spec:
// Within a particular coded video sequence, the contents of
// sequence_header_obu must be bit-identical each time the sequence header
// appears except for the contents of operating_parameters_info.
int are_seq_headers_consistent(const SequenceHeader *seq_params_old,
const SequenceHeader *seq_params_new) {
return !memcmp(seq_params_old, seq_params_new,
offsetof(SequenceHeader, op_params));
}
void av2_read_color_info(int *color_description_idc, int *color_primaries,
int *transfer_characteristics,
int *matrix_coefficients, int *full_range_flag,
struct avm_read_bit_buffer *rb) {
// color_description_idc: indicates the combination of color primaries,
// transfer characteristics and matrix coefficients as defined in Section
// 6.10.4 (Operating point set color info semantics) in the spec.
// The value of color_description_idc shall be in the range of 0 to 127,
// inclusive. Values larger than 5 are reserved for future use by AOMedia and
// should be ignored by decoders conforming to this version of this
// specification.
*color_description_idc = avm_rb_read_rice_golomb(rb, 2);
if (*color_description_idc > 127) {
rb->error_handler(rb->error_handler_data, AVM_CODEC_UNSUP_BITSTREAM,
"color_description_idc is not in the range of 0 to 127");
}
switch (*color_description_idc) {
case AVM_COLOR_DESC_IDC_EXPLICIT: // 0
*color_primaries = avm_rb_read_literal(rb, 8);
*transfer_characteristics = avm_rb_read_literal(rb, 8);
*matrix_coefficients = avm_rb_read_literal(rb, 8);
break;
case AVM_COLOR_DESC_IDC_BT709SDR: // 1
*color_primaries = AVM_CICP_CP_BT_709; // 1
*transfer_characteristics = AVM_CICP_TC_BT_709; // 1
*matrix_coefficients = AVM_CICP_MC_BT_709; // 1
break;
case AVM_COLOR_DESC_IDC_BT2100PQ: // 2
*color_primaries = AVM_CICP_CP_BT_2020; // 9
*transfer_characteristics = AVM_CICP_TC_SMPTE_2084; // 16
*matrix_coefficients = AVM_CICP_MC_BT_2020_NCL; // 9
break;
case AVM_COLOR_DESC_IDC_BT2100HLG: // 3
*color_primaries = AVM_CICP_CP_BT_2020; // 9
*transfer_characteristics = AVM_CICP_TC_HLG; // 18
*matrix_coefficients = AVM_CICP_MC_BT_2020_NCL; // 9
break;
case AVM_COLOR_DESC_IDC_SRGB: // 4
*color_primaries = AVM_CICP_CP_BT_709; // 1
*transfer_characteristics = AVM_CICP_TC_SRGB; // 13
*matrix_coefficients = AVM_CICP_MC_IDENTITY; // 0
break;
case AVM_COLOR_DESC_IDC_SYCC: // 5
*color_primaries = AVM_CICP_CP_BT_709; // 1
*transfer_characteristics = AVM_CICP_TC_SRGB; // 13
*matrix_coefficients = AVM_CICP_MC_BT_470_B_G; // 5
break;
default:
// Values larger than 5 are reserved for future use by AOMedia and should
// be ignored by decoders.
*color_primaries = AVM_CICP_CP_UNSPECIFIED;
*transfer_characteristics = AVM_CICP_TC_UNSPECIFIED;
*matrix_coefficients = AVM_CICP_MC_UNSPECIFIED;
break;
}
*full_range_flag = avm_rb_read_bit(rb);
}
// Helper function to store xlayer context
// Helper function to map xlayer_id to stream_id array index
int av2_get_stream_index(const AV2_COMMON *cm, int xlayer_id) {
// GLOBAL_XLAYER_ID doesn't use stream_info
if (xlayer_id == GLOBAL_XLAYER_ID) {
return -1;
}
// Find which index in stream_ids matches this xlayer_id
for (int i = 0; i < cm->num_streams; i++) {
if (cm->stream_ids[i] == xlayer_id) {
return i;
}
}
// Should never happen with valid bitstream
return -1;
}
void av2_store_xlayer_context(AV2Decoder *pbi, AV2_COMMON *cm, int xlayer_id) {
int stream_idx = av2_get_stream_index(cm, xlayer_id);
if (stream_idx < 0) return; // Invalid or GLOBAL_XLAYER_ID
for (int i = 0; i < REF_FRAMES; i++) {
pbi->stream_info[stream_idx].ref_frame_map_buf[i] = cm->ref_frame_map[i];
pbi->stream_info[stream_idx].valid_for_referencing_buf[i] =
pbi->valid_for_referencing[i];
}
for (int i = 0; i < INTER_REFS_PER_FRAME; i++) {
pbi->stream_info[stream_idx].remapped_ref_idx_buf[i] =
cm->remapped_ref_idx[i];
}
for (int i = 0; i < MAX_MFH_NUM; i++) {
pbi->stream_info[stream_idx].mfh_params_buf[i] = cm->mfh_params[i];
}
// Global OBUs (xlayer_id=31) excluded from per-layer save/restore
pbi->stream_info[stream_idx].lcr_counter_buf = pbi->lcr_counter;
pbi->stream_info[stream_idx].active_lcr_buf = pbi->active_lcr;
pbi->stream_info[stream_idx].active_atlas_segment_info_buf =
pbi->active_atlas_segment_info;
pbi->stream_info[stream_idx].ops_counter_buf = pbi->ops_counter;
for (int i = 0; i < NUM_CUSTOM_QMS; i++) {
pbi->stream_info[stream_idx].qm_list_buf[i] = pbi->qm_list[i];
pbi->stream_info[stream_idx].qm_protected_buf[i] = pbi->qm_protected[i];
}
pbi->stream_info[stream_idx].olk_encountered_buf = pbi->olk_encountered;
pbi->stream_info[stream_idx].random_access_point_index_buf =
pbi->random_access_point_index;
pbi->stream_info[stream_idx].random_access_point_count_buf =
pbi->random_access_point_count;
for (int i = 0; i < MAX_FGM_NUM; i++) {
pbi->stream_info[stream_idx].fgm_list_buf[i] = pbi->fgm_list[i];
}
pbi->stream_info[stream_idx].prev_frame_buf = cm->prev_frame;
pbi->stream_info[stream_idx].last_frame_seg_map_buf = cm->last_frame_seg_map;
for (int i = 0; i < MAX_NUM_MLAYERS; i++) {
pbi->stream_info[stream_idx].ci_params_per_layer_buf[i] =
cm->ci_params_per_layer[i];
}
for (int i = 0; i < MAX_NUM_MLAYERS; i++) {
pbi->stream_info[stream_idx].olk_refresh_frame_flags_buf[i] =
cm->olk_refresh_frame_flags[i];
pbi->stream_info[stream_idx].olk_co_vcl_refresh_frame_flags_buf[i] =
cm->olk_co_vcl_refresh_frame_flags[i];
}
pbi->stream_info[stream_idx].seq_params_buf = cm->seq_params;
for (int i = 0; i < MAX_MFH_NUM; i++) {
pbi->stream_info[stream_idx].mfh_valid_buf[i] = cm->mfh_valid[i];
}
pbi->stream_info[stream_idx].decoding_first_frame = pbi->decoding_first_frame;
pbi->stream_info[stream_idx].last_olk_tu_display_order_hint =
pbi->last_olk_tu_display_order_hint;
}
// Helper function to restore xlayer context
void av2_restore_xlayer_context(AV2Decoder *pbi, AV2_COMMON *cm,
int xlayer_id) {
int stream_idx = av2_get_stream_index(cm, xlayer_id);
if (stream_idx < 0) return; // Invalid or GLOBAL_XLAYER_ID
// Check if stream_info is valid
if (pbi->stream_info == NULL) {
return;
}
for (int i = 0; i < REF_FRAMES; i++) {
cm->ref_frame_map[i] = pbi->stream_info[stream_idx].ref_frame_map_buf[i];
pbi->valid_for_referencing[i] =
pbi->stream_info[stream_idx].valid_for_referencing_buf[i];
}
for (int i = 0; i < INTER_REFS_PER_FRAME; i++) {
cm->remapped_ref_idx[i] =
pbi->stream_info[stream_idx].remapped_ref_idx_buf[i];
}
for (int i = 0; i < MAX_MFH_NUM; i++) {
cm->mfh_params[i] = pbi->stream_info[stream_idx].mfh_params_buf[i];
}
// Global OBUs (xlayer_id=31) excluded from per-layer save/restore
pbi->lcr_counter = pbi->stream_info[stream_idx].lcr_counter_buf;
pbi->active_lcr = pbi->stream_info[stream_idx].active_lcr_buf;
pbi->active_atlas_segment_info =
pbi->stream_info[stream_idx].active_atlas_segment_info_buf;
pbi->ops_counter = pbi->stream_info[stream_idx].ops_counter_buf;
for (int i = 0; i < NUM_CUSTOM_QMS; i++) {
pbi->qm_list[i] = pbi->stream_info[stream_idx].qm_list_buf[i];
pbi->qm_protected[i] = pbi->stream_info[stream_idx].qm_protected_buf[i];
}
pbi->olk_encountered = pbi->stream_info[stream_idx].olk_encountered_buf;
pbi->random_access_point_index =
pbi->stream_info[stream_idx].random_access_point_index_buf;
pbi->random_access_point_count =
pbi->stream_info[stream_idx].random_access_point_count_buf;
for (int i = 0; i < MAX_FGM_NUM; i++) {
pbi->fgm_list[i] = pbi->stream_info[stream_idx].fgm_list_buf[i];
}
cm->prev_frame = pbi->stream_info[stream_idx].prev_frame_buf;
cm->last_frame_seg_map = pbi->stream_info[stream_idx].last_frame_seg_map_buf;
for (int i = 0; i < MAX_NUM_MLAYERS; i++) {
cm->ci_params_per_layer[i] =
pbi->stream_info[stream_idx].ci_params_per_layer_buf[i];
}
for (int i = 0; i < MAX_NUM_MLAYERS; i++) {
cm->olk_refresh_frame_flags[i] =
pbi->stream_info[stream_idx].olk_refresh_frame_flags_buf[i];
cm->olk_co_vcl_refresh_frame_flags[i] =
pbi->stream_info[stream_idx].olk_co_vcl_refresh_frame_flags_buf[i];
}
cm->seq_params = pbi->stream_info[stream_idx].seq_params_buf;
for (int i = 0; i < MAX_MFH_NUM; i++) {
cm->mfh_valid[i] = pbi->stream_info[stream_idx].mfh_valid_buf[i];
}
pbi->decoding_first_frame = pbi->stream_info[stream_idx].decoding_first_frame;
pbi->last_olk_tu_display_order_hint =
pbi->stream_info[stream_idx].last_olk_tu_display_order_hint;
}
static void init_stream_info(StreamInfo *stream_info) {
stream_info->olk_encountered_buf = 0;
stream_info->random_access_point_index_buf = -1;
stream_info->random_access_point_count_buf = 0;
for (int i = 0; i < INTER_REFS_PER_FRAME; ++i) {
stream_info->remapped_ref_idx_buf[i] = INVALID_IDX;
}
for (int i = 0; i < REF_FRAMES; i++) {
stream_info->ref_frame_map_buf[i] = NULL;
}
stream_info->mfh_valid_buf[0] = true;
for (int i = 1; i < MAX_MFH_NUM; i++) {
stream_info->mfh_valid_buf[i] = false;
}
for (int i = 0; i < MAX_NUM_MLAYERS; i++) {
av2_initialize_ci_params(&stream_info->ci_params_per_layer_buf[i]);
}
stream_info->decoding_first_frame = 1;
stream_info->last_olk_tu_display_order_hint = -1;
}
/*!
* \brief Save current MSDO configuration
*/
static void save_msdo_config(const AV2Decoder *pbi, MsdoConfig *config) {
const AV2_COMMON *const cm = &pbi->common;
config->multistream_profile_idc =
pbi->common.msdo_params.multistream_profile_idc;
config->multistream_tier_idx = pbi->common.msdo_params.multistream_tier_idx;
config->multistream_level_idx = pbi->common.msdo_params.multistream_level_idx;
config->num_streams = cm->num_streams;
memcpy(config->stream_ids, cm->stream_ids, cm->num_streams * sizeof(int));
}
/*!
* \brief Compare two MSDO configurations
* \return true if configurations are identical, false otherwise
*/
static bool msdo_config_equal(const MsdoConfig *a, const MsdoConfig *b) {
if (a->multistream_profile_idc != b->multistream_profile_idc) return false;
if (a->multistream_tier_idx != b->multistream_tier_idx) return false;
if (a->multistream_level_idx != b->multistream_level_idx) return false;
if (a->num_streams != b->num_streams) return false;
return true;
}
avm_codec_err_t flush_all_xlayer_frames(struct AV2Decoder *pbi, AV2_COMMON *cm,
bool release_dpb) {
BufferPool *const pool = cm->buffer_pool;
int saved_xlayer_id = cm->xlayer_id;
avm_codec_err_t err = AVM_CODEC_OK;
for (int xlayer = 0; xlayer < AVM_MAX_NUM_STREAMS; xlayer++) {
if (xlayer == GLOBAL_XLAYER_ID) continue; // Global context is not a stream
if (pbi->xlayer_id_map[xlayer] > 0) {
if (xlayer != cm->xlayer_id) {
av2_store_xlayer_context(pbi, cm, cm->xlayer_id);
cm->xlayer_id = xlayer;
av2_restore_xlayer_context(pbi, cm, xlayer);
}
size_t num_before = pbi->num_output_frames;
err = flush_remaining_frames(pbi, INT_MAX);
if (err != AVM_CODEC_OK) break;
for (size_t j = num_before; j < pbi->num_output_frames; j++) {
decrease_ref_count(pbi->output_frames[j], pool);
}
if (release_dpb) {
for (int r = 0; r < REF_FRAMES; r++) {
decrease_ref_count(cm->ref_frame_map[r], pool);
cm->ref_frame_map[r] = NULL;
}
}
}
}
if (cm->xlayer_id != saved_xlayer_id) {
av2_store_xlayer_context(pbi, cm, cm->xlayer_id);
cm->xlayer_id = saved_xlayer_id;
av2_restore_xlayer_context(pbi, cm, saved_xlayer_id);
}
return err;
}
static uint32_t read_multi_stream_decoder_operation_obu(
AV2Decoder *pbi, struct avm_read_bit_buffer *rb) {
AV2_COMMON *const cm = &pbi->common;
const uint32_t saved_bit_offset = rb->bit_offset;
// Verify rb has been configured to report errors.
assert(rb->error_handler);
// Save previous configuration if it exists
MsdoConfig prev_config;
bool has_previous = false;
if (pbi->stream_info != NULL) {
save_msdo_config(pbi, &prev_config);
has_previous = true;
}
const int num_streams =
avm_rb_read_literal(rb, 3) + 2; // read number of streams
if (num_streams > AVM_MAX_NUM_STREAMS) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"The number of streams cannot exceed the max value (4).");
}
cm->num_streams = num_streams;
pbi->common.msdo_params.multistream_profile_idc =
avm_rb_read_literal(rb, PROFILE_BITS); // read profile of multistream
pbi->common.msdo_params.multistream_level_idx =
avm_rb_read_literal(rb, LEVEL_BITS); // read level of multistream
pbi->common.msdo_params.multistream_tier_idx =
avm_rb_read_bit(rb); // read tier of multistream
const int multistream_even_allocation_flag =
avm_rb_read_bit(rb); // read multistream_even_allocation_flag
if (!multistream_even_allocation_flag) {
const int multistream_large_picture_idc =
avm_rb_read_literal(rb, 3); // read multistream_large_picture_idc
(void)multistream_large_picture_idc;
}
for (int i = 0; i < num_streams; i++) {
cm->stream_ids[i] = avm_rb_read_literal(rb, XLAYER_BITS); // read stream ID
const int substream_profile_idc =
avm_rb_read_literal(rb, PROFILE_BITS); // read profile of multistream
(void)substream_profile_idc;
const int substream_level_idx =
avm_rb_read_literal(rb, LEVEL_BITS); // read level of multistream
(void)substream_level_idx;
const int substream_tier_idx =
avm_rb_read_bit(rb); // read tier of multistream
(void)substream_tier_idx;
}
cm->msdo_params.msdo_doh_constraint_flag = avm_rb_read_bit(rb);
// Check if configuration changed
MsdoConfig new_config;
save_msdo_config(pbi, &new_config);
bool config_changed =
!has_previous || !msdo_config_equal(&prev_config, &new_config);
// Flush remaining frames from all active streams before switching config
if (pbi->stream_info != NULL && config_changed) {
flush_all_xlayer_frames(pbi, cm, true);
avm_free(pbi->stream_info);
pbi->stream_info = NULL;
// Reset xlayer_id_map so only the new segment's layers are active
for (int i = 0; i < AVM_MAX_NUM_STREAMS; i++) {
pbi->xlayer_id_map[i] = 0;
}
}
// Only allocate if stream_info is NULL (first time OR after freeing due to
// config change)
if (pbi->stream_info == NULL) {
pbi->stream_info =
(StreamInfo *)avm_malloc(num_streams * sizeof(StreamInfo));
if (pbi->stream_info == NULL) {
avm_internal_error(&cm->error, AVM_CODEC_MEM_ERROR,
"Memory allocation failed for pbi->stream_info\n");
}
memset(pbi->stream_info, 0, num_streams * sizeof(StreamInfo));
for (int i = 0; i < num_streams; i++) {
init_stream_info(&pbi->stream_info[i]);
}
}
pbi->msdo_is_present_in_tu = 1;
if (av2_check_trailing_bits(pbi, rb) != 0) {
return 0;
}
return ((rb->bit_offset - saved_bit_offset + 7) >> 3);
}
// On success, returns the number of bytes read from 'rb'.
// On failure, sets pbi->common.error.error_code and returns 0.
static uint32_t read_sequence_header_obu(AV2Decoder *pbi, int xlayer_id,
struct avm_read_bit_buffer *rb,
uint16_t *acc_sh_id_bitmap) {
AV2_COMMON *const cm = &pbi->common;
const uint32_t saved_bit_offset = rb->bit_offset;
// Verify rb has been configured to report errors.
assert(rb->error_handler);
// Use an element in the pbi->seq_list array to store the information as we
// decode. At the end, if no errors have occurred, cm->seq_params is updated.
uint32_t seq_header_id = avm_rb_read_uvlc(rb);
if (seq_header_id >= MAX_SEQ_NUM) {
cm->error.error_code = AVM_CODEC_UNSUP_BITSTREAM;
return 0;
}
struct SequenceHeader *seq_params = &pbi->seq_list[xlayer_id][seq_header_id];
seq_params->seq_header_id = seq_header_id;
seq_params->sh_from_leading = 0;
acc_sh_id_bitmap[xlayer_id] |= (1 << seq_header_id);
seq_params->seq_profile_idc = av2_read_profile(rb);
if (seq_params->seq_profile_idc >= MAX_PROFILES) {
cm->error.error_code = AVM_CODEC_UNSUP_BITSTREAM;
return 0;
}
seq_params->single_picture_header_flag = avm_rb_read_bit(rb);
if (!read_bitstream_level(&seq_params->seq_max_level_idx, rb)) {
cm->error.error_code = AVM_CODEC_UNSUP_BITSTREAM;
return 0;
}
if (seq_params->seq_max_level_idx >= SEQ_LEVEL_4_0 &&
!seq_params->single_picture_header_flag)
seq_params->seq_tier = avm_rb_read_bit(rb);
else
seq_params->seq_tier = 0;
av2_read_chroma_format_bitdepth(rb, seq_params, &cm->error);
if (seq_params->single_picture_header_flag) {
seq_params->seq_lcr_id = LCR_ID_UNSPECIFIED;
seq_params->still_picture = 1;
seq_params->max_tlayer_id = 0;
seq_params->max_mlayer_id = 0;
seq_params->seq_max_mlayer_cnt = 1;
seq_params->monotonic_output_order_flag = 1;
} else {
int seq_lcr_id = avm_rb_read_literal(rb, 3);
if (seq_lcr_id > MAX_NUM_SEQ_LCR_ID) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Unsupported LCR id in the Sequence Header.\n");
}
seq_params->seq_lcr_id = seq_lcr_id;
seq_params->still_picture = avm_rb_read_bit(rb);
seq_params->max_tlayer_id = avm_rb_read_literal(rb, TLAYER_BITS);
seq_params->max_mlayer_id = avm_rb_read_literal(rb, MLAYER_BITS);
if (seq_params->max_mlayer_id > 0) {
int n = avm_ceil_log2(seq_params->max_mlayer_id + 1);
int seq_max_mlayer_cnt_minus_1 = avm_rb_read_literal(rb, n);
if (seq_max_mlayer_cnt_minus_1 > seq_params->max_mlayer_id) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"seq_max_mlayer_cnt_minus_1 %d is greater than max_mlayer_id %d",
seq_max_mlayer_cnt_minus_1, seq_params->max_mlayer_id);
}
seq_params->seq_max_mlayer_cnt = seq_max_mlayer_cnt_minus_1 + 1;
} else {
seq_params->seq_max_mlayer_cnt = 1;
}
seq_params->monotonic_output_order_flag = avm_rb_read_bit(rb);
}
const int num_bits_width = avm_rb_read_literal(rb, 4) + 1;
const int num_bits_height = avm_rb_read_literal(rb, 4) + 1;
const int max_frame_width = avm_rb_read_literal(rb, num_bits_width) + 1;
const int max_frame_height = avm_rb_read_literal(rb, num_bits_height) + 1;
seq_params->num_bits_width = num_bits_width;
seq_params->num_bits_height = num_bits_height;
seq_params->max_frame_width = max_frame_width;
seq_params->max_frame_height = max_frame_height;
av2_read_conformance_window(rb, seq_params);
av2_validate_seq_conformance_window(seq_params, &cm->error);
if (seq_params->single_picture_header_flag) {
seq_params->decoder_model_info_present_flag = 0;
seq_params->display_model_info_present_flag = 0;
} else {
seq_params->seq_max_display_model_info_present_flag = avm_rb_read_bit(rb);
seq_params->seq_max_initial_display_delay_minus_1 =
BUFFER_POOL_MAX_SIZE - 1;
if (seq_params->seq_max_display_model_info_present_flag)
seq_params->seq_max_initial_display_delay_minus_1 =
avm_rb_read_literal(rb, 4);
seq_params->decoder_model_info_present_flag = avm_rb_read_bit(rb);
if (seq_params->decoder_model_info_present_flag) {
seq_params->decoder_model_info.num_units_in_decoding_tick =
avm_rb_read_unsigned_literal(rb, 32);
seq_params->seq_max_decoder_model_present_flag = avm_rb_read_bit(rb);
if (seq_params->seq_max_decoder_model_present_flag) {
seq_params->seq_max_decoder_buffer_delay = avm_rb_read_uvlc(rb);
seq_params->seq_max_encoder_buffer_delay = avm_rb_read_uvlc(rb);
seq_params->seq_max_low_delay_mode_flag = avm_rb_read_bit(rb);
} else {
seq_params->seq_max_decoder_buffer_delay = 70000;
seq_params->seq_max_encoder_buffer_delay = 20000;
seq_params->seq_max_low_delay_mode_flag = 0;
}
} else {
seq_params->decoder_model_info.num_units_in_decoding_tick = 1;
seq_params->seq_max_decoder_buffer_delay = 70000;
seq_params->seq_max_encoder_buffer_delay = 20000;
seq_params->seq_max_low_delay_mode_flag = 0;
}
// Configurable profile does not define bitrate and buffer size constraints
if (seq_params->seq_profile_idc != CONFIGURABLE) {
int64_t seq_bitrate = av2_max_level_bitrate(
seq_params->seq_profile_idc, seq_params->seq_max_level_idx,
seq_params->seq_tier, seq_params->subsampling_x,
seq_params->subsampling_y, seq_params->monochrome);
if (seq_bitrate == 0)
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"AV2 does not support this combination of "
"profile, level, and tier.");
// Buffer size in bits/s is bitrate in bits/s * 1 s
int64_t buffer_size = seq_bitrate;
if (buffer_size == 0)
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"AV2 does not support this combination of "
"profile, level, and tier.");
}
}
// setup default embedded layer dependency
setup_default_embedded_layer_dependency_structure(seq_params);
// setup default temporal layer dependency
setup_default_temporal_layer_dependency_structure(seq_params);
// mlayer dependency description
seq_params->mlayer_dependency_present_flag = 0;
if (seq_params->max_mlayer_id > 0) {
seq_params->mlayer_dependency_present_flag = avm_rb_read_bit(rb);
if (seq_params->mlayer_dependency_present_flag) {
av2_read_mlayer_dependency_info(seq_params, rb);
}
}
// tlayer dependency description
seq_params->tlayer_dependency_present_flag = 0;
seq_params->multi_tlayer_dependency_map_present_flag = 0;
if (seq_params->max_tlayer_id > 0) {
seq_params->tlayer_dependency_present_flag = avm_rb_read_bit(rb);
if (seq_params->tlayer_dependency_present_flag) {
if (seq_params->max_mlayer_id > 0) {
seq_params->multi_tlayer_dependency_map_present_flag =
avm_rb_read_bit(rb);
}
av2_read_tlayer_dependency_info(seq_params, rb);
}
}
if (!av2_check_profile_interop_conformance(seq_params, &cm->error, 1)) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Unsupported Bitdepth, Chroma format or number of embedded layers");
}
av2_read_sequence_header(rb, seq_params);
seq_params->film_grain_params_present = avm_rb_read_bit(rb);
size_t bits_before_ext = rb->bit_offset - saved_bit_offset;
seq_params->seq_extension_present_flag = avm_rb_read_bit(rb);
if (seq_params->seq_extension_present_flag) {
// Extension data bits = total - bits_read_before_extension -1 (ext flag) -
// trailing bits
int extension_bits = read_obu_extension_bits(
rb->bit_buffer, rb->bit_buffer_end - rb->bit_buffer, bits_before_ext,
&cm->error);
if (extension_bits > 0) {
// skip over the extension bits
rb->bit_offset += extension_bits;
} else {
// No extension data is present
}
}
if (av2_check_trailing_bits(pbi, rb) != 0) {
// cm->error.error_code is already set.
return 0;
}
return ((rb->bit_offset - saved_bit_offset + 7) >> 3);
}
static uint32_t read_multi_frame_header_obu(AV2Decoder *pbi,
uint32_t *acc_mfh_id_bitmap,
struct avm_read_bit_buffer *rb) {
AV2_COMMON *const cm = &pbi->common;
const uint32_t saved_bit_offset = rb->bit_offset;
const uint32_t cur_mfh_id = av2_read_multi_frame_header(cm, rb);
assert(cur_mfh_id < MAX_MFH_NUM);
if (acc_mfh_id_bitmap) {
*acc_mfh_id_bitmap |= (1 << cur_mfh_id);
}
size_t bits_before_ext = rb->bit_offset - saved_bit_offset;
cm->mfh_params[cur_mfh_id].mfh_extension_present_flag = avm_rb_read_bit(rb);
if (cm->mfh_params[cur_mfh_id].mfh_extension_present_flag) {
// Extension data bits = total - bits_read_before_extension -1 (ext flag) -
// trailing bits
int extension_bits = read_obu_extension_bits(
rb->bit_buffer, rb->bit_buffer_end - rb->bit_buffer, bits_before_ext,
&cm->error);
if (extension_bits > 0) {
// skip over the extension bits
rb->bit_offset += extension_bits;
} else {
// No extension data present
}
}
if (av2_check_trailing_bits(pbi, rb) != 0) {
// cm->error.error_code is already set.
return 0;
}
return ((rb->bit_offset - saved_bit_offset + 7) >> 3);
}
static uint32_t read_tilegroup_obu(AV2Decoder *pbi,
struct avm_read_bit_buffer *rb,
const uint8_t *data, const uint8_t *data_end,
const uint8_t **p_data_end,
OBU_TYPE obu_type, int obu_xlayer_id,
int *is_first_tg, int *is_last_tg) {
AV2_COMMON *const cm = &pbi->common;
int start_tile, end_tile;
int32_t header_size, tg_payload_size;
assert(rb->bit_offset == 0);
assert(rb->bit_buffer == data);
*is_first_tg = 1; // it is updated by av2_read_tilegroup_header()
header_size = av2_read_tilegroup_header(pbi, rb, data, p_data_end,
is_first_tg, &start_tile, &end_tile,
obu_type, obu_xlayer_id);
bool skip_payload = false;
skip_payload |= (obu_type == OBU_LEADING_SEF);
skip_payload |= (obu_type == OBU_REGULAR_SEF);
skip_payload |= (obu_type == OBU_LEADING_TIP);
skip_payload |= (obu_type == OBU_REGULAR_TIP);
skip_payload |= cm->bru.frame_inactive_flag;
skip_payload |= cm->bridge_frame_info.is_bridge_frame;
if (skip_payload) {
*is_last_tg = 1;
tg_payload_size = 0;
if (av2_check_trailing_bits(pbi, rb) != 0) {
// cm->error.error_code is already set.
return 0;
}
header_size = (int32_t)avm_rb_bytes_read(rb);
} else {
if (av2_check_byte_alignment(cm, rb)) return 0;
data += header_size;
av2_decode_tg_tiles_and_wrapup(pbi, data, data_end, p_data_end, start_tile,
end_tile, *is_first_tg);
tg_payload_size = (uint32_t)(*p_data_end - data);
*is_last_tg = end_tile == cm->tiles.rows * cm->tiles.cols - 1;
}
return header_size + tg_payload_size;
}
// Returns the last nonzero byte index in 'data'. If there is no nonzero byte in
// 'data', returns -1.
static int get_last_nonzero_byte_index(const uint8_t *data, size_t sz) {
// Scan backward and return on the first nonzero byte.
int i = (int)sz - 1;
while (i >= 0 && data[i] == 0) {
--i;
}
return i;
}
// Allocates metadata that was read and adds it to the decoders metadata array.
static void alloc_read_metadata(AV2Decoder *const pbi,
OBU_METADATA_TYPE metadata_type,
const uint8_t *data, size_t sz,
avm_metadata_insert_flags_t insert_flag) {
AV2_COMMON *const cm = &pbi->common;
if (!pbi->metadata) {
pbi->metadata = avm_img_metadata_array_alloc(0);
if (!pbi->metadata) {
avm_internal_error(&cm->error, AVM_CODEC_MEM_ERROR,
"Failed to allocate metadata array");
}
}
avm_metadata_t *metadata =
avm_img_metadata_alloc(metadata_type, data, sz, insert_flag);
if (!metadata) {
avm_internal_error(&cm->error, AVM_CODEC_MEM_ERROR,
"Error allocating metadata");
}
avm_metadata_t **metadata_array =
(avm_metadata_t **)realloc(pbi->metadata->metadata_array,
(pbi->metadata->sz + 1) * sizeof(metadata));
if (!metadata_array) {
avm_img_metadata_free(metadata);
avm_internal_error(&cm->error, AVM_CODEC_MEM_ERROR,
"Error growing metadata array");
}
pbi->metadata->metadata_array = metadata_array;
pbi->metadata->metadata_array[pbi->metadata->sz] = metadata;
pbi->metadata->sz++;
}
// On failure, calls avm_internal_error() and does not return.
static void read_metadata_itut_t35(AV2Decoder *const pbi, const uint8_t *data,
size_t sz) {
AV2_COMMON *const cm = &pbi->common;
if (sz == 0) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"itu_t_t35_country_code is missing");
}
int country_code_size = 1;
if (*data == 0xFF) {
if (sz == 1) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"itu_t_t35_country_code_extension_byte is missing");
}
++country_code_size;
}
const int end_index = (int)sz;
if (end_index < country_code_size) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"No trailing bits found in ITU-T T.35 metadata OBU");
}
alloc_read_metadata(pbi, OBU_METADATA_TYPE_ITUT_T35, data, end_index,
AVM_MIF_ANY_FRAME);
}
// On failure, calls avm_internal_error() and does not return.
static void read_metadata_itut_t35_short(AV2Decoder *const pbi,
const uint8_t *data, size_t sz) {
AV2_COMMON *const cm = &pbi->common;
if (sz == 0) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"itu_t_t35_country_code is missing");
}
int country_code_size = 1;
if (*data == 0xFF) {
if (sz == 1) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"itu_t_t35_country_code_extension_byte is missing");
}
++country_code_size;
}
int end_index = get_last_nonzero_byte_index(data, sz);
if (end_index < country_code_size) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"No trailing bits found in ITU-T T.35 metadata OBU");
}
// itu_t_t35_payload_bytes is byte aligned. Section 6.7.2 of the spec says:
// itu_t_t35_payload_bytes shall be bytes containing data registered as
// specified in Recommendation ITU-T T.35.
// Therefore the first trailing byte should be 0x80.
if (data[end_index] != 0x80) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"The last nonzero byte of the ITU-T T.35 metadata OBU "
"is 0x%02x, should be 0x80.",
data[end_index]);
}
alloc_read_metadata(pbi, OBU_METADATA_TYPE_ITUT_T35, data, end_index,
AVM_MIF_ANY_FRAME);
}
// On success, returns the number of bytes read from 'data'. On failure, calls
// avm_internal_error() and does not return.
static size_t read_metadata_hdr_cll(AV2Decoder *const pbi, const uint8_t *data,
size_t sz) {
const size_t kHdrCllPayloadSize = 4;
AV2_COMMON *const cm = &pbi->common;
if (sz < kHdrCllPayloadSize) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"Incorrect HDR CLL metadata payload size");
}
alloc_read_metadata(pbi, OBU_METADATA_TYPE_HDR_CLL, data, kHdrCllPayloadSize,
AVM_MIF_ANY_FRAME);
return kHdrCllPayloadSize;
}
// On success, returns the number of bytes read from 'data'. On failure, calls
// avm_internal_error() and does not return.
static size_t read_metadata_hdr_mdcv(AV2Decoder *const pbi, const uint8_t *data,
size_t sz) {
const size_t kMdcvPayloadSize = 24;
AV2_COMMON *const cm = &pbi->common;
if (sz < kMdcvPayloadSize) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"Incorrect HDR MDCV metadata payload size");
}
alloc_read_metadata(pbi, OBU_METADATA_TYPE_HDR_MDCV, data, kMdcvPayloadSize,
AVM_MIF_ANY_FRAME);
return kMdcvPayloadSize;
}
// On success, returns the number of bytes read from 'data'. On failure, calls
// avm_internal_error() and does not return.
static size_t read_metadata_banding_hints(AV2Decoder *const pbi,
const uint8_t *data, size_t sz) {
AV2_COMMON *const cm = &pbi->common;
// Validate minimum payload size (at least 3 bits for basic flags)
if (sz == 0) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"Empty banding hints metadata payload");
}
// Decode the banding hints metadata payload
if (avm_decode_banding_hints_metadata(data, sz, &pbi->band_metadata) == 0) {
// Successfully decoded
pbi->band_metadata_present = 1;
} else {
pbi->band_metadata_present = 0;
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"Failed to decode banding hints metadata");
}
alloc_read_metadata(pbi, OBU_METADATA_TYPE_BANDING_HINTS, data, sz,
AVM_MIF_ANY_FRAME);
return sz;
}
// Helper function to read banding hints from a bit buffer (short metadata path)
static void read_metadata_banding_hints_from_rb(
AV2Decoder *const pbi, struct avm_read_bit_buffer *rb) {
avm_banding_hints_metadata_t *md = &pbi->band_metadata;
memset(md, 0, sizeof(*md));
md->coding_banding_present_flag = avm_rb_read_bit(rb);
md->source_banding_present_flag = avm_rb_read_bit(rb);
if (md->coding_banding_present_flag) {
md->banding_hints_flag = avm_rb_read_bit(rb);
if (md->banding_hints_flag) {
md->three_color_components = avm_rb_read_bit(rb);
const int num_components = md->three_color_components ? 3 : 1;
for (int plane = 0; plane < num_components; plane++) {
md->banding_in_component_present_flag[plane] = avm_rb_read_bit(rb);
if (md->banding_in_component_present_flag[plane]) {
md->max_band_width_minus4[plane] = avm_rb_read_literal(rb, 6);
md->max_band_step_minus1[plane] = avm_rb_read_literal(rb, 4);
}
}
md->band_units_information_present_flag = avm_rb_read_bit(rb);
if (md->band_units_information_present_flag) {
md->num_band_units_rows_minus_1 = avm_rb_read_literal(rb, 5);
md->num_band_units_cols_minus_1 = avm_rb_read_literal(rb, 5);
md->varying_size_band_units_flag = avm_rb_read_bit(rb);
if (md->varying_size_band_units_flag) {
md->band_block_in_luma_samples = avm_rb_read_literal(rb, 3);
for (int r = 0; r <= md->num_band_units_rows_minus_1; r++) {
md->vert_size_in_band_blocks_minus1[r] = avm_rb_read_literal(rb, 5);
}
for (int c = 0; c <= md->num_band_units_cols_minus_1; c++) {
md->horz_size_in_band_blocks_minus1[c] = avm_rb_read_literal(rb, 5);
}
}
for (int r = 0; r <= md->num_band_units_rows_minus_1; r++) {
for (int c = 0; c <= md->num_band_units_cols_minus_1; c++) {
md->banding_in_band_unit_present_flag[r][c] = avm_rb_read_bit(rb);
}
}
}
}
}
pbi->band_metadata_present = 1;
// Re-encode to raw payload and store in metadata array
uint8_t payload_buf[256];
size_t payload_size = sizeof(payload_buf);
if (avm_encode_banding_hints_metadata(md, payload_buf, &payload_size) == 0) {
alloc_read_metadata(pbi, OBU_METADATA_TYPE_BANDING_HINTS, payload_buf,
payload_size, AVM_MIF_ANY_FRAME);
}
}
// On success, returns the number of bytes read from 'data'. On failure, calls
// avm_internal_error() and does not return.
static size_t read_metadata_icc_profile(AV2Decoder *const pbi,
const uint8_t *data, size_t sz) {
const size_t kMinIccProfileHeaderSize = 128;
AV2_COMMON *const cm = &pbi->common;
if (sz < kMinIccProfileHeaderSize) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"Incorrect ICC profile metadata payload size");
}
alloc_read_metadata(pbi, OBU_METADATA_TYPE_ICC_PROFILE, data, sz,
AVM_MIF_ANY_FRAME);
return sz;
}
// On failure, calls avm_internal_error() and does not return.
static void read_metadata_user_data_unregistered(AV2Decoder *const pbi,
const uint8_t *data,
size_t sz) {
AV2_COMMON *const cm = &pbi->common;
// uuid_iso_iec_11578 is 128 bits (16 bytes)
const size_t uuid_size = 16;
if (sz < uuid_size) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"uuid_iso_iec_11578 is missing or incomplete");
}
const int end_index = (int)sz;
alloc_read_metadata(pbi, OBU_METADATA_TYPE_USER_DATA_UNREGISTERED, data,
end_index, AVM_MIF_ANY_FRAME);
}
// On success, returns the number of bytes read from 'data'. On failure, calls
// avm_internal_error() and does not return.
static void read_metadata_scan_type(AV2Decoder *const pbi,
struct avm_read_bit_buffer *rb) {
AV2_COMMON *const cm = &pbi->common;
cm->pic_struct_metadata_params.mps_pic_struct_type =
avm_rb_read_literal(rb, 5);
cm->pic_struct_metadata_params.mps_source_scan_type_idc =
avm_rb_read_literal(rb, 2);
cm->pic_struct_metadata_params.mps_duplicate_flag = avm_rb_read_bit(rb);
uint8_t payload[1];
payload[0] = (cm->pic_struct_metadata_params.mps_pic_struct_type << 3) |
(cm->pic_struct_metadata_params.mps_source_scan_type_idc << 1) |
cm->pic_struct_metadata_params.mps_duplicate_flag;
alloc_read_metadata(pbi, OBU_METADATA_TYPE_SCAN_TYPE, payload, 1,
AVM_MIF_ANY_FRAME);
}
// On success, returns the number of bytes read from 'data'. On failure, calls
// avm_internal_error() and does not return.
static void read_metadata_temporal_point_info(AV2Decoder *const pbi,
struct avm_read_bit_buffer *rb) {
AV2_COMMON *const cm = &pbi->common;
cm->temporal_point_info_metadata.mtpi_frame_presentation_time =
avm_rb_read_uleb(rb);
uint8_t payload[1];
payload[0] =
(cm->temporal_point_info_metadata.mtpi_frame_presentation_time & 0XFF);
alloc_read_metadata(pbi, OBU_METADATA_TYPE_TEMPORAL_POINT_INFO, payload, 1,
AVM_MIF_ANY_FRAME);
}
static int read_metadata_frame_hash(AV2Decoder *const pbi,
struct avm_read_bit_buffer *rb) {
AV2_COMMON *const cm = &pbi->common;
const unsigned hash_type = avm_rb_read_literal(rb, 4);
const unsigned per_plane = avm_rb_read_bit(rb);
const unsigned has_grain = avm_rb_read_bit(rb);
const unsigned is_monochrome = avm_rb_read_bit(rb);
avm_rb_read_literal(rb, 1); // reserved
// If hash_type is reserved for future use, ignore the entire OBU
if (hash_type) return -1;
FrameHash *const frame_hash = has_grain ? &cm->cur_frame->grain_frame_hash
: &cm->cur_frame->raw_frame_hash;
memset(frame_hash, 0, sizeof(*frame_hash));
frame_hash->hash_type = hash_type;
frame_hash->per_plane = per_plane;
frame_hash->has_grain = has_grain;
if (per_plane) {
const int num_planes = is_monochrome ? 1 : 3;
for (int i = 0; i < num_planes; ++i) {
PlaneHash *plane = &frame_hash->plane[i];
for (size_t j = 0; j < 16; ++j)
plane->md5[j] = avm_rb_read_literal(rb, 8);
}
} else {
PlaneHash *plane = &frame_hash->plane[0];
for (size_t i = 0; i < 16; ++i) plane->md5[i] = avm_rb_read_literal(rb, 8);
}
frame_hash->is_present = 1;
return 0;
}
static void read_metadata_timecode(struct avm_read_bit_buffer *rb) {
avm_rb_read_literal(rb, 5); // counting_type f(5)
const int full_timestamp_flag =
avm_rb_read_bit(rb); // full_timestamp_flag f(1)
avm_rb_read_bit(rb); // discontinuity_flag (f1)
avm_rb_read_bit(rb); // cnt_dropped_flag f(1)
avm_rb_read_literal(rb, 9); // n_frames f(9)
if (full_timestamp_flag) {
avm_rb_read_literal(rb, 6); // seconds_value f(6)
avm_rb_read_literal(rb, 6); // minutes_value f(6)
avm_rb_read_literal(rb, 5); // hours_value f(5)
} else {
const int seconds_flag = avm_rb_read_bit(rb); // seconds_flag f(1)
if (seconds_flag) {
avm_rb_read_literal(rb, 6); // seconds_value f(6)
const int minutes_flag = avm_rb_read_bit(rb); // minutes_flag f(1)
if (minutes_flag) {
avm_rb_read_literal(rb, 6); // minutes_value f(6)
const int hours_flag = avm_rb_read_bit(rb); // hours_flag f(1)
if (hours_flag) {
avm_rb_read_literal(rb, 5); // hours_value f(5)
}
}
}
}
// time_offset_length f(5)
const int time_offset_length = avm_rb_read_literal(rb, 5);
if (time_offset_length) {
// time_offset_value f(time_offset_length)
avm_rb_read_literal(rb, time_offset_length);
}
}
// Returns the last nonzero byte in 'data'. If there is no nonzero byte in
// 'data', returns 0.
//
// Call this function to check the following requirement in the spec:
// This implies that when any payload data is present for this OBU type, at
// least one byte of the payload data (including the trailing bit) shall not
// be equal to 0.
static uint8_t get_last_nonzero_byte(const uint8_t *data, size_t sz) {
// Scan backward and return on the first nonzero byte.
size_t i = sz;
while (i != 0) {
--i;
if (data[i] != 0) return data[i];
}
return 0;
}
// Skip metadata_unit_remaining_bits: decoders conforming to this version of
// the specification shall ignore metadata_unit_remaining_bits.
static void skip_remaining_mu_payload_bits(struct avm_read_bit_buffer *rb,
size_t parsed_payload_bits,
size_t total_payload_bits) {
if (parsed_payload_bits < total_payload_bits) {
size_t remaining_bits = total_payload_bits - parsed_payload_bits;
while (remaining_bits > 0) {
const int chunk = (remaining_bits > 31) ? 31 : (int)remaining_bits;
avm_rb_read_literal(rb, chunk);
remaining_bits -= chunk;
}
}
}
// Checks the metadata for correct syntax but ignores the parsed metadata.
//
// On success, returns the number of bytes read from 'data'. On failure, sets
// pbi->common.error.error_code and returns 0, or calls avm_internal_error()
// and does not return.
static size_t read_metadata_unit_payload(AV2Decoder *pbi, const uint8_t *data,
avm_metadata_t *metadata) {
AV2_COMMON *const cm = &pbi->common;
size_t type_length = 0;
const OBU_METADATA_TYPE metadata_type = metadata->type;
const size_t sz = metadata->sz;
int known_metadata_type = metadata_type >= OBU_METADATA_TYPE_HDR_CLL &&
metadata_type < NUM_OBU_METADATA_TYPES;
known_metadata_type |= metadata_type == OBU_METADATA_TYPE_ICC_PROFILE;
known_metadata_type |= metadata_type == OBU_METADATA_TYPE_SCAN_TYPE;
known_metadata_type |= metadata_type == OBU_METADATA_TYPE_TEMPORAL_POINT_INFO;
known_metadata_type |=
metadata_type == OBU_METADATA_TYPE_USER_DATA_UNREGISTERED;
if (!known_metadata_type) {
return sz;
}
// Temporal point info metadata is only valid in SHORT format, not GROUP.
if (metadata_type == OBU_METADATA_TYPE_TEMPORAL_POINT_INFO) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"Temporal point info metadata shall only appear in "
"OBU_METADATA_SHORT, not OBU_METADATA_GROUP");
}
// Track bits consumed by the type-specific metadata reader, matching the
// proposal's parsedPayloadBits = currentPosition - startPosition.
size_t parsed_payload_bits = 0;
struct avm_read_bit_buffer rb;
if (metadata_type == OBU_METADATA_TYPE_ITUT_T35) {
read_metadata_itut_t35(pbi, data + type_length, sz - type_length);
parsed_payload_bits = sz * 8;
} else if (metadata_type == OBU_METADATA_TYPE_HDR_CLL) {
read_metadata_hdr_cll(pbi, data + type_length, sz - type_length);
parsed_payload_bits = sz * 8;
} else if (metadata_type == OBU_METADATA_TYPE_HDR_MDCV) {
read_metadata_hdr_mdcv(pbi, data + type_length, sz - type_length);
parsed_payload_bits = sz * 8;
} else if (metadata_type == OBU_METADATA_TYPE_BANDING_HINTS) {
read_metadata_banding_hints(pbi, data + type_length, sz - type_length);
parsed_payload_bits = sz * 8;
} else if (metadata_type == OBU_METADATA_TYPE_SCAN_TYPE) {
av2_init_read_bit_buffer(pbi, &rb, data + type_length, data + sz);
read_metadata_scan_type(pbi, &rb);
parsed_payload_bits = rb.bit_offset;
} else if (metadata_type == OBU_METADATA_TYPE_TEMPORAL_POINT_INFO) {
av2_init_read_bit_buffer(pbi, &rb, data + type_length, data + sz);
read_metadata_temporal_point_info(pbi, &rb);
parsed_payload_bits = rb.bit_offset;
} else if (metadata_type == OBU_METADATA_TYPE_ICC_PROFILE) {
read_metadata_icc_profile(pbi, data + type_length, sz - type_length);
parsed_payload_bits = sz * 8;
} else if (metadata_type == OBU_METADATA_TYPE_USER_DATA_UNREGISTERED) {
read_metadata_user_data_unregistered(pbi, data + type_length,
sz - type_length);
parsed_payload_bits = sz * 8;
} else {
// Remaining types: TIMECODE, DECODED_FRAME_HASH, SCALABILITY.
av2_init_read_bit_buffer(pbi, &rb, data + type_length, data + sz);
if (metadata_type == OBU_METADATA_TYPE_DECODED_FRAME_HASH) {
if (read_metadata_frame_hash(pbi, &rb)) {
return sz;
}
} else {
assert(metadata_type == OBU_METADATA_TYPE_TIMECODE);
read_metadata_timecode(&rb);
}
parsed_payload_bits = rb.bit_offset;
}
// Compute remaining payload bits and skip them.
const size_t total_payload_bits = (sz - type_length) * 8;
av2_init_read_bit_buffer(pbi, &rb, data + type_length, data + sz);
rb.bit_offset = (uint32_t)parsed_payload_bits;
skip_remaining_mu_payload_bits(&rb, parsed_payload_bits, total_payload_bits);
return sz;
}
static size_t read_metadata_obsp(AV2Decoder *pbi, const uint8_t *data,
size_t sz,
avm_metadata_array_t *metadata_array,
avm_metadata_t *metadata_base,
int expected_suffix) {
AV2_COMMON *const cm = &pbi->common;
struct avm_read_bit_buffer rb;
av2_init_read_bit_buffer(pbi, &rb, data, data + sz);
metadata_base->is_suffix = avm_rb_read_literal(&rb, 1);
// Validate suffix bit if requested
if (expected_suffix >= 0 && metadata_base->is_suffix != expected_suffix) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
metadata_base->necessity_idc =
(avm_metadata_necessity_t)avm_rb_read_literal(&rb, 2);
metadata_base->application_id =
(avm_metadata_application_id_t)avm_rb_read_literal(&rb, 5);
const size_t bytes_read = avm_rb_bytes_read(&rb);
assert(bytes_read == 1);
size_t count_length;
uint64_t count_minus_1;
if (avm_uleb_decode(data + bytes_read, sz - bytes_read, &count_minus_1,
&count_length) < 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
metadata_array->sz = count_minus_1 + 1;
// Ensure metadata_unit_cnt doesn't exceed 2^14 (uleb128 <= 2 bytes)
if (metadata_array->sz > 16384) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
return bytes_read + count_length;
}
static size_t read_metadata_unit_header(AV2Decoder *pbi, const uint8_t *data,
size_t sz, avm_metadata_t *metadata,
const ObuHeader *obu_header) {
AV2_COMMON *const cm = &pbi->common;
size_t type_length;
uint64_t type_value;
if (avm_uleb_decode(data, sz, &type_value, &type_length) < 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
metadata->type = (uint32_t)type_value;
size_t bytes_read = type_length;
struct avm_read_bit_buffer rb;
av2_init_read_bit_buffer(pbi, &rb, data + bytes_read, data + sz);
const size_t muh_header_size = avm_rb_read_literal(&rb, 7);
metadata->cancel_flag = avm_rb_read_literal(&rb, 1);
assert(avm_rb_bytes_read(&rb) == 1);
bytes_read += avm_rb_bytes_read(&rb);
const size_t total_size = bytes_read + muh_header_size;
assert(total_size <= sz);
if (!metadata->cancel_flag) {
size_t size_length;
uint64_t size_value = 0;
if (avm_uleb_decode(data + bytes_read, sz - bytes_read, &size_value,
&size_length) < 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
metadata->sz = size_value;
bytes_read += size_length;
av2_init_read_bit_buffer(pbi, &rb, data + bytes_read, data + sz);
metadata->layer_idc = (avm_metadata_layer_t)avm_rb_read_literal(&rb, 3);
metadata->persistence_idc =
(avm_metadata_persistence_t)avm_rb_read_literal(&rb, 3);
metadata->priority = avm_rb_read_literal(&rb, 8);
avm_rb_read_literal(&rb, 2); // reserved bits
assert(avm_rb_bytes_read(&rb) == 2);
if (metadata->layer_idc == AVM_LAYER_VALUES) {
if (obu_header->obu_xlayer_id == 31) {
metadata->xlayer_map = avm_rb_read_unsigned_literal(&rb, 32);
assert((metadata->xlayer_map & (1u << 31)) == 0);
for (int n = 0; n < 31; n++) {
if (metadata->xlayer_map & (1u << n)) {
metadata->mlayer_map[n] = avm_rb_read_unsigned_literal(&rb, 8);
}
}
} else {
metadata->mlayer_map[obu_header->obu_xlayer_id] =
avm_rb_read_unsigned_literal(&rb, 8);
}
}
bytes_read += avm_rb_bytes_read(&rb);
}
assert(bytes_read <= total_size);
return total_size;
}
static size_t read_metadata_obu(AV2Decoder *pbi, const uint8_t *data, size_t sz,
ObuHeader *obu_header, int expected_suffix) {
AV2_COMMON *const cm = &pbi->common;
avm_metadata_array_t metadata_array = { 0 };
avm_metadata_t metadata_base;
memset(&metadata_base, 0, sizeof(metadata_base));
size_t bytes_read = read_metadata_obsp(pbi, data, sz, &metadata_array,
&metadata_base, expected_suffix);
for (uint32_t i = 0; i < metadata_array.sz; i++) {
avm_metadata_t metadata = { 0 };
// copy shared fields read in `read_metadata_obsp`
memcpy(&metadata, &metadata_base, sizeof(metadata));
const size_t muh_size = read_metadata_unit_header(
pbi, data + bytes_read, sz - bytes_read, &metadata, obu_header);
if (muh_size == 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
bytes_read += muh_size;
if (!metadata.cancel_flag) {
if (sz - bytes_read < metadata.sz) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
const size_t mup_size =
read_metadata_unit_payload(pbi, data + bytes_read, &metadata);
bytes_read += mup_size;
}
}
if (bytes_read >= sz || data[bytes_read] != 0x80) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
return bytes_read + 1;
}
// Checks the metadata for correct syntax but ignores the parsed metadata.
//
// On success, returns the number of bytes read from 'data'. On failure, sets
// pbi->common.error.error_code and returns 0, or calls avm_internal_error()
// and does not return.
// expected_suffix: 0 for prefix metadata, 1 for suffix metadata, -1 for no
// validation
static size_t read_metadata_short(AV2Decoder *pbi, const uint8_t *data,
size_t sz, int expected_suffix) {
AV2_COMMON *const cm = &pbi->common;
size_t type_length;
uint64_t type_value;
struct avm_read_bit_buffer rb;
av2_init_read_bit_buffer(pbi, &rb, data, data + sz);
uint8_t metadata_is_suffix = avm_rb_read_bit(&rb);
// Validate suffix bit if requested
if (expected_suffix >= 0 && metadata_is_suffix != expected_suffix) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
uint8_t muh_layer_idc = avm_rb_read_literal(&rb, 3);
uint8_t muh_cancel_flag = avm_rb_read_bit(&rb);
uint8_t muh_persistence_idc = avm_rb_read_literal(&rb, 3);
if (avm_uleb_decode(
data + 1, // read type from the position data + 1
sz - 1, // one less bytes available due to extra parameters
&type_value, &type_length) < 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
const OBU_METADATA_TYPE metadata_type = (OBU_METADATA_TYPE)type_value;
// Increase the type_length by 1 byte since there is one prefix byte added
// before the type
++type_length;
if (muh_cancel_flag) return sz;
// Update the metadata with the header fields we read
if (pbi->metadata && pbi->metadata->sz > 0) {
avm_metadata_t *last_metadata =
pbi->metadata->metadata_array[pbi->metadata->sz - 1];
if (last_metadata && last_metadata->type == OBU_METADATA_TYPE_ITUT_T35) {
last_metadata->is_suffix = metadata_is_suffix;
last_metadata->layer_idc = muh_layer_idc;
last_metadata->cancel_flag = muh_cancel_flag;
last_metadata->persistence_idc = muh_persistence_idc;
}
}
const bool known_metadata_type =
(metadata_type > OBU_METADATA_TYPE_AVM_RESERVED_0) &&
(metadata_type < NUM_OBU_METADATA_TYPES);
if (!known_metadata_type) {
// If metadata_type is reserved for future use or a user private value,
// ignore the entire OBU and just check trailing bits.
if (get_last_nonzero_byte(data + type_length, sz - type_length) == 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
return sz;
}
if (metadata_type == OBU_METADATA_TYPE_ITUT_T35) {
// read_metadata_itut_t35() checks trailing bits.
read_metadata_itut_t35_short(pbi, data + type_length, sz - type_length);
// Update the metadata with the header fields we read
if (pbi->metadata && pbi->metadata->sz > 0) {
avm_metadata_t *last_metadata =
pbi->metadata->metadata_array[pbi->metadata->sz - 1];
if (last_metadata && last_metadata->type == OBU_METADATA_TYPE_ITUT_T35) {
last_metadata->is_suffix = metadata_is_suffix;
last_metadata->layer_idc = muh_layer_idc;
last_metadata->cancel_flag = muh_cancel_flag;
last_metadata->persistence_idc = muh_persistence_idc;
}
}
return sz;
} else if (metadata_type == OBU_METADATA_TYPE_HDR_CLL) {
size_t bytes_read =
type_length +
read_metadata_hdr_cll(pbi, data + type_length, sz - type_length);
if (get_last_nonzero_byte(data + bytes_read, sz - bytes_read) != 0x80) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
// Update the metadata with the header fields we read
if (pbi->metadata && pbi->metadata->sz > 0) {
avm_metadata_t *last_metadata =
pbi->metadata->metadata_array[pbi->metadata->sz - 1];
if (last_metadata && last_metadata->type == OBU_METADATA_TYPE_HDR_CLL) {
last_metadata->is_suffix = metadata_is_suffix;
last_metadata->layer_idc = muh_layer_idc;
last_metadata->cancel_flag = muh_cancel_flag;
last_metadata->persistence_idc = muh_persistence_idc;
}
}
return sz;
} else if (metadata_type == OBU_METADATA_TYPE_HDR_MDCV) {
size_t bytes_read =
type_length +
read_metadata_hdr_mdcv(pbi, data + type_length, sz - type_length);
if (get_last_nonzero_byte(data + bytes_read, sz - bytes_read) != 0x80) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
// Update the metadata with the header fields we read
if (pbi->metadata && pbi->metadata->sz > 0) {
avm_metadata_t *last_metadata =
pbi->metadata->metadata_array[pbi->metadata->sz - 1];
if (last_metadata && last_metadata->type == OBU_METADATA_TYPE_HDR_MDCV) {
last_metadata->is_suffix = metadata_is_suffix;
last_metadata->layer_idc = muh_layer_idc;
last_metadata->cancel_flag = muh_cancel_flag;
last_metadata->persistence_idc = muh_persistence_idc;
}
}
return sz;
} else if (metadata_type == OBU_METADATA_TYPE_SCAN_TYPE) {
const size_t kMinScanTypeHeaderSize = 1;
if (sz < kMinScanTypeHeaderSize) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"Incorrect scan type metadata payload size");
}
av2_init_read_bit_buffer(pbi, &rb, data + type_length, data + sz);
read_metadata_scan_type(pbi, &rb);
return sz;
} else if (metadata_type == OBU_METADATA_TYPE_TEMPORAL_POINT_INFO) {
const size_t kMinTemporalPointInfoHeaderSize = 1;
if (sz < kMinTemporalPointInfoHeaderSize) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"Incorrect temporal point info metadata payload size");
}
av2_init_read_bit_buffer(pbi, &rb, data + type_length, data + sz);
read_metadata_temporal_point_info(pbi, &rb);
return sz;
}
av2_init_read_bit_buffer(pbi, &rb, data + type_length, data + sz);
if (metadata_type == OBU_METADATA_TYPE_DECODED_FRAME_HASH) {
if (read_metadata_frame_hash(pbi, &rb)) {
// Unsupported Decoded Frame Hash metadata. Ignoring the entire OBU and
// just checking trailing bits
if (get_last_nonzero_byte(data + type_length, sz - type_length) == 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
return sz;
}
} else if (metadata_type == OBU_METADATA_TYPE_BANDING_HINTS) {
// Banding hints metadata is variable bits, not byte-aligned
read_metadata_banding_hints_from_rb(pbi, &rb);
// Update the metadata with the header fields we read
if (pbi->metadata && pbi->metadata->sz > 0) {
avm_metadata_t *last_metadata =
pbi->metadata->metadata_array[pbi->metadata->sz - 1];
if (last_metadata &&
last_metadata->type == OBU_METADATA_TYPE_BANDING_HINTS) {
last_metadata->is_suffix = metadata_is_suffix;
last_metadata->layer_idc = muh_layer_idc;
last_metadata->cancel_flag = muh_cancel_flag;
last_metadata->persistence_idc = muh_persistence_idc;
}
}
} else if (metadata_type == OBU_METADATA_TYPE_ICC_PROFILE) {
// ICC profile is byte-aligned binary data
// Find the last nonzero byte (should be 0x80 trailing byte)
const int last_nonzero_idx =
get_last_nonzero_byte_index(data + type_length, sz - type_length);
if (last_nonzero_idx < 0 || data[type_length + last_nonzero_idx] != 0x80) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
// ICC payload size excludes the trailing 0x80 byte
const size_t icc_payload_size = last_nonzero_idx;
read_metadata_icc_profile(pbi, data + type_length, icc_payload_size);
return sz;
} else if (metadata_type == OBU_METADATA_TYPE_USER_DATA_UNREGISTERED) {
// User data unregistered is byte-aligned binary data
// Find the last nonzero byte (should be 0x80 trailing byte)
const int last_nonzero_idx =
get_last_nonzero_byte_index(data + type_length, sz - type_length);
if (last_nonzero_idx < 0 || data[type_length + last_nonzero_idx] != 0x80) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
// User data payload size excludes the trailing 0x80 byte
const size_t user_data_payload_size = last_nonzero_idx;
read_metadata_user_data_unregistered(pbi, data + type_length,
user_data_payload_size);
return sz;
} else {
assert(metadata_type == OBU_METADATA_TYPE_TIMECODE);
read_metadata_timecode(&rb);
}
{
// Compute remaining payload bits and skip them.
// Subtract 1 from sz to exclude the trailing 0x80 byte, which is part of
// metadata_short_obu() trailing_bits() syntax, not metadata_unit() payload.
const size_t parsed_payload_bits = rb.bit_offset;
const size_t total_payload_bits = (sz - type_length - 1) * 8;
skip_remaining_mu_payload_bits(&rb, parsed_payload_bits,
total_payload_bits);
}
if (av2_check_trailing_bits(pbi, &rb) != 0) {
// cm->error.error_code is already set.
return 0;
}
assert((rb.bit_offset & 7) == 0);
return type_length + (rb.bit_offset >> 3);
}
// On success, returns 'sz'. On failure, sets pbi->common.error.error_code and
// returns 0.
static size_t read_padding(AV2_COMMON *const cm, const uint8_t *data,
size_t sz) {
// The spec allows a padding OBU to be header-only (i.e., obu_size = 0). So
// check trailing bits only if sz > 0.
if (sz > 0) {
// The payload of a padding OBU is byte aligned. Therefore the first
// trailing byte should be 0x80. See https://crbug.com/aomedia/2393.
const uint8_t last_nonzero_byte = get_last_nonzero_byte(data, sz);
if (last_nonzero_byte != 0x80) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return 0;
}
}
return sz;
}
int av2_is_leading_vcl_obu(OBU_TYPE obu_type) {
return (obu_type == OBU_LEADING_TILE_GROUP || obu_type == OBU_LEADING_SEF ||
obu_type == OBU_LEADING_TIP);
}
// Check if any obu is present between two tile groups of one frame unit.
static void check_tilegroup_obus_in_a_frame_unit(AV2_COMMON *const cm,
obu_info *current_obu,
obu_info *prev_obu) {
if (current_obu->obu_type != prev_obu->obu_type ||
current_obu->immediate_output_picture !=
prev_obu->immediate_output_picture ||
current_obu->showable_frame != prev_obu->showable_frame ||
current_obu->display_order_hint != prev_obu->display_order_hint ||
current_obu->mlayer_id != prev_obu->mlayer_id) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"%s : no non-padding obu is allowed between tilegroup obus in a frame "
"unit (current obu %s, current oh %d previous obu %s previous oh %d)",
__func__, avm_obu_type_to_string(current_obu->obu_type),
current_obu->display_order_hint,
avm_obu_type_to_string(prev_obu->obu_type),
prev_obu->display_order_hint);
}
}
// TU validation: Check if an OBU type is metadata obu
static int is_metadata_obu(OBU_TYPE obu_type) {
return (obu_type == OBU_METADATA_SHORT || obu_type == OBU_METADATA_GROUP);
}
// TU validation: Check if an OBU type is global configuration information
static int is_global_config_obu(OBU_TYPE obu_type, int xlayer_id) {
return obu_type == OBU_MULTI_STREAM_DECODER_OPERATION ||
(obu_type == OBU_LAYER_CONFIGURATION_RECORD &&
xlayer_id == GLOBAL_XLAYER_ID) ||
(obu_type == OBU_OPERATING_POINT_SET &&
xlayer_id == GLOBAL_XLAYER_ID) ||
(obu_type == OBU_ATLAS_SEGMENT && xlayer_id == GLOBAL_XLAYER_ID) ||
(obu_type == OBU_METADATA_SHORT && xlayer_id == GLOBAL_XLAYER_ID) ||
(obu_type == OBU_METADATA_GROUP && xlayer_id == GLOBAL_XLAYER_ID);
}
// TU validation: Check if an OBU type is local configuration information
static int is_local_config_obu(OBU_TYPE obu_type, int xlayer_id) {
return (obu_type == OBU_LAYER_CONFIGURATION_RECORD &&
xlayer_id != GLOBAL_XLAYER_ID) ||
(obu_type == OBU_OPERATING_POINT_SET &&
xlayer_id != GLOBAL_XLAYER_ID) ||
(obu_type == OBU_ATLAS_SEGMENT && xlayer_id != GLOBAL_XLAYER_ID);
}
// TU validation: Check if an OBU type is not global or local configuration
// information, not sequence header or not padding
static int is_frame_unit(OBU_TYPE obu_type, int xlayer_id) {
// OBU_MULTI_FRAME_HEADER,
// OBU_BUFFER_REMOVAL_TIMING,
// OBU_QUANTIZATION_MATRIX,
// OBU_FILM_GRAIN_MODEL,
// OBU_CONTENT_INTERPRETATION,
// OBU_METADATA_SHORT,
// OBU_METADATA_GROUP,
// OBU_CLOSED_LOOP_KEY,
// OBU_OPEN_LOOP_KEY,
// OBU_LEADING_TILE_GROUP,
// OBU_REGULAR_TILE_GROUP,
// OBU_SWITCH,
// OBU_LEADING_SEF,
// OBU_REGULAR_SEF,
// OBU_LEADING_TIP,
// OBU_REGULAR_TIP,
// OBU_BRIDGE_FRAME,
// OBU_RAS_FRAME
int non_frame_unit_obu = is_global_config_obu(obu_type, xlayer_id) ||
is_local_config_obu(obu_type, xlayer_id) ||
obu_type == OBU_SEQUENCE_HEADER ||
obu_type == OBU_PADDING;
return !non_frame_unit_obu;
}
static int is_coded_frame(OBU_TYPE obu_type) {
return is_multi_tile_vcl_obu(obu_type) || is_single_tile_vcl_obu(obu_type);
}
// Validates OBU order within a Temporal Unit with state machine.
// Returns 1 if OBU is valid for the current state, 0 if it violates rules.
// Note: The caller is responsible for filtering out padding OBUs and reserved
// OBUs before calling this function.
//
// After the global information phase, all remaining OBUs are grouped by
// xlayer_id in strictly increasing order. Within each xlayer group the full
// phase progression is enforced:
// local config (LCR -> OPS -> Atlas) -> SH -> frame unit data
// When the xlayer_id increases, the state resets to TU_STATE_LOCAL_INFO so
// the new xlayer group may begin with local config, SH, or frame unit data.
int check_temporal_unit_structure(temporal_unit_state_t *state, int obu_type,
int xlayer_id, int metadata_is_suffix,
int prev_obu_type, int prev_xlayer_id) {
// Validate input parameters
if (!state) return 0;
// After the global phase, detect an xlayer_id increase and reset the
// per-xlayer state machine so the new group starts fresh.
if (*state == TU_STATE_LOCAL_INFO || *state == TU_STATE_SEQUENCE_HEADER ||
*state == TU_STATE_FRAME_UINT_DATA) {
if (xlayer_id != GLOBAL_XLAYER_ID && prev_xlayer_id != GLOBAL_XLAYER_ID &&
xlayer_id != prev_xlayer_id) {
if (xlayer_id < prev_xlayer_id) return 0; // Must be increasing
// New xlayer group: reset to local info phase.
*state = TU_STATE_LOCAL_INFO;
}
}
switch (*state) {
case TU_STATE_START:
case TU_STATE_TEMPORAL_DELIMITER:
if (obu_type == OBU_TEMPORAL_DELIMITER) {
if (*state == TU_STATE_TEMPORAL_DELIMITER)
return 0; // Only one allowed
*state = TU_STATE_TEMPORAL_DELIMITER;
return 1;
} else if (is_global_config_obu(obu_type,
xlayer_id)) { // First OBU: global config
*state = TU_STATE_GLOBAL_INFO;
return 1;
} else if (is_local_config_obu(obu_type,
xlayer_id)) { // First OBU: local config
*state = TU_STATE_LOCAL_INFO;
return 1;
} else if (obu_type == OBU_SEQUENCE_HEADER) {
*state = TU_STATE_SEQUENCE_HEADER;
return 1;
} else if (is_frame_unit(obu_type, xlayer_id)) {
*state = TU_STATE_FRAME_UINT_DATA;
return 1;
} else { // Invalid OBU type for start of temporal unit
return 0;
}
case TU_STATE_GLOBAL_INFO:
if (is_global_config_obu(obu_type, xlayer_id)) {
// 0 or 1 OBU_MULTI_STREAM_DECODER_OPERATION,
// 0 or more: OBU_LAYER_CONFIGURATION_RECORD
// 0 or more: OBU_OPERATING_POINT_SET
// 0 or more: OBU_ATLAS_SEGMENT
// 0 or more: OBU_METADATA(obu_xlayer_id = 0x1F)
// MSDO -> LCR -> LCR -> OPS -> OPS -> ATS -> ATS -> METADATA ->
// METADATA
if (obu_type == OBU_MULTI_STREAM_DECODER_OPERATION)
return 0; // Only one allowed
else if (obu_type == OBU_LAYER_CONFIGURATION_RECORD &&
(prev_obu_type == OBU_OPERATING_POINT_SET ||
prev_obu_type == OBU_ATLAS_SEGMENT ||
is_metadata_obu(prev_obu_type))) {
return 0;
} else if (obu_type == OBU_OPERATING_POINT_SET &&
(prev_obu_type == OBU_ATLAS_SEGMENT ||
is_metadata_obu(prev_obu_type))) {
return 0;
} else if (obu_type == OBU_ATLAS_SEGMENT &&
is_metadata_obu(prev_obu_type)) {
return 0;
} else
return 1;
} else if (is_local_config_obu(obu_type, xlayer_id)) {
*state = TU_STATE_LOCAL_INFO; // Transition from global to local
return 1;
} else if (obu_type == OBU_SEQUENCE_HEADER) {
*state = TU_STATE_SEQUENCE_HEADER;
return 1;
} else if (is_frame_unit(obu_type, xlayer_id)) {
*state = TU_STATE_FRAME_UINT_DATA;
return 1;
} else {
// Invalid OBU type during global info phase or wrong xlayer_id
return 0;
}
case TU_STATE_LOCAL_INFO:
if (is_local_config_obu(obu_type, xlayer_id)) {
// Within the same xlayer group: LCR -> OPS -> Atlas (no going back).
if (xlayer_id == prev_xlayer_id) {
if (obu_type == OBU_LAYER_CONFIGURATION_RECORD &&
prev_obu_type != OBU_LAYER_CONFIGURATION_RECORD) {
return 0;
} else if (obu_type == OBU_OPERATING_POINT_SET &&
prev_obu_type == OBU_ATLAS_SEGMENT) {
return 0;
}
}
// First OBU in a new xlayer group: any local config type is valid.
return 1;
} else if (obu_type == OBU_SEQUENCE_HEADER) {
*state = TU_STATE_SEQUENCE_HEADER;
return 1;
} else if (is_frame_unit(obu_type, xlayer_id)) {
*state = TU_STATE_FRAME_UINT_DATA;
return 1;
} else {
return 0; // Invalid OBU type(such as global obus) during local info
// phase
}
case TU_STATE_SEQUENCE_HEADER:
if (obu_type == OBU_SEQUENCE_HEADER) {
return 1;
} else if (is_frame_unit(obu_type, xlayer_id)) {
*state = TU_STATE_FRAME_UINT_DATA;
return 1;
} else {
return 0; // Invalid OBU type(such as global obus) during sequence
// header phase
}
case TU_STATE_FRAME_UINT_DATA:
if (is_frame_unit(obu_type, xlayer_id)) {
// Within the same xlayer group: enforce existing type ordering.
// CI -> CI -> MFH -> MFH -> (BRT, QM, FGM, METADATA_prefix,
// METADATA_SHORT_prefix) -> coded_frame -> METADATA_suffix,
// METADATA_SHORT_suffix)
if (obu_type == OBU_CONTENT_INTERPRETATION &&
prev_obu_type != OBU_CONTENT_INTERPRETATION)
return 0;
else if (obu_type == OBU_MULTI_FRAME_HEADER &&
(prev_obu_type != OBU_CONTENT_INTERPRETATION &&
prev_obu_type != OBU_MULTI_FRAME_HEADER))
return 0;
else if (((is_metadata_obu(obu_type) &&
metadata_is_suffix == 0) || // prefix
obu_type == OBU_BUFFER_REMOVAL_TIMING ||
obu_type == OBU_QUANTIZATION_MATRIX ||
obu_type == OBU_FILM_GRAIN_MODEL) &&
(is_coded_frame(prev_obu_type) ||
(is_metadata_obu(prev_obu_type) &&
metadata_is_suffix == 1))) // suffix
return 0;
else // other cases may be evaluated later
return 1;
} else {
return 0;
}
default:
// Invalid state
return 0;
}
}
// Check if the CLK is the first frame of a mlayer.
static void check_clk_in_a_layer(AV2_COMMON *const cm,
obu_info *current_frame_unit,
obu_info *last_frame_unit) {
if (current_frame_unit->obu_type == OBU_CLOSED_LOOP_KEY &&
last_frame_unit->obu_type != OBU_CLOSED_LOOP_KEY &&
current_frame_unit->mlayer_id == last_frame_unit->mlayer_id &&
current_frame_unit->display_order_hint ==
last_frame_unit->display_order_hint) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"%s : a CLK should be the first frame of a mlayer. "
"current obu %s, current oh "
"%d, current mlayer_id %d, "
"previous obu %s previous oh %d previous mlayer_id %d ",
__func__, avm_obu_type_to_string(current_frame_unit->obu_type),
current_frame_unit->display_order_hint, current_frame_unit->mlayer_id,
avm_obu_type_to_string(last_frame_unit->obu_type),
last_frame_unit->display_order_hint, last_frame_unit->mlayer_id);
}
}
// Check the mlayer ids of frame units before the current hidden frame.
static void check_layerid_hidden_frame_units(AV2_COMMON *const cm,
obu_info *current_frame_unit,
obu_info *last_frame_unit) {
//[H:layer0][H:layer1] not allowed
//[H:layer1][H:layer1] checked later : [H:layer1][H:layer1][S:layer1] ok,
//[H:layer1][H:layer1][S:layer0] not allowed [H:layer2][H:layer1] not allowed
//[S:layer0][H:layer1] checked later:
// 1) [S:layer0][H:layer1][S:layer1] maybe ok,
// 2) [S:layer0][H:layer1][S:layer0] not allowed
//[S:layer1][H:layer1] checked later :
// 1) [S:layer1][H:layer1][S:layer1] maybe ok (e.g. CLK[0], Bridge[0], TG[1])
// 2) [S:layer1][H:layer1][S:layer0] not allowed
// 3) [S:layer1][H:layer1][S:layer2] not allowed
//[S:layer2][H:layer1] allowed
if ((last_frame_unit->showable_frame == 0 &&
current_frame_unit->mlayer_id != last_frame_unit->mlayer_id)) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"%s : hidden frames should proceed displayable frames in a "
"layer:\n\tcurrent : "
"(%s, OH%d, L%d, S%d)\n\t"
"previous : (%s, OH%d, L%d, S%d)",
__func__, avm_obu_type_to_string(current_frame_unit->obu_type),
current_frame_unit->display_order_hint, current_frame_unit->mlayer_id,
current_frame_unit->showable_frame,
avm_obu_type_to_string(last_frame_unit->obu_type),
last_frame_unit->display_order_hint, last_frame_unit->mlayer_id,
last_frame_unit->showable_frame);
}
}
// Check the mlayer ids of frame units before the current showable frame.
static void check_layerid_showable_frame_units(
AV2_COMMON *const cm, obu_info *current_frame_unit,
obu_info *last_frame_unit, obu_info *last_displayable_frame_unit) {
//[H:layer0][*S:layer1] not allowed
// 3) [S:layer1][H:layer1][*S:layer2] not allowed
//[H:layer1][*S:layer1] check last displayable frame unit
// 1) [S:layer0][H:layer1][*S:layer1] is allowed
// 1) [S:layer1][H:layer1][*S:layer1] maybe okay - doh comparison required
// betwee S and S
//[H:layer2][*S:layer1] check last displayable frame unit
// 2) [S:layer0][H:layer1][*S:layer0] not allowed
// 2) [S:layer1][H:layer1][*S:layer0] not allowed
//[S:layer0][*S:layer1] allowed
//[S:layer1][*S:layer1] check orderhint of [S:layer1] and [S:layer1]
//[S:layer2][*S:layer1] check orderhint of [S:layer2] and [S:layer1]
if (last_frame_unit->showable_frame == 0 &&
current_frame_unit->mlayer_id != last_frame_unit->mlayer_id) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"%s : hidden frames should proceed displayable frames in a "
"layer:\n\tcurrent : "
"(%s, OH%d, L%d, S%d)\n\t"
"previous : (%s, OH%d, L%d, S%d)",
__func__, avm_obu_type_to_string(current_frame_unit->obu_type),
current_frame_unit->display_order_hint, current_frame_unit->mlayer_id,
current_frame_unit->showable_frame,
avm_obu_type_to_string(last_frame_unit->obu_type),
last_frame_unit->display_order_hint, last_frame_unit->mlayer_id,
last_frame_unit->showable_frame);
} else if (last_frame_unit->showable_frame == 0 &&
current_frame_unit->mlayer_id == last_frame_unit->mlayer_id &&
current_frame_unit->mlayer_id ==
last_displayable_frame_unit->mlayer_id) {
if (current_frame_unit->display_order_hint ==
last_displayable_frame_unit->display_order_hint) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"%s: mlayer_id should be in ascending order or order_hint should be "
"different:\n"
"\tcurrent : (%s, OH%d, L%d, S%d)\n"
"\tprevious : (%s, S%d)\n"
"\tlast_displayable : (%s, OH%d, L%d)",
__func__, avm_obu_type_to_string(current_frame_unit->obu_type),
current_frame_unit->display_order_hint, current_frame_unit->mlayer_id,
current_frame_unit->showable_frame,
avm_obu_type_to_string(last_frame_unit->obu_type),
last_frame_unit->showable_frame,
avm_obu_type_to_string(last_displayable_frame_unit->obu_type),
last_displayable_frame_unit->display_order_hint,
last_displayable_frame_unit->mlayer_id);
}
} else if (last_frame_unit->showable_frame == 1 &&
current_frame_unit->mlayer_id <= last_frame_unit->mlayer_id) {
if (current_frame_unit->display_order_hint ==
last_frame_unit->display_order_hint) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"%s: mlayer_id should be in ascending order or order_hint should be "
"different:\n\tcurrent obu %s, current oh "
"%d, current mlayer_id %d\n\t"
"previous obu %s previous oh %d previous mlayer_id %d",
__func__, avm_obu_type_to_string(current_frame_unit->obu_type),
current_frame_unit->display_order_hint, current_frame_unit->mlayer_id,
avm_obu_type_to_string(last_frame_unit->obu_type),
last_frame_unit->display_order_hint, last_frame_unit->mlayer_id);
}
}
}
// Check xlayer_id, mlayer_id, and tlayer_id of the obu is valid for the
// obu_type. Reports with avm_internal_error.
static void check_valid_layer_id(ObuHeader obu_header, AV2_COMMON *const cm) {
// Ignore reserved OBUs.
if (!avm_obu_type_is_valid(obu_header.type)) return;
if (obu_header.obu_xlayer_id == GLOBAL_XLAYER_ID &&
(obu_header.obu_tlayer_id != 0 || obu_header.obu_mlayer_id != 0)) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Incorrect layer_id for %s: tlayer_id %d mlayer_id %d xlayer_id %d",
avm_obu_type_to_string(obu_header.type), obu_header.obu_tlayer_id,
obu_header.obu_mlayer_id, obu_header.obu_xlayer_id);
}
if (obu_header.type == OBU_MULTI_STREAM_DECODER_OPERATION &&
obu_header.obu_xlayer_id != GLOBAL_XLAYER_ID) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Incorrect obu_xlayer_id for MSDO: %d",
obu_header.obu_xlayer_id);
}
if (obu_header.type == OBU_SEQUENCE_HEADER ||
obu_header.type == OBU_TEMPORAL_DELIMITER ||
obu_header.type == OBU_LAYER_CONFIGURATION_RECORD ||
obu_header.type == OBU_OPERATING_POINT_SET ||
obu_header.type == OBU_ATLAS_SEGMENT) {
if (obu_header.obu_tlayer_id != 0 || obu_header.obu_mlayer_id != 0)
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Incorrect layer_id for %s: "
"tlayer_id %d mlayer_id %d",
avm_obu_type_to_string(obu_header.type),
obu_header.obu_tlayer_id, obu_header.obu_mlayer_id);
}
// MSDO, LCR, OPS, Atlas, Short Metadata OBU, Metadata Group OBU, Padding,
// Temporal Delimiter, and Buffer removal timing.
if (obu_header.obu_xlayer_id == GLOBAL_XLAYER_ID &&
!(obu_header.type == OBU_TEMPORAL_DELIMITER ||
obu_header.type == OBU_METADATA_SHORT ||
obu_header.type == OBU_METADATA_GROUP ||
obu_header.type == OBU_BUFFER_REMOVAL_TIMING ||
obu_header.type == OBU_LAYER_CONFIGURATION_RECORD ||
obu_header.type == OBU_ATLAS_SEGMENT ||
obu_header.type == OBU_OPERATING_POINT_SET ||
obu_header.type == OBU_MULTI_STREAM_DECODER_OPERATION ||
obu_header.type == OBU_PADDING)) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Incorrect layer_id for %s: xlayer_id %d",
avm_obu_type_to_string(obu_header.type),
obu_header.obu_xlayer_id);
}
// CLK/OLK are only present in temporal layer 0
if ((obu_header.type == OBU_CLOSED_LOOP_KEY ||
obu_header.type == OBU_OPEN_LOOP_KEY) &&
obu_header.obu_tlayer_id != 0) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Incorrect tlayer_id for %s: tlayer_id %d",
avm_obu_type_to_string(obu_header.type),
obu_header.obu_tlayer_id);
}
}
static BITSTREAM_PROFILE get_msdo_profile(struct AV2Decoder *pbi) {
return pbi->common.msdo_params.multistream_profile_idc;
}
static BITSTREAM_PROFILE get_lcr_global_profile(struct AV2Decoder *pbi) {
// Return the lcr_max_interop from the first valid global LCR's aggregate PTL.
// lcr_max_interop maps directly to the IOP (0, 1, 2) which corresponds to
// MAIN_420_10_IP0, MAIN_420_10_IP1, MAIN_420_10_IP2 respectively.
for (int j = 0; j < MAX_NUM_LCR; j++) {
if (pbi->lcr_list[GLOBAL_XLAYER_ID][j].valid) {
const struct GlobalLayerConfigurationRecord *glcr =
&pbi->lcr_list[GLOBAL_XLAYER_ID][j].global_lcr;
if (glcr->lcr_aggregate_info_present_flag)
return (BITSTREAM_PROFILE)glcr->aggregate_ptl.lcr_max_interop;
// Fall back to the first seq PTL if aggregate PTL is not present.
if (glcr->lcr_seq_profile_tier_level_info_present_flag &&
glcr->LcrMaxNumXLayerCount > 0)
return (BITSTREAM_PROFILE)glcr->seq_ptl[glcr->LcrXLayerID[0]]
.lcr_seq_profile_idc;
}
}
(void)pbi;
return MAIN_420_10_IP2; // Fallback
}
static BITSTREAM_PROFILE get_lcr_local_profile(struct AV2Decoder *pbi) {
// Return the lcr_seq_profile_idc from the first valid local LCR.
for (int i = 0; i < GLOBAL_XLAYER_ID; i++) {
for (int j = 0; j < MAX_NUM_LCR; j++) {
if (pbi->lcr_list[i][j].valid) {
const struct LocalLayerConfigurationRecord *llcr =
&pbi->lcr_list[i][j].local_lcr;
if (llcr->lcr_profile_tier_level_info_present_flag)
return (BITSTREAM_PROFILE)llcr->seq_ptl.lcr_seq_profile_idc;
}
}
}
(void)pbi;
return MAIN_420_10_IP2; // Fallback
}
// Conformance check for the presence of MSDO and LCR.
// The OBU requirements table in Annex A only applies when IOP < 3, i.e.
// when the MSDO profile or LCR profile is one of the IP profiles (0, 1, 2).
// For profile == 31 (CONFIGURABLE) the table does
// not impose additional constraints, so we return true.
bool conformance_check_msdo_lcr(struct AV2Decoder *pbi, bool global_lcr_present,
bool local_lcr_present) {
int msdo_present = pbi->multistream_decoder_mode;
int num_extended_layers = 0;
int num_embedded_layers = 0;
for (int i = 0; i < AVM_MAX_NUM_STREAMS - 1; i++) {
if (pbi->xlayer_id_map[i] > 0) num_extended_layers++;
}
for (int i = 0; i < MAX_NUM_MLAYERS; i++) {
bool found = false;
for (int x = 0; x < AVM_MAX_NUM_STREAMS; x++) {
if (pbi->mlayer_id_map[x][i] > 0) {
found = true;
break;
}
}
if (found) num_embedded_layers++;
}
assert(num_extended_layers > 0 && num_embedded_layers > 0);
// Determine the effective MSDO and LCR profiles.
const BITSTREAM_PROFILE msdo_prof = get_msdo_profile(pbi);
const BITSTREAM_PROFILE glcr_prof = get_lcr_global_profile(pbi);
const BITSTREAM_PROFILE llcr_prof = get_lcr_local_profile(pbi);
// The IOP table only applies for IOPs 0-2 (profiles IP0, IP1, IP2).
// If the signaled profile is >= 3, the table does not apply.
if (msdo_present && msdo_prof == CONFIGURABLE) return true;
if (global_lcr_present && glcr_prof == CONFIGURABLE) return true;
if (!msdo_present && !global_lcr_present && local_lcr_present &&
llcr_prof == CONFIGURABLE) {
return true;
}
if (!msdo_present && !global_lcr_present && !local_lcr_present &&
pbi->common.seq_params.seq_profile_idc == CONFIGURABLE) {
return true;
}
if (num_extended_layers == 1 && num_embedded_layers == 1) {
if (!msdo_present) return true;
}
if (num_extended_layers > 1 && num_embedded_layers == 1) {
if (msdo_present &&
(msdo_prof == MAIN_420_10_IP0 || msdo_prof == MAIN_420_10_IP1 ||
msdo_prof == MAIN_420_10_IP2 || msdo_prof == MAIN_422_10_IP1 ||
msdo_prof == MAIN_444_10_IP1))
return true;
if (global_lcr_present && glcr_prof == MAIN_420_10_IP2) return true;
}
if (num_extended_layers == 1 && num_embedded_layers > 1) {
if (!msdo_present && local_lcr_present &&
(llcr_prof == MAIN_420_10_IP1 || llcr_prof == MAIN_422_10_IP1 ||
llcr_prof == MAIN_444_10_IP1))
return true;
if (!msdo_present && (global_lcr_present || local_lcr_present) &&
(glcr_prof == MAIN_420_10_IP2 || llcr_prof == MAIN_420_10_IP2))
return true;
}
if (num_extended_layers > 1 && num_embedded_layers > 1) {
if (msdo_present && local_lcr_present && msdo_prof == MAIN_420_10_IP2)
return true;
if (global_lcr_present && glcr_prof == MAIN_420_10_IP2) return true;
}
return false;
}
static int get_ops_mlayer_count(const struct OpsMLayerInfo *mlayer_info,
int xlayer_id) {
if (mlayer_info == NULL) return 0;
if (xlayer_id < 0 || xlayer_id >= MAX_NUM_XLAYERS) return 0;
return mlayer_info->OPMLayerCount[xlayer_id];
}
static int get_ops_tlayer_count(const struct OpsMLayerInfo *mlayer_info,
int xlayer_id, int mlayer_id) {
if (mlayer_info == NULL) return 0;
if (xlayer_id < 0 || xlayer_id >= MAX_NUM_XLAYERS) return 0;
if (mlayer_id < 0 || mlayer_id >= MAX_NUM_MLAYERS) return 0;
return mlayer_info->OPTLayerCount[xlayer_id][mlayer_id];
}
// Default operating point set id
static const int default_ops_id = 0;
// Default operating point index
static const int default_op_index = 0;
// Select the operating point for the current xlayer.
// If the user has explicitly set an OPS via codec control, use that;
// otherwise fall back to the default (ops_id=0, op_index=0).
// This function determines the OPS ID to use:
// 1. selected_ops_id if >= 0 (user-specified via AV2D_SET_SELECTED_OPS)
// 2. otherwise, default_ops_id (0)
//
// When sbe_state.retention_map_ready is set, num_mlayers and num_tlayers are
// derived directly from the SBE retention map so that the decoder layer counts
// are consistent with the OBU-level filtering already applied by Annex F.
// When the retention map is not ready (no OPS selected, SBE disabled, or map
// not yet built), the decoder decodes all layers in the bitstream.
static void avm_set_current_operating_point(struct AV2Decoder *pbi,
int xlayer_id) {
struct DecOperatingPointParams *dec_op_params = &pbi->dec_op_params;
// If the Annex F retention map is ready, derive num_mlayers and num_tlayers
// from it so that the decoder layer counts match the OBU filter exactly.
// This ensures avm_set_current_operating_point and the SBE are always
// consistent and share the same source of truth.
if (pbi->sbe_state.retention_map_ready && pbi->sbe_state.extraction_enabled) {
int xid = xlayer_id;
if (xid < 0 || xid >= MAX_NUM_XLAYERS) {
avm_internal_error(&pbi->common.error, AVM_CODEC_UNSUP_BITSTREAM,
"Invalid xlayer_id %d for operating point selection",
xid);
}
// Count distinct mlayers retained for this xlayer.
unsigned int num_mlayers = 0;
for (int j = 0; j < MAX_NUM_MLAYERS; j++) {
for (int k = 0; k < MAX_NUM_TLAYERS; k++) {
if (pbi->sbe_state.retention_map[xid][j][k]) {
num_mlayers = j + 1;
break;
}
}
}
// Count distinct tlayers retained for the base mlayer of this xlayer.
unsigned int num_tlayers = 0;
for (int k = 0; k < MAX_NUM_TLAYERS; k++) {
for (int j = 0; j < MAX_NUM_MLAYERS; j++) {
if (pbi->sbe_state.retention_map[xid][j][k]) {
num_tlayers = k + 1;
break;
}
}
}
dec_op_params->num_mlayers = num_mlayers > 0 ? num_mlayers : 1;
dec_op_params->num_tlayers = num_tlayers > 0 ? num_tlayers : 1;
dec_op_params->isValid = 1;
// dec_ops/dec_op/DecOpSetId etc. are still populated below from the OPS
// for use by other decoder subsystems that inspect those fields.
}
int target_ops_id =
pbi->selected_ops_id >= 0 ? pbi->selected_ops_id : default_ops_id;
// Only proceed to check for the operating point iff the ops id is valid
if (target_ops_id < 0 || target_ops_id >= MAX_NUM_OPS_ID) return;
// Determine the target op point index.
int target_op_index =
(pbi->selected_ops_id >= 0) ? pbi->selected_op_index : default_op_index;
struct OperatingPointSet *ops = &pbi->ops_list[xlayer_id][target_ops_id];
dec_op_params->dec_ops = ops;
dec_op_params->dec_op = dec_op_params->dec_ops->op;
if (!ops->valid || ops->ops_id != target_ops_id) {
dec_op_params->DecOpSetId = 0;
dec_op_params->DecOpCount = 0;
dec_op_params->DecOpIndex = 0;
// If SBE retention map is not ready or not enabled, decode all layers.
if (!pbi->sbe_state.retention_map_ready ||
!pbi->sbe_state.extraction_enabled) {
dec_op_params->num_mlayers = MAX_NUM_MLAYERS;
dec_op_params->num_tlayers = MAX_NUM_TLAYERS;
}
dec_op_params->isValid = 0; // No matching OPS
dec_op_params->dec_ops = NULL;
dec_op_params->dec_op = NULL;
// No OPS was available, all layers to be decoded.
return;
}
// NOTE: if a GLOBAL vs. LOCAL OPS is to be used, then
// Additional logic can be placed here to differentiate between
// the global and local OPS
if (target_op_index >= 0 && target_op_index < ops->ops_cnt) {
struct OperatingPoint *op = &ops->op[target_op_index];
dec_op_params->DecOpSetId = ops->ops_id;
dec_op_params->DecOpCount = ops->ops_cnt;
dec_op_params->DecOpIndex = target_op_index;
dec_op_params->DecXlayerId = ops->obu_xlayer_id;
int xlayer = dec_op_params->DecXlayerId;
// Only overwrite num_mlayers/num_tlayers from OPS if the SBE retention
// map has not already set them. The SBE map is the authoritative source
// when Annex F extraction is active.
if (!pbi->sbe_state.retention_map_ready ||
!pbi->sbe_state.extraction_enabled) {
dec_op_params->num_mlayers =
get_ops_mlayer_count(&op->mlayer_info, xlayer);
dec_op_params->num_tlayers =
get_ops_tlayer_count(&op->mlayer_info, xlayer, pbi->common.mlayer_id);
if (dec_op_params->num_mlayers == 0) dec_op_params->num_mlayers = 1;
if (dec_op_params->num_tlayers == 0) dec_op_params->num_tlayers = 1;
}
dec_op_params->dec_op = op;
dec_op_params->isValid = 1;
return;
}
return;
}
// On success, sets *p_data_end and returns a boolean that indicates whether
// the decoding of the current frame is finished. On failure, sets
// cm->error.error_code and returns -1.
int avm_decode_frame_from_obus(struct AV2Decoder *pbi, const uint8_t *data,
const uint8_t *data_end,
const uint8_t **p_data_end) {
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(pbi, avm_decode_frame_from_obus_time);
#endif
AV2_COMMON *const cm = &pbi->common;
int frame_decoding_finished = 0;
ObuHeader obu_header;
memset(&obu_header, 0, sizeof(obu_header));
// Enable is_multistream if multiple extended layers are present.
// Enable multistream_decoder_mode only when an MSDO OBU is present.
if (pbi->obus_in_frame_unit_data[0][0][OBU_MULTI_STREAM_DECODER_OPERATION]) {
pbi->is_multistream = 1;
pbi->multistream_decoder_mode = 1;
} else if (pbi->glcr_is_present_in_tu) {
pbi->is_multistream = 1;
}
// Reset is_multistream at a random access point TU without MSDO/GLCR.
// obus_in_frame_unit_data reflects the current frame unit (populated before
// decode).
if (pbi->is_multistream) {
int tid = pbi->current_tlayer_id;
int mid = pbi->current_mlayer_id;
if (tid >= 0 && mid >= 0 &&
pbi->obus_in_frame_unit_data[0][0][OBU_TEMPORAL_DELIMITER] &&
!pbi->obus_in_frame_unit_data[0][0]
[OBU_MULTI_STREAM_DECODER_OPERATION] &&
!pbi->glcr_obu_in_frame_unit &&
(pbi->obus_in_frame_unit_data[tid][mid][OBU_CLOSED_LOOP_KEY] ||
pbi->obus_in_frame_unit_data[tid][mid][OBU_OPEN_LOOP_KEY] ||
pbi->obus_in_frame_unit_data[tid][mid][OBU_RAS_FRAME])) {
// Flush and reset like a config change
if (pbi->stream_info != NULL) {
flush_all_xlayer_frames(pbi, cm, true);
avm_free(pbi->stream_info);
pbi->stream_info = NULL;
}
cm->num_streams = 0;
for (int i = 0; i < AVM_MAX_NUM_STREAMS; i++) pbi->xlayer_id_map[i] = 0;
pbi->is_multistream = 0;
pbi->multistream_decoder_mode = 0;
}
}
// Per-TU conformance check: validate the PREVIOUS TU's accumulated
// xlayer/mlayer maps before resetting them for the current TU.
// This must run once per TU (here), not per-OBU (inside the loop).
if (pbi->random_accessed) {
int num_xlayers = 0;
int num_mlayers = 0;
for (int i = 0; i < AVM_MAX_NUM_STREAMS - 1; i++) {
if (pbi->xlayer_id_map[i] > 0) num_xlayers++;
}
for (int i = 0; i < MAX_NUM_MLAYERS; i++) {
bool found = false;
for (int x = 0; x < AVM_MAX_NUM_STREAMS; x++) {
if (pbi->mlayer_id_map[x][i] > 0) {
found = true;
break;
}
}
if (found) num_mlayers++;
}
if (num_xlayers > 0 && num_mlayers > 0) {
bool global_lcr_present = false;
bool local_lcr_present = false;
for (int j = 0; j < MAX_NUM_LCR; j++) {
if (pbi->lcr_list[GLOBAL_XLAYER_ID][j].valid) global_lcr_present = true;
}
for (int i = 0; i < GLOBAL_XLAYER_ID && !local_lcr_present; i++) {
for (int j = 0; j < MAX_NUM_LCR; j++) {
if (pbi->lcr_list[i][j].valid) {
local_lcr_present = true;
break;
}
}
}
if (!conformance_check_msdo_lcr(pbi, global_lcr_present,
local_lcr_present)) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"An MSDO or LCR OBU in the current CVS violates the requirements "
"of bitstream conformance for MSDO and LCR");
}
}
// Reset maps for the current TU
for (int i = 0; i < AVM_MAX_NUM_STREAMS - 1; i++) pbi->xlayer_id_map[i] = 0;
for (int x = 0; x < AVM_MAX_NUM_STREAMS; x++)
for (int i = 0; i < MAX_NUM_MLAYERS; i++) pbi->mlayer_id_map[x][i] = 0;
}
pbi->seen_frame_header = 0;
pbi->next_start_tile = 0;
pbi->num_tile_groups = 0;
pbi->msdo_is_present_in_tu = 0;
pbi->glcr_is_present_in_tu = 0;
if (data_end < data) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return -1;
}
int count_obus_with_frame_unit = 0;
obu_info *obu_list = pbi->obu_list;
uint32_t acc_qm_id_bitmap = 0;
int qm_bit_map_zero_signalled = 0;
// acc_fgm_id_bitmap accumulates fgm_id_bitmap in FGM OBU to check if film
// grain models signalled before a coded frame have the same fgm_id
uint32_t acc_fgm_id_bitmap = 0;
uint32_t acc_mfh_id_bitmap = 0;
uint16_t acc_sh_id_bitmap[MAX_NUM_XLAYERS] = { 0 };
uint8_t acc_lcr_id_bitmap[MAX_NUM_XLAYERS] = { 0 };
int prev_obu_xlayer_id = -1;
// prev_obu_type, prev_xlayer_id and tu_validation_state are used to compare
// obus in this "data"
OBU_TYPE prev_obu_type = NUM_OBU_TYPES;
int prev_xlayer_id = -1;
temporal_unit_state_t tu_validation_state = TU_STATE_START;
// decode frame as a series of OBUs
while (!frame_decoding_finished && cm->error.error_code == AVM_CODEC_OK) {
struct avm_read_bit_buffer rb;
size_t payload_size = 0;
size_t decoded_payload_size = 0;
size_t obu_payload_offset = 0;
size_t bytes_read = 0;
const size_t bytes_available = data_end - data;
if (bytes_available == 0 && !pbi->seen_frame_header) {
cm->error.error_code = AVM_CODEC_OK;
break;
}
avm_codec_err_t status = avm_read_obu_header_and_size(
data, bytes_available, &obu_header, &payload_size, &bytes_read);
if (status != AVM_CODEC_OK) {
cm->error.error_code = status;
return -1;
}
// Note: avm_read_obu_header_and_size() takes care of checking that this
// doesn't cause 'data' to advance past 'data_end'.
data += bytes_read;
if ((size_t)(data_end - data) < payload_size) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return -1;
}
// Annex F: Sub-bitstream extraction.
// When extraction is enabled, trigger retention map construction when
// transitioning from structural OBUs to non-structural OBUs, then
// filter OBUs based on the retention map.
if (pbi->sbe_state.extraction_enabled) {
if (!pbi->sbe_state.retention_map_ready &&
!is_sbe_structural_obu(obu_header.type)) {
av2_sbe_build_retention_map(&pbi->sbe_state, pbi);
}
if (pbi->sbe_state.retention_map_ready) {
if (!av2_sbe_should_retain_obu(
&pbi->sbe_state, obu_header.type, obu_header.obu_xlayer_id,
obu_header.obu_mlayer_id, obu_header.obu_tlayer_id)) {
pbi->sbe_state.obus_removed++;
data += payload_size;
continue;
}
pbi->sbe_state.obus_retained++;
}
}
if (is_single_tile_vcl_obu(obu_header.type) ||
is_multi_tile_vcl_obu(obu_header.type)) {
if (!pbi->seen_vcl_obu_in_this_tu) {
pbi->this_is_first_vcl_obu_in_tu = 1;
pbi->seen_vcl_obu_in_this_tu = 1;
} else {
pbi->this_is_first_vcl_obu_in_tu = 0;
}
}
if (obu_header.type == OBU_CLOSED_LOOP_KEY ||
obu_header.type == OBU_OPEN_LOOP_KEY) {
if (pbi->this_is_first_vcl_obu_in_tu)
pbi->this_is_first_keyframe_unit_in_tu = 1;
}
if (av2_is_leading_vcl_obu(obu_header.type))
cm->is_leading_picture = 1;
else if (av2_is_regular_vcl_obu(obu_header.type))
cm->is_leading_picture = 0;
else
cm->is_leading_picture = -1;
pbi->xlayer_id_map[obu_header.obu_xlayer_id] = 1;
pbi->mlayer_id_map[obu_header.obu_xlayer_id][obu_header.obu_mlayer_id] = 1;
obu_info *const curr_obu_info = &obu_list[count_obus_with_frame_unit];
curr_obu_info->obu_type = obu_header.type;
curr_obu_info->is_vcl = is_single_tile_vcl_obu(obu_header.type) ||
is_multi_tile_vcl_obu(obu_header.type);
if (curr_obu_info->is_vcl) {
assert(curr_obu_info->xlayer_id == obu_header.obu_xlayer_id);
assert(curr_obu_info->mlayer_id == obu_header.obu_mlayer_id);
assert(curr_obu_info->tlayer_id == obu_header.obu_tlayer_id);
}
curr_obu_info->first_tile_group = -1;
curr_obu_info->immediate_output_picture = -1;
curr_obu_info->showable_frame = -1;
curr_obu_info->display_order_hint = -1;
// Extract metadata_is_suffix for metadata OBUs
// Per spec sections 5.17.2 and 5.17.3, metadata_is_suffix is f(1) - the
// first bit of the payload
int metadata_is_suffix = -1; // -1 means not applicable
if (obu_header.type == OBU_METADATA_SHORT ||
obu_header.type == OBU_METADATA_GROUP) {
if (payload_size == 0) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"OBU_METADATA_x has an empty payload");
}
// data has been advanced by bytes_read, so data[0] is first payload byte
metadata_is_suffix = (data[0] & 0x80) >> 7;
}
// Validate OBU ordering within temporal units. Ignore padding OBUs and
// reserved OBUs in this check.
if (avm_obu_type_is_valid(obu_header.type) &&
obu_header.type != OBU_PADDING) {
if (!check_temporal_unit_structure(
&tu_validation_state, obu_header.type, obu_header.obu_xlayer_id,
metadata_is_suffix, prev_obu_type, prev_xlayer_id)) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"OBU order violation: current OBU %s with xlayer_id %d mlayer_id "
"%d previous OBU %s(xlayer_id=%d) invalid in current state",
avm_obu_type_to_string(obu_header.type), obu_header.obu_xlayer_id,
obu_header.obu_mlayer_id, avm_obu_type_to_string(prev_obu_type),
prev_xlayer_id);
}
prev_obu_type = obu_header.type;
prev_xlayer_id = obu_header.obu_xlayer_id;
}
check_valid_layer_id(obu_header, cm);
pbi->obu_type = obu_header.type;
cm->tlayer_id = obu_header.obu_tlayer_id;
cm->mlayer_id = obu_header.obu_mlayer_id;
if (!pbi->is_multistream || (obu_header.obu_xlayer_id == GLOBAL_XLAYER_ID &&
cm->xlayer_id == GLOBAL_XLAYER_ID)) {
cm->xlayer_id = obu_header.obu_xlayer_id;
} else if (cm->xlayer_id != GLOBAL_XLAYER_ID &&
obu_header.obu_xlayer_id == GLOBAL_XLAYER_ID) {
// Store xlayer context
av2_store_xlayer_context(pbi, cm, cm->xlayer_id);
cm->xlayer_id = obu_header.obu_xlayer_id;
} else if (cm->xlayer_id == GLOBAL_XLAYER_ID &&
obu_header.obu_xlayer_id != GLOBAL_XLAYER_ID) {
// Restore xlayer context
cm->xlayer_id = obu_header.obu_xlayer_id;
av2_restore_xlayer_context(pbi, cm, cm->xlayer_id);
} else if (cm->xlayer_id != obu_header.obu_xlayer_id) {
// Store and restore xlayer context
av2_store_xlayer_context(pbi, cm, cm->xlayer_id);
cm->xlayer_id = obu_header.obu_xlayer_id;
av2_restore_xlayer_context(pbi, cm, cm->xlayer_id);
}
if (obu_header.type == OBU_LEADING_TILE_GROUP ||
obu_header.type == OBU_REGULAR_TILE_GROUP) {
if (prev_obu_xlayer_id == -1) {
prev_obu_xlayer_id = obu_header.obu_xlayer_id;
} else {
if (pbi->is_multistream && prev_obu_xlayer_id >= 0 &&
obu_header.obu_xlayer_id != prev_obu_xlayer_id) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"tile group OBUs with the same stream_id shall "
"be contiguous within a temporal unit");
}
}
}
// Set is_bridge_frame flag based on OBU type
if (obu_header.type == OBU_BRIDGE_FRAME) {
cm->bridge_frame_info.is_bridge_frame = 1;
} else {
cm->bridge_frame_info.is_bridge_frame = 0;
}
// Flush remaining frames after xlayer context is correctly set.
// This must happen after xlayer switching but before processing frame OBUs.
// Skip flush while in GLOBAL_XLAYER_ID context -- the target layer's
// context has not been restored yet; flush will run once a non-global
// OBU restores it.
if (pbi->this_is_first_keyframe_unit_in_tu &&
cm->xlayer_id != GLOBAL_XLAYER_ID &&
pbi->obus_in_frame_unit_data[cm->tlayer_id][cm->mlayer_id]
[OBU_CLOSED_LOOP_KEY]) {
flush_remaining_frames(pbi, INT_MAX);
// Reset last_output_doh for this xlayer only, after flushing so that
// the flushed frames' DOH values are still checked against the old
// last_output_doh. Other xlayers may still be mid-sequence.
for (int ml = 0; ml < MAX_NUM_MLAYERS; ml++)
pbi->last_output_doh[cm->xlayer_id][ml] = -1;
}
// Flush leading frames (doh < last_olk_tu_display_order_hint) at the start
// of the first regular temporal unit after an OLK, before
// reset_buffer_other_than_OLK() clears their DPB slots.
if (pbi->olk_encountered && pbi->this_is_first_vcl_obu_in_tu &&
!pbi->seen_frame_header && cm->is_leading_picture == 0) {
flush_remaining_frames(pbi, pbi->last_olk_tu_display_order_hint);
}
av2_init_read_bit_buffer(pbi, &rb, data, data + payload_size);
switch (obu_header.type) {
case OBU_TEMPORAL_DELIMITER:
decoded_payload_size = read_temporal_delimiter_obu();
pbi->seen_frame_header = 0;
pbi->next_start_tile = 0;
pbi->seen_vcl_obu_in_this_tu = 0;
pbi->this_is_first_vcl_obu_in_tu = 0;
pbi->doh_tu_order_hint_bits_set = 0;
for (int i = 0; i < NUM_CUSTOM_QMS; i++) pbi->qm_protected[i] = 0;
break;
case OBU_MULTI_STREAM_DECODER_OPERATION:
decoded_payload_size =
read_multi_stream_decoder_operation_obu(pbi, &rb);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
if (pbi->sbe_state.extraction_enabled) {
av2_sbe_process_msdo(&pbi->sbe_state, cm->num_streams,
cm->stream_ids);
}
break;
case OBU_SEQUENCE_HEADER:
cm->xlayer_id = obu_header.obu_xlayer_id;
decoded_payload_size = read_sequence_header_obu(
pbi, obu_header.obu_xlayer_id, &rb, acc_sh_id_bitmap);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
if (pbi->sbe_state.extraction_enabled) {
av2_sbe_extract_seq_header_params(
&pbi->sbe_state, obu_header.obu_xlayer_id,
cm->seq_params.seq_profile_idc, cm->seq_params.seq_max_level_idx,
cm->seq_params.seq_tier, cm->seq_params.seq_max_mlayer_cnt);
}
break;
case OBU_BUFFER_REMOVAL_TIMING:
decoded_payload_size =
av2_read_buffer_removal_timing_obu(pbi, &rb, cm->xlayer_id);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
case OBU_LAYER_CONFIGURATION_RECORD:
decoded_payload_size = av2_read_layer_configuration_record_obu(
pbi, cm->xlayer_id, &rb, acc_lcr_id_bitmap);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
// Allocate stream_info if Global LCR triggers multi-stream mode
// (i.e., LcrMaxNumXLayerCount > 1 and no MSDO present)
if (cm->xlayer_id == GLOBAL_XLAYER_ID && pbi->glcr_is_present_in_tu &&
!pbi->msdo_is_present_in_tu && pbi->stream_info == NULL) {
const int num_streams = pbi->glcr_num_xlayers;
pbi->stream_info =
(StreamInfo *)avm_malloc(num_streams * sizeof(StreamInfo));
if (pbi->stream_info == NULL) {
avm_internal_error(&cm->error, AVM_CODEC_MEM_ERROR,
"Memory allocation failed for pbi->stream_info "
"(GLCR)\n");
}
memset(pbi->stream_info, 0, num_streams * sizeof(StreamInfo));
for (int i = 0; i < num_streams; i++) {
init_stream_info(&pbi->stream_info[i]);
}
}
if (pbi->sbe_state.extraction_enabled &&
cm->xlayer_id == GLOBAL_XLAYER_ID) {
av2_sbe_process_global_lcr(&pbi->sbe_state, cm->num_streams,
cm->stream_ids);
}
break;
case OBU_ATLAS_SEGMENT:
decoded_payload_size =
av2_read_atlas_segment_info_obu(pbi, cm->xlayer_id, &rb);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
case OBU_OPERATING_POINT_SET:
decoded_payload_size =
av2_read_operating_point_set_obu(pbi, cm->xlayer_id, &rb);
avm_set_current_operating_point(pbi, cm->xlayer_id);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
if (pbi->sbe_state.extraction_enabled) {
if (cm->xlayer_id == GLOBAL_XLAYER_ID) {
// Check if the user-selected global OPS is now available
if (pbi->selected_ops_id >= 0 &&
pbi->selected_ops_id < MAX_NUM_OPS_ID) {
const struct OperatingPointSet *sel_ops =
&pbi->ops_list[GLOBAL_XLAYER_ID][pbi->selected_ops_id];
if (sel_ops->valid) {
av2_sbe_process_global_ops(
&pbi->sbe_state, sel_ops->ops_id, sel_ops->ops_cnt,
pbi->selected_ops_id, pbi->selected_op_index,
(sel_ops->ops_cnt > 0 && pbi->selected_op_index >= 0 &&
pbi->selected_op_index < sel_ops->ops_cnt)
? sel_ops->op[pbi->selected_op_index].ops_xlayer_map
: 0,
sel_ops->ops_mlayer_info_idc);
}
}
} else {
av2_sbe_process_local_ops(&pbi->sbe_state, pbi, cm->xlayer_id,
pbi->dec_op_params.DecOpSetId,
pbi->dec_op_params.DecOpCount);
}
}
break;
case OBU_CONTENT_INTERPRETATION:
decoded_payload_size = av2_read_content_interpretation_obu(pbi, &rb);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
case OBU_MULTI_FRAME_HEADER:
decoded_payload_size =
read_multi_frame_header_obu(pbi, &acc_mfh_id_bitmap, &rb);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
case OBU_CLOSED_LOOP_KEY:
case OBU_OPEN_LOOP_KEY:
case OBU_LEADING_TILE_GROUP:
case OBU_REGULAR_TILE_GROUP:
case OBU_SWITCH:
case OBU_LEADING_SEF:
case OBU_REGULAR_SEF:
case OBU_LEADING_TIP:
case OBU_REGULAR_TIP:
case OBU_RAS_FRAME:
case OBU_BRIDGE_FRAME:
// Constraint 4: Switch frames shall be at the base temporal layer.
if ((obu_header.type == OBU_SWITCH ||
obu_header.type == OBU_RAS_FRAME) &&
obu_header.obu_tlayer_id != 0) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Switch frame (obu_type=%d) must have obu_tlayer_id equal to "
"0, got %d.",
obu_header.type, obu_header.obu_tlayer_id);
}
if (obu_header.type == OBU_SWITCH || obu_header.type == OBU_RAS_FRAME) {
const int mid = obu_header.obu_mlayer_id;
for (int j = 0; j < MAX_NUM_MLAYERS; j++) {
if (j != mid && cm->seq_params.mlayer_dependency_map[mid][j] != 0) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"Switch frame at mlayer_id=%d depends on mlayer %d. "
"Switch frames must be in independent embedded layers.",
mid, j);
}
}
}
// Drop picture unit HLS state that was derived exclusively from leading
// frame picture units when the first regular VCL OBU is encountered.
if (av2_is_leading_vcl_obu(obu_header.type)) {
// Tag every MFH/QM/FGM/CI/BRT signalled in the leading temporal
// unit so we can identify and discard them at the transition.
const int leading_mlayer_id = cm->mlayer_id;
for (int i = 0; i < MAX_MFH_NUM; i++)
if (acc_mfh_id_bitmap & (1 << i))
cm->mfh_params[i].mfh_from_leading = 1;
for (int i = 0; i < NUM_CUSTOM_QMS; i++)
if ((acc_qm_id_bitmap & (1 << i)) || qm_bit_map_zero_signalled) {
pbi->qm_list[i].qm_from_leading = 1;
}
for (int i = 0; i < MAX_FGM_NUM; i++)
if (acc_fgm_id_bitmap & (1 << i)) {
pbi->fgm_list[i].fgm_from_leading = 1;
}
if (pbi->obus_in_frame_unit_data[obu_header.obu_tlayer_id]
[leading_mlayer_id]
[OBU_CONTENT_INTERPRETATION])
cm->ci_params_per_layer[leading_mlayer_id].ci_from_leading = true;
if (pbi->obus_in_frame_unit_data[obu_header.obu_tlayer_id]
[leading_mlayer_id]
[OBU_BUFFER_REMOVAL_TIMING])
cm->brt_info.brt_from_leading = true;
for (int x = 0; x < MAX_NUM_XLAYERS; x++)
for (int s = 0; s < MAX_SEQ_NUM; s++)
if (acc_sh_id_bitmap[x] & (1 << s))
pbi->seq_list[x][s].sh_from_leading = 1;
for (int x = 0; x < MAX_NUM_XLAYERS; x++)
for (int l = 0; l < MAX_NUM_LCR; l++)
if (acc_lcr_id_bitmap[x] & (1 << l))
pbi->lcr_list[x][l].lcr_from_leading = true;
} else if (pbi->this_is_first_vcl_obu_in_tu == 1) {
// SEF, TIP, SWITCH, RAS, BRIDGE, TG (not CLK)
// MFH, QM, FGM, BRT, CI signalled in leading temporal unit cannot
// be used. Drop state not re-signalled in the regular picture unit.
const int regular_mlayer_id = cm->mlayer_id;
const int num_planes = av2_num_planes(cm);
for (int i = 0; i < NUM_CUSTOM_QMS; i++) {
struct quantization_matrix_set *qmset = &pbi->qm_list[i];
if (qmset->qm_from_leading == 1 && !(acc_qm_id_bitmap & (1 << i)) &&
!qm_bit_map_zero_signalled) {
qmset->qm_id = -1;
qmset->qm_mlayer_id = -1;
qmset->qm_tlayer_id = -1;
qmset->quantizer_matrix_num_planes = num_planes;
qmset->is_user_defined_qm = false;
pbi->qm_protected[i] = 0;
}
qmset->qm_from_leading = 0;
}
for (int i = 0; i < MAX_FGM_NUM; i++) {
if (pbi->fgm_list[i].fgm_from_leading == 1 &&
!(acc_fgm_id_bitmap & (1 << i))) {
pbi->fgm_list[i].fgm_id = -1;
pbi->fgm_list[i].fgm_tlayer_id = -1;
pbi->fgm_list[i].fgm_mlayer_id = -1;
}
pbi->fgm_list[i].fgm_from_leading = 0;
}
if (cm->ci_params_per_layer[regular_mlayer_id].ci_from_leading == 1 &&
!pbi->obus_in_frame_unit_data[obu_header.obu_tlayer_id]
[regular_mlayer_id]
[OBU_CONTENT_INTERPRETATION]) {
av2_initialize_ci_params(
&cm->ci_params_per_layer[regular_mlayer_id]);
}
cm->ci_params_per_layer[regular_mlayer_id].ci_from_leading = false;
for (int i = 0; i < MAX_MFH_NUM; i++) {
if (cm->mfh_params[i].mfh_from_leading == 1 &&
!(acc_mfh_id_bitmap & (1 << i))) {
cm->mfh_valid[i] = false;
}
cm->mfh_params[i].mfh_from_leading = 0;
}
if (cm->brt_info.brt_from_leading == 1 &&
!pbi->obus_in_frame_unit_data[obu_header.obu_tlayer_id]
[regular_mlayer_id]
[OBU_BUFFER_REMOVAL_TIMING]) {
memset(&cm->brt_info, 0, sizeof(cm->brt_info));
}
cm->brt_info.brt_from_leading = false;
for (int x = 0; x < MAX_NUM_XLAYERS; x++) {
for (int s = 0; s < MAX_SEQ_NUM; s++) {
if (!(pbi->seq_list[x][s].sh_from_leading == 1 &&
!(acc_sh_id_bitmap[x] & (1 << s))))
pbi->seq_list[x][s].sh_from_leading = 0;
}
for (int l = 0; l < MAX_NUM_LCR; l++) {
if (!(pbi->lcr_list[x][l].lcr_from_leading &&
!(acc_lcr_id_bitmap[x] & (1 << l))))
pbi->lcr_list[x][l].lcr_from_leading = false;
}
}
}
// It is a requirement that if multiple QM OBUs are present
// consecutively prior to a coded frame, other than a QM OBU with
// qm_bit_map equal to 0, such QM OBUs will not set the same QM ID more
// than once.
acc_qm_id_bitmap = 0;
qm_bit_map_zero_signalled = 0;
// It is a requirement that if multiple FGM OBUs are present
// consecutively prior to a coded frame, such FGM OBUs will not set
// the same FGM ID more than once.
acc_fgm_id_bitmap = 0;
acc_mfh_id_bitmap = 0;
memset(acc_sh_id_bitmap, 0, sizeof(acc_sh_id_bitmap));
memset(acc_lcr_id_bitmap, 0, sizeof(acc_lcr_id_bitmap));
decoded_payload_size = read_tilegroup_obu(
pbi, &rb, data, data + payload_size, p_data_end, obu_header.type,
obu_header.obu_xlayer_id, &curr_obu_info->first_tile_group,
&frame_decoding_finished);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
curr_obu_info->immediate_output_picture = cm->immediate_output_picture;
curr_obu_info->showable_frame =
cm->immediate_output_picture || cm->implicit_output_picture;
curr_obu_info->display_order_hint =
cm->current_frame.display_order_hint;
int num_mlayers = 0;
for (int i = 0; i < MAX_NUM_MLAYERS; ++i) {
if (pbi->mlayer_id_map[obu_header.obu_xlayer_id][i] == 1)
num_mlayers++;
}
if (num_mlayers > 1 &&
num_mlayers > cm->seq_params.seq_max_mlayer_cnt) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"The number of embedded layers (%d) in the current video "
"sequence is larger than the seq_max_mlayer_cnt (%d).",
num_mlayers, cm->seq_params.seq_max_mlayer_cnt);
}
if (cm->bru.frame_inactive_flag ||
cm->bridge_frame_info.is_bridge_frame) {
pbi->seen_frame_header = 0;
frame_decoding_finished = 1;
CommonTileParams *const tiles = &cm->tiles;
av2_get_tile_limits(
&cm->tiles, cm->mi_params.mi_rows, cm->mi_params.mi_cols,
cm->mib_size_log2, cm->seq_params.mib_size_log2,
cm->seq_params.seq_max_level_idx, cm->seq_params.seq_tier);
tiles->uniform_spacing = 1;
tiles->log2_cols = 0;
av2_calculate_tile_cols(tiles);
tiles->log2_rows = 0;
av2_calculate_tile_rows(tiles);
const int num_tiles = cm->tiles.cols * cm->tiles.rows;
const int end_tile = num_tiles - 1;
// skip parsing and go directly to decode
av2_decode_tg_tiles_and_wrapup(pbi, data, data + payload_size,
p_data_end, 0, end_tile, 0);
if (cm->bridge_frame_info.is_bridge_frame) {
*p_data_end = data + payload_size;
}
break;
}
if (obu_payload_offset > payload_size) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return -1;
}
if (cm->error.error_code != AVM_CODEC_OK) return -1;
if (frame_decoding_finished) pbi->seen_frame_header = 0;
pbi->num_tile_groups++;
break;
case OBU_QUANTIZATION_MATRIX:
decoded_payload_size =
read_qm_obu(pbi, obu_header.obu_tlayer_id, obu_header.obu_mlayer_id,
&acc_qm_id_bitmap, &qm_bit_map_zero_signalled, &rb);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
case OBU_METADATA_SHORT:
decoded_payload_size = read_metadata_short(pbi, data, payload_size, 0);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
case OBU_METADATA_GROUP:
decoded_payload_size =
read_metadata_obu(pbi, data, payload_size, &obu_header, 0);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
case OBU_FILM_GRAIN_MODEL:
decoded_payload_size =
read_fgm_obu(pbi, obu_header.obu_tlayer_id,
obu_header.obu_mlayer_id, &acc_fgm_id_bitmap, &rb);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
case OBU_PADDING:
decoded_payload_size = read_padding(cm, data, payload_size);
if (cm->error.error_code != AVM_CODEC_OK) return -1;
break;
default:
// Skip unrecognized OBUs
if (payload_size > 0 &&
get_last_nonzero_byte(data, payload_size) == 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return -1;
}
decoded_payload_size = payload_size;
break;
}
// Spec 6.2.2:
// "At the end of reading the OBU, it is a requirement of bitstream "
// "conformance that obu_mlayer_id is less than or equal to max_mlayer_id."
if (obu_header.obu_mlayer_id > cm->seq_params.max_mlayer_id &&
(!pbi->decoding_first_frame || frame_decoding_finished)) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"obu_mlayer_id (%d) must be less than or equal to "
"max_mlayer_id (%d).",
obu_header.obu_mlayer_id,
cm->seq_params.max_mlayer_id);
}
// Check that the signalled OBU size matches the actual amount of data read
if (decoded_payload_size > payload_size) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return -1;
}
// If there are extra padding bytes, they should all be zero
while (decoded_payload_size < payload_size) {
uint8_t padding_byte = data[decoded_payload_size++];
if (padding_byte != 0) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return -1;
}
}
data += payload_size;
count_obus_with_frame_unit++;
}
// check whether suffix metadata OBUs are present
while (cm->error.error_code == AVM_CODEC_OK && data < data_end) {
size_t payload_size = 0;
size_t decoded_payload_size = 0;
size_t bytes_read = 0;
const size_t bytes_available = data_end - data;
avm_codec_err_t status = avm_read_obu_header_and_size(
data, bytes_available, &obu_header, &payload_size, &bytes_read);
if (status != AVM_CODEC_OK) {
cm->error.error_code = status;
return -1;
}
// Accept both OBU_METADATA_SHORT and OBU_METADATA_GROUP for suffix metadata
if (!(is_metadata_obu(obu_header.type) || (obu_header.type == OBU_PADDING)))
break;
// Note: avm_read_obu_header_and_size() takes care of checking that this
// doesn't cause 'data' to advance past 'data_end'.
data += bytes_read;
if ((size_t)(data_end - data) < payload_size) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return -1;
}
if (obu_header.type == OBU_PADDING) {
decoded_payload_size = read_padding(cm, data, payload_size);
obu_info *const curr_obu_info = &obu_list[count_obus_with_frame_unit];
curr_obu_info->obu_type = obu_header.type;
curr_obu_info->first_tile_group = -1;
curr_obu_info->immediate_output_picture = -1;
curr_obu_info->showable_frame = -1;
curr_obu_info->display_order_hint = -1;
count_obus_with_frame_unit++;
if (cm->error.error_code != AVM_CODEC_OK) return -1;
} else if (is_metadata_obu(obu_header.type)) {
// check whether it is a suffix metadata OBU
if (payload_size == 0) {
avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
"OBU_METADATA_x has an empty payload");
}
if (!(data[0] & 0x80)) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"OBU order violation: OBU_METADATA_x(prefix) cannot "
"be present after a coded frame");
}
// Call the appropriate read function based on OBU type
if (obu_header.type == OBU_METADATA_GROUP) {
decoded_payload_size =
read_metadata_obu(pbi, data, payload_size, &obu_header, 1);
} else {
decoded_payload_size = read_metadata_short(pbi, data, payload_size, 1);
}
obu_info *const curr_obu_info = &obu_list[count_obus_with_frame_unit];
curr_obu_info->obu_type = obu_header.type;
curr_obu_info->first_tile_group = -1;
curr_obu_info->immediate_output_picture = -1;
curr_obu_info->showable_frame = -1;
curr_obu_info->display_order_hint = -1;
count_obus_with_frame_unit++;
}
if (cm->error.error_code != AVM_CODEC_OK) return -1;
// Spec 6.2.2:
// "At the end of reading the OBU, it is a requirement of bitstream "
// "conformance that obu_mlayer_id is less than or equal to max_mlayer_id."
if (obu_header.obu_mlayer_id > cm->seq_params.max_mlayer_id &&
(!pbi->decoding_first_frame || frame_decoding_finished)) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"obu_mlayer_id (%d) must be less than or equal to "
"max_mlayer_id (%d).",
obu_header.obu_mlayer_id,
cm->seq_params.max_mlayer_id);
}
// Check that the signalled OBU size matches the actual amount of data read
if (decoded_payload_size > payload_size) {
cm->error.error_code = AVM_CODEC_CORRUPT_FRAME;
return -1;
}
data += payload_size;
}
if (cm->error.error_code != AVM_CODEC_OK) return -1;
*p_data_end = data;
#if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(pbi, avm_decode_frame_from_obus_time);
// Print out timing information.
int i;
fprintf(stderr, "\n Frame number: %d, Frame type: %s, Show Frame: %d\n",
cm->current_frame.frame_number,
get_frame_type_enum(cm->current_frame.frame_type),
cm->immediate_output_picture);
for (i = 0; i < kTimingComponents; i++) {
pbi->component_time[i] += pbi->frame_component_time[i];
fprintf(stderr, " %s: %" PRId64 " us (total: %" PRId64 " us)\n",
get_component_name(i), pbi->frame_component_time[i],
pbi->component_time[i]);
pbi->frame_component_time[i] = 0;
}
#endif
obu_info current_frame_unit;
memset(&current_frame_unit, -1, sizeof(current_frame_unit));
for (int obu_idx = 0; obu_idx < count_obus_with_frame_unit; obu_idx++) {
obu_info *this_obu = &obu_list[obu_idx];
if (this_obu->first_tile_group == 1) {
current_frame_unit = *this_obu;
pbi->num_displayable_frame_unit[this_obu->mlayer_id]++;
}
if (is_multi_tile_vcl_obu(this_obu->obu_type) &&
this_obu->first_tile_group == 0) {
// Allow padding OBUs between tile group OBUs.
int prev_obu_idx = obu_idx - 1;
while (prev_obu_idx >= 0 &&
obu_list[prev_obu_idx].obu_type == OBU_PADDING) {
prev_obu_idx--;
}
if (prev_obu_idx < 0) {
avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"The first non-padding OBU in a frame unit cannot "
"be a tile group with is_first_tile_group == 0");
}
check_tilegroup_obus_in_a_frame_unit(cm, this_obu,
&obu_list[prev_obu_idx]);
}
}
// When SBE filters out all frame OBUs in a temporal unit, no frame was
// found: skip the consistency checks.
int xId = current_frame_unit.xlayer_id;
if (current_frame_unit.display_order_hint != -1 &&
pbi->last_frame_unit[xId].display_order_hint != -1 &&
(pbi->last_frame_unit[xId].xlayer_id == current_frame_unit.xlayer_id)) {
check_clk_in_a_layer(cm, &current_frame_unit, &pbi->last_frame_unit[xId]);
if (current_frame_unit.showable_frame == 0) {
check_layerid_hidden_frame_units(cm, &current_frame_unit,
&pbi->last_frame_unit[xId]);
} else {
check_layerid_showable_frame_units(
cm, &current_frame_unit, &pbi->last_frame_unit[xId],
&pbi->last_displayable_frame_unit[xId]);
}
}
return frame_decoding_finished;
}