mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
decoder.flush();
|
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 MATROSKA_DOC_TYPE = "matroska";
|
||||||
private static final String WEBM_DOC_TYPE = "webm";
|
private static final String WEBM_DOC_TYPE = "webm";
|
||||||
|
|
||||||
private MatroskaExtractor extractor;
|
private FakeExtractorOutput consumeTestData(byte[] data)
|
||||||
private FakeExtractorOutput extractorOutput;
|
throws IOException, InterruptedException {
|
||||||
|
return TestUtil.consumeTestData(new MatroskaExtractor(), data);
|
||||||
@Override
|
|
||||||
public void setUp() {
|
|
||||||
extractor = new MatroskaExtractor();
|
|
||||||
extractorOutput = new FakeExtractorOutput();
|
|
||||||
extractor.init(extractorOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tearDown() {
|
|
||||||
extractor = null;
|
|
||||||
extractorOutput = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadInitializationSegment() throws IOException, InterruptedException {
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
assertSeekMap(extractorOutput, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareOpus() throws IOException, InterruptedException {
|
public void testPrepareOpus() throws IOException, InterruptedException {
|
||||||
|
|
@ -124,11 +97,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareVorbis() throws IOException, InterruptedException {
|
public void testPrepareVorbis() throws IOException, InterruptedException {
|
||||||
|
|
@ -139,11 +112,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
getVorbisCodecPrivate())
|
getVorbisCodecPrivate())
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareH264() throws IOException, InterruptedException {
|
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)
|
.addH264Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, TEST_H264_CODEC_PRIVATE)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertH264VideoFormat(VIDEO_TRACK_NUMBER);
|
assertH264VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareTwoTracks() throws IOException, InterruptedException {
|
public void testPrepareTwoTracks() throws IOException, InterruptedException {
|
||||||
|
|
@ -169,13 +142,13 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertEquals(2, extractorOutput.numberOfTracks);
|
assertEquals(2, extractorOutput.numberOfTracks);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareThreeTracks() throws IOException, InterruptedException {
|
public void testPrepareThreeTracks() throws IOException, InterruptedException {
|
||||||
|
|
@ -188,14 +161,14 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
||||||
.build(1);
|
.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.
|
// Even though the input stream has 3 tracks, only 2 of them are supported and will be reported.
|
||||||
assertEquals(2, extractorOutput.numberOfTracks);
|
assertEquals(2, extractorOutput.numberOfTracks);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareFourTracks() throws IOException, InterruptedException {
|
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)
|
TEST_CODEC_DELAY, TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertEquals(4, extractorOutput.numberOfTracks);
|
assertEquals(4, extractorOutput.numberOfTracks);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
|
||||||
assertVp9VideoFormat(SECOND_VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, SECOND_VIDEO_TRACK_NUMBER);
|
||||||
assertAudioFormat(SECOND_AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, SECOND_AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException {
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertDrmInitData(VIDEO_TRACK_NUMBER);
|
assertDrmInitData(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareThreeCuePoints() throws IOException, InterruptedException {
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
||||||
.build(3);
|
.build(3);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 3);
|
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareCustomTimecodeScaleBeforeDuration()
|
public void testPrepareCustomTimecodeScaleBeforeDuration()
|
||||||
|
|
@ -274,11 +247,11 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
||||||
.build(3);
|
.build(3);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSeekMap(timecodeScale, 3);
|
assertSeekMap(extractorOutput, timecodeScale, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareNoCuesElement() throws IOException, InterruptedException {
|
public void testPrepareNoCuesElement() throws IOException, InterruptedException {
|
||||||
|
|
@ -301,10 +274,10 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* keyframe */, false /* invisible */, media)
|
true /* keyframe */, false /* invisible */, media)
|
||||||
.build(0);
|
.build(0);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertSeekMapUnseekable(timecodeScale);
|
assertSeekMapUnseekable(extractorOutput, timecodeScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAcceptsWebmDocType() throws IOException, InterruptedException {
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAcceptsMatroskaDocType() throws IOException, InterruptedException {
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPrepareInvalidDocType() throws IOException, InterruptedException {
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
||||||
.build(1);
|
.build(1);
|
||||||
try {
|
try {
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
fail();
|
fail();
|
||||||
} catch (ParserException exception) {
|
} catch (ParserException exception) {
|
||||||
assertEquals("DocType webB not supported", exception.getMessage());
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
||||||
.build(1);
|
.build(1);
|
||||||
try {
|
try {
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
fail();
|
fail();
|
||||||
} catch (ParserException exception) {
|
} catch (ParserException exception) {
|
||||||
assertEquals("ContentEncodingOrder 1 not supported", exception.getMessage());
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
||||||
.build(1);
|
.build(1);
|
||||||
try {
|
try {
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
fail();
|
fail();
|
||||||
} catch (ParserException exception) {
|
} catch (ParserException exception) {
|
||||||
assertEquals("ContentEncodingScope 0 not supported", exception.getMessage());
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
||||||
.build(1);
|
.build(1);
|
||||||
try {
|
try {
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
fail();
|
fail();
|
||||||
} catch (ParserException exception) {
|
} catch (ParserException exception) {
|
||||||
assertEquals("ContentCompAlgo 0 not supported", exception.getMessage());
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
||||||
.build(1);
|
.build(1);
|
||||||
try {
|
try {
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
fail();
|
fail();
|
||||||
} catch (ParserException exception) {
|
} catch (ParserException exception) {
|
||||||
assertEquals("ContentEncAlgo 4 not supported", exception.getMessage());
|
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)
|
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
||||||
.build(1);
|
.build(1);
|
||||||
try {
|
try {
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
fail();
|
fail();
|
||||||
} catch (ParserException exception) {
|
} catch (ParserException exception) {
|
||||||
assertEquals("AESSettingsCipherMode 0 not supported", exception.getMessage());
|
assertEquals("AESSettingsCipherMode 0 not supported", exception.getMessage());
|
||||||
|
|
@ -431,11 +404,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* keyframe */, false /* invisible */, media)
|
true /* keyframe */, false /* invisible */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSample(0, media, 0, true, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
|
assertSample(0, media, 0, true, false, null,
|
||||||
|
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadSampleKeyframeStripped() throws IOException, InterruptedException {
|
public void testReadSampleKeyframeStripped() throws IOException, InterruptedException {
|
||||||
|
|
@ -451,12 +425,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* keyframe */, false /* invisible */, sampleBytes)
|
true /* keyframe */, false /* invisible */, sampleBytes)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSample(0, unstrippedSampleBytes, 0, true, false, null,
|
assertSample(0, unstrippedSampleBytes, 0, true, false, null,
|
||||||
getTrackOutput(VIDEO_TRACK_NUMBER));
|
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadSampleKeyframeManyBytesStripped() throws IOException, InterruptedException {
|
public void testReadSampleKeyframeManyBytesStripped() throws IOException, InterruptedException {
|
||||||
|
|
@ -472,12 +446,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* keyframe */, false /* invisible */, sampleBytes)
|
true /* keyframe */, false /* invisible */, sampleBytes)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSample(0, unstrippedSampleBytes, 0, true, false, null,
|
assertSample(0, unstrippedSampleBytes, 0, true, false, null,
|
||||||
getTrackOutput(VIDEO_TRACK_NUMBER));
|
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadTwoTrackSamples() throws IOException, InterruptedException {
|
public void testReadTwoTrackSamples() throws IOException, InterruptedException {
|
||||||
|
|
@ -494,14 +468,16 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* keyframe */, false /* invisible */, media)
|
true /* keyframe */, false /* invisible */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertEquals(2, extractorOutput.numberOfTracks);
|
assertEquals(2, extractorOutput.numberOfTracks);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
assertSample(0, media, 0, true, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
|
assertSample(0, media, 0, true, false, null,
|
||||||
assertSample(0, media, 0, true, false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
|
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
||||||
|
assertSample(0, media, 0, true, false, null,
|
||||||
|
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadTwoTrackSamplesWithSkippedTrack() throws IOException, InterruptedException {
|
public void testReadTwoTrackSamplesWithSkippedTrack() throws IOException, InterruptedException {
|
||||||
|
|
@ -521,14 +497,16 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* keyframe */, false /* invisible */, media)
|
true /* keyframe */, false /* invisible */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertEquals(2, extractorOutput.numberOfTracks);
|
assertEquals(2, extractorOutput.numberOfTracks);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
assertSample(0, media, 0, true, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
|
assertSample(0, media, 0, true, false, null,
|
||||||
assertSample(0, media, 0, true, false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
|
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
||||||
|
assertSample(0, media, 0, true, false, null,
|
||||||
|
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadBlock() throws IOException, InterruptedException {
|
public void testReadBlock() throws IOException, InterruptedException {
|
||||||
|
|
@ -542,11 +520,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* keyframe */, false /* invisible */, media)
|
true /* keyframe */, false /* invisible */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
assertSample(0, media, 0, true, false, null, getTrackOutput(AUDIO_TRACK_NUMBER));
|
assertSample(0, media, 0, true, false, null,
|
||||||
|
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadBlockNonKeyframe() throws IOException, InterruptedException {
|
public void testReadBlockNonKeyframe() throws IOException, InterruptedException {
|
||||||
|
|
@ -559,11 +538,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
false /* keyframe */, false /* invisible */, media)
|
false /* keyframe */, false /* invisible */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSample(0, media, 0, false, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
|
assertSample(0, media, 0, false, false, null, getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadEncryptedFrame() throws IOException, InterruptedException {
|
public void testReadEncryptedFrame() throws IOException, InterruptedException {
|
||||||
|
|
@ -578,12 +558,12 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* validSignalByte */, media)
|
true /* validSignalByte */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSample(0, media, 0, true, false, TEST_ENCRYPTION_KEY_ID,
|
assertSample(0, media, 0, true, false, TEST_ENCRYPTION_KEY_ID,
|
||||||
getTrackOutput(VIDEO_TRACK_NUMBER));
|
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadEncryptedFrameWithInvalidSignalByte()
|
public void testReadEncryptedFrameWithInvalidSignalByte()
|
||||||
|
|
@ -600,10 +580,9 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
fail();
|
fail();
|
||||||
} catch (ParserException exception) {
|
} catch (ParserException exception) {
|
||||||
assertTracksEnded();
|
|
||||||
assertEquals("Extension bit is set in signal byte", exception.getMessage());
|
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)
|
false /* keyframe */, true /* invisible */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSample(0, media, 25000, false, true, null, getTrackOutput(VIDEO_TRACK_NUMBER));
|
assertSample(0, media, 25000, false, true, null, getTrackOutput(extractorOutput,
|
||||||
|
VIDEO_TRACK_NUMBER
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadSampleCustomTimecodeScale() throws IOException, InterruptedException {
|
public void testReadSampleCustomTimecodeScale() throws IOException, InterruptedException {
|
||||||
|
|
@ -636,11 +617,13 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
false /* keyframe */, false /* invisible */, media)
|
false /* keyframe */, false /* invisible */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSample(0, media, 25, false, false, null, getTrackOutput(VIDEO_TRACK_NUMBER));
|
assertSample(0, media, 25, false, false, null, getTrackOutput(extractorOutput,
|
||||||
|
VIDEO_TRACK_NUMBER
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadSampleNegativeSimpleBlockTimecode() throws IOException, InterruptedException {
|
public void testReadSampleNegativeSimpleBlockTimecode() throws IOException, InterruptedException {
|
||||||
|
|
@ -653,11 +636,13 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
true /* keyframe */, true /* invisible */, media)
|
true /* keyframe */, true /* invisible */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertVp9VideoFormat(VIDEO_TRACK_NUMBER);
|
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
||||||
assertSample(0, media, 1000, true, true, null, getTrackOutput(VIDEO_TRACK_NUMBER));
|
assertSample(0, media, 1000, true, true, null, getTrackOutput(extractorOutput,
|
||||||
|
VIDEO_TRACK_NUMBER
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadSampleWithFixedSizeLacing() throws IOException, InterruptedException {
|
public void testReadSampleWithFixedSizeLacing() throws IOException, InterruptedException {
|
||||||
|
|
@ -671,14 +656,14 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
0 /* blockTimecode */, 20 /* lacingFrameCount */, media)
|
0 /* blockTimecode */, 20 /* lacingFrameCount */, media)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
long expectedTimeUs = i * TEST_DEFAULT_DURATION_NS / 1000;
|
long expectedTimeUs = i * TEST_DEFAULT_DURATION_NS / 1000;
|
||||||
assertSample(i, Arrays.copyOfRange(media, i * 5, i * 5 + 5), expectedTimeUs, true, false,
|
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)
|
0 /* blockTimecode */, media, 256, 1, 243)
|
||||||
.build(1);
|
.build(1);
|
||||||
|
|
||||||
TestUtil.consumeTestData(extractor, data);
|
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
||||||
|
|
||||||
assertTracksEnded();
|
assertTracksEnded(extractorOutput);
|
||||||
assertAudioFormat(AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
||||||
assertSample(0, Arrays.copyOfRange(media, 0, 256), 0 * TEST_DEFAULT_DURATION_NS / 1000, true,
|
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,
|
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,
|
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);
|
return extractorOutput.trackOutputs.get(trackNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTracksEnded() {
|
private void assertTracksEnded(FakeExtractorOutput extractorOutput) {
|
||||||
assertTrue(extractorOutput.tracksEnded);
|
assertTrue(extractorOutput.tracksEnded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertVp9VideoFormat(int trackNumber) {
|
private void assertVp9VideoFormat(FakeExtractorOutput extractorOutput, int trackNumber) {
|
||||||
Format format = getTrackOutput(trackNumber).format;
|
Format format = getTrackOutput(extractorOutput, trackNumber).format;
|
||||||
assertEquals(TEST_WIDTH, format.width);
|
assertEquals(TEST_WIDTH, format.width);
|
||||||
assertEquals(TEST_HEIGHT, format.height);
|
assertEquals(TEST_HEIGHT, format.height);
|
||||||
assertEquals(MimeTypes.VIDEO_VP9, format.sampleMimeType);
|
assertEquals(MimeTypes.VIDEO_VP9, format.sampleMimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertH264VideoFormat(int trackNumber) {
|
private void assertH264VideoFormat(FakeExtractorOutput extractorOutput, int trackNumber) {
|
||||||
Format format = getTrackOutput(trackNumber).format;
|
Format format = getTrackOutput(extractorOutput, trackNumber).format;
|
||||||
assertEquals(TEST_WIDTH, format.width);
|
assertEquals(TEST_WIDTH, format.width);
|
||||||
assertEquals(TEST_HEIGHT, format.height);
|
assertEquals(TEST_HEIGHT, format.height);
|
||||||
assertEquals(MimeTypes.VIDEO_H264, format.sampleMimeType);
|
assertEquals(MimeTypes.VIDEO_H264, format.sampleMimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertAudioFormat(int trackNumber, String expectedMimeType) {
|
private void assertAudioFormat(FakeExtractorOutput extractorOutput, int trackNumber,
|
||||||
Format format = getTrackOutput(trackNumber).format;
|
String expectedMimeType) {
|
||||||
|
Format format = getTrackOutput(extractorOutput, trackNumber).format;
|
||||||
assertEquals(TEST_CHANNEL_COUNT, format.channelCount);
|
assertEquals(TEST_CHANNEL_COUNT, format.channelCount);
|
||||||
assertEquals(TEST_SAMPLE_RATE, format.sampleRate);
|
assertEquals(TEST_SAMPLE_RATE, format.sampleRate);
|
||||||
assertEquals(expectedMimeType, format.sampleMimeType);
|
assertEquals(expectedMimeType, format.sampleMimeType);
|
||||||
|
|
@ -747,8 +733,8 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertDrmInitData(int trackNumber) {
|
private void assertDrmInitData(FakeExtractorOutput extractorOutput, int trackNumber) {
|
||||||
DrmInitData drmInitData = getTrackOutput(trackNumber).format.drmInitData;
|
DrmInitData drmInitData = getTrackOutput(extractorOutput, trackNumber).format.drmInitData;
|
||||||
assertNotNull(drmInitData);
|
assertNotNull(drmInitData);
|
||||||
SchemeData widevineInitData = drmInitData.get(WIDEVINE_UUID);
|
SchemeData widevineInitData = drmInitData.get(WIDEVINE_UUID);
|
||||||
assertEquals(MimeTypes.VIDEO_WEBM, widevineInitData.mimeType);
|
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);
|
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;
|
ChunkIndex index = (ChunkIndex) extractorOutput.seekMap;
|
||||||
assertEquals(cuePointCount, index.length);
|
assertEquals(cuePointCount, index.length);
|
||||||
for (int i = 0; i < cuePointCount - 1; i++) {
|
for (int i = 0; i < cuePointCount - 1; i++) {
|
||||||
|
|
@ -777,7 +764,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
extractorOutput.seekMap.getDurationUs());
|
extractorOutput.seekMap.getDurationUs());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSeekMapUnseekable(long timecodeScale) {
|
private void assertSeekMapUnseekable(FakeExtractorOutput extractorOutput, long timecodeScale) {
|
||||||
assertFalse(extractorOutput.seekMap.isSeekable());
|
assertFalse(extractorOutput.seekMap.isSeekable());
|
||||||
long expectedDurationUs = Util.scaleLargeTimestamp(TEST_DURATION_TIMECODE, timecodeScale, 1000);
|
long expectedDurationUs = Util.scaleLargeTimestamp(TEST_DURATION_TIMECODE, timecodeScale, 1000);
|
||||||
assertEquals(expectedDurationUs, extractorOutput.seekMap.getDurationUs());
|
assertEquals(expectedDurationUs, extractorOutput.seekMap.getDurationUs());
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -100,25 +100,8 @@ public final class Mp4ExtractorTest extends TestCase {
|
||||||
/** Empty byte array. */
|
/** Empty byte array. */
|
||||||
private static final byte[] EMPTY = new byte[0];
|
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 {
|
public void testParsesValidMp4File() throws Exception {
|
||||||
TestUtil.consumeTestData(extractor,
|
FakeExtractorOutput extractorOutput = consumeTestData(true, false);
|
||||||
getTestInputData(true /* includeStss */, false /* mp4vFormat */));
|
|
||||||
|
|
||||||
// The seek map is correct.
|
// The seek map is correct.
|
||||||
assertSeekMap(extractorOutput.seekMap, true);
|
assertSeekMap(extractorOutput.seekMap, true);
|
||||||
|
|
@ -144,8 +127,7 @@ public final class Mp4ExtractorTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParsesValidMp4FileWithoutStss() throws Exception {
|
public void testParsesValidMp4FileWithoutStss() throws Exception {
|
||||||
TestUtil.consumeTestData(extractor,
|
FakeExtractorOutput extractorOutput = consumeTestData(false, false);
|
||||||
getTestInputData(false /* includeStss */, false /* mp4vFormat */));
|
|
||||||
|
|
||||||
// The seek map is correct.
|
// The seek map is correct.
|
||||||
assertSeekMap(extractorOutput.seekMap, false);
|
assertSeekMap(extractorOutput.seekMap, false);
|
||||||
|
|
@ -162,8 +144,7 @@ public final class Mp4ExtractorTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParsesValidMp4vFile() throws Exception {
|
public void testParsesValidMp4vFile() throws Exception {
|
||||||
TestUtil.consumeTestData(extractor,
|
FakeExtractorOutput extractorOutput = consumeTestData(true, true);
|
||||||
getTestInputData(true /* includeStss */, true /* mp4vFormat */));
|
|
||||||
|
|
||||||
// The seek map is correct.
|
// The seek map is correct.
|
||||||
assertSeekMap(extractorOutput.seekMap, true);
|
assertSeekMap(extractorOutput.seekMap, true);
|
||||||
|
|
@ -364,9 +345,11 @@ public final class Mp4ExtractorTest extends TestCase {
|
||||||
return CHUNK_OFFSETS[chunkIndex] + offsetInChunk;
|
return CHUNK_OFFSETS[chunkIndex] + offsetInChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] getTestInputData(boolean includeStss, boolean mp4vFormat) {
|
private static FakeExtractorOutput consumeTestData(boolean includeStss, boolean mp4vFormat)
|
||||||
return includeStss ? getTestMp4File(mp4vFormat)
|
throws IOException, InterruptedException {
|
||||||
|
byte[] testInputData = includeStss ? getTestMp4File(mp4vFormat)
|
||||||
: getTestMp4FileWithoutSynchronizationData(mp4vFormat);
|
: getTestMp4FileWithoutSynchronizationData(mp4vFormat);
|
||||||
|
return TestUtil.consumeTestData(new Mp4Extractor(), testInputData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets a valid MP4 file with audio/video tracks and synchronization data. */
|
/** 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 OPUS_TEST_FILE = "ogg/bear.opus";
|
||||||
public static final String FLAC_TEST_FILE = "ogg/bear_flac.ogg";
|
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 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 {
|
public void testOpus() throws Exception {
|
||||||
parseFile(OPUS_TEST_FILE, false, false, false, MimeTypes.AUDIO_OPUS, 2747500, 275);
|
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);
|
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,
|
private FakeTrackOutput parseFile(String testFile, boolean simulateIOErrors,
|
||||||
boolean simulateUnknownLength, boolean simulatePartialReads, String expectedMimeType,
|
boolean simulateUnknownLength, boolean simulatePartialReads, String expectedMimeType,
|
||||||
long expectedDuration, int expectedSampleCount) throws Exception {
|
long expectedDuration, int expectedSampleCount) throws Exception {
|
||||||
|
|
@ -78,9 +86,7 @@ public final class OggExtractorFileTests extends InstrumentationTestCase {
|
||||||
OggExtractor extractor = new OggExtractor();
|
OggExtractor extractor = new OggExtractor();
|
||||||
assertTrue(TestUtil.sniffTestData(extractor, input));
|
assertTrue(TestUtil.sniffTestData(extractor, input));
|
||||||
input.resetPeekPosition();
|
input.resetPeekPosition();
|
||||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
FakeExtractorOutput extractorOutput = TestUtil.consumeTestData(extractor, input, true);
|
||||||
extractor.init(extractorOutput);
|
|
||||||
TestUtil.consumeTestData(extractor, input);
|
|
||||||
|
|
||||||
assertEquals(1, extractorOutput.trackOutputs.size());
|
assertEquals(1, extractorOutput.trackOutputs.size());
|
||||||
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,15 @@ public final class FakeTrackOutput implements TrackOutput {
|
||||||
sampleEncryptionKeys = new ArrayList<>();
|
sampleEncryptionKeys = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
sampleData = new byte[0];
|
||||||
|
sampleTimesUs.clear();
|
||||||
|
sampleFlags.clear();
|
||||||
|
sampleStartOffsets.clear();
|
||||||
|
sampleEndOffsets.clear();
|
||||||
|
sampleEncryptionKeys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void format(Format format) {
|
public void format(Format format) {
|
||||||
this.format = format;
|
this.format = format;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.testutil;
|
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.Extractor;
|
||||||
import com.google.android.exoplayer.extractor.PositionHolder;
|
import com.google.android.exoplayer.extractor.PositionHolder;
|
||||||
import com.google.android.exoplayer.testutil.FakeExtractorInput.SimulatedIOException;
|
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 {
|
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 {
|
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();
|
PositionHolder seekPositionHolder = new PositionHolder();
|
||||||
int readResult = Extractor.RESULT_CONTINUE;
|
int readResult = Extractor.RESULT_CONTINUE;
|
||||||
while (readResult != Extractor.RESULT_END_OF_INPUT) {
|
while (readResult != Extractor.RESULT_END_OF_INPUT) {
|
||||||
|
|
@ -70,9 +79,22 @@ public class TestUtil {
|
||||||
input.setPosition((int) seekPosition);
|
input.setPosition((int) seekPosition);
|
||||||
}
|
}
|
||||||
} catch (SimulatedIOException e) {
|
} 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) {
|
public static byte[] buildTestData(int length) {
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput
|
||||||
extractor.init(this);
|
extractor.init(this);
|
||||||
extractorInitialized = true;
|
extractorInitialized = true;
|
||||||
} else {
|
} else {
|
||||||
extractor.seek();
|
extractor.seek(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,13 @@ public interface Extractor {
|
||||||
* Notifies the extractor that a seek has occurred.
|
* Notifies the extractor that a seek has occurred.
|
||||||
* <p>
|
* <p>
|
||||||
* Following a call to this method, the {@link ExtractorInput} passed to the next invocation of
|
* 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
|
* {@link #read(ExtractorInput, PositionHolder)} is required to provide data starting from {@code
|
||||||
* random access position in the stream. Valid random access positions are the start of the
|
* position} in the stream. Valid random access positions are the start of the stream and
|
||||||
* stream and positions that can be obtained from any {@link SeekMap} passed to the
|
* positions that can be obtained from any {@link SeekMap} passed to the {@link ExtractorOutput}.
|
||||||
* {@link ExtractorOutput}.
|
*
|
||||||
|
* @param position The seek position.
|
||||||
*/
|
*/
|
||||||
void seek();
|
void seek(long position);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases all kept resources.
|
* Releases all kept resources.
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||||
private TrackGroupArray tracks;
|
private TrackGroupArray tracks;
|
||||||
private long durationUs;
|
private long durationUs;
|
||||||
private boolean[] trackEnabledStates;
|
private boolean[] trackEnabledStates;
|
||||||
|
private long length;
|
||||||
|
|
||||||
private long downstreamPositionUs;
|
private long downstreamPositionUs;
|
||||||
private long lastSeekPositionUs;
|
private long lastSeekPositionUs;
|
||||||
|
|
@ -237,6 +238,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||||
extractorHolder = new ExtractorHolder(extractors, this);
|
extractorHolder = new ExtractorHolder(extractors, this);
|
||||||
pendingResetPositionUs = C.UNSET_TIME_US;
|
pendingResetPositionUs = C.UNSET_TIME_US;
|
||||||
sampleQueues = new DefaultTrackOutput[0];
|
sampleQueues = new DefaultTrackOutput[0];
|
||||||
|
length = C.LENGTH_UNBOUNDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -491,11 +493,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadCompleted(Loadable loadable, long elapsedMs) {
|
public void onLoadCompleted(Loadable loadable, long elapsedMs) {
|
||||||
|
copyLengthFromLoader();
|
||||||
loadingFinished = true;
|
loadingFinished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadCanceled(Loadable loadable, long elapsedMs) {
|
public void onLoadCanceled(Loadable loadable, long elapsedMs) {
|
||||||
|
copyLengthFromLoader();
|
||||||
if (enabledTrackCount > 0) {
|
if (enabledTrackCount > 0) {
|
||||||
restartFrom(pendingResetPositionUs);
|
restartFrom(pendingResetPositionUs);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -506,6 +510,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onLoadError(Loadable loadable, long elapsedMs, IOException e) {
|
public int onLoadError(Loadable loadable, long elapsedMs, IOException e) {
|
||||||
|
copyLengthFromLoader();
|
||||||
notifyLoadError(e);
|
notifyLoadError(e);
|
||||||
if (isLoadableExceptionFatal(e)) {
|
if (isLoadableExceptionFatal(e)) {
|
||||||
return Loader.DONT_RETRY_FATAL;
|
return Loader.DONT_RETRY_FATAL;
|
||||||
|
|
@ -539,6 +544,12 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
|
private void copyLengthFromLoader() {
|
||||||
|
if (length == C.LENGTH_UNBOUNDED) {
|
||||||
|
length = loadable.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void seekToInternal(long positionUs) {
|
private void seekToInternal(long positionUs) {
|
||||||
// Treat all seeks into non-seekable media as being to t=0.
|
// Treat all seeks into non-seekable media as being to t=0.
|
||||||
positionUs = seekMap.isSeekable() ? positionUs : 0;
|
positionUs = seekMap.isSeekable() ? positionUs : 0;
|
||||||
|
|
@ -588,23 +599,19 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||||
|
|
||||||
private void configureRetry() {
|
private void configureRetry() {
|
||||||
Assertions.checkState(loadable != null);
|
Assertions.checkState(loadable != null);
|
||||||
if (!prepared) {
|
if (length != C.LENGTH_UNBOUNDED
|
||||||
// We don't know whether we're playing an on-demand or a live stream. For a live stream we
|
|| (seekMap != null && seekMap.getDurationUs() != C.UNSET_TIME_US)) {
|
||||||
// need to load from the start, as outlined below. Since we might be playing a live stream,
|
// We're playing an on-demand stream. Resume the current loadable, which will
|
||||||
// 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
|
|
||||||
// request data starting from the point it left off.
|
// 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 volatile boolean loadCanceled;
|
||||||
|
|
||||||
private boolean pendingExtractorSeek;
|
private boolean pendingExtractorSeek;
|
||||||
|
private long length;
|
||||||
|
|
||||||
public ExtractingLoadable(Uri uri, DataSource dataSource, ExtractorHolder extractorHolder,
|
public ExtractingLoadable(Uri uri, DataSource dataSource, ExtractorHolder extractorHolder,
|
||||||
Allocator allocator, int requestedBufferSize) {
|
Allocator allocator, int requestedBufferSize) {
|
||||||
|
|
@ -711,8 +719,9 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||||
this.extractorHolder = Assertions.checkNotNull(extractorHolder);
|
this.extractorHolder = Assertions.checkNotNull(extractorHolder);
|
||||||
this.allocator = Assertions.checkNotNull(allocator);
|
this.allocator = Assertions.checkNotNull(allocator);
|
||||||
this.requestedBufferSize = requestedBufferSize;
|
this.requestedBufferSize = requestedBufferSize;
|
||||||
positionHolder = new PositionHolder();
|
this.positionHolder = new PositionHolder();
|
||||||
pendingExtractorSeek = true;
|
this.pendingExtractorSeek = true;
|
||||||
|
this.length = C.LENGTH_UNBOUNDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoadPosition(long position) {
|
public void setLoadPosition(long position) {
|
||||||
|
|
@ -737,14 +746,14 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
|
||||||
ExtractorInput input = null;
|
ExtractorInput input = null;
|
||||||
try {
|
try {
|
||||||
long position = positionHolder.position;
|
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) {
|
if (length != C.LENGTH_UNBOUNDED) {
|
||||||
length += position;
|
length += position;
|
||||||
}
|
}
|
||||||
input = new DefaultExtractorInput(dataSource, position, length);
|
input = new DefaultExtractorInput(dataSource, position, length);
|
||||||
Extractor extractor = extractorHolder.selectExtractor(input);
|
Extractor extractor = extractorHolder.selectExtractor(input);
|
||||||
if (pendingExtractorSeek) {
|
if (pendingExtractorSeek) {
|
||||||
extractor.seek();
|
extractor.seek(position);
|
||||||
pendingExtractorSeek = false;
|
pendingExtractorSeek = false;
|
||||||
}
|
}
|
||||||
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
|
while (result == Extractor.RESULT_CONTINUE && !loadCanceled) {
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ public final class FlvExtractor implements Extractor, SeekMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
parserState = STATE_READING_FLV_HEADER;
|
parserState = STATE_READING_FLV_HEADER;
|
||||||
bytesToNextTagHeader = 0;
|
bytesToNextTagHeader = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@ public final class MatroskaExtractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
clusterTimecodeUs = UNKNOWN;
|
clusterTimecodeUs = UNKNOWN;
|
||||||
blockState = BLOCK_STATE_START;
|
blockState = BLOCK_STATE_START;
|
||||||
reader.reset();
|
reader.reset();
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ public final class Mp3Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
synchronizedHeaderData = 0;
|
synchronizedHeaderData = 0;
|
||||||
samplesRead = 0;
|
samplesRead = 0;
|
||||||
basisTimeUs = -1;
|
basisTimeUs = -1;
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
int trackCount = trackBundles.size();
|
int trackCount = trackBundles.size();
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
trackBundles.valueAt(i).reset();
|
trackBundles.valueAt(i).reset();
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
containerAtoms.clear();
|
containerAtoms.clear();
|
||||||
atomHeaderBytesRead = 0;
|
atomHeaderBytesRead = 0;
|
||||||
sampleBytesWritten = 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.Format;
|
||||||
import com.google.android.exoplayer.extractor.ExtractorInput;
|
import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||||
import com.google.android.exoplayer.extractor.SeekMap;
|
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.FlacStreamInfo;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||||
|
|
@ -47,6 +46,15 @@ import java.util.List;
|
||||||
data.readUnsignedInt() == 0x464C4143; // ASCII signature "FLAC"
|
data.readUnsignedInt() == 0x464C4143; // ASCII signature "FLAC"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void reset(boolean headerData) {
|
||||||
|
super.reset(headerData);
|
||||||
|
if (headerData) {
|
||||||
|
streamInfo = null;
|
||||||
|
flacOggSeeker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//@VisibleForTesting
|
//@VisibleForTesting
|
||||||
public static boolean isAudioPacket(byte[] data) {
|
public static boolean isAudioPacket(byte[] data) {
|
||||||
return data[0] == AUDIO_PACKET_TYPE;
|
return data[0] == AUDIO_PACKET_TYPE;
|
||||||
|
|
@ -73,7 +81,6 @@ import java.util.List;
|
||||||
streamInfo.bitRate(), streamInfo.channels, streamInfo.sampleRate, initializationData,
|
streamInfo.bitRate(), streamInfo.channels, streamInfo.sampleRate, initializationData,
|
||||||
null, 0, null);
|
null, 0, null);
|
||||||
} else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) {
|
} else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE) {
|
||||||
Assertions.checkArgument(flacOggSeeker == null);
|
|
||||||
flacOggSeeker = new FlacOggSeeker();
|
flacOggSeeker = new FlacOggSeeker();
|
||||||
flacOggSeeker.parseSeekTable(packet);
|
flacOggSeeker.parseSeekTable(packet);
|
||||||
} else if (isAudioPacket(data)) {
|
} else if (isAudioPacket(data)) {
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,8 @@ public class OggExtractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
streamReader.seek();
|
streamReader.seek(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.Format;
|
import com.google.android.exoplayer.Format;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||||
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
@ -39,9 +40,10 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
private static final int SAMPLE_RATE = 48000;
|
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 static final byte[] OPUS_SIGNATURE = {'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'};
|
||||||
|
|
||||||
private boolean headerRead;
|
private boolean headerRead;
|
||||||
private boolean tagsSkipped;
|
|
||||||
|
|
||||||
public static boolean verifyBitstreamType(ParsableByteArray data) {
|
public static boolean verifyBitstreamType(ParsableByteArray data) {
|
||||||
if (data.bytesLeft() < OPUS_SIGNATURE.length) {
|
if (data.bytesLeft() < OPUS_SIGNATURE.length) {
|
||||||
|
|
@ -52,6 +54,14 @@ import java.util.List;
|
||||||
return Arrays.equals(header, OPUS_SIGNATURE);
|
return Arrays.equals(header, OPUS_SIGNATURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void reset(boolean headerData) {
|
||||||
|
super.reset(headerData);
|
||||||
|
if (headerData) {
|
||||||
|
headerRead = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected long preparePayload(ParsableByteArray packet) {
|
protected long preparePayload(ParsableByteArray packet) {
|
||||||
return convertTimeToGranule(getPacketDurationUs(packet.data));
|
return convertTimeToGranule(getPacketDurationUs(packet.data));
|
||||||
|
|
@ -73,11 +83,10 @@ import java.util.List;
|
||||||
setupData.format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_OPUS, Format.NO_VALUE,
|
setupData.format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_OPUS, Format.NO_VALUE,
|
||||||
Format.NO_VALUE, channelCount, SAMPLE_RATE, initializationData, null, 0, "und");
|
Format.NO_VALUE, channelCount, SAMPLE_RATE, initializationData, null, 0, "und");
|
||||||
headerRead = true;
|
headerRead = true;
|
||||||
} else if (!tagsSkipped) {
|
|
||||||
// Skip tags packet
|
|
||||||
tagsSkipped = true;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
boolean headerPacket = packet.readInt() == OPUS_CODE;
|
||||||
|
packet.setPosition(0);
|
||||||
|
return headerPacket;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,9 @@ import java.io.IOException;
|
||||||
/* package */ abstract class StreamReader {
|
/* package */ abstract class StreamReader {
|
||||||
|
|
||||||
private static final int STATE_READ_HEADERS = 0;
|
private static final int STATE_READ_HEADERS = 0;
|
||||||
private static final int STATE_READ_PAYLOAD = 1;
|
private static final int STATE_SKIP_HEADERS = 1;
|
||||||
private static final int STATE_END_OF_INPUT = 2;
|
private static final int STATE_READ_PAYLOAD = 2;
|
||||||
|
private static final int STATE_END_OF_INPUT = 3;
|
||||||
|
|
||||||
static class SetupData {
|
static class SetupData {
|
||||||
Format format;
|
Format format;
|
||||||
|
|
@ -38,27 +39,44 @@ import java.io.IOException;
|
||||||
private SetupData setupData;
|
private SetupData setupData;
|
||||||
private long lengthOfReadPacket;
|
private long lengthOfReadPacket;
|
||||||
private boolean seekMapSet;
|
private boolean seekMapSet;
|
||||||
|
private boolean formatSet;
|
||||||
|
|
||||||
void init(ExtractorOutput output, TrackOutput trackOutput) {
|
void init(ExtractorOutput output, TrackOutput trackOutput) {
|
||||||
this.extractorOutput = output;
|
this.extractorOutput = output;
|
||||||
this.trackOutput = trackOutput;
|
this.trackOutput = trackOutput;
|
||||||
this.oggPacket = new OggPacket();
|
this.oggPacket = new OggPacket();
|
||||||
this.setupData = new SetupData();
|
|
||||||
|
|
||||||
this.state = STATE_READ_HEADERS;
|
reset(true);
|
||||||
this.targetGranule = -1;
|
|
||||||
this.payloadStartPosition = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Extractor#seek()
|
* Resets the state of the {@link StreamReader}.
|
||||||
|
* @param headerData Resets parsed header data too.
|
||||||
*/
|
*/
|
||||||
final void seek() {
|
protected void reset(boolean headerData) {
|
||||||
oggPacket.reset();
|
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();
|
* @see Extractor#seek(long)
|
||||||
state = STATE_READ_PAYLOAD;
|
*/
|
||||||
|
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:
|
case STATE_READ_HEADERS:
|
||||||
return readHeaders(input);
|
return readHeaders(input);
|
||||||
|
|
||||||
|
case STATE_SKIP_HEADERS:
|
||||||
|
input.skipFully((int) payloadStartPosition);
|
||||||
|
state = STATE_READ_PAYLOAD;
|
||||||
|
return Extractor.RESULT_CONTINUE;
|
||||||
|
|
||||||
case STATE_READ_PAYLOAD:
|
case STATE_READ_PAYLOAD:
|
||||||
return readPayload(input, seekPosition);
|
return readPayload(input, seekPosition);
|
||||||
|
|
||||||
|
|
@ -80,8 +103,7 @@ import java.io.IOException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readHeaders(ExtractorInput input)
|
private int readHeaders(ExtractorInput input) throws IOException, InterruptedException {
|
||||||
throws IOException, InterruptedException {
|
|
||||||
boolean readingHeaders = true;
|
boolean readingHeaders = true;
|
||||||
while (readingHeaders) {
|
while (readingHeaders) {
|
||||||
if (!oggPacket.populate(input)) {
|
if (!oggPacket.populate(input)) {
|
||||||
|
|
@ -97,7 +119,10 @@ import java.io.IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleRate = setupData.format.sampleRate;
|
sampleRate = setupData.format.sampleRate;
|
||||||
trackOutput.format(setupData.format);
|
if (!formatSet) {
|
||||||
|
trackOutput.format(setupData.format);
|
||||||
|
formatSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (setupData.oggSeeker != null) {
|
if (setupData.oggSeeker != null) {
|
||||||
oggSeeker = setupData.oggSeeker;
|
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
|
@Override
|
||||||
protected void onSeekEnd(long currentGranule) {
|
protected void onSeekEnd(long currentGranule) {
|
||||||
super.onSeekEnd(currentGranule);
|
super.onSeekEnd(currentGranule);
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ public final class AdtsExtractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
startedPacket = false;
|
startedPacket = false;
|
||||||
adtsReader.seek();
|
adtsReader.seek();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ public final class PsExtractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
ptsTimestampAdjuster.reset();
|
ptsTimestampAdjuster.reset();
|
||||||
for (int i = 0; i < psPayloadReaders.size(); i++) {
|
for (int i = 0; i < psPayloadReaders.size(); i++) {
|
||||||
psPayloadReaders.valueAt(i).seek();
|
psPayloadReaders.valueAt(i).seek();
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ public final class TsExtractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
ptsTimestampAdjuster.reset();
|
ptsTimestampAdjuster.reset();
|
||||||
for (int i = 0; i < tsPayloadReaders.size(); i++) {
|
for (int i = 0; i < tsPayloadReaders.size(); i++) {
|
||||||
tsPayloadReaders.valueAt(i).seek();
|
tsPayloadReaders.valueAt(i).seek();
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ public final class WavExtractor implements Extractor, SeekMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek() {
|
public void seek(long position) {
|
||||||
pendingBytes = 0;
|
pendingBytes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ import java.util.regex.Pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.
|
// This extractor is only used for the HLS use case, which should not call this method.
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue