Remove/simplify some extractor tests.
We have our snazzy new file-based extractor tests now, and the other ones have historically proven way more hassle than they're worth (e.g. I spent a good few hours once just trying to work out how to fix the Mp4 extractor test, having established it was the test and not the code that was broken!). I think some more can go from the ogg package, but leaving in place for now because it's a bit less clear what to get rid of. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=125330072
This commit is contained in:
parent
53cc88e2dc
commit
4c434005d4
@ -24,7 +24,7 @@ import junit.framework.TestCase;
|
|||||||
*/
|
*/
|
||||||
public class ExtractorTest extends TestCase {
|
public class ExtractorTest extends TestCase {
|
||||||
|
|
||||||
public static void testContants() {
|
public static void testConstants() {
|
||||||
// Sanity check that constant values match those defined by {@link C}.
|
// Sanity check that constant values match those defined by {@link C}.
|
||||||
assertEquals(C.RESULT_END_OF_INPUT, Extractor.RESULT_END_OF_INPUT);
|
assertEquals(C.RESULT_END_OF_INPUT, Extractor.RESULT_END_OF_INPUT);
|
||||||
// Sanity check that the other constant values don't overlap.
|
// Sanity check that the other constant values don't overlap.
|
||||||
|
@ -15,788 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.extractor.mkv;
|
package com.google.android.exoplayer.extractor.mkv;
|
||||||
|
|
||||||
import static com.google.android.exoplayer.extractor.mkv.StreamBuilder.TEST_ENCRYPTION_KEY_ID;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer.C;
|
|
||||||
import com.google.android.exoplayer.Format;
|
|
||||||
import com.google.android.exoplayer.ParserException;
|
|
||||||
import com.google.android.exoplayer.drm.DrmInitData;
|
|
||||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
|
||||||
import com.google.android.exoplayer.extractor.ChunkIndex;
|
|
||||||
import com.google.android.exoplayer.extractor.Extractor;
|
import com.google.android.exoplayer.extractor.Extractor;
|
||||||
import com.google.android.exoplayer.extractor.mkv.StreamBuilder.ContentEncodingSettings;
|
|
||||||
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.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
|
||||||
import com.google.android.exoplayer.util.Util;
|
|
||||||
|
|
||||||
import android.test.InstrumentationTestCase;
|
import android.test.InstrumentationTestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link MatroskaExtractor}.
|
* Tests for {@link MatroskaExtractor}.
|
||||||
*/
|
*/
|
||||||
public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
private static final int DEFAULT_TIMECODE_SCALE = 1000000;
|
|
||||||
private static final long TEST_DURATION_TIMECODE = 9920L;
|
|
||||||
private static final int TEST_WIDTH = 1280;
|
|
||||||
private static final int TEST_HEIGHT = 720;
|
|
||||||
private static final int TEST_CHANNEL_COUNT = 1;
|
|
||||||
private static final int TEST_SAMPLE_RATE = 48000;
|
|
||||||
private static final int TEST_CODEC_DELAY = 6500000;
|
|
||||||
private static final int TEST_SEEK_PRE_ROLL = 80000000;
|
|
||||||
private static final String TEST_VORBIS_CODEC_PRIVATE = "webm/vorbis_codec_private";
|
|
||||||
private static final int TEST_VORBIS_INFO_SIZE = 30;
|
|
||||||
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 = 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);
|
|
||||||
private static final byte VIDEO_TRACK_NUMBER = 0x01;
|
|
||||||
private static final byte AUDIO_TRACK_NUMBER = 0x02;
|
|
||||||
private static final byte UNSUPPORTED_TRACK_NUMBER = 0x03;
|
|
||||||
private static final byte SECOND_VIDEO_TRACK_NUMBER = 0x04;
|
|
||||||
private static final byte SECOND_AUDIO_TRACK_NUMBER = 0x05;
|
|
||||||
|
|
||||||
private static final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
|
|
||||||
private static final String MATROSKA_DOC_TYPE = "matroska";
|
|
||||||
private static final String WEBM_DOC_TYPE = "webm";
|
|
||||||
|
|
||||||
private FakeExtractorOutput consumeTestData(byte[] data)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
return TestUtil.consumeTestData(new MatroskaExtractor(), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadInitializationSegment() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareOpus() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addOpusTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
|
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareVorbis() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVorbisTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE,
|
|
||||||
getVorbisCodecPrivate())
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareH264() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(MATROSKA_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addH264Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, TEST_H264_CODEC_PRIVATE)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertH264VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareTwoTracks() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addOpusTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
|
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertEquals(2, extractorOutput.numberOfTracks);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareThreeTracks() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addUnsupportedTrack(UNSUPPORTED_TRACK_NUMBER)
|
|
||||||
.addOpusTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
|
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
// Even though the input stream has 3 tracks, only 2 of them are supported and will be reported.
|
|
||||||
assertEquals(2, extractorOutput.numberOfTracks);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareFourTracks() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addVorbisTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE,
|
|
||||||
getVorbisCodecPrivate())
|
|
||||||
.addVp9Track(SECOND_VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addOpusTrack(SECOND_AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE,
|
|
||||||
TEST_CODEC_DELAY, TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertEquals(4, extractorOutput.numberOfTracks);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_VORBIS);
|
|
||||||
assertVp9VideoFormat(extractorOutput, SECOND_VIDEO_TRACK_NUMBER);
|
|
||||||
assertAudioFormat(extractorOutput, SECOND_AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException {
|
|
||||||
ContentEncodingSettings settings = new StreamBuilder.ContentEncodingSettings(0, 1, 5, 1);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertDrmInitData(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareThreeCuePoints() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.build(3);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSeekMap(extractorOutput, DEFAULT_TIMECODE_SCALE, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareCustomTimecodeScaleBeforeDuration()
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
testPrepareTimecodeScale(1000, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareCustomTimecodeScaleAfterDuration()
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
testPrepareTimecodeScale(1000, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareImplicitDefaultTimecode()
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
testPrepareTimecodeScale(1000, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testPrepareTimecodeScale(int timecodeScale, boolean omitTimecodeScaleIfDefault,
|
|
||||||
boolean afterDuration) throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(timecodeScale, TEST_DURATION_TIMECODE, omitTimecodeScaleIfDefault, afterDuration)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.build(3);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSeekMap(extractorOutput, timecodeScale, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareNoCuesElement() throws IOException, InterruptedException {
|
|
||||||
testPrepareNoCuesElement(DEFAULT_TIMECODE_SCALE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareNoCuesElementCustomTimecodeScale()
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
testPrepareNoCuesElement(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testPrepareNoCuesElement(int timecodeScale) throws IOException,
|
|
||||||
InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(timecodeScale, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, media)
|
|
||||||
.build(0);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertSeekMapUnseekable(extractorOutput, timecodeScale);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAcceptsWebmDocType() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAcceptsMatroskaDocType() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(MATROSKA_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareInvalidDocType() throws IOException, InterruptedException {
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader("webB")
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.build(1);
|
|
||||||
try {
|
|
||||||
consumeTestData(data);
|
|
||||||
fail();
|
|
||||||
} catch (ParserException exception) {
|
|
||||||
assertEquals("DocType webB not supported", exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareInvalidContentEncodingOrder() throws IOException, InterruptedException {
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(1, 1, 5, 1);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.build(1);
|
|
||||||
try {
|
|
||||||
consumeTestData(data);
|
|
||||||
fail();
|
|
||||||
} catch (ParserException exception) {
|
|
||||||
assertEquals("ContentEncodingOrder 1 not supported", exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareInvalidContentEncodingScope() throws IOException, InterruptedException {
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 0, 5, 1);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.build(1);
|
|
||||||
try {
|
|
||||||
consumeTestData(data);
|
|
||||||
fail();
|
|
||||||
} catch (ParserException exception) {
|
|
||||||
assertEquals("ContentEncodingScope 0 not supported", exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareInvalidContentCompAlgo()
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, new byte[0]);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.build(1);
|
|
||||||
try {
|
|
||||||
consumeTestData(data);
|
|
||||||
fail();
|
|
||||||
} catch (ParserException exception) {
|
|
||||||
assertEquals("ContentCompAlgo 0 not supported", exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareInvalidContentEncAlgo() throws IOException, InterruptedException {
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 4, 1);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.build(1);
|
|
||||||
try {
|
|
||||||
consumeTestData(data);
|
|
||||||
fail();
|
|
||||||
} catch (ParserException exception) {
|
|
||||||
assertEquals("ContentEncAlgo 4 not supported", exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPrepareInvalidAESSettingsCipherMode() throws IOException, InterruptedException {
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 0);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.build(1);
|
|
||||||
try {
|
|
||||||
consumeTestData(data);
|
|
||||||
fail();
|
|
||||||
} catch (ParserException exception) {
|
|
||||||
assertEquals("AESSettingsCipherMode 0 not supported", exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadSampleKeyframe() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSample(0, media, 0, true, false, null,
|
|
||||||
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadSampleKeyframeStripped() throws IOException, InterruptedException {
|
|
||||||
byte[] strippedBytes = new byte[] {-1, -1};
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 3, strippedBytes);
|
|
||||||
byte[] sampleBytes = createFrameData(100);
|
|
||||||
byte[] unstrippedSampleBytes = TestUtil.joinByteArrays(strippedBytes, sampleBytes);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, sampleBytes)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSample(0, unstrippedSampleBytes, 0, true, false, null,
|
|
||||||
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadSampleKeyframeManyBytesStripped() throws IOException, InterruptedException {
|
|
||||||
byte[] strippedBytes = createFrameData(100);
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 3, strippedBytes);
|
|
||||||
byte[] sampleBytes = createFrameData(5);
|
|
||||||
byte[] unstrippedSampleBytes = TestUtil.joinByteArrays(strippedBytes, sampleBytes);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, sampleBytes)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSample(0, unstrippedSampleBytes, 0, true, false, null,
|
|
||||||
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadTwoTrackSamples() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addOpusTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
|
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, media)
|
|
||||||
.addSimpleBlockMedia(2 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertEquals(2, extractorOutput.numberOfTracks);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
assertSample(0, media, 0, true, false, null,
|
|
||||||
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
|
||||||
assertSample(0, media, 0, true, false, null,
|
|
||||||
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadTwoTrackSamplesWithSkippedTrack() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addUnsupportedTrack(UNSUPPORTED_TRACK_NUMBER)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addOpusTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
|
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, media)
|
|
||||||
.addSimpleBlockMedia(2 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, media)
|
|
||||||
.addSimpleBlockMedia(17 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertEquals(2, extractorOutput.numberOfTracks);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
assertSample(0, media, 0, true, false, null,
|
|
||||||
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
|
||||||
assertSample(0, media, 0, true, false, null,
|
|
||||||
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadBlock() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addOpusTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
|
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
|
|
||||||
.addBlockMedia(2 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
true /* keyframe */, false /* invisible */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
assertSample(0, media, 0, true, false, null,
|
|
||||||
getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadBlockNonKeyframe() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
|
|
||||||
false /* keyframe */, false /* invisible */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSample(0, media, 0, false, false, null, getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadEncryptedFrame() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 1);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.addSimpleBlockEncryptedMedia(1 /* trackNumber */, 0 /* clusterTimecode */,
|
|
||||||
0 /* blockTimecode */, true /* keyframe */, false /* invisible */,
|
|
||||||
true /* validSignalByte */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSample(0, media, 0, true, false, TEST_ENCRYPTION_KEY_ID,
|
|
||||||
getTrackOutput(extractorOutput, VIDEO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadEncryptedFrameWithInvalidSignalByte()
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 1);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, settings)
|
|
||||||
.addSimpleBlockEncryptedMedia(1 /* trackNumber */, 0 /* clusterTimecode */,
|
|
||||||
0 /* blockTimecode */, true /* keyframe */, false /* invisible */,
|
|
||||||
false /* validSignalByte */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
try {
|
|
||||||
consumeTestData(data);
|
|
||||||
fail();
|
|
||||||
} catch (ParserException exception) {
|
|
||||||
assertEquals("Extension bit is set in signal byte", exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadSampleInvisible() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 12 /* clusterTimecode */, 13 /* blockTimecode */,
|
|
||||||
false /* keyframe */, true /* invisible */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSample(0, media, 25000, false, true, null, getTrackOutput(extractorOutput,
|
|
||||||
VIDEO_TRACK_NUMBER
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadSampleCustomTimecodeScale() throws IOException, InterruptedException {
|
|
||||||
int timecodeScale = 1000;
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(timecodeScale, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 12 /* clusterTimecode */, 13 /* blockTimecode */,
|
|
||||||
false /* keyframe */, false /* invisible */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSample(0, media, 25, false, false, null, getTrackOutput(extractorOutput,
|
|
||||||
VIDEO_TRACK_NUMBER
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadSampleNegativeSimpleBlockTimecode() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addVp9Track(VIDEO_TRACK_NUMBER, TEST_WIDTH, TEST_HEIGHT, null)
|
|
||||||
.addSimpleBlockMedia(1 /* trackNumber */, 13 /* clusterTimecode */, -12 /* blockTimecode */,
|
|
||||||
true /* keyframe */, true /* invisible */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertVp9VideoFormat(extractorOutput, VIDEO_TRACK_NUMBER);
|
|
||||||
assertSample(0, media, 1000, true, true, null, getTrackOutput(extractorOutput,
|
|
||||||
VIDEO_TRACK_NUMBER
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadSampleWithFixedSizeLacing() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(100);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addOpusTrack(AUDIO_TRACK_NUMBER, TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
|
|
||||||
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE, TEST_DEFAULT_DURATION_NS)
|
|
||||||
.addSimpleBlockMediaWithFixedSizeLacing(2 /* trackNumber */, 0 /* clusterTimecode */,
|
|
||||||
0 /* blockTimecode */, 20 /* lacingFrameCount */, media)
|
|
||||||
.build(1);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
long expectedTimeUs = i * TEST_DEFAULT_DURATION_NS / 1000;
|
|
||||||
assertSample(i, Arrays.copyOfRange(media, i * 5, i * 5 + 5), expectedTimeUs, true, false,
|
|
||||||
null, getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadSampleWithXiphLacing() throws IOException, InterruptedException {
|
|
||||||
byte[] media = createFrameData(300);
|
|
||||||
byte[] data = new StreamBuilder()
|
|
||||||
.setHeader(WEBM_DOC_TYPE)
|
|
||||||
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
|
|
||||||
.addOpusTrack(AUDIO_TRACK_NUMBER, 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);
|
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(data);
|
|
||||||
|
|
||||||
assertTracksEnded(extractorOutput);
|
|
||||||
assertAudioFormat(extractorOutput, AUDIO_TRACK_NUMBER, MimeTypes.AUDIO_OPUS);
|
|
||||||
assertSample(0, Arrays.copyOfRange(media, 0, 256), 0 * TEST_DEFAULT_DURATION_NS / 1000, true,
|
|
||||||
false, null, getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
|
||||||
assertSample(1, Arrays.copyOfRange(media, 256, 257), 1 * TEST_DEFAULT_DURATION_NS / 1000, true,
|
|
||||||
false, null, getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
|
||||||
assertSample(2, Arrays.copyOfRange(media, 257, 300), 2 * TEST_DEFAULT_DURATION_NS / 1000, true,
|
|
||||||
false, null, getTrackOutput(extractorOutput, AUDIO_TRACK_NUMBER));
|
|
||||||
}
|
|
||||||
|
|
||||||
private FakeTrackOutput getTrackOutput(FakeExtractorOutput extractorOutput, int trackNumber) {
|
|
||||||
return extractorOutput.trackOutputs.get(trackNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertTracksEnded(FakeExtractorOutput extractorOutput) {
|
|
||||||
assertTrue(extractorOutput.tracksEnded);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertVp9VideoFormat(FakeExtractorOutput extractorOutput, int trackNumber) {
|
|
||||||
Format format = getTrackOutput(extractorOutput, trackNumber).format;
|
|
||||||
assertEquals(TEST_WIDTH, format.width);
|
|
||||||
assertEquals(TEST_HEIGHT, format.height);
|
|
||||||
assertEquals(MimeTypes.VIDEO_VP9, format.sampleMimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertH264VideoFormat(FakeExtractorOutput extractorOutput, int trackNumber) {
|
|
||||||
Format format = getTrackOutput(extractorOutput, trackNumber).format;
|
|
||||||
assertEquals(TEST_WIDTH, format.width);
|
|
||||||
assertEquals(TEST_HEIGHT, format.height);
|
|
||||||
assertEquals(MimeTypes.VIDEO_H264, format.sampleMimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertAudioFormat(FakeExtractorOutput extractorOutput, int trackNumber,
|
|
||||||
String expectedMimeType) {
|
|
||||||
Format format = getTrackOutput(extractorOutput, trackNumber).format;
|
|
||||||
assertEquals(TEST_CHANNEL_COUNT, format.channelCount);
|
|
||||||
assertEquals(TEST_SAMPLE_RATE, format.sampleRate);
|
|
||||||
assertEquals(expectedMimeType, format.sampleMimeType);
|
|
||||||
if (MimeTypes.AUDIO_OPUS.equals(expectedMimeType)) {
|
|
||||||
assertEquals(3, format.initializationData.size());
|
|
||||||
android.test.MoreAsserts.assertEquals(TEST_OPUS_CODEC_PRIVATE,
|
|
||||||
format.initializationData.get(0));
|
|
||||||
assertEquals(TEST_CODEC_DELAY, ByteBuffer.wrap(format.initializationData.get(1))
|
|
||||||
.order(ByteOrder.nativeOrder()).getLong());
|
|
||||||
assertEquals(TEST_SEEK_PRE_ROLL, ByteBuffer.wrap(format.initializationData.get(2))
|
|
||||||
.order(ByteOrder.nativeOrder()).getLong());
|
|
||||||
} else if (MimeTypes.AUDIO_VORBIS.equals(expectedMimeType)) {
|
|
||||||
assertEquals(2, format.initializationData.size());
|
|
||||||
assertEquals(TEST_VORBIS_INFO_SIZE, format.initializationData.get(0).length);
|
|
||||||
assertEquals(TEST_VORBIS_BOOKS_SIZE, format.initializationData.get(1).length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertDrmInitData(FakeExtractorOutput extractorOutput, int trackNumber) {
|
|
||||||
DrmInitData drmInitData = getTrackOutput(extractorOutput, trackNumber).format.drmInitData;
|
|
||||||
assertNotNull(drmInitData);
|
|
||||||
SchemeData widevineInitData = drmInitData.get(WIDEVINE_UUID);
|
|
||||||
assertEquals(MimeTypes.VIDEO_WEBM, widevineInitData.mimeType);
|
|
||||||
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, widevineInitData.data);
|
|
||||||
SchemeData zeroInitData = drmInitData.get(C.UUID_NIL);
|
|
||||||
assertEquals(MimeTypes.VIDEO_WEBM, zeroInitData.mimeType);
|
|
||||||
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, zeroInitData.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertSeekMap(FakeExtractorOutput extractorOutput, int timecodeScale,
|
|
||||||
int cuePointCount) {
|
|
||||||
ChunkIndex index = (ChunkIndex) extractorOutput.seekMap;
|
|
||||||
assertEquals(cuePointCount, index.length);
|
|
||||||
for (int i = 0; i < cuePointCount - 1; i++) {
|
|
||||||
assertEquals(Util.scaleLargeTimestamp(10 * i, timecodeScale, 1000), index.timesUs[i]);
|
|
||||||
assertEquals(Util.scaleLargeTimestamp(10, timecodeScale, 1000), index.durationsUs[i]);
|
|
||||||
assertEquals(0, index.sizes[i]);
|
|
||||||
}
|
|
||||||
int lastIndex = cuePointCount - 1;
|
|
||||||
long lastTimecode = 10 * lastIndex;
|
|
||||||
long lastDurationTimecode = TEST_DURATION_TIMECODE - lastTimecode;
|
|
||||||
assertEquals(Util.scaleLargeTimestamp(lastTimecode, timecodeScale, 1000),
|
|
||||||
index.timesUs[lastIndex]);
|
|
||||||
assertEquals(Util.scaleLargeTimestamp(lastDurationTimecode, timecodeScale, 1000),
|
|
||||||
index.durationsUs[lastIndex]);
|
|
||||||
assertEquals(Util.scaleLargeTimestamp(TEST_DURATION_TIMECODE, timecodeScale, 1000),
|
|
||||||
extractorOutput.seekMap.getDurationUs());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertSeekMapUnseekable(FakeExtractorOutput extractorOutput, long timecodeScale) {
|
|
||||||
assertFalse(extractorOutput.seekMap.isSeekable());
|
|
||||||
long expectedDurationUs = Util.scaleLargeTimestamp(TEST_DURATION_TIMECODE, timecodeScale, 1000);
|
|
||||||
assertEquals(expectedDurationUs, extractorOutput.seekMap.getDurationUs());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertSample(int index, byte[] expectedMedia, long timeUs, boolean keyframe,
|
|
||||||
boolean invisible, byte[] encryptionKey, FakeTrackOutput output) {
|
|
||||||
if (encryptionKey != null) {
|
|
||||||
expectedMedia = TestUtil.joinByteArrays(
|
|
||||||
new byte[] {(byte) StreamBuilder.TEST_INITIALIZATION_VECTOR.length},
|
|
||||||
StreamBuilder.TEST_INITIALIZATION_VECTOR, expectedMedia);
|
|
||||||
}
|
|
||||||
int flags = 0;
|
|
||||||
flags |= keyframe ? C.BUFFER_FLAG_KEY_FRAME : 0;
|
|
||||||
flags |= invisible ? C.BUFFER_FLAG_DECODE_ONLY : 0;
|
|
||||||
flags |= encryptionKey != null ? C.BUFFER_FLAG_ENCRYPTED : 0;
|
|
||||||
output.assertSample(index, expectedMedia, timeUs, flags, encryptionKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] getVorbisCodecPrivate() throws IOException {
|
|
||||||
return TestUtil.getByteArray(getInstrumentation(), TEST_VORBIS_CODEC_PRIVATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] createFrameData(int size) {
|
|
||||||
byte[] data = new byte[size];
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
data[i] = (byte) i;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMkvSample() throws Exception {
|
public void testMkvSample() throws Exception {
|
||||||
TestUtil.assertOutput(new TestUtil.ExtractorFactory() {
|
TestUtil.assertOutput(new TestUtil.ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
@ -805,4 +33,5 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}, "mkv/sample.mkv", getInstrumentation());
|
}, "mkv/sample.mkv", getInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,561 +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.extractor.mkv;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer.testutil.TestUtil;
|
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides byte arrays containing Matroska data for {@link MatroskaExtractorTest}.
|
|
||||||
*/
|
|
||||||
/* package */ final class StreamBuilder {
|
|
||||||
|
|
||||||
/** Used by {@link #addVp9Track} to create a track header with encryption/compression. */
|
|
||||||
public static final class ContentEncodingSettings {
|
|
||||||
|
|
||||||
private final int order;
|
|
||||||
private final int scope;
|
|
||||||
private final int type;
|
|
||||||
private final int algorithm;
|
|
||||||
private final int aesCipherMode;
|
|
||||||
private final byte[] strippedBytes;
|
|
||||||
|
|
||||||
public ContentEncodingSettings(int order, int scope, int algorithm, int aesCipherMode) {
|
|
||||||
this.order = order;
|
|
||||||
this.scope = scope;
|
|
||||||
this.type = 1; // Encryption
|
|
||||||
this.algorithm = algorithm;
|
|
||||||
this.aesCipherMode = aesCipherMode;
|
|
||||||
this.strippedBytes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContentEncodingSettings(int order, int scope, int algorithm, byte[] strippedBytes) {
|
|
||||||
this.order = order;
|
|
||||||
this.scope = scope;
|
|
||||||
this.type = 0; // Compression
|
|
||||||
this.algorithm = algorithm;
|
|
||||||
this.aesCipherMode = 0;
|
|
||||||
this.strippedBytes = strippedBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final int NO_VALUE = -1;
|
|
||||||
|
|
||||||
private final List<EbmlElement> trackEntries;
|
|
||||||
private final List<EbmlElement> mediaSegments;
|
|
||||||
|
|
||||||
private EbmlElement header;
|
|
||||||
private EbmlElement info;
|
|
||||||
|
|
||||||
public StreamBuilder() {
|
|
||||||
trackEntries = new LinkedList<>();
|
|
||||||
mediaSegments = new LinkedList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder setHeader(String docType) {
|
|
||||||
header = createEbmlElement(1, docType, 2);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder setInfo(int timecodeScale, long durationTimecode) {
|
|
||||||
return setInfo(timecodeScale, durationTimecode, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder setInfo(int timecodeScale, long durationTimecode,
|
|
||||||
boolean omitTimecodeScaleIfDefault, boolean durationFirst) {
|
|
||||||
info = createInfoElement(timecodeScale, durationTimecode, omitTimecodeScaleIfDefault,
|
|
||||||
durationFirst);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder addVp9Track(byte trackNumber, int width, int height,
|
|
||||||
ContentEncodingSettings contentEncodingSettings) {
|
|
||||||
trackEntries.add(createVideoTrackEntry(trackNumber, "V_VP9", width, height,
|
|
||||||
contentEncodingSettings, null));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder addH264Track(byte trackNumber, int width, int height, byte[] codecPrivate) {
|
|
||||||
trackEntries.add(createVideoTrackEntry(trackNumber, "V_MPEG4/ISO/AVC", width, height, null,
|
|
||||||
codecPrivate));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder addOpusTrack(byte trackNumber, int channelCount, int sampleRate,
|
|
||||||
int codecDelay, int seekPreRoll, byte[] codecPrivate) {
|
|
||||||
trackEntries.add(createAudioTrackEntry(trackNumber, "A_OPUS", channelCount, sampleRate,
|
|
||||||
codecPrivate, codecDelay, seekPreRoll, NO_VALUE));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder addOpusTrack(byte trackNumber, int channelCount, int sampleRate,
|
|
||||||
int codecDelay, int seekPreRoll, byte[] codecPrivate, int defaultDurationNs) {
|
|
||||||
trackEntries.add(createAudioTrackEntry(trackNumber, "A_OPUS", channelCount, sampleRate,
|
|
||||||
codecPrivate, codecDelay, seekPreRoll, defaultDurationNs));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder addVorbisTrack(byte trackNumber, int channelCount, int sampleRate,
|
|
||||||
byte[] codecPrivate) {
|
|
||||||
trackEntries.add(createAudioTrackEntry(trackNumber, "A_VORBIS", channelCount, sampleRate,
|
|
||||||
codecPrivate, NO_VALUE, NO_VALUE, NO_VALUE));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder addUnsupportedTrack(byte trackNumber) {
|
|
||||||
trackEntries.add(element(0xAE, // TrackEntry
|
|
||||||
element(0x86, "D_WEBVTT/metadata".getBytes()), // CodecID
|
|
||||||
element(0xD7, trackNumber), // TrackNumber
|
|
||||||
element(0x83, (byte) 0x11))); // TrackType
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder addSimpleBlockEncryptedMedia(int trackNumber, int clusterTimecode,
|
|
||||||
int blockTimecode, boolean keyframe, boolean invisible, boolean validSignalByte,
|
|
||||||
byte[] data) {
|
|
||||||
int flags = (keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00);
|
|
||||||
EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode, flags, true,
|
|
||||||
validSignalByte, 1, data);
|
|
||||||
mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public StreamBuilder addSimpleBlockMedia(int trackNumber, int clusterTimecode,
|
|
||||||
int blockTimecode, boolean keyframe, boolean invisible, byte[] data) {
|
|
||||||
int flags = (keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00);
|
|
||||||
EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode, flags, false,
|
|
||||||
true, 1, data);
|
|
||||||
mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamBuilder addSimpleBlockMediaWithFixedSizeLacing(int trackNumber, int clusterTimecode,
|
|
||||||
int blockTimecode, int lacingFrameCount, byte[] data) {
|
|
||||||
EbmlElement simpleBlockElement = createSimpleBlock(trackNumber, blockTimecode,
|
|
||||||
0x80 /* flags = keyframe */, false, true, lacingFrameCount, data);
|
|
||||||
mediaSegments.add(createCluster(clusterTimecode, simpleBlockElement));
|
|
||||||
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);
|
|
||||||
EbmlElement blockElement =
|
|
||||||
createBlock(trackNumber, blockTimecode, keyframe, flags, data);
|
|
||||||
mediaSegments.add(createCluster(clusterTimecode, blockElement));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes the constructed stream to a {@code byte[]} using the specified number of cue points.
|
|
||||||
*/
|
|
||||||
public byte[] build(int cuePointCount) {
|
|
||||||
Assertions.checkNotNull(header);
|
|
||||||
Assertions.checkNotNull(info);
|
|
||||||
|
|
||||||
EbmlElement tracks = element(0x1654AE6B,
|
|
||||||
trackEntries.toArray(new EbmlElement[trackEntries.size()]));
|
|
||||||
EbmlElement[] children;
|
|
||||||
|
|
||||||
if (cuePointCount == 0) {
|
|
||||||
children = new EbmlElement[2 + mediaSegments.size()];
|
|
||||||
System.arraycopy(mediaSegments.toArray(), 0, children, 2, mediaSegments.size());
|
|
||||||
children[0] = info;
|
|
||||||
children[1] = tracks;
|
|
||||||
} else {
|
|
||||||
// Get the size of the initialization segment.
|
|
||||||
EbmlElement[] cuePointElements = new EbmlElement[cuePointCount];
|
|
||||||
for (int i = 0; i < cuePointCount; i++) {
|
|
||||||
cuePointElements[i] = createCuePointElement(10 * i, 0);
|
|
||||||
}
|
|
||||||
EbmlElement cues = element(0x1C53BB6B, cuePointElements); // Cues
|
|
||||||
long initializationSegmentSize = info.getSize() + tracks.getSize() + cues.getSize();
|
|
||||||
|
|
||||||
// Recreate the initialization segment using its size as an offset.
|
|
||||||
for (int i = 0; i < cuePointCount; i++) {
|
|
||||||
cuePointElements[i] = createCuePointElement(10 * i, (int) initializationSegmentSize);
|
|
||||||
}
|
|
||||||
cues = element(0x1C53BB6B, cuePointElements); // Cues
|
|
||||||
|
|
||||||
// Build the top-level segment element.
|
|
||||||
children = new EbmlElement[3 + mediaSegments.size()];
|
|
||||||
System.arraycopy(mediaSegments.toArray(), 0, children, 3, mediaSegments.size());
|
|
||||||
children[0] = info;
|
|
||||||
children[1] = tracks;
|
|
||||||
children[2] = cues;
|
|
||||||
}
|
|
||||||
|
|
||||||
EbmlElement segmentElement = element(0x18538067, children); // Segment
|
|
||||||
|
|
||||||
// Serialize the EBML header and the top-level segment element.
|
|
||||||
return EbmlElement.serialize(header, segmentElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static EbmlElement createCuePointElement(int cueTime, int cueClusterPosition) {
|
|
||||||
byte[] positionBytes = getLongBytes(cueClusterPosition);
|
|
||||||
return element(0xBB, // CuePoint
|
|
||||||
element(0xB3, (byte) (cueTime & 0xFF)), // CueTime
|
|
||||||
element(0xB7, // CueTrackPositions
|
|
||||||
element(0xF1, positionBytes))); // CueClusterPosition
|
|
||||||
}
|
|
||||||
|
|
||||||
private static EbmlElement createEbmlElement(int ebmlReadVersion, String docType,
|
|
||||||
int docTypeReadVersion) {
|
|
||||||
return element(0x1A45DFA3, // EBML
|
|
||||||
element(0x42F7, (byte) (ebmlReadVersion & 0xFF)), // EBMLReadVersion
|
|
||||||
element(0x4282, docType.getBytes()), // DocType
|
|
||||||
element(0x4285, (byte) (docTypeReadVersion & 0xFF))); // DocTypeReadVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
private EbmlElement createInfoElement(int timecodeScale, long durationTimecode,
|
|
||||||
boolean durationFirst, boolean omitDefaultTimecodeScale) {
|
|
||||||
byte[] timecodeScaleBytes = getIntegerBytes(timecodeScale);
|
|
||||||
byte[] durationBytes = getLongBytes(Double.doubleToLongBits(durationTimecode));
|
|
||||||
EbmlElement durationElement = element(0x4489, durationBytes);
|
|
||||||
EbmlElement timescaleElement = element(0x2AD7B1, timecodeScaleBytes);
|
|
||||||
if (omitDefaultTimecodeScale && timecodeScale == 1000000) {
|
|
||||||
return element(0x1549A966, // Info
|
|
||||||
durationElement);
|
|
||||||
}
|
|
||||||
return element(0x1549A966, // Info
|
|
||||||
durationFirst ? durationElement : timescaleElement,
|
|
||||||
durationFirst ? timescaleElement : durationElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static EbmlElement createVideoTrackEntry(byte trackNumber, String codecId, int pixelWidth,
|
|
||||||
int pixelHeight, ContentEncodingSettings contentEncodingSettings, byte[] codecPrivate) {
|
|
||||||
byte[] widthBytes = getIntegerBytes(pixelWidth);
|
|
||||||
byte[] heightBytes = getIntegerBytes(pixelHeight);
|
|
||||||
EbmlElement contentEncodingSettingsElement;
|
|
||||||
if (contentEncodingSettings != null) {
|
|
||||||
EbmlElement encryptionOrCompressionElement;
|
|
||||||
if (contentEncodingSettings.type == 0) {
|
|
||||||
encryptionOrCompressionElement = element(0x5034, // ContentCompression
|
|
||||||
element(0x4254, (byte) (contentEncodingSettings.algorithm & 0xFF)), // ContentCompAlgo
|
|
||||||
element(0x4255, contentEncodingSettings.strippedBytes)); // ContentCompSettings
|
|
||||||
} else if (contentEncodingSettings.type == 1) {
|
|
||||||
encryptionOrCompressionElement = element(0x5035, // ContentEncryption
|
|
||||||
// ContentEncAlgo
|
|
||||||
element(0x47E1, (byte) (contentEncodingSettings.algorithm & 0xFF)),
|
|
||||||
element(0x47E2, TEST_ENCRYPTION_KEY_ID), // ContentEncKeyID
|
|
||||||
element(0x47E7, // ContentEncAESSettings
|
|
||||||
// AESSettingsCipherMode
|
|
||||||
element(0x47E8, (byte) (contentEncodingSettings.aesCipherMode & 0xFF))));
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Unexpected encoding type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
contentEncodingSettingsElement =
|
|
||||||
element(0x6D80, // ContentEncodings
|
|
||||||
element(0x6240, // ContentEncoding
|
|
||||||
// ContentEncodingOrder
|
|
||||||
element(0x5031, (byte) (contentEncodingSettings.order & 0xFF)),
|
|
||||||
// ContentEncodingScope
|
|
||||||
element(0x5032, (byte) (contentEncodingSettings.scope & 0xFF)),
|
|
||||||
// ContentEncodingType
|
|
||||||
element(0x5033, (byte) (contentEncodingSettings.type & 0xFF)),
|
|
||||||
encryptionOrCompressionElement));
|
|
||||||
} else {
|
|
||||||
contentEncodingSettingsElement = empty();
|
|
||||||
}
|
|
||||||
EbmlElement codecPrivateElement;
|
|
||||||
if (codecPrivate != null) {
|
|
||||||
codecPrivateElement = element(0x63A2, codecPrivate); // CodecPrivate
|
|
||||||
} else {
|
|
||||||
codecPrivateElement = empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
return element(0xAE, // TrackEntry
|
|
||||||
element(0x86, codecId.getBytes()), // CodecID
|
|
||||||
element(0xD7, trackNumber), // TrackNumber
|
|
||||||
element(0x83, (byte) 0x01), // TrackType
|
|
||||||
contentEncodingSettingsElement,
|
|
||||||
element(0xE0, // Video
|
|
||||||
element(0xB0, widthBytes[2], widthBytes[3]),
|
|
||||||
element(0xBA, heightBytes[2], heightBytes[3])),
|
|
||||||
codecPrivateElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static EbmlElement createAudioTrackEntry(byte trackNumber, String codecId,
|
|
||||||
int channelCount, int sampleRate, byte[] codecPrivate, int codecDelay, int seekPreRoll,
|
|
||||||
int defaultDurationNs) {
|
|
||||||
byte channelCountByte = (byte) (channelCount & 0xFF);
|
|
||||||
byte[] sampleRateDoubleBytes = getLongBytes(Double.doubleToLongBits(sampleRate));
|
|
||||||
return element(0xAE, // TrackEntry
|
|
||||||
element(0x86, codecId.getBytes()), // CodecID
|
|
||||||
element(0xD7, trackNumber), // TrackNumber
|
|
||||||
element(0x83, (byte) 0x02), // TrackType
|
|
||||||
// CodecDelay
|
|
||||||
codecDelay != NO_VALUE ? element(0x56AA, getIntegerBytes(codecDelay)) : empty(),
|
|
||||||
// SeekPreRoll
|
|
||||||
seekPreRoll != NO_VALUE ? element(0x56BB, getIntegerBytes(seekPreRoll)) : empty(),
|
|
||||||
element(0xE1, // Audio
|
|
||||||
element(0x9F, channelCountByte), // Channels
|
|
||||||
element(0xB5, sampleRateDoubleBytes)), // SamplingFrequency
|
|
||||||
// DefaultDuration
|
|
||||||
defaultDurationNs != NO_VALUE ? element(0x23E383, getIntegerBytes(defaultDurationNs))
|
|
||||||
: empty(),
|
|
||||||
element(0x63A2, codecPrivate)); // CodecPrivate
|
|
||||||
}
|
|
||||||
|
|
||||||
private static EbmlElement createCluster(int timecode, EbmlElement blockGroupOrSimpleBlock) {
|
|
||||||
return element(0x1F43B675, // Cluster
|
|
||||||
element(0xE7, getIntegerBytes(timecode)), // Timecode
|
|
||||||
blockGroupOrSimpleBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static EbmlElement createSimpleBlock(int trackNumber, int timecode, int flags,
|
|
||||||
boolean encrypted, boolean validSignalByte, int lacingFrameCount, byte[] data) {
|
|
||||||
byte[] trackNumberBytes = getIntegerBytes(trackNumber);
|
|
||||||
byte[] timeBytes = getIntegerBytes(timecode);
|
|
||||||
byte[] simpleBlockBytes;
|
|
||||||
if (lacingFrameCount > 1) {
|
|
||||||
flags |= 0x04; // Fixed-size lacing
|
|
||||||
simpleBlockBytes = TestUtil.joinByteArrays(
|
|
||||||
new byte[] {0x40, trackNumberBytes[3], timeBytes[2], timeBytes[3]},
|
|
||||||
TestUtil.createByteArray(flags, lacingFrameCount - 1));
|
|
||||||
} else {
|
|
||||||
simpleBlockBytes = TestUtil.joinByteArrays(
|
|
||||||
new byte[] {0x40, trackNumberBytes[3], timeBytes[2], timeBytes[3]},
|
|
||||||
TestUtil.createByteArray(flags));
|
|
||||||
}
|
|
||||||
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 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 = TestUtil.createByteArray(
|
|
||||||
0x40, trackNumberBytes[3], // Track number size=2
|
|
||||||
timeBytes[2], timeBytes[3], flags); // Timecode and flags
|
|
||||||
EbmlElement block = element(0xA1, // Block
|
|
||||||
TestUtil.joinByteArrays(blockBytes, data));
|
|
||||||
EbmlElement referenceBlock = keyframe ? empty() : element(0xFB, (byte) 0x00); // ReferenceBlock
|
|
||||||
return element(0xA0, // BlockGroup
|
|
||||||
referenceBlock,
|
|
||||||
block);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getIntegerBytes(int value) {
|
|
||||||
return TestUtil.createByteArray(
|
|
||||||
(value >> 24) & 0xFF,
|
|
||||||
(value >> 16) & 0xFF,
|
|
||||||
(value >> 8) & 0xFF,
|
|
||||||
(value) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getLongBytes(long value) {
|
|
||||||
byte[] result = new byte[8];
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
result[7 - i] = (byte) ((value >> (8 * i)) & 0xFF);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see EbmlElement#EbmlElement(int, EbmlElement...) */
|
|
||||||
private static EbmlElement element(int type, EbmlElement... childElements) {
|
|
||||||
return new EbmlElement(type, childElements);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see EbmlElement#EbmlElement(int, byte...) */
|
|
||||||
private static EbmlElement element(int type, byte... payload) {
|
|
||||||
return new EbmlElement(type, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see EbmlElement#EbmlElement() */
|
|
||||||
private static EbmlElement empty() {
|
|
||||||
return new EbmlElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Represents an EBML element that can be serialized as a byte array. */
|
|
||||||
private static final class EbmlElement {
|
|
||||||
|
|
||||||
/** Returns a byte[] containing the concatenation of the data from all {@code elements}. */
|
|
||||||
public static byte[] serialize(EbmlElement... elements) {
|
|
||||||
int size = 0;
|
|
||||||
for (EbmlElement element : elements) {
|
|
||||||
size += element.getSize();
|
|
||||||
}
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(size);
|
|
||||||
for (EbmlElement element : elements) {
|
|
||||||
element.getData(buffer);
|
|
||||||
}
|
|
||||||
return buffer.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int id;
|
|
||||||
private final EbmlElement[] childElements;
|
|
||||||
private final byte[] payload;
|
|
||||||
|
|
||||||
/** Creates an element containing the specified {@code childElements}. */
|
|
||||||
EbmlElement(int id, EbmlElement... childElements) {
|
|
||||||
this.id = id;
|
|
||||||
this.childElements = childElements;
|
|
||||||
payload = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates an element containing the specified {@code payload}. */
|
|
||||||
EbmlElement(int id, byte... payload) {
|
|
||||||
this.id = id;
|
|
||||||
this.payload = payload;
|
|
||||||
childElements = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates a completely empty element that will contribute no bytes to the output. */
|
|
||||||
EbmlElement() {
|
|
||||||
id = NO_VALUE;
|
|
||||||
payload = null;
|
|
||||||
childElements = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getSize() {
|
|
||||||
if (id == NO_VALUE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
long payloadSize = getPayloadSize();
|
|
||||||
return getIdLength() + getVIntLength(payloadSize) + payloadSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getPayloadSize() {
|
|
||||||
if (payload != null) {
|
|
||||||
return payload.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int payloadSize = 0;
|
|
||||||
for (EbmlElement element : childElements) {
|
|
||||||
payloadSize += element.getSize();
|
|
||||||
}
|
|
||||||
return payloadSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getData(ByteBuffer byteBuffer) {
|
|
||||||
if (id == NO_VALUE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
putId(byteBuffer);
|
|
||||||
putVInt(byteBuffer, getPayloadSize());
|
|
||||||
if (payload != null) {
|
|
||||||
byteBuffer.put(payload);
|
|
||||||
} else {
|
|
||||||
for (EbmlElement atom : childElements) {
|
|
||||||
atom.getData(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getIdLength() {
|
|
||||||
if (id == NO_VALUE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
if (id < 1 << (7 * i + 8)) {
|
|
||||||
return i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getVIntLength(long vInt) {
|
|
||||||
for (int i = 1; i < 9; i++) {
|
|
||||||
if (vInt < 1 << (7 * i)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void putId(ByteBuffer byteBuffer) {
|
|
||||||
int length = getIdLength();
|
|
||||||
for (int i = length - 1; i >= 0; i--) {
|
|
||||||
byteBuffer.put((byte) ((id >> (i * 8)) & 0xFF));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void putVInt(ByteBuffer byteBuffer, long vInt) {
|
|
||||||
int vIntLength = getVIntLength(vInt);
|
|
||||||
vInt |= 1 << (vIntLength * 7);
|
|
||||||
for (int i = vIntLength - 1; i >= 0; i--) {
|
|
||||||
byteBuffer.put((byte) ((vInt >> (i * 8)) & 0xFF));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -15,488 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.extractor.mp4;
|
package com.google.android.exoplayer.extractor.mp4;
|
||||||
|
|
||||||
import com.google.android.exoplayer.C;
|
|
||||||
import com.google.android.exoplayer.Format;
|
|
||||||
import com.google.android.exoplayer.extractor.Extractor;
|
import com.google.android.exoplayer.extractor.Extractor;
|
||||||
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.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
|
||||||
import com.google.android.exoplayer.util.Util;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.test.InstrumentationTestCase;
|
import android.test.InstrumentationTestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link Mp4Extractor}.
|
* Tests for {@link Mp4Extractor}.
|
||||||
*/
|
*/
|
||||||
@TargetApi(16)
|
@TargetApi(16)
|
||||||
public final class Mp4ExtractorTest extends InstrumentationTestCase {
|
public final class Mp4ExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
/** String of hexadecimal bytes containing the video stsd payload from an AVC video. */
|
|
||||||
private static final byte[] VIDEO_STSD_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"00000000000000010000009961766331000000000000000100000000000000000000000000000000050002d00048"
|
|
||||||
+ "000000480000000000000001000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
+ "18ffff0000002f617663430164001fffe100186764001facb402802dd80880000003008000001e078c19500100"
|
|
||||||
+ "0468ee3cb000000014627472740000e35c0042a61000216cb8");
|
|
||||||
private static final byte[] VIDEO_HDLR_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"000000000000000076696465");
|
|
||||||
private static final byte[] VIDEO_MDHD_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"0000000000000000cf6c48890000001e00001c8a55c40000");
|
|
||||||
private static final int TIMESCALE = 30;
|
|
||||||
private static final int VIDEO_WIDTH = 1280;
|
|
||||||
private static final int VIDEO_HEIGHT = 720;
|
|
||||||
|
|
||||||
/** String of hexadecimal bytes containing the video stsd payload for an mp4v track. */
|
|
||||||
private static final byte[] VIDEO_STSD_MP4V_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"0000000000000001000000A36D703476000000000000000100000000000000000000000000000000014000B40048"
|
|
||||||
+ "000000480000000000000001000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
+ "18FFFF0000004D6573647300000000033F00000004372011001A400004CF280002F1180528000001B001000001"
|
|
||||||
+ "B58913000001000000012000C48D8800F50A04169463000001B2476F6F676C65060102");
|
|
||||||
private static final int VIDEO_MP4V_WIDTH = 320;
|
|
||||||
private static final int VIDEO_MP4V_HEIGHT = 180;
|
|
||||||
|
|
||||||
/** String of hexadecimal bytes containing the audio stsd payload from an AAC track. */
|
|
||||||
private static final byte[] AUDIO_STSD_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"0000000000000001000000596d703461000000000000000100000000000000000001001000000000ac4400000000"
|
|
||||||
+ "003565736473000000000327000000041f401500023e00024bc000023280051012080000000000000000000000"
|
|
||||||
+ "000000060102");
|
|
||||||
private static final byte[] AUDIO_HDLR_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"0000000000000000736f756e");
|
|
||||||
private static final byte[] AUDIO_MDHD_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"00000000cf6c4889cf6c488a0000ac4400a3e40055c40000");
|
|
||||||
|
|
||||||
/** String of hexadecimal bytes for an ftyp payload with major_brand mp41 and minor_version 0. **/
|
|
||||||
private static final byte[] FTYP_PAYLOAD = Util.getBytesFromHexString("6d70343100000000");
|
|
||||||
|
|
||||||
/** String of hexadecimal bytes containing an mvhd payload from an AVC/AAC video. */
|
|
||||||
private static final byte[] MVHD_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"00000000cf6c4888cf6c48880000025800023ad40001000001000000000000000000000000010000000000000000"
|
|
||||||
+ "000000000000000100000000000000000000000000004000000000000000000000000000000000000000000000"
|
|
||||||
+ "000000000000000003");
|
|
||||||
|
|
||||||
/** String of hexadecimal bytes containing a tkhd payload with an unknown duration. */
|
|
||||||
private static final byte[] TKHD_PAYLOAD = Util.getBytesFromHexString(
|
|
||||||
"00000007D1F0C7BFD1F0C7BF0000000000000000FFFFFFFF00000000000000000000000000000000000100000000"
|
|
||||||
+ "0000000000000000000000010000000000000000000000000000400000000780000004380000");
|
|
||||||
|
|
||||||
/** Video frame timestamps in time units. */
|
|
||||||
private static final int[] SAMPLE_TIMESTAMPS = {0, 2, 3, 5, 6, 7};
|
|
||||||
/** Video frame sizes in bytes, including a very large sample. */
|
|
||||||
private static final int[] SAMPLE_SIZES = {100, 20, 20, 44, 100, 1024 * 1024};
|
|
||||||
/** Indices of key-frames. */
|
|
||||||
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 = {1208, 2128, 3128, 4128};
|
|
||||||
/** 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;
|
|
||||||
/** Empty byte array. */
|
|
||||||
private static final byte[] EMPTY = new byte[0];
|
|
||||||
|
|
||||||
public void testParsesValidMp4File() throws Exception {
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(true, false);
|
|
||||||
|
|
||||||
// The seek map is correct.
|
|
||||||
assertSeekMap(extractorOutput.seekMap, true);
|
|
||||||
|
|
||||||
// The video and audio formats are set correctly.
|
|
||||||
assertEquals(2, extractorOutput.trackOutputs.size());
|
|
||||||
Format videoFormat = extractorOutput.trackOutputs.get(0).format;
|
|
||||||
Format audioFormat = extractorOutput.trackOutputs.get(1).format;
|
|
||||||
assertEquals(MimeTypes.VIDEO_H264, videoFormat.sampleMimeType);
|
|
||||||
assertEquals(VIDEO_WIDTH, videoFormat.width);
|
|
||||||
assertEquals(VIDEO_HEIGHT, videoFormat.height);
|
|
||||||
assertEquals(MimeTypes.AUDIO_AAC, audioFormat.sampleMimeType);
|
|
||||||
|
|
||||||
// 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.BUFFER_FLAG_KEY_FRAME : 0;
|
|
||||||
long sampleTimestampUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]);
|
|
||||||
videoTrackOutput.assertSample(i, sampleData, sampleTimestampUs, sampleFlags, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testParsesValidMp4FileWithoutStss() throws Exception {
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(false, false);
|
|
||||||
|
|
||||||
// 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.BUFFER_FLAG_KEY_FRAME;
|
|
||||||
long sampleTimestampUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]);
|
|
||||||
videoTrackOutput.assertSample(i, sampleData, sampleTimestampUs, sampleFlags, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testParsesValidMp4vFile() throws Exception {
|
|
||||||
FakeExtractorOutput extractorOutput = consumeTestData(true, true);
|
|
||||||
|
|
||||||
// The seek map is correct.
|
|
||||||
assertSeekMap(extractorOutput.seekMap, true);
|
|
||||||
|
|
||||||
// The video and audio formats are set correctly.
|
|
||||||
assertEquals(2, extractorOutput.trackOutputs.size());
|
|
||||||
Format videoFormat = extractorOutput.trackOutputs.get(0).format;
|
|
||||||
Format audioFormat = extractorOutput.trackOutputs.get(1).format;
|
|
||||||
assertEquals(MimeTypes.VIDEO_MP4V, videoFormat.sampleMimeType);
|
|
||||||
assertEquals(VIDEO_MP4V_WIDTH, videoFormat.width);
|
|
||||||
assertEquals(VIDEO_MP4V_HEIGHT, videoFormat.height);
|
|
||||||
assertEquals(MimeTypes.AUDIO_AAC, audioFormat.sampleMimeType);
|
|
||||||
|
|
||||||
// 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, false);
|
|
||||||
int sampleFlags = SAMPLE_IS_SYNC[i] ? C.BUFFER_FLAG_KEY_FRAME : 0;
|
|
||||||
long sampleTimestampUs = getVideoTimestampUs(SAMPLE_TIMESTAMPS[i]);
|
|
||||||
videoTrackOutput.assertSample(i, sampleData, sampleTimestampUs, sampleFlags, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a video timestamp in microseconds corresponding to {@code timeUnits}. */
|
|
||||||
private static long getVideoTimestampUs(int timeUnits) {
|
|
||||||
return Util.scaleLargeTimestamp(timeUnits, C.MICROS_PER_SECOND, TIMESCALE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getStco() {
|
|
||||||
byte[] result = new byte[4 + 4 + 4 * CHUNK_OFFSETS.length];
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(result);
|
|
||||||
buffer.putInt(0); // Version (skipped)
|
|
||||||
buffer.putInt(CHUNK_OFFSETS.length);
|
|
||||||
for (int chunkOffset : CHUNK_OFFSETS) {
|
|
||||||
buffer.putInt(chunkOffset);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getStsc() {
|
|
||||||
int samplesPerChunk = -1;
|
|
||||||
List<Integer> samplesInChunkChangeIndices = new ArrayList<>();
|
|
||||||
for (int i = 0; i < SAMPLES_IN_CHUNK.length; i++) {
|
|
||||||
if (SAMPLES_IN_CHUNK[i] != samplesPerChunk) {
|
|
||||||
samplesInChunkChangeIndices.add(i);
|
|
||||||
samplesPerChunk = SAMPLES_IN_CHUNK[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] result = new byte[4 + 4 + 3 * 4 * samplesInChunkChangeIndices.size()];
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(result);
|
|
||||||
buffer.putInt(0); // Version (skipped)
|
|
||||||
buffer.putInt(samplesInChunkChangeIndices.size());
|
|
||||||
for (int index : samplesInChunkChangeIndices) {
|
|
||||||
buffer.putInt(index + 1);
|
|
||||||
buffer.putInt(SAMPLES_IN_CHUNK[index]);
|
|
||||||
buffer.putInt(0); // Sample description index (skipped)
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getStsz() {
|
|
||||||
byte[] result = new byte[4 + 4 + 4 + 4 * SAMPLE_SIZES.length];
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(result);
|
|
||||||
buffer.putInt(0); // Version (skipped)
|
|
||||||
buffer.putInt(0); // No fixed sample size.
|
|
||||||
buffer.putInt(SAMPLE_SIZES.length);
|
|
||||||
for (int size : SAMPLE_SIZES) {
|
|
||||||
buffer.putInt(size);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getStss() {
|
|
||||||
int synchronizationSampleCount = 0;
|
|
||||||
for (boolean sampleIsSync : SAMPLE_IS_SYNC) {
|
|
||||||
if (sampleIsSync) {
|
|
||||||
synchronizationSampleCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
byte[] result = new byte[4 + 4 + 4 * synchronizationSampleCount];
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(result);
|
|
||||||
buffer.putInt(0); // Version (skipped)
|
|
||||||
buffer.putInt(synchronizationSampleCount);
|
|
||||||
for (int i = 0; i < SAMPLE_IS_SYNC.length; i++) {
|
|
||||||
if (SAMPLE_IS_SYNC[i]) {
|
|
||||||
buffer.putInt(i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getStts() {
|
|
||||||
int sampleTimestampDeltaChanges = 0;
|
|
||||||
int currentSampleTimestampDelta = -1;
|
|
||||||
for (int i = 1; i < SAMPLE_TIMESTAMPS.length; i++) {
|
|
||||||
int timestampDelta = SAMPLE_TIMESTAMPS[i] - SAMPLE_TIMESTAMPS[i - 1];
|
|
||||||
if (timestampDelta != currentSampleTimestampDelta) {
|
|
||||||
sampleTimestampDeltaChanges++;
|
|
||||||
currentSampleTimestampDelta = timestampDelta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] result = new byte[4 + 4 + 2 * 4 * sampleTimestampDeltaChanges];
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(result);
|
|
||||||
buffer.putInt(0); // Version (skipped);
|
|
||||||
buffer.putInt(sampleTimestampDeltaChanges);
|
|
||||||
int lastTimestampDeltaChangeIndex = 1;
|
|
||||||
currentSampleTimestampDelta = SAMPLE_TIMESTAMPS[1] - SAMPLE_TIMESTAMPS[0];
|
|
||||||
for (int i = 2; i < SAMPLE_TIMESTAMPS.length; i++) {
|
|
||||||
int timestampDelta = SAMPLE_TIMESTAMPS[i] - SAMPLE_TIMESTAMPS[i - 1];
|
|
||||||
if (timestampDelta != currentSampleTimestampDelta) {
|
|
||||||
buffer.putInt(i - lastTimestampDeltaChangeIndex);
|
|
||||||
lastTimestampDeltaChangeIndex = i;
|
|
||||||
buffer.putInt(currentSampleTimestampDelta);
|
|
||||||
currentSampleTimestampDelta = timestampDelta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The last sample also has a duration, so the number of entries is the number of samples.
|
|
||||||
buffer.putInt(SAMPLE_TIMESTAMPS.length - lastTimestampDeltaChangeIndex + 1);
|
|
||||||
buffer.putInt(currentSampleTimestampDelta);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
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++) {
|
|
||||||
mdat.position(CHUNK_OFFSETS[chunk] - mdatOffset);
|
|
||||||
for (int sample = 0; sample < SAMPLES_IN_CHUNK[chunk]; sample++) {
|
|
||||||
mdat.put(getInputSampleData(sampleIndex++, isH264));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mdat.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 FakeExtractorOutput consumeTestData(boolean includeStss, boolean mp4vFormat)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
byte[] testInputData = includeStss ? getTestMp4File(mp4vFormat)
|
|
||||||
: getTestMp4FileWithoutSynchronizationData(mp4vFormat);
|
|
||||||
return TestUtil.consumeTestData(new Mp4Extractor(), testInputData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a valid MP4 file with audio/video tracks and synchronization data. */
|
|
||||||
private static byte[] getTestMp4File(boolean mp4vFormat) {
|
|
||||||
return Mp4Atom.serialize(
|
|
||||||
atom(Atom.TYPE_ftyp, FTYP_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_moov,
|
|
||||||
atom(Atom.TYPE_mvhd, MVHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_trak,
|
|
||||||
atom(Atom.TYPE_tkhd, TKHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_mdia,
|
|
||||||
atom(Atom.TYPE_mdhd, VIDEO_MDHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_hdlr, VIDEO_HDLR_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_minf,
|
|
||||||
atom(Atom.TYPE_vmhd, EMPTY),
|
|
||||||
atom(Atom.TYPE_stbl,
|
|
||||||
atom(Atom.TYPE_stsd,
|
|
||||||
mp4vFormat ? VIDEO_STSD_MP4V_PAYLOAD : VIDEO_STSD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_stts, getStts()),
|
|
||||||
atom(Atom.TYPE_stss, getStss()),
|
|
||||||
atom(Atom.TYPE_stsc, getStsc()),
|
|
||||||
atom(Atom.TYPE_stsz, getStsz()),
|
|
||||||
atom(Atom.TYPE_stco, getStco()))))),
|
|
||||||
atom(Atom.TYPE_trak,
|
|
||||||
atom(Atom.TYPE_tkhd, TKHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_mdia,
|
|
||||||
atom(Atom.TYPE_mdhd, AUDIO_MDHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_hdlr, AUDIO_HDLR_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_minf,
|
|
||||||
atom(Atom.TYPE_vmhd, EMPTY),
|
|
||||||
atom(Atom.TYPE_stbl,
|
|
||||||
atom(Atom.TYPE_stsd, AUDIO_STSD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_stts, getStts()),
|
|
||||||
atom(Atom.TYPE_stss, getStss()),
|
|
||||||
atom(Atom.TYPE_stsc, getStsc()),
|
|
||||||
atom(Atom.TYPE_stsz, getStsz()),
|
|
||||||
atom(Atom.TYPE_stco, getStco())))))),
|
|
||||||
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 1176 : 1166, !mp4vFormat)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a valid MP4 file with audio/video tracks and without a synchronization table. */
|
|
||||||
private static byte[] getTestMp4FileWithoutSynchronizationData(boolean mp4vFormat) {
|
|
||||||
return Mp4Atom.serialize(
|
|
||||||
atom(Atom.TYPE_ftyp, FTYP_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_moov,
|
|
||||||
atom(Atom.TYPE_mvhd, MVHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_trak,
|
|
||||||
atom(Atom.TYPE_tkhd, TKHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_mdia,
|
|
||||||
atom(Atom.TYPE_mdhd, VIDEO_MDHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_hdlr, VIDEO_HDLR_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_minf,
|
|
||||||
atom(Atom.TYPE_vmhd, EMPTY),
|
|
||||||
atom(Atom.TYPE_stbl,
|
|
||||||
atom(Atom.TYPE_stsd,
|
|
||||||
mp4vFormat ? VIDEO_STSD_MP4V_PAYLOAD : VIDEO_STSD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_stts, getStts()),
|
|
||||||
atom(Atom.TYPE_stsc, getStsc()),
|
|
||||||
atom(Atom.TYPE_stsz, getStsz()),
|
|
||||||
atom(Atom.TYPE_stco, getStco()))))),
|
|
||||||
atom(Atom.TYPE_trak,
|
|
||||||
atom(Atom.TYPE_tkhd, TKHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_mdia,
|
|
||||||
atom(Atom.TYPE_mdhd, AUDIO_MDHD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_hdlr, AUDIO_HDLR_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_minf,
|
|
||||||
atom(Atom.TYPE_vmhd, EMPTY),
|
|
||||||
atom(Atom.TYPE_stbl,
|
|
||||||
atom(Atom.TYPE_stsd, AUDIO_STSD_PAYLOAD),
|
|
||||||
atom(Atom.TYPE_stts, getStts()),
|
|
||||||
atom(Atom.TYPE_stsc, getStsc()),
|
|
||||||
atom(Atom.TYPE_stsz, getStsz()),
|
|
||||||
atom(Atom.TYPE_stco, getStco())))))),
|
|
||||||
atom(Atom.TYPE_mdat, getMdat(mp4vFormat ? 1120 : 1110, !mp4vFormat)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Mp4Atom atom(int type, Mp4Atom... containedMp4Atoms) {
|
|
||||||
return new Mp4Atom(type, containedMp4Atoms);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Mp4Atom atom(int type, byte[] payload) {
|
|
||||||
return new Mp4Atom(type, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MP4 atom that can be serialized as a byte array.
|
|
||||||
*/
|
|
||||||
private static final class Mp4Atom {
|
|
||||||
|
|
||||||
public static byte[] serialize(Mp4Atom... atoms) {
|
|
||||||
int size = 0;
|
|
||||||
for (Mp4Atom atom : atoms) {
|
|
||||||
size += atom.getSize();
|
|
||||||
}
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(size);
|
|
||||||
for (Mp4Atom atom : atoms) {
|
|
||||||
atom.getData(buffer);
|
|
||||||
}
|
|
||||||
return buffer.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int HEADER_SIZE = 8;
|
|
||||||
|
|
||||||
private final int type;
|
|
||||||
private final Mp4Atom[] containedMp4Atoms;
|
|
||||||
private final byte[] payload;
|
|
||||||
|
|
||||||
private Mp4Atom(int type, Mp4Atom... containedMp4Atoms) {
|
|
||||||
this.type = type;
|
|
||||||
this.containedMp4Atoms = containedMp4Atoms;
|
|
||||||
payload = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mp4Atom(int type, byte[] payload) {
|
|
||||||
this.type = type;
|
|
||||||
this.payload = payload;
|
|
||||||
containedMp4Atoms = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getSize() {
|
|
||||||
int size = HEADER_SIZE;
|
|
||||||
if (payload != null) {
|
|
||||||
size += payload.length;
|
|
||||||
} else {
|
|
||||||
for (Mp4Atom atom : containedMp4Atoms) {
|
|
||||||
size += atom.getSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getData(ByteBuffer byteBuffer) {
|
|
||||||
byteBuffer.putInt(getSize());
|
|
||||||
byteBuffer.putInt(type);
|
|
||||||
|
|
||||||
if (payload != null) {
|
|
||||||
byteBuffer.put(payload);
|
|
||||||
} else {
|
|
||||||
for (Mp4Atom atom : containedMp4Atoms) {
|
|
||||||
atom.getData(byteBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMp4Sample() throws Exception {
|
public void testMp4Sample() throws Exception {
|
||||||
TestUtil.assertOutput(new TestUtil.ExtractorFactory() {
|
TestUtil.assertOutput(new TestUtil.ExtractorFactory() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 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.extractor.ogg;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer.extractor.Extractor;
|
|
||||||
import com.google.android.exoplayer.testutil.TestUtil;
|
|
||||||
import com.google.android.exoplayer.testutil.TestUtil.ExtractorFactory;
|
|
||||||
|
|
||||||
import android.test.InstrumentationTestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit test for {@link OpusReader}.
|
|
||||||
*/
|
|
||||||
public final class OggExtractorFileTests extends InstrumentationTestCase {
|
|
||||||
|
|
||||||
private static final ExtractorFactory OGG_EXTRACTOR_FACTORY = new ExtractorFactory() {
|
|
||||||
@Override
|
|
||||||
public Extractor create() {
|
|
||||||
return new OggExtractor();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public void testOpus() throws Exception {
|
|
||||||
TestUtil.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear.opus", getInstrumentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFlac() throws Exception {
|
|
||||||
TestUtil.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac.ogg", getInstrumentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFlacNoSeektable() throws Exception {
|
|
||||||
TestUtil.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac_noseektable.ogg",
|
|
||||||
getInstrumentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testVorbis() throws Exception {
|
|
||||||
TestUtil.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_vorbis.ogg", getInstrumentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -15,24 +15,42 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.extractor.ogg;
|
package com.google.android.exoplayer.extractor.ogg;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.extractor.Extractor;
|
||||||
import com.google.android.exoplayer.testutil.FakeExtractorInput;
|
import com.google.android.exoplayer.testutil.FakeExtractorInput;
|
||||||
import com.google.android.exoplayer.testutil.TestUtil;
|
import com.google.android.exoplayer.testutil.TestUtil;
|
||||||
|
import com.google.android.exoplayer.testutil.TestUtil.ExtractorFactory;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import android.test.InstrumentationTestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit test for {@link OggExtractor}.
|
* Unit test for {@link OggExtractor}.
|
||||||
*/
|
*/
|
||||||
public final class OggExtractorTest extends TestCase {
|
public final class OggExtractorTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
private OggExtractor extractor;
|
private static final ExtractorFactory OGG_EXTRACTOR_FACTORY = new ExtractorFactory() {
|
||||||
|
@Override
|
||||||
|
public Extractor create() {
|
||||||
|
return new OggExtractor();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
public void testOpus() throws Exception {
|
||||||
public void setUp() throws Exception {
|
TestUtil.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear.opus", getInstrumentation());
|
||||||
super.setUp();
|
}
|
||||||
extractor = new OggExtractor();
|
|
||||||
|
public void testFlac() throws Exception {
|
||||||
|
TestUtil.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac.ogg", getInstrumentation());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFlacNoSeektable() throws Exception {
|
||||||
|
TestUtil.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac_noseektable.ogg",
|
||||||
|
getInstrumentation());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testVorbis() throws Exception {
|
||||||
|
TestUtil.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_vorbis.ogg", getInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSniffVorbis() throws Exception {
|
public void testSniffVorbis() throws Exception {
|
||||||
@ -80,7 +98,7 @@ public final class OggExtractorTest extends TestCase {
|
|||||||
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data)
|
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data)
|
||||||
.setSimulateIOErrors(true).setSimulateUnknownLength(true).setSimulatePartialReads(true)
|
.setSimulateIOErrors(true).setSimulateUnknownLength(true).setSimulatePartialReads(true)
|
||||||
.build();
|
.build();
|
||||||
return TestUtil.sniffTestData(extractor, input);
|
return TestUtil.sniffTestData(OGG_EXTRACTOR_FACTORY.create(), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.extractor.ogg;
|
package com.google.android.exoplayer.extractor.ogg;
|
||||||
|
|
||||||
import com.google.android.exoplayer.util.ParsableBitArray;
|
import com.google.android.exoplayer.testutil.TestUtil;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
@ -25,9 +25,7 @@ import junit.framework.TestCase;
|
|||||||
public final class VorbisBitArrayTest extends TestCase {
|
public final class VorbisBitArrayTest extends TestCase {
|
||||||
|
|
||||||
public void testReadBit() {
|
public void testReadBit() {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0x5c, 0x50));
|
||||||
(byte) 0x5c, 0x50
|
|
||||||
});
|
|
||||||
|
|
||||||
assertFalse(bitArray.readBit());
|
assertFalse(bitArray.readBit());
|
||||||
assertFalse(bitArray.readBit());
|
assertFalse(bitArray.readBit());
|
||||||
@ -56,9 +54,7 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testSkipBits() {
|
public void testSkipBits() {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F));
|
||||||
(byte) 0xF0, 0x0F
|
|
||||||
});
|
|
||||||
|
|
||||||
bitArray.skipBits(10);
|
bitArray.skipBits(10);
|
||||||
assertEquals(10, bitArray.getPosition());
|
assertEquals(10, bitArray.getPosition());
|
||||||
@ -79,9 +75,7 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
|
|
||||||
|
|
||||||
public void testSkipBitsThrowsErrorIfEOB() {
|
public void testSkipBitsThrowsErrorIfEOB() {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F));
|
||||||
(byte) 0xF0, 0x0F
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bitArray.skipBits(17);
|
bitArray.skipBits(17);
|
||||||
@ -90,9 +84,7 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testGetPosition() throws Exception {
|
public void testGetPosition() throws Exception {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F));
|
||||||
(byte) 0xF0, 0x0F
|
|
||||||
});
|
|
||||||
|
|
||||||
assertEquals(0, bitArray.getPosition());
|
assertEquals(0, bitArray.getPosition());
|
||||||
bitArray.readBit();
|
bitArray.readBit();
|
||||||
@ -104,9 +96,7 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testSetPosition() throws Exception {
|
public void testSetPosition() throws Exception {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F));
|
||||||
(byte) 0xF0, 0x0F
|
|
||||||
});
|
|
||||||
|
|
||||||
assertEquals(0, bitArray.getPosition());
|
assertEquals(0, bitArray.getPosition());
|
||||||
bitArray.setPosition(4);
|
bitArray.setPosition(4);
|
||||||
@ -121,9 +111,7 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
public void testSetPositionIllegalPositions() throws Exception {
|
public void testSetPositionIllegalPositions() throws Exception {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F));
|
||||||
(byte) 0xF0, 0x0F
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bitArray.setPosition(16);
|
bitArray.setPosition(16);
|
||||||
@ -141,19 +129,14 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testReadInt32() {
|
public void testReadInt32() {
|
||||||
byte[] data = {(byte) 0xF0, 0x0F, (byte) 0xF0, 0x0F};
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F, 0xF0, 0x0F));
|
||||||
VorbisBitArray lsb = new VorbisBitArray(data);
|
assertEquals(0x0FF00FF0, bitArray.readBits(32));
|
||||||
assertEquals(0x0FF00FF0, lsb.readBits(32));
|
bitArray = new VorbisBitArray(TestUtil.createByteArray(0x0F, 0xF0, 0x0F, 0xF0));
|
||||||
|
assertEquals(0xF00FF00F, bitArray.readBits(32));
|
||||||
data = new byte[]{0x0F, (byte) 0xF0, 0x0F, (byte) 0xF0};
|
|
||||||
lsb = new VorbisBitArray(data);
|
|
||||||
assertEquals(0xF00FF00F, lsb.readBits(32));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadBits() throws Exception {
|
public void testReadBits() throws Exception {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0x03, 0x22));
|
||||||
(byte) 0x03, 0x22
|
|
||||||
});
|
|
||||||
|
|
||||||
assertEquals(3, bitArray.readBits(2));
|
assertEquals(3, bitArray.readBits(2));
|
||||||
bitArray.skipBits(6);
|
bitArray.skipBits(6);
|
||||||
@ -166,18 +149,14 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testRead4BitsBeyondBoundary() throws Exception {
|
public void testRead4BitsBeyondBoundary() throws Exception {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0x2e, 0x10));
|
||||||
0x2e, 0x10
|
|
||||||
});
|
|
||||||
assertEquals(0x2e, bitArray.readBits(7));
|
assertEquals(0x2e, bitArray.readBits(7));
|
||||||
assertEquals(7, bitArray.getPosition());
|
assertEquals(7, bitArray.getPosition());
|
||||||
assertEquals(0x0, bitArray.readBits(4));
|
assertEquals(0x0, bitArray.readBits(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadBitsBeyondByteBoundaries() throws Exception {
|
public void testReadBitsBeyondByteBoundaries() throws Exception {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xFF, 0x0F, 0xFF, 0x0F));
|
||||||
(byte) 0xFF, (byte) 0x0F, (byte) 0xFF, (byte) 0x0F
|
|
||||||
});
|
|
||||||
|
|
||||||
assertEquals(0x0FFF0FFF, bitArray.readBits(32));
|
assertEquals(0x0FFF0FFF, bitArray.readBits(32));
|
||||||
|
|
||||||
@ -202,9 +181,7 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testReadBitsIllegalLengths() throws Exception {
|
public void testReadBitsIllegalLengths() throws Exception {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0x03, 0x22, 0x30));
|
||||||
(byte) 0x03, 0x22, 0x30
|
|
||||||
});
|
|
||||||
|
|
||||||
// reading zero bits gets 0 without advancing position
|
// reading zero bits gets 0 without advancing position
|
||||||
// (like a zero-bit read is defined to yield zer0)
|
// (like a zero-bit read is defined to yield zer0)
|
||||||
@ -222,9 +199,7 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testLimit() {
|
public void testLimit() {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xc0, 0x02), 1);
|
||||||
(byte) 0xc0, 0x02
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bitArray.skipBits(9);
|
bitArray.skipBits(9);
|
||||||
@ -240,7 +215,8 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
assertEquals(0, bitArray.getPosition());
|
assertEquals(0, bitArray.getPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
bitArray.readBits(8);
|
int byteValue = bitArray.readBits(8);
|
||||||
|
assertEquals(0xc0, byteValue);
|
||||||
assertEquals(8, bitArray.getPosition());
|
assertEquals(8, bitArray.getPosition());
|
||||||
try {
|
try {
|
||||||
bitArray.readBit();
|
bitArray.readBit();
|
||||||
@ -251,9 +227,8 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testBitsLeft() {
|
public void testBitsLeft() {
|
||||||
VorbisBitArray bitArray = new VorbisBitArray(new byte[]{
|
VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xc0, 0x02));
|
||||||
(byte) 0xc0, 0x02
|
|
||||||
});
|
|
||||||
assertEquals(16, bitArray.bitsLeft());
|
assertEquals(16, bitArray.bitsLeft());
|
||||||
assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft());
|
assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft());
|
||||||
|
|
||||||
@ -293,44 +268,4 @@ public final class VorbisBitArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadBitCompareWithMSb() {
|
|
||||||
byte[] data = {0x0F};
|
|
||||||
VorbisBitArray lsb = new VorbisBitArray(data);
|
|
||||||
ParsableBitArray msb = new ParsableBitArray(data);
|
|
||||||
|
|
||||||
assertEquals(lsb.readBit(), !msb.readBit());
|
|
||||||
assertEquals(lsb.readBit(), !msb.readBit());
|
|
||||||
assertEquals(lsb.readBit(), !msb.readBit());
|
|
||||||
assertEquals(lsb.readBit(), !msb.readBit());
|
|
||||||
assertEquals(lsb.readBit(), !msb.readBit());
|
|
||||||
assertEquals(lsb.readBit(), !msb.readBit());
|
|
||||||
assertEquals(lsb.readBit(), !msb.readBit());
|
|
||||||
assertEquals(lsb.readBit(), !msb.readBit());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadBitsCompareWithMSb() {
|
|
||||||
byte[] data = {0x0F};
|
|
||||||
VorbisBitArray lsb = new VorbisBitArray(data);
|
|
||||||
ParsableBitArray msb = new ParsableBitArray(data);
|
|
||||||
|
|
||||||
assertEquals(15, lsb.readBits(4));
|
|
||||||
assertEquals(lsb.readBits(4), msb.readBits(4));
|
|
||||||
assertEquals(15, msb.readBits(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadBitsCompareWithMSbBeyondByteBoundary() {
|
|
||||||
byte[] data = {(byte) 0xF0, 0x0F};
|
|
||||||
VorbisBitArray lsb = new VorbisBitArray(data);
|
|
||||||
ParsableBitArray msb = new ParsableBitArray(data);
|
|
||||||
|
|
||||||
assertEquals(0x00, lsb.readBits(4));
|
|
||||||
assertEquals(0x0F, msb.readBits(4));
|
|
||||||
|
|
||||||
assertEquals(0xFF, lsb.readBits(8));
|
|
||||||
assertEquals(0x00, msb.readBits(8));
|
|
||||||
|
|
||||||
assertEquals(0x00, lsb.readBits(4));
|
|
||||||
assertEquals(0x0F, msb.readBits(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.extractor.ogg;
|
package com.google.android.exoplayer.extractor.ogg;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||||
import com.google.android.exoplayer.extractor.ogg.VorbisReader.VorbisSetup;
|
import com.google.android.exoplayer.extractor.ogg.VorbisReader.VorbisSetup;
|
||||||
import com.google.android.exoplayer.testutil.FakeExtractorInput;
|
import com.google.android.exoplayer.testutil.FakeExtractorInput;
|
||||||
import com.google.android.exoplayer.testutil.FakeExtractorInput.SimulatedIOException;
|
import com.google.android.exoplayer.testutil.FakeExtractorInput.SimulatedIOException;
|
||||||
@ -29,14 +30,6 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public final class VorbisReaderTest extends TestCase {
|
public final class VorbisReaderTest extends TestCase {
|
||||||
|
|
||||||
private VorbisReader extractor;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
extractor = new VorbisReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testReadBits() throws Exception {
|
public void testReadBits() throws Exception {
|
||||||
assertEquals(0, VorbisReader.readBits((byte) 0x00, 2, 2));
|
assertEquals(0, VorbisReader.readBits((byte) 0x00, 2, 2));
|
||||||
assertEquals(1, VorbisReader.readBits((byte) 0x02, 1, 1));
|
assertEquals(1, VorbisReader.readBits((byte) 0x02, 1, 1));
|
||||||
@ -57,7 +50,11 @@ public final class VorbisReaderTest extends TestCase {
|
|||||||
|
|
||||||
public void testReadSetupHeadersWithIOExceptions() throws IOException, InterruptedException {
|
public void testReadSetupHeadersWithIOExceptions() throws IOException, InterruptedException {
|
||||||
byte[] data = TestData.getVorbisHeaderPages();
|
byte[] data = TestData.getVorbisHeaderPages();
|
||||||
VorbisReader.VorbisSetup vorbisSetup = readSetupHeaders(createInput(data));
|
ExtractorInput input = new FakeExtractorInput.Builder().setData(data).setSimulateIOErrors(true)
|
||||||
|
.setSimulateUnknownLength(true).setSimulatePartialReads(true).build();
|
||||||
|
|
||||||
|
VorbisReader reader = new VorbisReader();
|
||||||
|
VorbisReader.VorbisSetup vorbisSetup = readSetupHeaders(reader, input);
|
||||||
|
|
||||||
assertNotNull(vorbisSetup.idHeader);
|
assertNotNull(vorbisSetup.idHeader);
|
||||||
assertNotNull(vorbisSetup.commentHeader);
|
assertNotNull(vorbisSetup.commentHeader);
|
||||||
@ -88,12 +85,7 @@ public final class VorbisReaderTest extends TestCase {
|
|||||||
assertTrue(vorbisSetup.modes[1].blockFlag);
|
assertTrue(vorbisSetup.modes[1].blockFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FakeExtractorInput createInput(byte[] data) {
|
private static VorbisSetup readSetupHeaders(VorbisReader reader, ExtractorInput input)
|
||||||
return new FakeExtractorInput.Builder().setData(data).setSimulateIOErrors(true)
|
|
||||||
.setSimulateUnknownLength(true).setSimulatePartialReads(true).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private VorbisSetup readSetupHeaders(FakeExtractorInput input)
|
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
OggPacket oggPacket = new OggPacket();
|
OggPacket oggPacket = new OggPacket();
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -101,7 +93,7 @@ public final class VorbisReaderTest extends TestCase {
|
|||||||
if (!oggPacket.populate(input)) {
|
if (!oggPacket.populate(input)) {
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
VorbisSetup vorbisSetup = extractor.readSetupHeaders(oggPacket.getPayload());
|
VorbisSetup vorbisSetup = reader.readSetupHeaders(oggPacket.getPayload());
|
||||||
if (vorbisSetup != null) {
|
if (vorbisSetup != null) {
|
||||||
return vorbisSetup;
|
return vorbisSetup;
|
||||||
}
|
}
|
||||||
|
@ -90,13 +90,13 @@ public final class VorbisUtilTest extends TestCase {
|
|||||||
|
|
||||||
public void testVerifyVorbisHeaderCapturePattern() throws ParserException {
|
public void testVerifyVorbisHeaderCapturePattern() throws ParserException {
|
||||||
ParsableByteArray header = new ParsableByteArray(
|
ParsableByteArray header = new ParsableByteArray(
|
||||||
new byte[]{0x01, 'v', 'o', 'r', 'b', 'i', 's'});
|
new byte[] {0x01, 'v', 'o', 'r', 'b', 'i', 's'});
|
||||||
assertEquals(true, VorbisUtil.verifyVorbisHeaderCapturePattern(0x01, header, false));
|
assertEquals(true, VorbisUtil.verifyVorbisHeaderCapturePattern(0x01, header, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testVerifyVorbisHeaderCapturePatternInvalidHeader() {
|
public void testVerifyVorbisHeaderCapturePatternInvalidHeader() {
|
||||||
ParsableByteArray header = new ParsableByteArray(
|
ParsableByteArray header = new ParsableByteArray(
|
||||||
new byte[]{0x01, 'v', 'o', 'r', 'b', 'i', 's'});
|
new byte[] {0x01, 'v', 'o', 'r', 'b', 'i', 's'});
|
||||||
try {
|
try {
|
||||||
VorbisUtil.verifyVorbisHeaderCapturePattern(0x99, header, false);
|
VorbisUtil.verifyVorbisHeaderCapturePattern(0x99, header, false);
|
||||||
fail();
|
fail();
|
||||||
@ -107,13 +107,13 @@ public final class VorbisUtilTest extends TestCase {
|
|||||||
|
|
||||||
public void testVerifyVorbisHeaderCapturePatternInvalidHeaderQuite() throws ParserException {
|
public void testVerifyVorbisHeaderCapturePatternInvalidHeaderQuite() throws ParserException {
|
||||||
ParsableByteArray header = new ParsableByteArray(
|
ParsableByteArray header = new ParsableByteArray(
|
||||||
new byte[]{0x01, 'v', 'o', 'r', 'b', 'i', 's'});
|
new byte[] {0x01, 'v', 'o', 'r', 'b', 'i', 's'});
|
||||||
assertFalse(VorbisUtil.verifyVorbisHeaderCapturePattern(0x99, header, true));
|
assertFalse(VorbisUtil.verifyVorbisHeaderCapturePattern(0x99, header, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testVerifyVorbisHeaderCapturePatternInvalidPattern() {
|
public void testVerifyVorbisHeaderCapturePatternInvalidPattern() {
|
||||||
ParsableByteArray header = new ParsableByteArray(
|
ParsableByteArray header = new ParsableByteArray(
|
||||||
new byte[]{0x01, 'x', 'v', 'o', 'r', 'b', 'i', 's'});
|
new byte[] {0x01, 'x', 'v', 'o', 'r', 'b', 'i', 's'});
|
||||||
try {
|
try {
|
||||||
VorbisUtil.verifyVorbisHeaderCapturePattern(0x01, header, false);
|
VorbisUtil.verifyVorbisHeaderCapturePattern(0x01, header, false);
|
||||||
fail();
|
fail();
|
||||||
@ -125,7 +125,7 @@ public final class VorbisUtilTest extends TestCase {
|
|||||||
public void testVerifyVorbisHeaderCapturePatternQuiteInvalidPatternQuite()
|
public void testVerifyVorbisHeaderCapturePatternQuiteInvalidPatternQuite()
|
||||||
throws ParserException {
|
throws ParserException {
|
||||||
ParsableByteArray header = new ParsableByteArray(
|
ParsableByteArray header = new ParsableByteArray(
|
||||||
new byte[]{0x01, 'x', 'v', 'o', 'r', 'b', 'i', 's'});
|
new byte[] {0x01, 'x', 'v', 'o', 'r', 'b', 'i', 's'});
|
||||||
assertFalse(VorbisUtil.verifyVorbisHeaderCapturePattern(0x01, header, true));
|
assertFalse(VorbisUtil.verifyVorbisHeaderCapturePattern(0x01, header, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user