From 30c59c7a3f93387bbf2edd66a2089d54705dddf8 Mon Sep 17 00:00:00 2001 From: Devin Tuchsen Date: Fri, 13 Jan 2017 19:45:36 -0600 Subject: [PATCH 1/4] Apple Lossless (ALAC) support --- extensions/ffmpeg/README.md | 1 + .../android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java | 7 ++++++- .../android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java | 2 ++ .../com/google/android/exoplayer2/extractor/mp4/Atom.java | 1 + .../android/exoplayer2/extractor/mp4/AtomParsers.java | 8 +++++++- .../com/google/android/exoplayer2/util/MimeTypes.java | 1 + 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/extensions/ffmpeg/README.md b/extensions/ffmpeg/README.md index d7c5e21fcc..0d669f826d 100644 --- a/extensions/ffmpeg/README.md +++ b/extensions/ffmpeg/README.md @@ -63,6 +63,7 @@ git clone git://source.ffmpeg.org/ffmpeg ffmpeg && cd ffmpeg && \ --enable-decoder=vorbis \ --enable-decoder=opus \ --enable-decoder=flac \ + --enable-decoder=alac \ && \ make -j4 && \ make install-libs diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java index 12f4bcf672..92240a50c1 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java @@ -87,7 +87,11 @@ import java.util.List; } if (!hasOutputFormat) { channelCount = ffmpegGetChannelCount(nativeContext); - sampleRate = ffmpegGetSampleRate(nativeContext); + if ("alac".equals(codecName)) { + sampleRate = ByteBuffer.wrap(extraData, extraData.length - 4, 4).getInt(); + } else { + sampleRate = ffmpegGetSampleRate(nativeContext); + } hasOutputFormat = true; } outputBuffer.data.position(0); @@ -123,6 +127,7 @@ import java.util.List; private static byte[] getExtraData(String mimeType, List initializationData) { switch (mimeType) { case MimeTypes.AUDIO_AAC: + case MimeTypes.AUDIO_ALAC: case MimeTypes.AUDIO_OPUS: return initializationData.get(0); case MimeTypes.AUDIO_VORBIS: diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java index 90b42c01bb..4992bcbb3e 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java @@ -92,6 +92,8 @@ public final class FfmpegLibrary { return "amrwb"; case MimeTypes.AUDIO_FLAC: return "flac"; + case MimeTypes.AUDIO_ALAC: + return "alac"; default: return null; } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java index c8ee8ff8c3..cc7e662336 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java @@ -135,6 +135,7 @@ import java.util.List; public static final int TYPE_vp09 = Util.getIntegerCodeForString("vp09"); public static final int TYPE_vpcC = Util.getIntegerCodeForString("vpcC"); public static final int TYPE_camm = Util.getIntegerCodeForString("camm"); + public static final int TYPE_alac = Util.getIntegerCodeForString("alac"); public final int type; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 9dc0578263..5288a3e6ba 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -604,7 +604,7 @@ import java.util.List; || childAtomType == Atom.TYPE_dtsh || childAtomType == Atom.TYPE_dtsl || childAtomType == Atom.TYPE_samr || childAtomType == Atom.TYPE_sawb || childAtomType == Atom.TYPE_lpcm || childAtomType == Atom.TYPE_sowt - || childAtomType == Atom.TYPE__mp3) { + || childAtomType == Atom.TYPE__mp3 || childAtomType == Atom.TYPE_alac) { parseAudioSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize, trackId, language, isQuickTime, drmInitData, out, i); } else if (childAtomType == Atom.TYPE_TTML) { @@ -839,6 +839,8 @@ import java.util.List; mimeType = MimeTypes.AUDIO_RAW; } else if (atomType == Atom.TYPE__mp3) { mimeType = MimeTypes.AUDIO_MPEG; + } else if (atomType == Atom.TYPE_alac) { + mimeType = MimeTypes.AUDIO_ALAC; } byte[] initializationData = null; @@ -876,6 +878,10 @@ import java.util.List; out.format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType, null, Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, 0, language); + } else if (childAtomType == Atom.TYPE_alac) { + initializationData = new byte[childAtomSize]; + parent.setPosition(childPosition); + parent.readBytes(initializationData, 0, childAtomSize); } childPosition += childAtomSize; } diff --git a/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index 9870b6547a..1c43ccddc1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -60,6 +60,7 @@ public final class MimeTypes { public static final String AUDIO_AMR_NB = BASE_TYPE_AUDIO + "/3gpp"; public static final String AUDIO_AMR_WB = BASE_TYPE_AUDIO + "/amr-wb"; public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/x-flac"; + public static final String AUDIO_ALAC = BASE_TYPE_AUDIO + "/alac"; public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt"; From 24724158b1622955c3af2cfcefeb19248deced1e Mon Sep 17 00:00:00 2001 From: Devin Tuchsen Date: Sun, 22 Jan 2017 13:05:00 -0600 Subject: [PATCH 2/4] Use ParseableByteArray to get ALAC sample rate --- .../google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java index 92240a50c1..27f329fbbf 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.SimpleDecoder; import com.google.android.exoplayer2.decoder.SimpleOutputBuffer; import com.google.android.exoplayer2.util.MimeTypes; +import com.google.android.exoplayer2.util.ParsableByteArray; import java.nio.ByteBuffer; import java.util.List; @@ -88,7 +89,9 @@ import java.util.List; if (!hasOutputFormat) { channelCount = ffmpegGetChannelCount(nativeContext); if ("alac".equals(codecName)) { - sampleRate = ByteBuffer.wrap(extraData, extraData.length - 4, 4).getInt(); + ParsableByteArray parsableExtraData = new ParsableByteArray(extraData); + parsableExtraData.setPosition(extraData.length - 4); + sampleRate = parsableExtraData.readUnsignedIntToInt(); } else { sampleRate = ffmpegGetSampleRate(nativeContext); } From f7b2452d46365f092f83102a543d23609d85b0fc Mon Sep 17 00:00:00 2001 From: Devin Tuchsen Date: Sun, 22 Jan 2017 13:41:29 -0600 Subject: [PATCH 3/4] Reference ALAC sample rate bug --- .../com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java index 27f329fbbf..4c9eaa49c4 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java @@ -89,6 +89,8 @@ import java.util.List; if (!hasOutputFormat) { channelCount = ffmpegGetChannelCount(nativeContext); if ("alac".equals(codecName)) { + // ALAC decoder did not set the sample rate in earlier versions of FFMPEG. + // See https://trac.ffmpeg.org/ticket/6096 ParsableByteArray parsableExtraData = new ParsableByteArray(extraData); parsableExtraData.setPosition(extraData.length - 4); sampleRate = parsableExtraData.readUnsignedIntToInt(); From 6becba8c42ae87b715b822a01781eed6301ca2fb Mon Sep 17 00:00:00 2001 From: Devin Tuchsen Date: Sat, 28 Jan 2017 15:15:41 -0600 Subject: [PATCH 4/4] Only use ALAC workaround if sample rate is 0 This prevents the workaround from occuring once FFmpeg has the bug patched. --- .../google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java index 4c9eaa49c4..2af2101ee7 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java @@ -88,14 +88,13 @@ import java.util.List; } if (!hasOutputFormat) { channelCount = ffmpegGetChannelCount(nativeContext); - if ("alac".equals(codecName)) { + sampleRate = ffmpegGetSampleRate(nativeContext); + if (sampleRate == 0 && "alac".equals(codecName)) { // ALAC decoder did not set the sample rate in earlier versions of FFMPEG. // See https://trac.ffmpeg.org/ticket/6096 ParsableByteArray parsableExtraData = new ParsableByteArray(extraData); parsableExtraData.setPosition(extraData.length - 4); sampleRate = parsableExtraData.readUnsignedIntToInt(); - } else { - sampleRate = ffmpegGetSampleRate(nativeContext); } hasOutputFormat = true; }