Tests for Mp4vAviTrack and StreamHeaderBox

This commit is contained in:
Dustin 2022-01-22 20:41:39 -07:00
parent 3ce652ead2
commit c4cf876ddb
8 changed files with 156 additions and 22 deletions

View File

@ -19,7 +19,11 @@ android {
testCoverageEnabled = true
}
}
testOptions{
unitTests.all {
jvmArgs '-noverify'
}
}
sourceSets.test.assets.srcDir '../../testdata/src/test/assets/'
}

View File

@ -1,6 +1,8 @@
package com.google.android.exoplayer2.extractor.avi;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.TrackOutput;
@ -13,7 +15,7 @@ public class Mp4vAviTrack extends AviTrack {
private static final float[] ASPECT_RATIO = {0f, 1f, 12f/11f, 10f/11f, 16f/11f, 40f/33f};
private static final int Extended_PAR = 0xf;
private final Format.Builder formatBuilder;
private float pixelWidthHeightRatio = 1f;
float pixelWidthHeightRatio = 1f;
Mp4vAviTrack(int id, @NonNull StreamHeaderBox streamHeaderBox, @NonNull TrackOutput trackOutput,
@NonNull Format.Builder formatBuilder) {
@ -21,8 +23,8 @@ public class Mp4vAviTrack extends AviTrack {
this.formatBuilder = formatBuilder;
}
private void processLayerStart(byte[] peek, int offset) {
final ParsableNalUnitBitArray in = new ParsableNalUnitBitArray(peek, offset, peek.length);
@VisibleForTesting
void processLayerStart(@NonNull final ParsableNalUnitBitArray in) {
in.skipBit(); // random_accessible_vol
in.skipBits(8); // video_object_type_indication
boolean is_object_layer_identifier = in.readBit();
@ -44,23 +46,35 @@ public class Mp4vAviTrack extends AviTrack {
}
}
private void seekLayerStart(ExtractorInput input) throws IOException {
byte[] peek = new byte[128];
input.peekFully(peek, 0, peek.length);
@VisibleForTesting
@Nullable
static ParsableNalUnitBitArray findLayerStart(ExtractorInput input, final int peekSize)
throws IOException {
byte[] peek = new byte[peekSize];
input.peekFully(peek, 0, peekSize);
for (int i = 4;i<peek.length - 4;i++) {
if (peek[i] == 0 && peek[i+1] == 0 && peek[i+2] == 1 && (peek[i+3] & 0xf0) == LAYER_START_CODE) {
processLayerStart(peek, i+4);
break;
return new ParsableNalUnitBitArray(peek, i+4, peek.length);
}
}
return null;
}
@VisibleForTesting
static boolean isSequenceStart(ExtractorInput input) throws IOException {
final byte[] peek = new byte[4];
input.peekFully(peek, 0, peek.length);
return peek[0] == 0 && peek[1] == 0 && peek[2] == 1 && peek[3] == SEQUENCE_START_CODE;
}
@Override
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
final byte[] peek = new byte[4];
input.peekFully(peek, 0, peek.length);
if (peek[0] == 0 && peek[1] == 0 && peek[2] == 1 && peek[3] == SEQUENCE_START_CODE) {
seekLayerStart(input);
if (isSequenceStart(input)) {
// -4 because isSequenceStart peeks 4
final ParsableNalUnitBitArray layerStart = findLayerStart(input, Math.min(size - 4, 128));
if (layerStart != null) {
processLayerStart(layerStart);
}
}
return super.newChunk(tag, size, input);
}

View File

@ -11,10 +11,12 @@ public class StreamHeaderBox extends ResidentBox {
public static final int STRH = 's' | ('t' << 8) | ('r' << 16) | ('h' << 24);
//Audio Stream
private static final int AUDS = 'a' | ('u' << 8) | ('d' << 16) | ('s' << 24);
static final int AUDS = 'a' | ('u' << 8) | ('d' << 16) | ('s' << 24);
//Videos Stream
private static final int VIDS = 'v' | ('i' << 8) | ('d' << 16) | ('s' << 24);
static final int VIDS = 'v' | ('i' << 8) | ('d' << 16) | ('s' << 24);
static final int XVID = 'X' | ('V' << 8) | ('I' << 16) | ('D' << 24);
private static final SparseArray<String> STREAM_MAP = new SparseArray<>();
@ -30,7 +32,7 @@ public class StreamHeaderBox extends ResidentBox {
STREAM_MAP.put('A' | ('V' << 8) | ('C' << 16) | ('1' << 24), MimeTypes.VIDEO_H264);
STREAM_MAP.put('3' | ('V' << 8) | ('I' << 16) | ('D' << 24), mimeType);
STREAM_MAP.put('x' | ('v' << 8) | ('i' << 16) | ('d' << 24), mimeType);
STREAM_MAP.put('X' | ('V' << 8) | ('I' << 16) | ('D' << 24), mimeType);
STREAM_MAP.put(XVID, mimeType);
STREAM_MAP.put('D' | ('X' << 8) | ('5' << 16) | ('0' << 24), mimeType);
STREAM_MAP.put('m' | ('j' << 8) | ('p' << 16) | ('g' << 24), MimeTypes.VIDEO_JPEG);
@ -86,15 +88,17 @@ public class StreamHeaderBox extends ResidentBox {
public int getRate() {
return byteBuffer.getInt(24);
}
public int getStart() {
return byteBuffer.getInt(28);
}
// 28 - dwStart
// public int getStart() {
// return byteBuffer.getInt(28);
// }
public long getLength() {
return byteBuffer.getInt(32) & AviExtractor.UINT_MASK;
}
//36 - dwSuggestedBufferSize
//40 - dwQuality
public int getSampleSize() {
return byteBuffer.getInt(44);
}
//44 - dwSampleSize
// public int getSampleSize() {
// return byteBuffer.getInt(44);
// }
}

View File

@ -0,0 +1,32 @@
package com.google.android.exoplayer2.extractor.avi;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class DataHelper {
//Base path "\ExoPlayer\library\extractor\."
private static final File RELATIVE_PATH = new File("../../testdata/src/test/assets/extractordumps/avi/");
public static FakeExtractorInput getInput(final String fileName) throws IOException {
return new FakeExtractorInput.Builder().setData(getBytes(fileName)).build();
}
public static byte[] getBytes(final String fileName) throws IOException {
final File file = new File(RELATIVE_PATH, fileName);
try (FileInputStream in = new FileInputStream(file)) {
final byte[] buffer = new byte[in.available()];
in.read(buffer);
return buffer;
}
}
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);
}
}

View File

@ -0,0 +1,51 @@
package com.google.android.exoplayer2.extractor.avi;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.FakeTrackOutput;
import com.google.android.exoplayer2.util.ParsableNalUnitBitArray;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class Mp4vAviTrackTest {
@Test
public void isSequenceStart_givenSequence() throws IOException {
final FakeExtractorInput input = DataHelper.getInput("mp4v_sequence.dump");
Assert.assertTrue(Mp4vAviTrack.isSequenceStart(input));
}
@Test
public void findLayerStart_givenSequence() throws IOException {
final FakeExtractorInput input = DataHelper.getInput("mp4v_sequence.dump");
final ParsableNalUnitBitArray bitArray = Mp4vAviTrack.findLayerStart(input,
(int)input.getLength());
//Offset 0x12
Assert.assertEquals(8, bitArray.readBits(8));
}
@Test
public void findLayerStart_givenAllZeros() throws IOException {
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder().
setData(new byte[128]).build();
Assert.assertNull(Mp4vAviTrack.findLayerStart(fakeExtractorInput, 128));
}
@Test
public void pixelWidthHeightRatio_givenSequence() throws IOException {
final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false);
final Format.Builder formatBuilder = new Format.Builder();
final Mp4vAviTrack mp4vAviTrack = new Mp4vAviTrack(0, DataHelper.getVidsStreamHeader(),
fakeTrackOutput, formatBuilder);
final FakeExtractorInput input = DataHelper.getInput("mp4v_sequence.dump");
mp4vAviTrack.newChunk(0, (int)input.getLength(), input);
// final ParsableNalUnitBitArray bitArray = Mp4vAviTrack.findLayerStart(input,
// (int)input.getLength());
// mp4vAviTrack.processLayerStart(bitArray);
Assert.assertEquals(mp4vAviTrack.pixelWidthHeightRatio, 1.2121212, 0.01);
}
}

View File

@ -0,0 +1,29 @@
package com.google.android.exoplayer2.extractor.avi;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.util.MimeTypes;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class StreamHeaderBoxTest {
private static float FPS24 = 24000f/1001f;
private static final long US_SAMPLE24FPS = (long)(1_000_000L / FPS24);
@Test
public void getters_givenXvidStreamHeader() throws IOException {
final StreamHeaderBox streamHeaderBox = DataHelper.getVidsStreamHeader();
Assert.assertTrue(streamHeaderBox.isVideo());
Assert.assertFalse(streamHeaderBox.isAudio());
Assert.assertEquals(StreamHeaderBox.VIDS, streamHeaderBox.getSteamType());
Assert.assertEquals(StreamHeaderBox.XVID, streamHeaderBox.getFourCC());
Assert.assertEquals(0, streamHeaderBox.getInitialFrames());
Assert.assertEquals(FPS24, streamHeaderBox.getFrameRate(), 0.1);
Assert.assertEquals(US_SAMPLE24FPS, streamHeaderBox.getUsPerSample());
Assert.assertEquals(MimeTypes.VIDEO_MP4V, streamHeaderBox.getMimeType());
Assert.assertEquals(11805L, streamHeaderBox.getLength());
}
}

Binary file not shown.

Binary file not shown.