mirror of
https://github.com/androidx/media.git
synced 2025-05-17 04:29:55 +08:00
Found out RESULT_SEEK is a bad thing. Greatly improved Extractor efficiency.
This commit is contained in:
parent
e9fcc967a3
commit
1528b8b5ee
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user