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
This commit is contained in:
parent
38c30bd7a1
commit
9ddf9276ce
|
@ -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',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<uint8_t>& 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<uint8_t>& 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
|
|
@ -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 <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t>& ec3_data,
|
||||
uint32_t* channel_map);
|
||||
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
||||
|
||||
#endif // MEDIA_FILTERS_EC3_AUDIO_UTIL_H_
|
|
@ -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 <gtest/gtest.h>
|
||||
|
||||
#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<uint8_t> 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<uint8_t> 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<uint8_t> 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
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -315,32 +315,10 @@ struct AC3Specific : Box {
|
|||
std::vector<uint8_t> 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<uint8_t> data;
|
||||
|
||||
size_t number_independent_substreams; // num_id_sub: 3 bits.
|
||||
std::vector<IndependentSubstream> independent_substreams;
|
||||
};
|
||||
|
||||
struct AudioSampleEntry : Box {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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<xmlDoc> doc(MakeDoc(representation_.PassScopedPtr()));
|
||||
|
||||
ASSERT_TRUE(XmlEqual(
|
||||
"<Representation audioSamplingRate=\"44100\">\n"
|
||||
" <AudioChannelConfiguration\n"
|
||||
" schemeIdUri=\n"
|
||||
" \"tag:dolby.com,2014:dash:audio_channel_configuration:2011\"\n"
|
||||
" value=\"F801\"/>\n"
|
||||
"</Representation>\n",
|
||||
doc.get()));
|
||||
}
|
||||
|
||||
// Some template names cannot be used for init segment name.
|
||||
TEST_F(RepresentationTest, InvalidLiveInitSegmentName) {
|
||||
MediaInfo media_info;
|
||||
|
|
Loading…
Reference in New Issue