Support Dolby audio AC3 in ISO BMFF (Part 1)

- Box definitions for box type DAC3.
- Parser/muxer changes to support AC3 audio codecs.
- EC3 audio sample entry will come in Part 2.
- MPD signaling will come in Part 3.

Issue #64

Change-Id: I790b46ae8179b933bb8f7da9cdd38591fe8da43d
This commit is contained in:
Bei Li 2016-01-08 15:56:33 -08:00
parent b17d240060
commit 58b95fd3d5
9 changed files with 98 additions and 56 deletions

View File

@ -19,42 +19,24 @@ std::string AudioCodecToString(AudioCodec audio_codec) {
switch (audio_codec) { switch (audio_codec) {
case kCodecAAC: case kCodecAAC:
return "AAC"; return "AAC";
case kCodecMP3: case kCodecAC3:
return "MP3"; return "AC3";
case kCodecPCM:
return "PCM";
case kCodecVorbis:
return "Vorbis";
case kCodecFLAC:
return "FLAC";
case kCodecAMR_NB:
return "AMR_NB";
case kCodecAMR_WB:
return "AMR_WB";
case kCodecPCM_MULAW:
return "PCM_MULAW";
case kCodecGSM_MS:
return "GSM_MS";
case kCodecPCM_S16BE:
return "PCM_S16BE";
case kCodecPCM_S24BE:
return "PCM_S24BE";
case kCodecOpus:
return "Opus";
case kCodecEAC3:
return "EAC3";
case kCodecDTSC: case kCodecDTSC:
return "DTSC"; return "DTSC";
case kCodecDTSE:
return "DTSE";
case kCodecDTSH: case kCodecDTSH:
return "DTSH"; return "DTSH";
case kCodecDTSL: case kCodecDTSL:
return "DTSL"; return "DTSL";
case kCodecDTSE:
return "DTSE";
case kCodecDTSP:
return "DTS+";
case kCodecDTSM: case kCodecDTSM:
return "DTS-"; return "DTS-";
case kCodecDTSP:
return "DTS+";
case kCodecOpus:
return "Opus";
case kCodecVorbis:
return "Vorbis";
default: default:
NOTIMPLEMENTED() << "Unknown Audio Codec: " << audio_codec; NOTIMPLEMENTED() << "Unknown Audio Codec: " << audio_codec;
return "UnknownAudioCodec"; return "UnknownAudioCodec";
@ -131,6 +113,8 @@ std::string AudioStreamInfo::GetCodecString(AudioCodec codec,
return "dts+"; return "dts+";
case kCodecDTSM: case kCodecDTSM:
return "dts-"; return "dts-";
case kCodecAC3:
return "ac-3";
default: default:
NOTIMPLEMENTED() << "Codec: " << codec; NOTIMPLEMENTED() << "Codec: " << codec;
return "unknown"; return "unknown";

View File

@ -17,24 +17,15 @@ namespace media {
enum AudioCodec { enum AudioCodec {
kUnknownAudioCodec = 0, kUnknownAudioCodec = 0,
kCodecAAC, kCodecAAC,
kCodecMP3, kCodecAC3,
kCodecPCM,
kCodecVorbis,
kCodecFLAC,
kCodecAMR_NB,
kCodecAMR_WB,
kCodecPCM_MULAW,
kCodecGSM_MS,
kCodecPCM_S16BE,
kCodecPCM_S24BE,
kCodecOpus,
kCodecEAC3,
kCodecDTSC, kCodecDTSC,
kCodecDTSE,
kCodecDTSH, kCodecDTSH,
kCodecDTSL, kCodecDTSL,
kCodecDTSE,
kCodecDTSP,
kCodecDTSM, kCodecDTSM,
kCodecDTSP,
kCodecOpus,
kCodecVorbis,
kNumAudioCodec kNumAudioCodec
}; };

View File

@ -134,7 +134,8 @@ bool FileType::ReadWriteInternal(BoxBuffer* buffer) {
buffer->ReadWriteUInt32(&minor_version)); buffer->ReadWriteUInt32(&minor_version));
size_t num_brands; size_t num_brands;
if (buffer->Reading()) { if (buffer->Reading()) {
num_brands = (buffer->Size() - buffer->Pos()) / sizeof(FourCC); RCHECK(buffer->BytesLeft() % sizeof(FourCC) == 0);
num_brands = buffer->BytesLeft() / sizeof(FourCC);
compatible_brands.resize(num_brands); compatible_brands.resize(num_brands);
} else { } else {
num_brands = compatible_brands.size(); num_brands = compatible_brands.size();
@ -318,8 +319,8 @@ bool SampleEncryption::ReadWriteInternal(BoxBuffer* buffer) {
// If we don't know |iv_size|, store sample encryption data to parse later // If we don't know |iv_size|, store sample encryption data to parse later
// after we know iv_size. // after we know iv_size.
if (buffer->Reading() && iv_size == 0) { if (buffer->Reading() && iv_size == 0) {
RCHECK(buffer->ReadWriteVector(&sample_encryption_data, RCHECK(
buffer->Size() - buffer->Pos())); buffer->ReadWriteVector(&sample_encryption_data, buffer->BytesLeft()));
return true; return true;
} }
@ -1395,7 +1396,7 @@ bool DTSSpecific::ReadWriteInternal(BoxBuffer* buffer) {
buffer->ReadWriteUInt8(&pcm_sample_depth)); buffer->ReadWriteUInt8(&pcm_sample_depth));
if (buffer->Reading()) { if (buffer->Reading()) {
RCHECK(buffer->ReadWriteVector(&extra_data, buffer->Size() - buffer->Pos())); RCHECK(buffer->ReadWriteVector(&extra_data, buffer->BytesLeft()));
} else { } else {
if (extra_data.empty()) { if (extra_data.empty()) {
extra_data.assign(kDdtsExtraData, extra_data.assign(kDdtsExtraData,
@ -1415,6 +1416,25 @@ uint32_t DTSSpecific::ComputeSizeInternal() {
sizeof(kDdtsExtraData); sizeof(kDdtsExtraData);
} }
AC3Specific::AC3Specific() {}
AC3Specific::~AC3Specific() {}
FourCC AC3Specific::BoxType() const { return FOURCC_DAC3; }
bool AC3Specific::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteVector(
&data, buffer->Reading() ? buffer->BytesLeft() : data.size()));
return true;
}
uint32_t AC3Specific::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),
@ -1468,15 +1488,16 @@ 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));
return true; return true;
} }
uint32_t AudioSampleEntry::ComputeSizeInternal() { 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() + 6 + esds.ComputeSize() + ddts.ComputeSize() + dac3.ComputeSize() +
8 + // 6 + 8 bytes reserved. 6 + 8 + // 6 + 8 bytes reserved.
4; // 4 bytes predefined. 4; // 4 bytes predefined.
} }
WebVTTConfigurationBox::WebVTTConfigurationBox() {} WebVTTConfigurationBox::WebVTTConfigurationBox() {}
@ -1623,7 +1644,7 @@ FourCC DataEntryUrl::BoxType() const { return FOURCC_URL; }
bool DataEntryUrl::ReadWriteInternal(BoxBuffer* buffer) { bool DataEntryUrl::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer)); RCHECK(ReadWriteHeaderInternal(buffer));
if (buffer->Reading()) { if (buffer->Reading()) {
RCHECK(buffer->ReadWriteVector(&location, buffer->Size() - buffer->Pos())); RCHECK(buffer->ReadWriteVector(&location, buffer->BytesLeft()));
} else { } else {
RCHECK(buffer->ReadWriteVector(&location, location.size())); RCHECK(buffer->ReadWriteVector(&location, location.size()));
} }

View File

@ -309,6 +309,12 @@ struct DTSSpecific : Box {
std::vector<uint8_t> extra_data; std::vector<uint8_t> extra_data;
}; };
struct AC3Specific : Box {
DECLARE_BOX_METHODS(AC3Specific);
std::vector<uint8_t> data;
};
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.
@ -325,6 +331,7 @@ struct AudioSampleEntry : Box {
ProtectionSchemeInfo sinf; ProtectionSchemeInfo sinf;
ElementaryStreamDescriptor esds; ElementaryStreamDescriptor esds;
DTSSpecific ddts; DTSSpecific ddts;
AC3Specific dac3;
}; };
struct WebVTTConfigurationBox : Box { struct WebVTTConfigurationBox : Box {

View File

@ -240,14 +240,19 @@ inline bool operator==(const DTSSpecific& lhs,
lhs.extra_data == rhs.extra_data; lhs.extra_data == rhs.extra_data;
} }
inline bool operator==(const AC3Specific& lhs,
const AC3Specific& 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 &&
lhs.data_reference_index == rhs.data_reference_index && lhs.data_reference_index == rhs.data_reference_index &&
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.sinf == rhs.sinf && lhs.esds == rhs.esds && lhs.ddts == rhs.ddts &&
lhs.ddts == rhs.ddts; lhs.dac3 == rhs.dac3;
} }
inline bool operator==(const WebVTTConfigurationBox& lhs, inline bool operator==(const WebVTTConfigurationBox& lhs,

View File

@ -380,6 +380,16 @@ class BoxDefinitionsTestGeneral : public testing::Test {
ddts->pcm_sample_depth = 24; ddts->pcm_sample_depth = 24;
} }
void Fill(AC3Specific* dac3) {
const uint8_t kAc3Data[] = {0x50, 0x11, 0x60};
dac3->data.assign(kAc3Data, kAc3Data + arraysize(kAc3Data));
}
void Modify(AC3Specific* dac3) {
const uint8_t kAc3Data[] = {0x50, 0x11, 0x40};
dac3->data.assign(kAc3Data, kAc3Data + arraysize(kAc3Data));
}
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;
@ -884,6 +894,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
bool IsOptional(const CodecConfigurationRecord* box) { return true; } bool IsOptional(const CodecConfigurationRecord* box) { return true; }
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; }
// 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; }
@ -926,6 +937,7 @@ typedef testing::Types<FileType,
VideoSampleEntry, VideoSampleEntry,
ElementaryStreamDescriptor, ElementaryStreamDescriptor,
DTSSpecific, DTSSpecific,
AC3Specific,
AudioSampleEntry, AudioSampleEntry,
WebVTTConfigurationBox, WebVTTConfigurationBox,
WebVTTSourceLabelBox, WebVTTSourceLabelBox,
@ -1072,6 +1084,21 @@ TEST_F(BoxDefinitionsTest, DTSSampleEntry) {
ASSERT_EQ(entry, entry_readback); ASSERT_EQ(entry, entry_readback);
} }
TEST_F(BoxDefinitionsTest, AC3SampleEntry) {
AudioSampleEntry entry;
entry.format = FOURCC_AC3;
entry.data_reference_index = 2;
entry.channelcount = 5;
entry.samplesize = 16;
entry.samplerate = 44100;
Fill(&entry.dac3);
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

@ -16,12 +16,14 @@ enum FourCC {
FOURCC_NULL = 0, FOURCC_NULL = 0,
FOURCC_ID32 = 0x49443332, FOURCC_ID32 = 0x49443332,
FOURCC_PRIV = 0x50524956, FOURCC_PRIV = 0x50524956,
FOURCC_AC3 = 0x61632d33, // This fourcc is "ac-3".
FOURCC_AVC1 = 0x61766331, FOURCC_AVC1 = 0x61766331,
FOURCC_AVCC = 0x61766343, FOURCC_AVCC = 0x61766343,
FOURCC_BLOC = 0x626C6F63, FOURCC_BLOC = 0x626C6F63,
FOURCC_CENC = 0x63656e63, FOURCC_CENC = 0x63656e63,
FOURCC_CO64 = 0x636f3634, FOURCC_CO64 = 0x636f3634,
FOURCC_CTTS = 0x63747473, FOURCC_CTTS = 0x63747473,
FOURCC_DAC3 = 0x64616333,
FOURCC_DASH = 0x64617368, FOURCC_DASH = 0x64617368,
FOURCC_DDTS = 0x64647473, FOURCC_DDTS = 0x64647473,
FOURCC_DINF = 0x64696e66, FOURCC_DINF = 0x64696e66,
@ -32,7 +34,6 @@ enum FourCC {
FOURCC_DTSL = 0x6474736c, FOURCC_DTSL = 0x6474736c,
FOURCC_DTSM = 0x6474732d, FOURCC_DTSM = 0x6474732d,
FOURCC_DTSP = 0x6474732b, FOURCC_DTSP = 0x6474732b,
FOURCC_EAC3 = 0x65632d33,
FOURCC_EDTS = 0x65647473, FOURCC_EDTS = 0x65647473,
FOURCC_ELST = 0x656c7374, FOURCC_ELST = 0x656c7374,
FOURCC_ENCA = 0x656e6361, FOURCC_ENCA = 0x656e6361,

View File

@ -74,8 +74,8 @@ AudioCodec FourCCToAudioCodec(FourCC fourcc) {
return kCodecDTSP; return kCodecDTSP;
case FOURCC_DTSM: case FOURCC_DTSM:
return kCodecDTSM; return kCodecDTSM;
case FOURCC_EAC3: case FOURCC_AC3:
return kCodecEAC3; return kCodecAC3;
default: default:
return kUnknownAudioCodec; return kUnknownAudioCodec;
} }
@ -410,7 +410,8 @@ 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: case FOURCC_AC3:
extra_data = entry.dac3.data;
num_channels = entry.channelcount; num_channels = entry.channelcount;
sampling_frequency = entry.samplerate; sampling_frequency = entry.samplerate;
break; break;

View File

@ -63,6 +63,8 @@ FourCC AudioCodecToFourCC(AudioCodec codec) {
switch (codec) { switch (codec) {
case kCodecAAC: case kCodecAAC:
return FOURCC_MP4A; return FOURCC_MP4A;
case kCodecAC3:
return FOURCC_AC3;
case kCodecDTSC: case kCodecDTSC:
return FOURCC_DTSC; return FOURCC_DTSC;
case kCodecDTSH: case kCodecDTSH:
@ -261,6 +263,9 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
audio.ddts.sampling_frequency = audio_info->sampling_frequency(); audio.ddts.sampling_frequency = audio_info->sampling_frequency();
audio.ddts.pcm_sample_depth = audio_info->sample_bits(); audio.ddts.pcm_sample_depth = audio_info->sample_bits();
break; break;
case kCodecAC3:
audio.dac3.data = audio_info->extra_data();
break;
default: default:
NOTIMPLEMENTED(); NOTIMPLEMENTED();
break; break;