From 2ba23e1075cce54f79811352d4a63e849d48285e Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Fri, 5 Jan 2018 17:18:23 -0800 Subject: [PATCH] Fix incorrect CHANNELS reporting for AC3/EC3 Fixes #313 Change-Id: I0b8d8081cda8aaa4db2ca4916bfc6721bfa3bd22 --- packager/media/codecs/ac3_audio_util.cc | 52 +++++++++++++++++++ packager/media/codecs/ac3_audio_util.h | 27 ++++++++++ .../media/codecs/ac3_audio_util_unittest.cc | 30 +++++++++++ packager/media/codecs/codecs.gyp | 3 ++ packager/media/codecs/ec3_audio_util.cc | 36 +++++++++++-- packager/media/codecs/ec3_audio_util.h | 6 +++ .../media/codecs/ec3_audio_util_unittest.cc | 31 ++++++----- .../media/formats/mp4/mp4_media_parser.cc | 6 ++- packager/media/formats/mp4/mp4_muxer.cc | 11 +++- 9 files changed, 179 insertions(+), 23 deletions(-) create mode 100644 packager/media/codecs/ac3_audio_util.cc create mode 100644 packager/media/codecs/ac3_audio_util.h create mode 100644 packager/media/codecs/ac3_audio_util_unittest.cc diff --git a/packager/media/codecs/ac3_audio_util.cc b/packager/media/codecs/ac3_audio_util.cc new file mode 100644 index 0000000000..7a1bff7d9e --- /dev/null +++ b/packager/media/codecs/ac3_audio_util.cc @@ -0,0 +1,52 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/codecs/ac3_audio_util.h" + +#include "packager/base/strings/string_number_conversions.h" +#include "packager/media/base/bit_reader.h" +#include "packager/media/base/rcheck.h" + +namespace shaka { +namespace media { + +namespace { + +// ASTC Standard A/52:2012 Table 5.8 Audio Coding Mode. +const uint8_t kAc3NumChannelsTable[] = {2, 1, 2, 3, 3, 4, 4, 5}; + +bool ExtractAc3Data(const std::vector& ac3_data, + uint8_t* audio_coding_mode, + bool* lfe_channel_on) { + BitReader bit_reader(ac3_data.data(), ac3_data.size()); + + // fscod: 2 bits + // bsid: 5 bits + // bsmod: 3 bits + // acmod: 3 bits + // lfeon: 1 bit + // bit_rate_code: 5 bits + RCHECK(bit_reader.SkipBits(10)); + RCHECK(bit_reader.ReadBits(3, audio_coding_mode)); + RCHECK(bit_reader.ReadBits(1, lfe_channel_on)); + return true; +} + +} // namespace + +size_t GetAc3NumChannels(const std::vector& ac3_data) { + uint8_t audio_coding_mode; + bool lfe_channel_on; + if (!ExtractAc3Data(ac3_data, &audio_coding_mode, &lfe_channel_on)) { + LOG(WARNING) << "Seeing invalid AC3 data: " + << base::HexEncode(ac3_data.data(), ac3_data.size()); + return 0; + } + return kAc3NumChannelsTable[audio_coding_mode] + (lfe_channel_on ? 1 : 0); +} + +} // namespace media +} // namespace shaka diff --git a/packager/media/codecs/ac3_audio_util.h b/packager/media/codecs/ac3_audio_util.h new file mode 100644 index 0000000000..f47de5f23c --- /dev/null +++ b/packager/media/codecs/ac3_audio_util.h @@ -0,0 +1,27 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd +// +// AC3 audio utility functions. + +#ifndef PACKAGER_MEDIA_CODECS_AC3_AUDIO_UTIL_H_ +#define PACKAGER_MEDIA_CODECS_AC3_AUDIO_UTIL_H_ + +#include +#include +#include + +namespace shaka { +namespace media { + +/// Parse data from AC3Specific box and calculate number of channels. +/// @return The number of channels associated with the input ac3 data on +/// success; otherwise 0 is returned. +size_t GetAc3NumChannels(const std::vector& ac3_data); + +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_CODECS_AC3_AUDIO_UTIL_H_ diff --git a/packager/media/codecs/ac3_audio_util_unittest.cc b/packager/media/codecs/ac3_audio_util_unittest.cc new file mode 100644 index 0000000000..fe737a8191 --- /dev/null +++ b/packager/media/codecs/ac3_audio_util_unittest.cc @@ -0,0 +1,30 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include + +#include "packager/media/codecs/ac3_audio_util.h" + +namespace shaka { +namespace media { + +TEST(Ac3AudioUtilTest, ChannelTest1) { + // audio_coding_mode is 7, which is Left, Center, Right, Left surround, Right + // surround. LFE channel on. + const std::vector ac3_data = {0x10, 0x3d, 0xc0}; + + EXPECT_EQ(6u, GetAc3NumChannels(ac3_data)); +} + +TEST(Ac3AudioUtilTest, ChannelTest2) { + // audio_coding_mode is 2, which is Left and Right. LFE channel off. + const std::vector ac3_data = {0x10, 0x11, 0xc0}; + + EXPECT_EQ(2u, GetAc3NumChannels(ac3_data)); +} + +} // namespace media +} // namespace shaka diff --git a/packager/media/codecs/codecs.gyp b/packager/media/codecs/codecs.gyp index d0a4b49b03..824853bf64 100644 --- a/packager/media/codecs/codecs.gyp +++ b/packager/media/codecs/codecs.gyp @@ -15,6 +15,8 @@ 'sources': [ 'aac_audio_specific_config.cc', 'aac_audio_specific_config.h', + 'ac3_audio_util.cc', + 'ac3_audio_util.h', 'avc_decoder_configuration_record.cc', 'avc_decoder_configuration_record.h', 'decoder_configuration_record.cc', @@ -61,6 +63,7 @@ 'type': '<(gtest_target_type)', 'sources': [ 'aac_audio_specific_config_unittest.cc', + 'ac3_audio_util_unittest.cc', 'avc_decoder_configuration_record_unittest.cc', 'ec3_audio_util_unittest.cc', 'es_descriptor_unittest.cc', diff --git a/packager/media/codecs/ec3_audio_util.cc b/packager/media/codecs/ec3_audio_util.cc index a8588d9fa0..faa1cf173a 100644 --- a/packager/media/codecs/ec3_audio_util.cc +++ b/packager/media/codecs/ec3_audio_util.cc @@ -4,9 +4,12 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +#include "packager/media/codecs/ec3_audio_util.h" + +#include "packager/base/macros.h" +#include "packager/base/strings/string_number_conversions.h" #include "packager/media/base/bit_reader.h" #include "packager/media/base/rcheck.h" -#include "packager/media/codecs/ec3_audio_util.h" namespace shaka { namespace media { @@ -49,6 +52,13 @@ enum kEC3AudioChannelMap { kLFE2 = 0x2, kLFEScreen = 0x1 }; +// Number of channels for the channel bit above. The first entry corresponds to +// kLeft, which has one channel. All the XxxPairs bits have two channels. +const size_t kChannelCountArray[] = { + 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1, 2, 1, 1, +}; +static_assert(arraysize(kChannelCountArray) == 16u, + "Channel count array should have 16 entries."); // EC3 Audio coding mode map (acmod) to determine EC3 audio channel layout. The // value stands for the existence of Left, Center, Right, Left surround, and @@ -118,13 +128,17 @@ bool ExtractEc3Data(const std::vector& ec3_data, } // namespace -bool CalculateEC3ChannelMap(const std::vector& ec3_data, uint32_t* channel_map) { +bool CalculateEC3ChannelMap(const std::vector& ec3_data, + uint32_t* channel_map) { uint8_t audio_coding_mode; bool lfe_channel_on; uint16_t dependent_substreams_layout; if (!ExtractEc3Data(ec3_data, &audio_coding_mode, &lfe_channel_on, - &dependent_substreams_layout)) + &dependent_substreams_layout)) { + LOG(WARNING) << "Seeing invalid EC3 data: " + << base::HexEncode(ec3_data.data(), ec3_data.size()); return false; + } // Dependent substreams layout bit map: // Bit, Location @@ -151,5 +165,21 @@ bool CalculateEC3ChannelMap(const std::vector& ec3_data, uint32_t* chan return true; } +size_t GetEc3NumChannels(const std::vector& ec3_data) { + uint32_t channel_map; + if (!CalculateEC3ChannelMap(ec3_data, &channel_map)) + return 0; + + size_t num_channels = 0; + int bit = kLeft; + for (size_t channel_count : kChannelCountArray) { + if (channel_map & bit) + num_channels += channel_count; + bit >>= 1; + } + DCHECK_EQ(bit, 0); + return num_channels; +} + } // namespace media } // namespace shaka diff --git a/packager/media/codecs/ec3_audio_util.h b/packager/media/codecs/ec3_audio_util.h index 15d24ef574..a698c7c819 100644 --- a/packager/media/codecs/ec3_audio_util.h +++ b/packager/media/codecs/ec3_audio_util.h @@ -9,6 +9,7 @@ #ifndef PACKAGER_MEDIA_CODECS_EC3_AUDIO_UTIL_H_ #define PACKAGER_MEDIA_CODECS_EC3_AUDIO_UTIL_H_ +#include #include #include @@ -21,6 +22,11 @@ namespace media { bool CalculateEC3ChannelMap(const std::vector& ec3_data, uint32_t* channel_map); +/// Parse data from EC3Specific box and calculate number of channels. +/// @return The number of channels associated with the input ec3 data on +/// success; otherwise 0 is returned. +size_t GetEc3NumChannels(const std::vector& ec3_data); + } // namespace media } // namespace shaka diff --git a/packager/media/codecs/ec3_audio_util_unittest.cc b/packager/media/codecs/ec3_audio_util_unittest.cc index e78cca84e4..77a81d166a 100644 --- a/packager/media/codecs/ec3_audio_util_unittest.cc +++ b/packager/media/codecs/ec3_audio_util_unittest.cc @@ -6,44 +6,43 @@ #include -#include "packager/base/macros.h" #include "packager/media/codecs/ec3_audio_util.h" namespace shaka { namespace media { -TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest1) { +TEST(EC3AudioUtilTest, ChannelTest1) { // audio_coding_mode is 7, which is Left, Center, Right, Left surround, Right // surround. No dependent substreams. LFE channel on. - const uint8_t kEc3Data[] = {0, 0, 0, 0x0f, 0}; + const std::vector ec3_data = {0, 0, 0, 0x0f, 0}; + uint32_t channel_map; - EXPECT_TRUE(CalculateEC3ChannelMap( - std::vector(kEc3Data, kEc3Data + arraysize(kEc3Data)), - &channel_map)); + EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map)); EXPECT_EQ(0xF801u, channel_map); + EXPECT_EQ(6u, GetEc3NumChannels(ec3_data)); } -TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest2) { +TEST(EC3AudioUtilTest, ChannelTest2) { // audio_coding_mode is 2, which is Left and Right. No dependent substreams. // LFE channel off. - const uint8_t kEc3Data[] = {0, 0, 0, 0x04, 0}; + const std::vector ec3_data = {0, 0, 0, 0x04, 0}; + uint32_t channel_map; - EXPECT_TRUE(CalculateEC3ChannelMap( - std::vector(kEc3Data, kEc3Data + arraysize(kEc3Data)), - &channel_map)); + EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map)); EXPECT_EQ(0xA000u, channel_map); + EXPECT_EQ(2u, GetEc3NumChannels(ec3_data)); } -TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest3) { +TEST(EC3AudioUtilTest, ChannelTest3) { // audio_coding_mode is 3, which is Left, Center, and Right. Dependent // substreams layout is 0b100000011, which is Left center/ Right center pair, // Left rear surround/ Right rear surround pair, LFE2 on. LFE channel on. - const uint8_t kEc3Data[] = {0, 0, 0, 0x07, 0x07, 0x03}; + const std::vector ec3_data = {0, 0, 0, 0x07, 0x07, 0x03}; + uint32_t channel_map; - EXPECT_TRUE(CalculateEC3ChannelMap( - std::vector(kEc3Data, kEc3Data + arraysize(kEc3Data)), - &channel_map)); + EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map)); EXPECT_EQ(0xE603u, channel_map); + EXPECT_EQ(9u, GetEc3NumChannels(ec3_data)); } } // namespace media diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index 614436b584..cf0f238ee2 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -21,7 +21,9 @@ #include "packager/media/base/media_sample.h" #include "packager/media/base/rcheck.h" #include "packager/media/base/video_stream_info.h" +#include "packager/media/codecs/ac3_audio_util.h" #include "packager/media/codecs/avc_decoder_configuration_record.h" +#include "packager/media/codecs/ec3_audio_util.h" #include "packager/media/codecs/es_descriptor.h" #include "packager/media/codecs/hevc_decoder_configuration_record.h" #include "packager/media/codecs/vp_codec_configuration_record.h" @@ -438,12 +440,12 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { break; case FOURCC_ac_3: codec_config = entry.dac3.data; - num_channels = entry.channelcount; + num_channels = static_cast(GetAc3NumChannels(codec_config)); sampling_frequency = entry.samplerate; break; case FOURCC_ec_3: codec_config = entry.dec3.data; - num_channels = entry.channelcount; + num_channels = static_cast(GetEc3NumChannels(codec_config)); sampling_frequency = entry.samplerate; break; case FOURCC_Opus: diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index b6aa59604b..42ed5acc00 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -348,8 +348,15 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info, break; } - audio.channelcount = audio_info->num_channels(); - audio.samplesize = audio_info->sample_bits(); + if (audio_info->codec() == kCodecAC3 || audio_info->codec() == kCodecEAC3) { + // AC3 and EC3 does not fill in actual channel count and sample size in + // sample description entry. Instead, two constants are used. + audio.channelcount = 2; + audio.samplesize = 16; + } else { + audio.channelcount = audio_info->num_channels(); + audio.samplesize = audio_info->sample_bits(); + } audio.samplerate = audio_info->sampling_frequency(); SampleTable& sample_table = trak->media.information.sample_table; SampleDescription& sample_description = sample_table.description;