From 2806dffb47169f88c18e95f7af7b1be1535e2cbf Mon Sep 17 00:00:00 2001 From: Bei Li Date: Mon, 7 Dec 2015 11:56:07 -0800 Subject: [PATCH] Support DTS audio in MP4 - Part 1 Issue #52 Change-Id: I5061e2a674d5c1b039a49729e53d4d15a4120a5c --- packager/media/base/audio_stream_info.cc | 24 +++++ packager/media/base/audio_stream_info.h | 6 ++ packager/media/formats/mp4/box_definitions.cc | 32 +++++- packager/media/formats/mp4/box_definitions.h | 7 ++ .../formats/mp4/box_definitions_comparison.h | 8 +- .../formats/mp4/box_definitions_unittest.cc | 19 ++++ packager/media/formats/mp4/es_descriptor.h | 1 - packager/media/formats/mp4/fourccs.h | 7 ++ .../media/formats/mp4/mp4_media_parser.cc | 98 ++++++++++++------- 9 files changed, 161 insertions(+), 41 deletions(-) diff --git a/packager/media/base/audio_stream_info.cc b/packager/media/base/audio_stream_info.cc index 3f9d50618c..e8c7b0ede2 100644 --- a/packager/media/base/audio_stream_info.cc +++ b/packager/media/base/audio_stream_info.cc @@ -43,6 +43,18 @@ std::string AudioCodecToString(AudioCodec audio_codec) { return "Opus"; case kCodecEAC3: 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: NOTIMPLEMENTED() << "Unknown Audio Codec: " << audio_codec; return "UnknownAudioCodec"; @@ -104,6 +116,18 @@ std::string AudioStreamInfo::GetCodecString(AudioCodec codec, return "opus"; case kCodecAAC: 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: NOTIMPLEMENTED() << "Codec: " << codec; return "unknown"; diff --git a/packager/media/base/audio_stream_info.h b/packager/media/base/audio_stream_info.h index 10c7964361..71e3d79995 100644 --- a/packager/media/base/audio_stream_info.h +++ b/packager/media/base/audio_stream_info.h @@ -29,6 +29,12 @@ enum AudioCodec { kCodecPCM_S24BE, kCodecOpus, kCodecEAC3, + kCodecDTSC, + kCodecDTSH, + kCodecDTSL, + kCodecDTSE, + kCodecDTSP, + kCodecDTSM, kNumAudioCodec }; diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 62acd32148..bcf752f48e 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -1065,6 +1065,30 @@ uint32_t ElementaryStreamDescriptor::ComputeSize() { 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() : format(FOURCC_NULL), 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(&ddts)); + return true; } uint32_t AudioSampleEntry::ComputeSize() { atom_size = kBoxSize + sizeof(data_reference_index) + sizeof(channelcount) + sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() + - esds.ComputeSize() + 6 + 8 + // 6 + 8 bytes reserved. - 4; // 4 bytes predefined. + esds.ComputeSize() + ddts.ComputeSize() + + 6 + 8 + // 6 + 8 bytes reserved. + 4; // 4 bytes predefined. return atom_size; } diff --git a/packager/media/formats/mp4/box_definitions.h b/packager/media/formats/mp4/box_definitions.h index 652a6d94d1..dca7676186 100644 --- a/packager/media/formats/mp4/box_definitions.h +++ b/packager/media/formats/mp4/box_definitions.h @@ -203,6 +203,12 @@ struct ElementaryStreamDescriptor : FullBox { ESDescriptor es_descriptor; }; +struct DTSSpecificBox : Box { + DECLARE_BOX_METHODS(DTSSpecificBox); + + std::vector data; +}; + struct AudioSampleEntry : Box { DECLARE_BOX_METHODS(AudioSampleEntry); // Returns actual format of this sample entry. @@ -218,6 +224,7 @@ struct AudioSampleEntry : Box { ProtectionSchemeInfo sinf; ElementaryStreamDescriptor esds; + DTSSpecificBox ddts; }; struct SampleDescription : FullBox { diff --git a/packager/media/formats/mp4/box_definitions_comparison.h b/packager/media/formats/mp4/box_definitions_comparison.h index 8ff2813f82..15e474f97f 100644 --- a/packager/media/formats/mp4/box_definitions_comparison.h +++ b/packager/media/formats/mp4/box_definitions_comparison.h @@ -194,13 +194,19 @@ inline bool operator==(const ElementaryStreamDescriptor& lhs, 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, const AudioSampleEntry& rhs) { return lhs.format == rhs.format && lhs.data_reference_index == rhs.data_reference_index && lhs.channelcount == rhs.channelcount && 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) { diff --git a/packager/media/formats/mp4/box_definitions_unittest.cc b/packager/media/formats/mp4/box_definitions_unittest.cc index 4ad162691f..6641be131d 100644 --- a/packager/media/formats/mp4/box_definitions_unittest.cc +++ b/packager/media/formats/mp4/box_definitions_unittest.cc @@ -854,6 +854,25 @@ INSTANTIATE_TYPED_TEST_CASE_P(BoxDefinitionTypedTests2, // Test other cases of box input. class BoxDefinitionsTest : public BoxDefinitionsTestGeneral {}; +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) { ProtectionSystemSpecificHeader pssh; Fill(&pssh); diff --git a/packager/media/formats/mp4/es_descriptor.h b/packager/media/formats/mp4/es_descriptor.h index 04efc4e8a5..a29479a4cf 100644 --- a/packager/media/formats/mp4/es_descriptor.h +++ b/packager/media/formats/mp4/es_descriptor.h @@ -24,7 +24,6 @@ enum ObjectType { kForbidden = 0, kISO_14496_3 = 0x40, // MPEG4 AAC 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 diff --git a/packager/media/formats/mp4/fourccs.h b/packager/media/formats/mp4/fourccs.h index 19b603cd57..8f399281a0 100644 --- a/packager/media/formats/mp4/fourccs.h +++ b/packager/media/formats/mp4/fourccs.h @@ -20,8 +20,15 @@ enum FourCC { FOURCC_CO64 = 0x636f3634, FOURCC_CTTS = 0x63747473, FOURCC_DASH = 0x64617368, + FOURCC_DDTS = 0x64647473, FOURCC_DINF = 0x64696e66, 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_EDTS = 0x65647473, FOURCC_ELST = 0x656c7374, diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index 98d85f1f2f..16ca16bfbe 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -40,7 +40,7 @@ uint64_t Rescale(uint64_t time_in_old_scale, return (static_cast(time_in_old_scale) / old_scale) * new_scale; } -VideoCodec FourCCToCodec(FourCC fourcc) { +VideoCodec FourCCToVideoCodec(FourCC fourcc) { switch (fourcc) { case FOURCC_AVC1: 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"; } // namespace @@ -310,45 +331,50 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { // description indices, so we fail gracefully in that case. if (desc_idx >= samp_descr.audio_entries.size()) desc_idx = 0; + const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx]; - - if (!(entry.format == FOURCC_MP4A || entry.format == FOURCC_EAC3 || - (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; + const FourCC actual_format = entry.GetActualFormat(); + AudioCodec codec = FourCCToAudioCodec(actual_format); uint8_t num_channels = 0; uint32_t sampling_frequency = 0; uint8_t audio_object_type = 0; std::vector extra_data; - // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or - // supported MPEG2 AAC variants. - if (entry.esds.es_descriptor.IsAAC()) { - codec = kCodecAAC; - const AACAudioSpecificConfig& aac_audio_specific_config = - entry.esds.aac_audio_specific_config; - num_channels = aac_audio_specific_config.num_channels(); - sampling_frequency = aac_audio_specific_config.frequency(); - audio_object_type = aac_audio_specific_config.audio_object_type(); - extra_data = entry.esds.es_descriptor.decoder_specific_info(); - } else if (audio_type == kEAC3) { - codec = kCodecEAC3; - num_channels = entry.channelcount; - sampling_frequency = entry.samplerate; - } else { - LOG(ERROR) << "Unsupported audio object type 0x" - << std::hex << audio_type << " in esds."; - return false; + + switch (actual_format) { + case FOURCC_MP4A: + // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or + // supported MPEG2 AAC variants. + if (entry.esds.es_descriptor.IsAAC()) { + codec = kCodecAAC; + const AACAudioSpecificConfig& aac_audio_specific_config = + entry.esds.aac_audio_specific_config; + num_channels = aac_audio_specific_config.num_channels(); + sampling_frequency = aac_audio_specific_config.frequency(); + audio_object_type = aac_audio_specific_config.audio_object_type(); + extra_data = entry.esds.es_descriptor.decoder_specific_info(); + break; + } else { + LOG(ERROR) << "Unsupported audio format 0x" << std::hex + << actual_format << " in stsd box."; + 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; @@ -386,7 +412,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { uint8_t nalu_length_size = 0; const FourCC actual_format = entry.GetActualFormat(); - const VideoCodec video_codec = FourCCToCodec(actual_format); + const VideoCodec video_codec = FourCCToVideoCodec(actual_format); switch (actual_format) { case FOURCC_AVC1: { AVCDecoderConfiguration avc_config;