mirror of
https://github.com/samsonjs/media.git
synced 2026-04-10 12:05:47 +00:00
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:
parent
c2b89d6285
commit
17f8ac8adf
25 changed files with 324 additions and 254 deletions
|
|
@ -127,7 +127,7 @@ public final class FlacExtractor implements Extractor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void seek() {
|
||||
public void seek(long position) {
|
||||
decoder.flush();
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
library/src/androidTest/assets/ogg/bear_vorbis.ogg
Normal file
BIN
library/src/androidTest/assets/ogg/bear_vorbis.ogg
Normal file
Binary file not shown.
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
|
|||
extractor.init(this);
|
||||
extractorInitialized = true;
|
||||
} else {
|
||||
extractor.seek();
|
||||
extractor.seek(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ public class OggExtractor implements Extractor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void seek() {
|
||||
streamReader.seek();
|
||||
public void seek(long position) {
|
||||
streamReader.seek(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ public final class AdtsExtractor implements Extractor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void seek() {
|
||||
public void seek(long position) {
|
||||
startedPacket = false;
|
||||
adtsReader.seek();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public final class WavExtractor implements Extractor, SeekMap {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void seek() {
|
||||
public void seek(long position) {
|
||||
pendingBytes = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue