Validate Extractor behavior when load position is reset to 0 following an error.

Added a new method TestUtil.consumeTestData() to emulate
the exact behaviour and modified OggExtractorFileTests to
use it. Rest of the test will be fixed in follow up CLs.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123320538
This commit is contained in:
eguven 2016-05-26 08:16:16 -07:00 committed by Oliver Woodman
parent c2b89d6285
commit 17f8ac8adf
25 changed files with 324 additions and 254 deletions

View file

@ -127,7 +127,7 @@ public final class FlacExtractor implements Extractor {
}
@Override
public void seek() {
public void seek(long position) {
decoder.flush();
}

Binary file not shown.

View file

@ -70,20 +70,9 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
private static final String MATROSKA_DOC_TYPE = "matroska";
private static final String WEBM_DOC_TYPE = "webm";
private MatroskaExtractor extractor;
private FakeExtractorOutput extractorOutput;
@Override
public void setUp() {
extractor = new MatroskaExtractor();
extractorOutput = new FakeExtractorOutput();
extractor.init(extractorOutput);
}
@Override
public void tearDown() {
extractor = null;
extractorOutput = null;
private FakeExtractorOutput consumeTestData(byte[] data)
throws IOException, InterruptedException {
return TestUtil.consumeTestData(new MatroskaExtractor(), data);
}
public void testReadInitializationSegment() throws IOException, InterruptedException {
@ -93,27 +82,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
}
public void testReadSegmentTwice() throws IOException, InterruptedException {
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
.build(1);
TestUtil.consumeTestData(extractor, data);
extractor.seek();
TestUtil.consumeTestData(extractor, data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
}
public void testPrepareOpus() throws IOException, InterruptedException {
@ -124,11 +97,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
assertTracksEnded(extractorOutput);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
}
public void testPrepareVorbis() throws IOException, InterruptedException {
@ -139,11 +112,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
getVorbisCodecPrivate())
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
assertTracksEnded(extractorOutput);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
}
public void testPrepareH264() throws IOException, InterruptedException {
@ -153,11 +126,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addH264Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, TEST_H264_CODEC_PRIVATE)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertH264VideoFormat(VIDEO_TRACK_NUMBER);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
assertTracksEnded(extractorOutput);
assertH264VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
}
public void testPrepareTwoTracks() throws IOException, InterruptedException {
@ -169,13 +142,13 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertTracksEnded(extractorOutput);
assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
}
public void testPrepareThreeTracks() throws IOException, InterruptedException {
@ -188,14 +161,14 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertTracksEnded(extractorOutput);
// Even though the input stream has 3 tracks, only 2 of them are supported and will be reported.
assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
}
public void testPrepareFourTracks() throws IOException, InterruptedException {
@ -210,15 +183,15 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
TEST_CODEC_DELAY, TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertTracksEnded(extractorOutput);
assertEquals(4, extractorOutput.numberOfTracks);
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
assertVp9VideoFormat(SECOND_VIDEO_TRACK_NUMBER);
assertAudioFormat(SECOND_AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
assertVp9VideoFormat(extractorOutput, SECOND_VIDEO_TRACK_NUMBER);
assertAudioFormat(extractorOutput, SECOND_AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
}
public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException {
@ -229,12 +202,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertDrmInitData(VIDEO_TRACK_NUMBER);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertDrmInitData(extractorOutput, VIDEO_TRACK_NUMBER);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
}
public void testPrepareThreeCuePoints() throws IOException, InterruptedException {
@ -244,11 +217,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
.build(3);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSeekMap(DEFAULT_TIMECODE_SCALE, 3);
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 3);
}
public void testPrepareCustomTimecodeScaleBeforeDuration()
@ -274,11 +247,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
.build(3);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSeekMap(timecodeScale, 3);
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSeekMap(extractorOutput, timecodeScale, 3);
}
public void testPrepareNoCuesElement() throws IOException, InterruptedException {
@ -301,10 +274,10 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(0);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertSeekMapUnseekable(timecodeScale);
assertTracksEnded(extractorOutput);
assertSeekMapUnseekable(extractorOutput, timecodeScale);
}
public void testAcceptsWebmDocType() throws IOException, InterruptedException {
@ -314,9 +287,9 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertTracksEnded(extractorOutput);
}
public void testAcceptsMatroskaDocType() throws IOException, InterruptedException {
@ -326,9 +299,9 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertTracksEnded(extractorOutput);
}
public void testPrepareInvalidDocType() throws IOException, InterruptedException {
@ -338,7 +311,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
.build(1);
try {
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
fail();
} catch (ParserException exception) {
assertEquals("DocType webB not supported", exception.getMessage());
@ -353,7 +326,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
fail();
} catch (ParserException exception) {
assertEquals("ContentEncodingOrder 1 not supported", exception.getMessage());
@ -368,7 +341,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
fail();
} catch (ParserException exception) {
assertEquals("ContentEncodingScope 0 not supported", exception.getMessage());
@ -384,7 +357,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
fail();
} catch (ParserException exception) {
assertEquals("ContentCompAlgo 0 not supported", exception.getMessage());
@ -399,7 +372,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
fail();
} catch (ParserException exception) {
assertEquals("ContentEncAlgo 4 not supported", exception.getMessage());
@ -414,7 +387,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
fail();
} catch (ParserException exception) {
assertEquals("AESSettingsCipherMode 0 not supported", exception.getMessage());
@ -431,11 +404,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSample(0, media, 0, true, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSample(0, media, 0, true, false, null,
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
}
public void testReadSampleKeyframeStripped() throws IOException, InterruptedException {
@ -451,12 +425,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, sampleBytes)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSample(0, unstrippedSampleBytes, 0, true, false, null,
getTrackOutput(VIDEO_TRACK_NUMBER));
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
}
public void testReadSampleKeyframeManyBytesStripped() throws IOException, InterruptedException {
@ -472,12 +446,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, sampleBytes)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSample(0, unstrippedSampleBytes, 0, true, false, null,
getTrackOutput(VIDEO_TRACK_NUMBER));
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
}
public void testReadTwoTrackSamples() throws IOException, InterruptedException {
@ -494,14 +468,16 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertTracksEnded(extractorOutput);
assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
assertSample(0, media, 0, true, false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null,
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
assertSample(0, media, 0, true, false, null,
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
}
public void testReadTwoTrackSamplesWithSkippedTrack() throws IOException, InterruptedException {
@ -521,14 +497,16 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertTracksEnded(extractorOutput);
assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
assertSample(0, media, 0, true, false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null,
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
assertSample(0, media, 0, true, false, null,
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
}
public void testReadBlock() throws IOException, InterruptedException {
@ -542,11 +520,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
assertTracksEnded(extractorOutput);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null,
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
}
public void testReadBlockNonKeyframe() throws IOException, InterruptedException {
@ -559,11 +538,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
false /* keyframe */, false /* invisible */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSample(0, media, 0, false, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSample(0, media, 0, false, false, null, getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER
));
}
public void testReadEncryptedFrame() throws IOException, InterruptedException {
@ -578,12 +558,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* validSignalByte */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSample(0, media, 0, true, false, TEST_ENCRYPTION_KEY_ID,
getTrackOutput(VIDEO_TRACK_NUMBER));
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
}
public void testReadEncryptedFrameWithInvalidSignalByte()
@ -600,10 +580,9 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
.build(1);
try {
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
fail();
} catch (ParserException exception) {
assertTracksEnded();
assertEquals("Extension bit is set in signal byte", exception.getMessage());
}
}
@ -618,11 +597,13 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
false /* keyframe */, true /* invisible */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSample(0, media, 25000, false, true, null, getTrackOutput(VIDEO_TRACK_NUMBER));
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSample(0, media, 25000, false, true, null, getTrackOutput(extractorOutput,
VIDEO_TRACK_NUMBER
));
}
public void testReadSampleCustomTimecodeScale() throws IOException, InterruptedException {
@ -636,11 +617,13 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
false /* keyframe */, false /* invisible */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSample(0, media, 25, false, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSample(0, media, 25, false, false, null, getTrackOutput(extractorOutput,
VIDEO_TRACK_NUMBER
));
}
public void testReadSampleNegativeSimpleBlockTimecode() throws IOException, InterruptedException {
@ -653,11 +636,13 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
true /* keyframe */, true /* invisible */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
assertSample(0, media, 1000, true, true, null, getTrackOutput(VIDEO_TRACK_NUMBER));
assertTracksEnded(extractorOutput);
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
assertSample(0, media, 1000, true, true, null, getTrackOutput(extractorOutput,
VIDEO_TRACK_NUMBER
));
}
public void testReadSampleWithFixedSizeLacing() throws IOException, InterruptedException {
@ -671,14 +656,14 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
0 /* blockTimecode */, 20 /* lacingFrameCount */, media)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertTracksEnded(extractorOutput);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
for (int i = 0; i < 20; i++) {
long expectedTimeUs = i * TEST_DEFAULT_DURATION_NS / 1000;
assertSample(i, Arrays.copyOfRange(media, i * 5, i * 5 + 5), expectedTimeUs, true, false,
null, getTrackOutput(AUDIO_TRACK_NUMBER));
null, getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
}
}
@ -693,42 +678,43 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
0 /* blockTimecode */, media, 256, 1, 243)
.build(1);
TestUtil.consumeTestData(extractor, data);
FakeExtractorOutput extractorOutput = consumeTestData(data);
assertTracksEnded();
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertTracksEnded(extractorOutput);
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
assertSample(0, Arrays.copyOfRange(media, 0, 256), 0 * TEST_DEFAULT_DURATION_NS / 1000, true,
false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
false, null, getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
assertSample(1, Arrays.copyOfRange(media, 256, 257), 1 * TEST_DEFAULT_DURATION_NS / 1000, true,
false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
false, null, getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
assertSample(2, Arrays.copyOfRange(media, 257, 300), 2 * TEST_DEFAULT_DURATION_NS / 1000, true,
false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
false, null, getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
}
private FakeTrackOutput getTrackOutput(int trackNumber) {
private FakeTrackOutput getTrackOutput(FakeExtractorOutput extractorOutput, int trackNumber) {
return extractorOutput.trackOutputs.get(trackNumber);
}
private void assertTracksEnded() {
private void assertTracksEnded(FakeExtractorOutput extractorOutput) {
assertTrue(extractorOutput.tracksEnded);
}
private void assertVp9VideoFormat(int trackNumber) {
Format format = getTrackOutput(trackNumber).format;
private void assertVp9VideoFormat(FakeExtractorOutput extractorOutput, int trackNumber) {
Format format = getTrackOutput(extractorOutput, trackNumber).format;
assertEquals(TEST_WIDTH, format.width);
assertEquals(TEST_HEIGHT, format.height);
assertEquals(MimeTypes.VIDEO_VP9, format.sampleMimeType);
}
private void assertH264VideoFormat(int trackNumber) {
Format format = getTrackOutput(trackNumber).format;
private void assertH264VideoFormat(FakeExtractorOutput extractorOutput, int trackNumber) {
Format format = getTrackOutput(extractorOutput, trackNumber).format;
assertEquals(TEST_WIDTH, format.width);
assertEquals(TEST_HEIGHT, format.height);
assertEquals(MimeTypes.VIDEO_H264, format.sampleMimeType);
}
private void assertAudioFormat(int trackNumber, String expectedMimeType) {
Format format = getTrackOutput(trackNumber).format;
private void assertAudioFormat(FakeExtractorOutput extractorOutput, int trackNumber,
String expectedMimeType) {
Format format = getTrackOutput(extractorOutput, trackNumber).format;
assertEquals(TEST_CHANNEL_COUNT, format.channelCount);
assertEquals(TEST_SAMPLE_RATE, format.sampleRate);
assertEquals(expectedMimeType, format.sampleMimeType);
@ -747,8 +733,8 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
}
}
private void assertDrmInitData(int trackNumber) {
DrmInitData drmInitData = getTrackOutput(trackNumber).format.drmInitData;
private void assertDrmInitData(FakeExtractorOutput extractorOutput, int trackNumber) {
DrmInitData drmInitData = getTrackOutput(extractorOutput, trackNumber).format.drmInitData;
assertNotNull(drmInitData);
SchemeData widevineInitData = drmInitData.get(WIDEVINE_UUID);
assertEquals(MimeTypes.VIDEO_WEBM, widevineInitData.mimeType);
@ -758,7 +744,8 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, zeroInitData.data);
}
private void assertSeekMap(int timecodeScale, int cuePointCount) {
private void assertSeekMap(FakeExtractorOutput extractorOutput, int timecodeScale,
int cuePointCount) {
ChunkIndex index = (ChunkIndex) extractorOutput.seekMap;
assertEquals(cuePointCount, index.length);
for (int i = 0; i < cuePointCount - 1; i++) {
@ -777,7 +764,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
extractorOutput.seekMap.getDurationUs());
}
private void assertSeekMapUnseekable(long timecodeScale) {
private void assertSeekMapUnseekable(FakeExtractorOutput extractorOutput, long timecodeScale) {
assertFalse(extractorOutput.seekMap.isSeekable());
long expectedDurationUs = Util.scaleLargeTimestamp(TEST_DURATION_TIMECODE, timecodeScale, 1000);
assertEquals(expectedDurationUs, extractorOutput.seekMap.getDurationUs());

View file

@ -25,9 +25,9 @@ import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util;
import android.annotation.TargetApi;
import junit.framework.TestCase;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
@ -100,25 +100,8 @@ public final class Mp4ExtractorTest extends TestCase {
/** Empty byte array. */
private static final byte[] EMPTY = new byte[0];
private Mp4Extractor extractor;
private FakeExtractorOutput extractorOutput;
@Override
public void setUp() {
extractor = new Mp4Extractor();
extractorOutput = new FakeExtractorOutput();
extractor.init(extractorOutput);
}
@Override
public void tearDown() {
extractor = null;
extractorOutput = null;
}
public void testParsesValidMp4File() throws Exception {
TestUtil.consumeTestData(extractor,
getTestInputData(true /* includeStss */, false /* mp4vFormat */));
FakeExtractorOutput extractorOutput = consumeTestData(true, false);
// The seek map is correct.
assertSeekMap(extractorOutput.seekMap, true);
@ -144,8 +127,7 @@ public final class Mp4ExtractorTest extends TestCase {
}
public void testParsesValidMp4FileWithoutStss() throws Exception {
TestUtil.consumeTestData(extractor,
getTestInputData(false /* includeStss */, false /* mp4vFormat */));
FakeExtractorOutput extractorOutput = consumeTestData(false, false);
// The seek map is correct.
assertSeekMap(extractorOutput.seekMap, false);
@ -162,8 +144,7 @@ public final class Mp4ExtractorTest extends TestCase {
}
public void testParsesValidMp4vFile() throws Exception {
TestUtil.consumeTestData(extractor,
getTestInputData(true /* includeStss */, true /* mp4vFormat */));
FakeExtractorOutput extractorOutput = consumeTestData(true, true);
// The seek map is correct.
assertSeekMap(extractorOutput.seekMap, true);
@ -364,9 +345,11 @@ public final class Mp4ExtractorTest extends TestCase {
return CHUNK_OFFSETS[chunkIndex] + offsetInChunk;
}
private static byte[] getTestInputData(boolean includeStss, boolean mp4vFormat) {
return includeStss ? getTestMp4File(mp4vFormat)
private static FakeExtractorOutput consumeTestData(boolean includeStss, boolean mp4vFormat)
throws IOException, InterruptedException {
byte[] testInputData = includeStss ? getTestMp4File(mp4vFormat)
: getTestMp4FileWithoutSynchronizationData(mp4vFormat);
return TestUtil.consumeTestData(new Mp4Extractor(), testInputData);
}
/** Gets a valid MP4 file with audio/video tracks and synchronization data. */

View file

@ -34,6 +34,7 @@ public final class OggExtractorFileTests extends InstrumentationTestCase {
public static final String OPUS_TEST_FILE = "ogg/bear.opus";
public static final String FLAC_TEST_FILE = "ogg/bear_flac.ogg";
public static final String FLAC_NS_TEST_FILE = "ogg/bear_flac_noseektable.ogg";
public static final String VORBIS_TEST_FILE = "ogg/bear_vorbis.ogg";
public void testOpus() throws Exception {
parseFile(OPUS_TEST_FILE, false, false, false, MimeTypes.AUDIO_OPUS, 2747500, 275);
@ -66,6 +67,13 @@ public final class OggExtractorFileTests extends InstrumentationTestCase {
parseFile(FLAC_NS_TEST_FILE, true, true, true, MimeTypes.AUDIO_FLAC, C.UNSET_TIME_US, 33);
}
public void testVorbis() throws Exception {
parseFile(VORBIS_TEST_FILE, false, false, false, MimeTypes.AUDIO_VORBIS, 2741000, 180);
parseFile(VORBIS_TEST_FILE, false, true, false, MimeTypes.AUDIO_VORBIS, C.UNSET_TIME_US, 180);
parseFile(VORBIS_TEST_FILE, true, false, true, MimeTypes.AUDIO_VORBIS, 2741000, 180);
parseFile(VORBIS_TEST_FILE, true, true, true, MimeTypes.AUDIO_VORBIS, C.UNSET_TIME_US, 180);
}
private FakeTrackOutput parseFile(String testFile, boolean simulateIOErrors,
boolean simulateUnknownLength, boolean simulatePartialReads, String expectedMimeType,
long expectedDuration, int expectedSampleCount) throws Exception {
@ -78,9 +86,7 @@ public final class OggExtractorFileTests extends InstrumentationTestCase {
OggExtractor extractor = new OggExtractor();
assertTrue(TestUtil.sniffTestData(extractor, input));
input.resetPeekPosition();
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
extractor.init(extractorOutput);
TestUtil.consumeTestData(extractor, input);
FakeExtractorOutput extractorOutput = TestUtil.consumeTestData(extractor, input, true);
assertEquals(1, extractorOutput.trackOutputs.size());
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);

View file

@ -50,6 +50,15 @@ public final class FakeTrackOutput implements TrackOutput {
sampleEncryptionKeys = new ArrayList<>();
}
public void clear() {
sampleData = new byte[0];
sampleTimesUs.clear();
sampleFlags.clear();
sampleStartOffsets.clear();
sampleEndOffsets.clear();
sampleEncryptionKeys.clear();
}
@Override
public void format(Format format) {
this.format = format;

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer.testutil;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.PositionHolder;
import com.google.android.exoplayer.testutil.FakeExtractorInput.SimulatedIOException;
@ -52,13 +53,21 @@ public class TestUtil {
}
}
public static void consumeTestData(Extractor extractor, byte[] data)
public static FakeExtractorOutput consumeTestData(Extractor extractor, byte[] data)
throws IOException, InterruptedException {
consumeTestData(extractor, newExtractorInput(data));
return consumeTestData(extractor, newExtractorInput(data));
}
public static void consumeTestData(Extractor extractor, FakeExtractorInput input)
public static FakeExtractorOutput consumeTestData(Extractor extractor, FakeExtractorInput input)
throws IOException, InterruptedException {
return consumeTestData(extractor, input, false);
}
public static FakeExtractorOutput consumeTestData(Extractor extractor, FakeExtractorInput input,
boolean retryFromStartIfLive) throws IOException, InterruptedException {
FakeExtractorOutput output = new FakeExtractorOutput();
extractor.init(output);
PositionHolder seekPositionHolder = new PositionHolder();
int readResult = Extractor.RESULT_CONTINUE;
while (readResult != Extractor.RESULT_END_OF_INPUT) {
@ -70,9 +79,22 @@ public class TestUtil {
input.setPosition((int) seekPosition);
}
} catch (SimulatedIOException e) {
// Ignore.
if (!retryFromStartIfLive) {
continue;
}
boolean isOnDemand = input.getLength() != C.LENGTH_UNBOUNDED
|| (output.seekMap != null && output.seekMap.getDurationUs() != C.UNSET_TIME_US);
if (isOnDemand) {
continue;
}
input.setPosition(0);
for (int i = 0; i < output.numberOfTracks; i++) {
output.trackOutputs.valueAt(i).clear();
}
extractor.seek(0);
}
}
return output;
}
public static byte[] buildTestData(int length) {

View file

@ -81,7 +81,7 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
extractor.init(this);
extractorInitialized = true;
} else {
extractor.seek();
extractor.seek(0);
}
}

View file

@ -90,12 +90,13 @@ public interface Extractor {
* Notifies the extractor that a seek has occurred.
* <p>
* Following a call to this method, the {@link ExtractorInput} passed to the next invocation of
* {@link #read(ExtractorInput, PositionHolder)} is required to provide data starting from a
* random access position in the stream. Valid random access positions are the start of the
* stream and positions that can be obtained from any {@link SeekMap} passed to the
* {@link ExtractorOutput}.
* {@link #read(ExtractorInput, PositionHolder)} is required to provide data starting from {@code
* position} in the stream. Valid random access positions are the start of the stream and
* positions that can be obtained from any {@link SeekMap} passed to the {@link ExtractorOutput}.
*
* @param position The seek position.
*/
void seek();
void seek(long position);
/**
* Releases all kept resources.

View file

@ -134,6 +134,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private TrackGroupArray tracks;
private long durationUs;
private boolean[] trackEnabledStates;
private long length;
private long downstreamPositionUs;
private long lastSeekPositionUs;
@ -237,6 +238,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
extractorHolder = new ExtractorHolder(extractors, this);
pendingResetPositionUs = C.UNSET_TIME_US;
sampleQueues = new DefaultTrackOutput[0];
length = C.LENGTH_UNBOUNDED;
}
/**
@ -491,11 +493,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
@Override
public void onLoadCompleted(Loadable loadable, long elapsedMs) {
copyLengthFromLoader();
loadingFinished = true;
}
@Override
public void onLoadCanceled(Loadable loadable, long elapsedMs) {
copyLengthFromLoader();
if (enabledTrackCount > 0) {
restartFrom(pendingResetPositionUs);
} else {
@ -506,6 +510,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
@Override
public int onLoadError(Loadable loadable, long elapsedMs, IOException e) {
copyLengthFromLoader();
notifyLoadError(e);
if (isLoadableExceptionFatal(e)) {
return Loader.DONT_RETRY_FATAL;
@ -539,6 +544,12 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
// Internal methods.
private void copyLengthFromLoader() {
if (length == C.LENGTH_UNBOUNDED) {
length = loadable.length;
}
}
private void seekToInternal(long positionUs) {
// Treat all seeks into non-seekable media as being to t=0.
positionUs = seekMap.isSeekable() ? positionUs : 0;
@ -588,23 +599,19 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private void configureRetry() {
Assertions.checkState(loadable != null);
if (!prepared) {
// We don't know whether we're playing an on-demand or a live stream. For a live stream we
// need to load from the start, as outlined below. Since we might be playing a live stream,
// play it safe and load from the start.
clearSampleQueues();
loadable.setLoadPosition(0);
} else if (!seekMap.isSeekable() && durationUs == C.UNSET_TIME_US) {
// We're playing a non-seekable stream with unknown duration. Assume it's live, and
// therefore that the data at the uri is a continuously shifting window of the latest
// available media. For this case there's no way to continue loading from where a previous
// load finished, so it's necessary to load from the start whenever commencing a new load.
notifyReset = true;
clearSampleQueues();
loadable.setLoadPosition(0);
} else {
// We're playing a seekable on-demand stream. Resume the current loadable, which will
if (length != C.LENGTH_UNBOUNDED
|| (seekMap != null && seekMap.getDurationUs() != C.UNSET_TIME_US)) {
// We're playing an on-demand stream. Resume the current loadable, which will
// request data starting from the point it left off.
} else {
// We're playing a stream of unknown length and duration. Assume it's live, and
// therefore that the data at the uri is a continuously shifting window of the latest
// available media. For this case there's no way to continue loading from where a
// previous load finished, so it's necessary to load from the start whenever commencing
// a new load.
notifyReset = prepared;
clearSampleQueues();
loadable.setLoadPosition(0);
}
}
@ -703,6 +710,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private volatile boolean loadCanceled;
private boolean pendingExtractorSeek;
private long length;
public ExtractingLoadable(Uri uri, DataSource dataSource, ExtractorHolder extractorHolder,
Allocator allocator, int requestedBufferSize) {
@ -711,8 +719,9 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
this.extractorHolder = Assertions.checkNotNull(extractorHolder);
this.allocator = Assertions.checkNotNull(allocator);
this.requestedBufferSize = requestedBufferSize;
positionHolder = new PositionHolder();
pendingExtractorSeek = true;
this.positionHolder = new PositionHolder();
this.pendingExtractorSeek = true;
this.length = C.LENGTH_UNBOUNDED;
}
public void setLoadPosition(long position) {
@ -737,14 +746,14 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
ExtractorInput input = null;
try {
long position = positionHolder.position;
long length = dataSource.open(new DataSpec(uri, position, C.LENGTH_UNBOUNDED, null));
length = dataSource.open(new DataSpec(uri, position, C.LENGTH_UNBOUNDED, null));
if (length != C.LENGTH_UNBOUNDED) {
length += position;
}
input = new DefaultExtractorInput(dataSource, position, length);
Extractor extractor = extractorHolder.selectExtractor(input);
if (pendingExtractorSeek) {
extractor.seek();
extractor.seek(position);
pendingExtractorSeek = false;
}
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {

View file

@ -114,7 +114,7 @@ public final class FlvExtractor implements Extractor, SeekMap {
}
@Override
public void seek() {
public void seek(long position) {
parserState = STATE_READING_FLV_HEADER;
bytesToNextTagHeader = 0;
}

View file

@ -289,7 +289,7 @@ public final class MatroskaExtractor implements Extractor {
}
@Override
public void seek() {
public void seek(long position) {
clusterTimecodeUs = UNKNOWN;
blockState = BLOCK_STATE_START;
reader.reset();

View file

@ -104,7 +104,7 @@ public final class Mp3Extractor implements Extractor {
}
@Override
public void seek() {
public void seek(long position) {
synchronizedHeaderData = 0;
samplesRead = 0;
basisTimeUs = -1;

View file

@ -165,7 +165,7 @@ public final class FragmentedMp4Extractor implements Extractor {
}
@Override
public void seek() {
public void seek(long position) {
int trackCount = trackBundles.size();
for (int i = 0; i < trackCount; i++) {
trackBundles.valueAt(i).reset();

View file

@ -97,7 +97,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}
@Override
public void seek() {
public void seek(long position) {
containerAtoms.clear();
atomHeaderBytesRead = 0;
sampleBytesWritten = 0;

View file

@ -18,7 +18,6 @@ package com.google.android.exoplayer.extractor.ogg;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.FlacStreamInfo;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParsableByteArray;
@ -47,6 +46,15 @@ import java.util.List;
data.readUnsignedInt() == 0x464C4143; // ASCII signature "FLAC"
}
@Override
protected void reset(boolean headerData) {
super.reset(headerData);
if (headerData) {
streamInfo = null;
flacOggSeeker = null;
}
}
//@VisibleForTesting
public static boolean isAudioPacket(byte[] data) {
return data[0] == AUDIO_PACKET_TYPE;
@ -73,7 +81,6 @@ import java.util.List;
streamInfo.bitRate(), streamInfo.channels, streamInfo.sampleRate, initializationData,
null, 0, null);
} else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) {
Assertions.checkArgument(flacOggSeeker == null);
flacOggSeeker = new FlacOggSeeker();
flacOggSeeker.parseSeekTable(packet);
} else if (isAudioPacket(data)) {

View file

@ -70,8 +70,8 @@ public class OggExtractor implements Extractor {
}
@Override
public void seek() {
streamReader.seek();
public void seek(long position) {
streamReader.seek(position);
}
@Override

View file

@ -19,6 +19,7 @@ import com.google.android.exoplayer.C;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParsableByteArray;
import com.google.android.exoplayer.util.Util;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -39,9 +40,10 @@ import java.util.List;
*/
private static final int SAMPLE_RATE = 48000;
private static final int OPUS_CODE = Util.getIntegerCodeForString("Opus");
private static final byte[] OPUS_SIGNATURE = {'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'};
private boolean headerRead;
private boolean tagsSkipped;
public static boolean verifyBitstreamType(ParsableByteArray data) {
if (data.bytesLeft() < OPUS_SIGNATURE.length) {
@ -52,6 +54,14 @@ import java.util.List;
return Arrays.equals(header, OPUS_SIGNATURE);
}
@Override
protected void reset(boolean headerData) {
super.reset(headerData);
if (headerData) {
headerRead = false;
}
}
@Override
protected long preparePayload(ParsableByteArray packet) {
return convertTimeToGranule(getPacketDurationUs(packet.data));
@ -73,11 +83,10 @@ import java.util.List;
setupData.format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_OPUS, Format.NO_VALUE,
Format.NO_VALUE, channelCount, SAMPLE_RATE, initializationData, null, 0, "und");
headerRead = true;
} else if (!tagsSkipped) {
// Skip tags packet
tagsSkipped = true;
} else {
return false;
boolean headerPacket = packet.readInt() == OPUS_CODE;
packet.setPosition(0);
return headerPacket;
}
return true;
}

View file

@ -18,8 +18,9 @@ import java.io.IOException;
/* package */ abstract class StreamReader {
private static final int STATE_READ_HEADERS = 0;
private static final int STATE_READ_PAYLOAD = 1;
private static final int STATE_END_OF_INPUT = 2;
private static final int STATE_SKIP_HEADERS = 1;
private static final int STATE_READ_PAYLOAD = 2;
private static final int STATE_END_OF_INPUT = 3;
static class SetupData {
Format format;
@ -38,27 +39,44 @@ import java.io.IOException;
private SetupData setupData;
private long lengthOfReadPacket;
private boolean seekMapSet;
private boolean formatSet;
void init(ExtractorOutput output, TrackOutput trackOutput) {
this.extractorOutput = output;
this.trackOutput = trackOutput;
this.oggPacket = new OggPacket();
this.setupData = new SetupData();
this.state = STATE_READ_HEADERS;
this.targetGranule = -1;
this.payloadStartPosition = 0;
reset(true);
}
/**
* @see Extractor#seek()
* Resets the state of the {@link StreamReader}.
* @param headerData Resets parsed header data too.
*/
final void seek() {
oggPacket.reset();
protected void reset(boolean headerData) {
if (headerData) {
setupData = new SetupData();
payloadStartPosition = 0;
state = STATE_READ_HEADERS;
} else {
state = STATE_SKIP_HEADERS;
}
targetGranule = -1;
currentGranule = 0;
}
if (state != STATE_READ_HEADERS) {
targetGranule = oggSeeker.startSeek();
state = STATE_READ_PAYLOAD;
/**
* @see Extractor#seek(long)
*/
final void seek(long position) {
oggPacket.reset();
if (position == 0) {
reset(!seekMapSet);
} else {
if (state != STATE_READ_HEADERS) {
targetGranule = oggSeeker.startSeek();
state = STATE_READ_PAYLOAD;
}
}
}
@ -71,6 +89,11 @@ import java.io.IOException;
case STATE_READ_HEADERS:
return readHeaders(input);
case STATE_SKIP_HEADERS:
input.skipFully((int) payloadStartPosition);
state = STATE_READ_PAYLOAD;
return Extractor.RESULT_CONTINUE;
case STATE_READ_PAYLOAD:
return readPayload(input, seekPosition);
@ -80,8 +103,7 @@ import java.io.IOException;
}
}
private int readHeaders(ExtractorInput input)
throws IOException, InterruptedException {
private int readHeaders(ExtractorInput input) throws IOException, InterruptedException {
boolean readingHeaders = true;
while (readingHeaders) {
if (!oggPacket.populate(input)) {
@ -97,7 +119,10 @@ import java.io.IOException;
}
sampleRate = setupData.format.sampleRate;
trackOutput.format(setupData.format);
if (!formatSet) {
trackOutput.format(setupData.format);
formatSet = true;
}
if (setupData.oggSeeker != null) {
oggSeeker = setupData.oggSeeker;

View file

@ -44,6 +44,18 @@ import java.util.ArrayList;
}
}
@Override
protected void reset(boolean headerData) {
super.reset(headerData);
if (headerData) {
vorbisSetup = null;
vorbisIdHeader = null;
commentHeader = null;
}
previousPacketBlockSize = 0;
seenFirstAudioPacket = false;
}
@Override
protected void onSeekEnd(long currentGranule) {
super.onSeekEnd(currentGranule);

View file

@ -120,7 +120,7 @@ public final class AdtsExtractor implements Extractor {
}
@Override
public void seek() {
public void seek(long position) {
startedPacket = false;
adtsReader.seek();
}

View file

@ -113,7 +113,7 @@ public final class PsExtractor implements Extractor {
}
@Override
public void seek() {
public void seek(long position) {
ptsTimestampAdjuster.reset();
for (int i = 0; i < psPayloadReaders.size(); i++) {
psPayloadReaders.valueAt(i).seek();

View file

@ -116,7 +116,7 @@ public final class TsExtractor implements Extractor {
}
@Override
public void seek() {
public void seek(long position) {
ptsTimestampAdjuster.reset();
for (int i = 0; i < tsPayloadReaders.size(); i++) {
tsPayloadReaders.valueAt(i).seek();

View file

@ -54,7 +54,7 @@ public final class WavExtractor implements Extractor, SeekMap {
}
@Override
public void seek() {
public void seek(long position) {
pendingBytes = 0;
}

View file

@ -80,7 +80,7 @@ import java.util.regex.Pattern;
}
@Override
public void seek() {
public void seek(long position) {
// This extractor is only used for the HLS use case, which should not call this method.
throw new IllegalStateException();
}