From b77fc64cef1ddf766990c6cf5a1f409ea10b71ca Mon Sep 17 00:00:00 2001 From: jwcullen Date: Fri, 19 Jul 2024 11:20:35 -0400 Subject: [PATCH] `ChannelAudioLayerConfig`: Refactor existing read/write functionality into `Read` and `Write` functions. - There is nothing being validated at the single-layer level - so these functions do not have `Validate` in the name. - Increase coverage here which was otherwise very light - add some tests adjacent to planned features regarding `expanded_loudspeaker_layout`. PiperOrigin-RevId: 654013559 --- iamf/obu/audio_element.cc | 74 ++++---- iamf/obu/audio_element.h | 21 ++- iamf/obu/tests/BUILD | 1 + iamf/obu/tests/audio_element_test.cc | 265 +++++++++++++++++++++++++-- 4 files changed, 309 insertions(+), 52 deletions(-) diff --git a/iamf/obu/audio_element.cc b/iamf/obu/audio_element.cc index 7178826d..11c9d670 100644 --- a/iamf/obu/audio_element.cc +++ b/iamf/obu/audio_element.cc @@ -194,23 +194,7 @@ absl::Status ValidateAndWriteScalableChannelLayout( // Loop to write the `channel_audio_layer_configs` array. for (const auto& layer_config : layout.channel_audio_layer_configs) { - RETURN_IF_NOT_OK( - wb.WriteUnsignedLiteral(layer_config.loudspeaker_layout, 4)); - RETURN_IF_NOT_OK( - wb.WriteUnsignedLiteral(layer_config.output_gain_is_present_flag, 1)); - RETURN_IF_NOT_OK( - wb.WriteUnsignedLiteral(layer_config.recon_gain_is_present_flag, 1)); - RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(layer_config.reserved_a, 2)); - RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(layer_config.substream_count, 8)); - RETURN_IF_NOT_OK( - wb.WriteUnsignedLiteral(layer_config.coupled_substream_count, 8)); - - if (layer_config.output_gain_is_present_flag == 1) { - RETURN_IF_NOT_OK( - wb.WriteUnsignedLiteral(layer_config.output_gain_flag, 6)); - RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(layer_config.reserved_b, 2)); - RETURN_IF_NOT_OK(wb.WriteSigned16(layer_config.output_gain)); - } + RETURN_IF_NOT_OK(layer_config.Write(wb)); } return absl::OkStatus(); @@ -226,26 +210,7 @@ absl::Status ReadAndValidateScalableChannelLayout( for (int i = 0; i < layout.num_layers; ++i) { ChannelAudioLayerConfig layer_config; - uint8_t loudspeaker_layout; - RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(4, loudspeaker_layout)); - layer_config.loudspeaker_layout = - static_cast( - loudspeaker_layout); - RETURN_IF_NOT_OK( - rb.ReadUnsignedLiteral(1, layer_config.output_gain_is_present_flag)); - RETURN_IF_NOT_OK( - rb.ReadUnsignedLiteral(1, layer_config.recon_gain_is_present_flag)); - RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, layer_config.reserved_a)); - RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, layer_config.substream_count)); - RETURN_IF_NOT_OK( - rb.ReadUnsignedLiteral(8, layer_config.coupled_substream_count)); - - if (layer_config.output_gain_is_present_flag == 1) { - RETURN_IF_NOT_OK( - rb.ReadUnsignedLiteral(6, layer_config.output_gain_flag)); - RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, layer_config.reserved_b)); - RETURN_IF_NOT_OK(rb.ReadSigned16(layer_config.output_gain)); - } + RETURN_IF_NOT_OK(layer_config.Read(rb)); layout.channel_audio_layer_configs.push_back(layer_config); } @@ -414,6 +379,41 @@ absl::Status AudioElementParam::ReadAndValidate(uint32_t audio_element_id, } } +absl::Status ChannelAudioLayerConfig::Write(WriteBitBuffer& wb) const { + RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(loudspeaker_layout, 4)); + RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(output_gain_is_present_flag, 1)); + RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(recon_gain_is_present_flag, 1)); + RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(reserved_a, 2)); + RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(substream_count, 8)); + RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(coupled_substream_count, 8)); + + if (output_gain_is_present_flag == 1) { + RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(output_gain_flag, 6)); + RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(reserved_b, 2)); + RETURN_IF_NOT_OK(wb.WriteSigned16(output_gain)); + } + return absl::OkStatus(); +} + +absl::Status ChannelAudioLayerConfig::Read(ReadBitBuffer& rb) { + uint8_t loudspeaker_layout_uint8; + RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(4, loudspeaker_layout_uint8)); + loudspeaker_layout = static_cast( + loudspeaker_layout_uint8); + RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(1, output_gain_is_present_flag)); + RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(1, recon_gain_is_present_flag)); + RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, reserved_a)); + RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, substream_count)); + RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(8, coupled_substream_count)); + + if (output_gain_is_present_flag == 1) { + RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(6, output_gain_flag)); + RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(2, reserved_b)); + RETURN_IF_NOT_OK(rb.ReadSigned16(output_gain)); + } + return absl::OkStatus(); +} + absl::Status ScalableChannelLayoutConfig::Validate( DecodedUleb128 num_substreams_in_audio_element) const { if (num_layers == 0 || num_layers > 6) { diff --git a/iamf/obu/audio_element.h b/iamf/obu/audio_element.h index ab92dd3c..1690d0fa 100644 --- a/iamf/obu/audio_element.h +++ b/iamf/obu/audio_element.h @@ -54,7 +54,11 @@ struct AudioElementParam { std::unique_ptr param_definition; }; -/*!\brief An element of the `ScalableChannelLayoutConfig` vector. */ +/*!\brief An element of the `ScalableChannelLayoutConfig` vector. + * + * Implements the `ChannelAudioLayerConfig` as defined by section 3.6.2 of + * https://aomediacodec.github.io/iamf/v1.0.0-errata.html. + */ struct ChannelAudioLayerConfig { /*!\brief A 4-bit enum for the type of layout. */ enum LoudspeakerLayout : uint8_t { @@ -75,6 +79,21 @@ struct ChannelAudioLayerConfig { friend bool operator==(const ChannelAudioLayerConfig& lhs, const ChannelAudioLayerConfig& rhs) = default; + /*!\brief Writes the `ChannelAudioLayerConfig` payload to the buffer. + * + * \param wb Buffer to write to. + * \return `absl::OkStatus()` if the payload is valid. A specific status on + * failure. + */ + absl::Status Write(WriteBitBuffer& wb) const; + + /*!\brief Reads the `ChannelAudioLayerConfig` payload from the buffer. + * + * \param rb Buffer to read from. + * \return `absl::OkStatus()` if successful. A specific status on failure. + */ + absl::Status Read(ReadBitBuffer& rb); + LoudspeakerLayout loudspeaker_layout; // 4 bits. uint8_t output_gain_is_present_flag; // 1 bit. uint8_t recon_gain_is_present_flag; // 1 bit. diff --git a/iamf/obu/tests/BUILD b/iamf/obu/tests/BUILD index 529e86e3..077470a2 100644 --- a/iamf/obu/tests/BUILD +++ b/iamf/obu/tests/BUILD @@ -37,6 +37,7 @@ cc_test( "//iamf/cli:leb_generator", "//iamf/common:read_bit_buffer", "//iamf/common:write_bit_buffer", + "//iamf/common/tests:test_utils", "//iamf/obu:audio_element", "//iamf/obu:demixing_info_param_data", "//iamf/obu:leb128", diff --git a/iamf/obu/tests/audio_element_test.cc b/iamf/obu/tests/audio_element_test.cc index ab18ec1f..76c13994 100644 --- a/iamf/obu/tests/audio_element_test.cc +++ b/iamf/obu/tests/audio_element_test.cc @@ -25,6 +25,7 @@ #include "gtest/gtest.h" #include "iamf/cli/leb_generator.h" #include "iamf/common/read_bit_buffer.h" +#include "iamf/common/tests/test_utils.h" #include "iamf/common/write_bit_buffer.h" #include "iamf/obu/demixing_info_param_data.h" #include "iamf/obu/leb128.h" @@ -478,6 +479,256 @@ TEST_F(AudioElementScalableChannelTest, ParamDefinitionExtensionNonZero) { InitAndTestWrite(); } +constexpr int kLoudspeakerLayoutBitShift = 4; +constexpr int kOutputGainIsPresentBitShift = 3; +constexpr int kReconGainIsPresentBitShift = 2; +constexpr int kOutputGainIsPresentFlagBitShift = 2; + +constexpr uint8_t kBinauralSubstreamCount = 1; +constexpr uint8_t kBinauralCoupledSubstreamCount = 1; +const ChannelAudioLayerConfig kChannelAudioLayerConfigBinaural = { + .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutBinaural, + .output_gain_is_present_flag = false, + .recon_gain_is_present_flag = false, + .substream_count = kBinauralSubstreamCount, + .coupled_substream_count = kBinauralCoupledSubstreamCount}; + +constexpr uint8_t kOneLayerStereoSubstreamCount = 1; +constexpr uint8_t kOneLayerStereoCoupledSubstreamCount = 1; +const ChannelAudioLayerConfig kChannelAudioLayerConfigStereo = { + .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutStereo, + .output_gain_is_present_flag = false, + .recon_gain_is_present_flag = false, + .substream_count = kOneLayerStereoSubstreamCount, + .coupled_substream_count = kOneLayerStereoCoupledSubstreamCount}; + +TEST(ChannelAudioLayerConfig, WritesBinauralLayer) { + WriteBitBuffer wb(1024); + EXPECT_THAT(kChannelAudioLayerConfigBinaural.Write(wb), IsOk()); + + ValidateWriteResults( + wb, std::vector{ChannelAudioLayerConfig::kLayoutBinaural + << kLoudspeakerLayoutBitShift, + kBinauralSubstreamCount, + kBinauralCoupledSubstreamCount}); +} + +TEST(ChannelAudioLayerConfig, WritesStereoLayer) { + const std::vector kExpectedData = { + ChannelAudioLayerConfig::kLayoutStereo << kLoudspeakerLayoutBitShift, + kOneLayerStereoSubstreamCount, kOneLayerStereoCoupledSubstreamCount}; + WriteBitBuffer wb(1024); + EXPECT_THAT(kChannelAudioLayerConfigStereo.Write(wb), IsOk()); + + ValidateWriteResults(wb, kExpectedData); +} + +TEST(ChannelAudioLayerConfig, WritesReserved10Layer) { + constexpr uint8_t kExpectedSubstreamCount = 1; + constexpr uint8_t kExpectedCoupledSubstreamCount = 1; + const ChannelAudioLayerConfig kChannelAudioLayerConfigReserved10 = { + .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutReservedBegin, + .output_gain_is_present_flag = false, + .recon_gain_is_present_flag = false, + .substream_count = kExpectedSubstreamCount, + .coupled_substream_count = kExpectedCoupledSubstreamCount}; + + const std::vector kExpectedData = { + ChannelAudioLayerConfig::kLayoutReservedBegin + << kLoudspeakerLayoutBitShift, + kExpectedSubstreamCount, kExpectedCoupledSubstreamCount}; + WriteBitBuffer wb(1024); + EXPECT_THAT(kChannelAudioLayerConfigReserved10.Write(wb), IsOk()); + + ValidateWriteResults(wb, kExpectedData); +} + +TEST(ChannelAudioLayerConfig, WritesReserved15Layer) { + constexpr uint8_t kExpectedSubstreamCount = 1; + constexpr uint8_t kExpectedCoupledSubstreamCount = 1; + const ChannelAudioLayerConfig kChannelAudioLayerConfigReserved15 = { + .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutReservedEnd, + .output_gain_is_present_flag = false, + .recon_gain_is_present_flag = false, + .substream_count = kExpectedSubstreamCount, + .coupled_substream_count = kExpectedCoupledSubstreamCount}; + + const std::vector kExpectedData = { + ChannelAudioLayerConfig::kLayoutReservedEnd << kLoudspeakerLayoutBitShift, + kExpectedSubstreamCount, kExpectedCoupledSubstreamCount}; + WriteBitBuffer wb(1024); + EXPECT_THAT(kChannelAudioLayerConfigReserved15.Write(wb), IsOk()); + + ValidateWriteResults(wb, kExpectedData); +} + +TEST(ChannelAudioLayerConfig, WritesOutputGainIsPresentFields) { + constexpr bool kOutputGainIsPresent = true; + constexpr uint8_t kOutputGainFlag = 0b100000; + constexpr uint8_t kReservedB = 0b01; + constexpr int16_t kOutputGain = 5; + const ChannelAudioLayerConfig kSecondLayerStereo = { + .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutStereo, + .output_gain_is_present_flag = kOutputGainIsPresent, + .recon_gain_is_present_flag = false, + .substream_count = 1, + .coupled_substream_count = 0, + .output_gain_flag = kOutputGainFlag, + .reserved_b = kReservedB, + .output_gain = kOutputGain, + }; + + const std::vector kExpectedData = { + ChannelAudioLayerConfig::kLayoutStereo << kLoudspeakerLayoutBitShift | + kOutputGainIsPresent << kOutputGainIsPresentBitShift, + 1, + 0, + kOutputGainFlag << kOutputGainIsPresentFlagBitShift | kReservedB, + 0, + 5}; + WriteBitBuffer wb(1024); + EXPECT_THAT(kSecondLayerStereo.Write(wb), IsOk()); + + ValidateWriteResults(wb, kExpectedData); +} + +TEST(ChannelAudioLayerConfig, WritesReconGainIsPresentFlag) { + constexpr bool kReconGainIsPresent = true; + const ChannelAudioLayerConfig kSecondLayerStereo = { + .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutStereo, + .output_gain_is_present_flag = false, + .recon_gain_is_present_flag = kReconGainIsPresent, + .substream_count = 1, + .coupled_substream_count = 0, + }; + + const std::vector kExpectedData = { + ChannelAudioLayerConfig::kLayoutStereo << kLoudspeakerLayoutBitShift | + kReconGainIsPresent << kReconGainIsPresentBitShift, + 1, 0}; + WriteBitBuffer wb(1024); + EXPECT_THAT(kSecondLayerStereo.Write(wb), IsOk()); + + ValidateWriteResults(wb, kExpectedData); +} + +TEST(ChannelAudioLayerConfig, WritesFirstReservedField) { + const uint8_t kFirstReservedField = 3; + const ChannelAudioLayerConfig kSecondLayerStereo = { + .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutStereo, + .output_gain_is_present_flag = false, + .recon_gain_is_present_flag = false, + .reserved_a = kFirstReservedField, + .substream_count = 1, + .coupled_substream_count = 0, + }; + + const std::vector kExpectedData = { + ChannelAudioLayerConfig::kLayoutStereo << kLoudspeakerLayoutBitShift | + kFirstReservedField, + 1, 0}; + WriteBitBuffer wb(1024); + EXPECT_THAT(kSecondLayerStereo.Write(wb), IsOk()); + + ValidateWriteResults(wb, kExpectedData); +} + +TEST(ChannelAudioLayerConfig, ReadsBinauralLayer) { + std::vector data = { + ChannelAudioLayerConfig::kLayoutBinaural << kLoudspeakerLayoutBitShift, 1, + 1}; + ReadBitBuffer buffer(1024, &data); + ChannelAudioLayerConfig config; + + EXPECT_THAT(config.Read(buffer), IsOk()); + + EXPECT_EQ(config.loudspeaker_layout, + ChannelAudioLayerConfig::kLayoutBinaural); + EXPECT_EQ(config.output_gain_is_present_flag, false); + EXPECT_EQ(config.recon_gain_is_present_flag, false); + EXPECT_EQ(config.reserved_a, 0); + EXPECT_EQ(config.substream_count, kBinauralSubstreamCount); + EXPECT_EQ(config.coupled_substream_count, kBinauralCoupledSubstreamCount); +} + +TEST(ChannelAudioLayerConfig, ReadsReserved10Layer) { + std::vector data = {ChannelAudioLayerConfig::kLayoutReservedBegin + << kLoudspeakerLayoutBitShift, + 1, 1}; + ReadBitBuffer buffer(1024, &data); + ChannelAudioLayerConfig config; + + EXPECT_THAT(config.Read(buffer), IsOk()); + + EXPECT_EQ(config.loudspeaker_layout, + ChannelAudioLayerConfig::kLayoutReservedBegin); +} + +TEST(ChannelAudioLayerConfig, ReadsReserved15Layer) { + std::vector data = { + ChannelAudioLayerConfig::kLayoutReservedEnd << kLoudspeakerLayoutBitShift, + 1, 1}; + ReadBitBuffer buffer(1024, &data); + ChannelAudioLayerConfig config; + + EXPECT_THAT(config.Read(buffer), IsOk()); + + EXPECT_EQ(config.loudspeaker_layout, + ChannelAudioLayerConfig::kLayoutReservedEnd); +} + +TEST(ChannelAudioLayerConfig, ReadsOutputGainIsPresentRelatedFields) { + constexpr bool kOutputGainIsPresent = true; + constexpr uint8_t kOutputGainFlag = 0b100000; + constexpr uint8_t kReservedB = 0b01; + constexpr int16_t kOutputGain = 5; + std::vector data = { + ChannelAudioLayerConfig::kLayoutStereo << kLoudspeakerLayoutBitShift | + kOutputGainIsPresent << kOutputGainIsPresentBitShift, + 1, + 0, + kOutputGainFlag << kOutputGainIsPresentFlagBitShift | kReservedB, + 0, + 5}; + ReadBitBuffer buffer(1024, &data); + ChannelAudioLayerConfig config; + + EXPECT_THAT(config.Read(buffer), IsOk()); + + EXPECT_EQ(config.output_gain_is_present_flag, kOutputGainIsPresent); + EXPECT_EQ(config.output_gain_flag, kOutputGainFlag); + EXPECT_EQ(config.reserved_b, kReservedB); + EXPECT_EQ(config.output_gain, kOutputGain); +} + +TEST(ChannelAudioLayerConfig, ReadsReconGainIsPresent) { + constexpr bool kReconGainIsPresent = true; + std::vector data = { + ChannelAudioLayerConfig::kLayoutStereo << kLoudspeakerLayoutBitShift | + kReconGainIsPresent << kReconGainIsPresentBitShift, + 1, 0}; + ReadBitBuffer buffer(1024, &data); + ChannelAudioLayerConfig config; + + EXPECT_THAT(config.Read(buffer), IsOk()); + + EXPECT_EQ(config.recon_gain_is_present_flag, kReconGainIsPresent); +} + +TEST(ChannelAudioLayerConfig, ReadsFirstReservedField) { + constexpr uint8_t kReservedField = 3; + std::vector data = { + ChannelAudioLayerConfig::kLayoutStereo << kLoudspeakerLayoutBitShift | + kReservedField, + 1, 0}; + ReadBitBuffer buffer(1024, &data); + ChannelAudioLayerConfig config; + + EXPECT_THAT(config.Read(buffer), IsOk()); + + EXPECT_EQ(config.reserved_a, kReservedField); +} + const ScalableChannelLayoutConfig kTwoLayerStereoConfig = { .num_layers = 2, .channel_audio_layer_configs = { @@ -520,20 +771,6 @@ TEST(ScalableChannelLayoutConfigValidate, TooManyLayers) { EXPECT_FALSE(kConfigWithZeroLayer.Validate(0).ok()); } -const ChannelAudioLayerConfig kChannelAudioLayerConfigBinaural = { - .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutBinaural, - .output_gain_is_present_flag = false, - .recon_gain_is_present_flag = false, - .substream_count = 1, - .coupled_substream_count = 1}; - -const ChannelAudioLayerConfig kChannelAudioLayerConfigStereo = { - .loudspeaker_layout = ChannelAudioLayerConfig::kLayoutStereo, - .output_gain_is_present_flag = false, - .recon_gain_is_present_flag = false, - .substream_count = 1, - .coupled_substream_count = 1}; - TEST(ScalableChannelLayoutConfigValidate, IsOkWithOneLayerBinaural) { const ScalableChannelLayoutConfig kBinauralConfig = { .num_layers = 1,