From 432ff5ea7079e6573b42e27dac02e19188e24810 Mon Sep 17 00:00:00 2001 From: Dustin Date: Sat, 29 Jan 2022 14:51:03 -0700 Subject: [PATCH] More AviExtractor tests --- .../extractor/avi/AviExtractor.java | 26 ++- .../extractor/avi/AviHeaderBox.java | 6 + .../extractor/avi/AviExtractorTest.java | 153 ++++++++++++++++++ .../extractor/avi/AviHeaderBoxTest.java | 4 +- .../exoplayer2/extractor/avi/DataHelper.java | 8 +- 5 files changed, 191 insertions(+), 6 deletions(-) diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java index 72fea3cb6c..d45914796c 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviExtractor.java @@ -351,7 +351,8 @@ public class AviExtractor implements Extractor { return RESULT_SEEK; } - private AviTrack getVideoTrack() { + @VisibleForTesting + AviTrack getVideoTrack() { for (@Nullable AviTrack aviTrack : aviTracks) { if (aviTrack != null && aviTrack.isVideo()) { return aviTrack; @@ -510,7 +511,7 @@ public class AviExtractor implements Extractor { final AviTrack aviTrack = getAviTrack(chunkId); if (aviTrack == null) { seekPosition.position = alignPosition(input.getPosition() + size); - Log.w(TAG, "Unknown tag=" + toString(chunkId) + " pos=" + (input.getPosition() - 8) + w("Unknown tag=" + toString(chunkId) + " pos=" + (input.getPosition() - 8) + " size=" + size + " moviEnd=" + moviEnd); return RESULT_SEEK; } @@ -590,6 +591,27 @@ public class AviExtractor implements Extractor { this.aviTracks = aviTracks; } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + void setAviHeader(final AviHeaderBox aviHeaderBox) { + aviHeader = aviHeaderBox; + } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + void setMovi(final int offset, final int end) { + moviOffset = offset; + moviEnd = end; + } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + AviTrack getChunkHandler() { + return chunkHandler; + } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + void setChunkHandler(final AviTrack aviTrack) { + chunkHandler = aviTrack; + } + private static void w(String message) { try { Log.w(TAG, message); diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java index 84b6557d0b..d2a21dd727 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBox.java @@ -1,5 +1,6 @@ package com.google.android.exoplayer2.extractor.avi; +import androidx.annotation.VisibleForTesting; import java.nio.ByteBuffer; public class AviHeaderBox extends ResidentBox { @@ -49,4 +50,9 @@ public class AviHeaderBox extends ResidentBox { // 28 - dwSuggestedBufferSize // 32 - dwWidth // 36 - dwHeight + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + void setFlags(int flags) { + byteBuffer.putInt(12, flags); + } } diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviExtractorTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviExtractorTest.java index 5c731e88f3..6f1e26bd79 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviExtractorTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviExtractorTest.java @@ -1,10 +1,14 @@ package com.google.android.exoplayer2.extractor.avi; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.Extractor; +import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.testutil.FakeExtractorInput; import com.google.android.exoplayer2.testutil.FakeExtractorOutput; +import com.google.android.exoplayer2.testutil.FakeTrackOutput; +import com.google.android.exoplayer2.util.MimeTypes; import java.io.IOException; import java.nio.ByteBuffer; import org.junit.Assert; @@ -296,4 +300,153 @@ public class AviExtractorTest { Assert.assertTrue(listBox.getChildren().get(0) instanceof AviHeaderBox); } + + @Test + public void findMovi_givenMoviListAndIndex() throws IOException { + final AviExtractor aviExtractor = new AviExtractor(); + aviExtractor.setAviHeader(DataHelper.createAviHeaderBox()); + final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput(); + aviExtractor.init(fakeExtractorOutput); + + ByteBuffer byteBuffer = AviExtractor.allocate(12); + byteBuffer.putInt(ListBox.LIST); + byteBuffer.putInt(64*1024); + byteBuffer.putInt(AviExtractor.MOVI); + final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).build(); + aviExtractor.findMovi(input, new PositionHolder()); + Assert.assertEquals(aviExtractor.state, AviExtractor.STATE_READ_IDX1); + } + + @Test + public void findMovi_givenMoviListAndNoIndex() throws IOException { + final AviExtractor aviExtractor = new AviExtractor(); + final AviHeaderBox aviHeaderBox = DataHelper.createAviHeaderBox(); + aviHeaderBox.setFlags(0); + aviExtractor.setAviHeader(aviHeaderBox); + final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput(); + aviExtractor.init(fakeExtractorOutput); + + ByteBuffer byteBuffer = AviExtractor.allocate(12); + byteBuffer.putInt(ListBox.LIST); + byteBuffer.putInt(64*1024); + byteBuffer.putInt(AviExtractor.MOVI); + final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).build(); + aviExtractor.state = AviExtractor.STATE_FIND_MOVI; + aviExtractor.read(input, new PositionHolder()); + Assert.assertEquals(aviExtractor.state, AviExtractor.STATE_READ_TRACKS); + Assert.assertTrue(fakeExtractorOutput.seekMap instanceof SeekMap.Unseekable); + } + + @Test + public void findMovi_givenJunk() throws IOException { + final AviExtractor aviExtractor = new AviExtractor(); + aviExtractor.setAviHeader(DataHelper.createAviHeaderBox()); + final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput(); + aviExtractor.init(fakeExtractorOutput); + + ByteBuffer byteBuffer = AviExtractor.allocate(12); + byteBuffer.putInt(AviExtractor.JUNK); + byteBuffer.putInt(64*1024); + final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).build(); + final PositionHolder positionHolder = new PositionHolder(); + aviExtractor.findMovi(input, positionHolder); + Assert.assertEquals(64 * 1024 + 8, positionHolder.position); + } + + private AviExtractor setupReadSamples() { + final AviExtractor aviExtractor = new AviExtractor(); + aviExtractor.setAviHeader(DataHelper.createAviHeaderBox()); + final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput(); + aviExtractor.init(fakeExtractorOutput); + + final AviTrack aviTrack = DataHelper.getVideoAviTrack(9); + aviExtractor.setAviTracks(new AviTrack[]{aviTrack}); + final Format format = new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_MP4V).build(); + aviTrack.trackOutput.format(format); + + aviExtractor.state = AviExtractor.STATE_READ_SAMPLES; + aviExtractor.setMovi(DataHelper.MOVI_OFFSET, 128*1024); + return aviExtractor; + } + + @Test + public void readSamples_givenAtEndOfInput() throws IOException { + AviExtractor aviExtractor = setupReadSamples(); + aviExtractor.setMovi(0, 0); + final AviTrack aviTrack = aviExtractor.getVideoTrack(); + final ByteBuffer byteBuffer = AviExtractor.allocate(32); + byteBuffer.putInt(aviTrack.chunkId); + byteBuffer.putInt(24); + + final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).build(); + Assert.assertEquals(Extractor.RESULT_END_OF_INPUT, aviExtractor.read(input, new PositionHolder())); + } + + @Test + public void readSamples_completeChunk() throws IOException { + AviExtractor aviExtractor = setupReadSamples(); + final AviTrack aviTrack = aviExtractor.getVideoTrack(); + final ByteBuffer byteBuffer = AviExtractor.allocate(32); + byteBuffer.putInt(aviTrack.chunkId); + byteBuffer.putInt(24); + + final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()) + .build(); + Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(input, new PositionHolder())); + + final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput; + Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length); + } + + @Test + public void readSamples_fragmentedChunk() throws IOException { + AviExtractor aviExtractor = setupReadSamples(); + final AviTrack aviTrack = aviExtractor.getVideoTrack(); + final int size = 24 + 16; + final ByteBuffer byteBuffer = AviExtractor.allocate(32); + byteBuffer.putInt(aviTrack.chunkId); + byteBuffer.putInt(size); + + final ExtractorInput chunk0 = new FakeExtractorInput.Builder().setData(byteBuffer.array()) + .build(); + Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(chunk0, new PositionHolder())); + + final ExtractorInput chunk1 = new FakeExtractorInput.Builder().setData(new byte[16]) + .build(); + Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(chunk1, new PositionHolder())); + + final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput; + Assert.assertEquals(size, fakeTrackOutput.getSampleData(0).length); + } + + @Test + public void seek_givenPosition0() throws IOException { + final AviExtractor aviExtractor = setupReadSamples(); + final AviTrack aviTrack = aviExtractor.getVideoTrack(); + aviExtractor.setChunkHandler(aviTrack); + aviTrack.getClock().setIndex(10); + + aviExtractor.seek(0L, 0L); + + Assert.assertNull(aviExtractor.getChunkHandler()); + Assert.assertEquals(0, aviTrack.getClock().getIndex()); + Assert.assertEquals(aviExtractor.state, AviExtractor.STATE_SEEK_START); + + + final ExtractorInput input = new FakeExtractorInput.Builder().setData(new byte[0]).build(); + final PositionHolder positionHolder = new PositionHolder(); + Assert.assertEquals(Extractor.RESULT_SEEK, aviExtractor.read(input, positionHolder)); + Assert.assertEquals(DataHelper.MOVI_OFFSET + 4, positionHolder.position); + } + + @Test + public void seek_givenKeyFrame() throws IOException { + final AviExtractor aviExtractor = setupReadSamples(); + final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap(); + aviExtractor.aviSeekMap = aviSeekMap; + final AviTrack aviTrack = aviExtractor.getVideoTrack(); + final long position = DataHelper.MOVI_OFFSET + aviSeekMap.keyFrameOffsetsDiv2[1] * 2L; + aviExtractor.seek(position, 0L); + Assert.assertEquals(aviSeekMap.seekIndexes[aviTrack.id][1], aviTrack.getClock().getIndex()); + } } \ No newline at end of file diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBoxTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBoxTest.java index 13eee629ef..e7b3a93e7b 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBoxTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviHeaderBoxTest.java @@ -8,9 +8,7 @@ public class AviHeaderBoxTest { @Test public void getters() { - final ByteBuffer byteBuffer = DataHelper.createAviHeader(); - final AviHeaderBox aviHeaderBox = new AviHeaderBox(AviHeaderBox.AVIH, - byteBuffer.capacity(), byteBuffer); + final AviHeaderBox aviHeaderBox = DataHelper.createAviHeaderBox(); Assert.assertEquals(DataHelper.VIDEO_US, aviHeaderBox.getMicroSecPerFrame()); Assert.assertTrue(aviHeaderBox.hasIndex()); Assert.assertFalse(aviHeaderBox.mustUseIndex()); diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/DataHelper.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/DataHelper.java index c50b04f8e5..805655317e 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/DataHelper.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/DataHelper.java @@ -18,6 +18,7 @@ public class DataHelper { static final int VIDEO_SIZE = 4096; static final int AUDIO_SIZE = 256; static final int AUDIO_ID = 1; + static final int MOVI_OFFSET = 4096; private static final long AUDIO_US = VIDEO_US / AUDIO_PER_VIDEO; //Base path "\ExoPlayer\library\extractor\." @@ -124,7 +125,7 @@ public class DataHelper { audioArray.add(0); audioArray.add(128); return new AviSeekMap(0, 100L, 8, keyFrameOffsetsDiv2, - new UnboundedIntArray[]{videoArray, audioArray}, 4096); + new UnboundedIntArray[]{videoArray, audioArray}, MOVI_OFFSET); } private static void putIndex(final ByteBuffer byteBuffer, int chunkId, int flags, int offset, @@ -186,4 +187,9 @@ public class DataHelper { byteBuffer.clear(); return byteBuffer; } + + public static AviHeaderBox createAviHeaderBox() { + final ByteBuffer byteBuffer = createAviHeader(); + return new AviHeaderBox(AviHeaderBox.AVIH, byteBuffer.capacity(), byteBuffer); + } }