Support Dolby audio Enhanced AC3 in ISO BMFF (Part 2)

- Box definitions for box type DEC3.
- Parser/muxer changes to support Enhanced-AC3 audio codecs.
- MPD signaling will come in Part 3.

Issue #64

Change-Id: Ifcd5efa1f61b470ec225127925631e4329853259
This commit is contained in:
Bei Li 2016-01-15 13:40:44 -08:00
parent d1d75f477c
commit 8563db4cff
9 changed files with 133 additions and 2 deletions

View File

@ -33,6 +33,8 @@ std::string AudioCodecToString(AudioCodec audio_codec) {
return "DTS-"; return "DTS-";
case kCodecDTSP: case kCodecDTSP:
return "DTS+"; return "DTS+";
case kCodecEAC3:
return "EAC3";
case kCodecOpus: case kCodecOpus:
return "Opus"; return "Opus";
case kCodecVorbis: case kCodecVorbis:
@ -115,6 +117,8 @@ std::string AudioStreamInfo::GetCodecString(AudioCodec codec,
return "dts-"; return "dts-";
case kCodecAC3: case kCodecAC3:
return "ac-3"; return "ac-3";
case kCodecEAC3:
return "ec-3";
default: default:
NOTIMPLEMENTED() << "Codec: " << codec; NOTIMPLEMENTED() << "Codec: " << codec;
return "unknown"; return "unknown";

View File

@ -24,6 +24,7 @@ enum AudioCodec {
kCodecDTSL, kCodecDTSL,
kCodecDTSM, kCodecDTSM,
kCodecDTSP, kCodecDTSP,
kCodecEAC3,
kCodecOpus, kCodecOpus,
kCodecVorbis, kCodecVorbis,

View File

@ -8,6 +8,7 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/base/bit_reader.h" #include "packager/media/base/bit_reader.h"
#include "packager/media/base/macros.h"
#include "packager/media/formats/mp4/box_buffer.h" #include "packager/media/formats/mp4/box_buffer.h"
#include "packager/media/formats/mp4/rcheck.h" #include "packager/media/formats/mp4/rcheck.h"
@ -1435,6 +1436,54 @@ uint32_t AC3Specific::ComputeSizeInternal() {
return HeaderSize() + data.size(); return HeaderSize() + data.size();
} }
EC3Specific::EC3Specific() : number_independent_substreams(0) {}
EC3Specific::~EC3Specific() {}
FourCC EC3Specific::BoxType() const { return FOURCC_DEC3; }
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;
}
uint32_t EC3Specific::ComputeSizeInternal() {
// This box is optional. Skip it if not initialized.
if (data.empty())
return 0;
return HeaderSize() + data.size();
}
AudioSampleEntry::AudioSampleEntry() AudioSampleEntry::AudioSampleEntry()
: format(FOURCC_NULL), : format(FOURCC_NULL),
data_reference_index(1), data_reference_index(1),
@ -1489,6 +1538,7 @@ bool AudioSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(buffer->TryReadWriteChild(&esds)); RCHECK(buffer->TryReadWriteChild(&esds));
RCHECK(buffer->TryReadWriteChild(&ddts)); RCHECK(buffer->TryReadWriteChild(&ddts));
RCHECK(buffer->TryReadWriteChild(&dac3)); RCHECK(buffer->TryReadWriteChild(&dac3));
RCHECK(buffer->TryReadWriteChild(&dec3));
return true; return true;
} }
@ -1496,6 +1546,7 @@ uint32_t AudioSampleEntry::ComputeSizeInternal() {
return HeaderSize() + sizeof(data_reference_index) + sizeof(channelcount) + return HeaderSize() + sizeof(data_reference_index) + sizeof(channelcount) +
sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() + sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() +
esds.ComputeSize() + ddts.ComputeSize() + dac3.ComputeSize() + esds.ComputeSize() + ddts.ComputeSize() + dac3.ComputeSize() +
dec3.ComputeSize() +
6 + 8 + // 6 + 8 bytes reserved. 6 + 8 + // 6 + 8 bytes reserved.
4; // 4 bytes predefined. 4; // 4 bytes predefined.
} }

View File

