Support DTS audio in MP4 - Part 1
Issue #52 Change-Id: I5061e2a674d5c1b039a49729e53d4d15a4120a5c
This commit is contained in:
parent
329afd0ade
commit
2806dffb47
|
@ -43,6 +43,18 @@ std::string AudioCodecToString(AudioCodec audio_codec) {
|
||||||
return "Opus";
|
return "Opus";
|
||||||
case kCodecEAC3:
|
case kCodecEAC3:
|
||||||
return "EAC3";
|
return "EAC3";
|
||||||
|
case kCodecDTSC:
|
||||||
|
return "DTSC";
|
||||||
|
case kCodecDTSH:
|
||||||
|
return "DTSH";
|
||||||
|
case kCodecDTSL:
|
||||||
|
return "DTSL";
|
||||||
|
case kCodecDTSE:
|
||||||
|
return "DTSE";
|
||||||
|
case kCodecDTSP:
|
||||||
|
return "DTS+";
|
||||||
|
case kCodecDTSM:
|
||||||
|
return "DTS-";
|
||||||
default:
|
default:
|
||||||
NOTIMPLEMENTED() << "Unknown Audio Codec: " << audio_codec;
|
NOTIMPLEMENTED() << "Unknown Audio Codec: " << audio_codec;
|
||||||
return "UnknownAudioCodec";
|
return "UnknownAudioCodec";
|
||||||
|
@ -104,6 +116,18 @@ std::string AudioStreamInfo::GetCodecString(AudioCodec codec,
|
||||||
return "opus";
|
return "opus";
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
return "mp4a.40." + base::UintToString(audio_object_type);
|
return "mp4a.40." + base::UintToString(audio_object_type);
|
||||||
|
case kCodecDTSC:
|
||||||
|
return "dtsc";
|
||||||
|
case kCodecDTSH:
|
||||||
|
return "dtsh";
|
||||||
|
case kCodecDTSL:
|
||||||
|
return "dtsl";
|
||||||
|
case kCodecDTSE:
|
||||||
|
return "dtse";
|
||||||
|
case kCodecDTSP:
|
||||||
|
return "dts+";
|
||||||
|
case kCodecDTSM:
|
||||||
|
return "dts-";
|
||||||
default:
|
default:
|
||||||
NOTIMPLEMENTED() << "Codec: " << codec;
|
NOTIMPLEMENTED() << "Codec: " << codec;
|
||||||
return "unknown";
|
return "unknown";
|
||||||
|
|
|
@ -29,6 +29,12 @@ enum AudioCodec {
|
||||||
kCodecPCM_S24BE,
|
kCodecPCM_S24BE,
|
||||||
kCodecOpus,
|
kCodecOpus,
|
||||||
kCodecEAC3,
|
kCodecEAC3,
|
||||||
|
kCodecDTSC,
|
||||||
|
kCodecDTSH,
|
||||||
|
kCodecDTSL,
|
||||||
|
kCodecDTSE,
|
||||||
|
kCodecDTSP,
|
||||||
|
kCodecDTSM,
|
||||||
|
|
||||||
kNumAudioCodec
|
kNumAudioCodec
|
||||||
};
|
};
|
||||||
|
|
|
@ -1065,6 +1065,30 @@ uint32_t ElementaryStreamDescriptor::ComputeSize() {
|
||||||
return atom_size;
|
return atom_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DTSSpecificBox::DTSSpecificBox() {}
|
||||||
|
DTSSpecificBox::~DTSSpecificBox() {}
|
||||||
|
FourCC DTSSpecificBox::BoxType() const { return FOURCC_DDTS; }
|
||||||
|
|
||||||
|
bool DTSSpecificBox::ReadWrite(BoxBuffer* buffer) {
|
||||||
|
RCHECK(Box::ReadWrite(buffer));
|
||||||
|
|
||||||
|
if (buffer->Reading()) {
|
||||||
|
RCHECK(
|
||||||
|
buffer->ReadWriteVector(&data, buffer->Size() - buffer->Pos()));
|
||||||
|
} else {
|
||||||
|
RCHECK(buffer->ReadWriteVector(&data, data.size()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DTSSpecificBox::ComputeSize() {
|
||||||
|
// This box is optional. Skip it if not initialized.
|
||||||
|
atom_size = 0;
|
||||||
|
if (data.size() > 0)
|
||||||
|
atom_size = kBoxSize + data.size();
|
||||||
|
return atom_size;
|
||||||
|
}
|
||||||
|
|
||||||
AudioSampleEntry::AudioSampleEntry()
|
AudioSampleEntry::AudioSampleEntry()
|
||||||
: format(FOURCC_NULL),
|
: format(FOURCC_NULL),
|
||||||
data_reference_index(1),
|
data_reference_index(1),
|
||||||
|
@ -1115,16 +1139,18 @@ bool AudioSampleEntry::ReadWrite(BoxBuffer* buffer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ESDS is not valid in case of EAC3.
|
|
||||||
RCHECK(buffer->TryReadWriteChild(&esds));
|
RCHECK(buffer->TryReadWriteChild(&esds));
|
||||||
|
RCHECK(buffer->TryReadWriteChild(&ddts));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AudioSampleEntry::ComputeSize() {
|
uint32_t AudioSampleEntry::ComputeSize() {
|
||||||
atom_size = kBoxSize + sizeof(data_reference_index) + sizeof(channelcount) +
|
atom_size = kBoxSize + sizeof(data_reference_index) + sizeof(channelcount) +
|
||||||
sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() +
|
sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() +
|
||||||
esds.ComputeSize() + 6 + 8 + // 6 + 8 bytes reserved.
|
esds.ComputeSize() + ddts.ComputeSize() +
|
||||||
4; // 4 bytes predefined.
|
6 + 8 + // 6 + 8 bytes reserved.
|
||||||
|
4; // 4 bytes predefined.
|
||||||
return atom_size;
|
return atom_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,12 @@ struct ElementaryStreamDescriptor : FullBox {
|
||||||
ESDescriptor es_descriptor;
|
ESDescriptor es_descriptor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DTSSpecificBox : Box {
|
||||||
|
DECLARE_BOX_METHODS(DTSSpecificBox);
|
||||||
|
|
||||||
|
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.
|
||||||
|
@ -218,6 +224,7 @@ struct AudioSampleEntry : Box {
|
||||||
|
|
||||||
ProtectionSchemeInfo sinf;
|
ProtectionSchemeInfo sinf;
|
||||||
ElementaryStreamDescriptor esds;
|
ElementaryStreamDescriptor esds;
|
||||||
|
DTSSpecificBox ddts;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SampleDescription : FullBox {
|
struct SampleDescription : FullBox {
|
||||||
|
|
|
@ -194,13 +194,19 @@ inline bool operator==(const ElementaryStreamDescriptor& lhs,
|
||||||
return lhs.es_descriptor == rhs.es_descriptor;
|
return lhs.es_descriptor == rhs.es_descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const DTSSpecificBox& lhs,
|
||||||
|
const DTSSpecificBox& 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const MediaHeader& lhs, const MediaHeader& rhs) {
|
inline bool operator==(const MediaHeader& lhs, const MediaHeader& rhs) {
|
||||||
|
|
|
@ -854,6 +854,25 @@ INSTANTIATE_TYPED_TEST_CASE_P(BoxDefinitionTypedTests2,
|
||||||
// Test other cases of box input.
|
// Test other cases of box input.
|
||||||
class BoxDefinitionsTest : public BoxDefinitionsTestGeneral<Box> {};
|
class BoxDefinitionsTest : public BoxDefinitionsTestGeneral<Box> {};
|
||||||
|
|
||||||
|
TEST_F(BoxDefinitionsTest, DTSSampleEntry) {
|
||||||
|
const uint8_t kDtseData[] = {0x00, 0x00, 0x00, 0x1c, 0x64, 0x64, 0x74,
|
||||||
|
0x73, 0x00, 0x00, 0xbb, 0x80, 0x00, 0x03,
|
||||||
|
0xe4, 0x18, 0x00, 0x03, 0xe4, 0x18, 0x18,
|
||||||
|
0xe4, 0x7c, 0x00, 0x04, 0x00, 0x0f, 0x00};
|
||||||
|
AudioSampleEntry entry;
|
||||||
|
entry.format = FOURCC_DTSE;
|
||||||
|
entry.data_reference_index = 2;
|
||||||
|
entry.channelcount = 5;
|
||||||
|
entry.samplesize = 16;
|
||||||
|
entry.samplerate = 44100;
|
||||||
|
entry.ddts.data.assign(kDtseData, kDtseData + arraysize(kDtseData));
|
||||||
|
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);
|
||||||
|
|
|
@ -24,7 +24,6 @@ enum ObjectType {
|
||||||
kForbidden = 0,
|
kForbidden = 0,
|
||||||
kISO_14496_3 = 0x40, // MPEG4 AAC
|
kISO_14496_3 = 0x40, // MPEG4 AAC
|
||||||
kISO_13818_7_AAC_LC = 0x67, // MPEG2 AAC-LC
|
kISO_13818_7_AAC_LC = 0x67, // MPEG2 AAC-LC
|
||||||
kEAC3 = 0xa6 // Dolby Digital Plus
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This class parses object type and decoder specific information from an
|
/// This class parses object type and decoder specific information from an
|
||||||
|
|
|
@ -20,8 +20,15 @@ enum FourCC {
|
||||||
FOURCC_CO64 = 0x636f3634,
|
FOURCC_CO64 = 0x636f3634,
|
||||||
FOURCC_CTTS = 0x63747473,
|
FOURCC_CTTS = 0x63747473,
|
||||||
FOURCC_DASH = 0x64617368,
|
FOURCC_DASH = 0x64617368,
|
||||||
|
FOURCC_DDTS = 0x64647473,
|
||||||
FOURCC_DINF = 0x64696e66,
|
FOURCC_DINF = 0x64696e66,
|
||||||
FOURCC_DREF = 0x64726566,
|
FOURCC_DREF = 0x64726566,
|
||||||
|
FOURCC_DTSC = 0x64747363,
|
||||||
|
FOURCC_DTSE = 0x64747365,
|
||||||
|
FOURCC_DTSH = 0x64747368,
|
||||||
|
FOURCC_DTSL = 0x6474736c,
|
||||||
|
FOURCC_DTSM = 0x6474732d,
|
||||||
|
FOURCC_DTSP = 0x6474732b,
|
||||||
FOURCC_EAC3 = 0x65632d33,
|
FOURCC_EAC3 = 0x65632d33,
|
||||||
FOURCC_EDTS = 0x65647473,
|
FOURCC_EDTS = 0x65647473,
|
||||||
FOURCC_ELST = 0x656c7374,
|
FOURCC_ELST = 0x656c7374,
|
||||||
|
|
|
@ -40,7 +40,7 @@ uint64_t Rescale(uint64_t time_in_old_scale,
|
||||||
return (static_cast<double>(time_in_old_scale) / old_scale) * new_scale;
|
return (static_cast<double>(time_in_old_scale) / old_scale) * new_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCodec FourCCToCodec(FourCC fourcc) {
|
VideoCodec FourCCToVideoCodec(FourCC fourcc) {
|
||||||
switch (fourcc) {
|
switch (fourcc) {
|
||||||
case FOURCC_AVC1:
|
case FOURCC_AVC1:
|
||||||
return kCodecH264;
|
return kCodecH264;
|
||||||
|
@ -59,6 +59,27 @@ VideoCodec FourCCToCodec(FourCC fourcc) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioCodec FourCCToAudioCodec(FourCC fourcc) {
|
||||||
|
switch(fourcc) {
|
||||||
|
case FOURCC_DTSC:
|
||||||
|
return kCodecDTSC;
|
||||||
|
case FOURCC_DTSH:
|
||||||
|
return kCodecDTSH;
|
||||||
|
case FOURCC_DTSL:
|
||||||
|
return kCodecDTSL;
|
||||||
|
case FOURCC_DTSE:
|
||||||
|
return kCodecDTSE;
|
||||||
|
case FOURCC_DTSP:
|
||||||
|
return kCodecDTSP;
|
||||||
|
case FOURCC_DTSM:
|
||||||
|
return kCodecDTSM;
|
||||||
|
case FOURCC_EAC3:
|
||||||
|
return kCodecEAC3;
|
||||||
|
default:
|
||||||
|
return kUnknownAudioCodec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char kWidevineKeySystemId[] = "edef8ba979d64acea3c827dcd51d21ed";
|
const char kWidevineKeySystemId[] = "edef8ba979d64acea3c827dcd51d21ed";
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -310,45 +331,50 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
// description indices, so we fail gracefully in that case.
|
// description indices, so we fail gracefully in that case.
|
||||||
if (desc_idx >= samp_descr.audio_entries.size())
|
if (desc_idx >= samp_descr.audio_entries.size())
|
||||||
desc_idx = 0;
|
desc_idx = 0;
|
||||||
|
|
||||||
const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx];
|
const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx];
|
||||||
|
const FourCC actual_format = entry.GetActualFormat();
|
||||||
if (!(entry.format == FOURCC_MP4A || entry.format == FOURCC_EAC3 ||
|
AudioCodec codec = FourCCToAudioCodec(actual_format);
|
||||||
(entry.format == FOURCC_ENCA &&
|
|
||||||
entry.sinf.format.format == FOURCC_MP4A))) {
|
|
||||||
LOG(ERROR) << "Unsupported audio format 0x"
|
|
||||||
<< std::hex << entry.format << " in stsd box.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectType audio_type = entry.esds.es_descriptor.object_type();
|
|
||||||
DVLOG(1) << "audio_type " << std::hex << audio_type;
|
|
||||||
if (audio_type == kForbidden && entry.format == FOURCC_EAC3) {
|
|
||||||
audio_type = kEAC3;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioCodec codec = kUnknownAudioCodec;
|
|
||||||
uint8_t num_channels = 0;
|
uint8_t num_channels = 0;
|
||||||
uint32_t sampling_frequency = 0;
|
uint32_t sampling_frequency = 0;
|
||||||
uint8_t audio_object_type = 0;
|
uint8_t audio_object_type = 0;
|
||||||
std::vector<uint8_t> extra_data;
|
std::vector<uint8_t> extra_data;
|
||||||
// Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or
|
|
||||||
// supported MPEG2 AAC variants.
|
switch (actual_format) {
|
||||||
if (entry.esds.es_descriptor.IsAAC()) {
|
case FOURCC_MP4A:
|
||||||
codec = kCodecAAC;
|
// Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or
|
||||||
const AACAudioSpecificConfig& aac_audio_specific_config =
|
// supported MPEG2 AAC variants.
|
||||||
entry.esds.aac_audio_specific_config;
|
if (entry.esds.es_descriptor.IsAAC()) {
|
||||||
num_channels = aac_audio_specific_config.num_channels();
|
codec = kCodecAAC;
|
||||||
sampling_frequency = aac_audio_specific_config.frequency();
|
const AACAudioSpecificConfig& aac_audio_specific_config =
|
||||||
audio_object_type = aac_audio_specific_config.audio_object_type();
|
entry.esds.aac_audio_specific_config;
|
||||||
extra_data = entry.esds.es_descriptor.decoder_specific_info();
|
num_channels = aac_audio_specific_config.num_channels();
|
||||||
} else if (audio_type == kEAC3) {
|
sampling_frequency = aac_audio_specific_config.frequency();
|
||||||
codec = kCodecEAC3;
|
audio_object_type = aac_audio_specific_config.audio_object_type();
|
||||||
num_channels = entry.channelcount;
|
extra_data = entry.esds.es_descriptor.decoder_specific_info();
|
||||||
sampling_frequency = entry.samplerate;
|
break;
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Unsupported audio object type 0x"
|
LOG(ERROR) << "Unsupported audio format 0x" << std::hex
|
||||||
<< std::hex << audio_type << " in esds.";
|
<< actual_format << " in stsd box.";
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
case FOURCC_DTSC:
|
||||||
|
case FOURCC_DTSH:
|
||||||
|
case FOURCC_DTSL:
|
||||||
|
case FOURCC_DTSE:
|
||||||
|
case FOURCC_DTSM:
|
||||||
|
extra_data = entry.ddts.data;
|
||||||
|
num_channels = entry.channelcount;
|
||||||
|
sampling_frequency = entry.samplerate;
|
||||||
|
break;
|
||||||
|
case FOURCC_EAC3:
|
||||||
|
num_channels = entry.channelcount;
|
||||||
|
sampling_frequency = entry.samplerate;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Unsupported audio format 0x" << std::hex
|
||||||
|
<< actual_format << " in stsd box.";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted;
|
bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted;
|
||||||
|
@ -386,7 +412,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
uint8_t nalu_length_size = 0;
|
uint8_t nalu_length_size = 0;
|
||||||
|
|
||||||
const FourCC actual_format = entry.GetActualFormat();
|
const FourCC actual_format = entry.GetActualFormat();
|
||||||
const VideoCodec video_codec = FourCCToCodec(actual_format);
|
const VideoCodec video_codec = FourCCToVideoCodec(actual_format);
|
||||||
switch (actual_format) {
|
switch (actual_format) {
|
||||||
case FOURCC_AVC1: {
|
case FOURCC_AVC1: {
|
||||||
AVCDecoderConfiguration avc_config;
|
AVCDecoderConfiguration avc_config;
|
||||||
|
|
Loading…
Reference in New Issue