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
|
@NonNull
|
||||||
static ByteBuffer allocate(int bytes) {
|
static ByteBuffer allocate(int bytes) {
|
||||||
final byte[] buffer = new byte[bytes];
|
final byte[] buffer = new byte[bytes];
|
||||||
@ -503,47 +494,56 @@ public class AviExtractor implements Extractor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int readSamples(@NonNull ExtractorInput input, @NonNull PositionHolder seekPosition) throws IOException {
|
int readSamples(@NonNull ExtractorInput input) throws IOException {
|
||||||
if (chunkHandler != null) {
|
if (chunkHandler != null) {
|
||||||
if (chunkHandler.resume(input)) {
|
if (chunkHandler.resume(input)) {
|
||||||
chunkHandler = null;
|
chunkHandler = null;
|
||||||
return alignPositionHolder(input, seekPosition);
|
alignInput(input);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ByteBuffer byteBuffer = allocate(8);
|
final int toRead = 8;
|
||||||
|
ByteBuffer byteBuffer = allocate(toRead);
|
||||||
final byte[] bytes = byteBuffer.array();
|
final byte[] bytes = byteBuffer.array();
|
||||||
alignInput(input);
|
alignInput(input);
|
||||||
input.readFully(bytes, 0, 1);
|
input.readFully(bytes, 0, toRead);
|
||||||
|
//This is super inefficient, but should be rare
|
||||||
while (bytes[0] == 0) {
|
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();
|
final int chunkId = byteBuffer.getInt();
|
||||||
if (chunkId == ListBox.LIST) {
|
if (chunkId == ListBox.LIST) {
|
||||||
seekPosition.position = input.getPosition() + 8;
|
input.skipFully(8);
|
||||||
return RESULT_SEEK;
|
return RESULT_CONTINUE;
|
||||||
}
|
}
|
||||||
final int size = byteBuffer.getInt();
|
final int size = byteBuffer.getInt();
|
||||||
if (chunkId == JUNK) {
|
if (chunkId == JUNK) {
|
||||||
seekPosition.position = alignPosition(input.getPosition() + size);
|
input.skipFully(size);
|
||||||
return RESULT_SEEK;
|
alignInput(input);
|
||||||
|
return RESULT_CONTINUE;
|
||||||
}
|
}
|
||||||
final AviTrack aviTrack = getAviTrack(chunkId);
|
final AviTrack aviTrack = getAviTrack(chunkId);
|
||||||
if (aviTrack == null) {
|
if (aviTrack == null) {
|
||||||
seekPosition.position = alignPosition(input.getPosition() + size);
|
input.skipFully(size);
|
||||||
|
alignInput(input);
|
||||||
w("Unknown tag=" + toString(chunkId) + " pos=" + (input.getPosition() - 8)
|
w("Unknown tag=" + toString(chunkId) + " pos=" + (input.getPosition() - 8)
|
||||||
+ " size=" + size + " moviEnd=" + moviEnd);
|
+ " size=" + size + " moviEnd=" + moviEnd);
|
||||||
return RESULT_SEEK;
|
return RESULT_CONTINUE;
|
||||||
}
|
}
|
||||||
if (aviTrack.newChunk(chunkId, size, input)) {
|
if (aviTrack.newChunk(chunkId, size, input)) {
|
||||||
return alignPositionHolder(input, seekPosition);
|
alignInput(input);
|
||||||
} else {
|
} else {
|
||||||
chunkHandler = aviTrack;
|
chunkHandler = aviTrack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (input.getPosition() == input.getLength()) {
|
||||||
|
return C.RESULT_END_OF_INPUT;
|
||||||
|
}
|
||||||
return RESULT_CONTINUE;
|
return RESULT_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,7 +551,7 @@ public class AviExtractor implements Extractor {
|
|||||||
public int read(@NonNull ExtractorInput input, @NonNull PositionHolder seekPosition) throws IOException {
|
public int read(@NonNull ExtractorInput input, @NonNull PositionHolder seekPosition) throws IOException {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE_READ_SAMPLES:
|
case STATE_READ_SAMPLES:
|
||||||
return readSamples(input, seekPosition);
|
return readSamples(input);
|
||||||
case STATE_SEEK_START:
|
case STATE_SEEK_START:
|
||||||
state = STATE_READ_SAMPLES;
|
state = STATE_READ_SAMPLES;
|
||||||
seekPosition.position = moviOffset + 4;
|
seekPosition.position = moviOffset + 4;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.extractor.avi;
|
package com.google.android.exoplayer2.extractor.avi;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +47,7 @@ public class StreamHeaderBox extends ResidentBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getDurationUs() {
|
public long getDurationUs() {
|
||||||
return 1_000_000L * getScale() * getLength() / getRate();
|
return C.MICROS_PER_SECOND * getScale() * getLength() / getRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSteamType() {
|
public int getSteamType() {
|
||||||
|
@ -112,4 +112,22 @@ public class AviExtractorRoboTest {
|
|||||||
Assert.assertEquals(aviTrack.getClock().durationUs, streamHeaderBox.getDurationUs());
|
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);
|
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
|
@Test
|
||||||
public void readHeaderList_givenBadHeader() throws IOException {
|
public void readHeaderList_givenBadHeader() throws IOException {
|
||||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(new byte[32]).build();
|
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);
|
Assert.assertEquals(64 * 1024 + 8, positionHolder.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AviExtractor setupVideoAviExtractor() {
|
static AviExtractor setupVideoAviExtractor() {
|
||||||
final AviExtractor aviExtractor = new AviExtractor();
|
final AviExtractor aviExtractor = new AviExtractor();
|
||||||
aviExtractor.setAviHeader(DataHelper.createAviHeaderBox());
|
aviExtractor.setAviHeader(DataHelper.createAviHeaderBox());
|
||||||
final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput();
|
final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput();
|
||||||
@ -444,31 +422,27 @@ public class AviExtractorTest {
|
|||||||
|
|
||||||
final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array())
|
final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array())
|
||||||
.build();
|
.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;
|
final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput;
|
||||||
Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length);
|
Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readSamples_fragmentedChunk() throws IOException {
|
public void readSamples_givenLeadingZeros() throws IOException {
|
||||||
AviExtractor aviExtractor = setupVideoAviExtractor();
|
AviExtractor aviExtractor = setupVideoAviExtractor();
|
||||||
final AviTrack aviTrack = aviExtractor.getVideoTrack();
|
final AviTrack aviTrack = aviExtractor.getVideoTrack();
|
||||||
final int size = 24 + 16;
|
final ByteBuffer byteBuffer = AviExtractor.allocate(48);
|
||||||
final ByteBuffer byteBuffer = AviExtractor.allocate(32);
|
byteBuffer.position(16);
|
||||||
byteBuffer.putInt(aviTrack.chunkId);
|
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();
|
.build();
|
||||||
Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(chunk0, new PositionHolder()));
|
Assert.assertEquals(Extractor.RESULT_END_OF_INPUT, aviExtractor.read(input, 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;
|
final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput;
|
||||||
Assert.assertEquals(size, fakeTrackOutput.getSampleData(0).length);
|
Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.extractor.avi;
|
package com.google.android.exoplayer2.extractor.avi;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -29,7 +30,7 @@ public class AviSeekMapTest {
|
|||||||
final AviTrack[] aviTracks = new AviTrack[]{DataHelper.getVideoAviTrack(secs),
|
final AviTrack[] aviTracks = new AviTrack[]{DataHelper.getVideoAviTrack(secs),
|
||||||
DataHelper.getAudioAviTrack(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++) {
|
for (int i=0;i<aviTracks.length;i++) {
|
||||||
Assert.assertEquals(aviSeekMap.seekIndexes[i][1], aviTracks[i].getClock().getIndex());
|
Assert.assertEquals(aviSeekMap.seekIndexes[i][1], aviTracks[i].getClock().getIndex());
|
||||||
}
|
}
|
||||||
@ -41,7 +42,7 @@ public class AviSeekMapTest {
|
|||||||
final AviTrack[] aviTracks = new AviTrack[2];
|
final AviTrack[] aviTracks = new AviTrack[2];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aviSeekMap.setFrames(1L, 1_000_000L, aviTracks);
|
aviSeekMap.setFrames(1L, C.MICROS_PER_SECOND, aviTracks);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
//Intentionally blank
|
//Intentionally blank
|
||||||
|
@ -38,5 +38,6 @@ public class StreamHeaderBoxTest {
|
|||||||
Assert.assertEquals(FPS24, streamHeaderBox.getFrameRate(), 0.1);
|
Assert.assertEquals(FPS24, streamHeaderBox.getFrameRate(), 0.1);
|
||||||
Assert.assertEquals(9 * DataHelper.FPS, streamHeaderBox.getLength());
|
Assert.assertEquals(9 * DataHelper.FPS, streamHeaderBox.getLength());
|
||||||
Assert.assertEquals(128 * 1024, streamHeaderBox.getSuggestedBufferSize());
|
Assert.assertEquals(128 * 1024, streamHeaderBox.getSuggestedBufferSize());
|
||||||
|
Assert.assertTrue(streamHeaderBox.toString().startsWith("scale=" + streamHeaderBox.getScale()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user