More AviExtractor tests
This commit is contained in:
parent
a17d36de12
commit
b520b26f0f
@ -28,7 +28,7 @@ public class AviExtractor implements Extractor {
|
||||
static final long MIN_KEY_FRAME_RATE_US = 2_000_000L;
|
||||
static final long UINT_MASK = 0xffffffffL;
|
||||
|
||||
static long getUInt(ByteBuffer byteBuffer) {
|
||||
static long getUInt(@NonNull ByteBuffer byteBuffer) {
|
||||
return byteBuffer.getInt() & UINT_MASK;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ public class AviExtractor implements Extractor {
|
||||
return position;
|
||||
}
|
||||
|
||||
static void alignInput(ExtractorInput input) throws IOException {
|
||||
static void alignInput(@NonNull ExtractorInput input) throws IOException {
|
||||
// This isn't documented anywhere, but most files are aligned to even bytes
|
||||
// and can have gaps of zeros
|
||||
if ((input.getPosition() & 1) == 1) {
|
||||
@ -57,15 +57,16 @@ public class AviExtractor implements Extractor {
|
||||
}
|
||||
}
|
||||
|
||||
static int checkAlign(final ExtractorInput input, PositionHolder seekPosition) {
|
||||
static int alignPositionHolder(@NonNull ExtractorInput input, @NonNull PositionHolder seekPosition) {
|
||||
final long position = input.getPosition();
|
||||
if ((position & 1) ==1) {
|
||||
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];
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
|
||||
@ -141,7 +142,7 @@ public class AviExtractor implements Extractor {
|
||||
* @param bytes Must be at least 20
|
||||
*/
|
||||
@Nullable
|
||||
private ByteBuffer getAviBuffer(ExtractorInput input, int bytes) throws IOException {
|
||||
static private ByteBuffer getAviBuffer(@NonNull ExtractorInput input, int bytes) throws IOException {
|
||||
if (input.getLength() < bytes) {
|
||||
return null;
|
||||
}
|
||||
@ -190,7 +191,7 @@ public class AviExtractor implements Extractor {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ListBox readHeaderList(ExtractorInput input) throws IOException {
|
||||
static ListBox readHeaderList(ExtractorInput input) throws IOException {
|
||||
final ByteBuffer byteBuffer = getAviBuffer(input, 20);
|
||||
if (byteBuffer == null) {
|
||||
return null;
|
||||
@ -245,7 +246,7 @@ public class AviExtractor implements Extractor {
|
||||
final VideoFormat videoFormat = streamFormat.getVideoFormat();
|
||||
final String mimeType = videoFormat.getMimeType();
|
||||
if (mimeType == null) {
|
||||
Log.w(TAG, "Unknown FourCC: " + toString(streamHeader.getFourCC()));
|
||||
Log.w(TAG, "Unknown FourCC: " + toString(videoFormat.getCompression()));
|
||||
return null;
|
||||
}
|
||||
final TrackOutput trackOutput = output.track(streamId, C.TRACK_TYPE_VIDEO);
|
||||
@ -478,11 +479,11 @@ public class AviExtractor implements Extractor {
|
||||
return null;
|
||||
}
|
||||
|
||||
int readSamples(ExtractorInput input, PositionHolder seekPosition) throws IOException {
|
||||
int readSamples(@NonNull ExtractorInput input, @NonNull PositionHolder seekPosition) throws IOException {
|
||||
if (chunkHandler != null) {
|
||||
if (chunkHandler.resume(input)) {
|
||||
chunkHandler = null;
|
||||
return checkAlign(input, seekPosition);
|
||||
return alignPositionHolder(input, seekPosition);
|
||||
}
|
||||
} else {
|
||||
ByteBuffer byteBuffer = allocate(8);
|
||||
@ -514,7 +515,7 @@ public class AviExtractor implements Extractor {
|
||||
return RESULT_SEEK;
|
||||
}
|
||||
if (aviTrack.newChunk(chunkId, size, input)) {
|
||||
return checkAlign(input, seekPosition);
|
||||
return alignPositionHolder(input, seekPosition);
|
||||
} else {
|
||||
chunkHandler = aviTrack;
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ package com.google.android.exoplayer2.extractor.avi;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class AviHeaderBox extends ResidentBox {
|
||||
private static final int AVIF_HASINDEX = 0x10;
|
||||
static final int LEN = 0x38;
|
||||
static final int AVIF_HASINDEX = 0x10;
|
||||
private static final int AVIF_MUSTUSEINDEX = 0x20;
|
||||
static final int AVIH = 'a' | ('v' << 8) | ('i' << 16) | ('h' << 24);
|
||||
|
||||
@ -46,15 +47,6 @@ public class AviHeaderBox extends ResidentBox {
|
||||
}
|
||||
|
||||
// 28 - dwSuggestedBufferSize
|
||||
// int getSuggestedBufferSize() {
|
||||
// return byteBuffer.getInt(28);
|
||||
// }
|
||||
//
|
||||
// int getWidth() {
|
||||
// return byteBuffer.getInt(32);
|
||||
// }
|
||||
//
|
||||
// int getHeight() {
|
||||
// return byteBuffer.getInt(36);
|
||||
// }
|
||||
// 32 - dwWidth
|
||||
// 36 - dwHeight
|
||||
}
|
||||
|
@ -19,9 +19,4 @@ public class Box {
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
boolean simpleAssert(final int expected) {
|
||||
return getType() == expected;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,13 +37,7 @@ public class StreamHeaderBox extends ResidentBox {
|
||||
public int getSteamType() {
|
||||
return byteBuffer.getInt(0);
|
||||
}
|
||||
/**
|
||||
* Only meaningful for video
|
||||
* @return FourCC
|
||||
*/
|
||||
public int getFourCC() {
|
||||
return byteBuffer.getInt(4);
|
||||
}
|
||||
//4 - fourCC
|
||||
//8 - dwFlags
|
||||
//12 - wPriority
|
||||
//14 - wLanguage
|
||||
|
@ -6,6 +6,7 @@ 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.util.Collections;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -35,4 +36,23 @@ public class AviExtractorRoboTest {
|
||||
Assert.assertEquals(MimeTypes.AUDIO_AAC, trackOutput.lastFormat.sampleMimeType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseStream_givenNoStreamHeader() {
|
||||
final AviExtractor aviExtractor = new AviExtractor();
|
||||
final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput();
|
||||
aviExtractor.init(fakeExtractorOutput);
|
||||
final ListBox streamList = new ListBox(128, AviExtractor.STRL, Collections.EMPTY_LIST);
|
||||
Assert.assertNull(aviExtractor.parseStream(streamList, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseStream_givenNoStreamFormat() throws IOException {
|
||||
final AviExtractor aviExtractor = new AviExtractor();
|
||||
final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput();
|
||||
aviExtractor.init(fakeExtractorOutput);
|
||||
final ListBox streamList = new ListBox(128, AviExtractor.STRL,
|
||||
Collections.singletonList(DataHelper.getVidsStreamHeader()));
|
||||
Assert.assertNull(aviExtractor.parseStream(streamList, 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import com.google.android.exoplayer2.extractor.Extractor;
|
||||
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;
|
||||
@ -9,6 +11,7 @@ import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AviExtractorTest {
|
||||
|
||||
@Test
|
||||
public void init_givenFakeExtractorOutput() {
|
||||
AviExtractor aviExtractor = new AviExtractor();
|
||||
@ -83,14 +86,7 @@ public class AviExtractorTest {
|
||||
|
||||
@Test
|
||||
public void peek_givenOnlyRiffAvi_ListHdrlAvih() {
|
||||
ByteBuffer byteBuffer = AviExtractor.allocate(AviExtractor.PEEK_BYTES);
|
||||
byteBuffer.putInt(AviExtractor.RIFF);
|
||||
byteBuffer.putInt(128);
|
||||
byteBuffer.putInt(AviExtractor.AVI_);
|
||||
byteBuffer.putInt(ListBox.LIST);
|
||||
byteBuffer.putInt(64);
|
||||
byteBuffer.putInt(ListBox.TYPE_HDRL);
|
||||
byteBuffer.putInt(AviHeaderBox.AVIH);
|
||||
final ByteBuffer byteBuffer = DataHelper.getAviHeader(AviExtractor.PEEK_BYTES, 128);
|
||||
Assert.assertTrue(sniff(byteBuffer));
|
||||
}
|
||||
|
||||
@ -118,6 +114,7 @@ public class AviExtractorTest {
|
||||
AviExtractor.alignInput(fakeExtractorInput);
|
||||
Assert.assertEquals(2, fakeExtractorInput.getPosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
public void alignInput_givenEvenPosition() throws IOException {
|
||||
@ -149,7 +146,8 @@ public class AviExtractorTest {
|
||||
Assert.assertEquals(1, AviExtractor.getStreamId('0' | ('1' << 8) | ('d' << 16) | ('c' << 24)));
|
||||
}
|
||||
|
||||
private void assertIdx1(AviSeekMap aviSeekMap, AviTrack videoTrack, int keyFrames, int keyFrameRate) {
|
||||
private void assertIdx1(AviSeekMap aviSeekMap, AviTrack videoTrack, int keyFrames,
|
||||
int keyFrameRate) {
|
||||
Assert.assertEquals(keyFrames, videoTrack.keyFrames.length);
|
||||
|
||||
final int framesPerKeyFrame = 24 * 3;
|
||||
@ -179,11 +177,13 @@ public class AviExtractorTest {
|
||||
final AviTrack audioTrack = DataHelper.getAudioAviTrack(secs);
|
||||
aviExtractor.setAviTracks(new AviTrack[]{videoTrack, audioTrack});
|
||||
|
||||
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder().setData(idx1.array()).build();
|
||||
aviExtractor.readIdx1(fakeExtractorInput, (int)fakeExtractorInput.getLength());
|
||||
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder()
|
||||
.setData(idx1.array()).build();
|
||||
aviExtractor.readIdx1(fakeExtractorInput, (int) fakeExtractorInput.getLength());
|
||||
final AviSeekMap aviSeekMap = aviExtractor.aviSeekMap;
|
||||
assertIdx1(aviSeekMap, videoTrack, keyFrames, keyFrameRate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readIdx1_givenNoVideo() throws IOException {
|
||||
final AviExtractor aviExtractor = new AviExtractor();
|
||||
@ -195,8 +195,9 @@ public class AviExtractorTest {
|
||||
final AviTrack audioTrack = DataHelper.getAudioAviTrack(secs);
|
||||
aviExtractor.setAviTracks(new AviTrack[]{audioTrack});
|
||||
|
||||
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder().setData(idx1.array()).build();
|
||||
aviExtractor.readIdx1(fakeExtractorInput, (int)fakeExtractorInput.getLength());
|
||||
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder()
|
||||
.setData(idx1.array()).build();
|
||||
aviExtractor.readIdx1(fakeExtractorInput, (int) fakeExtractorInput.getLength());
|
||||
Assert.assertTrue(fakeExtractorOutput.seekMap instanceof SeekMap.Unseekable);
|
||||
}
|
||||
|
||||
@ -222,7 +223,7 @@ public class AviExtractorTest {
|
||||
|
||||
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder().
|
||||
setData(junk.array()).build();
|
||||
aviExtractor.readIdx1(fakeExtractorInput, (int)fakeExtractorInput.getLength());
|
||||
aviExtractor.readIdx1(fakeExtractorInput, (int) fakeExtractorInput.getLength());
|
||||
|
||||
assertIdx1(aviExtractor.aviSeekMap, videoTrack, keyFrames, keyFrameRate);
|
||||
}
|
||||
@ -240,9 +241,59 @@ public class AviExtractorTest {
|
||||
|
||||
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder().
|
||||
setData(idx1.array()).build();
|
||||
aviExtractor.readIdx1(fakeExtractorInput, (int)fakeExtractorInput.getLength());
|
||||
aviExtractor.readIdx1(fakeExtractorInput, (int) fakeExtractorInput.getLength());
|
||||
|
||||
//We should be throttled to 2 key frame per second
|
||||
Assert.assertSame(AviTrack.ALL_KEY_FRAMES, videoTrack.keyFrames);
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
Assert.assertNull(AviExtractor.readHeaderList(input));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readHeaderList_givenNoHeaderList() throws IOException {
|
||||
final ByteBuffer byteBuffer = DataHelper.getAviHeader(88, 0x44);
|
||||
byteBuffer.putInt(0x14, AviExtractor.STRL); //Overwrite header list with stream list
|
||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().
|
||||
setData(byteBuffer.array()).build();
|
||||
Assert.assertNull(AviExtractor.readHeaderList(input));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readHeaderList_givenEmptyHeaderList() throws IOException {
|
||||
final ByteBuffer byteBuffer = DataHelper.getAviHeader(88, 0x44);
|
||||
byteBuffer.putInt(AviHeaderBox.LEN);
|
||||
byteBuffer.put(DataHelper.createHeader());
|
||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().
|
||||
setData(byteBuffer.array()).build();
|
||||
final ListBox listBox = AviExtractor.readHeaderList(input);
|
||||
Assert.assertEquals(1, listBox.getChildren().size());
|
||||
|
||||
Assert.assertTrue(listBox.getChildren().get(0) instanceof AviHeaderBox);
|
||||
}
|
||||
}
|
@ -35,18 +35,22 @@ public class DataHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static StreamHeaderBox getVidsStreamHeader() throws IOException {
|
||||
final byte[] buffer = getBytes("vids_stream_header.dump");
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
return new StreamHeaderBox(StreamHeaderBox.STRH, buffer.length, byteBuffer);
|
||||
public static StreamHeaderBox getStreamHeader(int type, int scale, int rate, int length) {
|
||||
final ByteBuffer byteBuffer = AviExtractor.allocate(0x40);
|
||||
byteBuffer.putInt(type);
|
||||
byteBuffer.putInt(20, scale);
|
||||
byteBuffer.putInt(24, rate);
|
||||
byteBuffer.putInt(32, length);
|
||||
byteBuffer.putInt(36, (type == StreamHeaderBox.VIDS ? 128 : 16) * 1024); //Suggested buffer size
|
||||
return new StreamHeaderBox(StreamHeaderBox.STRH, 0x40, byteBuffer);
|
||||
}
|
||||
|
||||
public static StreamHeaderBox getAudioStreamHeader() throws IOException {
|
||||
final byte[] buffer = getBytes("auds_stream_header.dump");
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
return new StreamHeaderBox(StreamHeaderBox.STRH, buffer.length, byteBuffer);
|
||||
public static StreamHeaderBox getVidsStreamHeader() {
|
||||
return getStreamHeader(StreamHeaderBox.VIDS, 1001, 24000, 9 * FPS);
|
||||
}
|
||||
|
||||
public static StreamHeaderBox getAudioStreamHeader() {
|
||||
return getStreamHeader(StreamHeaderBox.AUDS, 1, 44100, 9 * FPS);
|
||||
}
|
||||
|
||||
public static StreamFormatBox getAacStreamFormat() throws IOException {
|
||||
@ -154,4 +158,32 @@ public class DataHelper {
|
||||
}
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RIFF header up to AVI Header
|
||||
* @param bufferSize
|
||||
* @return
|
||||
*/
|
||||
public static ByteBuffer getAviHeader(int bufferSize, int headerListSize) {
|
||||
ByteBuffer byteBuffer = AviExtractor.allocate(bufferSize);
|
||||
byteBuffer.putInt(AviExtractor.RIFF);
|
||||
byteBuffer.putInt(128);
|
||||
byteBuffer.putInt(AviExtractor.AVI_);
|
||||
byteBuffer.putInt(ListBox.LIST);
|
||||
byteBuffer.putInt(headerListSize);
|
||||
byteBuffer.putInt(ListBox.TYPE_HDRL);
|
||||
byteBuffer.putInt(AviHeaderBox.AVIH);
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
public static ByteBuffer createHeader() {
|
||||
final ByteBuffer byteBuffer = ByteBuffer.allocate(AviHeaderBox.LEN);
|
||||
byteBuffer.putInt((int)VIDEO_US);
|
||||
byteBuffer.putLong(0); //skip 4+4
|
||||
byteBuffer.putInt(AviHeaderBox.AVIF_HASINDEX);
|
||||
byteBuffer.putInt(FPS * 5); //5 seconds
|
||||
byteBuffer.putInt(24, 2); // Number of streams
|
||||
byteBuffer.clear();
|
||||
return byteBuffer;
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,9 @@ public class StreamHeaderBoxTest {
|
||||
Assert.assertTrue(streamHeaderBox.isVideo());
|
||||
Assert.assertFalse(streamHeaderBox.isAudio());
|
||||
Assert.assertEquals(StreamHeaderBox.VIDS, streamHeaderBox.getSteamType());
|
||||
Assert.assertEquals(VideoFormat.XVID, streamHeaderBox.getFourCC());
|
||||
Assert.assertEquals(0, streamHeaderBox.getInitialFrames());
|
||||
Assert.assertEquals(FPS24, streamHeaderBox.getFrameRate(), 0.1);
|
||||
Assert.assertEquals(11805L, streamHeaderBox.getLength());
|
||||
Assert.assertEquals(0, streamHeaderBox.getSuggestedBufferSize());
|
||||
Assert.assertEquals(9 * DataHelper.FPS, streamHeaderBox.getLength());
|
||||
Assert.assertEquals(128 * 1024, streamHeaderBox.getSuggestedBufferSize());
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user