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:
parent
d1d75f477c
commit
8563db4cff
|
@ -33,6 +33,8 @@ std::string AudioCodecToString(AudioCodec audio_codec) {
|
|||
return "DTS-";
|
||||
case kCodecDTSP:
|
||||
return "DTS+";
|
||||
case kCodecEAC3:
|
||||
return "EAC3";
|
||||
case kCodecOpus:
|
||||
return "Opus";
|
||||
case kCodecVorbis:
|
||||
|
@ -115,6 +117,8 @@ std::string AudioStreamInfo::GetCodecString(AudioCodec codec,
|
|||
return "dts-";
|
||||
case kCodecAC3:
|
||||
return "ac-3";
|
||||
case kCodecEAC3:
|
||||
return "ec-3";
|
||||
default:
|
||||
NOTIMPLEMENTED() << "Codec: " << codec;
|
||||
return "unknown";
|
||||
|
|
|
@ -24,6 +24,7 @@ enum AudioCodec {
|
|||
kCodecDTSL,
|
||||
kCodecDTSM,
|
||||
kCodecDTSP,
|
||||
kCodecEAC3,
|
||||
kCodecOpus,
|
||||
kCodecVorbis,
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "packager/base/logging.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/rcheck.h"
|
||||
|
||||
|
@ -1435,6 +1436,54 @@ uint32_t AC3Specific::ComputeSizeInternal() {
|
|||
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()
|
||||
: format(FOURCC_NULL),
|
||||
data_reference_index(1),
|
||||
|
@ -1489,6 +1538,7 @@ bool AudioSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
RCHECK(buffer->TryReadWriteChild(&esds));
|
||||
RCHECK(buffer->TryReadWriteChild(&ddts));
|
||||
RCHECK(buffer->TryReadWriteChild(&dac3));
|
||||
RCHECK(buffer->TryReadWriteChild(&dec3));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1496,6 +1546,7 @@ uint32_t AudioSampleEntry::ComputeSizeInternal() {
|
|||
return HeaderSize() + sizeof(data_reference_index) + sizeof(channelcount) +
|
||||
sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() +
|
||||
esds.ComputeSize() + ddts.ComputeSize() + dac3.ComputeSize() +
|
||||
dec3.ComputeSize() +
|
||||
6 + 8 + // 6 + 8 bytes reserved.
|
||||
4; // 4 bytes predefined.
|
||||
}
|
||||
|
|
|
@ -315,6 +315,34 @@ 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 {
|
||||
DECLARE_BOX_METHODS(AudioSampleEntry);
|
||||
// Returns actual format of this sample entry.
|
||||
|
@ -329,9 +357,11 @@ struct AudioSampleEntry : Box {
|
|||
uint32_t samplerate;
|
||||
|
||||
ProtectionSchemeInfo sinf;
|
||||
|
||||
ElementaryStreamDescriptor esds;
|
||||
DTSSpecific ddts;
|
||||
AC3Specific dac3;
|
||||
EC3Specific dec3;
|
||||
};
|
||||
|
||||
struct WebVTTConfigurationBox : Box {
|
||||
|
|
|
@ -245,6 +245,11 @@ inline bool operator==(const AC3Specific& lhs,
|
|||
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,
|
||||
const AudioSampleEntry& rhs) {
|
||||
return lhs.format == rhs.format &&
|
||||
|
@ -252,7 +257,7 @@ inline bool operator==(const AudioSampleEntry& lhs,
|
|||
lhs.channelcount == rhs.channelcount &&
|
||||
lhs.samplesize == rhs.samplesize && lhs.samplerate == rhs.samplerate &&
|
||||
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,
|
||||
|
|
|
@ -390,6 +390,16 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
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) {
|
||||
enca->format = FOURCC_ENCA;
|
||||
enca->data_reference_index = 2;
|
||||
|
@ -895,6 +905,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
bool IsOptional(const PixelAspectRatio* box) { return true; }
|
||||
bool IsOptional(const ElementaryStreamDescriptor* box) { return true; }
|
||||
bool IsOptional(const AC3Specific* box) { return true; }
|
||||
bool IsOptional(const EC3Specific* box) { return true; }
|
||||
// Recommended, but optional.
|
||||
bool IsOptional(const WebVTTSourceLabelBox* box) { return true; }
|
||||
bool IsOptional(const CompositionTimeToSample* box) { return true; }
|
||||
|
@ -938,6 +949,7 @@ typedef testing::Types<FileType,
|
|||
ElementaryStreamDescriptor,
|
||||
DTSSpecific,
|
||||
AC3Specific,
|
||||
EC3Specific,
|
||||
AudioSampleEntry,
|
||||
WebVTTConfigurationBox,
|
||||
WebVTTSourceLabelBox,
|
||||
|
@ -1078,7 +1090,6 @@ TEST_F(BoxDefinitionsTest, DTSSampleEntry) {
|
|||
entry.samplerate = 44100;
|
||||
Fill(&entry.ddts);
|
||||
entry.Write(this->buffer_.get());
|
||||
|
||||
AudioSampleEntry entry_readback;
|
||||
ASSERT_TRUE(ReadBack(&entry_readback));
|
||||
ASSERT_EQ(entry, entry_readback);
|
||||
|
@ -1099,6 +1110,21 @@ TEST_F(BoxDefinitionsTest, AC3SampleEntry) {
|
|||
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) {
|
||||
ProtectionSystemSpecificHeader pssh;
|
||||
Fill(&pssh);
|
||||
|
|
|
@ -26,6 +26,7 @@ enum FourCC {
|
|||
FOURCC_DAC3 = 0x64616333,
|
||||
FOURCC_DASH = 0x64617368,
|
||||
FOURCC_DDTS = 0x64647473,
|
||||
FOURCC_DEC3 = 0x64656333,
|
||||
FOURCC_DINF = 0x64696e66,
|
||||
FOURCC_DREF = 0x64726566,
|
||||
FOURCC_DTSC = 0x64747363,
|
||||
|
@ -34,6 +35,7 @@ enum FourCC {
|
|||
FOURCC_DTSL = 0x6474736c,
|
||||
FOURCC_DTSM = 0x6474732d,
|
||||
FOURCC_DTSP = 0x6474732b,
|
||||
FOURCC_EAC3 = 0x65632d33, // This fourcc is "ec-3".
|
||||
FOURCC_EDTS = 0x65647473,
|
||||
FOURCC_ELST = 0x656c7374,
|
||||
FOURCC_ENCA = 0x656e6361,
|
||||
|
|
|
@ -76,6 +76,8 @@ AudioCodec FourCCToAudioCodec(FourCC fourcc) {
|
|||
return kCodecDTSM;
|
||||
case FOURCC_AC3:
|
||||
return kCodecAC3;
|
||||
case FOURCC_EAC3:
|
||||
return kCodecEAC3;
|
||||
default:
|
||||
return kUnknownAudioCodec;
|
||||
}
|
||||
|
@ -415,6 +417,11 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
num_channels = entry.channelcount;
|
||||
sampling_frequency = entry.samplerate;
|
||||
break;
|
||||
case FOURCC_EAC3:
|
||||
extra_data = entry.dec3.data;
|
||||
num_channels = entry.channelcount;
|
||||
sampling_frequency = entry.samplerate;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "Unsupported audio format 0x" << std::hex
|
||||
<< actual_format << " in stsd box.";
|
||||
|
|
|
@ -75,6 +75,8 @@ FourCC AudioCodecToFourCC(AudioCodec codec) {
|
|||
return FOURCC_DTSE;
|
||||
case kCodecDTSM:
|
||||
return FOURCC_DTSM;
|
||||
case kCodecEAC3:
|
||||
return FOURCC_EAC3;
|
||||
default:
|
||||
return FOURCC_NULL;
|
||||
}
|
||||
|
@ -266,6 +268,9 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
|||
case kCodecAC3:
|
||||
audio.dac3.data = audio_info->extra_data();
|
||||
break;
|
||||
case kCodecEAC3:
|
||||
audio.dec3.data = audio_info->extra_data();
|
||||
break;
|
||||
default:
|
||||
NOTIMPLEMENTED();
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue