Skip to content

Commit

Permalink
Add AC-4 format support
Browse files Browse the repository at this point in the history
 * 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
  • Loading branch information
ybai001 committed Jan 25, 2019
1 parent ae40208 commit e462c6a
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 12 deletions.
Expand Up @@ -49,6 +49,7 @@
* <li>WAV ({@link WavExtractor})
* <li>AC3 ({@link Ac3Extractor})
* <li>AMR ({@link AmrExtractor})
* <li>AC4 ({@link Ac4Extractor})
* <li>FLAC (only available if the FLAC extension is built and included)
* </ul>
*/
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -262,6 +264,8 @@ public FragmentedMp4Extractor(
durationUs = C.TIME_UNSET;
pendingSeekTimeUs = C.TIME_UNSET;
segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET;
isAc4HeaderAdded = false;
ac4SampleHeaderSize = 0;
enterReadingAtomHeaderState();
}

Expand Down Expand Up @@ -1217,6 +1221,7 @@ private boolean readSample(ExtractorInput input) throws IOException, Interrupted
sampleSize += sampleBytesWritten;
parserState = STATE_READING_SAMPLE_CONTINUE;
sampleCurrentNalBytesRemaining = 0;
isAc4HeaderAdded = false;
}

TrackFragment fragment = currentTrackBundle.fragment;
Expand Down Expand Up @@ -1277,17 +1282,20 @@ private boolean readSample(ExtractorInput input) throws IOException, Interrupted
}
}
} 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]
Expand Down
Expand Up @@ -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.
Expand All @@ -139,6 +141,8 @@ public Mp4Extractor(@Flags int flags) {
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
nalLength = new ParsableByteArray(4);
sampleTrackIndex = C.INDEX_UNSET;
isAc4HeaderAdded = false;
ac4SampleHeaderSize = 0;
}

@Override
Expand Down Expand Up @@ -489,6 +493,7 @@ private int readSample(ExtractorInput input, PositionHolder positionHolder)
if (sampleTrackIndex == C.INDEX_UNSET) {
return RESULT_END_OF_INPUT;
}
isAc4HeaderAdded = false;
}
Mp4Track track = tracks[sampleTrackIndex];
TrackOutput trackOutput = track.trackOutput;
Expand Down Expand Up @@ -538,18 +543,21 @@ private int readSample(ExtractorInput input, PositionHolder positionHolder)
}
}
} 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);
Expand Down
Expand Up @@ -115,9 +115,7 @@ public void consume(ParsableByteArray data) {
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] = (byte)(hasCRC ? 0x41 : 0x40);
bytesRead = 2;
}
break;
Expand Down
Binary file added library/core/src/test/assets/ts/sample.ac4
Binary file not shown.
106 changes: 106 additions & 0 deletions 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
Expand Up @@ -25,6 +25,7 @@
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;
Expand Down Expand Up @@ -62,7 +63,8 @@ public void testCreateExtractors_returnExpectedClasses() {
OggExtractor.class,
PsExtractor.class,
WavExtractor.class,
AmrExtractor.class
AmrExtractor.class,
Ac4Extractor.class
};

assertThat(listCreatedExtractorClasses).containsNoDuplicates();
Expand Down
@@ -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");
}
}

0 comments on commit e462c6a

Please sign in to comment.