Found out RESULT_SEEK is a bad thing. Greatly improved Extractor efficiency.

This commit is contained in:
Dustin 2022-02-02 14:35:52 -07:00
parent e9fcc967a3
commit 1528b8b5ee
6 changed files with 59 additions and 64 deletions

View File

@ -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<toRead;i++) {
bytes[i - 1] = bytes[i];
}
int read = input.read(bytes, toRead - 1, 1);
if (read == C.RESULT_END_OF_INPUT) {
return RESULT_END_OF_INPUT;
}
}
if (input.getPosition() >= 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;

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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

View File

@ -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<aviTracks.length;i++) {
Assert.assertEquals(aviSeekMap.seekIndexes[i][1], aviTracks[i].getClock().getIndex());
}
@ -41,7 +42,7 @@ public class AviSeekMapTest {
final AviTrack[] aviTracks = new AviTrack[2];
try {
aviSeekMap.setFrames(1L, 1_000_000L, aviTracks);
aviSeekMap.setFrames(1L, C.MICROS_PER_SECOND, aviTracks);
Assert.fail();
} catch (IllegalArgumentException e) {
//Intentionally blank

View File

@ -38,5 +38,6 @@ public class StreamHeaderBoxTest {
Assert.assertEquals(FPS24, streamHeaderBox.getFrameRate(), 0.1);
Assert.assertEquals(9 * DataHelper.FPS, streamHeaderBox.getLength());
Assert.assertEquals(128 * 1024, streamHeaderBox.getSuggestedBufferSize());
Assert.assertTrue(streamHeaderBox.toString().startsWith("scale=" + streamHeaderBox.getScale()));
}
}