@ -315,6 +315,34 @@ struct AC3Specific : Box {
std::vector<uint8_t> data; 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 { struct AudioSampleEntry : Box {
DECLARE_BOX_METHODS(AudioSampleEntry); DECLARE_BOX_METHODS(AudioSampleEntry);
// Returns actual format of this sample entry. // Returns actual format of this sample entry.
@ -329,9 +357,11 @@ struct AudioSampleEntry : Box {
uint32_t samplerate; uint32_t samplerate;
ProtectionSchemeInfo sinf; ProtectionSchemeInfo sinf;
ElementaryStreamDescriptor esds; ElementaryStreamDescriptor esds;
DTSSpecific ddts; DTSSpecific ddts;
AC3Specific dac3; AC3Specific dac3;
EC3Specific dec3;
}; };
struct WebVTTConfigurationBox : Box { struct WebVTTConfigurationBox : Box {

View File

@ -245,6 +245,11 @@ inline bool operator==(const AC3Specific& lhs,
return lhs.data == rhs.data; return lhs.data == rhs.data;
} }
inline bool operator==(const EC3Specific& lhs,
const EC3Specific& rhs) {
return lhs.data == rhs.data;
}
inline bool operator==(const AudioSampleEntry& lhs, inline bool operator==(const AudioSampleEntry& lhs,
const AudioSampleEntry& rhs) { const AudioSampleEntry& rhs) {
return lhs.format == rhs.format && return lhs.format == rhs.format &&
@ -252,7 +257,7 @@ inline bool operator==(const AudioSampleEntry& lhs,
lhs.channelcount == rhs.channelcount && lhs.channelcount == rhs.channelcount &&
lhs.samplesize == rhs.samplesize && lhs.samplerate == rhs.samplerate && lhs.samplesize == rhs.samplesize && lhs.samplerate == rhs.samplerate &&
lhs.sinf == rhs.sinf && lhs.esds == rhs.esds && lhs.ddts == rhs.ddts && lhs.sinf == rhs.sinf && lhs.esds == rhs.esds && lhs.ddts == rhs.ddts &&
lhs.dac3 == rhs.dac3; lhs.dac3 == rhs.dac3 && lhs.dec3 == rhs.dec3;
} }
inline bool operator==(const WebVTTConfigurationBox& lhs, inline bool operator==(const WebVTTConfigurationBox& lhs,

View File

@ -390,6 +390,16 @@ class BoxDefinitionsTestGeneral : public testing::Test {
dac3->data.assign(kAc3Data, kAc3Data + arraysize(kAc3Data)); dac3->data.assign(kAc3Data, kAc3Data + arraysize(kAc3Data));
} }
void Fill(EC3Specific* dec3) {
const uint8_t kEc3Data[] = {0x08, 0x00, 0x20, 0x0f, 0x00};
dec3->data.assign(kEc3Data, kEc3Data + arraysize(kEc3Data));
}
void Modify(EC3Specific* dec3) {
const uint8_t kEc3Data[] = {0x07, 0x00, 0x60, 0x04, 0x00};
dec3->data.assign(kEc3Data, kEc3Data + arraysize(kEc3Data));
}
void Fill(AudioSampleEntry* enca) { void Fill(AudioSampleEntry* enca) {
enca->format = FOURCC_ENCA; enca->format = FOURCC_ENCA;
enca->data_reference_index = 2; enca->data_reference_index = 2;
@ -895,6 +905,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
bool IsOptional(const PixelAspectRatio* box) { return true; } bool IsOptional(const PixelAspectRatio* box) { return true; }
bool IsOptional(const ElementaryStreamDescriptor* box) { return true; } bool IsOptional(const ElementaryStreamDescriptor* box) { return true; }
bool IsOptional(const AC3Specific* box) { return true; } bool IsOptional(const AC3Specific* box) { return true; }
bool IsOptional(const EC3Specific* box) { return true; }
// Recommended, but optional. // Recommended, but optional.
bool IsOptional(const WebVTTSourceLabelBox* box) { return true; } bool IsOptional(const WebVTTSourceLabelBox* box) { return true; }
bool IsOptional(const CompositionTimeToSample* box) { return true; } bool IsOptional(const CompositionTimeToSample* box) { return true; }
@ -938,6 +949,7 @@ typedef testing::Types<FileType,
ElementaryStreamDescriptor, ElementaryStreamDescriptor,
DTSSpecific, DTSSpecific,
AC3Specific, AC3Specific,
EC3Specific,
AudioSampleEntry, AudioSampleEntry,
WebVTTConfigurationBox, WebVTTConfigurationBox,
WebVTTSourceLabelBox, WebVTTSourceLabelBox,
@ -1078,7 +1090,6 @@ TEST_F(BoxDefinitionsTest, DTSSampleEntry) {
entry.samplerate = 44100; entry.samplerate = 44100;
Fill(&entry.ddts); Fill(&entry.ddts);
entry.Write(this->buffer_.get()); entry.Write(this->buffer_.get());
AudioSampleEntry entry_readback; AudioSampleEntry entry_readback;
ASSERT_TRUE(ReadBack(&entry_readback)); ASSERT_TRUE(ReadBack(&entry_readback));
ASSERT_EQ(entry, entry_readback); ASSERT_EQ(entry, entry_readback);
@ -1099,6 +1110,21 @@ TEST_F(BoxDefinitionsTest, AC3SampleEntry) {
ASSERT_EQ(entry, entry_readback); ASSERT_EQ(entry, entry_readback);
} }
TEST_F(BoxDefinitionsTest, EC3SampleEntry) {
AudioSampleEntry entry;
entry.format = FOURCC_EAC3;
entry.data_reference_index = 2;
entry.channelcount = 5;
entry.samplesize = 16;
entry.samplerate = 44100;
Fill(&entry.dec3);
entry.Write(this->buffer_.get());
AudioSampleEntry entry_readback;
ASSERT_TRUE(ReadBack(&entry_readback));
ASSERT_EQ(entry, entry_readback);
}
TEST_F(BoxDefinitionsTest, ProtectionSystemSpecificHeader) { TEST_F(BoxDefinitionsTest, ProtectionSystemSpecificHeader) {
ProtectionSystemSpecificHeader pssh; ProtectionSystemSpecificHeader pssh;
Fill(&pssh); Fill(&pssh);

View File

@ -26,6 +26,7 @@ enum FourCC {
FOURCC_DAC3 = 0x64616333, FOURCC_DAC3 = 0x64616333,
FOURCC_DASH = 0x64617368, FOURCC_DASH = 0x64617368,
FOURCC_DDTS = 0x64647473, FOURCC_DDTS = 0x64647473,
FOURCC_DEC3 = 0x64656333,
FOURCC_DINF = 0x64696e66, FOURCC_DINF = 0x64696e66,
FOURCC_DREF = 0x64726566, FOURCC_DREF = 0x64726566,
FOURCC_DTSC = 0x64747363, FOURCC_DTSC = 0x64747363,
@ -34,6 +35,7 @@ enum FourCC {
FOURCC_DTSL = 0x6474736c, FOURCC_DTSL = 0x6474736c,
FOURCC_DTSM = 0x6474732d, FOURCC_DTSM = 0x6474732d,
FOURCC_DTSP = 0x6474732b, FOURCC_DTSP = 0x6474732b,
FOURCC_EAC3 = 0x65632d33, // This fourcc is "ec-3".
FOURCC_EDTS = 0x65647473, FOURCC_EDTS = 0x65647473,
FOURCC_ELST = 0x656c7374, FOURCC_ELST = 0x656c7374,
FOURCC_ENCA = 0x656e6361, FOURCC_ENCA = 0x656e6361,

View File

@ -76,6 +76,8 @@ AudioCodec FourCCToAudioCodec(FourCC fourcc) {
return kCodecDTSM; return kCodecDTSM;
case FOURCC_AC3: case FOURCC_AC3:
return kCodecAC3; return kCodecAC3;
case FOURCC_EAC3:
return kCodecEAC3;
default: default:
return kUnknownAudioCodec; return kUnknownAudioCodec;
} }
@ -415,6 +417,11 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
num_channels = entry.channelcount; num_channels = entry.channelcount;
sampling_frequency = entry.samplerate; sampling_frequency = entry.samplerate;
break; break;
case FOURCC_EAC3:
extra_data = entry.dec3.data;
num_channels = entry.channelcount;
sampling_frequency = entry.samplerate;
break;
default: default:
LOG(ERROR) << "Unsupported audio format 0x" << std::hex LOG(ERROR) << "Unsupported audio format 0x" << std::hex
<< actual_format << " in stsd box."; << actual_format << " in stsd box.";

View File

@ -75,6 +75,8 @@ FourCC AudioCodecToFourCC(AudioCodec codec) {
return FOURCC_DTSE; return FOURCC_DTSE;
case kCodecDTSM: case kCodecDTSM:
return FOURCC_DTSM; return FOURCC_DTSM;
case kCodecEAC3:
return FOURCC_EAC3;
default: default:
return FOURCC_NULL; return FOURCC_NULL;
} }
@ -266,6 +268,9 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
case kCodecAC3: case kCodecAC3:
audio.dac3.data = audio_info->extra_data(); audio.dac3.data = audio_info->extra_data();
break; break;
case kCodecEAC3:
audio.dec3.data = audio_info->extra_data();
break;
default: default:
NOTIMPLEMENTED(); NOTIMPLEMENTED();
break; break;