mirror of
https://github.com/androidx/media.git
synced 2025-05-18 04:59:54 +08:00
Some extractor fixes
- Fix Ogg extractor to work without sniffing. - Fix extractors to handle seek() before init(). - Add tests for both issues. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163992343
This commit is contained in:
parent
cad25e5a4d
commit
a3df29a246
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public class FlacExtractorTest extends InstrumentationTestCase {
|
public class FlacExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testSample() throws Exception {
|
public void testSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new FlacExtractor();
|
return new FlacExtractor();
|
||||||
|
@ -159,13 +159,17 @@ public final class FlacExtractor implements Extractor {
|
|||||||
if (position == 0) {
|
if (position == 0) {
|
||||||
metadataParsed = false;
|
metadataParsed = false;
|
||||||
}
|
}
|
||||||
decoderJni.reset(position);
|
if (decoderJni != null) {
|
||||||
|
decoderJni.reset(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() {
|
||||||
decoderJni.release();
|
if (decoderJni != null) {
|
||||||
decoderJni = null;
|
decoderJni.release();
|
||||||
|
decoderJni = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class FlvExtractorTest extends InstrumentationTestCase {
|
public final class FlvExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testSample() throws Exception {
|
public void testSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new FlvExtractor();
|
return new FlvExtractor();
|
||||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testMkvSample() throws Exception {
|
public void testMkvSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new MatroskaExtractor();
|
return new MatroskaExtractor();
|
||||||
@ -35,7 +35,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testWebmSubsampleEncryption() throws Exception {
|
public void testWebmSubsampleEncryption() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new MatroskaExtractor();
|
return new MatroskaExtractor();
|
||||||
@ -44,7 +44,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testWebmSubsampleEncryptionWithAltrefFrames() throws Exception {
|
public void testWebmSubsampleEncryptionWithAltrefFrames() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new MatroskaExtractor();
|
return new MatroskaExtractor();
|
||||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class Mp3ExtractorTest extends InstrumentationTestCase {
|
public final class Mp3ExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testMp3Sample() throws Exception {
|
public void testMp3Sample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new Mp3Extractor();
|
return new Mp3Extractor();
|
||||||
@ -35,7 +35,7 @@ public final class Mp3ExtractorTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testTrimmedMp3Sample() throws Exception {
|
public void testTrimmedMp3Sample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new Mp3Extractor();
|
return new Mp3Extractor();
|
||||||
|
@ -27,13 +27,13 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class FragmentedMp4ExtractorTest extends InstrumentationTestCase {
|
public final class FragmentedMp4ExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testSample() throws Exception {
|
public void testSample() throws Exception {
|
||||||
ExtractorAsserts
|
ExtractorAsserts.assertBehavior(getExtractorFactory(), "mp4/sample_fragmented.mp4",
|
||||||
.assertOutput(getExtractorFactory(), "mp4/sample_fragmented.mp4", getInstrumentation());
|
getInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSampleWithSeiPayloadParsing() throws Exception {
|
public void testSampleWithSeiPayloadParsing() throws Exception {
|
||||||
// Enabling the CEA-608 track enables SEI payload parsing.
|
// Enabling the CEA-608 track enables SEI payload parsing.
|
||||||
ExtractorAsserts.assertOutput(
|
ExtractorAsserts.assertBehavior(
|
||||||
getExtractorFactory(FragmentedMp4Extractor.FLAG_ENABLE_CEA608_TRACK),
|
getExtractorFactory(FragmentedMp4Extractor.FLAG_ENABLE_CEA608_TRACK),
|
||||||
"mp4/sample_fragmented_sei.mp4", getInstrumentation());
|
"mp4/sample_fragmented_sei.mp4", getInstrumentation());
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class Mp4ExtractorTest extends InstrumentationTestCase {
|
public final class Mp4ExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testMp4Sample() throws Exception {
|
public void testMp4Sample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new Mp4Extractor();
|
return new Mp4Extractor();
|
||||||
|
@ -36,20 +36,21 @@ public final class OggExtractorTest extends InstrumentationTestCase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public void testOpus() throws Exception {
|
public void testOpus() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear.opus", getInstrumentation());
|
ExtractorAsserts.assertBehavior(OGG_EXTRACTOR_FACTORY, "ogg/bear.opus", getInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFlac() throws Exception {
|
public void testFlac() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac.ogg", getInstrumentation());
|
ExtractorAsserts.assertBehavior(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac.ogg",
|
||||||
|
getInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFlacNoSeektable() throws Exception {
|
public void testFlacNoSeektable() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac_noseektable.ogg",
|
ExtractorAsserts.assertBehavior(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac_noseektable.ogg",
|
||||||
getInstrumentation());
|
getInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testVorbis() throws Exception {
|
public void testVorbis() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_vorbis.ogg",
|
ExtractorAsserts.assertBehavior(OGG_EXTRACTOR_FACTORY, "ogg/bear_vorbis.ogg",
|
||||||
getInstrumentation());
|
getInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import com.google.android.exoplayer2.util.MimeTypes;
|
|||||||
public final class RawCcExtractorTest extends InstrumentationTestCase {
|
public final class RawCcExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testRawCcSample() throws Exception {
|
public void testRawCcSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(
|
ExtractorAsserts.assertBehavior(
|
||||||
new ExtractorFactory() {
|
new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class Ac3ExtractorTest extends InstrumentationTestCase {
|
public final class Ac3ExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testSample() throws Exception {
|
public void testSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new Ac3Extractor();
|
return new Ac3Extractor();
|
||||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class AdtsExtractorTest extends InstrumentationTestCase {
|
public final class AdtsExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testSample() throws Exception {
|
public void testSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new AdtsExtractor();
|
return new AdtsExtractor();
|
||||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class PsExtractorTest extends InstrumentationTestCase {
|
public final class PsExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testSample() throws Exception {
|
public void testSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new PsExtractor();
|
return new PsExtractor();
|
||||||
|
@ -45,7 +45,7 @@ public final class TsExtractorTest extends InstrumentationTestCase {
|
|||||||
private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet.
|
private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet.
|
||||||
|
|
||||||
public void testSample() throws Exception {
|
public void testSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new TsExtractor();
|
return new TsExtractor();
|
||||||
|
@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory;
|
|||||||
public final class WavExtractorTest extends InstrumentationTestCase {
|
public final class WavExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testSample() throws Exception {
|
public void testSample() throws Exception {
|
||||||
ExtractorAsserts.assertOutput(new ExtractorFactory() {
|
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Extractor create() {
|
public Extractor create() {
|
||||||
return new WavExtractor();
|
return new WavExtractor();
|
||||||
|
@ -63,7 +63,8 @@ public interface Extractor {
|
|||||||
void init(ExtractorOutput output);
|
void init(ExtractorOutput output);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts data read from a provided {@link ExtractorInput}.
|
* Extracts data read from a provided {@link ExtractorInput}. Must not be called before
|
||||||
|
* {@link #init(ExtractorOutput)}.
|
||||||
* <p>
|
* <p>
|
||||||
* A single call to this method will block until some progress has been made, but will not block
|
* A single call to this method will block until some progress has been made, but will not block
|
||||||
* for longer than this. Hence each call will consume only a small amount of input data.
|
* for longer than this. Hence each call will consume only a small amount of input data.
|
||||||
|
@ -45,30 +45,14 @@ public class OggExtractor implements Extractor {
|
|||||||
|
|
||||||
private static final int MAX_VERIFICATION_BYTES = 8;
|
private static final int MAX_VERIFICATION_BYTES = 8;
|
||||||
|
|
||||||
|
private ExtractorOutput output;
|
||||||
private StreamReader streamReader;
|
private StreamReader streamReader;
|
||||||
|
private boolean streamReaderInitialized;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
|
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
|
||||||
try {
|
try {
|
||||||
OggPageHeader header = new OggPageHeader();
|
return sniffInternal(input);
|
||||||
if (!header.populate(input, true) || (header.type & 0x02) != 0x02) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = Math.min(header.bodySize, MAX_VERIFICATION_BYTES);
|
|
||||||
ParsableByteArray scratch = new ParsableByteArray(length);
|
|
||||||
input.peekFully(scratch.data, 0, length);
|
|
||||||
|
|
||||||
if (FlacReader.verifyBitstreamType(resetPosition(scratch))) {
|
|
||||||
streamReader = new FlacReader();
|
|
||||||
} else if (VorbisReader.verifyBitstreamType(resetPosition(scratch))) {
|
|
||||||
streamReader = new VorbisReader();
|
|
||||||
} else if (OpusReader.verifyBitstreamType(resetPosition(scratch))) {
|
|
||||||
streamReader = new OpusReader();
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (ParserException e) {
|
} catch (ParserException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -76,15 +60,14 @@ public class OggExtractor implements Extractor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(ExtractorOutput output) {
|
public void init(ExtractorOutput output) {
|
||||||
TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_AUDIO);
|
this.output = output;
|
||||||
output.endTracks();
|
|
||||||
// TODO: fix the case if sniff() isn't called
|
|
||||||
streamReader.init(output, trackOutput);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek(long position, long timeUs) {
|
public void seek(long position, long timeUs) {
|
||||||
streamReader.seek(position, timeUs);
|
if (streamReader != null) {
|
||||||
|
streamReader.seek(position, timeUs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -95,12 +78,41 @@ public class OggExtractor implements Extractor {
|
|||||||
@Override
|
@Override
|
||||||
public int read(ExtractorInput input, PositionHolder seekPosition)
|
public int read(ExtractorInput input, PositionHolder seekPosition)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
|
if (streamReader == null) {
|
||||||
|
if (!sniffInternal(input)) {
|
||||||
|
throw new ParserException("Failed to determine bitstream type");
|
||||||
|
}
|
||||||
|
input.resetPeekPosition();
|
||||||
|
}
|
||||||
|
if (!streamReaderInitialized) {
|
||||||
|
TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_AUDIO);
|
||||||
|
output.endTracks();
|
||||||
|
streamReader.init(output, trackOutput);
|
||||||
|
streamReaderInitialized = true;
|
||||||
|
}
|
||||||
return streamReader.read(input, seekPosition);
|
return streamReader.read(input, seekPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
//@VisibleForTesting
|
private boolean sniffInternal(ExtractorInput input) throws IOException, InterruptedException {
|
||||||
/* package */ StreamReader getStreamReader() {
|
OggPageHeader header = new OggPageHeader();
|
||||||
return streamReader;
|
if (!header.populate(input, true) || (header.type & 0x02) != 0x02) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = Math.min(header.bodySize, MAX_VERIFICATION_BYTES);
|
||||||
|
ParsableByteArray scratch = new ParsableByteArray(length);
|
||||||
|
input.peekFully(scratch.data, 0, length);
|
||||||
|
|
||||||
|
if (FlacReader.verifyBitstreamType(resetPosition(scratch))) {
|
||||||
|
streamReader = new FlacReader();
|
||||||
|
} else if (VorbisReader.verifyBitstreamType(resetPosition(scratch))) {
|
||||||
|
streamReader = new VorbisReader();
|
||||||
|
} else if (OpusReader.verifyBitstreamType(resetPosition(scratch))) {
|
||||||
|
streamReader = new OpusReader();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParsableByteArray resetPosition(ParsableByteArray scratch) {
|
private static ParsableByteArray resetPosition(ParsableByteArray scratch) {
|
||||||
|
@ -41,7 +41,8 @@ import java.io.IOException;
|
|||||||
OggSeeker oggSeeker;
|
OggSeeker oggSeeker;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OggPacket oggPacket;
|
private final OggPacket oggPacket;
|
||||||
|
|
||||||
private TrackOutput trackOutput;
|
private TrackOutput trackOutput;
|
||||||
private ExtractorOutput extractorOutput;
|
private ExtractorOutput extractorOutput;
|
||||||
private OggSeeker oggSeeker;
|
private OggSeeker oggSeeker;
|
||||||
@ -55,11 +56,13 @@ import java.io.IOException;
|
|||||||
private boolean seekMapSet;
|
private boolean seekMapSet;
|
||||||
private boolean formatSet;
|
private boolean formatSet;
|
||||||
|
|
||||||
|
public StreamReader() {
|
||||||
|
oggPacket = new OggPacket();
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
reset(true);
|
reset(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,9 +56,9 @@ public final class Ac3Extractor implements Extractor {
|
|||||||
private static final int ID3_TAG = Util.getIntegerCodeForString("ID3");
|
private static final int ID3_TAG = Util.getIntegerCodeForString("ID3");
|
||||||
|
|
||||||
private final long firstSampleTimestampUs;
|
private final long firstSampleTimestampUs;
|
||||||
|
private final Ac3Reader reader;
|
||||||
private final ParsableByteArray sampleData;
|
private final ParsableByteArray sampleData;
|
||||||
|
|
||||||
private Ac3Reader reader;
|
|
||||||
private boolean startedPacket;
|
private boolean startedPacket;
|
||||||
|
|
||||||
public Ac3Extractor() {
|
public Ac3Extractor() {
|
||||||
@ -67,6 +67,7 @@ public final class Ac3Extractor implements Extractor {
|
|||||||
|
|
||||||
public Ac3Extractor(long firstSampleTimestampUs) {
|
public Ac3Extractor(long firstSampleTimestampUs) {
|
||||||
this.firstSampleTimestampUs = firstSampleTimestampUs;
|
this.firstSampleTimestampUs = firstSampleTimestampUs;
|
||||||
|
reader = new Ac3Reader();
|
||||||
sampleData = new ParsableByteArray(MAX_SYNC_FRAME_SIZE);
|
sampleData = new ParsableByteArray(MAX_SYNC_FRAME_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +118,6 @@ public final class Ac3Extractor implements Extractor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(ExtractorOutput output) {
|
public void init(ExtractorOutput output) {
|
||||||
reader = new Ac3Reader(); // TODO: Add support for embedded ID3.
|
|
||||||
reader.createTracks(output, new TrackIdGenerator(0, 1));
|
reader.createTracks(output, new TrackIdGenerator(0, 1));
|
||||||
output.endTracks();
|
output.endTracks();
|
||||||
output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
|
output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
|
||||||
|
@ -55,10 +55,9 @@ public final class AdtsExtractor implements Extractor {
|
|||||||
private static final int MAX_SNIFF_BYTES = 8 * 1024;
|
private static final int MAX_SNIFF_BYTES = 8 * 1024;
|
||||||
|
|
||||||
private final long firstSampleTimestampUs;
|
private final long firstSampleTimestampUs;
|
||||||
|
private final AdtsReader reader;
|
||||||
private final ParsableByteArray packetBuffer;
|
private final ParsableByteArray packetBuffer;
|
||||||
|
|
||||||
// Accessed only by the loading thread.
|
|
||||||
private AdtsReader reader;
|
|
||||||
private boolean startedPacket;
|
private boolean startedPacket;
|
||||||
|
|
||||||
public AdtsExtractor() {
|
public AdtsExtractor() {
|
||||||
@ -67,6 +66,7 @@ public final class AdtsExtractor implements Extractor {
|
|||||||
|
|
||||||
public AdtsExtractor(long firstSampleTimestampUs) {
|
public AdtsExtractor(long firstSampleTimestampUs) {
|
||||||
this.firstSampleTimestampUs = firstSampleTimestampUs;
|
this.firstSampleTimestampUs = firstSampleTimestampUs;
|
||||||
|
reader = new AdtsReader(true);
|
||||||
packetBuffer = new ParsableByteArray(MAX_PACKET_SIZE);
|
packetBuffer = new ParsableByteArray(MAX_PACKET_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +127,6 @@ public final class AdtsExtractor implements Extractor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(ExtractorOutput output) {
|
public void init(ExtractorOutput output) {
|
||||||
reader = new AdtsReader(true);
|
|
||||||
reader.createTracks(output, new TrackIdGenerator(0, 1));
|
reader.createTracks(output, new TrackIdGenerator(0, 1));
|
||||||
output.endTracks();
|
output.endTracks();
|
||||||
output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
|
output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
|
||||||
|
@ -18,6 +18,8 @@ package com.google.android.exoplayer2.testutil;
|
|||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.extractor.Extractor;
|
import com.google.android.exoplayer2.extractor.Extractor;
|
||||||
|
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||||
|
import com.google.android.exoplayer2.extractor.ExtractorOutput;
|
||||||
import com.google.android.exoplayer2.extractor.PositionHolder;
|
import com.google.android.exoplayer2.extractor.PositionHolder;
|
||||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||||
import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException;
|
import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException;
|
||||||
@ -42,46 +44,57 @@ public final class ExtractorAsserts {
|
|||||||
private static final String UNKNOWN_LENGTH_EXTENSION = ".unklen" + DUMP_EXTENSION;
|
private static final String UNKNOWN_LENGTH_EXTENSION = ".unklen" + DUMP_EXTENSION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls {@link #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean,
|
* Asserts that an extractor behaves correctly given valid input data:
|
||||||
* boolean)} with all possible combinations of "simulate" parameters.
|
* <ul>
|
||||||
|
* <li>Calls {@link Extractor#seek(long, long)} and {@link Extractor#release()} without calling
|
||||||
|
* {@link Extractor#init(ExtractorOutput)} to check these calls do not fail.</li>
|
||||||
|
* <li>Calls {@link #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean,
|
||||||
|
* boolean, boolean)} with all possible combinations of "simulate" parameters.</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param factory An {@link ExtractorFactory} which creates instances of the {@link Extractor}
|
* @param factory An {@link ExtractorFactory} which creates instances of the {@link Extractor}
|
||||||
* class which is to be tested.
|
* class which is to be tested.
|
||||||
* @param sampleFile The path to the input sample.
|
* @param file The path to the input sample.
|
||||||
* @param instrumentation To be used to load the sample file.
|
* @param instrumentation To be used to load the sample file.
|
||||||
* @throws IOException If reading from the input fails.
|
* @throws IOException If reading from the input fails.
|
||||||
* @throws InterruptedException If interrupted while reading from the input.
|
* @throws InterruptedException If interrupted while reading from the input.
|
||||||
* @see #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean, boolean)
|
|
||||||
*/
|
*/
|
||||||
public static void assertOutput(ExtractorFactory factory, String sampleFile,
|
public static void assertBehavior(ExtractorFactory factory, String file,
|
||||||
Instrumentation instrumentation) throws IOException, InterruptedException {
|
Instrumentation instrumentation) throws IOException, InterruptedException {
|
||||||
byte[] fileData = TestUtil.getByteArray(instrumentation, sampleFile);
|
// Check behavior prior to initialization.
|
||||||
assertOutput(factory, sampleFile, fileData, instrumentation);
|
Extractor extractor = factory.create();
|
||||||
|
extractor.seek(0, 0);
|
||||||
|
extractor.release();
|
||||||
|
// Assert output.
|
||||||
|
byte[] fileData = TestUtil.getByteArray(instrumentation, file);
|
||||||
|
assertOutput(factory, file, fileData, instrumentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls {@link #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean,
|
* Calls {@link #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean,
|
||||||
* boolean)} with all possible combinations of "simulate" parameters.
|
* boolean, boolean)} with all possible combinations of "simulate" parameters with
|
||||||
|
* {@code sniffFirst} set to true, and makes one additional call with the "simulate" and
|
||||||
|
* {@code sniffFirst} parameters all set to false.
|
||||||
*
|
*
|
||||||
* @param factory An {@link ExtractorFactory} which creates instances of the {@link Extractor}
|
* @param factory An {@link ExtractorFactory} which creates instances of the {@link Extractor}
|
||||||
* class which is to be tested.
|
* class which is to be tested.
|
||||||
* @param sampleFile The path to the input sample.
|
* @param file The path to the input sample.
|
||||||
* @param fileData Content of the input file.
|
* @param data Content of the input file.
|
||||||
* @param instrumentation To be used to load the sample file.
|
* @param instrumentation To be used to load the sample file.
|
||||||
* @throws IOException If reading from the input fails.
|
* @throws IOException If reading from the input fails.
|
||||||
* @throws InterruptedException If interrupted while reading from the input.
|
* @throws InterruptedException If interrupted while reading from the input.
|
||||||
* @see #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean, boolean)
|
|
||||||
*/
|
*/
|
||||||
public static void assertOutput(ExtractorFactory factory, String sampleFile, byte[] fileData,
|
public static void assertOutput(ExtractorFactory factory, String file, byte[] data,
|
||||||
Instrumentation instrumentation) throws IOException, InterruptedException {
|
Instrumentation instrumentation) throws IOException, InterruptedException {
|
||||||
assertOutput(factory.create(), sampleFile, fileData, instrumentation, false, false, false);
|
assertOutput(factory.create(), file, data, instrumentation, true, false, false, false);
|
||||||
assertOutput(factory.create(), sampleFile, fileData, instrumentation, true, false, false);
|
assertOutput(factory.create(), file, data, instrumentation, true, false, false, true);
|
||||||
assertOutput(factory.create(), sampleFile, fileData, instrumentation, false, true, false);
|
assertOutput(factory.create(), file, data, instrumentation, true, false, true, false);
|
||||||
assertOutput(factory.create(), sampleFile, fileData, instrumentation, true, true, false);
|
assertOutput(factory.create(), file, data, instrumentation, true, false, true, true);
|
||||||
assertOutput(factory.create(), sampleFile, fileData, instrumentation, false, false, true);
|
assertOutput(factory.create(), file, data, instrumentation, true, true, false, false);
|
||||||
assertOutput(factory.create(), sampleFile, fileData, instrumentation, true, false, true);
|
assertOutput(factory.create(), file, data, instrumentation, true, true, false, true);
|
||||||
assertOutput(factory.create(), sampleFile, fileData, instrumentation, false, true, true);
|
assertOutput(factory.create(), file, data, instrumentation, true, true, true, false);
|
||||||
assertOutput(factory.create(), sampleFile, fileData, instrumentation, true, true, true);
|
assertOutput(factory.create(), file, data, instrumentation, true, true, true, true);
|
||||||
|
assertOutput(factory.create(), file, data, instrumentation, false, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,34 +104,38 @@ public final class ExtractorAsserts {
|
|||||||
* #UNKNOWN_LENGTH_EXTENSION}" exists, it's preferred.
|
* #UNKNOWN_LENGTH_EXTENSION}" exists, it's preferred.
|
||||||
*
|
*
|
||||||
* @param extractor The {@link Extractor} to be tested.
|
* @param extractor The {@link Extractor} to be tested.
|
||||||
* @param sampleFile The path to the input sample.
|
* @param file The path to the input sample.
|
||||||
* @param fileData Content of the input file.
|
* @param data Content of the input file.
|
||||||
* @param instrumentation To be used to load the sample file.
|
* @param instrumentation To be used to load the sample file.
|
||||||
* @param simulateIOErrors If true simulates IOErrors.
|
* @param sniffFirst Whether to sniff the data by calling {@link Extractor#sniff(ExtractorInput)}
|
||||||
* @param simulateUnknownLength If true simulates unknown input length.
|
* prior to consuming it.
|
||||||
* @param simulatePartialReads If true simulates partial reads.
|
* @param simulateIOErrors Whether to simulate IO errors.
|
||||||
|
* @param simulateUnknownLength Whether to simulate unknown input length.
|
||||||
|
* @param simulatePartialReads Whether to simulate partial reads.
|
||||||
* @return The {@link FakeExtractorOutput} used in the test.
|
* @return The {@link FakeExtractorOutput} used in the test.
|
||||||
* @throws IOException If reading from the input fails.
|
* @throws IOException If reading from the input fails.
|
||||||
* @throws InterruptedException If interrupted while reading from the input.
|
* @throws InterruptedException If interrupted while reading from the input.
|
||||||
*/
|
*/
|
||||||
public static FakeExtractorOutput assertOutput(Extractor extractor, String sampleFile,
|
public static FakeExtractorOutput assertOutput(Extractor extractor, String file, byte[] data,
|
||||||
byte[] fileData, Instrumentation instrumentation, boolean simulateIOErrors,
|
Instrumentation instrumentation, boolean sniffFirst, boolean simulateIOErrors,
|
||||||
boolean simulateUnknownLength, boolean simulatePartialReads) throws IOException,
|
boolean simulateUnknownLength, boolean simulatePartialReads) throws IOException,
|
||||||
InterruptedException {
|
InterruptedException {
|
||||||
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(fileData)
|
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data)
|
||||||
.setSimulateIOErrors(simulateIOErrors)
|
.setSimulateIOErrors(simulateIOErrors)
|
||||||
.setSimulateUnknownLength(simulateUnknownLength)
|
.setSimulateUnknownLength(simulateUnknownLength)
|
||||||
.setSimulatePartialReads(simulatePartialReads).build();
|
.setSimulatePartialReads(simulatePartialReads).build();
|
||||||
|
|
||||||
Assert.assertTrue(TestUtil.sniffTestData(extractor, input));
|
if (sniffFirst) {
|
||||||
input.resetPeekPosition();
|
Assert.assertTrue(TestUtil.sniffTestData(extractor, input));
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(extractor, input, 0, true);
|
input.resetPeekPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
FakeExtractorOutput extractorOutput = consumeTestData(extractor, input, 0, true);
|
||||||
if (simulateUnknownLength
|
if (simulateUnknownLength
|
||||||
&& assetExists(instrumentation, sampleFile + UNKNOWN_LENGTH_EXTENSION)) {
|
&& assetExists(instrumentation, file + UNKNOWN_LENGTH_EXTENSION)) {
|
||||||
extractorOutput.assertOutput(instrumentation, sampleFile + UNKNOWN_LENGTH_EXTENSION);
|
extractorOutput.assertOutput(instrumentation, file + UNKNOWN_LENGTH_EXTENSION);
|
||||||
} else {
|
} else {
|
||||||
extractorOutput.assertOutput(instrumentation, sampleFile + ".0" + DUMP_EXTENSION);
|
extractorOutput.assertOutput(instrumentation, file + ".0" + DUMP_EXTENSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
SeekMap seekMap = extractorOutput.seekMap;
|
SeekMap seekMap = extractorOutput.seekMap;
|
||||||
@ -133,7 +150,7 @@ public final class ExtractorAsserts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
consumeTestData(extractor, input, timeUs, extractorOutput, false);
|
consumeTestData(extractor, input, timeUs, extractorOutput, false);
|
||||||
extractorOutput.assertOutput(instrumentation, sampleFile + '.' + j + DUMP_EXTENSION);
|
extractorOutput.assertOutput(instrumentation, file + '.' + j + DUMP_EXTENSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user