From 9ddf9276ce6236394eea60ef8d4cf37e0e152e52 Mon Sep 17 00:00:00 2001 From: Bei Li Date: Mon, 8 Feb 2016 16:22:01 -0800 Subject: [PATCH] MPD signaling for Dolby Enhanced AC3 audio. Parse channel layout value from dec3 box. Pass it through audio stream info. MPD builder forms audio channel configuration to signal ec-3 codec. Specs: 1) ETSI TS 102 366 V1.3.1 Digital Audio Compression (AC-3, Enhanced AC-3) Standard E.1.3.1.8. 2) DASH-IF Interoperability Points v3.0 9.2.1.2. Issue #64 Change-Id: Ia2c22dd3d82e757ba5a88ba1de35c5d593f5005e --- packager/media/event/media_event.gyp | 1 + .../media/event/muxer_listener_internal.cc | 11 ++ packager/media/filters/ec3_audio_util.cc | 155 ++++++++++++++++++ packager/media/filters/ec3_audio_util.h | 27 +++ .../media/filters/ec3_audio_util_unittest.cc | 43 +++++ packager/media/filters/filters.gyp | 3 + packager/media/formats/mp4/box_definitions.cc | 31 +--- packager/media/formats/mp4/box_definitions.h | 22 --- packager/mpd/base/media_info.proto | 7 + packager/mpd/base/xml/xml_node.cc | 27 ++- packager/mpd/base/xml/xml_node_unittest.cc | 18 ++ 11 files changed, 288 insertions(+), 57 deletions(-) create mode 100644 packager/media/filters/ec3_audio_util.cc create mode 100644 packager/media/filters/ec3_audio_util.h create mode 100644 packager/media/filters/ec3_audio_util_unittest.cc diff --git a/packager/media/event/media_event.gyp b/packager/media/event/media_event.gyp index 24c2a1439b..c17ff7b343 100644 --- a/packager/media/event/media_event.gyp +++ b/packager/media/event/media_event.gyp @@ -27,6 +27,7 @@ '../../third_party/protobuf/protobuf.gyp:protobuf_full_do_not_use', '../base/media_base.gyp:media_base', '../file/file.gyp:file', + '../filters/filters.gyp:filters', ], }, { diff --git a/packager/media/event/muxer_listener_internal.cc b/packager/media/event/muxer_listener_internal.cc index 6b6d205a8d..01f5e4cd1f 100644 --- a/packager/media/event/muxer_listener_internal.cc +++ b/packager/media/event/muxer_listener_internal.cc @@ -12,6 +12,7 @@ #include "packager/media/base/audio_stream_info.h" #include "packager/media/base/muxer_options.h" #include "packager/media/base/video_stream_info.h" +#include "packager/media/filters/ec3_audio_util.h" #include "packager/mpd/base/media_info.pb.h" namespace edash_packager { @@ -118,6 +119,16 @@ void AddAudioInfo(const AudioStreamInfo* audio_stream_info, if (!extra_data.empty()) { audio_info->set_decoder_config(&extra_data[0], extra_data.size()); } + + if (audio_stream_info->codec_string() == "ec-3") { + uint32_t ec3_channel_map; + if (!CalculateEC3ChannelMap(extra_data, &ec3_channel_map)) { + LOG(ERROR) << "Failed to calculate EC3 channel map."; + return; + } + audio_info->mutable_codec_specific_data()->set_ec3_channel_map( + ec3_channel_map); + } } void SetMediaInfoStreamInfo(const StreamInfo& stream_info, diff --git a/packager/media/filters/ec3_audio_util.cc b/packager/media/filters/ec3_audio_util.cc new file mode 100644 index 0000000000..9c06aa4ac3 --- /dev/null +++ b/packager/media/filters/ec3_audio_util.cc @@ -0,0 +1,155 @@ +// Copyright 2016 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/base/bit_reader.h" +#include "packager/media/filters/ec3_audio_util.h" +#include "packager/media/formats/mp4/rcheck.h" + +namespace edash_packager { +namespace media { + +namespace { + +// Channels bit map. 16 bits. +// Bit, Location +// 0(MSB), Left +// 1, Center +// 2, Right +// 3, Left Surround +// 4, Right Surround +// 5, Left center/Right center pair +// 6, Left rear surround/Right rear surround pair +// 7, Center surround +// 8, Top center surround +// 9, Left surround direct/Right surround direct pair +// 10, Left wide/Right wide pair +// 11, Lvertical height/Right vertical height pair +// 12, Center vertical height +// 13, Lts/Rts pair +// 14, LFE2 +// 15, LFE +enum kEC3AudioChannelMap { + kLeft = 0x8000, + kCenter = 0x4000, + kRight = 0x2000, + kLeftSurround = 0x1000, + kRightSurround = 0x800, + kLcRcPair = 0x400, + kLrsRrsPair = 0x200, + kCenterSurround = 0x100, + kTopCenterSurround = 0x80, + kLsdRsdPair = 0x40, + kLwRwPair = 0x20, + kLvhRvhPair = 0x10, + kCenterVerticalHeight = 0x8, + kLtsRtsPair = 0x4, + kLFE2 = 0x2, + kLFEScreen = 0x1 +}; + +// 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 +// Right surround. +const uint16_t kEC3AudioCodingModeMap[] = { + kLeft | kRight, + kCenter, + kLeft | kRight, + kLeft | kCenter | kRight, + kLeft | kRight | kLeftSurround | kRightSurround, + kLeft | kCenter | kRight | kLeftSurround | kRightSurround, + kLeft | kRight | kLeftSurround | kRightSurround, + kLeft | kCenter | kRight | kLeftSurround | kRightSurround, +}; + +// Reverse bit order. +uint8_t ReverseBits8(uint8_t n) { + n = ((n >> 1) & 0x55) | ((n & 0x55) << 1); + n = ((n >> 2) & 0x33) | ((n & 0x33) << 2); + return ((n >> 4) & 0x0f) | ((n & 0x0f) << 4); +} + +bool ExtractEc3Data(const std::vector& ec3_data, + uint8_t* audio_coding_mode, + bool* lfe_channel_on, + uint16_t* dependent_substreams_layout) { + BitReader bit_reader(ec3_data.data(), ec3_data.size()); + // Read number of independent substreams and parse the independent substreams. + uint8_t number_independent_substreams; + RCHECK(bit_reader.SkipBits(13) && + bit_reader.ReadBits(3, &number_independent_substreams)); + // The value of this field is one less than the number of independent + // substreams present. + ++number_independent_substreams; + + // Parse audio_coding_mode, dependent_substreams_layout and lfe_channel_on + // from the first independent substream. + // Independent substream in EC3Specific box: + // fscod: 2 bits + // bsid: 5 bits + // reserved_1: 1 bit + // asvc: 1 bit + // bsmod: 3 bits + // acmod: 3 bits + // lfeon: 1 bit + // reserved_2: 3 bits + // num_dep_sub: 4 bits + // If num_dep_sub > 0, chan_loc is present and the size is 9 bits. + // Otherwise, reserved_3 is present and the size is 1 bit. + // chan_loc: 9 bits + // reserved_3: 1 bit + RCHECK(bit_reader.SkipBits(12)); + RCHECK(bit_reader.ReadBits(3, audio_coding_mode)); + RCHECK(bit_reader.ReadBits(1, lfe_channel_on)); + + uint8_t number_dependent_substreams = 0; + RCHECK(bit_reader.SkipBits(3)); + RCHECK(bit_reader.ReadBits(4, &number_dependent_substreams)); + + *dependent_substreams_layout = 0; + if (number_dependent_substreams > 0) { + RCHECK(bit_reader.ReadBits(9, dependent_substreams_layout)); + } + + return true; +} + +} // namespace + +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)) + return false; + + // Dependent substreams layout bit map: + // Bit, Location + // 0, Lc/Rc pair + // 1, Lrs/Rrs pair + // 2, Cs + // 3, Ts + // 4, Lsd/Rsd pair + // 5, Lw/Rw pair + // 6, Lvh/Rvh pair + // 7, Cvh + // 8(MSB), LFE2 + // Reverse bit order of dependent substreams channel layout (LFE2 not + // included) to apply on channel_map bit 5 - 12. + const uint8_t reversed_dependent_substreams_layout = + ReverseBits8(dependent_substreams_layout & 0xFF); + + *channel_map = kEC3AudioCodingModeMap[audio_coding_mode] | + (reversed_dependent_substreams_layout << 3); + if (dependent_substreams_layout & 0x100) + *channel_map |= kLFE2; + if (lfe_channel_on) + *channel_map |= kLFEScreen; + return true; +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/filters/ec3_audio_util.h b/packager/media/filters/ec3_audio_util.h new file mode 100644 index 0000000000..05626e0973 --- /dev/null +++ b/packager/media/filters/ec3_audio_util.h @@ -0,0 +1,27 @@ +// Copyright 2016 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 +// +// Enhanced AC3 audio utility functions. + +#ifndef MEDIA_FILTERS_EC3_AUDIO_UTIL_H_ +#define MEDIA_FILTERS_EC3_AUDIO_UTIL_H_ + +#include +#include + +namespace edash_packager { +namespace media { + +/// Parse data from EC3Specific box and calculate EC3 channel map value based on +/// ETSI TS 102 366 V1.3.1 Digital Audio Compression (AC-3, Enhanced AC-3) +/// Standard E.1.3.1.8. +bool CalculateEC3ChannelMap(const std::vector& ec3_data, + uint32_t* channel_map); + +} // namespace media +} // namespace edash_packager + +#endif // MEDIA_FILTERS_EC3_AUDIO_UTIL_H_ diff --git a/packager/media/filters/ec3_audio_util_unittest.cc b/packager/media/filters/ec3_audio_util_unittest.cc new file mode 100644 index 0000000000..d1b7d2b375 --- /dev/null +++ b/packager/media/filters/ec3_audio_util_unittest.cc @@ -0,0 +1,43 @@ +// Copyright 2016 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/filters/ec3_audio_util.h" + +namespace edash_packager { +namespace media { + +TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest1) { + // audio_coding_mode is 7, which is Left, Center, Right, Left surround, Right + // surround. No dependent substreams. LFE channel on. + const std::vector ec3_data = {0, 0, 0, 0x0f, 0}; + uint32_t channel_map; + EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map)); + EXPECT_EQ(0xF801u, channel_map); +} + +TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest2) { + // audio_coding_mode is 2, which is Left and Right. No dependent substreams. + // LFE channel off. + const std::vector ec3_data = {0, 0, 0, 0x04, 0}; + uint32_t channel_map; + EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map)); + EXPECT_EQ(0xA000u, channel_map); +} + +TEST(EC3AudioUtilTest, CalculateEC3ChannelMapTest3) { + // 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 std::vector ec3_data = {0, 0, 0, 0x07, 0x07, 0x03}; + uint32_t channel_map; + EXPECT_TRUE(CalculateEC3ChannelMap(ec3_data, &channel_map)); + EXPECT_EQ(0xE603u, channel_map); +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/filters/filters.gyp b/packager/media/filters/filters.gyp index a3abafa453..32b28095e4 100644 --- a/packager/media/filters/filters.gyp +++ b/packager/media/filters/filters.gyp @@ -15,6 +15,8 @@ 'sources': [ 'avc_decoder_configuration.cc', 'avc_decoder_configuration.h', + 'ec3_audio_util.cc', + 'ec3_audio_util.h', 'hevc_decoder_configuration.cc', 'hevc_decoder_configuration.h', 'h264_bit_reader.cc', @@ -40,6 +42,7 @@ 'type': '<(gtest_target_type)', 'sources': [ 'avc_decoder_configuration_unittest.cc', + 'ec3_audio_util_unittest.cc', 'h264_bit_reader_unittest.cc', 'h264_byte_to_unit_stream_converter_unittest.cc', 'h264_parser_unittest.cc', diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 2d9fc41354..c26f9c6f61 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -1439,7 +1439,7 @@ uint32_t AC3Specific::ComputeSizeInternal() { return HeaderSize() + data.size(); } -EC3Specific::EC3Specific() : number_independent_substreams(0) {} +EC3Specific::EC3Specific() {} EC3Specific::~EC3Specific() {} FourCC EC3Specific::BoxType() const { return FOURCC_DEC3; } @@ -1448,35 +1448,6 @@ bool EC3Specific::ReadWriteInternal(BoxBuffer* buffer) { RCHECK(ReadWriteHeaderInternal(buffer)); uint32_t size = buffer->Reading() ? buffer->BytesLeft() : data.size(); RCHECK(buffer->ReadWriteVector(&data, size)); - - // Skip data rate, read number of independent substreams and parse the - // independent substreams. - BitReader bit_reader(&data[0], size); - RCHECK(bit_reader.SkipBits(13) && - bit_reader.ReadBits(3, &number_independent_substreams)); - - // The value of this field is one less than the number of independent - // substreams present. - ++number_independent_substreams; - IndependentSubstream substream; - for (size_t i = 0; i < number_independent_substreams; ++i) { - RCHECK(bit_reader.ReadBits(2, &substream.sample_rate_code)); - RCHECK(bit_reader.ReadBits(5, &substream.bit_stream_identification)); - RCHECK(bit_reader.SkipBits(1)); - RCHECK(bit_reader.ReadBits(1, &substream.audio_service)); - RCHECK(bit_reader.ReadBits(3, &substream.bit_stream_mode)); - RCHECK(bit_reader.ReadBits(3, &substream.audio_coding_mode)); - RCHECK(bit_reader.ReadBits(1, &substream.lfe_channel_on)); - RCHECK(bit_reader.SkipBits(3)); - RCHECK(bit_reader.ReadBits(4, &substream.number_dependent_substreams)); - if (substream.number_dependent_substreams > 0) { - RCHECK(bit_reader.ReadBits(9, &substream.channel_location)); - } else { - RCHECK(bit_reader.SkipBits(1)); - } - independent_substreams.push_back(substream); - } - return true; } diff --git a/packager/media/formats/mp4/box_definitions.h b/packager/media/formats/mp4/box_definitions.h index a92b058529..b1e2bfb04a 100644 --- a/packager/media/formats/mp4/box_definitions.h +++ b/packager/media/formats/mp4/box_definitions.h @@ -315,32 +315,10 @@ struct AC3Specific : Box { std::vector data; }; -// Independent substream in EC3Specific box. -struct IndependentSubstream { - uint8_t sample_rate_code; // fscod: 2 bits - uint8_t bit_stream_identification; // bsid: 5 bits - // reserved_1: 1 bit - uint8_t audio_service; // asvc: 1 bit - uint8_t bit_stream_mode; // bsmod: 3 bits - uint8_t audio_coding_mode; // acmod: 3 bits - uint8_t lfe_channel_on; // lfeon: 1 bit - // reserved_2: 3 bit - uint8_t number_dependent_substreams; // num_dep_sub: 4 bits. - // If num_dep_sub > 0, chan_loc is present and the size is 9 bits. - // Otherwise, reserved_3 is present and the size is 1 bit. - uint16_t channel_location; // chan_loc: 9 bits. - // reserved_3: 1 bit -}; - struct EC3Specific : Box { DECLARE_BOX_METHODS(EC3Specific); - // Before we know the number of independent substreams, data in EC3Specific - // box is store for parsing later. std::vector data; - - size_t number_independent_substreams; // num_id_sub: 3 bits. - std::vector independent_substreams; }; struct AudioSampleEntry : Box { diff --git a/packager/mpd/base/media_info.proto b/packager/mpd/base/media_info.proto index be2aa8065f..6ca28e90e6 100644 --- a/packager/mpd/base/media_info.proto +++ b/packager/mpd/base/media_info.proto @@ -49,6 +49,13 @@ message MediaInfo { optional uint32 num_channels = 4; optional string language = 5; optional bytes decoder_config = 6; + optional AudioCodecSpecificData codec_specific_data = 7; + } + + message AudioCodecSpecificData { + // EC3 Channel map bit fields, encoded based on ETSI TS 102 366 V1.3.1 + // Digital Audio Compression (AC-3, Enhanced AC-3) Standard E.1.3.1.8. + optional uint32 ec3_channel_map = 1; } message TextInfo { diff --git a/packager/mpd/base/xml/xml_node.cc b/packager/mpd/base/xml/xml_node.cc index 7e2b1e66b7..6eb0d413b2 100644 --- a/packager/mpd/base/xml/xml_node.cc +++ b/packager/mpd/base/xml/xml_node.cc @@ -12,6 +12,7 @@ #include "packager/base/logging.h" #include "packager/base/macros.h" #include "packager/base/stl_util.h" +#include "packager/base/sys_byteorder.h" #include "packager/base/strings/string_number_conversions.h" #include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/segment_info.h" @@ -27,6 +28,7 @@ typedef ContentProtectionXml::AttributeNameValuePair AttributeNameValuePair; namespace edash_packager { namespace { +const char kEC3Codec[] = "ec-3"; std::string RangeToString(const Range& range) { return base::Uint64ToString(range.begin()) + "-" + @@ -343,13 +345,28 @@ bool RepresentationXmlNode::AddLiveOnlyInfo( } bool RepresentationXmlNode::AddAudioChannelInfo(const AudioInfo& audio_info) { - const uint32_t num_channels = audio_info.num_channels(); + std::string audio_channel_config_scheme; + std::string audio_channel_config_value; + + if (audio_info.codec() == kEC3Codec) { + // Convert EC3 channel map into string of hexadecimal digits. Spec: DASH-IF + // Interoperability Points v3.0 9.2.1.2. + const uint16_t ec3_channel_map = + base::HostToNet16(audio_info.codec_specific_data().ec3_channel_map()); + audio_channel_config_value = + base::HexEncode(&ec3_channel_map, sizeof(ec3_channel_map)); + audio_channel_config_scheme = + "tag:dolby.com,2014:dash:audio_channel_configuration:2011"; + } else { + audio_channel_config_value = base::UintToString(audio_info.num_channels()); + audio_channel_config_scheme = + "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"; + } + XmlNode audio_channel_config("AudioChannelConfiguration"); - const char kAudioChannelConfigScheme[] = - "urn:mpeg:dash:23003:3:audio_channel_configuration:2011"; audio_channel_config.SetStringAttribute("schemeIdUri", - kAudioChannelConfigScheme); - audio_channel_config.SetIntegerAttribute("value", num_channels); + audio_channel_config_scheme); + audio_channel_config.SetStringAttribute("value", audio_channel_config_value); return AddChild(audio_channel_config.PassScopedPtr()); } diff --git a/packager/mpd/base/xml/xml_node_unittest.cc b/packager/mpd/base/xml/xml_node_unittest.cc index c3bb49b491..71e38aed60 100644 --- a/packager/mpd/base/xml/xml_node_unittest.cc +++ b/packager/mpd/base/xml/xml_node_unittest.cc @@ -217,6 +217,24 @@ TEST_F(RepresentationTest, AddContentProtectionElements) { doc.get())); } +TEST_F(RepresentationTest, AddEC3AudioInfo) { + MediaInfo::AudioInfo audio_info; + audio_info.set_codec("ec-3"); + audio_info.set_sampling_frequency(44100); + audio_info.mutable_codec_specific_data()->set_ec3_channel_map(0xF801); + representation_.AddAudioInfo(audio_info); + scoped_xml_ptr doc(MakeDoc(representation_.PassScopedPtr())); + + ASSERT_TRUE(XmlEqual( + "\n" + " \n" + "\n", + doc.get())); +} + // Some template names cannot be used for init segment name. TEST_F(RepresentationTest, InvalidLiveInitSegmentName) { MediaInfo media_info;