Make reusable fakes for extractor+track outputs.

Improve Mp4Extractor test.

Add support for Xiph lacing in Matroska files.

Add support for EBML lacing in Matroska files.

Handle the initial sticky intent for HDMI audio plug.
This commit is contained in:
Andrew Lewis 2015-06-23 17:57:41 +01:00
parent bc14e87cfb
commit e328546607
12 changed files with 694 additions and 592 deletions

View file

@ -68,7 +68,12 @@ public final class AudioCapabilitiesReceiver {
@TargetApi(21)
public void register() {
if (receiver != null) {
context.registerReceiver(receiver, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG));
Intent initialStickyIntent =
context.registerReceiver(receiver, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG));
if (initialStickyIntent != null) {
receiver.onReceive(context, initialStickyIntent);
return;
}
}
listener.onAudioCapabilitiesChanged(DEFAULT_AUDIO_CAPABILITIES);
@ -86,6 +91,10 @@ public final class AudioCapabilitiesReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (isInitialStickyBroadcast()) {
return;
}
String action = intent.getAction();
if (!action.equals(AudioManager.ACTION_HDMI_AUDIO_PLUG)) {
return;

View file

@ -126,7 +126,9 @@ public final class WebmExtractor implements Extractor {
private static final int ID_CUE_CLUSTER_POSITION = 0xF1;
private static final int LACING_NONE = 0;
private static final int LACING_XIPH = 1;
private static final int LACING_FIXED_SIZE = 2;
private static final int LACING_EBML = 3;
private final EbmlReader reader;
private final VarintReader varintReader;
@ -168,7 +170,7 @@ public final class WebmExtractor implements Extractor {
private long blockTimeUs;
private int blockLacingSampleIndex;
private int blockLacingSampleCount;
private int blockLacingSampleSize;
private int[] blockLacingSampleSizes;
private int blockTrackNumber;
private int blockTrackNumberLength;
private int blockFlags;
@ -600,8 +602,9 @@ public final class WebmExtractor implements Extractor {
int lacing = (scratch.data[2] & 0x06) >> 1;
if (lacing == LACING_NONE) {
blockLacingSampleCount = 1;
blockLacingSampleSize = contentSize - blockTrackNumberLength - 3;
} else if (lacing == LACING_FIXED_SIZE) {
blockLacingSampleSizes = ensureArrayCapacity(blockLacingSampleSizes, 1);
blockLacingSampleSizes[0] = contentSize - blockTrackNumberLength - 3;
} else {
if (id != ID_SIMPLE_BLOCK) {
throw new ParserException("Lacing only supported in SimpleBlocks.");
}
@ -609,10 +612,69 @@ public final class WebmExtractor implements Extractor {
// Read the sample count (1 byte).
readScratch(input, 4);
blockLacingSampleCount = (scratch.data[3] & 0xFF) + 1;
blockLacingSampleSize =
(contentSize - blockTrackNumberLength - 4) / blockLacingSampleCount;
} else {
throw new ParserException("Lacing mode not supported: " + lacing);
blockLacingSampleSizes =
ensureArrayCapacity(blockLacingSampleSizes, blockLacingSampleCount);
if (lacing == LACING_FIXED_SIZE) {
int blockLacingSampleSize =
(contentSize - blockTrackNumberLength - 4) / blockLacingSampleCount;
Arrays.fill(blockLacingSampleSizes, 0, blockLacingSampleCount, blockLacingSampleSize);
} else if (lacing == LACING_XIPH) {
int totalSamplesSize = 0;
int headerSize = 4;
for (int sampleIndex = 0; sampleIndex < blockLacingSampleCount - 1; sampleIndex++) {
blockLacingSampleSizes[sampleIndex] = 0;
int byteValue;
do {
readScratch(input, ++headerSize);
byteValue = scratch.data[headerSize - 1] & 0xFF;
blockLacingSampleSizes[sampleIndex] += byteValue;
} while (byteValue == 0xFF);
totalSamplesSize += blockLacingSampleSizes[sampleIndex];
}
blockLacingSampleSizes[blockLacingSampleCount - 1] =
contentSize - blockTrackNumberLength - headerSize - totalSamplesSize;
} else if (lacing == LACING_EBML) {
int totalSamplesSize = 0;
int headerSize = 4;
for (int sampleIndex = 0; sampleIndex < blockLacingSampleCount - 1; sampleIndex++) {
blockLacingSampleSizes[sampleIndex] = 0;
readScratch(input, ++headerSize);
if (scratch.data[headerSize - 1] == 0) {
throw new ParserException("No valid varint length mask found");
}
long readValue = 0;
for (int i = 0; i < 8; i++) {
int lengthMask = 1 << (7 - i);
if ((scratch.data[headerSize - 1] & lengthMask) != 0) {
int readPosition = headerSize - 1;
headerSize += i;
readScratch(input, headerSize);
readValue = (scratch.data[readPosition++] & 0xFF) & ~lengthMask;
while (readPosition < headerSize) {
readValue <<= 8;
readValue |= (scratch.data[readPosition++] & 0xFF);
}
// The first read value is the first size. Later values are signed offsets.
if (sampleIndex > 0) {
readValue -= (1L << 6 + i * 7) - 1;
}
break;
}
}
if (readValue < Integer.MIN_VALUE || readValue > Integer.MAX_VALUE) {
throw new ParserException("EBML lacing sample size out of range.");
}
int intReadValue = (int) readValue;
blockLacingSampleSizes[sampleIndex] = sampleIndex == 0
? intReadValue : blockLacingSampleSizes[sampleIndex - 1] + intReadValue;
totalSamplesSize += blockLacingSampleSizes[sampleIndex];
}
blockLacingSampleSizes[blockLacingSampleCount - 1] =
contentSize - blockTrackNumberLength - headerSize - totalSamplesSize;
} else {
// Lacing is always in the range 0--3.
throw new IllegalStateException("Unexpected lacing value: " + lacing);
}
}
int timecode = (scratch.data[0] << 8) | (scratch.data[1] & 0xFF);
@ -629,7 +691,8 @@ public final class WebmExtractor implements Extractor {
if (id == ID_SIMPLE_BLOCK) {
// For SimpleBlock, we have metadata for each sample here.
while (blockLacingSampleIndex < blockLacingSampleCount) {
writeSampleData(input, trackOutput, sampleTrackFormat, blockLacingSampleSize);
writeSampleData(input, trackOutput, sampleTrackFormat,
blockLacingSampleSizes[blockLacingSampleIndex]);
long sampleTimeUs = this.blockTimeUs
+ (blockLacingSampleIndex * sampleTrackFormat.defaultSampleDurationNs) / 1000;
outputSampleMetadata(trackOutput, sampleTimeUs);
@ -639,7 +702,7 @@ public final class WebmExtractor implements Extractor {
} else {
// For Block, we send the metadata at the end of the BlockGroup element since we'll know
// if the sample is a keyframe or not only at that point.
writeSampleData(input, trackOutput, sampleTrackFormat, blockLacingSampleSize);
writeSampleData(input, trackOutput, sampleTrackFormat, blockLacingSampleSizes[0]);
}
return;
@ -665,6 +728,10 @@ public final class WebmExtractor implements Extractor {
if (scratch.limit() >= requiredLength) {
return;
}
if (scratch.capacity() < requiredLength) {
scratch.reset(Arrays.copyOf(scratch.data, Math.max(scratch.data.length * 2, requiredLength)),
scratch.limit());
}
input.readFully(scratch.data, scratch.limit(), requiredLength - scratch.limit());
scratch.setLimit(requiredLength);
}
@ -814,7 +881,7 @@ public final class WebmExtractor implements Extractor {
return TimeUnit.NANOSECONDS.toMicros(unscaledTimecode * timecodeScale);
}
private boolean isCodecSupported(String codecId) {
private static boolean isCodecSupported(String codecId) {
return CODEC_ID_VP8.equals(codecId)
|| CODEC_ID_VP9.equals(codecId)
|| CODEC_ID_H264.equals(codecId)
@ -824,6 +891,21 @@ public final class WebmExtractor implements Extractor {
|| CODEC_ID_AC3.equals(codecId);
}
/**
* Returns an array that can store (at least) {@code length} elements, which will be either a new
* array or {@code array} if it's not null and large enough.
*/
private static int[] ensureArrayCapacity(int[] array, int length) {
if (array == null) {
return new int[length];
} else if (array.length >= length) {
return array;
} else {
// Double the size to avoid allocating constantly if the required length increases gradually.
return new int[Math.max(array.length * 2, length)];
}
}
/**
* Passes events through to the outer {@link WebmExtractor}.
*/

View file

@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer.util;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
@ -104,23 +103,6 @@ public final class NalUnitUtil {
}
}
/**
* Replaces length prefixes of NAL units in {@code buffer} with start code prefixes, within the
* {@code size} bytes preceding the buffer's position.
*/
public static void replaceLengthPrefixesWithAvcStartCodes(ByteBuffer buffer, int size) {
int sampleOffset = buffer.position() - size;
int position = sampleOffset;
while (position < sampleOffset + size) {
buffer.position(position);
int length = readUnsignedIntToInt(buffer);
buffer.position(position);
buffer.put(NAL_START_CODE);
position += length + 4;
}
buffer.position(sampleOffset + size);
}
/**
* Constructs and returns a NAL unit with a start code followed by the data in {@code atom}.
*/
@ -254,24 +236,6 @@ public final class NalUnitUtil {
return limit;
}
/**
* Reads an unsigned integer into an integer. This method is suitable for use when it can be
* assumed that the top bit will always be set to zero.
*
* @throws IllegalArgumentException If the top bit of the input data is set.
*/
private static int readUnsignedIntToInt(ByteBuffer data) {
int result = 0xFF & data.get();
for (int i = 1; i < 4; i++) {
result <<= 8;
result |= 0xFF & data.get();
}
if (result < 0) {
throw new IllegalArgumentException("Top bit not zero: " + result);
}
return result;
}
private NalUnitUtil() {
// Prevent instantiation.
}

View file

@ -37,7 +37,7 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTemplate;
import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTimelineElement;
import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
import com.google.android.exoplayer.dash.mpd.UrlTemplate;
import com.google.android.exoplayer.testutil.Util;
import com.google.android.exoplayer.testutil.TestUtil;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.util.FakeClock;
import com.google.android.exoplayer.util.ManifestFetcher;
@ -86,7 +86,7 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
@Override
public void setUp() throws Exception {
Util.setUpMockito(this);
TestUtil.setUpMockito(this);
}
public void testMaxVideoDimensions() {

View file

@ -24,7 +24,7 @@ import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.testutil.FakeDataSource;
import com.google.android.exoplayer.testutil.Util;
import com.google.android.exoplayer.testutil.TestUtil;
import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.ParsableByteArray;
@ -52,7 +52,7 @@ public class BufferingInputTest extends InstrumentationTestCase {
@Override
public void setUp() throws Exception {
Util.setUpMockito(this);
TestUtil.setUpMockito(this);
FakeDataSource.Builder builder = new FakeDataSource.Builder();
builder.appendReadData(STREAM_DATA);

View file

@ -17,35 +17,26 @@ package com.google.android.exoplayer.extractor.mp4;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.upstream.ByteArrayDataSource;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.testutil.FakeExtractorOutput;
import com.google.android.exoplayer.testutil.FakeTrackOutput;
import com.google.android.exoplayer.testutil.TestUtil;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import junit.framework.TestCase;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* Tests for {@link Mp4Extractor}.
*/
@TargetApi(16)
public class Mp4ExtractorTest extends TestCase {
public final class Mp4ExtractorTest extends TestCase {
/** String of hexadecimal bytes containing the video stsd payload from an AVC video. */
private static final byte[] VIDEO_STSD_PAYLOAD = getByteArray(
@ -94,159 +85,122 @@ public class Mp4ExtractorTest extends TestCase {
/** Video frame sizes in bytes, including a very large sample. */
private static final int[] SAMPLE_SIZES = {100, 20, 20, 44, 100, 1 * 1024 * 1024};
/** Indices of key-frames. */
private static final int[] SYNCHRONIZATION_SAMPLE_INDICES = {0, 4, 5};
private static final boolean[] SAMPLE_IS_SYNC = {true, false, false, false, true, true};
/** Indices of video frame chunk offsets. */
private static final int[] CHUNK_OFFSETS = {1080, 2000, 3000, 4000};
/** Numbers of video frames in each chunk. */
private static final int[] SAMPLES_IN_CHUNK = {2, 2, 1, 1};
/** The mdat box must be large enough to avoid reading chunk sample data out of bounds. */
private static final int MDAT_SIZE = 10 * 1024 * 1024;
/** Fake HTTP URI that can't be opened. */
private static final Uri FAKE_URI = Uri.parse("http://");
/** Empty byte array. */
private static final byte[] EMPTY = new byte[0];
private Mp4Extractor extractor;
private FakeExtractorOutput extractorOutput;
@Override
public void setUp() {
extractor = new Mp4Extractor();
extractorOutput = new FakeExtractorOutput();
extractor.init(extractorOutput);
}
@Override
public void tearDown() {
extractor = null;
extractorOutput = null;
}
public void testParsesValidMp4File() throws Exception {
// Given an extractor with an AVC/AAC file
Mp4ExtractorWrapper extractor =
prepareSampleExtractor(getFakeDataSource(true /* includeStss */, false /* mp4vFormat */));
TestUtil.consumeTestData(extractor,
getTestInputData(true /* includeStss */, false /* mp4vFormat */));
// The MIME type and metadata are set correctly.
assertEquals(MimeTypes.VIDEO_H264, extractor.mediaFormats[0].mimeType);
assertEquals(MimeTypes.AUDIO_AAC, extractor.mediaFormats[1].mimeType);
// The seek map is correct.
assertSeekMap(extractorOutput.seekMap, true);
assertEquals(VIDEO_WIDTH, extractor.selectedTrackMediaFormat.width);
assertEquals(VIDEO_HEIGHT, extractor.selectedTrackMediaFormat.height);
// The video and audio formats are set correctly.
assertEquals(2, extractorOutput.trackOutputs.size());
MediaFormat videoFormat = extractorOutput.trackOutputs.get(0).format;
MediaFormat audioFormat = extractorOutput.trackOutputs.get(1).format;
assertEquals(MimeTypes.VIDEO_H264, videoFormat.mimeType);
assertEquals(VIDEO_WIDTH, videoFormat.width);
assertEquals(VIDEO_HEIGHT, videoFormat.height);
assertEquals(MimeTypes.AUDIO_AAC, audioFormat.mimeType);
// The timestamps and sizes are set correctly.
FakeTrackOutput videoTrackOutput = extractorOutput.trackOutputs.get(0);
videoTrackOutput.assertSampleCount(SAMPLE_TIMESTAMPS.length);
for (int i = 0; i < SAMPLE_TIMESTAMPS.length; i++) {
byte[] sampleData = getOutputSampleData(i, true);
int sampleFlags = SAMPLE_IS_SYNC[i] ? C.SAMPLE_FLAG_SYNC : 0;
long sampleTimestampUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]);
videoTrackOutput.assertSample(i, sampleData, sampleTimestampUs, sampleFlags, null);
}
}
public void testParsesValidMp4FileWithoutStss() throws Exception {
TestUtil.consumeTestData(extractor,
getTestInputData(false /* includeStss */, false /* mp4vFormat */));
// The seek map is correct.
assertSeekMap(extractorOutput.seekMap, false);
// The timestamps and sizes are set correctly, and all samples are synchronization samples.
FakeTrackOutput videoTrackOutput = extractorOutput.trackOutputs.get(0);
videoTrackOutput.assertSampleCount(SAMPLE_TIMESTAMPS.length);
for (int i = 0; i < SAMPLE_TIMESTAMPS.length; i++) {
byte[] sampleData = getOutputSampleData(i, true);
int sampleFlags = C.SAMPLE_FLAG_SYNC;
long sampleTimestampUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]);
videoTrackOutput.assertSample(i, sampleData, sampleTimestampUs, sampleFlags, null);
}
}
public void testParsesValidMp4vFile() throws Exception {
// Given an extractor with an mp4v file
Mp4ExtractorWrapper extractor =
prepareSampleExtractor(getFakeDataSource(true /* includeStss */, true /* mp4vFormat */));
TestUtil.consumeTestData(extractor,
getTestInputData(true /* includeStss */, true /* mp4vFormat */));
// The MIME type and metadata are set correctly.
assertEquals(MimeTypes.VIDEO_MP4V, extractor.selectedTrackMediaFormat.mimeType);
assertEquals(VIDEO_MP4V_WIDTH, extractor.selectedTrackMediaFormat.width);
assertEquals(VIDEO_MP4V_HEIGHT, extractor.selectedTrackMediaFormat.height);
}
// The seek map is correct.
assertSeekMap(extractorOutput.seekMap, true);
public void testSampleTimestampsMatch() throws Exception {
// Given an extractor
Mp4ExtractorWrapper extractor =
prepareSampleExtractor(getFakeDataSource(true /* includeStss */, false /* mp4vFormat */));
// The video and audio formats are set correctly.
assertEquals(2, extractorOutput.trackOutputs.size());
MediaFormat videoFormat = extractorOutput.trackOutputs.get(0).format;
MediaFormat audioFormat = extractorOutput.trackOutputs.get(1).format;
assertEquals(MimeTypes.VIDEO_MP4V, videoFormat.mimeType);
assertEquals(VIDEO_MP4V_WIDTH, videoFormat.width);
assertEquals(VIDEO_MP4V_HEIGHT, videoFormat.height);
assertEquals(MimeTypes.AUDIO_AAC, audioFormat.mimeType);
// The timestamps are set correctly.
SampleHolder sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
// The timestamps and sizes are set correctly.
FakeTrackOutput videoTrackOutput = extractorOutput.trackOutputs.get(0);
videoTrackOutput.assertSampleCount(SAMPLE_TIMESTAMPS.length);
for (int i = 0; i < SAMPLE_TIMESTAMPS.length; i++) {
extractor.readSample(0, sampleHolder);
assertEquals(getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]), sampleHolder.timeUs);
byte[] sampleData = getOutputSampleData(i, false);
int sampleFlags = SAMPLE_IS_SYNC[i] ? C.SAMPLE_FLAG_SYNC : 0;
long sampleTimestampUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]);
videoTrackOutput.assertSample(i, sampleData, sampleTimestampUs, sampleFlags, null);
}
assertEquals(SampleSource.END_OF_STREAM, extractor.readSample(0, sampleHolder));
}
public void testSeekToStart() throws Exception {
// When seeking to the start
int timestampTimeUnits = SAMPLE_TIMESTAMPS[0];
long sampleTimestampUs =
getTimestampUsResultingFromSeek(getVideoTimestampUs(timestampTimeUnits));
// The timestamp is at the start.
assertEquals(getVideoTimestampUs(timestampTimeUnits), sampleTimestampUs);
}
public void testSeekToEnd() throws Exception {
// When seeking to the end
int timestampTimeUnits = SAMPLE_TIMESTAMPS[SAMPLE_TIMESTAMPS.length - 1];
long sampleTimestampUs =
getTimestampUsResultingFromSeek(getVideoTimestampUs(timestampTimeUnits));
// The timestamp is at the end.
assertEquals(getVideoTimestampUs(timestampTimeUnits), sampleTimestampUs);
}
public void testSeekToNearStart() throws Exception {
// When seeking to just after the start
int timestampTimeUnits = SAMPLE_TIMESTAMPS[0];
long sampleTimestampUs =
getTimestampUsResultingFromSeek(getVideoTimestampUs(timestampTimeUnits) + 1);
// The timestamp is at the start.
assertEquals(getVideoTimestampUs(timestampTimeUnits), sampleTimestampUs);
}
public void testSeekToBeforeLastSynchronizationSample() throws Exception {
// When seeking to just after the start
long sampleTimestampUs =
getTimestampUsResultingFromSeek(getVideoTimestampUs(SAMPLE_TIMESTAMPS[4]) - 1);
// The timestamp is at the start.
assertEquals(getVideoTimestampUs(SAMPLE_TIMESTAMPS[0]), sampleTimestampUs);
}
public void testAllSamplesAreSynchronizationSamplesWhenStssIsMissing() throws Exception {
// Given an extractor without an stss box
Mp4ExtractorWrapper extractor =
prepareSampleExtractor(getFakeDataSource(false /* includeStss */, false /* mp4vFormat */));
// All samples are synchronization samples.
SampleHolder sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
int sampleIndex = 0;
while (true) {
int result = extractor.readSample(0, sampleHolder);
if (result == SampleSource.SAMPLE_READ) {
assertTrue(sampleHolder.isSyncFrame());
sampleHolder.clearData();
sampleIndex++;
} else if (result == SampleSource.END_OF_STREAM) {
break;
private static void assertSeekMap(SeekMap seekMap, boolean haveStss) {
assertNotNull(seekMap);
int expectedSeekPosition = getSampleOffset(0);
for (int i = 0; i < SAMPLE_TIMESTAMPS.length; i++) {
// Seek to just before the current sample.
long seekPositionUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]) - 1;
assertEquals(expectedSeekPosition, seekMap.getPosition(seekPositionUs));
// If the current sample is a sync sample, the expected seek position will change.
if (SAMPLE_IS_SYNC[i] || !haveStss) {
expectedSeekPosition = getSampleOffset(i);
}
// Seek to the current sample.
seekPositionUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]);
assertEquals(expectedSeekPosition, seekMap.getPosition(seekPositionUs));
// Seek to just after the current sample.
seekPositionUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]) + 1;
assertEquals(expectedSeekPosition, seekMap.getPosition(seekPositionUs));
}
assertTrue(sampleIndex == SAMPLE_SIZES.length);
}
public void testReadAllSamplesSucceeds() throws Exception {
// Given an extractor
Mp4ExtractorWrapper extractor =
prepareSampleExtractor(getFakeDataSource(true /* includeStss */, false /* mp4vFormat */));
// The sample sizes are set correctly.
SampleHolder sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
int sampleIndex = 0;
while (true) {
int result = extractor.readSample(0, sampleHolder);
if (result == SampleSource.SAMPLE_READ) {
assertEquals(SAMPLE_SIZES[sampleIndex], sampleHolder.size);
sampleHolder.clearData();
sampleIndex++;
} else if (result == SampleSource.END_OF_STREAM) {
break;
}
}
assertEquals(SAMPLE_SIZES.length, sampleIndex);
}
/** Returns the sample time read after seeking to {@code timestampTimeUnits}. */
private static long getTimestampUsResultingFromSeek(long timestampTimeUnits) throws Exception {
Mp4ExtractorWrapper extractor =
prepareSampleExtractor(getFakeDataSource(true /* includeStss */, false /* mp4vFormat */));
extractor.seekTo(timestampTimeUnits);
SampleHolder sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
while (true) {
int result = extractor.readSample(0, sampleHolder);
if (result == SampleSource.SAMPLE_READ) {
return sampleHolder.timeUs;
} else if (result == SampleSource.END_OF_STREAM) {
return -1;
}
}
}
private static Mp4ExtractorWrapper prepareSampleExtractor(DataSource dataSource)
throws Exception {
Mp4ExtractorWrapper extractor = new Mp4ExtractorWrapper(dataSource);
extractor.prepare();
return extractor;
}
/** Returns a video timestamp in microseconds corresponding to {@code timeUnits}. */
@ -300,12 +254,20 @@ public class Mp4ExtractorTest extends TestCase {
}
private static byte[] getStss() {
byte[] result = new byte[4 + 4 + 4 * SYNCHRONIZATION_SAMPLE_INDICES.length];
int synchronizationSampleCount = 0;
for (int i = 0; i < SAMPLE_IS_SYNC.length; i++) {
if (SAMPLE_IS_SYNC[i]) {
synchronizationSampleCount++;
}
}
byte[] result = new byte[4 + 4 + 4 * synchronizationSampleCount];
ByteBuffer buffer = ByteBuffer.wrap(result);
buffer.putInt(0); // Version (skipped)
buffer.putInt(SYNCHRONIZATION_SAMPLE_INDICES.length);
for (int synchronizationSampleIndex : SYNCHRONIZATION_SAMPLE_INDICES) {
buffer.putInt(synchronizationSampleIndex + 1);
buffer.putInt(synchronizationSampleCount);
for (int i = 0; i < SAMPLE_IS_SYNC.length; i++) {
if (SAMPLE_IS_SYNC[i]) {
buffer.putInt(i + 1);
}
}
return result;
}
@ -342,23 +304,64 @@ public class Mp4ExtractorTest extends TestCase {
return result;
}
private static byte[] getMdat(int mdatOffset) {
private static byte[] getMdat(int mdatOffset, boolean isH264) {
ByteBuffer mdat = ByteBuffer.allocate(MDAT_SIZE);
int sampleIndex = 0;
for (int chunk = 0; chunk < CHUNK_OFFSETS.length; chunk++) {
int sampleOffset = CHUNK_OFFSETS[chunk];
mdat.position(CHUNK_OFFSETS[chunk] - mdatOffset);
for (int sample = 0; sample < SAMPLES_IN_CHUNK[chunk]; sample++) {
int sampleSize = SAMPLE_SIZES[sampleIndex++];
mdat.putInt(sampleOffset - mdatOffset, sampleSize);
sampleOffset += sampleSize;
mdat.put(getInputSampleData(sampleIndex++, isH264));
}
}
return mdat.array();
}
private static final DataSource getFakeDataSource(boolean includeStss, boolean mp4vFormat) {
return new ByteArrayDataSource(includeStss
? getTestMp4File(mp4vFormat) : getTestMp4FileWithoutSynchronizationData(mp4vFormat));
private static byte[] getInputSampleData(int index, boolean isH264) {
ByteBuffer sample = ByteBuffer.allocate(SAMPLE_SIZES[index]);
for (int i = 0; i < SAMPLE_SIZES[index]; i++) {
sample.put((byte) i);
}
if (isH264) {
// First four bytes should specify the remaining length of the sample. This assumes that the
// sample consists of a single length delimited NAL unit.
sample.position(0);
sample.putInt(SAMPLE_SIZES[index] - 4);
}
return sample.array();
}
private static byte[] getOutputSampleData(int index, boolean isH264) {
byte[] sampleData = getInputSampleData(index, isH264);
if (isH264) {
// The output sample should begin with a NAL start code.
sampleData[0] = 0;
sampleData[1] = 0;
sampleData[2] = 0;
sampleData[3] = 1;
}
return sampleData;
}
private static int getSampleOffset(int index) {
int sampleCount = 0;
int chunkIndex = 0;
int samplesLeftInChunk = SAMPLES_IN_CHUNK[chunkIndex];
int offsetInChunk = 0;
while (sampleCount < index) {
offsetInChunk += SAMPLE_SIZES[sampleCount++];
samplesLeftInChunk--;
if (samplesLeftInChunk == 0) {
chunkIndex++;
samplesLeftInChunk = SAMPLES_IN_CHUNK[chunkIndex];
offsetInChunk = 0;
}
}
return CHUNK_OFFSETS[chunkIndex] + offsetInChunk;
}
private static final byte[] getTestInputData(boolean includeStss, boolean mp4vFormat) {
return includeStss ? getTestMp4File(mp4vFormat)
: getTestMp4FileWithoutSynchronizationData(mp4vFormat);
}
/** Gets a valid MP4 file with audio/video tracks and synchronization data. */
@ -396,7 +399,7 @@ public class Mp4ExtractorTest extends TestCase {
atom(Atom.TYPE_stsc, getStsc()),
atom(Atom.TYPE_stsz, getStsz()),
atom(Atom.TYPE_stco, getStco())))))),
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 1048 : 1038)));
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 1048 : 1038, !mp4vFormat)));
}
/** Gets a valid MP4 file with audio/video tracks and without a synchronization table. */
@ -432,7 +435,7 @@ public class Mp4ExtractorTest extends TestCase {
atom(Atom.TYPE_stsc, getStsc()),
atom(Atom.TYPE_stsz, getStsz()),
atom(Atom.TYPE_stco, getStco())))))),
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 992 : 982)));
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 992 : 982, !mp4vFormat)));
}
private static Mp4Atom atom(int type, Mp4Atom... containedMp4Atoms) {
@ -452,7 +455,9 @@ public class Mp4ExtractorTest extends TestCase {
return result;
}
/** MP4 atom that can be serialized as a byte array. */
/**
* MP4 atom that can be serialized as a byte array.
*/
private static final class Mp4Atom {
public static byte[] serialize(Mp4Atom... atoms) {
@ -512,122 +517,4 @@ public class Mp4ExtractorTest extends TestCase {
}
/**
* Creates a {@link Mp4Extractor} on a separate thread with a looper, so that it can use a handler
* for loading, and provides blocking operations like {@link #seekTo} and {@link #readSample}.
*/
private static final class Mp4ExtractorWrapper extends Thread {
private static final int MSG_PREPARE = 0;
private static final int MSG_SEEK_TO = 1;
private static final int MSG_READ_SAMPLE = 2;
private final DataSource dataSource;
// Written by the handler's thread and read by the main thread.
public volatile MediaFormat[] mediaFormats;
public volatile MediaFormat selectedTrackMediaFormat;
private volatile Handler handler;
private volatile int readSampleResult;
private volatile Exception exception;
private volatile CountDownLatch pendingOperationLatch;
public Mp4ExtractorWrapper(DataSource dataSource) {
super("Mp4ExtractorTest");
this.dataSource = Assertions.checkNotNull(dataSource);
pendingOperationLatch = new CountDownLatch(1);
start();
}
public void prepare() throws Exception {
// Block until the handler has been created.
pendingOperationLatch.await();
// Block until the extractor has been prepared.
pendingOperationLatch = new CountDownLatch(1);
handler.sendEmptyMessage(MSG_PREPARE);
pendingOperationLatch.await();
if (exception != null) {
throw exception;
}
}
public void seekTo(long timestampUs) {
handler.obtainMessage(MSG_SEEK_TO, timestampUs).sendToTarget();
}
public int readSample(int trackIndex, SampleHolder sampleHolder) throws Exception {
// Block until the extractor has completed readSample.
pendingOperationLatch = new CountDownLatch(1);
handler.obtainMessage(MSG_READ_SAMPLE, trackIndex, 0, sampleHolder).sendToTarget();
pendingOperationLatch.await();
if (exception != null) {
throw exception;
}
return readSampleResult;
}
@SuppressLint("HandlerLeak")
@Override
public void run() {
final ExtractorSampleSource source = new ExtractorSampleSource(FAKE_URI, dataSource,
new Mp4Extractor(), 2 * 1024 * 1024);
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message message) {
try {
switch (message.what) {
case MSG_PREPARE:
if (!source.prepare(0)) {
sendEmptyMessage(MSG_PREPARE);
} else {
// Select the video track and get its metadata.
mediaFormats = new MediaFormat[source.getTrackCount()];
MediaFormatHolder mediaFormatHolder = new MediaFormatHolder();
for (int track = 0; track < source.getTrackCount(); track++) {
source.enable(track, 0);
source.readData(track, 0, mediaFormatHolder, null, false);
MediaFormat mediaFormat = mediaFormatHolder.format;
mediaFormats[track] = mediaFormat;
if (MimeTypes.isVideo(mediaFormat.mimeType)) {
selectedTrackMediaFormat = mediaFormat;
} else {
source.disable(track);
}
}
pendingOperationLatch.countDown();
}
break;
case MSG_SEEK_TO:
long timestampUs = (Long) message.obj;
source.seekToUs(timestampUs);
break;
case MSG_READ_SAMPLE:
int trackIndex = message.arg1;
SampleHolder sampleHolder = (SampleHolder) message.obj;
sampleHolder.clearData();
readSampleResult = source.readData(trackIndex, 0, null, sampleHolder, false);
if (readSampleResult == SampleSource.NOTHING_READ) {
Message.obtain(message).sendToTarget();
return;
}
pendingOperationLatch.countDown();
break;
}
} catch (Exception e) {
exception = e;
pendingOperationLatch.countDown();
}
}
};
// Unblock waiting for the handler.
pendingOperationLatch.countDown();
Looper.loop();
}
}
}

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer.extractor.webm;
import com.google.android.exoplayer.testutil.TestUtil;
import com.google.android.exoplayer.util.Assertions;
import java.nio.ByteBuffer;
@ -47,28 +48,6 @@ import java.util.List;
}
public static byte[] createByteArray(int... intArray) {
byte[] byteArray = new byte[intArray.length];
for (int i = 0; i < byteArray.length; i++) {
byteArray[i] = (byte) intArray[i];
}
return byteArray;
}
public static byte[] joinByteArrays(byte[]... byteArrays) {
int length = 0;
for (byte[] byteArray : byteArrays) {
length += byteArray.length;
}
byte[] joined = new byte[length];
length = 0;
for (byte[] byteArray : byteArrays) {
System.arraycopy(byteArray, 0, joined, length, byteArray.length);
length += byteArray.length;
}
return joined;
}
public static final byte[] TEST_ENCRYPTION_KEY_ID = { 0x00, 0x01, 0x02, 0x03 };
public static final byte[] TEST_INITIALIZATION_VECTOR = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
@ -163,6 +142,14 @@ import java.util.List;
return this;
}
public StreamBuilder addSimpleBlockMediaWithXiphLacing(int trackNumber, int clusterTimecode,
int blockTimecode, byte[] data, int... lacingFrameSizes) {
EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode,
0x80 /* flags = keyframe */, false, true, data, lacingFrameSizes);
mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement));
return this;
}
public StreamBuilder addBlockMedia(int trackNumber, int clusterTimecode, int blockTimecode,
boolean keyframe, boolean invisible, byte[] data) {
byte flags = (byte) (invisible ? 0x08 : 0x00);
@ -309,32 +296,66 @@ import java.util.List;
byte[] simpleBlockBytes;
if (lacingFrameCount > 1) {
flags |= 0x04; // Fixed-size lacing
simpleBlockBytes = createByteArray(
simpleBlockBytes = TestUtil.createByteArray(
0x40, trackNumberBytes[3], // Track number size=2
timeBytes[2], timeBytes[3], flags, lacingFrameCount - 1); // Timecode, flags and lacing.
} else {
simpleBlockBytes = createByteArray(
simpleBlockBytes = TestUtil.createByteArray(
0x40, trackNumberBytes[3], // Track number size=2
timeBytes[2], timeBytes[3], flags); // Timecode and flags
}
if (encrypted) {
simpleBlockBytes = joinByteArrays(
simpleBlockBytes, createByteArray(validSignalByte ? 0x01 : 0x80),
simpleBlockBytes = TestUtil.joinByteArrays(
simpleBlockBytes, TestUtil.createByteArray(validSignalByte ? 0x01 : 0x80),
Arrays.copyOfRange(TEST_INITIALIZATION_VECTOR, 0, 8));
}
return element(0xA3, // SimpleBlock
joinByteArrays(simpleBlockBytes, data));
TestUtil.joinByteArrays(simpleBlockBytes, data));
}
private static EbmlElement createSimpleBlock(int trackNumber, int timecode, int flags,
boolean encrypted, boolean validSignalByte, byte[] data, int... xiphLacingSampleSizes) {
byte[] trackNumberBytes = getIntegerBytes(trackNumber);
byte[] timeBytes = getIntegerBytes(timecode);
byte[] simpleBlockBytes;
flags |= 0x02; // Xiph lacing
simpleBlockBytes = TestUtil.createByteArray(
0x40, trackNumberBytes[3], // Track number size=2
timeBytes[2], timeBytes[3], // Timecode
flags, xiphLacingSampleSizes.length - 1); // Flags and lacing.
int lacingBufferSize = 0;
for (int sampleIndex = 0; sampleIndex < xiphLacingSampleSizes.length - 1; sampleIndex++) {
lacingBufferSize += (xiphLacingSampleSizes[sampleIndex] + 254) / 255;
}
ByteBuffer lacingBytes = ByteBuffer.allocate(lacingBufferSize);
for (int sampleIndex = 0; sampleIndex < xiphLacingSampleSizes.length - 1; sampleIndex++) {
int sampleSize = xiphLacingSampleSizes[sampleIndex];
while (sampleSize > 255) {
sampleSize -= 255;
lacingBytes.put((byte) 0xFF);
}
lacingBytes.put((byte) sampleSize);
}
simpleBlockBytes = TestUtil.joinByteArrays(simpleBlockBytes, lacingBytes.array());
if (encrypted) {
simpleBlockBytes = TestUtil.joinByteArrays(
simpleBlockBytes, TestUtil.createByteArray(validSignalByte ? 0x01 : 0x80),
Arrays.copyOfRange(TEST_INITIALIZATION_VECTOR, 0, 8));
}
return element(0xA3, // SimpleBlock
TestUtil.joinByteArrays(simpleBlockBytes, data));
}
private static EbmlElement createBlock(int trackNumber, int timecode, boolean keyframe, int flags,
byte[] data) {
byte[] trackNumberBytes = getIntegerBytes(trackNumber);
byte[] timeBytes = getIntegerBytes(timecode);
byte[] blockBytes = createByteArray(
byte[] blockBytes = TestUtil.createByteArray(
0x40, trackNumberBytes[3], // Track number size=2
timeBytes[2], timeBytes[3], flags); // Timecode and flags
EbmlElement block = element(0xA1, // Block
joinByteArrays(blockBytes, data));
TestUtil.joinByteArrays(blockBytes, data));
EbmlElement referenceBlock = keyframe ? empty() : element(0xFB, (byte) 0x00); // ReferenceBlock
return element(0xA0, // BlockGroup
referenceBlock,
@ -342,7 +363,7 @@ import java.util.List;
}
private static byte[] getIntegerBytes(int value) {
return createByteArray(
return TestUtil.createByteArray(
(value & 0xFF000000) >> 24,
(value & 0x00FF0000) >> 16,
(value & 0x0000FF00) >> 8,

View file

@ -22,34 +22,23 @@ import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.ChunkIndex;
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.ExtractorOutput;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.extractor.webm.StreamBuilder.ContentEncodingSettings;
import com.google.android.exoplayer.testutil.FakeDataSource;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.testutil.FakeExtractorOutput;
import com.google.android.exoplayer.testutil.FakeTrackOutput;
import com.google.android.exoplayer.testutil.TestUtil;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParsableByteArray;
import android.net.Uri;
import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.UUID;
/**
* Tests for {@link WebmExtractor}.
*/
public class WebmExtractorTest extends InstrumentationTestCase {
public final class WebmExtractorTest extends InstrumentationTestCase {
private static final int DEFAULT_TIMECODE_SCALE = 1000000;
private static final long TEST_DURATION_US = 9920000L;
@ -64,7 +53,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
private static final int TEST_VORBIS_BOOKS_SIZE = 4140;
private static final byte[] TEST_OPUS_CODEC_PRIVATE = new byte[] {0, 0};
private static final int TEST_DEFAULT_DURATION_NS = 33 * 1000 * 1000;
private static final byte[] TEST_H264_CODEC_PRIVATE = StreamBuilder.createByteArray(0x01, 0x4D,
private static final byte[] TEST_H264_CODEC_PRIVATE = TestUtil.createByteArray(0x01, 0x4D,
0x40, 0x1E, 0xFF, 0xE1, 0x00, 0x17, 0x67, 0x4D, 0x40, 0x1E, 0xE8, 0x80, 0x50, 0x17, 0xFC,
0xB8, 0x08, 0x80, 0x00, 0x01, 0xF4, 0x80, 0x00, 0x75, 0x30, 0x07, 0x8B, 0x16, 0x89, 0x01,
0x00, 0x04, 0x68, 0xEB, 0xEF, 0x20);
@ -75,16 +64,12 @@ public class WebmExtractorTest extends InstrumentationTestCase {
private static final String MATROSKA_DOC_TYPE = "matroska";
private WebmExtractor extractor;
private TestExtractorOutput extractorOutput;
private TestTrackOutput audioOutput;
private TestTrackOutput videoOutput;
private FakeExtractorOutput extractorOutput;
@Override
public void setUp() {
extractor = new WebmExtractor();
extractorOutput = new TestExtractorOutput();
audioOutput = new TestTrackOutput();
videoOutput = new TestTrackOutput();
extractorOutput = new FakeExtractorOutput();
extractor.init(extractorOutput);
}
@ -92,8 +77,6 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public void tearDown() {
extractor = null;
extractorOutput = null;
audioOutput = null;
videoOutput = null;
}
public void testReadInitializationSegment() throws IOException, InterruptedException {
@ -103,7 +86,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
@ -117,7 +100,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_OPUS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
@ -130,7 +113,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVorbisTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, getVorbisCodecPrivate())
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_VORBIS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
@ -143,7 +126,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addH264Track(TEST_WIDTH, TEST_HEIGHT, TEST_H264_CODEC_PRIVATE)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertH264VideoFormat();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
@ -158,7 +141,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat();
@ -176,7 +159,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
// Even though the input stream has 3 tracks, only 2 of them are supported and will be reported.
assertEquals(2, extractorOutput.numberOfTracks);
@ -196,7 +179,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
// Even though the input stream has 4 supported tracks, only the first video and audio track
// will be reported.
@ -214,7 +197,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
@ -231,7 +214,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(3);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertIndex(
@ -247,7 +230,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(3);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertIndex(
@ -263,7 +246,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(0);
try {
consume(data);
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("Invalid/missing cue points", exception.getMessage());
@ -278,7 +261,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.build(1);
// No exception is thrown.
consume(data);
TestUtil.consumeTestData(extractor, data);
}
public void testAcceptsMatroskaDocType() throws IOException, InterruptedException {
@ -289,7 +272,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.build(1);
// No exception is thrown.
consume(data);
TestUtil.consumeTestData(extractor, data);
}
public void testPrepareInvalidDocType() throws IOException, InterruptedException {
@ -299,7 +282,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(1);
try {
consume(data);
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("DocType webB not supported", exception.getMessage());
@ -314,7 +297,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
consume(data);
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("ContentEncodingOrder 1 not supported", exception.getMessage());
@ -329,7 +312,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
consume(data);
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("ContentEncodingScope 0 not supported", exception.getMessage());
@ -344,7 +327,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
consume(data);
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("ContentEncodingType 0 not supported", exception.getMessage());
@ -359,7 +342,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
consume(data);
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("ContentEncAlgo 4 not supported", exception.getMessage());
@ -374,7 +357,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1);
try {
consume(data);
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("AESSettingsCipherMode 0 not supported", exception.getMessage());
@ -391,10 +374,10 @@ public class WebmExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertSample(media, 0, true, false, null, videoOutput);
assertSample(0, media, 0, true, false, null, getVideoOutput());
}
public void testReadTwoTrackSamples() throws IOException, InterruptedException {
@ -411,13 +394,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat();
assertAudioFormat(MimeTypes.AUDIO_OPUS);
assertSample(media, 0, true, false, null, videoOutput);
assertSample(media, 0, true, false, null, audioOutput);
assertSample(0, media, 0, true, false, null, getVideoOutput());
assertSample(0, media, 0, true, false, null, getAudioOutput());
}
public void testReadTwoTrackSamplesWithSkippedTrack() throws IOException, InterruptedException {
@ -437,13 +420,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat();
assertAudioFormat(MimeTypes.AUDIO_OPUS);
assertSample(media, 0, true, false, null, videoOutput);
assertSample(media, 0, true, false, null, audioOutput);
assertSample(0, media, 0, true, false, null, getVideoOutput());
assertSample(0, media, 0, true, false, null, getAudioOutput());
}
public void testReadBlock() throws IOException, InterruptedException {
@ -457,10 +440,10 @@ public class WebmExtractorTest extends InstrumentationTestCase {
true /* keyframe */, false /* invisible */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_OPUS);
assertSample(media, 0, true, false, null, audioOutput);
assertSample(0, media, 0, true, false, null, getAudioOutput());
}
public void testReadBlockNonKeyframe() throws IOException, InterruptedException {
@ -473,10 +456,10 @@ public class WebmExtractorTest extends InstrumentationTestCase {
false /* keyframe */, false /* invisible */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertSample(media, 0, false, false, null, videoOutput);
assertSample(0, media, 0, false, false, null, getVideoOutput());
}
public void testReadEncryptedFrame() throws IOException, InterruptedException {
@ -491,10 +474,10 @@ public class WebmExtractorTest extends InstrumentationTestCase {
true /* validSignalByte */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertSample(media, 0, true, false, TEST_ENCRYPTION_KEY_ID, videoOutput);
assertSample(0, media, 0, true, false, TEST_ENCRYPTION_KEY_ID, getVideoOutput());
}
public void testReadEncryptedFrameWithInvalidSignalByte()
@ -511,7 +494,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
.build(1);
try {
consume(data);
TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) {
assertEquals("Extension bit is set in signal byte", exception.getMessage());
@ -528,10 +511,10 @@ public class WebmExtractorTest extends InstrumentationTestCase {
false /* keyframe */, true /* invisible */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertSample(media, 25000, false, true, null, videoOutput);
assertSample(0, media, 25000, false, true, null, getVideoOutput());
}
public void testReadSampleCustomTimescale() throws IOException, InterruptedException {
@ -544,10 +527,10 @@ public class WebmExtractorTest extends InstrumentationTestCase {
false /* keyframe */, false /* invisible */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertSample(media, 25, false, false, null, videoOutput);
assertSample(0, media, 25, false, false, null, getVideoOutput());
}
public void testReadSampleNegativeSimpleBlockTimecode() throws IOException, InterruptedException {
@ -560,13 +543,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
true /* keyframe */, true /* invisible */, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat();
assertSample(media, 1000, true, true, null, videoOutput);
assertSample(0, media, 1000, true, true, null, getVideoOutput());
}
public void testReadSampleWithLacing() throws IOException, InterruptedException {
public void testReadSampleWithFixedSizeLacing() throws IOException, InterruptedException {
byte[] media = createFrameData(100);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
@ -577,48 +560,64 @@ public class WebmExtractorTest extends InstrumentationTestCase {
0 /* blockTimecode */, 20, media)
.build(1);
consume(data);
TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_OPUS);
for (int i = 0; i < 20; i++) {
long expectedTimeUs = i * TEST_DEFAULT_DURATION_NS / 1000;
assertSample(Arrays.copyOfRange(media, i * 5, i * 5 + 5), expectedTimeUs, true, false, null,
audioOutput);
assertSample(i, Arrays.copyOfRange(media, i * 5, i * 5 + 5), expectedTimeUs, true, false,
null, getAudioOutput());
}
}
private void consume(byte[] data) throws IOException, InterruptedException {
ExtractorInput input = createTestInput(data);
int readResult = Extractor.RESULT_CONTINUE;
while (readResult == Extractor.RESULT_CONTINUE) {
readResult = extractor.read(input, null);
}
assertEquals(Extractor.RESULT_END_OF_INPUT, readResult);
public void testReadSampleWithXiphLacing() throws IOException, InterruptedException {
byte[] media = createFrameData(300);
byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US)
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, TEST_SEEK_PRE_ROLL,
TEST_OPUS_CODEC_PRIVATE, TEST_DEFAULT_DURATION_NS)
.addSimpleBlockMediaWithXiphLacing(2 /* trackNumber */, 0 /* clusterTimecode */,
0 /* blockTimecode */, media, 256, 1, 243)
.build(1);
TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_OPUS);
assertSample(0, Arrays.copyOfRange(media, 0, 256), 0 * TEST_DEFAULT_DURATION_NS / 1000, true,
false, null, getAudioOutput());
assertSample(1, Arrays.copyOfRange(media, 256, 257), 1 * TEST_DEFAULT_DURATION_NS / 1000, true,
false, null, getAudioOutput());
assertSample(2, Arrays.copyOfRange(media, 257, 300), 2 * TEST_DEFAULT_DURATION_NS / 1000, true,
false, null, getAudioOutput());
}
private static ExtractorInput createTestInput(byte[] data) throws IOException {
DataSource dataSource = new FakeDataSource.Builder().appendReadData(data).build();
dataSource.open(new DataSpec(Uri.parse("http://www.google.com")));
ExtractorInput input = new DefaultExtractorInput(dataSource, 0, C.LENGTH_UNBOUNDED);
return input;
private FakeTrackOutput getVideoOutput() {
// In the sample data the video track has id 1.
return extractorOutput.trackOutputs.get(1);
}
private FakeTrackOutput getAudioOutput() {
// In the sample data the video track has id 2.
return extractorOutput.trackOutputs.get(2);
}
private void assertVp9VideoFormat() {
MediaFormat format = videoOutput.format;
MediaFormat format = getVideoOutput().format;
assertEquals(TEST_WIDTH, format.width);
assertEquals(TEST_HEIGHT, format.height);
assertEquals(MimeTypes.VIDEO_VP9, format.mimeType);
}
private void assertH264VideoFormat() {
MediaFormat format = videoOutput.format;
MediaFormat format = getVideoOutput().format;
assertEquals(TEST_WIDTH, format.width);
assertEquals(TEST_HEIGHT, format.height);
assertEquals(MimeTypes.VIDEO_H264, format.mimeType);
}
private void assertAudioFormat(String expectedMimeType) {
MediaFormat format = audioOutput.format;
MediaFormat format = getAudioOutput().format;
assertEquals(TEST_CHANNEL_COUNT, format.channelCount);
assertEquals(TEST_SAMPLE_RATE, format.sampleRate);
assertEquals(expectedMimeType, format.mimeType);
@ -646,10 +645,10 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
}
private void assertSample(byte[] expectedMedia, long timeUs, boolean keyframe, boolean invisible,
byte[] encryptionKey, TestTrackOutput output) {
private void assertSample(int index, byte[] expectedMedia, long timeUs, boolean keyframe,
boolean invisible, byte[] encryptionKey, FakeTrackOutput output) {
if (encryptionKey != null) {
expectedMedia = StreamBuilder.joinByteArrays(
expectedMedia = TestUtil.joinByteArrays(
new byte[] {(byte) StreamBuilder.TEST_INITIALIZATION_VECTOR.length},
StreamBuilder.TEST_INITIALIZATION_VECTOR, expectedMedia);
}
@ -657,7 +656,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
flags |= keyframe ? C.SAMPLE_FLAG_SYNC : 0;
flags |= invisible ? C.SAMPLE_FLAG_DECODE_ONLY : 0;
flags |= encryptionKey != null ? C.SAMPLE_FLAG_ENCRYPTED : 0;
output.assertNextSample(expectedMedia, timeUs, flags, encryptionKey);
output.assertSample(index, expectedMedia, timeUs, flags, encryptionKey);
}
private byte[] getVorbisCodecPrivate() {
@ -694,103 +693,4 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
/** Implements {@link ExtractorOutput} for test purposes. */
public class TestExtractorOutput implements ExtractorOutput {
public boolean tracksEnded;
public SeekMap seekMap;
public DrmInitData drmInitData;
public int numberOfTracks;
@Override
public TrackOutput track(int trackId) {
numberOfTracks++;
// In the test samples, track number 1 is always video and track number 2 is always audio.
return (trackId == 1) ? videoOutput : audioOutput;
}
@Override
public void endTracks() {
tracksEnded = true;
}
@Override
public void seekMap(SeekMap seekMap) {
this.seekMap = seekMap;
}
@Override
public void drmInitData(DrmInitData drmInitData) {
this.drmInitData = drmInitData;
}
}
/** Implements {@link TrackOutput} for test purposes. */
public static class TestTrackOutput implements TrackOutput {
private final Queue<byte[]> sampleData;
private final Queue<Long> sampleTimesUs;
private final Queue<Integer> sampleFlags;
private final Queue<Integer> sampleSizes;
private final Queue<byte[]> sampleEncryptionKeys;
public MediaFormat format;
private byte[] currentSampleData;
public TestTrackOutput() {
sampleData = new LinkedList<>();
sampleTimesUs = new LinkedList<>();
sampleFlags = new LinkedList<>();
sampleSizes = new LinkedList<>();
sampleEncryptionKeys = new LinkedList<>();
}
@Override
public void format(MediaFormat format) {
this.format = format;
}
@Override
public int sampleData(ExtractorInput input, int length) throws IOException,
InterruptedException {
byte[] newData = new byte[length];
input.readFully(newData, 0, length);
currentSampleData = currentSampleData == null
? newData : StreamBuilder.joinByteArrays(currentSampleData, newData);
return length;
}
@Override
public void sampleData(ParsableByteArray data, int length) {
byte[] newData = new byte[length];
data.readBytes(newData, 0, length);
currentSampleData = currentSampleData == null
? newData : StreamBuilder.joinByteArrays(currentSampleData, newData);
}
@Override
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
sampleData.add(currentSampleData);
sampleTimesUs.add(timeUs);
sampleFlags.add(flags);
sampleSizes.add(size);
sampleEncryptionKeys.add(encryptionKey);
currentSampleData = null;
}
public void assertNextSample(byte[] data, Long timeUs, Integer flags, byte[] encryptionKey) {
assertEquals((Integer) data.length, sampleSizes.poll());
MoreAsserts.assertEquals(data, sampleData.poll());
assertEquals(timeUs, sampleTimesUs.poll());
assertEquals(flags, sampleFlags.poll());
byte[] sampleEncryptionKey = sampleEncryptionKeys.poll();
if (encryptionKey == null) {
assertEquals(null, sampleEncryptionKey);
} else {
MoreAsserts.assertEquals(encryptionKey, sampleEncryptionKey);
}
}
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.testutil;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.ExtractorOutput;
import com.google.android.exoplayer.extractor.SeekMap;
import android.util.SparseArray;
import junit.framework.TestCase;
/**
* A fake {@link ExtractorOutput}.
*/
public final class FakeExtractorOutput implements ExtractorOutput {
private final boolean allowDuplicateTrackIds;
public final SparseArray<FakeTrackOutput> trackOutputs;
public boolean tracksEnded;
public SeekMap seekMap;
public DrmInitData drmInitData;
public int numberOfTracks;
public FakeExtractorOutput() {
this(false);
}
public FakeExtractorOutput(boolean allowDuplicateTrackIds) {
this.allowDuplicateTrackIds = allowDuplicateTrackIds;
trackOutputs = new SparseArray<>();
}
@Override
public FakeTrackOutput track(int trackId) {
FakeTrackOutput output = trackOutputs.get(trackId);
if (output == null) {
numberOfTracks++;
output = new FakeTrackOutput();
trackOutputs.put(trackId, output);
} else {
TestCase.assertTrue("Duplicate track id: " + trackId, allowDuplicateTrackIds);
}
return output;
}
@Override
public void endTracks() {
tracksEnded = true;
}
@Override
public void seekMap(SeekMap seekMap) {
this.seekMap = seekMap;
}
@Override
public void drmInitData(DrmInitData drmInitData) {
this.drmInitData = drmInitData;
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.testutil;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.util.ParsableByteArray;
import android.test.MoreAsserts;
import junit.framework.TestCase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* A fake {@link TrackOutput}.
*/
public final class FakeTrackOutput implements TrackOutput {
private final ArrayList<Long> sampleTimesUs;
private final ArrayList<Integer> sampleFlags;
private final ArrayList<Integer> sampleStartOffsets;
private final ArrayList<Integer> sampleEndOffsets;
private final ArrayList<byte[]> sampleEncryptionKeys;
private byte[] sampleData;
public MediaFormat format;
public FakeTrackOutput() {
sampleData = new byte[0];
sampleTimesUs = new ArrayList<>();
sampleFlags = new ArrayList<>();
sampleStartOffsets = new ArrayList<>();
sampleEndOffsets = new ArrayList<>();
sampleEncryptionKeys = new ArrayList<>();
}
@Override
public void format(MediaFormat format) {
this.format = format;
}
@Override
public int sampleData(ExtractorInput input, int length) throws IOException,
InterruptedException {
byte[] newData = new byte[length];
input.readFully(newData, 0, length);
sampleData = TestUtil.joinByteArrays(sampleData, newData);
return length;
}
@Override
public void sampleData(ParsableByteArray data, int length) {
byte[] newData = new byte[length];
data.readBytes(newData, 0, length);
sampleData = TestUtil.joinByteArrays(sampleData, newData);
}
@Override
public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) {
sampleTimesUs.add(timeUs);
sampleFlags.add(flags);
sampleStartOffsets.add(sampleData.length - offset - size);
sampleEndOffsets.add(sampleData.length - offset);
sampleEncryptionKeys.add(encryptionKey);
}
public void assertSampleCount(int count) {
TestCase.assertEquals(count, sampleTimesUs.size());
}
public void assertSample(int index, byte[] data, long timeUs, int flags, byte[] encryptionKey) {
byte[] actualData = Arrays.copyOfRange(sampleData, sampleStartOffsets.get(index),
sampleEndOffsets.get(index));
MoreAsserts.assertEquals(data, actualData);
TestCase.assertEquals(timeUs, (long) sampleTimesUs.get(index));
TestCase.assertEquals(flags, (int) sampleFlags.get(index));
byte[] sampleEncryptionKey = sampleEncryptionKeys.get(index);
if (encryptionKey == null) {
TestCase.assertEquals(null, sampleEncryptionKey);
} else {
MoreAsserts.assertEquals(encryptionKey, sampleEncryptionKey);
}
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.testutil;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.extractor.DefaultExtractorInput;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.PositionHolder;
import com.google.android.exoplayer.upstream.DataSpec;
import android.net.Uri;
import android.test.InstrumentationTestCase;
import org.mockito.MockitoAnnotations;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
/**
* Utility methods for tests.
*/
public class TestUtil {
private TestUtil() {}
public static void consumeTestData(Extractor extractor, byte[] data)
throws IOException, InterruptedException {
ExtractorInput input = createTestExtractorInput(data);
PositionHolder seekPositionHolder = new PositionHolder();
int readResult = Extractor.RESULT_CONTINUE;
while (readResult != Extractor.RESULT_END_OF_INPUT) {
readResult = extractor.read(input, seekPositionHolder);
if (readResult == Extractor.RESULT_SEEK) {
input = createTestExtractorInput(data, (int) seekPositionHolder.position);
}
}
}
public static ExtractorInput createTestExtractorInput(byte[] data) throws IOException {
return createTestExtractorInput(data, 0);
}
public static ExtractorInput createTestExtractorInput(byte[] data, int offset)
throws IOException {
if (offset != 0) {
data = Arrays.copyOfRange(data, offset, data.length);
}
FakeDataSource dataSource = new FakeDataSource.Builder().appendReadData(data).build();
dataSource.open(new DataSpec(Uri.parse("http://www.google.com")));
ExtractorInput input = new DefaultExtractorInput(dataSource, offset, C.LENGTH_UNBOUNDED);
return input;
}
public static byte[] buildTestData(int length) {
return buildTestData(length, length);
}
public static byte[] buildTestData(int length, int seed) {
Random random = new Random(seed);
byte[] source = new byte[length];
random.nextBytes(source);
return source;
}
public static byte[] createByteArray(int... intArray) {
byte[] byteArray = new byte[intArray.length];
for (int i = 0; i < byteArray.length; i++) {
byteArray[i] = (byte) intArray[i];
}
return byteArray;
}
public static byte[] joinByteArrays(byte[]... byteArrays) {
int length = 0;
for (byte[] byteArray : byteArrays) {
length += byteArray.length;
}
byte[] joined = new byte[length];
length = 0;
for (byte[] byteArray : byteArrays) {
System.arraycopy(byteArray, 0, joined, length, byteArray.length);
length += byteArray.length;
}
return joined;
}
public static void setUpMockito(InstrumentationTestCase instrumentationTestCase) {
// Workaround for https://code.google.com/p/dexmaker/issues/detail?id=2.
System.setProperty("dexmaker.dexcache",
instrumentationTestCase.getInstrumentation().getTargetContext().getCacheDir().getPath());
MockitoAnnotations.initMocks(instrumentationTestCase);
}
}

View file

@ -1,49 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.testutil;
import android.test.InstrumentationTestCase;
import org.mockito.MockitoAnnotations;
import java.util.Random;
/**
* Utility methods for tests.
*/
public class Util {
private Util() {}
public static byte[] buildTestData(int length) {
return buildTestData(length, length);
}
public static byte[] buildTestData(int length, int seed) {
Random random = new Random(seed);
byte[] source = new byte[length];
random.nextBytes(source);
return source;
}
public static void setUpMockito(InstrumentationTestCase instrumentationTestCase) {
// Workaround for https://code.google.com/p/dexmaker/issues/detail?id=2.
System.setProperty("dexmaker.dexcache",
instrumentationTestCase.getInstrumentation().getTargetContext().getCacheDir().getPath());
MockitoAnnotations.initMocks(instrumentationTestCase);
}
}