From 1528b8b5ee4c43036253d1d2ab674ee65fe44ba8 Mon Sep 17 00:00:00 2001 From: Dustin Date: Wed, 2 Feb 2022 14:35:52 -0700 Subject: [PATCH] Found out RESULT_SEEK is a bad thing. Greatly improved Extractor efficiency. --- .../extractor/avi/AviExtractor.java | 52 +++++++++---------- .../extractor/avi/StreamHeaderBox.java | 3 +- .../extractor/avi/AviExtractorRoboTest.java | 18 +++++++ .../extractor/avi/AviExtractorTest.java | 44 ++++------------ .../extractor/avi/AviSeekMapTest.java | 5 +- .../extractor/avi/StreamHeaderBoxTest.java | 1 + 6 files changed, 59 insertions(+), 64 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 dd06a90c26..ded38ab0a6 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 @@ -72,15 +72,6 @@ public class AviExtractor implements Extractor { } } - static int alignPositionHolder(@NonNull ExtractorInput input, @NonNull PositionHolder seekPosition) { - final long position = input.getPosition(); - if ((position & 1) == 1) { - seekPosition.position = position + 1; - return RESULT_SEEK; - } - return RESULT_CONTINUE; - } - @NonNull static ByteBuffer allocate(int bytes) { final byte[] buffer = new byte[bytes]; @@ -503,47 +494,56 @@ public class AviExtractor implements Extractor { return null; } - int readSamples(@NonNull ExtractorInput input, @NonNull PositionHolder seekPosition) throws IOException { + int readSamples(@NonNull ExtractorInput input) throws IOException { if (chunkHandler != null) { if (chunkHandler.resume(input)) { chunkHandler = null; - return alignPositionHolder(input, seekPosition); + alignInput(input); } } else { - ByteBuffer byteBuffer = allocate(8); + final int toRead = 8; + ByteBuffer byteBuffer = allocate(toRead); final byte[] bytes = byteBuffer.array(); alignInput(input); - input.readFully(bytes, 0, 1); + input.readFully(bytes, 0, toRead); + //This is super inefficient, but should be rare while (bytes[0] == 0) { - input.readFully(bytes, 0, 1); + for (int i=1;i= moviEnd) { - return RESULT_END_OF_INPUT; - } - input.readFully(bytes, 1, 7); final int chunkId = byteBuffer.getInt(); if (chunkId == ListBox.LIST) { - seekPosition.position = input.getPosition() + 8; - return RESULT_SEEK; + input.skipFully(8); + return RESULT_CONTINUE; } final int size = byteBuffer.getInt(); if (chunkId == JUNK) { - seekPosition.position = alignPosition(input.getPosition() + size); - return RESULT_SEEK; + input.skipFully(size); + alignInput(input); + return RESULT_CONTINUE; } final AviTrack aviTrack = getAviTrack(chunkId); if (aviTrack == null) { - seekPosition.position = alignPosition(input.getPosition() + size); + input.skipFully(size); + alignInput(input); w("Unknown tag=" + toString(chunkId) + " pos=" + (input.getPosition() - 8) + " size=" + size + " moviEnd=" + moviEnd); - return RESULT_SEEK; + return RESULT_CONTINUE; } if (aviTrack.newChunk(chunkId, size, input)) { - return alignPositionHolder(input, seekPosition); + alignInput(input); } else { chunkHandler = aviTrack; } } + if (input.getPosition() == input.getLength()) { + return C.RESULT_END_OF_INPUT; + } return RESULT_CONTINUE; } @@ -551,7 +551,7 @@ public class AviExtractor implements Extractor { public int read(@NonNull ExtractorInput input, @NonNull PositionHolder seekPosition) throws IOException { switch (state) { case STATE_READ_SAMPLES: - return readSamples(input, seekPosition); + return readSamples(input); case STATE_SEEK_START: state = STATE_READ_SAMPLES; seekPosition.position = moviOffset + 4; diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeaderBox.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeaderBox.java index 5486d43d7f..58a8859efa 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeaderBox.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/avi/StreamHeaderBox.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.avi; +import com.google.android.exoplayer2.C; import java.nio.ByteBuffer; /** @@ -46,7 +47,7 @@ public class StreamHeaderBox extends ResidentBox { } public long getDurationUs() { - return 1_000_000L * getScale() * getLength() / getRate(); + return C.MICROS_PER_SECOND * getScale() * getLength() / getRate(); } public int getSteamType() { diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviExtractorRoboTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviExtractorRoboTest.java index acfd3ae6b1..6e85eeaa5d 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviExtractorRoboTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviExtractorRoboTest.java @@ -112,4 +112,22 @@ public class AviExtractorRoboTest { Assert.assertEquals(aviTrack.getClock().durationUs, streamHeaderBox.getDurationUs()); } + @Test + public void readSamples_fragmentedChunk() throws IOException { + AviExtractor aviExtractor = AviExtractorTest.setupVideoAviExtractor(); + final AviTrack aviTrack = aviExtractor.getVideoTrack(); + final int size = 24 + 16; + final ByteBuffer byteBuffer = AviExtractor.allocate(size + 8); + byteBuffer.putInt(aviTrack.chunkId); + byteBuffer.putInt(size); + + final ExtractorInput chunk = new FakeExtractorInput.Builder().setData(byteBuffer.array()). + setSimulatePartialReads(true).build(); + Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(chunk, new PositionHolder())); + + Assert.assertEquals(Extractor.RESULT_END_OF_INPUT, aviExtractor.read(chunk, new PositionHolder())); + + final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput; + Assert.assertEquals(size, fakeTrackOutput.getSampleData(0).length); + } } 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 c6a4d7842d..ead5808eaa 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 @@ -303,28 +303,6 @@ public class AviExtractorTest { Assert.assertEquals(0, aviExtractor.aviSeekMap.seekOffset); } - @Test - public void alignPositionHolder_givenOddPosition() { - final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder(). - setData(new byte[4]).build(); - fakeExtractorInput.setPosition(1); - final PositionHolder positionHolder = new PositionHolder(); - final int result = AviExtractor.alignPositionHolder(fakeExtractorInput, positionHolder); - Assert.assertEquals(Extractor.RESULT_SEEK, result); - Assert.assertEquals(2, positionHolder.position); - } - - @Test - public void alignPositionHolder_givenEvenPosition() { - - final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder(). - setData(new byte[4]).build(); - fakeExtractorInput.setPosition(2); - final PositionHolder positionHolder = new PositionHolder(); - final int result = AviExtractor.alignPositionHolder(fakeExtractorInput, positionHolder); - Assert.assertEquals(Extractor.RESULT_CONTINUE, result); - } - @Test public void readHeaderList_givenBadHeader() throws IOException { final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(new byte[32]).build(); @@ -405,7 +383,7 @@ public class AviExtractorTest { Assert.assertEquals(64 * 1024 + 8, positionHolder.position); } - private AviExtractor setupVideoAviExtractor() { + static AviExtractor setupVideoAviExtractor() { final AviExtractor aviExtractor = new AviExtractor(); aviExtractor.setAviHeader(DataHelper.createAviHeaderBox()); final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput(); @@ -444,31 +422,27 @@ public class AviExtractorTest { final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()) .build(); - Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(input, new PositionHolder())); + Assert.assertEquals(Extractor.RESULT_END_OF_INPUT, 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 { + public void readSamples_givenLeadingZeros() throws IOException { AviExtractor aviExtractor = setupVideoAviExtractor(); final AviTrack aviTrack = aviExtractor.getVideoTrack(); - final int size = 24 + 16; - final ByteBuffer byteBuffer = AviExtractor.allocate(32); + final ByteBuffer byteBuffer = AviExtractor.allocate(48); + byteBuffer.position(16); byteBuffer.putInt(aviTrack.chunkId); - byteBuffer.putInt(size); + byteBuffer.putInt(24); - final ExtractorInput chunk0 = new FakeExtractorInput.Builder().setData(byteBuffer.array()) + final ExtractorInput input = 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())); + Assert.assertEquals(Extractor.RESULT_END_OF_INPUT, aviExtractor.read(input, new PositionHolder())); final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput; - Assert.assertEquals(size, fakeTrackOutput.getSampleData(0).length); + Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length); } @Test diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviSeekMapTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviSeekMapTest.java index 8328d9a3f5..91aded8755 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviSeekMapTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/avi/AviSeekMapTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.avi; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.SeekMap; import org.junit.Assert; import org.junit.Test; @@ -29,7 +30,7 @@ public class AviSeekMapTest { final AviTrack[] aviTracks = new AviTrack[]{DataHelper.getVideoAviTrack(secs), DataHelper.getAudioAviTrack(secs)}; - aviSeekMap.setFrames(position, 1_000_000L, aviTracks); + aviSeekMap.setFrames(position, C.MICROS_PER_SECOND, aviTracks); for (int i=0;i