Optimize AC-4 code and add related test cases

-- Optimize Mp4Extractor for AC-4
-- Optimize FragmentedMp4Extractor for AC-4
-- Add test case for AC-4 in MP4
-- Add test case for AC-4 in Fragmented MP4
This commit is contained in:
ybai001 2020-01-06 15:21:43 +08:00
parent 781f848893
commit a67394b230
15 changed files with 744 additions and 24 deletions

View File

@ -57,6 +57,12 @@ public final class Ac4Util {
/** The channel count of AC-4 stream. */
// TODO: Parse AC-4 stream channel count.
private static final int CHANNEL_COUNT_2 = 2;
/**
* The AC-4 sync frame header size for extractor.
* The 7 bytes are 0xAC, 0x40, 0xFF, 0xFF, sizeByte1, sizeByte2, sizeByte3.
* See ETSI TS 103 190-1 V1.3.1, Annex G
*/
public static final int SAMPLE_HEADER_SIZE = 7;
/**
* The header size for AC-4 parser. Only needs to be as big as we need to read, not the full
* header size.

View File

@ -168,7 +168,6 @@ public class FragmentedMp4Extractor implements Extractor {
private int sampleBytesWritten;
private int sampleCurrentNalBytesRemaining;
private boolean processSeiNalUnitPayload;
private boolean isAc4HeaderRequired;
// Extractor output.
@MonotonicNonNull private ExtractorOutput extractorOutput;
@ -302,7 +301,6 @@ public class FragmentedMp4Extractor implements Extractor {
pendingMetadataSampleBytes = 0;
pendingSeekTimeUs = timeUs;
containerAtoms.clear();
isAc4HeaderRequired = false;
enterReadingAtomHeaderState();
}
@ -1267,12 +1265,18 @@ public class FragmentedMp4Extractor implements Extractor {
sampleSize -= Atom.HEADER_SIZE;
input.skipFully(Atom.HEADER_SIZE);
}
boolean isAc4HeaderRequired =
MimeTypes.AUDIO_AC4.equals(currentTrackBundle.track.format.sampleMimeType);
sampleBytesWritten = currentTrackBundle.outputSampleEncryptionData();
sampleSize += sampleBytesWritten;
if (isAc4HeaderRequired) {
Ac4Util.getAc4SampleHeader(sampleSize, scratch);
currentTrackBundle.output.sampleData(scratch, Ac4Util.SAMPLE_HEADER_SIZE);
sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE;
sampleSize += Ac4Util.SAMPLE_HEADER_SIZE;
}
parserState = STATE_READING_SAMPLE_CONTINUE;
sampleCurrentNalBytesRemaining = 0;
isAc4HeaderRequired =
MimeTypes.AUDIO_AC4.equals(currentTrackBundle.track.format.sampleMimeType);
}
TrackFragment fragment = currentTrackBundle.fragment;
@ -1337,14 +1341,6 @@ public class FragmentedMp4Extractor implements Extractor {
}
}
} else {
if (isAc4HeaderRequired) {
Ac4Util.getAc4SampleHeader(sampleSize, scratch);
int length = scratch.limit();
output.sampleData(scratch, length);
sampleSize += length;
sampleBytesWritten += length;
isAc4HeaderRequired = false;
}
while (sampleBytesWritten < sampleSize) {
int writtenBytes = output.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes;

View File

@ -112,7 +112,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private int sampleTrackIndex;
private int sampleBytesWritten;
private int sampleCurrentNalBytesRemaining;
private boolean isAc4HeaderRequired;
// Extractor outputs.
@MonotonicNonNull private ExtractorOutput extractorOutput;
@ -162,7 +161,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
sampleTrackIndex = C.INDEX_UNSET;
sampleBytesWritten = 0;
sampleCurrentNalBytesRemaining = 0;
isAc4HeaderRequired = false;
if (position == 0) {
enterReadingAtomHeaderState();
} else if (tracks != null) {
@ -507,8 +505,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
if (sampleTrackIndex == C.INDEX_UNSET) {
return RESULT_END_OF_INPUT;
}
isAc4HeaderRequired =
MimeTypes.AUDIO_AC4.equals(tracks[sampleTrackIndex].track.format.sampleMimeType);
}
Mp4Track track = tracks[sampleTrackIndex];
TrackOutput trackOutput = track.trackOutput;
@ -527,6 +523,13 @@ public final class Mp4Extractor implements Extractor, SeekMap {
sampleSize -= Atom.HEADER_SIZE;
}
input.skipFully((int) skipAmount);
if (MimeTypes.AUDIO_AC4.equals(track.track.format.sampleMimeType)) {
Ac4Util.getAc4SampleHeader(sampleSize, scratch);
int length = scratch.limit();
trackOutput.sampleData(scratch, length);
sampleSize += length;
sampleBytesWritten += length;
}
if (track.track.nalUnitLengthFieldLength != 0) {
// Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
// they're only 1 or 2 bytes long.
@ -562,14 +565,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}
}
} else {
if (isAc4HeaderRequired) {
Ac4Util.getAc4SampleHeader(sampleSize, scratch);
int length = scratch.limit();
trackOutput.sampleData(scratch, length);
sampleSize += length;
sampleBytesWritten += length;
isAc4HeaderRequired = false;
}
while (sampleBytesWritten < sampleSize) {
int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes;

Binary file not shown.

View File

@ -0,0 +1,107 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=758]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = 622
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 = und
drmInitData = -
metadata = null
initializationData:
total output bytes = 7613
sample count = 19
sample 0:
time = 0
flags = 1
data = length 367, hash D2762FA
sample 1:
time = 40000
flags = 0
data = length 367, hash BDD3224A
sample 2:
time = 80000
flags = 0
data = length 367, hash 9302227B
sample 3:
time = 120000
flags = 0
data = length 367, hash 72996003
sample 4:
time = 160000
flags = 0
data = length 367, hash 88AE5A1B
sample 5:
time = 200000
flags = 0
data = length 367, hash E5346FE3
sample 6:
time = 240000
flags = 0
data = length 367, hash CE558362
sample 7:
time = 280000
flags = 0
data = length 367, hash 51AD3043
sample 8:
time = 320000
flags = 0
data = length 367, hash EB72E95B
sample 9:
time = 360000
flags = 0
data = length 367, hash 47F8FF23
sample 10:
time = 400000
flags = 0
data = length 367, hash 8133883D
sample 11:
time = 440000
flags = 0
data = length 495, hash E14BDFEE
sample 12:
time = 480000
flags = 0
data = length 520, hash FEE56928
sample 13:
time = 519999
flags = 0
data = length 599, hash 41F496C5
sample 14:
time = 560000
flags = 0
data = length 436, hash 76D6404
sample 15:
time = 600000
flags = 0
data = length 366, hash 56D49D4D
sample 16:
time = 640000
flags = 0
data = length 393, hash 822FC8
sample 17:
time = 680000
flags = 0
data = length 374, hash FA8AE217
sample 18:
time = 720000
flags = 536870912
data = length 393, hash 8506A1B
tracksEnded = true

View File

@ -0,0 +1,107 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=758]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = 622
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 = und
drmInitData = -
metadata = null
initializationData:
total output bytes = 7613
sample count = 19
sample 0:
time = 0
flags = 1
data = length 367, hash D2762FA
sample 1:
time = 40000
flags = 0
data = length 367, hash BDD3224A
sample 2:
time = 80000
flags = 0
data = length 367, hash 9302227B
sample 3:
time = 120000
flags = 0
data = length 367, hash 72996003
sample 4:
time = 160000
flags = 0
data = length 367, hash 88AE5A1B
sample 5:
time = 200000
flags = 0
data = length 367, hash E5346FE3
sample 6:
time = 240000
flags = 0
data = length 367, hash CE558362
sample 7:
time = 280000
flags = 0
data = length 367, hash 51AD3043
sample 8:
time = 320000
flags = 0
data = length 367, hash EB72E95B
sample 9:
time = 360000
flags = 0
data = length 367, hash 47F8FF23
sample 10:
time = 400000
flags = 0
data = length 367, hash 8133883D
sample 11:
time = 440000
flags = 0
data = length 495, hash E14BDFEE
sample 12:
time = 480000
flags = 0
data = length 520, hash FEE56928
sample 13:
time = 519999
flags = 0
data = length 599, hash 41F496C5
sample 14:
time = 560000
flags = 0
data = length 436, hash 76D6404
sample 15:
time = 600000
flags = 0
data = length 366, hash 56D49D4D
sample 16:
time = 640000
flags = 0
data = length 393, hash 822FC8
sample 17:
time = 680000
flags = 0
data = length 374, hash FA8AE217
sample 18:
time = 720000
flags = 536870912
data = length 393, hash 8506A1B
tracksEnded = true

View File

@ -0,0 +1,107 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=758]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = 622
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 = und
drmInitData = -
metadata = null
initializationData:
total output bytes = 7613
sample count = 19
sample 0:
time = 0
flags = 1
data = length 367, hash D2762FA
sample 1:
time = 40000
flags = 0
data = length 367, hash BDD3224A
sample 2:
time = 80000
flags = 0
data = length 367, hash 9302227B
sample 3:
time = 120000
flags = 0
data = length 367, hash 72996003
sample 4:
time = 160000
flags = 0
data = length 367, hash 88AE5A1B
sample 5:
time = 200000
flags = 0
data = length 367, hash E5346FE3
sample 6:
time = 240000
flags = 0
data = length 367, hash CE558362
sample 7:
time = 280000
flags = 0
data = length 367, hash 51AD3043
sample 8:
time = 320000
flags = 0
data = length 367, hash EB72E95B
sample 9:
time = 360000
flags = 0
data = length 367, hash 47F8FF23
sample 10:
time = 400000
flags = 0
data = length 367, hash 8133883D
sample 11:
time = 440000
flags = 0
data = length 495, hash E14BDFEE
sample 12:
time = 480000
flags = 0
data = length 520, hash FEE56928
sample 13:
time = 519999
flags = 0
data = length 599, hash 41F496C5
sample 14:
time = 560000
flags = 0
data = length 436, hash 76D6404
sample 15:
time = 600000
flags = 0
data = length 366, hash 56D49D4D
sample 16:
time = 640000
flags = 0
data = length 393, hash 822FC8
sample 17:
time = 680000
flags = 0
data = length 374, hash FA8AE217
sample 18:
time = 720000
flags = 536870912
data = length 393, hash 8506A1B
tracksEnded = true

View File

@ -0,0 +1,107 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=758]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
containerMimeType = null
sampleMimeType = audio/ac4
maxInputSize = 622
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 = und
drmInitData = -
metadata = null
initializationData:
total output bytes = 7613
sample count = 19
sample 0:
time = 0
flags = 1
data = length 367, hash D2762FA
sample 1:
time = 40000
flags = 0
data = length 367, hash BDD3224A
sample 2:
time = 80000
flags = 0
data = length 367, hash 9302227B
sample 3:
time = 120000
flags = 0
data = length 367, hash 72996003
sample 4:
time = 160000
flags = 0
data = length 367, hash 88AE5A1B
sample 5:
time = 200000
flags = 0
data = length 367, hash E5346FE3
sample 6:
time = 240000
flags = 0
data = length 367, hash CE558362
sample 7:
time = 280000
flags = 0
data = length 367, hash 51AD3043
sample 8:
time = 320000
flags = 0
data = length 367, hash EB72E95B
sample 9:
time = 360000
flags = 0
data = length 367, hash 47F8FF23
sample 10:
time = 400000
flags = 0
data = length 367, hash 8133883D
sample 11:
time = 440000
flags = 0
data = length 495, hash E14BDFEE
sample 12:
time = 480000
flags = 0
data = length 520, hash FEE56928
sample 13:
time = 519999
flags = 0
data = length 599, hash 41F496C5
sample 14:
time = 560000
flags = 0
data = length 436, hash 76D6404
sample 15:
time = 600000
flags = 0
data = length 366, hash 56D49D4D
sample 16:
time = 640000
flags = 0
data = length 393, hash 822FC8
sample 17:
time = 680000
flags = 0
data = length 374, hash FA8AE217
sample 18:
time = 720000
flags = 536870912
data = length 393, hash 8506A1B
tracksEnded = true

View File

@ -0,0 +1,107 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=685]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
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 = und
drmInitData = -
metadata = null
initializationData:
total output bytes = 7613
sample count = 19
sample 0:
time = 0
flags = 1
data = length 367, hash D2762FA
sample 1:
time = 40000
flags = 1
data = length 367, hash BDD3224A
sample 2:
time = 80000
flags = 1
data = length 367, hash 9302227B
sample 3:
time = 120000
flags = 1
data = length 367, hash 72996003
sample 4:
time = 160000
flags = 1
data = length 367, hash 88AE5A1B
sample 5:
time = 200000
flags = 1
data = length 367, hash E5346FE3
sample 6:
time = 240000
flags = 1
data = length 367, hash CE558362
sample 7:
time = 280000
flags = 1
data = length 367, hash 51AD3043
sample 8:
time = 320000
flags = 1
data = length 367, hash EB72E95B
sample 9:
time = 360000
flags = 1
data = length 367, hash 47F8FF23
sample 10:
time = 400000
flags = 1
data = length 367, hash 8133883D
sample 11:
time = 440000
flags = 1
data = length 495, hash E14BDFEE
sample 12:
time = 480000
flags = 1
data = length 520, hash FEE56928
sample 13:
time = 520000
flags = 1
data = length 599, hash 41F496C5
sample 14:
time = 560000
flags = 1
data = length 436, hash 76D6404
sample 15:
time = 600000
flags = 1
data = length 366, hash 56D49D4D
sample 16:
time = 640000
flags = 1
data = length 393, hash 822FC8
sample 17:
time = 680000
flags = 1
data = length 374, hash FA8AE217
sample 18:
time = 720000
flags = 1
data = length 393, hash 8506A1B
tracksEnded = true

View File

@ -0,0 +1,83 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=685]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
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 = und
drmInitData = -
metadata = null
initializationData:
total output bytes = 5411
sample count = 13
sample 0:
time = 240000
flags = 1
data = length 367, hash CE558362
sample 1:
time = 280000
flags = 1
data = length 367, hash 51AD3043
sample 2:
time = 320000
flags = 1
data = length 367, hash EB72E95B
sample 3:
time = 360000
flags = 1
data = length 367, hash 47F8FF23
sample 4:
time = 400000
flags = 1
data = length 367, hash 8133883D
sample 5:
time = 440000
flags = 1
data = length 495, hash E14BDFEE
sample 6:
time = 480000
flags = 1
data = length 520, hash FEE56928
sample 7:
time = 520000
flags = 1
data = length 599, hash 41F496C5
sample 8:
time = 560000
flags = 1
data = length 436, hash 76D6404
sample 9:
time = 600000
flags = 1
data = length 366, hash 56D49D4D
sample 10:
time = 640000
flags = 1
data = length 393, hash 822FC8
sample 11:
time = 680000
flags = 1
data = length 374, hash FA8AE217
sample 12:
time = 720000
flags = 1
data = length 393, hash 8506A1B
tracksEnded = true

View File

@ -0,0 +1,59 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=685]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
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 = und
drmInitData = -
metadata = null
initializationData:
total output bytes = 3081
sample count = 7
sample 0:
time = 480000
flags = 1
data = length 520, hash FEE56928
sample 1:
time = 520000
flags = 1
data = length 599, hash 41F496C5
sample 2:
time = 560000
flags = 1
data = length 436, hash 76D6404
sample 3:
time = 600000
flags = 1
data = length 366, hash 56D49D4D
sample 4:
time = 640000
flags = 1
data = length 393, hash 822FC8
sample 5:
time = 680000
flags = 1
data = length 374, hash FA8AE217
sample 6:
time = 720000
flags = 1
data = length 393, hash 8506A1B
tracksEnded = true

View File

@ -0,0 +1,35 @@
seekMap:
isSeekable = true
duration = 760000
getPosition(0) = [[timeUs=0, position=685]]
numberOfTracks = 1
track 0:
format:
bitrate = -1
id = 1
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 = und
drmInitData = -
metadata = null
initializationData:
total output bytes = 393
sample count = 1
sample 0:
time = 720000
flags = 1
data = length 393, hash 8506A1B
tracksEnded = true

View File

@ -51,6 +51,12 @@ public final class FragmentedMp4ExtractorTest {
ExtractorAsserts.assertBehavior(extractorFactory, "mp4/sample_fragmented_sei.mp4");
}
@Test
public void testSampleWithAc4Track() throws Exception {
ExtractorAsserts.assertBehavior(
getExtractorFactory(Collections.emptyList()), "mp4/sample_ac4_fragmented.mp4");
}
private static ExtractorFactory getExtractorFactory(final List<Format> closedCaptionFormats) {
return () -> new FragmentedMp4Extractor(0, null, null, null, closedCaptionFormats);
}

View File

@ -42,4 +42,9 @@ public final class Mp4ExtractorTest {
public void testMp4SampleWithMdatTooLong() throws Exception {
ExtractorAsserts.assertBehavior(Mp4Extractor::new, "mp4/sample_mdat_too_long.mp4");
}
@Test
public void testMp4SampleWithAc4Track() throws Exception {
ExtractorAsserts.assertBehavior(Mp4Extractor::new, "mp4/sample_ac4.mp4");
}
}