diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 4b7e4ed31f..308064ebd1 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -1477,6 +1477,15 @@ class PackagerFunctionalTest(PackagerAppTest): self.assertPackageSuccess(streams, flags) self._CheckTestResults('flac-with-encryption', verify_decryption=True) + def testAlac(self): + streams = [ + self._GetStream('audio', test_file='bear-alac.mp4'), + ] + flags = self._GetFlags(output_dash=True) + + self.assertPackageSuccess(streams, flags) + self._CheckTestResults('audio-alac') + def testAv1Mp4WithEncryption(self): self.assertPackageSuccess( self._GetStreams(['video'], test_files=['bear-av1.mp4']), diff --git a/packager/app/test/testdata/audio-alac/bear-alac-audio.mp4 b/packager/app/test/testdata/audio-alac/bear-alac-audio.mp4 new file mode 100644 index 0000000000..2239643239 Binary files /dev/null and b/packager/app/test/testdata/audio-alac/bear-alac-audio.mp4 differ diff --git a/packager/app/test/testdata/audio-alac/output.mpd b/packager/app/test/testdata/audio-alac/output.mpd new file mode 100644 index 0000000000..7678c0fed6 --- /dev/null +++ b/packager/app/test/testdata/audio-alac/output.mpd @@ -0,0 +1,15 @@ + + + + + + + + bear-alac-audio.mp4 + + + + + + + diff --git a/packager/media/base/audio_stream_info.cc b/packager/media/base/audio_stream_info.cc index df38386950..bb1a6b1975 100644 --- a/packager/media/base/audio_stream_info.cc +++ b/packager/media/base/audio_stream_info.cc @@ -25,6 +25,8 @@ std::string AudioCodecToString(Codec codec) { return "AAC"; case kCodecAC3: return "AC3"; + case kCodecALAC: + return "ALAC"; case kCodecDTSC: return "DTSC"; case kCodecDTSE: @@ -138,6 +140,8 @@ std::string AudioStreamInfo::GetCodecString(Codec codec, return "mp4a.40." + absl::StrFormat("%hhu", audio_object_type); case kCodecAC3: return "ac-3"; + case kCodecALAC: + return "alac"; case kCodecDTSC: return "dtsc"; case kCodecDTSE: diff --git a/packager/media/base/fourccs.h b/packager/media/base/fourccs.h index b075d5a306..4c85c02c10 100644 --- a/packager/media/base/fourccs.h +++ b/packager/media/base/fourccs.h @@ -23,6 +23,7 @@ enum FourCC : uint32_t { FOURCC_ac_3 = 0x61632d33, // "ac-3" FOURCC_ac_4 = 0x61632d34, // "ac-4" FOURCC_ac3d = 0x61633364, + FOURCC_alac = 0x616c6163, FOURCC_apad = 0x61706164, FOURCC_av01 = 0x61763031, FOURCC_av1C = 0x61763143, diff --git a/packager/media/base/stream_info.h b/packager/media/base/stream_info.h index 355b032a54..1a40816673 100644 --- a/packager/media/base/stream_info.h +++ b/packager/media/base/stream_info.h @@ -41,6 +41,7 @@ enum Codec { kCodecAAC = kCodecAudio, kCodecAC3, kCodecAC4, + kCodecALAC, // TODO(kqyang): Use kCodecDTS and a kDtsStreamFormat for the various DTS // streams. kCodecDTSC, diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 6ce12b5500..68665fe7b6 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -1969,6 +1969,27 @@ size_t FlacSpecific::ComputeSizeInternal() { return HeaderSize() + data.size(); } +ALACSpecific::ALACSpecific() = default; +ALACSpecific::~ALACSpecific() = default; + +FourCC ALACSpecific::BoxType() const { + return FOURCC_alac; +} + +bool ALACSpecific::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + size_t size = buffer->Reading() ? buffer->BytesLeft() : data.size(); + RCHECK(buffer->ReadWriteVector(&data, size)); + return true; +} + +size_t ALACSpecific::ComputeSizeInternal() { + // This box is optional. Skip it if not initialized. + if (data.empty()) + return 0; + return HeaderSize() + data.size(); +} + AudioSampleEntry::AudioSampleEntry() = default; AudioSampleEntry::~AudioSampleEntry() = default; @@ -2011,6 +2032,7 @@ bool AudioSampleEntry::ReadWriteInternal(BoxBuffer* buffer) { RCHECK(buffer->TryReadWriteChild(&dops)); RCHECK(buffer->TryReadWriteChild(&dfla)); RCHECK(buffer->TryReadWriteChild(&mhac)); + RCHECK(buffer->TryReadWriteChild(&alac)); // Somehow Edge does not support having sinf box before codec_configuration, // box, so just do it in the end of AudioSampleEntry. See @@ -2036,6 +2058,7 @@ size_t AudioSampleEntry::ComputeSizeInternal() { sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() + esds.ComputeSize() + ddts.ComputeSize() + dac3.ComputeSize() + dec3.ComputeSize() + dops.ComputeSize() + dfla.ComputeSize() + + dac4.ComputeSize() + mhac.ComputeSize() + alac.ComputeSize() + dac4.ComputeSize() + mhac.ComputeSize() + udts.ComputeSize() + // Reserved and predefined bytes. 6 + 8 + // 6 + 8 bytes reserved. diff --git a/packager/media/formats/mp4/box_definitions.h b/packager/media/formats/mp4/box_definitions.h index e299f8dba6..a3debb60c1 100644 --- a/packager/media/formats/mp4/box_definitions.h +++ b/packager/media/formats/mp4/box_definitions.h @@ -382,6 +382,15 @@ struct FlacSpecific : FullBox { std::vector data; }; +// ALAC specific decoder configuration box: +// https://wiki.multimedia.cx/index.php/Apple_Lossless_Audio_Coding +// We do not care about the actual data inside, which is simply copied over. +struct ALACSpecific : FullBox { + DECLARE_BOX_METHODS(ALACSpecific); + + std::vector data; +}; + struct AudioSampleEntry : Box { DECLARE_BOX_METHODS(AudioSampleEntry); @@ -409,6 +418,7 @@ struct AudioSampleEntry : Box { OpusSpecific dops; FlacSpecific dfla; MHAConfiguration mhac; + ALACSpecific alac; }; struct WebVTTConfigurationBox : Box { diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index 53aa1fc0a1..b3be126385 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -102,6 +102,8 @@ Codec FourCCToCodec(FourCC fourcc) { return kCodecEAC3; case FOURCC_ac_4: return kCodecAC4; + case FOURCC_alac: + return kCodecALAC; case FOURCC_fLaC: return kCodecFlac; case FOURCC_mha1: @@ -514,6 +516,9 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { return false; } break; + case FOURCC_alac: + codec_config = entry.alac.data; + break; case FOURCC_fLaC: codec_config = entry.dfla.data; break; diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index faae886f8e..a599283839 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -76,6 +76,8 @@ FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) { return FOURCC_mp4a; case kCodecAC3: return FOURCC_ac_3; + case kCodecALAC: + return FOURCC_alac; case kCodecDTSC: return FOURCC_dtsc; case kCodecDTSH: @@ -512,6 +514,9 @@ bool MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info, case kCodecAC4: audio.dac4.data = audio_info->codec_config(); break; + case kCodecALAC: + audio.alac.data = audio_info->codec_config(); + break; case kCodecFlac: audio.dfla.data = audio_info->codec_config(); break; diff --git a/packager/media/test/data/bear-alac.mp4 b/packager/media/test/data/bear-alac.mp4 new file mode 100644 index 0000000000..b6a9bcdd51 Binary files /dev/null and b/packager/media/test/data/bear-alac.mp4 differ