From 994f23900a147197ddad49350cbb482ff1ac9c9b Mon Sep 17 00:00:00 2001 From: jwcullen Date: Wed, 3 Jul 2024 09:25:37 -0400 Subject: [PATCH] Implement a function to convert an internal subblock to a metadata subblock. PiperOrigin-RevId: 649051798 --- README.md | 2 + iamf/cli/obu_to_proto/BUILD | 25 ++ .../parameter_block_metadata_generator.cc | 212 +++++++++++++++ .../parameter_block_metadata_generator.h | 38 +++ iamf/cli/obu_to_proto/tests/BUILD | 16 ++ ...parameter_block_metadata_generator_test.cc | 254 ++++++++++++++++++ 6 files changed, 547 insertions(+) create mode 100644 iamf/cli/obu_to_proto/BUILD create mode 100644 iamf/cli/obu_to_proto/parameter_block_metadata_generator.cc create mode 100644 iamf/cli/obu_to_proto/parameter_block_metadata_generator.h create mode 100644 iamf/cli/obu_to_proto/tests/BUILD create mode 100644 iamf/cli/obu_to_proto/tests/parameter_block_metadata_generator_test.cc diff --git a/README.md b/README.md index 66bdf655..01276376 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ format. These tools can be used as a complement to the `libiamf` * `codec/` - Files that encode or decode substreams with codec-specific libraries. * `tests/` - Unit tests for files under `codec/`. + * `obu_to_proto/` - Components to convert classes under `iamf/obu` to + protocol buffers. * `proto/` - [Protocol buffers](https://protobuf.dev/) defining the data scheme for the input files under `iamf/cli/testdata/` or user-created ones. diff --git a/iamf/cli/obu_to_proto/BUILD b/iamf/cli/obu_to_proto/BUILD new file mode 100644 index 00000000..5dad9ba5 --- /dev/null +++ b/iamf/cli/obu_to_proto/BUILD @@ -0,0 +1,25 @@ +package(default_visibility = [ + "//iamf/cli:__subpackages__", +]) + +cc_library( + name = "parameter_block_metadata_generator", + srcs = ["parameter_block_metadata_generator.cc"], + hdrs = ["parameter_block_metadata_generator.h"], + deps = [ + "//iamf/cli/proto:parameter_block_cc_proto", + "//iamf/cli/proto:parameter_data_cc_proto", + "//iamf/common:macros", + "//iamf/common:obu_util", + "//iamf/obu:demixing_info_param_data", + "//iamf/obu:leb128", + "//iamf/obu:parameter_block", + "@com_google_absl//absl/base:no_destructor", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/log", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + ], +) diff --git a/iamf/cli/obu_to_proto/parameter_block_metadata_generator.cc b/iamf/cli/obu_to_proto/parameter_block_metadata_generator.cc new file mode 100644 index 00000000..298ed0c2 --- /dev/null +++ b/iamf/cli/obu_to_proto/parameter_block_metadata_generator.cc @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2024, 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 www.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 + * www.aomedia.org/license/patent. + */ +#include "iamf/cli/obu_to_proto/parameter_block_metadata_generator.h" + +#include +#include + +#include "absl/base/no_destructor.h" +#include "absl/container/flat_hash_map.h" +#include "absl/log/check.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "iamf/cli/proto/parameter_block.pb.h" +#include "iamf/cli/proto/parameter_data.pb.h" +#include "iamf/common/macros.h" +#include "iamf/common/obu_util.h" +#include "iamf/obu/demixing_info_param_data.h" +#include "iamf/obu/leb128.h" +#include "iamf/obu/parameter_block.h" + +namespace iamf_tools { + +namespace { + +using ParameterSubblockMetadata = iamf_tools_cli_proto::ParameterSubblock; + +// Returns a proto representation of the input `AnimationStepInt16`. +absl::StatusOr +AnimatedParameterDataInt16ToMetadata( + MixGainParameterData::AnimationType animation_type, + const AnimationStepInt16& step) { + RETURN_IF_NOT_OK(ValidateEqual( + animation_type, MixGainParameterData::AnimationType::kAnimateStep, + "Expected a step. Got animation_type= ")); + iamf_tools_cli_proto::MixGainParameterData result; + result.set_animation_type(iamf_tools_cli_proto::ANIMATE_STEP); + + result.mutable_param_data()->mutable_step()->set_start_point_value( + step.start_point_value); + return result; +} + +// Returns a proto representation of the input `AnimationLinearInt16`. +absl::StatusOr +AnimatedParameterDataInt16ToMetadata( + MixGainParameterData::AnimationType animation_type, + const AnimationLinearInt16& linear) { + RETURN_IF_NOT_OK(ValidateEqual( + animation_type, MixGainParameterData::AnimationType::kAnimateLinear, + "Expected a linear. Got animation_type= ")); + iamf_tools_cli_proto::MixGainParameterData result; + result.set_animation_type(iamf_tools_cli_proto::ANIMATE_LINEAR); + + result.mutable_param_data()->mutable_linear()->set_start_point_value( + linear.start_point_value); + result.mutable_param_data()->mutable_linear()->set_end_point_value( + linear.end_point_value); + return result; +} + +// Returns a proto representation of the input `AnimationBezierInt16`. +absl::StatusOr +AnimatedParameterDataInt16ToMetadata( + MixGainParameterData::AnimationType animation_type, + const AnimationBezierInt16& bezier) { + RETURN_IF_NOT_OK(ValidateEqual( + animation_type, MixGainParameterData::AnimationType::kAnimateBezier, + "Expected a bezier. Got animation_type= ")); + iamf_tools_cli_proto::MixGainParameterData result; + result.set_animation_type(iamf_tools_cli_proto::ANIMATE_BEZIER); + + result.mutable_param_data()->mutable_bezier()->set_start_point_value( + bezier.start_point_value); + result.mutable_param_data()->mutable_bezier()->set_end_point_value( + bezier.end_point_value); + result.mutable_param_data()->mutable_bezier()->set_control_point_value( + bezier.control_point_value); + result.mutable_param_data() + ->mutable_bezier() + ->set_control_point_relative_time(bezier.control_point_relative_time); + return result; +} + +// Copies the input `obu_dmixp_mode` to the output proto `dmixp_mode`. +absl::Status CopyDMixPMode(DemixingInfoParameterData::DMixPMode obu_dmixp_mode, + iamf_tools_cli_proto::DMixPMode& dmixp_mode) { + using enum DemixingInfoParameterData::DMixPMode; + using enum iamf_tools_cli_proto::DMixPMode; + static const absl::NoDestructor> + kObuDmixPModeToMetadataDMixPMode({ + {kDMixPMode1, DMIXP_MODE_1}, + {kDMixPMode2, DMIXP_MODE_2}, + {kDMixPMode3, DMIXP_MODE_3}, + {kDMixPModeReserved1, DMIXP_MODE_RESERVED_A}, + {kDMixPMode1_n, DMIXP_MODE_1_N}, + {kDMixPMode2_n, DMIXP_MODE_2_N}, + {kDMixPMode3_n, DMIXP_MODE_3_N}, + {kDMixPModeReserved2, DMIXP_MODE_RESERVED_B}, + }); + if (!LookupInMap(*kObuDmixPModeToMetadataDMixPMode, obu_dmixp_mode, + dmixp_mode) + .ok()) { + return absl::InvalidArgumentError( + absl::StrCat("Unknown dmixp_mode = ", obu_dmixp_mode)); + } + + return absl::OkStatus(); +} + +// Gets the proto representation of the input `mix_gain_parameter_data`. +absl::StatusOr ParamDataToMetadata( + const MixGainParameterData& mix_gain_parameter_data) { + ParameterSubblockMetadata result; + + auto mix_gain_parameter_data_metadata = std::visit( + [=](const auto& param_data) { + return AnimatedParameterDataInt16ToMetadata( + mix_gain_parameter_data.animation_type, param_data); + }, + mix_gain_parameter_data.param_data); + if (!mix_gain_parameter_data_metadata.ok()) { + return mix_gain_parameter_data_metadata.status(); + } + *result.mutable_mix_gain_parameter_data() = + *std::move(mix_gain_parameter_data_metadata); + + return result; +} + +// Gets the proto representation of the input `demixing_info_parameter_data`. +absl::StatusOr ParamDataToMetadata( + const DemixingInfoParameterData& demixing_info_parameter_data) { + ParameterSubblockMetadata result; + iamf_tools_cli_proto::DMixPMode dmixp_mode; + RETURN_IF_NOT_OK( + CopyDMixPMode(demixing_info_parameter_data.dmixp_mode, dmixp_mode)); + result.mutable_demixing_info_parameter_data()->set_dmixp_mode(dmixp_mode); + result.mutable_demixing_info_parameter_data()->set_reserved( + demixing_info_parameter_data.reserved); + return result; +} + +// Gets the proto representation of the input `recon_gain_parameter_data`. +absl::StatusOr ParamDataToMetadata( + const ReconGainInfoParameterData& recon_gain_parameter_data) { + ParameterSubblockMetadata result; + for (const auto& recon_gain_element : + recon_gain_parameter_data.recon_gain_elements) { + auto& recon_gains_for_layer = + *result.mutable_recon_gain_info_parameter_data() + ->add_recon_gains_for_layer() + ->mutable_recon_gain(); + DecodedUleb128 bitmask = 1; + for (int counter = 0; counter < recon_gain_element.recon_gain.size(); + ++counter) { + if (recon_gain_element.recon_gain_flag & bitmask) { + recon_gains_for_layer[counter] = recon_gain_element.recon_gain[counter]; + } + + bitmask <<= 1; + } + } + + return result; +} + +// Gets the proto representation of the input `extension_parameter_data`. +absl::StatusOr ParamDataToMetadata( + const ExtensionParameterData& extension_parameter_data) { + ParameterSubblockMetadata result; + + result.mutable_parameter_data_extension()->set_parameter_data_size( + extension_parameter_data.parameter_data_size); + result.mutable_parameter_data_extension() + ->mutable_parameter_data_bytes() + ->assign(extension_parameter_data.parameter_data_bytes.begin(), + extension_parameter_data.parameter_data_bytes.end()); + return result; +} + +} // namespace + +absl::StatusOr +ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + const ParameterSubblock& parameter_subblock) { + // Get the proto representation filled in with the specific type of + // param_data. + auto metadata_subblock = std::visit( + [](const auto& param_data) { return ParamDataToMetadata(param_data); }, + parameter_subblock.param_data); + if (!metadata_subblock.ok()) { + return metadata_subblock.status(); + } + metadata_subblock->set_subblock_duration( + parameter_subblock.subblock_duration); + + return metadata_subblock; +} + +} // namespace iamf_tools diff --git a/iamf/cli/obu_to_proto/parameter_block_metadata_generator.h b/iamf/cli/obu_to_proto/parameter_block_metadata_generator.h new file mode 100644 index 00000000..21ef99f6 --- /dev/null +++ b/iamf/cli/obu_to_proto/parameter_block_metadata_generator.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, 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 www.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 + * www.aomedia.org/license/patent. + */ + +#ifndef CLI_OBU_TO_PROTO_PARAMETER_BLOCK_METADATA_GENERATOR_H_ +#define CLI_OBU_TO_PROTO_PARAMETER_BLOCK_METADATA_GENERATOR_H_ + +#include "absl/status/statusor.h" +#include "iamf/cli/proto/parameter_block.pb.h" +#include "iamf/obu/parameter_block.h" + +namespace iamf_tools { + +/*!\brief Static functions to convert parameter blocks and related to protos. */ +class ParameterBlockMetadataGenerator { + public: + /*!\brief Generates a proto representation of a `ParameterSubblock` + * + * \param parameter_subblock The parameter subblock to convert to a proto. + * \return A proto representation of the parameter subblock or a specific + * error on failure. + */ + static absl::StatusOr + GenerateParameterSubblockMetadata( + const ParameterSubblock& parameter_subblock); +}; + +} // namespace iamf_tools + +#endif // CLI_OBU_TO_PROTO_PARAMETER_BLOCK_METADATA_GENERATOR_H_ diff --git a/iamf/cli/obu_to_proto/tests/BUILD b/iamf/cli/obu_to_proto/tests/BUILD new file mode 100644 index 00000000..981bd23f --- /dev/null +++ b/iamf/cli/obu_to_proto/tests/BUILD @@ -0,0 +1,16 @@ +cc_test( + name = "parameter_block_metadata_generator_test", + srcs = ["parameter_block_metadata_generator_test.cc"], + deps = [ + "//iamf/cli/obu_to_proto:parameter_block_metadata_generator", + "//iamf/cli/proto:parameter_block_cc_proto", + "//iamf/cli/proto:parameter_data_cc_proto", + "//iamf/cli/proto:user_metadata_cc_proto", + "//iamf/obu:demixing_info_param_data", + "//iamf/obu:leb128", + "//iamf/obu:parameter_block", + "@com_google_absl//absl/status:status_matchers", + "@com_google_absl//absl/strings:string_view", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/iamf/cli/obu_to_proto/tests/parameter_block_metadata_generator_test.cc b/iamf/cli/obu_to_proto/tests/parameter_block_metadata_generator_test.cc new file mode 100644 index 00000000..27068db6 --- /dev/null +++ b/iamf/cli/obu_to_proto/tests/parameter_block_metadata_generator_test.cc @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2024, 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 www.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 + * www.aomedia.org/license/patent. + */ +#include "iamf/cli/obu_to_proto/parameter_block_metadata_generator.h" + +#include +#include +#include + +#include "absl/status/status_matchers.h" +#include "absl/strings/string_view.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "iamf/cli/proto/parameter_block.pb.h" +#include "iamf/cli/proto/parameter_data.pb.h" +#include "iamf/cli/proto/user_metadata.pb.h" +#include "iamf/obu/demixing_info_param_data.h" +#include "iamf/obu/leb128.h" +#include "iamf/obu/parameter_block.h" + +namespace iamf_tools { +namespace { + +using ::absl_testing::IsOk; + +constexpr DecodedUleb128 kSubblockDuration = 99; +constexpr int16_t kStartPointValue = 100; +constexpr int16_t kEndPointValue = 200; +constexpr int16_t kControlPointValue = 300; +constexpr int16_t kControlPointRelativeTime = 68; + +using enum MixGainParameterData::AnimationType; +using enum iamf_tools_cli_proto::AnimationType; + +constexpr auto kStepMixGainParamData = + MixGainParameterData{.animation_type = kAnimateStep, + .param_data = AnimationStepInt16{kStartPointValue}}; + +TEST(GenerateParameterSubblockMetadata, + GetsSubblockMetadataForStepMixGainParameterSubblock) { + constexpr ParameterSubblock kStepMixGainSubblock{ + .subblock_duration = kSubblockDuration, + .param_data = kStepMixGainParamData}; + + const auto subblock_metadata = + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kStepMixGainSubblock); + ASSERT_THAT(subblock_metadata, IsOk()); + + ASSERT_TRUE(subblock_metadata->has_mix_gain_parameter_data()); + EXPECT_EQ(subblock_metadata->mix_gain_parameter_data().animation_type(), + ANIMATE_STEP); + ASSERT_TRUE( + subblock_metadata->mix_gain_parameter_data().param_data().has_step()); + EXPECT_EQ(subblock_metadata->mix_gain_parameter_data() + .param_data() + .step() + .start_point_value(), + kStartPointValue); +} + +TEST(GenerateParameterSubblockMetadata, + GetsSubblockMetadataForLinearMixGainParameterSubblock) { + constexpr ParameterSubblock kLinearMixGainSubblock{ + .subblock_duration = kSubblockDuration, + .param_data = + MixGainParameterData{.animation_type = kAnimateLinear, + .param_data = AnimationLinearInt16{ + kStartPointValue, kEndPointValue}}}; + + const auto subblock_metadata = + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kLinearMixGainSubblock); + ASSERT_THAT(subblock_metadata, IsOk()); + + ASSERT_TRUE(subblock_metadata->has_mix_gain_parameter_data()); + EXPECT_EQ(subblock_metadata->mix_gain_parameter_data().animation_type(), + ANIMATE_LINEAR); + ASSERT_TRUE( + subblock_metadata->mix_gain_parameter_data().param_data().has_linear()); + const auto& linear_param_data = + subblock_metadata->mix_gain_parameter_data().param_data().linear(); + EXPECT_EQ(linear_param_data.start_point_value(), kStartPointValue); + EXPECT_EQ(linear_param_data.end_point_value(), kEndPointValue); +} + +TEST(GenerateParameterSubblockMetadata, + GetsSubblockMetadataForBezierMixGainParameterSubblock) { + constexpr ParameterSubblock kBezierMixGainSubblock{ + .subblock_duration = kSubblockDuration, + .param_data = MixGainParameterData{ + .animation_type = kAnimateBezier, + .param_data = AnimationBezierInt16{kStartPointValue, kEndPointValue, + kControlPointValue, + kControlPointRelativeTime}}}; + + const auto subblock_metadata = + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kBezierMixGainSubblock); + ASSERT_THAT(subblock_metadata, IsOk()); + + ASSERT_TRUE(subblock_metadata->has_mix_gain_parameter_data()); + EXPECT_EQ(subblock_metadata->mix_gain_parameter_data().animation_type(), + ANIMATE_BEZIER); + ASSERT_TRUE( + subblock_metadata->mix_gain_parameter_data().param_data().has_bezier()); + const auto& bezier_param_data = + subblock_metadata->mix_gain_parameter_data().param_data().bezier(); + EXPECT_EQ(bezier_param_data.start_point_value(), kStartPointValue); + EXPECT_EQ(bezier_param_data.end_point_value(), kEndPointValue); + EXPECT_EQ(bezier_param_data.control_point_value(), kControlPointValue); + EXPECT_EQ(bezier_param_data.control_point_relative_time(), + kControlPointRelativeTime); +} + +TEST(GenerateParameterSubblockMetadata, + ReturnsErrorForInconsistentAnimationType) { + constexpr ParameterSubblock kInconsistentStepSubblock = { + .subblock_duration = kSubblockDuration, + .param_data = MixGainParameterData{.animation_type = kAnimateLinear, + .param_data = AnimationStepInt16{}}}; + constexpr ParameterSubblock kInconsistentLinearSubblock = { + .subblock_duration = kSubblockDuration, + .param_data = MixGainParameterData{.animation_type = kAnimateStep, + .param_data = AnimationLinearInt16{}}}; + constexpr ParameterSubblock kInconsistentBezierSubblock = { + .subblock_duration = kSubblockDuration, + .param_data = MixGainParameterData{.animation_type = kAnimateStep, + .param_data = AnimationBezierInt16{}}}; + + EXPECT_FALSE( + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kInconsistentStepSubblock) + .ok()); + EXPECT_FALSE( + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kInconsistentLinearSubblock) + .ok()); + EXPECT_FALSE( + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kInconsistentBezierSubblock) + .ok()); +} + +TEST(GenerateParameterSubblockMetadata, + GetsSubblockMetadataForDemixingSParameterSubblock) { + const uint8_t kReserved = 99; + const auto kExpectedDmixpMode = iamf_tools_cli_proto::DMIXP_MODE_1; + DemixingInfoParameterData demixing_info_param_data; + demixing_info_param_data.dmixp_mode = DemixingInfoParameterData::kDMixPMode1; + demixing_info_param_data.reserved = kReserved; + const ParameterSubblock subblock{.subblock_duration = kSubblockDuration, + .param_data = demixing_info_param_data}; + + const auto subblock_metadata = + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + subblock); + ASSERT_THAT(subblock_metadata, IsOk()); + + ASSERT_TRUE(subblock_metadata->has_demixing_info_parameter_data()); + EXPECT_EQ(subblock_metadata->demixing_info_parameter_data().dmixp_mode(), + kExpectedDmixpMode); + EXPECT_EQ(subblock_metadata->demixing_info_parameter_data().reserved(), + kReserved); +} + +TEST(GenerateParameterSubblockMetadata, GeneratesExtensionParameterSubblocks) { + const std::vector kParameterDataBytes = {0x01, 0x02, 0x03, 0x04, + 0x05}; + constexpr absl::string_view kExpectedParameterData = "\x01\x02\x03\x04\x05"; + const DecodedUleb128 kParameterDataSize = kParameterDataBytes.size(); + + const ParameterSubblock kExtensionSubblock{ + .subblock_duration = kSubblockDuration, + .param_data = + ExtensionParameterData{.parameter_data_size = kParameterDataSize, + .parameter_data_bytes = kParameterDataBytes}}; + + const auto subblock_metadata = + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kExtensionSubblock); + ASSERT_THAT(subblock_metadata, IsOk()); + + ASSERT_TRUE(subblock_metadata->has_parameter_data_extension()); + EXPECT_EQ(subblock_metadata->parameter_data_extension().parameter_data_size(), + kParameterDataSize); + EXPECT_THAT( + subblock_metadata->parameter_data_extension().parameter_data_bytes(), + kExpectedParameterData); +} + +TEST(GenerateParameterSubblockMetadata, GenerateReconGainParameterSubblocks) { + constexpr uint8_t kCenterReconGain = 100; + constexpr uint8_t kRightReconGain = 200; + constexpr size_t kExpectedNumLayers = 2; + constexpr uint32_t kExpectedCenterReconGainLayer = 0; + constexpr uint32_t kExpectedCenterReconGainIndex = 1; + constexpr uint32_t kExpectedRightReconGainLayer = 1; + constexpr uint32_t kExpectedRightReconGainIndex = 2; + const ParameterSubblock kReconGainSubblock{ + .subblock_duration = kSubblockDuration, + .param_data = ReconGainInfoParameterData{ + .recon_gain_elements = { + {.recon_gain_flag = ReconGainElement::kReconGainFlagC, + .recon_gain = {0, kCenterReconGain, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0}}, + {.recon_gain_flag = ReconGainElement::kReconGainFlagR, + .recon_gain = {0, 0, kRightReconGain, 0, 0, 0, 0, 0, 0, 0, 0, + 0}}, + }}}; + + auto subblock_metadata = + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kReconGainSubblock); + ASSERT_THAT(subblock_metadata, IsOk()); + ASSERT_TRUE(subblock_metadata->has_recon_gain_info_parameter_data()); + + EXPECT_EQ(subblock_metadata->recon_gain_info_parameter_data() + .recon_gains_for_layer_size(), + kExpectedNumLayers); + EXPECT_EQ(subblock_metadata->recon_gain_info_parameter_data() + .recon_gains_for_layer(kExpectedCenterReconGainLayer) + .recon_gain() + .at(kExpectedCenterReconGainIndex), + kCenterReconGain); + EXPECT_EQ(subblock_metadata->recon_gain_info_parameter_data() + .recon_gains_for_layer(kExpectedRightReconGainLayer) + .recon_gain() + .at(kExpectedRightReconGainIndex), + kRightReconGain); +} + +TEST(GenerateParameterSubblockMetadata, SetsDuration) { + constexpr ParameterSubblock kSubblock{.subblock_duration = kSubblockDuration, + .param_data = kStepMixGainParamData}; + + const auto subblock_metadata = + ParameterBlockMetadataGenerator::GenerateParameterSubblockMetadata( + kSubblock); + ASSERT_THAT(subblock_metadata, IsOk()); + + EXPECT_EQ(subblock_metadata->subblock_duration(), kSubblockDuration); +} + +} // namespace +} // namespace iamf_tools