Commit 9b6c3052 by Oliver Woodman

webm_extractor_tests: Add tests for multi track Webm Extractor Support

Refactor the extractor tests to enable testing of multi track Webm support.

Github Issue: #363
parent 48fc9635
...@@ -70,55 +70,105 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -70,55 +70,105 @@ public class WebmExtractorTest extends InstrumentationTestCase {
private static final int ID_VP9 = 0; private static final int ID_VP9 = 0;
private static final int ID_OPUS = 1; private static final int ID_OPUS = 1;
private static final int ID_VORBIS = 2; private static final int ID_VORBIS = 2;
private static final int ID_DUMMY = 3;
private WebmExtractor extractor; private WebmExtractor extractor;
private TestOutput output; private TestExtractorOutput extractorOutput;
private TestTrackOutput audioOutput;
private TestTrackOutput videoOutput;
@Override @Override
public void setUp() { public void setUp() {
extractor = new WebmExtractor(); extractor = new WebmExtractor();
output = new TestOutput(); extractorOutput = new TestExtractorOutput();
extractor.init(output); audioOutput = new TestTrackOutput();
videoOutput = new TestTrackOutput();
extractor.init(extractorOutput);
} }
@Override @Override
public void tearDown() { public void tearDown() {
extractor = null; extractor = null;
output = null; extractorOutput = null;
audioOutput = null;
videoOutput = null;
} }
public void testReadInitializationSegment() throws IOException, InterruptedException { public void testReadInitializationSegment() throws IOException, InterruptedException {
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, null)); consume(
assertFormat(); createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, null));
assertVideoFormat();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
} }
public void testPrepareOpus() throws IOException, InterruptedException { public void testPrepareOpus() throws IOException, InterruptedException {
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_OPUS, null)); consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_OPUS}, null));
assertAudioFormat(ID_OPUS); assertAudioFormat(ID_OPUS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
} }
public void testPrepareVorbis() throws IOException, InterruptedException { public void testPrepareVorbis() throws IOException, InterruptedException {
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VORBIS, null)); consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VORBIS}, null));
assertAudioFormat(ID_VORBIS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
}
public void testPrepareTwoTracks() throws IOException, InterruptedException {
consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9, ID_OPUS}, null));
assertEquals(2, extractorOutput.numberOfTracks);
assertVideoFormat();
assertAudioFormat(ID_OPUS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
}
public void testPrepareThreeTracks() throws IOException, InterruptedException {
consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9, ID_DUMMY, ID_OPUS}, null));
// Eventhough the input stream has 3 tracks, only 2 of them are supported and will be reported.
assertEquals(2, extractorOutput.numberOfTracks);
assertVideoFormat();
assertAudioFormat(ID_OPUS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
}
public void testPrepareFourTracks() throws IOException, InterruptedException {
consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9, ID_VORBIS, ID_VP9, ID_OPUS}, null));
// Eventhough the input stream has 4 supported tracks, only the first video and audio track will
// be reported.
assertEquals(2, extractorOutput.numberOfTracks);
assertVideoFormat();
assertAudioFormat(ID_VORBIS); assertAudioFormat(ID_VORBIS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
} }
public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException { public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 1);
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, settings)); consume(
assertFormat(); createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, settings));
assertVideoFormat();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
DrmInitData drmInitData = output.drmInitData; DrmInitData drmInitData = extractorOutput.drmInitData;
assertNotNull(output.drmInitData); assertNotNull(drmInitData);
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(WIDEVINE_UUID)); android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(WIDEVINE_UUID));
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(ZERO_UUID)); android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(ZERO_UUID));
} }
public void testPrepareThreeCuePoints() throws IOException, InterruptedException { public void testPrepareThreeCuePoints() throws IOException, InterruptedException {
consume(createInitializationSegment(3, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, null)); consume(
assertFormat(); createInitializationSegment(3, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, null));
assertVideoFormat();
assertIndex( assertIndex(
new IndexPoint(0, 0, 10000), new IndexPoint(0, 0, 10000),
new IndexPoint(10000, 0, 10000), new IndexPoint(10000, 0, 10000),
...@@ -126,8 +176,8 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -126,8 +176,8 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
public void testPrepareCustomTimecodeScale() throws IOException, InterruptedException { public void testPrepareCustomTimecodeScale() throws IOException, InterruptedException {
consume(createInitializationSegment(3, 0, true, 1000, ID_VP9, null)); consume(createInitializationSegment(3, 0, true, 1000, new int[] {ID_VP9}, null));
assertFormat(); assertVideoFormat();
assertIndex( assertIndex(
new IndexPoint(0, 0, 10), new IndexPoint(0, 0, 10),
new IndexPoint(10, 0, 10), new IndexPoint(10, 0, 10),
...@@ -136,7 +186,9 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -136,7 +186,9 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public void testPrepareNoCuePoints() throws IOException, InterruptedException { public void testPrepareNoCuePoints() throws IOException, InterruptedException {
try { try {
consume(createInitializationSegment(0, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, null)); consume(
createInitializationSegment(0, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, null));
fail(); fail();
} catch (ParserException exception) { } catch (ParserException exception) {
assertEquals("Invalid/missing cue points", exception.getMessage()); assertEquals("Invalid/missing cue points", exception.getMessage());
...@@ -145,7 +197,9 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -145,7 +197,9 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public void testPrepareInvalidDocType() throws IOException, InterruptedException { public void testPrepareInvalidDocType() throws IOException, InterruptedException {
try { try {
consume(createInitializationSegment(1, 0, false, DEFAULT_TIMECODE_SCALE, ID_VP9, null)); consume(
createInitializationSegment(1, 0, false, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, null));
fail(); fail();
} catch (ParserException exception) { } catch (ParserException exception) {
assertEquals("DocType webB not supported", exception.getMessage()); assertEquals("DocType webB not supported", exception.getMessage());
...@@ -155,7 +209,9 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -155,7 +209,9 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public void testPrepareInvalidContentEncodingOrder() throws IOException, InterruptedException { public void testPrepareInvalidContentEncodingOrder() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(1, 1, 1, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(1, 1, 1, 5, 1);
try { try {
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, settings)); consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, settings));
fail(); fail();
} catch (ParserException exception) { } catch (ParserException exception) {
assertEquals("ContentEncodingOrder 1 not supported", exception.getMessage()); assertEquals("ContentEncodingOrder 1 not supported", exception.getMessage());
...@@ -165,7 +221,9 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -165,7 +221,9 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public void testPrepareInvalidContentEncodingScope() throws IOException, InterruptedException { public void testPrepareInvalidContentEncodingScope() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 0, 1, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 0, 1, 5, 1);
try { try {
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, settings)); consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, settings));
fail(); fail();
} catch (ParserException exception) { } catch (ParserException exception) {
assertEquals("ContentEncodingScope 0 not supported", exception.getMessage()); assertEquals("ContentEncodingScope 0 not supported", exception.getMessage());
...@@ -175,7 +233,9 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -175,7 +233,9 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public void testPrepareInvalidContentEncodingType() throws IOException, InterruptedException { public void testPrepareInvalidContentEncodingType() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, 5, 1);
try { try {
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, settings)); consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, settings));
fail(); fail();
} catch (ParserException exception) { } catch (ParserException exception) {
assertEquals("ContentEncodingType 0 not supported", exception.getMessage()); assertEquals("ContentEncodingType 0 not supported", exception.getMessage());
...@@ -185,7 +245,9 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -185,7 +245,9 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public void testPrepareInvalidContentEncAlgo() throws IOException, InterruptedException { public void testPrepareInvalidContentEncAlgo() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 4, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 4, 1);
try { try {
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, settings)); consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, settings));
fail(); fail();
} catch (ParserException exception) { } catch (ParserException exception) {
assertEquals("ContentEncAlgo 4 not supported", exception.getMessage()); assertEquals("ContentEncAlgo 4 not supported", exception.getMessage());
...@@ -195,7 +257,9 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -195,7 +257,9 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public void testPrepareInvalidAESSettingsCipherMode() throws IOException, InterruptedException { public void testPrepareInvalidAESSettingsCipherMode() throws IOException, InterruptedException {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 0); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 0);
try { try {
consume(createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9, settings)); consume(
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, settings));
fail(); fail();
} catch (ParserException exception) { } catch (ParserException exception) {
assertEquals("AESSettingsCipherMode 0 not supported", exception.getMessage()); assertEquals("AESSettingsCipherMode 0 not supported", exception.getMessage());
...@@ -203,46 +267,94 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -203,46 +267,94 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
public void testReadSampleKeyframe() throws IOException, InterruptedException { public void testReadSampleKeyframe() throws IOException, InterruptedException {
MediaSegment mediaSegment = createMediaSegment(100, 0, 0, true, false, true, false, false); MediaSegment mediaSegment =
createMediaSegment(100, 0, 0, true, false, true, false, false, 1);
byte[] testInputData = joinByteArrays( byte[] testInputData = joinByteArrays(
createInitializationSegment( createInitializationSegment(
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_VP9, null), 1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, null),
mediaSegment.clusterBytes); mediaSegment.clusterBytes);
consume(testInputData); consume(testInputData);
assertFormat(); assertVideoFormat();
assertSample(mediaSegment, 0, true, false, false); assertSample(mediaSegment, 0, true, false, false, videoOutput);
}
public void testReadTwoTrackSamples() throws IOException, InterruptedException {
MediaSegment mediaSegmentAudio =
createMediaSegment(100, 0, 0, true, false, true, false, false, 2);
MediaSegment mediaSegmentVideo =
createMediaSegment(100, 0, 0, true, false, true, false, false, 1);
byte[] testInputData = joinByteArrays(
createInitializationSegment(
1, mediaSegmentAudio.clusterBytes.length + mediaSegmentVideo.clusterBytes.length,
true, DEFAULT_TIMECODE_SCALE, new int[] {ID_VP9, ID_OPUS}, null),
mediaSegmentVideo.clusterBytes, mediaSegmentAudio.clusterBytes);
consume(testInputData);
assertEquals(2, extractorOutput.numberOfTracks);
assertVideoFormat();
assertAudioFormat(ID_OPUS);
assertSample(mediaSegmentVideo, 0, true, false, false, videoOutput);
assertSample(mediaSegmentAudio, 0, true, false, false, audioOutput);
}
public void testReadTwoTrackSamplesWithSkippedTrack() throws IOException, InterruptedException {
MediaSegment mediaSegmentAudio =
createMediaSegment(100, 0, 0, true, false, true, false, false, 2);
MediaSegment mediaSegmentVideo =
createMediaSegment(100, 0, 0, true, false, true, false, false, 1);
MediaSegment mediaSegmentDummy =
createMediaSegment(100, 0, 0, true, false, true, false, false, 17);
byte[] testInputData = joinByteArrays(
createInitializationSegment(
1,
mediaSegmentAudio.clusterBytes.length + mediaSegmentVideo.clusterBytes.length
+ mediaSegmentDummy.clusterBytes.length,
true, DEFAULT_TIMECODE_SCALE, new int[] {ID_DUMMY, ID_VP9, ID_OPUS}, null),
mediaSegmentVideo.clusterBytes,
mediaSegmentDummy.clusterBytes,
mediaSegmentAudio.clusterBytes);
consume(testInputData);
assertEquals(2, extractorOutput.numberOfTracks);
assertVideoFormat();
assertAudioFormat(ID_OPUS);
assertSample(mediaSegmentVideo, 0, true, false, false, videoOutput);
assertSample(mediaSegmentAudio, 0, true, false, false, audioOutput);
} }
public void testReadBlock() throws IOException, InterruptedException { public void testReadBlock() throws IOException, InterruptedException {
MediaSegment mediaSegment = createMediaSegment(100, 0, 0, true, false, false, false, false); MediaSegment mediaSegment =
createMediaSegment(100, 0, 0, true, false, false, false, false, 2);
byte[] testInputData = joinByteArrays( byte[] testInputData = joinByteArrays(
createInitializationSegment( createInitializationSegment(
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_OPUS, null), 1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_OPUS}, null),
mediaSegment.clusterBytes); mediaSegment.clusterBytes);
consume(testInputData); consume(testInputData);
assertAudioFormat(ID_OPUS); assertAudioFormat(ID_OPUS);
assertSample(mediaSegment, 0, true, false, false); assertSample(mediaSegment, 0, true, false, false, audioOutput);
} }
public void testReadEncryptedFrame() throws IOException, InterruptedException { public void testReadEncryptedFrame() throws IOException, InterruptedException {
MediaSegment mediaSegment = createMediaSegment(100, 0, 0, true, false, true, true, true); MediaSegment mediaSegment = createMediaSegment(100, 0, 0, true, false, true, true, true, 1);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 1);
byte[] testInputData = joinByteArrays( byte[] testInputData = joinByteArrays(
createInitializationSegment( createInitializationSegment(
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_VP9, settings), 1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, settings),
mediaSegment.clusterBytes); mediaSegment.clusterBytes);
consume(testInputData); consume(testInputData);
assertFormat(); assertVideoFormat();
assertSample(mediaSegment, 0, true, false, true); assertSample(mediaSegment, 0, true, false, true, videoOutput);
} }
public void testReadEncryptedFrameWithInvalidSignalByte() public void testReadEncryptedFrameWithInvalidSignalByte()
throws IOException, InterruptedException { throws IOException, InterruptedException {
MediaSegment mediaSegment = createMediaSegment(100, 0, 0, true, false, true, true, false); MediaSegment mediaSegment = createMediaSegment(100, 0, 0, true, false, true, true, false, 1);
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 1, 5, 1);
byte[] testInputData = joinByteArrays( byte[] testInputData = joinByteArrays(
createInitializationSegment( createInitializationSegment(
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_VP9, settings), 1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, settings),
mediaSegment.clusterBytes); mediaSegment.clusterBytes);
try { try {
consume(testInputData); consume(testInputData);
...@@ -253,36 +365,41 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -253,36 +365,41 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
public void testReadSampleInvisible() throws IOException, InterruptedException { public void testReadSampleInvisible() throws IOException, InterruptedException {
MediaSegment mediaSegment = createMediaSegment(100, 12, 13, false, true, true, false, false); MediaSegment mediaSegment =
createMediaSegment(100, 12, 13, false, true, true, false, false, 1);
byte[] testInputData = joinByteArrays( byte[] testInputData = joinByteArrays(
createInitializationSegment( createInitializationSegment(
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_VP9, null), 1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, null),
mediaSegment.clusterBytes); mediaSegment.clusterBytes);
consume(testInputData); consume(testInputData);
assertFormat(); assertVideoFormat();
assertSample(mediaSegment, 25000, false, true, false); assertSample(mediaSegment, 25000, false, true, false, videoOutput);
} }
public void testReadSampleCustomTimescale() throws IOException, InterruptedException { public void testReadSampleCustomTimescale() throws IOException, InterruptedException {
MediaSegment mediaSegment = createMediaSegment(100, 12, 13, false, false, true, false, false); MediaSegment mediaSegment =
createMediaSegment(100, 12, 13, false, false, true, false, false, 1);
byte[] testInputData = joinByteArrays( byte[] testInputData = joinByteArrays(
createInitializationSegment( createInitializationSegment(
1, mediaSegment.clusterBytes.length, true, 1000, ID_VP9, null), 1, mediaSegment.clusterBytes.length, true, 1000, new int[] {ID_VP9}, null),
mediaSegment.clusterBytes); mediaSegment.clusterBytes);
consume(testInputData); consume(testInputData);
assertFormat(); assertVideoFormat();
assertSample(mediaSegment, 25, false, false, false); assertSample(mediaSegment, 25, false, false, false, videoOutput);
} }
public void testReadSampleNegativeSimpleBlockTimecode() throws IOException, InterruptedException { public void testReadSampleNegativeSimpleBlockTimecode() throws IOException, InterruptedException {
MediaSegment mediaSegment = createMediaSegment(100, 13, -12, true, true, true, false, false); MediaSegment mediaSegment =
createMediaSegment(100, 13, -12, true, true, true, false, false, 1);
byte[] testInputData = joinByteArrays( byte[] testInputData = joinByteArrays(
createInitializationSegment( createInitializationSegment(
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_VP9, null), 1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE,
new int[] {ID_VP9}, null),
mediaSegment.clusterBytes); mediaSegment.clusterBytes);
consume(testInputData); consume(testInputData);
assertFormat(); assertVideoFormat();
assertSample(mediaSegment, 1000, true, true, false); assertSample(mediaSegment, 1000, true, true, false, videoOutput);
} }
private void consume(byte[] data) throws IOException, InterruptedException { private void consume(byte[] data) throws IOException, InterruptedException {
...@@ -301,15 +418,15 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -301,15 +418,15 @@ public class WebmExtractorTest extends InstrumentationTestCase {
return input; return input;
} }
private void assertFormat() { private void assertVideoFormat() {
MediaFormat format = output.format; MediaFormat format = videoOutput.format;
assertEquals(TEST_WIDTH, format.width); assertEquals(TEST_WIDTH, format.width);
assertEquals(TEST_HEIGHT, format.height); assertEquals(TEST_HEIGHT, format.height);
assertEquals(MimeTypes.VIDEO_VP9, format.mimeType); assertEquals(MimeTypes.VIDEO_VP9, format.mimeType);
} }
private void assertAudioFormat(int codecId) { private void assertAudioFormat(int codecId) {
MediaFormat format = output.format; MediaFormat format = audioOutput.format;
assertEquals(TEST_CHANNEL_COUNT, format.channelCount); assertEquals(TEST_CHANNEL_COUNT, format.channelCount);
assertEquals(TEST_SAMPLE_RATE, format.sampleRate); assertEquals(TEST_SAMPLE_RATE, format.sampleRate);
if (codecId == ID_OPUS) { if (codecId == ID_OPUS) {
...@@ -327,7 +444,7 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -327,7 +444,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
private void assertIndex(IndexPoint... indexPoints) { private void assertIndex(IndexPoint... indexPoints) {
ChunkIndex index = (ChunkIndex) output.seekMap; ChunkIndex index = (ChunkIndex) extractorOutput.seekMap;
assertEquals(indexPoints.length, index.length); assertEquals(indexPoints.length, index.length);
for (int i = 0; i < indexPoints.length; i++) { for (int i = 0; i < indexPoints.length; i++) {
IndexPoint indexPoint = indexPoints[i]; IndexPoint indexPoint = indexPoints[i];
...@@ -338,8 +455,8 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -338,8 +455,8 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
private void assertSample(MediaSegment mediaSegment, int timeUs, boolean keyframe, private void assertSample(MediaSegment mediaSegment, int timeUs, boolean keyframe,
boolean invisible, boolean encrypted) { boolean invisible, boolean encrypted, TestTrackOutput output) {
byte[] expectedOutput = mediaSegment.videoBytes; byte[] expectedOutput = mediaSegment.data;
if (encrypted) { if (encrypted) {
expectedOutput = joinByteArrays(new byte[] {(byte) TEST_INITIALIZATION_VECTOR.length}, expectedOutput = joinByteArrays(new byte[] {(byte) TEST_INITIALIZATION_VECTOR.length},
TEST_INITIALIZATION_VECTOR, expectedOutput); TEST_INITIALIZATION_VECTOR, expectedOutput);
...@@ -353,24 +470,11 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -353,24 +470,11 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
private byte[] createInitializationSegment(int cuePoints, int mediaSegmentSize, private byte[] createInitializationSegment(int cuePoints, int mediaSegmentSize,
boolean docTypeIsWebm, int timecodeScale, int codecId, boolean docTypeIsWebm, int timecodeScale, int[] codecIds,
ContentEncodingSettings contentEncodingSettings) { ContentEncodingSettings contentEncodingSettings) {
byte[] tracksElement = null; byte[] tracksElement = createTracksElement(codecIds, contentEncodingSettings);
switch (codecId) {
case ID_VP9:
tracksElement = createTracksElementWithVideo(
true, TEST_WIDTH, TEST_HEIGHT, contentEncodingSettings);
break;
case ID_OPUS:
tracksElement = createTracksElementWithOpusAudio(TEST_CHANNEL_COUNT);
break;
case ID_VORBIS:
tracksElement = createTracksElementWithVorbisAudio(TEST_CHANNEL_COUNT);
break;
}
byte[] infoElement = createInfoElement(timecodeScale); byte[] infoElement = createInfoElement(timecodeScale);
byte[] cuesElement = createCuesElement(CUE_POINT_ELEMENT_BYTE_SIZE * cuePoints); byte[] cuesElement = createCuesElement(CUE_POINT_ELEMENT_BYTE_SIZE * cuePoints);
int initalizationSegmentSize = infoElement.length + tracksElement.length int initalizationSegmentSize = infoElement.length + tracksElement.length
+ cuesElement.length + CUE_POINT_ELEMENT_BYTE_SIZE * cuePoints; + cuesElement.length + CUE_POINT_ELEMENT_BYTE_SIZE * cuePoints;
byte[] segmentElement = createSegmentElement(initalizationSegmentSize + mediaSegmentSize); byte[] segmentElement = createSegmentElement(initalizationSegmentSize + mediaSegmentSize);
...@@ -382,20 +486,47 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -382,20 +486,47 @@ public class WebmExtractorTest extends InstrumentationTestCase {
return bytes; return bytes;
} }
private static MediaSegment createMediaSegment(int videoBytesLength, int clusterTimecode, private byte[] createTracksElement(int[] codecIds,
ContentEncodingSettings contentEncodingSettings) {
byte[] trackBytes = new byte[0];
for (int codecId : codecIds) {
switch (codecId) {
case ID_VP9:
trackBytes = joinByteArrays(trackBytes,
createVideoTrackEntry(true, TEST_WIDTH, TEST_HEIGHT, contentEncodingSettings));
break;
case ID_OPUS:
trackBytes = joinByteArrays(trackBytes, createOpusAudioTrackEntry(TEST_CHANNEL_COUNT));
break;
case ID_VORBIS:
trackBytes = joinByteArrays(trackBytes, createVorbisAudioTrackEntry(TEST_CHANNEL_COUNT));
break;
case ID_DUMMY:
trackBytes = joinByteArrays(trackBytes, createUnsupportedTrackEntry());
break;
}
}
byte[] tracksSize = getIntegerBytes(trackBytes.length);
byte[] tracksHeader = createByteArray(
0x16, 0x54, 0xAE, 0x6B, // Tracks
0x01, 0x00, 0x00, 0x00, tracksSize[0], tracksSize[1], tracksSize[2], tracksSize[3]);
return joinByteArrays(tracksHeader, trackBytes);
}
private static MediaSegment createMediaSegment(int dataLength, int clusterTimecode,
int blockTimecode, boolean keyframe, boolean invisible, boolean simple, int blockTimecode, boolean keyframe, boolean invisible, boolean simple,
boolean encrypted, boolean validSignalByte) { boolean encrypted, boolean validSignalByte, int trackNumber) {
byte[] videoBytes = createVideoBytes(videoBytesLength); byte[] data = createFrameData(dataLength);
byte[] blockBytes; byte[] blockBytes;
if (simple) { if (simple) {
blockBytes = createSimpleBlockElement(videoBytes.length, blockTimecode, blockBytes = createSimpleBlockElement(data.length, blockTimecode,
keyframe, invisible, true, encrypted, validSignalByte); keyframe, invisible, true, encrypted, validSignalByte, trackNumber);
} else { } else {
blockBytes = createBlockElement(videoBytes.length, blockTimecode, invisible, true); blockBytes = createBlockElement(data.length, blockTimecode, invisible, true, trackNumber);
} }
byte[] clusterBytes = byte[] clusterBytes =
createClusterElement(blockBytes.length + videoBytes.length, clusterTimecode); createClusterElement(blockBytes.length + data.length, clusterTimecode);
return new MediaSegment(joinByteArrays(clusterBytes, blockBytes, videoBytes), videoBytes); return new MediaSegment(joinByteArrays(clusterBytes, blockBytes, data), data);
} }
private static byte[] joinByteArrays(byte[]... byteArrays) { private static byte[] joinByteArrays(byte[]... byteArrays) {
...@@ -443,7 +574,7 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -443,7 +574,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
0x88, 0x40, 0xC3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00); // size=8 value=9920.0 0x88, 0x40, 0xC3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00); // size=8 value=9920.0
} }
private static byte[] createTracksElementWithVideo( private static byte[] createVideoTrackEntry(
boolean codecIsVp9, int pixelWidth, int pixelHeight, boolean codecIsVp9, int pixelWidth, int pixelHeight,
ContentEncodingSettings contentEncodingSettings) { ContentEncodingSettings contentEncodingSettings) {
byte[] widthBytes = getIntegerBytes(pixelWidth); byte[] widthBytes = getIntegerBytes(pixelWidth);
...@@ -455,8 +586,6 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -455,8 +586,6 @@ public class WebmExtractorTest extends InstrumentationTestCase {
byte[] algorithmBytes = getIntegerBytes(contentEncodingSettings.algorithm); byte[] algorithmBytes = getIntegerBytes(contentEncodingSettings.algorithm);
byte[] cipherModeBytes = getIntegerBytes(contentEncodingSettings.aesCipherMode); byte[] cipherModeBytes = getIntegerBytes(contentEncodingSettings.aesCipherMode);
return createByteArray( return createByteArray(
0x16, 0x54, 0xAE, 0x6B, // Tracks
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, // size=78
0xAE, // TrackEntry 0xAE, // TrackEntry
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, // size=69 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, // size=69
0x86, // CodecID 0x86, // CodecID
...@@ -495,10 +624,8 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -495,10 +624,8 @@ public class WebmExtractorTest extends InstrumentationTestCase {
0x82, heightBytes[2], heightBytes[3]); // size=2 0x82, heightBytes[2], heightBytes[3]); // size=2
} else { } else {
return createByteArray( return createByteArray(
0x16, 0x54, 0xAE, 0x6B, // Tracks
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, // size=42
0xAE, // TrackEntry 0xAE, // TrackEntry
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, // size=33 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, // size=30
0x86, // CodecID 0x86, // CodecID
0x85, 0x56, 0x5F, 0x56, 0x50, codecIsVp9 ? 0x39 : 0x30, // size=5 value=V_VP9/0 0x85, 0x56, 0x5F, 0x56, 0x50, codecIsVp9 ? 0x39 : 0x30, // size=5 value=V_VP9/0
0xD7, // TrackNumber 0xD7, // TrackNumber
...@@ -514,17 +641,15 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -514,17 +641,15 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
} }
private static byte[] createTracksElementWithOpusAudio(int channelCount) { private static byte[] createOpusAudioTrackEntry(int channelCount) {
byte[] channelCountBytes = getIntegerBytes(channelCount); byte[] channelCountBytes = getIntegerBytes(channelCount);
return createByteArray( return createByteArray(
0x16, 0x54, 0xAE, 0x6B, // Tracks
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, // size=63
0xAE, // TrackEntry 0xAE, // TrackEntry
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, // size=54 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, // size=54
0x86, // CodecID 0x86, // CodecID
0x86, 0x41, 0x5F, 0x4F, 0x50, 0x55, 0x53, // size=6 value=A_OPUS 0x86, 0x41, 0x5F, 0x4F, 0x50, 0x55, 0x53, // size=6 value=A_OPUS
0xD7, // TrackNumber 0xD7, // TrackNumber
0x81, 0x01, // size=1 value=1 0x81, 0x02, // size=1 value=2
0x83, // TrackType 0x83, // TrackType
0x81, 0x02, // size=1 value=2 0x81, 0x02, // size=1 value=2
0x56, 0xAA, // CodecDelay 0x56, 0xAA, // CodecDelay
...@@ -541,17 +666,15 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -541,17 +666,15 @@ public class WebmExtractorTest extends InstrumentationTestCase {
0x82, 0x00, 0x00); // size=2 0x82, 0x00, 0x00); // size=2
} }
private byte[] createTracksElementWithVorbisAudio(int channelCount) { private byte[] createVorbisAudioTrackEntry(int channelCount) {
byte[] channelCountBytes = getIntegerBytes(channelCount); byte[] channelCountBytes = getIntegerBytes(channelCount);
byte[] tracksElement = createByteArray( byte[] tracksElement = createByteArray(
0x16, 0x54, 0xAE, 0x6B, // Tracks
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xA2, // size=4258
0xAE, // TrackEntry 0xAE, // TrackEntry
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, // size=4249 (42+4207) 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, // size=4249 (42+4207)
0x86, // CodecID 0x86, // CodecID
0x88, 0x41, 0x5f, 0x56, 0x4f, 0x52, 0x42, 0x49, 0x53, // size=8 value=A_VORBIS 0x88, 0x41, 0x5f, 0x56, 0x4f, 0x52, 0x42, 0x49, 0x53, // size=8 value=A_VORBIS
0xD7, // TrackNumber 0xD7, // TrackNumber
0x81, 0x01, // size=1 value=1 0x81, 0x02, // size=1 value=2
0x83, // TrackType 0x83, // TrackType
0x81, 0x02, // size=1 value=2 0x81, 0x02, // size=1 value=2
0xE1, // Audio 0xE1, // Audio
...@@ -572,6 +695,20 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -572,6 +695,20 @@ public class WebmExtractorTest extends InstrumentationTestCase {
return joinByteArrays(tracksElement, codecPrivate); return joinByteArrays(tracksElement, codecPrivate);
} }
private static byte[] createUnsupportedTrackEntry() {
return createByteArray(
0xAE, // TrackEntry
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, // size=32
0x86, // CodecID
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, // size =17
0x44, 0x5f, 0x57, 0x45, 0x42, 0x56, 0x54, 0x54,
0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, // value=D_WEBVTT/metadata
0xD7, // TrackNumber
0x81, 0x03, // size=1 value=3
0x83, // TrackType
0x81, 0x11); // size=1 value=11
}
private static byte[] createCuesElement(int size) { private static byte[] createCuesElement(int size) {
byte[] sizeBytes = getIntegerBytes(size); byte[] sizeBytes = getIntegerBytes(size);
return createByteArray( return createByteArray(
...@@ -605,15 +742,16 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -605,15 +742,16 @@ public class WebmExtractorTest extends InstrumentationTestCase {
private static byte[] createSimpleBlockElement( private static byte[] createSimpleBlockElement(
int size, int timecode, boolean keyframe, boolean invisible, boolean noLacing, int size, int timecode, boolean keyframe, boolean invisible, boolean noLacing,
boolean encrypted, boolean validSignalByte) { boolean encrypted, boolean validSignalByte, int trackNumber) {
byte[] sizeBytes = getIntegerBytes(size + 4 + (encrypted ? 9 : 0)); byte[] sizeBytes = getIntegerBytes(size + 5 + (encrypted ? 9 : 0));
byte[] timeBytes = getIntegerBytes(timecode); byte[] timeBytes = getIntegerBytes(timecode);
byte[] trackNumberBytes = getIntegerBytes(trackNumber);
byte flags = (byte) byte flags = (byte)
((keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00) | (noLacing ? 0x00 : 0x06)); ((keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00) | (noLacing ? 0x00 : 0x06));
byte[] simpleBlock = createByteArray( byte[] simpleBlock = createByteArray(
0xA3, // SimpleBlock 0xA3, // SimpleBlock
0x01, 0x00, 0x00, 0x00, sizeBytes[0], sizeBytes[1], sizeBytes[2], sizeBytes[3], 0x01, 0x00, 0x00, 0x00, sizeBytes[0], sizeBytes[1], sizeBytes[2], sizeBytes[3],
0x81, // Track number value=1 0x40, trackNumberBytes[3], // Track number size=2
timeBytes[2], timeBytes[3], flags); // Timecode and flags timeBytes[2], timeBytes[3], flags); // Timecode and flags
if (encrypted) { if (encrypted) {
simpleBlock = joinByteArrays( simpleBlock = joinByteArrays(
...@@ -624,10 +762,11 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -624,10 +762,11 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
private static byte[] createBlockElement( private static byte[] createBlockElement(
int size, int timecode, boolean invisible, boolean noLacing) { int size, int timecode, boolean invisible, boolean noLacing, int trackNumber) {
int blockSize = size + 4; int blockSize = size + 5;
byte[] blockSizeBytes = getIntegerBytes(blockSize); byte[] blockSizeBytes = getIntegerBytes(blockSize);
byte[] timeBytes = getIntegerBytes(timecode); byte[] timeBytes = getIntegerBytes(timecode);
byte[] trackNumberBytes = getIntegerBytes(trackNumber);
int blockElementSize = 1 + 8 + blockSize; // id + size + length of data int blockElementSize = 1 + 8 + blockSize; // id + size + length of data
byte[] sizeBytes = getIntegerBytes(blockElementSize); byte[] sizeBytes = getIntegerBytes(blockElementSize);
byte flags = (byte) ((invisible ? 0x08 : 0x00) | (noLacing ? 0x00 : 0x06)); byte flags = (byte) ((invisible ? 0x08 : 0x00) | (noLacing ? 0x00 : 0x06));
...@@ -637,16 +776,16 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -637,16 +776,16 @@ public class WebmExtractorTest extends InstrumentationTestCase {
0xA1, // Block 0xA1, // Block
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
blockSizeBytes[0], blockSizeBytes[1], blockSizeBytes[2], blockSizeBytes[3], blockSizeBytes[0], blockSizeBytes[1], blockSizeBytes[2], blockSizeBytes[3],
0x81, // Track number value=1 0x40, trackNumberBytes[3], // Track number size=2
timeBytes[2], timeBytes[3], flags); // Timecode and flags timeBytes[2], timeBytes[3], flags); // Timecode and flags
} }
private static byte[] createVideoBytes(int size) { private static byte[] createFrameData(int size) {
byte[] videoBytes = new byte[size]; byte[] data = new byte[size];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
videoBytes[i] = (byte) i; data[i] = (byte) i;
} }
return videoBytes; return data;
} }
private static byte[] getIntegerBytes(int value) { private static byte[] getIntegerBytes(int value) {
...@@ -669,11 +808,11 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -669,11 +808,11 @@ public class WebmExtractorTest extends InstrumentationTestCase {
private static final class MediaSegment { private static final class MediaSegment {
private final byte[] clusterBytes; private final byte[] clusterBytes;
private final byte[] videoBytes; private final byte[] data;
private MediaSegment(byte[] clusterBytes, byte[] videoBytes) { private MediaSegment(byte[] clusterBytes, byte[] data) {
this.clusterBytes = clusterBytes; this.clusterBytes = clusterBytes;
this.videoBytes = videoBytes; this.data = data;
} }
} }
...@@ -713,21 +852,19 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -713,21 +852,19 @@ public class WebmExtractorTest extends InstrumentationTestCase {
} }
/** Implements {@link ExtractorOutput} and {@link TrackOutput} for test purposes. */ /** Implements {@link ExtractorOutput} for test purposes. */
public static class TestOutput implements ExtractorOutput, TrackOutput { public class TestExtractorOutput implements ExtractorOutput {
public boolean tracksEnded; public boolean tracksEnded;
public SeekMap seekMap; public SeekMap seekMap;
public DrmInitData drmInitData; public DrmInitData drmInitData;
public int numberOfTracks;
public MediaFormat format;
private long sampleTimeUs;
private int sampleFlags;
private byte[] sampleData;
@Override @Override
public TrackOutput track(int trackId) { public TrackOutput track(int trackId) {
return this; numberOfTracks++;
// In the test samples, track number 1 is always video and track number 2 is always audio.
return (trackId == 1) ? videoOutput : audioOutput;
} }
@Override @Override
...@@ -745,6 +882,16 @@ public class WebmExtractorTest extends InstrumentationTestCase { ...@@ -745,6 +882,16 @@ public class WebmExtractorTest extends InstrumentationTestCase {
this.drmInitData = drmInitData; this.drmInitData = drmInitData;
} }
}
/** Implements {@link TrackOutput} for test purposes. */
public static class TestTrackOutput implements TrackOutput {
public MediaFormat format;
private long sampleTimeUs;
private int sampleFlags;
private byte[] sampleData;
@Override @Override
public void format(MediaFormat format) { public void format(MediaFormat format) {
this.format = format; this.format = format;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment