Skip to content

Commit e462c6a

Browse files
committed
Add AC-4 format support
* Add AC-4 MIME type definition * Add AC-4 format support in Mp4Extractor and TsExtractor * Add AC-4 Extractor * Add AC-4 playback support in MPEG-4, MPEG-DASH, TS and HLS
1 parent ae40208 commit e462c6a

File tree

8 files changed

+167
-12
lines changed

8 files changed

+167
-12
lines changed

library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
* <li>WAV ({@link WavExtractor})
5050
* <li>AC3 ({@link Ac3Extractor})
5151
* <li>AMR ({@link AmrExtractor})
52+
* <li>AC4 ({@link Ac4Extractor})
5253
* <li>FLAC (only available if the FLAC extension is built and included)
5354
* </ul>
5455
*/

library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ public class FragmentedMp4Extractor implements Extractor {
161161
private int sampleBytesWritten;
162162
private int sampleCurrentNalBytesRemaining;
163163
private boolean processSeiNalUnitPayload;
164+
private boolean isAc4HeaderAdded;
165+
private int ac4SampleHeaderSize;
164166

165167
// Extractor output.
166168
private ExtractorOutput extractorOutput;
@@ -262,6 +264,8 @@ public FragmentedMp4Extractor(
262264
durationUs = C.TIME_UNSET;
263265
pendingSeekTimeUs = C.TIME_UNSET;
264266
segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET;
267+
isAc4HeaderAdded = false;
268+
ac4SampleHeaderSize = 0;
265269
enterReadingAtomHeaderState();
266270
}
267271

@@ -1217,6 +1221,7 @@ private boolean readSample(ExtractorInput input) throws IOException, Interrupted
12171221
sampleSize += sampleBytesWritten;
12181222
parserState = STATE_READING_SAMPLE_CONTINUE;
12191223
sampleCurrentNalBytesRemaining = 0;
1224+
isAc4HeaderAdded = false;
12201225
}
12211226

