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