diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
index 14db0b2ae1..e2f8887b5c 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
@@ -49,6 +49,7 @@ import java.lang.reflect.Constructor;
*
WAV ({@link WavExtractor})
* AC3 ({@link Ac3Extractor})
* AMR ({@link AmrExtractor})
+ * AC4 ({@link Ac4Extractor})
* FLAC (only available if the FLAC extension is built and included)
*
*/
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
index 1d45854647..b1b598d6ef 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
@@ -161,6 +161,8 @@ public class FragmentedMp4Extractor implements Extractor {
private int sampleBytesWritten;
private int sampleCurrentNalBytesRemaining;
private boolean processSeiNalUnitPayload;
+ private boolean isAc4HeaderAdded;
+ private int ac4SampleHeaderSize;
// Extractor output.
private ExtractorOutput extractorOutput;
@@ -262,6 +264,8 @@ public class FragmentedMp4Extractor implements Extractor {
durationUs = C.TIME_UNSET;
pendingSeekTimeUs = C.TIME_UNSET;
segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET;
+ isAc4HeaderAdded = false;
+ ac4SampleHeaderSize = 0;
enterReadingAtomHeaderState();
}
@@ -1217,6 +1221,7 @@ public class FragmentedMp4Extractor implements Extractor {
sampleSize += sampleBytesWritten;
parserState = STATE_READING_SAMPLE_CONTINUE;
sampleCurrentNalBytesRemaining = 0;
+ isAc4HeaderAdded = false;
}
TrackFragment fragment = currentTrackBundle.fragment;
@@ -1277,17 +1282,20 @@ public class FragmentedMp4Extractor implements Extractor {
}
}
} else {
- int sampleHeaderSize = 0;
- if (MimeTypes.AUDIO_AC4.equals(track.format.sampleMimeType)) {
+ if (MimeTypes.AUDIO_AC4.equals(track.format.sampleMimeType) && !isAc4HeaderAdded) {
ParsableByteArray ac4SampleHeaderData = Ac4Util.getAc4SampleHeader(sampleSize);
output.sampleData(ac4SampleHeaderData, ac4SampleHeaderData.capacity());
- sampleHeaderSize = ac4SampleHeaderData.capacity();
+ ac4SampleHeaderSize = ac4SampleHeaderData.capacity();
+ isAc4HeaderAdded = true;
}
while (sampleBytesWritten < sampleSize) {
int writtenBytes = output.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes;
}
- sampleSize += sampleHeaderSize;
+ if (MimeTypes.AUDIO_AC4.equals(track.format.sampleMimeType)) {
+ sampleSize += ac4SampleHeaderSize;
+ isAc4HeaderAdded = false;
+ }
}
@C.BufferFlags int sampleFlags = fragment.sampleIsSyncFrameTable[sampleIndex]
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
index 7c9fe2a187..49f5fac7a4 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
@@ -118,6 +118,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private int firstVideoTrackIndex;
private long durationUs;
private boolean isQuickTime;
+ private boolean isAc4HeaderAdded;
+ private int ac4SampleHeaderSize;
/**
* Creates a new extractor for unfragmented MP4 streams.
@@ -139,6 +141,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
nalLength = new ParsableByteArray(4);
sampleTrackIndex = C.INDEX_UNSET;
+ isAc4HeaderAdded = false;
+ ac4SampleHeaderSize = 0;
}
@Override
@@ -489,6 +493,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
if (sampleTrackIndex == C.INDEX_UNSET) {
return RESULT_END_OF_INPUT;
}
+ isAc4HeaderAdded = false;
}
Mp4Track track = tracks[sampleTrackIndex];
TrackOutput trackOutput = track.trackOutput;
@@ -538,18 +543,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}
}
} else {
- int sampleHeaderSize = 0;
- if (MimeTypes.AUDIO_AC4.equals(track.track.format.sampleMimeType)) {
+ if (MimeTypes.AUDIO_AC4.equals(track.track.format.sampleMimeType) && !isAc4HeaderAdded) {
ParsableByteArray ac4SampleHeaderData = Ac4Util.getAc4SampleHeader(sampleSize);
trackOutput.sampleData(ac4SampleHeaderData, ac4SampleHeaderData.capacity());
- sampleHeaderSize = ac4SampleHeaderData.capacity();
+ ac4SampleHeaderSize = ac4SampleHeaderData.capacity();
+ isAc4HeaderAdded = true;
}
while (sampleBytesWritten < sampleSize) {
int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes;
}
- sampleSize += sampleHeaderSize;
+ if (MimeTypes.AUDIO_AC4.equals(track.track.format.sampleMimeType)) {
+ sampleSize += ac4SampleHeaderSize;
+ isAc4HeaderAdded = false;
+ }
}
trackOutput.sampleMetadata(track.sampleTable.timestampsUs[sampleIndex],
track.sampleTable.flags[sampleIndex], sampleSize, 0, null);
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac4Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac4Reader.java
index f5d50cde92..2486ab5400 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac4Reader.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac4Reader.java
@@ -115,9 +115,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
if (skipToNextSync(data)) {
state = STATE_READING_HEADER;
headerScratchBytes.data[0] = (byte)0xAC;
- headerScratchBytes.data[1] = 0x40;
- if (hasCRC)
- headerScratchBytes.data[1] = 0x41;
+ headerScratchBytes.data[1] = headerScratchBytes.data[1] = (byte)(hasCRC ? 0x41 : 0x40);
bytesRead = 2;
}
break;
diff --git a/library/core/src/test/assets/ts/sample.ac4 b/library/core/src/test/assets/ts/sample.ac4
new file mode 100644
index 0000000000..721f53cdd7
Binary files /dev/null and b/library/core/src/test/assets/ts/sample.ac4 differ
diff --git a/library/core/src/test/assets/ts/sample.ac4.0.dump b/library/core/src/test/assets/ts/sample.ac4.0.dump
new file mode 100644
index 0000000000..03ae07707a
--- /dev/null
+++ b/library/core/src/test/assets/ts/sample.ac4.0.dump
@@ -0,0 +1,106 @@
+seekMap:
+ isSeekable = false
+ duration = UNSET TIME
+ getPosition(0) = [[timeUs=0, position=0]]
+numberOfTracks = 1
+track 0:
+ format:
+ bitrate = -1
+ id = 0
+ containerMimeType = null
+ sampleMimeType = audio/ac4
+ maxInputSize = -1
+ width = -1
+ height = -1
+ frameRate = -1.0
+ rotationDegrees = 0
+ pixelWidthHeightRatio = 1.0
+ channelCount = 2
+ sampleRate = 48000
+ pcmEncoding = -1
+ encoderDelay = 0
+ encoderPadding = 0
+ subsampleOffsetUs = 9223372036854775807
+ selectionFlags = 0
+ language = null
+ drmInitData = -
+ initializationData:
+ total output bytes = 7594
+ sample count = 19
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 366, hash B4277F9E
+ sample 1:
+ time = 40000
+ flags = 1
+ data = length 366, hash E8E0A142
+ sample 2:
+ time = 80000
+ flags = 1
+ data = length 366, hash 2E5073D0
+ sample 3:
+ time = 120000
+ flags = 1
+ data = length 366, hash 850E71D8
+ sample 4:
+ time = 160000
+ flags = 1
+ data = length 366, hash 69CD444E
+ sample 5:
+ time = 200000
+ flags = 1
+ data = length 366, hash BD24F36D
+ sample 6:
+ time = 240000
+ flags = 1
+ data = length 366, hash E24F2490
+ sample 7:
+ time = 280000
+ flags = 1
+ data = length 366, hash EE6F1F06
+ sample 8:
+ time = 320000
+ flags = 1
+ data = length 366, hash 2DAB000F
+ sample 9:
+ time = 360000
+ flags = 1
+ data = length 366, hash 8102B7EC
+ sample 10:
+ time = 400000
+ flags = 1
+ data = length 366, hash 55BF59AC
+ sample 11:
+ time = 440000
+ flags = 1
+ data = length 494, hash CBC2E09F
+ sample 12:
+ time = 480000
+ flags = 1
+ data = length 519, hash 9DAF56E9
+ sample 13:
+ time = 520000
+ flags = 1
+ data = length 598, hash 8169EE2
+ sample 14:
+ time = 560000
+ flags = 1
+ data = length 435, hash 28C21246
+ sample 15:
+ time = 600000
+ flags = 1
+ data = length 365, hash FF14716D
+ sample 16:
+ time = 640000
+ flags = 1
+ data = length 392, hash 4CC96B29
+ sample 17:
+ time = 680000
+ flags = 1
+ data = length 373, hash D7AC6D4E
+ sample 18:
+ time = 720000
+ flags = 1
+ data = length 392, hash 99F2511F
+tracksEnded = true
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactoryTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactoryTest.java
index 148e04ca77..bbe7985568 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactoryTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactoryTest.java
@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor;
import com.google.android.exoplayer2.extractor.ogg.OggExtractor;
import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
+import com.google.android.exoplayer2.extractor.ts.Ac4Extractor;
import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
import com.google.android.exoplayer2.extractor.ts.PsExtractor;
import com.google.android.exoplayer2.extractor.ts.TsExtractor;
@@ -62,7 +63,8 @@ public final class DefaultExtractorsFactoryTest {
OggExtractor.class,
PsExtractor.class,
WavExtractor.class,
- AmrExtractor.class
+ AmrExtractor.class,
+ Ac4Extractor.class
};
assertThat(listCreatedExtractorClasses).containsNoDuplicates();
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/Ac4ExtractorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/Ac4ExtractorTest.java
new file mode 100644
index 0000000000..782956dee7
--- /dev/null
+++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/Ac4ExtractorTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer2.extractor.ts;
+
+import com.google.android.exoplayer2.testutil.ExtractorAsserts;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Unit test for {@link Ac4Extractor}. */
+@RunWith(RobolectricTestRunner.class)
+public final class Ac4ExtractorTest {
+
+ @Test
+ public void testAc4Sample() throws Exception {
+ ExtractorAsserts.assertBehavior(Ac4Extractor::new, "ts/sample.ac4");
+ }
+}
+