12221227
TrackFragment fragment = currentTrackBundle.fragment;
@@ -1277,17 +1282,20 @@ private boolean readSample(ExtractorInput input) throws IOException, Interrupted
12771282
}
12781283
}
12791284
} else {
1280-
int sampleHeaderSize = 0;
1281-
if (MimeTypes.AUDIO_AC4.equals(track.format.sampleMimeType)) {
1285+
if (MimeTypes.AUDIO_AC4.equals(track.format.sampleMimeType) && !isAc4HeaderAdded) {
12821286
ParsableByteArray ac4SampleHeaderData = Ac4Util.getAc4SampleHeader(sampleSize);
12831287
output.sampleData(ac4SampleHeaderData, ac4SampleHeaderData.capacity());
1284-
sampleHeaderSize = ac4SampleHeaderData.capacity();
1288+
ac4SampleHeaderSize = ac4SampleHeaderData.capacity();
1289+
isAc4HeaderAdded = true;
12851290
}
12861291
while (sampleBytesWritten < sampleSize) {
12871292
int writtenBytes = output.sampleData(input, sampleSize - sampleBytesWritten, false);
12881293
sampleBytesWritten += writtenBytes;
12891294
}
1290-
sampleSize += sampleHeaderSize;
1295+
if (MimeTypes.AUDIO_AC4.equals(track.format.sampleMimeType)) {
1296+
sampleSize += ac4SampleHeaderSize;
1297+
isAc4HeaderAdded = false;
1298+
}
12911299
}
12921300

12931301
@C.BufferFlags int sampleFlags = fragment.sampleIsSyncFrameTable[sampleIndex]

library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
118118
private int firstVideoTrackIndex;
119119
private long durationUs;
120120
private boolean isQuickTime;
121+
private boolean isAc4HeaderAdded;
122+
private int ac4SampleHeaderSize;
121123

122124
/**
123125
* Creates a new extractor for unfragmented MP4 streams.
@@ -139,6 +141,8 @@ public Mp4Extractor(@Flags int flags) {
139141
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
140142
nalLength = new ParsableByteArray(4);
141143
sampleTrackIndex = C.INDEX_UNSET;
144+
isAc4HeaderAdded = false;
145+
ac4SampleHeaderSize = 0;
142146
}
143147

144148
@Override
@@ -489,6 +493,7 @@ private int readSample(ExtractorInput input, PositionHolder positionHolder)
489493
if (sampleTrackIndex == C.INDEX_UNSET) {
490494
return RESULT_END_OF_INPUT;
491495
}
496+
isAc4HeaderAdded = false;
492497
}
493498
Mp4Track track = tracks[sampleTrackIndex];
494499
TrackOutput trackOutput = track.trackOutput;
@@ -538,18 +543,21 @@ private int readSample(ExtractorInput input, PositionHolder positionHolder)
538543
}
539544
}
540545
} else {
541-
int sampleHeaderSize = 0;
542-
if (MimeTypes.AUDIO_AC4.equals(track.track.format.sampleMimeType)) {
546+
if (MimeTypes.AUDIO_AC4.equals(track.track.format.sampleMimeType) && !isAc4HeaderAdded) {
543547
ParsableByteArray ac4SampleHeaderData = Ac4Util.getAc4SampleHeader(sampleSize);
544548
trackOutput.sampleData(ac4SampleHeaderData, ac4SampleHeaderData.capacity());
545-
sampleHeaderSize = ac4SampleHeaderData.capacity();
549+
ac4SampleHeaderSize = ac4SampleHeaderData.capacity();
550+
isAc4HeaderAdded = true;
546551
}
547552
while (sampleBytesWritten < sampleSize) {
548553
int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
549554
sampleBytesWritten += writtenBytes;
550555
sampleCurrentNalBytesRemaining -= writtenBytes;
551556
}
552-
sampleSize += sampleHeaderSize;
557+
if (MimeTypes.AUDIO_AC4.equals(track.track.format.sampleMimeType)) {
558+
sampleSize += ac4SampleHeaderSize;
559+
isAc4HeaderAdded = false;
560+
}
553561
}
554562
trackOutput.sampleMetadata(track.sampleTable.timestampsUs[sampleIndex],
555563
track.sampleTable.flags[sampleIndex], sampleSize, 0, null);

library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac4Reader.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,7 @@ public void consume(ParsableByteArray data) {
115115
if (skipToNextSync(data)) {
116116
state = STATE_READING_HEADER;
117117
headerScratchBytes.data[0] = (byte)0xAC;
118-
headerScratchBytes.data[1] = 0x40;
119-
if (hasCRC)
120-
headerScratchBytes.data[1] = 0x41;
118+
headerScratchBytes.data[1] = (byte)(hasCRC ? 0x41 : 0x40);
121119
bytesRead = 2;
122120
}
123121
break;
7.42 KB
Binary file not shown.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
seekMap:
2+
isSeekable = false
3+
duration = UNSET TIME
4+
getPosition(0) = [[timeUs=0, position=0]]
5+
numberOfTracks = 1
6+
track 0:
7+
format:
8+
bitrate = -1
9+
id = 0
10+
containerMimeType = null
11+
sampleMimeType = audio/ac4
12+
maxInputSize = -1
13+
width = -1
14+
height = -1
15+
frameRate = -1.0
16+
rotationDegrees = 0
17+
pixelWidthHeightRatio = 1.0
18+
channelCount = 2
19+
sampleRate = 48000
20+
pcmEncoding = -1
21+
encoderDelay = 0
22+
encoderPadding = 0
23+
subsampleOffsetUs = 9223372036854775807
24+
selectionFlags = 0
25+
language = null
26+
drmInitData = -
27+
initializationData:
28+
total output bytes = 7594
29+
sample count = 19
30+
sample 0:
31+
time = 0
32+
flags = 1
33+
data = length 366, hash B4277F9E
34+
sample 1:
35+
time = 40000
36+
flags = 1
37+
data = length 366, hash E8E0A142
38+
sample 2:
39+
time = 80000
40+
flags = 1
41+
data = length 366, hash 2E5073D0
42+
sample 3:
43+
time = 120000
44+
flags = 1
45+
data = length 366, hash 850E71D8
46+
sample 4:
47+
time = 160000
48+
flags = 1
49+
data = length 366, hash 69CD444E
50+
sample 5:
51+
time = 200000
52+
flags = 1
53+
data = length 366, hash BD24F36D
54+
sample 6:
55+
time = 240000
56+
flags = 1
57+
data = length 366, hash E24F2490
58+
sample 7:
59+
time = 280000
60+
flags = 1
61+
data = length 366, hash EE6F1F06
62+
sample 8:
63+
time = 320000
64+
flags = 1
65+
data = length 366, hash 2DAB000F
66+
sample 9:
67+
time = 360000
68+
flags = 1
69+
data = length 366, hash 8102B7EC
70+
sample 10:
71+
time = 400000
72+
flags = 1
73+
data = length 366, hash 55BF59AC
74+
sample 11:
75+
time = 440000
76+
flags = 1
77+
data = length 494, hash CBC2E09F
78+
sample 12:
79+
time = 480000
80+
flags = 1
81+
data = length 519, hash 9DAF56E9
82+
sample 13:
83+
time = 520000
84+
flags = 1
85+
data = length 598, hash 8169EE2
86+
sample 14:
87+
time = 560000
88+
flags = 1
89+
data = length 435, hash 28C21246
90+
sample 15:
91+
time = 600000
92+
flags = 1
93+
data = length 365, hash FF14716D
94+
sample 16:
95+
time = 640000
96+
flags = 1
97+
data = length 392, hash 4CC96B29
98+
sample 17:
99+
time = 680000
100+
flags = 1
101+
data = length 373, hash D7AC6D4E
102+
sample 18:
103+
time = 720000
104+
flags = 1
105+
data = length 392, hash 99F2511F
106+
tracksEnded = true

library/core/src/test/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactoryTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor;
2626
import com.google.android.exoplayer2.extractor.ogg.OggExtractor;
2727
import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
28+
import com.google.android.exoplayer2.extractor.ts.Ac4Extractor;
2829
import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
2930
import com.google.android.exoplayer2.extractor.ts.PsExtractor;
3031
import com.google.android.exoplayer2.extractor.ts.TsExtractor;
@@ -62,7 +63,8 @@ public void testCreateExtractors_returnExpectedClasses() {
6263
OggExtractor.class,
6364
PsExtractor.class,
6465
WavExtractor.class,
65-
AmrExtractor.class
66+
AmrExtractor.class,
67+
Ac4Extractor.class
6668
};
6769

6870
assertThat(listCreatedExtractorClasses).containsNoDuplicates();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (C) 2019 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.android.exoplayer2.extractor.ts;
17+
18+
import com.google.android.exoplayer2.testutil.ExtractorAsserts;
19+
import org.junit.Test;
20+
import org.junit.runner.RunWith;
21+
import org.robolectric.RobolectricTestRunner;
22+
23+
/** Unit test for {@link Ac4Extractor}. */
24+
@RunWith(RobolectricTestRunner.class)
25+
public final class Ac4ExtractorTest {
26+
27+
@Test
28+
public void testAc4Sample() throws Exception {
29+
ExtractorAsserts.assertBehavior(Ac4Extractor::new, "ts/sample.ac4");
30+
}
31+
}
32+

0 commit comments

Comments
 (0)