diff --git a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/OggReaderTest.java b/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/OggReaderTest.java index 5b249c31b7..ddc645628c 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/OggReaderTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/OggReaderTest.java @@ -15,78 +15,73 @@ */ package com.google.android.exoplayer.extractor.ogg; +import com.google.android.exoplayer.testutil.FakeExtractorInput; +import com.google.android.exoplayer.testutil.FakeExtractorInput.SimulatedIOException; +import com.google.android.exoplayer.testutil.TestUtil; import com.google.android.exoplayer.util.ParsableByteArray; -import android.util.Log; +import android.test.MoreAsserts; import junit.framework.TestCase; import java.io.IOException; +import java.util.Arrays; +import java.util.Random; /** * Unit test for {@link OggReader} */ public final class OggReaderTest extends TestCase { - private static final String TAG = "OggReaderTest"; - + private Random random; private OggReader oggReader; - private RecordableOggExtractorInput extractorInput; + private ParsableByteArray scratch; @Override public void setUp() throws Exception { super.setUp(); - extractorInput = new RecordableOggExtractorInput(1024 * 64); - // we want the mocked ExtractorInput to throw errors often - extractorInput.doThrowExceptionsAtPeek(true); - extractorInput.doThrowExceptionsAtRead(true); - // create reader + random = new Random(0); oggReader = new OggReader(); - oggReader.reset(); + scratch = new ParsableByteArray(new byte[255 * 255], 0); } - public void testReadPacketUntilEOFIncludingAnEmptyPage() throws Exception { - // record first page with a single packet - extractorInput.recordOggHeader((byte) 0x02, 0, (byte) 0x01); - extractorInput.recordOggLaces(new byte[]{0x08}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(8)); - // record intermediate page with two packets - extractorInput.recordOggHeader((byte) 0x00, 16, (byte) 0x02); - extractorInput.recordOggLaces(new byte[]{(byte) 0xFF, 0x11}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(255 + 17)); - // empty page - extractorInput.recordOggHeader((byte) 0x00, 16, (byte) 0x00); - // record last page with two packets (256 and 271 bytes) - extractorInput.recordOggHeader((byte) 0x04, 128, (byte) 0x04); - extractorInput.recordOggLaces(new byte[]{(byte) 0xFF, 0x01, (byte) 0xff, 0x10}); - extractorInput.recordOggPacket(RecordableExtractorInput - .getBytesGrowingValues(255 + 1 + 255 + 16)); + public void testReadPacketsWithEmptyPage() throws Exception { + byte[] firstPacket = TestUtil.buildTestData(8, random); + byte[] secondPacket = TestUtil.buildTestData(272, random); + byte[] thirdPacket = TestUtil.buildTestData(256, random); + byte[] fourthPacket = TestUtil.buildTestData(271, random); + FakeExtractorInput input = createInput( + TestUtil.joinByteArrays( + // First page with a single packet. + TestData.buildOggHeader(0x02, 0, 1000, 0x01), + TestUtil.createByteArray(0x08), // Laces + firstPacket, + // Second page with a single packet. + TestData.buildOggHeader(0x00, 16, 1001, 0x02), + TestUtil.createByteArray(0xFF, 0x11), // Laces + secondPacket, + // Third page with zero packets. + TestData.buildOggHeader(0x00, 16, 1002, 0x00), + // Fourth page with two packets. + TestData.buildOggHeader(0x04, 128, 1003, 0x04), + TestUtil.createByteArray(0xFF, 0x01, 0xFF, 0x10), // Laces + thirdPacket, + fourthPacket)); - // read first packet - final ParsableByteArray packetArray = new ParsableByteArray(new byte[255 * 255], 0); - readPacketUntilSuccess(packetArray); - // verify - assertEquals(8, packetArray.limit()); + assertReadPacket(input, firstPacket); assertTrue((oggReader.getPageHeader().type & 0x02) == 0x02); assertFalse((oggReader.getPageHeader().type & 0x04) == 0x04); assertEquals(0x02, oggReader.getPageHeader().type); assertEquals(27 + 1, oggReader.getPageHeader().headerSize); assertEquals(8, oggReader.getPageHeader().bodySize); - assertEquals(RecordableOggExtractorInput.STREAM_REVISION, oggReader.getPageHeader().revision); + assertEquals(0x00, oggReader.getPageHeader().revision); assertEquals(1, oggReader.getPageHeader().pageSegmentCount); assertEquals(1000, oggReader.getPageHeader().pageSequenceNumber); assertEquals(4096, oggReader.getPageHeader().streamSerialNumber); assertEquals(0, oggReader.getPageHeader().granulePosition); - for (int i = 0; i < 8; i++) { - assertEquals(i, packetArray.readUnsignedByte()); - } - packetArray.reset(); - // read second packet - readPacketUntilSuccess(packetArray); - // verify - assertEquals(255 + 17, packetArray.limit()); + assertReadPacket(input, secondPacket); assertFalse((oggReader.getPageHeader().type & 0x02) == 0x02); assertFalse((oggReader.getPageHeader().type & 0x04) == 0x04); assertEquals(0, oggReader.getPageHeader().type); @@ -96,251 +91,140 @@ public final class OggReaderTest extends TestCase { assertEquals(1001, oggReader.getPageHeader().pageSequenceNumber); assertEquals(16, oggReader.getPageHeader().granulePosition); - packetArray.reset(); - // read next packet and skip empty page - readPacketUntilSuccess(packetArray); - // verify - assertEquals(255 + 1, packetArray.limit()); + assertReadPacket(input, thirdPacket); assertFalse((oggReader.getPageHeader().type & 0x02) == 0x02); assertTrue((oggReader.getPageHeader().type & 0x04) == 0x04); assertEquals(4, oggReader.getPageHeader().type); assertEquals(27 + 4, oggReader.getPageHeader().headerSize); assertEquals(255 + 1 + 255 + 16, oggReader.getPageHeader().bodySize); assertEquals(4, oggReader.getPageHeader().pageSegmentCount); - // page 1002 is empty, so current is 1003 + // Page 1002 is empty, so current page is 1003. assertEquals(1003, oggReader.getPageHeader().pageSequenceNumber); assertEquals(128, oggReader.getPageHeader().granulePosition); - packetArray.reset(); - // read last packet - readPacketUntilSuccess(packetArray); - assertEquals(255 + 16, packetArray.limit()); - // EOF! - readEOFUntilSuccess(packetArray, 10); + assertReadPacket(input, fourthPacket); + + assertReadEof(input); } public void testReadPacketWithZeroSizeTerminator() throws Exception { - // record first page with a single packet - extractorInput.recordOggHeader((byte) 0x06, 0, (byte) 0x04); - extractorInput.recordOggLaces(new byte[]{(byte) 0xff, 0x00, 0x00, 0x08}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(255 + 8)); + byte[] firstPacket = TestUtil.buildTestData(255, random); + byte[] secondPacket = TestUtil.buildTestData(8, random); - ParsableByteArray packetArray = new ParsableByteArray(new byte[255 * 255], 0); - readPacketUntilSuccess(packetArray); - assertEquals(255, packetArray.limit()); + FakeExtractorInput input = createInput( + TestUtil.joinByteArrays( + TestData.buildOggHeader(0x06, 0, 1000, 0x04), + TestUtil.createByteArray(0xFF, 0x00, 0x00, 0x08), // Laces. + firstPacket, + secondPacket)); - packetArray.reset(); - readPacketUntilSuccess(packetArray); - assertEquals(8, packetArray.limit()); - - readEOFUntilSuccess(packetArray, 10); + assertReadPacket(input, firstPacket); + assertReadPacket(input, secondPacket); + assertReadEof(input); } - public void testReadContinuedPacket() throws Exception { - // record first page with a packet continuing on the second page - extractorInput.recordOggHeader((byte) 0x02, 0, (byte) 0x02); - extractorInput.recordOggLaces(new byte[]{(byte) 0xFF, (byte) 0xFF}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(510)); - // record the continuing page - extractorInput.recordOggHeader((byte) 0x05, 10, (byte) 0x01); - extractorInput.recordOggLaces(new byte[]{0x08}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(8, (byte) 0x22)); + public void testReadContinuedPacketOverTwoPages() throws Exception { + byte[] firstPacket = TestUtil.buildTestData(518); - // there is only one single packet across two pages - ParsableByteArray packetArray = new ParsableByteArray(new byte[255 * 255], 0); - readPacketUntilSuccess(packetArray); + FakeExtractorInput input = createInput( + TestUtil.joinByteArrays( + // First page. + TestData.buildOggHeader(0x02, 0, 1000, 0x02), + TestUtil.createByteArray(0xFF, 0xFF), // Laces. + Arrays.copyOf(firstPacket, 510), + // Second page (continued packet). + TestData.buildOggHeader(0x05, 10, 1001, 0x01), + TestUtil.createByteArray(0x08), // Laces. + Arrays.copyOfRange(firstPacket, 510, 510 + 8))); - assertEquals(255 + 255 + 8, packetArray.limit()); + assertReadPacket(input, firstPacket); assertTrue((oggReader.getPageHeader().type & 0x04) == 0x04); assertFalse((oggReader.getPageHeader().type & 0x02) == 0x02); - // we must be on the second page already assertEquals(1001, oggReader.getPageHeader().pageSequenceNumber); - // verify packet data - for (int i = 0; i < 255; i++) { - assertEquals(i, packetArray.readUnsignedByte()); - } - assertEquals(255, packetArray.getPosition()); - for (int i = 0; i < 255; i++) { - assertEquals(i, packetArray.readUnsignedByte()); - } - assertEquals(510, packetArray.getPosition()); - for (int i = 0; i < 8; i++) { - assertEquals(i + 0x22, packetArray.readUnsignedByte()); - } - assertEquals(0, packetArray.bytesLeft()); - // EOF! - readEOFUntilSuccess(packetArray, 10); + assertReadEof(input); } - // no one does this with vorbis buts it's supported - public void testReadContinuedPacketOverMoreThan2Pages() throws Exception { - // record first page with a packet continuing on the second page - extractorInput.recordOggHeader((byte) 0x02, 0, (byte) 0x02); - extractorInput.recordOggLaces(new byte[]{(byte) 0xFF, (byte) 0xFF}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(510)); - // record the first continuing page - extractorInput.recordOggHeader((byte) 0x01, 10, (byte) 0x01); - extractorInput.recordOggLaces(new byte[]{(byte) 0xFF}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(255)); - // record the second continuing page - extractorInput.recordOggHeader((byte) 0x01, 10, (byte) 0x01); - extractorInput.recordOggLaces(new byte[]{(byte) 0xFF}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(255)); - // record the third continuing page - extractorInput.recordOggHeader((byte) 0x05, 10, (byte) 0x01); - extractorInput.recordOggLaces(new byte[]{(byte) 0x08}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(8, (byte) 0x22)); + public void testReadContinuedPacketOverFourPages() throws Exception { + byte[] firstPacket = TestUtil.buildTestData(1028); - // there is only one single packet across two pages - ParsableByteArray packetArray = new ParsableByteArray(new byte[255 * 255], 0); - readPacketUntilSuccess(packetArray); + FakeExtractorInput input = createInput( + TestUtil.joinByteArrays( + // First page. + TestData.buildOggHeader(0x02, 0, 1000, 0x02), + TestUtil.createByteArray(0xFF, 0xFF), // Laces. + Arrays.copyOf(firstPacket, 510), + // Second page (continued packet). + TestData.buildOggHeader(0x01, 10, 1001, 0x01), + TestUtil.createByteArray(0xFF), // Laces. + Arrays.copyOfRange(firstPacket, 510, 510 + 255), + // Third page (continued packet). + TestData.buildOggHeader(0x01, 10, 1002, 0x01), + TestUtil.createByteArray(0xFF), // Laces. + Arrays.copyOfRange(firstPacket, 510 + 255, 510 + 255 + 255), + // Fourth page (continued packet). + TestData.buildOggHeader(0x05, 10, 1003, 0x01), + TestUtil.createByteArray(0x08), // Laces. + Arrays.copyOfRange(firstPacket, 510 + 255 + 255, 510 + 255 + 255 + 8))); - assertEquals(255 + 255 + 255 + 255 + 8, packetArray.limit()); + assertReadPacket(input, firstPacket); assertTrue((oggReader.getPageHeader().type & 0x04) == 0x04); assertFalse((oggReader.getPageHeader().type & 0x02) == 0x02); - // we must be on the fourth page already assertEquals(1003, oggReader.getPageHeader().pageSequenceNumber); - // verify packet data - for (int i = 0; i < 255; i++) { - assertEquals(i, packetArray.readUnsignedByte()); - } - assertEquals(255, packetArray.getPosition()); - for (int i = 0; i < 255; i++) { - assertEquals(i, packetArray.readUnsignedByte()); - } - assertEquals(510, packetArray.getPosition()); - for (int i = 0; i < 255; i++) { - assertEquals(i, packetArray.readUnsignedByte()); - } - assertEquals(765, packetArray.getPosition()); - for (int i = 0; i < 255; i++) { - assertEquals(i, packetArray.readUnsignedByte()); - } - assertEquals(1020, packetArray.getPosition()); - for (int i = 0; i < 8; i++) { - assertEquals(i + 0x22, packetArray.readUnsignedByte()); - } - assertEquals(0, packetArray.bytesLeft()); - // EOF! - readEOFUntilSuccess(packetArray, 10); - } - - public void testReadExceptionThrownWhilePeekingHeader() throws Exception { - // record first page with two packets packet - extractorInput.recordOggHeader((byte) 0x02, 0, (byte) 0x02); - extractorInput.recordOggLaces(new byte[]{(byte) 0x01, (byte) 0x08}); - extractorInput.recordOggPacket(new byte[]{0x10}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(8)); - - // record next page - extractorInput.recordOggHeader((byte) 0x05, 10, (byte) 0x01); - extractorInput.recordOggLaces(new byte[]{0x08}); - extractorInput.recordOggPacket(RecordableExtractorInput.getBytesGrowingValues(8, (byte) 0x22)); - - ParsableByteArray packetArray = new ParsableByteArray(new byte[255 * 255], 0); - readPacketUntilSuccess(packetArray); - // verify packet data - assertEquals(1, packetArray.limit()); - assertEquals(0x10, packetArray.data[0]); - // verify header - assertTrue((oggReader.getPageHeader().type & 0x02) == 0x02); - assertFalse((oggReader.getPageHeader().type & 0x04) == 0x04); - assertEquals(27 + 2, oggReader.getPageHeader().headerSize); - assertEquals(9, oggReader.getPageHeader().bodySize); - assertEquals(2, oggReader.getPageHeader().pageSegmentCount); - assertEquals(1000, oggReader.getPageHeader().pageSequenceNumber); - assertEquals(0, oggReader.getPageHeader().granulePosition); - - packetArray.reset(); - readPacketUntilSuccess(packetArray); - } - - public void testReadNoZeroSizedPacketsAreReturned() throws Exception { - extractorInput.recordOggHeader((byte) 0x02, 0, (byte) 0x04); - extractorInput.recordOggLaces(new byte[]{(byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x08}); - extractorInput.recordOggPacket(new byte[]{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}); - extractorInput.recordOggPacket(new byte[]{0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}); - - ParsableByteArray packetArray = new ParsableByteArray(new byte[1024], 0); - - readPacketUntilSuccess(packetArray); - assertEquals(8, packetArray.limit()); - assertEquals(0x10, packetArray.data[0]); - assertEquals(0x10, packetArray.data[7]); - - packetArray.reset(); - readPacketUntilSuccess(packetArray); - assertEquals(8, packetArray.limit()); - assertEquals(0x20, packetArray.data[0]); - assertEquals(0x20, packetArray.data[7]); - - readEOFUntilSuccess(packetArray, 10); + assertReadEof(input); } public void testReadZeroSizedPacketsAtEndOfStream() throws Exception { - extractorInput.recordOggHeader((byte) 0x02, 0, (byte) 0x01); - extractorInput.recordOggLaces(new byte[]{(byte) 0x08}); - extractorInput.recordOggPacket(new byte[]{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}); + byte[] firstPacket = TestUtil.buildTestData(8, random); + byte[] secondPacket = TestUtil.buildTestData(8, random); + byte[] thirdPacket = TestUtil.buildTestData(8, random); - extractorInput.recordOggHeader((byte) 0x04, 0, (byte) 0x03); - extractorInput.recordOggLaces(new byte[]{(byte) 0x08, (byte) 0x00, (byte) 0x00}); - extractorInput.recordOggPacket(new byte[]{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}); + FakeExtractorInput input = createInput( + TestUtil.joinByteArrays( + TestData.buildOggHeader(0x02, 0, 1000, 0x01), + TestUtil.createByteArray(0x08), // Laces. + firstPacket, + TestData.buildOggHeader(0x04, 0, 1001, 0x03), + TestUtil.createByteArray(0x08, 0x00, 0x00), // Laces. + secondPacket, + TestData.buildOggHeader(0x04, 0, 1002, 0x03), + TestUtil.createByteArray(0x08, 0x00, 0x00), // Laces. + thirdPacket)); - extractorInput.recordOggHeader((byte) 0x04, 0, (byte) 0x03); - extractorInput.recordOggLaces(new byte[]{(byte) 0x08, 0x00, 0x00}); - extractorInput.recordOggPacket(new byte[]{0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}); - - ParsableByteArray packetArray = new ParsableByteArray(new byte[1024], 0); - - readPacketUntilSuccess(packetArray); - assertEquals(8, packetArray.limit()); - - packetArray.reset(); - readPacketUntilSuccess(packetArray); - assertEquals(8, packetArray.limit()); - - packetArray.reset(); - readPacketUntilSuccess(packetArray); - assertEquals(8, packetArray.limit()); - - packetArray.reset(); - readEOFUntilSuccess(packetArray, 10); - assertEquals(0, packetArray.limit()); + assertReadPacket(input, firstPacket); + assertReadPacket(input, secondPacket); + assertReadPacket(input, thirdPacket); + assertReadEof(input); } - private void readPacketUntilSuccess(ParsableByteArray packetArray) { - int exceptionCount = 0; - while (exceptionCount < 10) { - try { - assertTrue(oggReader.readPacket(extractorInput, packetArray)); - break; - } catch (IOException | InterruptedException e) { - exceptionCount++; - extractorInput.resetPeekPosition(); - } - } - - if (exceptionCount == 10) { - fail("maxException threshold reached"); - } + private static FakeExtractorInput createInput(byte[] data) { + return new FakeExtractorInput.Builder().setData(data).setSimulateIOErrors(true) + .setSimulateUnknownLength(true).setSimulatePartialReads(true).build(); } - private void readEOFUntilSuccess(ParsableByteArray packetArray, int maxExceptions) { - int exceptionCount = 0; - while (exceptionCount < maxExceptions) { + private void assertReadPacket(FakeExtractorInput extractorInput, byte[] expected) + throws IOException, InterruptedException { + scratch.reset(); + assertTrue(readPacket(extractorInput, scratch)); + MoreAsserts.assertEquals(expected, Arrays.copyOf(scratch.data, scratch.limit())); + } + + private void assertReadEof(FakeExtractorInput extractorInput) + throws IOException, InterruptedException { + scratch.reset(); + assertFalse(readPacket(extractorInput, scratch)); + } + + private boolean readPacket(FakeExtractorInput input, ParsableByteArray scratch) + throws InterruptedException, IOException { + while (true) { try { - assertFalse(oggReader.readPacket(extractorInput, packetArray)); - break; - } catch (IOException | InterruptedException e) { - exceptionCount++; - Log.e(TAG, e.getMessage(), e); + return oggReader.readPacket(input, scratch); + } catch (SimulatedIOException e) { + // Ignore. } } - if (exceptionCount == maxExceptions) { - fail("maxException threshold reached"); - } } } diff --git a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/OggVorbisExtractorTest.java b/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/OggVorbisExtractorTest.java index a64bc54865..45b4ad63a6 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/OggVorbisExtractorTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/OggVorbisExtractorTest.java @@ -15,38 +15,41 @@ */ package com.google.android.exoplayer.extractor.ogg; +import com.google.android.exoplayer.extractor.ogg.OggVorbisExtractor.VorbisSetup; +import com.google.android.exoplayer.testutil.FakeExtractorInput; +import com.google.android.exoplayer.testutil.FakeExtractorInput.SimulatedIOException; +import com.google.android.exoplayer.testutil.TestUtil; import com.google.android.exoplayer.util.ParsableByteArray; -import android.util.Log; - import junit.framework.TestCase; +import java.io.IOException; + /** * Unit test for {@link OggVorbisExtractor}. */ public final class OggVorbisExtractorTest extends TestCase { - private static final String TAG = "OggVorbisExtractorTest"; - private OggVorbisExtractor extractor; - private RecordableOggExtractorInput extractorInput; + private ParsableByteArray scratch; @Override public void setUp() throws Exception { super.setUp(); - extractorInput = new RecordableOggExtractorInput(1024 * 64); extractor = new OggVorbisExtractor(); + scratch = new ParsableByteArray(new byte[255 * 255], 0); } public void testSniff() throws Exception { - extractorInput.recordOggHeader((byte) 0x02, 0, (byte) 0x02); - extractorInput.recordOggLaces(new byte[]{120, 120}); - assertTrue(extractor.sniff(extractorInput)); + byte[] data = TestUtil.joinByteArrays( + TestData.buildOggHeader(0x02, 0, 1000, 0x02), + TestUtil.createByteArray(120, 120)); // Laces + assertTrue(sniff(createInput(data))); } public void testSniffFails() throws Exception { - extractorInput.recordOggHeader((byte) 0x00, 0, (byte) 0); - assertFalse(extractor.sniff(extractorInput)); + byte[] data = TestData.buildOggHeader(0x00, 0, 1000, 0x00); + assertFalse(sniff(createInput(data))); } public void testAppendNumberOfSamples() throws Exception { @@ -60,57 +63,62 @@ public final class OggVorbisExtractorTest extends TestCase { assertEquals(0x01, buffer.data[3]); } - public void testReadSetupHeadersWithIOExceptions() { - extractorInput.doThrowExceptionsAtRead(true); - extractorInput.doThrowExceptionsAtPeek(true); - + public void testReadSetupHeadersWithIOExceptions() throws IOException, InterruptedException { byte[] data = TestData.getVorbisHeaderPages(); - extractorInput.record(data); + OggVorbisExtractor.VorbisSetup vorbisSetup = readSetupHeaders(createInput(data)); - int exceptionCount = 0; - int maxExceptions = 20; - OggVorbisExtractor.VorbisSetup vorbisSetup; - while (exceptionCount < maxExceptions) { + assertNotNull(vorbisSetup.idHeader); + assertNotNull(vorbisSetup.commentHeader); + assertNotNull(vorbisSetup.setupHeaderData); + assertNotNull(vorbisSetup.modes); + + assertEquals(45, vorbisSetup.commentHeader.length); + assertEquals(30, vorbisSetup.idHeader.data.length); + assertEquals(3597, vorbisSetup.setupHeaderData.length); + + assertEquals(-1, vorbisSetup.idHeader.bitrateMax); + assertEquals(-1, vorbisSetup.idHeader.bitrateMin); + assertEquals(66666, vorbisSetup.idHeader.bitrateNominal); + assertEquals(512, vorbisSetup.idHeader.blockSize0); + assertEquals(1024, vorbisSetup.idHeader.blockSize1); + assertEquals(2, vorbisSetup.idHeader.channels); + assertTrue(vorbisSetup.idHeader.framingFlag); + assertEquals(22050, vorbisSetup.idHeader.sampleRate); + assertEquals(0, vorbisSetup.idHeader.version); + + assertEquals("Xiph.Org libVorbis I 20030909", vorbisSetup.commentHeader.vendor); + assertEquals(1, vorbisSetup.iLogModes); + + assertEquals(data[data.length - 1], + vorbisSetup.setupHeaderData[vorbisSetup.setupHeaderData.length - 1]); + + assertFalse(vorbisSetup.modes[0].blockFlag); + assertTrue(vorbisSetup.modes[1].blockFlag); + } + + private static FakeExtractorInput createInput(byte[] data) { + return new FakeExtractorInput.Builder().setData(data).setSimulateIOErrors(true) + .setSimulateUnknownLength(true).setSimulatePartialReads(true).build(); + } + + private boolean sniff(FakeExtractorInput input) throws InterruptedException, IOException { + while (true) { try { - vorbisSetup = extractor.readSetupHeaders(extractorInput, - new ParsableByteArray(new byte[255 * 255], 0)); - - assertNotNull(vorbisSetup.idHeader); - assertNotNull(vorbisSetup.commentHeader); - assertNotNull(vorbisSetup.setupHeaderData); - assertNotNull(vorbisSetup.modes); - - assertEquals(45, vorbisSetup.commentHeader.length); - assertEquals(30, vorbisSetup.idHeader.data.length); - assertEquals(3597, vorbisSetup.setupHeaderData.length); - - assertEquals(-1, vorbisSetup.idHeader.bitrateMax); - assertEquals(-1, vorbisSetup.idHeader.bitrateMin); - assertEquals(66666, vorbisSetup.idHeader.bitrateNominal); - assertEquals(512, vorbisSetup.idHeader.blockSize0); - assertEquals(1024, vorbisSetup.idHeader.blockSize1); - assertEquals(2, vorbisSetup.idHeader.channels); - assertTrue(vorbisSetup.idHeader.framingFlag); - assertEquals(22050, vorbisSetup.idHeader.sampleRate); - assertEquals(0, vorbisSetup.idHeader.version); - - assertEquals("Xiph.Org libVorbis I 20030909", vorbisSetup.commentHeader.vendor); - assertEquals(1, vorbisSetup.iLogModes); - - assertEquals(data[data.length - 1], - vorbisSetup.setupHeaderData[vorbisSetup.setupHeaderData.length - 1]); - - assertFalse(vorbisSetup.modes[0].blockFlag); - assertTrue(vorbisSetup.modes[1].blockFlag); - break; - } catch (Throwable e) { - Log.e(TAG, e.getMessage(), e); - extractorInput.resetPeekPosition(); - exceptionCount++; + return extractor.sniff(input); + } catch (SimulatedIOException e) { + // Ignore. } } - if (exceptionCount >= maxExceptions) { - fail("more than " + maxExceptions + " exceptions thrown"); + } + + private VorbisSetup readSetupHeaders(FakeExtractorInput input) + throws IOException, InterruptedException { + while (true) { + try { + return extractor.readSetupHeaders(input, scratch); + } catch (SimulatedIOException e) { + // Ignore. + } } } diff --git a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/RecordableExtractorInput.java b/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/RecordableExtractorInput.java deleted file mode 100644 index 0f6c126f60..0000000000 --- a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/RecordableExtractorInput.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer.extractor.ogg; - -import com.google.android.exoplayer.C; -import com.google.android.exoplayer.extractor.ExtractorInput; - -import java.io.EOFException; -import java.io.IOException; - -/** - * Implementation of {@link ExtractorInput} for testing purpose. - */ -/* package */ class RecordableExtractorInput implements ExtractorInput { - - private byte[] data; - private int readPosition; - private int writePosition; - private int peekPosition; - - private boolean throwExceptionsAtRead = false; - private boolean throwExceptionsAtPeek = false; - private int numberOfReadsUntilException = 1; - private int numberOfPeeksUntilException = 1; - private int readCounter; - private int peekCounter; - private int maxReadExceptions = Integer.MAX_VALUE; - private int maxPeekExceptions = Integer.MAX_VALUE; - private int readExceptionCounter; - private int peekExceptionCounter; - - - /** - * Constructs an instance with a initial array of bytes. - * - * @param data The initial data. - * @param writePosition The {@code writePosition} from where to start recording. - */ - public RecordableExtractorInput(byte[] data, int writePosition) { - this.data = data; - this.writePosition = writePosition; - } - - /** - * Constructs an instance with an empty data array with length {@code maxBytes}. - * - * @param maxBytes the maximal number of bytes this {@code ExtractorInput} can store. - */ - public RecordableExtractorInput(int maxBytes) { - this(new byte[maxBytes], 0); - } - - @Override - public int read(byte[] target, int offset, int length) throws IOException, InterruptedException { - readFully(target, offset, length); - return isEOF() ? C.RESULT_END_OF_INPUT : length; - } - - @Override - public boolean readFully(byte[] target, int offset, int length, boolean allowEndOfInput) - throws IOException, InterruptedException { - readCounter++; - if (throwExceptionsAtRead - && readExceptionCounter < maxReadExceptions - && readCounter % numberOfReadsUntilException == 0) { - readCounter = 0; - numberOfReadsUntilException++; - readExceptionCounter++; - throw new IOException("deliberately thrown an exception for testing"); - } - if (readPosition + length > writePosition) { - if (!allowEndOfInput) { - throw new EOFException(); - } - return false; - } - System.arraycopy(data, readPosition, target, offset, length); - readPosition += length; - peekPosition = readPosition; - return true; - } - - @Override - public void readFully(byte[] target, int offset, int length) - throws IOException, InterruptedException { - readFully(target, offset, length, false); - } - - @Override - public int skip(int length) throws IOException, InterruptedException { - skipFully(length); - return isEOF() ? C.RESULT_END_OF_INPUT : length; - } - - private boolean isEOF() { - return readPosition == writePosition; - } - - @Override - public boolean skipFully(int length, boolean allowEndOfInput) - throws IOException, InterruptedException { - if (readPosition + length >= writePosition) { - if (!allowEndOfInput) { - throw new EOFException(); - } - return false; - } - readPosition += length; - peekPosition = readPosition; - return true; - } - - @Override - public void skipFully(int length) throws IOException, InterruptedException { - skipFully(length, false); - } - - @Override - public boolean peekFully(byte[] target, int offset, int length, boolean allowEndOfInput) - throws IOException, InterruptedException { - peekCounter++; - if (throwExceptionsAtPeek - && peekExceptionCounter < maxPeekExceptions - && peekCounter % numberOfPeeksUntilException == 0) { - peekCounter = 0; - numberOfPeeksUntilException++; - peekExceptionCounter++; - throw new IOException("deliberately thrown an exception for testing"); - } - if (peekPosition + length > writePosition) { - if (!allowEndOfInput) { - throw new EOFException(); - } - return false; - } - System.arraycopy(data, peekPosition, target, offset, length); - peekPosition += length; - return true; - } - - @Override - public void peekFully(byte[] target, int offset, int length) - throws IOException, InterruptedException { - peekFully(target, offset, length, false); - } - - @Override - public boolean advancePeekPosition(int length, boolean allowEndOfInput) - throws IOException, InterruptedException { - if (peekPosition + length >= writePosition) { - if (!allowEndOfInput) { - throw new EOFException(); - } - return false; - } - peekPosition += length; - return true; - } - - @Override - public void advancePeekPosition(int length) throws IOException, InterruptedException { - advancePeekPosition(length, false); - } - - @Override - public void resetPeekPosition() { - peekPosition = readPosition; - } - - @Override - public long getPeekPosition() { - return peekPosition; - } - - @Override - public long getPosition() { - return readPosition; - } - - @Override - public long getLength() { - return writePosition; - } - - /** - * Records the {@code bytes}. - * - * @param bytes the bytes to record. - */ - public void record(final byte[] bytes) { - System.arraycopy(bytes, 0, data, writePosition, bytes.length); - writePosition += bytes.length; - } - - /** Records a single byte. **/ - public void record(byte b) { - record(new byte[] {b}); - } - - /** - * Gets a byte array with length {@code length} with ascending values starting from 0 (zero). - * - * @param length the length of the array. - * @return an array of bytes with ascending values. - */ - public static byte[] getBytesGrowingValues(int length) { - return fillBytesGrowingValues(new byte[length], length, (byte) 0); - } - - /** - * Gets a byte array with length {@code length} with ascending values starting - * from {@code startValue}. - * - * @param length the length of the array. - * @param startValue the value from which to start. - * @return an array of bytes with ascending values starting from {@code startValue}. - */ - public static byte[] getBytesGrowingValues(int length, byte startValue) { - return fillBytesGrowingValues(new byte[length], length, startValue); - } - - /** - * Fills the byte array passed as argument with ascending values. - * - * @param bytes the byte array to fill with values. - * @param limit the number of bytes to set in the array. - * @param startValue the startValue from which the values in the array have to start. - */ - public static byte[] fillBytesGrowingValues(byte[] bytes, int limit, byte startValue) { - for (int i = 0; i < bytes.length; i++) { - if (i < limit) { - bytes[i] = (byte) ((i + startValue) % 255); - } else { - bytes[i] = 0; - } - } - return bytes; - } - - public void setMaxReadExceptions(int maxReadExceptions) { - this.maxReadExceptions = maxReadExceptions; - } - - public void setMaxPeekExceptions(int maxPeekExceptions) { - this.maxPeekExceptions = maxPeekExceptions; - } - - public void setNumberOfReadsUntilException(int numberOfReadsUntilException) { - this.numberOfReadsUntilException = numberOfReadsUntilException; - } - - public void setNumberOfPeeksUntilException(int numberOfPeeksUntilException) { - this.numberOfPeeksUntilException = numberOfPeeksUntilException; - } - - public void doThrowExceptionsAtRead(boolean throwExceptionsAtRead) { - this.throwExceptionsAtRead = throwExceptionsAtRead; - } - - public void doThrowExceptionsAtPeek(boolean throwExceptionsAtPeek) { - this.throwExceptionsAtPeek = throwExceptionsAtPeek; - } - -} diff --git a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/RecordableOggExtractorInput.java b/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/RecordableOggExtractorInput.java deleted file mode 100644 index 450585f9ab..0000000000 --- a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/RecordableOggExtractorInput.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer.extractor.ogg; - -/** - * A {@link RecordableOggExtractorInput} with convenient methods to record an OGG byte stream. - */ -/* package */ final class RecordableOggExtractorInput extends RecordableExtractorInput { - - public static final byte STREAM_REVISION = 0x00; - - private long pageSequenceCounter; - - public RecordableOggExtractorInput(byte[] data, int writeOffset) { - super(data, writeOffset); - pageSequenceCounter = 1000; - } - - public RecordableOggExtractorInput(int maxBytes) { - this(new byte[maxBytes], 0); - } - - /** - * Syntax sugar to make tests more readable. - * - * @param laces the laces to record to the data. - */ - protected void recordOggLaces(final byte[] laces) { - record(laces); - } - - /** - * Syntax sugar to make tests more readable. - * - * @param packet the packet bytes to record to the data. - */ - protected void recordOggPacket(final byte[] packet) { - record(packet); - } - - protected void recordOggHeader(final byte headerType, final long granule, - final byte pageSegmentCount) { - record((byte) 0x4F); // O - record((byte) 0x67); // g - record((byte) 0x67); // g - record((byte) 0x53); // S - record(STREAM_REVISION); - record(headerType); - recordGranulePosition(granule); - record((byte) 0x00); // LSB of data serial number - record((byte) 0x10); - record((byte) 0x00); - record((byte) 0x00); // MSB of data serial number - recordPageSequenceCounter(); - record((byte) 0x00); // LSB of page checksum - record((byte) 0x00); - record((byte) 0x00); - record((byte) 0x00); // MSB of page checksum - record(pageSegmentCount); // 0 - 255 - } - - protected void recordGranulePosition(long granule) { - record((byte) (granule & 0xFF)); - record((byte) ((granule >> 8) & 0xFF)); - record((byte) ((granule >> 16) & 0xFF)); - record((byte) ((granule >> 24) & 0xFF)); - record((byte) ((granule >> 32) & 0xFF)); - record((byte) ((granule >> 40) & 0xFF)); - record((byte) ((granule >> 48) & 0xFF)); - record((byte) ((granule >> 56) & 0xFF)); - } - - protected void recordPageSequenceCounter() { - record((byte) (pageSequenceCounter & 0xFF)); - record((byte) ((pageSequenceCounter >> 8) & 0xFF)); - record((byte) ((pageSequenceCounter >> 16) & 0xFF)); - record((byte) ((pageSequenceCounter++ >> 24) & 0xFF)); - } - -} diff --git a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/TestData.java b/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/TestData.java index e76086249e..caf97e32a3 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/TestData.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/extractor/ogg/TestData.java @@ -15,11 +15,42 @@ */ package com.google.android.exoplayer.extractor.ogg; +import com.google.android.exoplayer.testutil.TestUtil; + /** * Provides ogg/vorbis test data in bytes for unit tests. */ /* package */ final class TestData { + public static byte[] buildOggHeader(int headerType, long granule, int pageSequenceCounter, + int pageSegmentCount) { + return TestUtil.createByteArray( + 0x4F, 0x67, 0x67, 0x53, // Oggs. + 0x00, // Stream revision. + headerType, + (int) (granule >> 0) & 0xFF, + (int) (granule >> 8) & 0xFF, + (int) (granule >> 16) & 0xFF, + (int) (granule >> 24) & 0xFF, + (int) (granule >> 32) & 0xFF, + (int) (granule >> 40) & 0xFF, + (int) (granule >> 48) & 0xFF, + (int) (granule >> 56) & 0xFF, + 0x00, // LSB of data serial number. + 0x10, + 0x00, + 0x00, // MSB of data serial number. + (pageSequenceCounter >> 0) & 0xFF, + (pageSequenceCounter >> 8) & 0xFF, + (pageSequenceCounter >> 16) & 0xFF, + (pageSequenceCounter >> 24) & 0xFF, + 0x00, // LSB of page checksum. + 0x00, + 0x00, + 0x00, // MSB of page checksum. + pageSegmentCount); + } + /** * Returns the initial two pages of bytes which by spec contain the three vorbis header packets: * identification, comment and setup header. diff --git a/library/src/androidTest/java/com/google/android/exoplayer/testutil/FakeExtractorInput.java b/library/src/androidTest/java/com/google/android/exoplayer/testutil/FakeExtractorInput.java index 4ed060845c..8b2c43746d 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/testutil/FakeExtractorInput.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/testutil/FakeExtractorInput.java @@ -28,7 +28,7 @@ import java.io.IOException; /** * A fake {@link ExtractorInput} capable of simulating various scenarios. *

- * Read, skip and peek errors can be simulated using {@link Builder#setSimulateIOErrors)}. When + * Read, skip and peek errors can be simulated using {@link Builder#setSimulateIOErrors}. When * enabled each read and skip will throw a {@link SimulatedIOException} unless one has already been * thrown from the current position. Each peek will throw {@link SimulatedIOException} unless one * has already been thrown from the current peek position. When a {@link SimulatedIOException} is @@ -43,7 +43,7 @@ import java.io.IOException; * bytes then it will be fully satisfied, since it has the same target position of 10. *

* Unknown data length can be simulated using {@link Builder#setSimulateUnknownLength}. When enabled - * {@link getLength()} will return {@link C#LENGTH_UNBOUNDED} rather than the length of the data. + * {@link #getLength()} will return {@link C#LENGTH_UNBOUNDED} rather than the length of the data. */ public final class FakeExtractorInput implements ExtractorInput { diff --git a/library/src/androidTest/java/com/google/android/exoplayer/testutil/TestUtil.java b/library/src/androidTest/java/com/google/android/exoplayer/testutil/TestUtil.java index ab544b2530..fc479bbc28 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/testutil/TestUtil.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/testutil/TestUtil.java @@ -56,7 +56,10 @@ public class TestUtil { } public static byte[] buildTestData(int length, int seed) { - Random random = new Random(seed); + return buildTestData(length, new Random(seed)); + } + + public static byte[] buildTestData(int length, Random random) { byte[] source = new byte[length]; random.nextBytes(source); return source;