From ac9671afbc614fd4a3398858c0471a39a8dcaab7 Mon Sep 17 00:00:00 2001 From: Jiawei Chen Date: Tue, 2 May 2023 08:15:35 +0000 Subject: [PATCH] Audio: Add AC3/EAC3 support in EME on Windows platform For AC3/EAC3 stream protected under DRM, we need to parse dac3 and dec3 box infomation in encryption box. And if CDM support AC3/EAC3 codec, we also need to convert AC3/EAC3 AudioCodec to EME_CODEC. This change is backported from m120+. (cherry picked from commit 866e79517d2d4fb1aeccc7162c6c90833ed96bb2) Bug: 388666932 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4489383 --- .../cdm/renderer/key_system_support_update.cc | 8 +++ media/formats/BUILD.gn | 7 +++ media/formats/mp4/ac3.cc | 29 +++++++--- media/formats/mp4/ac3_unittest.cc | 53 +++++++++++++++++++ media/formats/mp4/box_definitions.cc | 6 ++- media/formats/mp4/eac3.cc | 38 ++++++++++--- media/formats/mp4/eac3_unittest.cc | 53 +++++++++++++++++++ 7 files changed, 178 insertions(+), 16 deletions(-) create mode 100644 media/formats/mp4/ac3_unittest.cc create mode 100644 media/formats/mp4/eac3_unittest.cc diff --git a/components/cdm/renderer/key_system_support_update.cc b/components/cdm/renderer/key_system_support_update.cc index 52dd9fca98df..206c533705e7 100644 --- a/components/cdm/renderer/key_system_support_update.cc +++ b/components/cdm/renderer/key_system_support_update.cc @@ -189,6 +189,14 @@ SupportedCodecs GetSupportedCodecs(const media::CdmCapability& capability, supported_codecs |= media::EME_CODEC_DTSXP2; break; #endif // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO) +#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO) + case media::AudioCodec::kAC3: + supported_codecs |= media::EME_CODEC_AC3; + break; + case media::AudioCodec::kEAC3: + supported_codecs |= media::EME_CODEC_EAC3; + break; +#endif // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO) #endif // BUILDFLAG(USE_PROPRIETARY_CODECS) default: DVLOG(1) << "Unexpected supported codec: " << GetCodecName(codec); diff --git a/media/formats/BUILD.gn b/media/formats/BUILD.gn index c42bd14703cd..0dac1c24b37a 100644 --- a/media/formats/BUILD.gn +++ b/media/formats/BUILD.gn @@ -318,6 +318,13 @@ source_set("unit_tests") { ] } + if (enable_platform_ac3_eac3_audio) { + sources += [ + "mp4/ac3_unittest.cc", + "mp4/eac3_unittest.cc", + ] + } + if (enable_mse_mpeg2ts_stream_parser) { sources += [ "mp2t/es_adapter_video_unittest.cc", diff --git a/media/formats/mp4/ac3.cc b/media/formats/mp4/ac3.cc index b08e8dcb5ffd..ba6b4e6fa24c 100644 --- a/media/formats/mp4/ac3.cc +++ b/media/formats/mp4/ac3.cc @@ -30,24 +30,41 @@ bool AC3::Parse(const std::vector& data, MediaLog* media_log) { return false; } + // For AC3SpecificBox, Please refer to ETSI TS 102 366 V1.4.1 + // https://www.etsi.org/deliver/etsi_ts/102300_102399/102366/01.03.01_60/ts_102366v010301p.pdf + // F.4 AC3SpecificBox + // fscod 2 bits + // bsid 5 bits + // bsmod 3 bits + // acmod 3 bits + // lfeon 1 bits + // bit_rate_code 5 bits + // reserved 5 bits + + if (data.size() * 8 < (2 + 5 + 3 + 3 + 1 + 5 + 5)) { + return false; + } + // Parse dac3 box using reader. BitReader reader(&data[0], data.size()); - // Please refer to ETSI TS 102 366 V1.4.1 - // https://www.etsi.org/deliver/etsi_ts/102300_102399/102366/01.03.01_60/ts_102366v010301p.pdf - // F.4 AC3SpecificBox - // fscod 2 bits - // bsid 5 bits - // bsmod 3 bits + // skip fscod, bsid, bsmod RCHECK(reader.SkipBits(2 + 5 + 3)); int acmod; RCHECK(reader.ReadBits(3, &acmod)); + if (acmod >= static_cast(sizeof(kGlobalChannelArray))) { + return false; + } + int lfeon; RCHECK(reader.ReadBits(1, &lfeon)); channel_count_ = kGlobalChannelArray[acmod] + lfeon; RCHECK(channel_count_ >= 1 && channel_count_ <= limits::kMaxChannels); + + // skip bit_rate_code, reserved + RCHECK(reader.SkipBits(5 + 5)); return true; } diff --git a/media/formats/mp4/ac3_unittest.cc b/media/formats/mp4/ac3_unittest.cc new file mode 100644 index 000000000000..6e791dcaaef4 --- /dev/null +++ b/media/formats/mp4/ac3_unittest.cc @@ -0,0 +1,53 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "media/base/mock_media_log.h" +#include "media/formats/mp4/ac3.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::AllOf; +using ::testing::HasSubstr; +using ::testing::InSequence; +using ::testing::StrictMock; + +namespace media { + +namespace mp4 { + +class AC3Test : public testing::Test { + public: + AC3Test() = default; + + bool Parse(const std::vector& data) { + return ac3_.Parse(data, &media_log_); + } + + StrictMock media_log_; + AC3 ac3_; +}; + +TEST_F(AC3Test, NoInputTest) { + std::vector data; + EXPECT_FALSE(Parse(data)); +} + +TEST_F(AC3Test, ShortInvalidInputTest) { + std::vector data({0x50, 0x11}); + + EXPECT_FALSE(Parse(data)); +} + +TEST_F(AC3Test, NormalInputTest) { + std::vector data({0x50, 0x11, 0x40}); + + EXPECT_TRUE(Parse(data)); + EXPECT_EQ(ac3_.GetChannelCount(), 2u); +} + +} // namespace mp4 + +} // namespace media diff --git a/media/formats/mp4/box_definitions.cc b/media/formats/mp4/box_definitions.cc index 2cded6670f5e..01db74ed694d 100644 --- a/media/formats/mp4/box_definitions.cc +++ b/media/formats/mp4/box_definitions.cc @@ -1624,11 +1624,13 @@ bool AudioSampleEntry::Parse(BoxReader* reader) { #endif // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO) #if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO) - if (format == FOURCC_AC3) { + if (format == FOURCC_AC3 || + (format == FOURCC_ENCA && sinf.format.format == FOURCC_AC3)) { RCHECK_MEDIA_LOGGED(reader->ReadChild(&ac3), reader->media_log(), "Failure parsing AC3SpecificBox (dac3)"); } - if (format == FOURCC_EAC3) { + if (format == FOURCC_EAC3 || + (format == FOURCC_ENCA && sinf.format.format == FOURCC_EAC3)) { RCHECK_MEDIA_LOGGED(reader->ReadChild(&eac3), reader->media_log(), "Failure parsing EC3SpecificBox (dec3)"); } diff --git a/media/formats/mp4/eac3.cc b/media/formats/mp4/eac3.cc index 4bdd58aad219..95dfc41cb0f3 100644 --- a/media/formats/mp4/eac3.cc +++ b/media/formats/mp4/eac3.cc @@ -30,6 +30,31 @@ bool EAC3::Parse(const std::vector& data, MediaLog* media_log) { return false; } + // For EC3SpecificBox, please refer to ETSI TS 102 366 V1.4.1 + // https://www.etsi.org/deliver/etsi_ts/102300_102399/102366/01.03.01_60/ts_102366v010301p.pdf + // F.6 EC3SpecificBox + // data_rate 13 bits + // num_ind_sub 3 bits + // { + // fscod 2 bits + // bsid 5 bits + // reserved 1 bits + // asvc 1 bits + // bsmod 3 bits + // acmod 3 bits + // lfeon 1 bits + // reserved 3 bits + // num_dep_sub 4 bits + // if num_dep_sub > 0 chan_loc 9 bits + // else reserved 1 bits + // } + // reserved variable bits + + // At least one independent substreams exist without ndependent substream + if (data.size() * 8 < (13 + 3 + (2 + 5 + 1 + 1 + 3 + 3 + 1 + 3 + 4 + 1))) { + return false; + } + // Parse dec3 box using reader. BitReader reader(&data[0], data.size()); @@ -41,18 +66,15 @@ bool EAC3::Parse(const std::vector& data, MediaLog* media_log) { int max_channel_count = 0; for (int i = 0; i < num_ind_sub + 1; i++) { - // Please refer to ETSI TS 102 366 V1.4.1 - // https://www.etsi.org/deliver/etsi_ts/102300_102399/102366/01.03.01_60/ts_102366v010301p.pdf - // F.4 AC3SpecificBox - // fscod 2 bits - // bsid 5 bits - // reserved 1 bits - // asvc 1 bits - // bsmod 3 bits + // skip fscod, bsid, reserved, asvc, bsmod RCHECK(reader.SkipBits(2 + 5 + 1 + 1 + 3)); int acmod; RCHECK(reader.ReadBits(3, &acmod)); + if (acmod >= static_cast(sizeof(kGlobalChannelArray))) { + return false; + } + int lfeon; RCHECK(reader.ReadBits(1, &lfeon)); diff --git a/media/formats/mp4/eac3_unittest.cc b/media/formats/mp4/eac3_unittest.cc new file mode 100644 index 000000000000..afdc1b32b673 --- /dev/null +++ b/media/formats/mp4/eac3_unittest.cc @@ -0,0 +1,53 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "media/base/mock_media_log.h" +#include "media/formats/mp4/eac3.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::AllOf; +using ::testing::HasSubstr; +using ::testing::InSequence; +using ::testing::StrictMock; + +namespace media { + +namespace mp4 { + +class EAC3Test : public testing::Test { + public: + EAC3Test() = default; + + bool Parse(const std::vector& data) { + return eac3_.Parse(data, &media_log_); + } + + StrictMock media_log_; + EAC3 eac3_; +}; + +TEST_F(EAC3Test, NoInputTest) { + std::vector data; + EXPECT_FALSE(Parse(data)); +} + +TEST_F(EAC3Test, ShortInvalidInputTest) { + std::vector data({0x06, 0xC8}); + + EXPECT_FALSE(Parse(data)); +} + +TEST_F(EAC3Test, NormalInputTest) { + std::vector data({0x06, 0xC8, 0x60, 0x04, 0x00}); + + EXPECT_TRUE(Parse(data)); + EXPECT_EQ(eac3_.GetChannelCount(), 2u); +} + +} // namespace mp4 + +} // namespace media