blob: 6d447c64a0fec43e24d54da26ff79469915f4ded [file] [edit]
/*
* Copyright (c) 2026, 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 <cstring>
#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
#include "av2/common/av2_common_int.h"
#include "av2/common/quant_common.h"
#include "av2/decoder/decoder.h"
#include "av2/decoder/decodeframe.h"
extern "C" {
#include "av2/decoder/obu.h"
}
#include "avm_dsp/bitwriter_buffer.h"
#include "avm_dsp/bitreader_buffer.h"
#include "avm_mem/avm_mem.h"
namespace {
static void rb_error_handler(void *data, avm_codec_err_t error,
const char *detail) {
(void)data;
(void)error;
(void)detail;
}
// Write a QM OBU manually. Supports predefined matrices only
// (user-defined matrices require codec-internal scan order tables).
static uint32_t write_qm_obu_predefined(int qm_bit_map,
int qm_chroma_info_present_flag,
uint8_t *dst) {
struct avm_write_bit_buffer wb = { dst, 0 };
avm_wb_write_literal(&wb, qm_bit_map, NUM_CUSTOM_QMS);
avm_wb_write_bit(&wb, qm_chroma_info_present_flag);
if (qm_bit_map != 0) {
for (int j = 0; j < NUM_CUSTOM_QMS; j++) {
if (qm_bit_map & (1 << j)) {
avm_wb_write_bit(&wb, 1); // qm_is_predefined_flag = true
}
}
}
avm_wb_write_bit(&wb, 1); // trailing stop bit
int pad = (8 - wb.bit_offset % 8) % 8;
if (pad > 0) {
avm_wb_write_literal(&wb, 0, pad);
}
return avm_wb_bytes_written(&wb);
}
class QmTest : public ::testing::Test {
protected:
void SetUp() override {
pbi_ = static_cast<AV2Decoder *>(avm_memalign(32, sizeof(AV2Decoder)));
ASSERT_NE(pbi_, nullptr);
memset(pbi_, 0, sizeof(*pbi_));
memset(buf_, 0, sizeof(buf_));
}
void TearDown() override { avm_free(pbi_); }
AV2Decoder *pbi_;
uint8_t buf_[4096];
};
// qm_bit_map == 0: all predefined, resets all QM slots.
TEST_F(QmTest, AllPredefinedBitmapZero) {
int qm_bit_map = 0;
uint32_t written = write_qm_obu_predefined(qm_bit_map, 1, buf_);
ASSERT_GT(written, 0u);
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
uint32_t read = read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb);
ASSERT_EQ(read, written);
EXPECT_EQ(zero_signalled, 1);
for (int j = 0; j < NUM_CUSTOM_QMS; j++) {
EXPECT_FALSE(pbi_->qm_list[j].is_user_defined_qm) << "qm_id=" << j;
EXPECT_EQ(pbi_->qm_protected[j], 1) << "qm_id=" << j;
}
}
// Single predefined QM at slot 0.
TEST_F(QmTest, SinglePredefinedSlot0) {
int qm_bit_map = 0x1;
uint32_t written = write_qm_obu_predefined(qm_bit_map, 1, buf_);
ASSERT_GT(written, 0u);
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
uint32_t read = read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb);
ASSERT_EQ(read, written);
EXPECT_EQ(zero_signalled, 0);
EXPECT_EQ(acc_bitmap, 0x1u);
EXPECT_FALSE(pbi_->qm_list[0].is_user_defined_qm);
EXPECT_EQ(pbi_->qm_list[0].qm_id, 0);
EXPECT_EQ(pbi_->qm_protected[0], 1);
}
// Multiple predefined QMs at slots 0, 3, 14.
TEST_F(QmTest, MultiplePredefinedSlots) {
int qm_bit_map = (1 << 0) | (1 << 3) | (1 << 14);
uint32_t written = write_qm_obu_predefined(qm_bit_map, 0, buf_);
ASSERT_GT(written, 0u);
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
uint32_t read = read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb);
ASSERT_EQ(read, written);
EXPECT_EQ(acc_bitmap, (uint32_t)qm_bit_map);
EXPECT_FALSE(pbi_->qm_list[0].is_user_defined_qm);
EXPECT_FALSE(pbi_->qm_list[3].is_user_defined_qm);
EXPECT_FALSE(pbi_->qm_list[14].is_user_defined_qm);
EXPECT_EQ(pbi_->qm_list[0].qm_id, 0);
EXPECT_EQ(pbi_->qm_list[3].qm_id, 3);
EXPECT_EQ(pbi_->qm_list[14].qm_id, 14);
}
// Chroma info present flag: true vs false.
TEST_F(QmTest, ChromaInfoPresentFlag) {
for (int chroma = 0; chroma < 2; chroma++) {
int qm_bit_map = 0x1;
memset(buf_, 0, sizeof(buf_));
uint32_t written = write_qm_obu_predefined(qm_bit_map, chroma, buf_);
ASSERT_GT(written, 0u) << "chroma=" << chroma;
memset(pbi_, 0, sizeof(*pbi_));
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
uint32_t read = read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb);
ASSERT_EQ(read, written) << "chroma=" << chroma;
int expected_planes = chroma ? 3 : 1;
EXPECT_EQ(pbi_->qm_list[0].quantizer_matrix_num_planes, expected_planes);
}
}
// Bitmap boundary: all bits set (0x7FFF for 15 QMs).
TEST_F(QmTest, AllSlotsPredefined) {
int qm_bit_map = (1 << NUM_CUSTOM_QMS) - 1;
uint32_t written = write_qm_obu_predefined(qm_bit_map, 1, buf_);
ASSERT_GT(written, 0u);
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
uint32_t read = read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb);
ASSERT_EQ(read, written);
EXPECT_EQ(acc_bitmap, (uint32_t)qm_bit_map);
for (int j = 0; j < NUM_CUSTOM_QMS; j++) {
EXPECT_FALSE(pbi_->qm_list[j].is_user_defined_qm) << "qm_id=" << j;
EXPECT_EQ(pbi_->qm_list[j].qm_id, j) << "qm_id=" << j;
}
}
// Accumulated bitmap across multiple OBU reads.
TEST_F(QmTest, AccumulatedBitmap) {
// First OBU: slots 0 and 1.
int bm1 = 0x3;
uint32_t w1 = write_qm_obu_predefined(bm1, 1, buf_);
ASSERT_GT(w1, 0u);
struct avm_read_bit_buffer rb1 = { buf_, buf_ + w1, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
uint32_t r1 = read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb1);
ASSERT_EQ(r1, w1);
EXPECT_EQ(acc_bitmap, 0x3u);
// Second OBU: slots 4 and 5 (non-overlapping).
uint8_t buf2[256];
int bm2 = 0x30;
uint32_t w2 = write_qm_obu_predefined(bm2, 1, buf2);
ASSERT_GT(w2, 0u);
struct avm_read_bit_buffer rb2 = { buf2, buf2 + w2, 0, nullptr,
rb_error_handler };
uint32_t r2 = read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb2);
ASSERT_EQ(r2, w2);
EXPECT_EQ(acc_bitmap, 0x33u);
}
// Tlayer and mlayer IDs are stored per QM slot.
TEST_F(QmTest, TlayerMlayerIds) {
int qm_bit_map = 0x1;
uint32_t written = write_qm_obu_predefined(qm_bit_map, 1, buf_);
ASSERT_GT(written, 0u);
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
uint32_t read = read_qm_obu(pbi_, 2, 1, &acc_bitmap, &zero_signalled, &rb);
ASSERT_EQ(read, written);
EXPECT_EQ(pbi_->qm_list[0].qm_tlayer_id, 2);
EXPECT_EQ(pbi_->qm_list[0].qm_mlayer_id, 1);
}
TEST_F(QmTest, SingleBitPositionSweep) {
for (int bit = 0; bit < NUM_CUSTOM_QMS; bit++) {
int qm_bit_map = 1 << bit;
memset(buf_, 0, sizeof(buf_));
uint32_t written = write_qm_obu_predefined(qm_bit_map, 1, buf_);
ASSERT_GT(written, 0u) << "bit=" << bit;
memset(pbi_, 0, sizeof(*pbi_));
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
uint32_t read = read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb);
ASSERT_EQ(read, written) << "bit=" << bit;
EXPECT_EQ(acc_bitmap, (uint32_t)(1 << bit)) << "bit=" << bit;
EXPECT_EQ(pbi_->qm_list[bit].qm_id, bit) << "bit=" << bit;
EXPECT_FALSE(pbi_->qm_list[bit].is_user_defined_qm) << "bit=" << bit;
EXPECT_EQ(pbi_->qm_protected[bit], 1) << "bit=" << bit;
}
}
TEST_F(QmTest, ZeroBitmapFlagNotSetForNonZero) {
int qm_bit_map = 0x1;
uint32_t written = write_qm_obu_predefined(qm_bit_map, 1, buf_);
ASSERT_GT(written, 0u);
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb);
EXPECT_EQ(zero_signalled, 0);
}
TEST_F(QmTest, PredefinedMatrixNotUserDefined) {
int qm_bit_map = (1 << 0) | (1 << 7) | (1 << 14);
uint32_t written = write_qm_obu_predefined(qm_bit_map, 0, buf_);
ASSERT_GT(written, 0u);
struct avm_read_bit_buffer rb = { buf_, buf_ + written, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb);
EXPECT_FALSE(pbi_->qm_list[0].is_user_defined_qm);
EXPECT_FALSE(pbi_->qm_list[7].is_user_defined_qm);
EXPECT_FALSE(pbi_->qm_list[14].is_user_defined_qm);
EXPECT_EQ(pbi_->qm_list[0].quantizer_matrix_num_planes, 1);
EXPECT_EQ(pbi_->qm_list[7].quantizer_matrix_num_planes, 1);
EXPECT_EQ(pbi_->qm_list[14].quantizer_matrix_num_planes, 1);
}
TEST_F(QmTest, ProtectedFlagPersistsAcrossObus) {
// First OBU: slots 0 and 1.
uint32_t w1 = write_qm_obu_predefined(0x3, 1, buf_);
ASSERT_GT(w1, 0u);
struct avm_read_bit_buffer rb1 = { buf_, buf_ + w1, 0, nullptr,
rb_error_handler };
uint32_t acc_bitmap = 0;
int zero_signalled = 0;
read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb1);
EXPECT_EQ(pbi_->qm_protected[0], 1);
EXPECT_EQ(pbi_->qm_protected[1], 1);
EXPECT_EQ(pbi_->qm_protected[2], 0);
// Second OBU: slot 5.
uint8_t buf2[256];
uint32_t w2 = write_qm_obu_predefined(1 << 5, 1, buf2);
ASSERT_GT(w2, 0u);
struct avm_read_bit_buffer rb2 = { buf2, buf2 + w2, 0, nullptr,
rb_error_handler };
read_qm_obu(pbi_, 0, 0, &acc_bitmap, &zero_signalled, &rb2);
// All three slots should be protected.
EXPECT_EQ(pbi_->qm_protected[0], 1);
EXPECT_EQ(pbi_->qm_protected[1], 1);
EXPECT_EQ(pbi_->qm_protected[5], 1);
// Untouched slots remain unprotected.
EXPECT_EQ(pbi_->qm_protected[3], 0);
}
} // namespace