Commit eb4920bb by Oliver Woodman

Better compatibility with MKV test streams.

1. Fix seeking in test2.mkv by handling non-default timescale
   after duration.
2. Fix handling of missing cues in test6.mkv by allowing playback
   to continue (but all seeks will reset to t=0).

Issue #631
parent 7bc1241e
...@@ -21,6 +21,23 @@ package com.google.android.exoplayer.extractor; ...@@ -21,6 +21,23 @@ package com.google.android.exoplayer.extractor;
public interface SeekMap { public interface SeekMap {
/** /**
* A {@link SeekMap} that does not support seeking.
*/
public static final SeekMap UNSEEKABLE = new SeekMap() {
@Override
public boolean isSeekable() {
return false;
}
@Override
public long getPosition(long timeUs) {
return 0;
}
};
/**
* Whether or not the seeking is supported. * Whether or not the seeking is supported.
* <p> * <p>
* If seeking is not supported then the only valid seek position is the start of the file, and so * If seeking is not supported then the only valid seek position is the start of the file, and so
......
...@@ -28,7 +28,7 @@ import java.io.IOException; ...@@ -28,7 +28,7 @@ import java.io.IOException;
* Facilitates the extraction of AAC samples from elementary audio files formatted as AAC with ADTS * Facilitates the extraction of AAC samples from elementary audio files formatted as AAC with ADTS
* headers. * headers.
*/ */
public class AdtsExtractor implements Extractor, SeekMap { public class AdtsExtractor implements Extractor {
private static final int MAX_PACKET_SIZE = 200; private static final int MAX_PACKET_SIZE = 200;
...@@ -53,7 +53,7 @@ public class AdtsExtractor implements Extractor, SeekMap { ...@@ -53,7 +53,7 @@ public class AdtsExtractor implements Extractor, SeekMap {
public void init(ExtractorOutput output) { public void init(ExtractorOutput output) {
adtsReader = new AdtsReader(output.track(0)); adtsReader = new AdtsReader(output.track(0));
output.endTracks(); output.endTracks();
output.seekMap(this); output.seekMap(SeekMap.UNSEEKABLE);
} }
@Override @Override
...@@ -81,16 +81,4 @@ public class AdtsExtractor implements Extractor, SeekMap { ...@@ -81,16 +81,4 @@ public class AdtsExtractor implements Extractor, SeekMap {
return RESULT_CONTINUE; return RESULT_CONTINUE;
} }
// SeekMap implementation.
@Override
public boolean isSeekable() {
return false;
}
@Override
public long getPosition(long timeUs) {
return 0;
}
} }
...@@ -34,7 +34,7 @@ import java.io.IOException; ...@@ -34,7 +34,7 @@ import java.io.IOException;
/** /**
* Facilitates the extraction of data from the MPEG-2 TS container format. * Facilitates the extraction of data from the MPEG-2 TS container format.
*/ */
public final class TsExtractor implements Extractor, SeekMap { public final class TsExtractor implements Extractor {
private static final String TAG = "TsExtractor"; private static final String TAG = "TsExtractor";
...@@ -98,7 +98,7 @@ public final class TsExtractor implements Extractor, SeekMap { ...@@ -98,7 +98,7 @@ public final class TsExtractor implements Extractor, SeekMap {
@Override @Override
public void init(ExtractorOutput output) { public void init(ExtractorOutput output) {
this.output = output; this.output = output;
output.seekMap(this); output.seekMap(SeekMap.UNSEEKABLE);
} }
@Override @Override
...@@ -153,18 +153,6 @@ public final class TsExtractor implements Extractor, SeekMap { ...@@ -153,18 +153,6 @@ public final class TsExtractor implements Extractor, SeekMap {
return RESULT_CONTINUE; return RESULT_CONTINUE;
} }
// SeekMap implementation.
@Override
public boolean isSeekable() {
return false;
}
@Override
public long getPosition(long timeUs) {
return 0;
}
// Internals. // Internals.
/** /**
......
...@@ -24,12 +24,14 @@ import com.google.android.exoplayer.extractor.Extractor; ...@@ -24,12 +24,14 @@ import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput; import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.ExtractorOutput; import com.google.android.exoplayer.extractor.ExtractorOutput;
import com.google.android.exoplayer.extractor.PositionHolder; import com.google.android.exoplayer.extractor.PositionHolder;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.extractor.TrackOutput; import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.LongArray; import com.google.android.exoplayer.util.LongArray;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.NalUnitUtil; import com.google.android.exoplayer.util.NalUnitUtil;
import com.google.android.exoplayer.util.ParsableByteArray; import com.google.android.exoplayer.util.ParsableByteArray;
import com.google.android.exoplayer.util.Util;
import android.util.Pair; import android.util.Pair;
...@@ -39,7 +41,6 @@ import java.util.ArrayList; ...@@ -39,7 +41,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
/** /**
* An extractor to facilitate data retrieval from the WebM container format. * An extractor to facilitate data retrieval from the WebM container format.
...@@ -83,6 +84,7 @@ public final class WebmExtractor implements Extractor { ...@@ -83,6 +84,7 @@ public final class WebmExtractor implements Extractor {
private static final int ID_DOC_TYPE = 0x4282; private static final int ID_DOC_TYPE = 0x4282;
private static final int ID_DOC_TYPE_READ_VERSION = 0x4285; private static final int ID_DOC_TYPE_READ_VERSION = 0x4285;
private static final int ID_SEGMENT = 0x18538067; private static final int ID_SEGMENT = 0x18538067;
private static final int ID_SEGMENT_INFO = 0x1549A966;
private static final int ID_SEEK_HEAD = 0x114D9B74; private static final int ID_SEEK_HEAD = 0x114D9B74;
private static final int ID_SEEK = 0x4DBB; private static final int ID_SEEK = 0x4DBB;
private static final int ID_SEEK_ID = 0x53AB; private static final int ID_SEEK_ID = 0x53AB;
...@@ -147,7 +149,8 @@ public final class WebmExtractor implements Extractor { ...@@ -147,7 +149,8 @@ public final class WebmExtractor implements Extractor {
private long segmentContentPosition = UNKNOWN; private long segmentContentPosition = UNKNOWN;
private long segmentContentSize = UNKNOWN; private long segmentContentSize = UNKNOWN;
private long timecodeScale = 1000000L; private long timecodeScale = C.UNKNOWN_TIME_US;
private long durationTimecode = C.UNKNOWN_TIME_US;
private long durationUs = C.UNKNOWN_TIME_US; private long durationUs = C.UNKNOWN_TIME_US;
private TrackFormat trackFormat; // Used to store the last seen track. private TrackFormat trackFormat; // Used to store the last seen track.
...@@ -320,10 +323,17 @@ public final class WebmExtractor implements Extractor { ...@@ -320,10 +323,17 @@ public final class WebmExtractor implements Extractor {
seenClusterPositionForCurrentCuePoint = false; seenClusterPositionForCurrentCuePoint = false;
return; return;
case ID_CLUSTER: case ID_CLUSTER:
// If we encounter a Cluster before building Cues, then we should try to build cues first if (cuesState == CUES_STATE_NOT_BUILT) {
// before parsing the Cluster. // We need to build cues before parsing the cluster.
if (cuesState == CUES_STATE_NOT_BUILT && cuesContentPosition != UNKNOWN) { if (cuesContentPosition != UNKNOWN) {
// We know where the Cues element is located. Seek to request it.
seekForCues = true; seekForCues = true;
} else {
// We don't know where the Cues element is located. It's most likely omitted. Allow
// playback, but disable seeking.
extractorOutput.seekMap(SeekMap.UNSEEKABLE);
cuesState = CUES_STATE_BUILT;
}
} }
return; return;
case ID_BLOCK_GROUP: case ID_BLOCK_GROUP:
...@@ -345,6 +355,15 @@ public final class WebmExtractor implements Extractor { ...@@ -345,6 +355,15 @@ public final class WebmExtractor implements Extractor {
/* package */ void endMasterElement(int id) throws ParserException { /* package */ void endMasterElement(int id) throws ParserException {
switch (id) { switch (id) {
case ID_SEGMENT_INFO:
if (timecodeScale == C.UNKNOWN_TIME_US) {
// timecodeScale was omitted. Use the default value.
timecodeScale = 1000000;
}
if (durationTimecode != C.UNKNOWN_TIME_US) {
durationUs = scaleTimecodeToUs(durationTimecode);
}
return;
case ID_SEEK: case ID_SEEK:
if (seekEntryId == UNKNOWN || seekEntryPosition == UNKNOWN) { if (seekEntryId == UNKNOWN || seekEntryPosition == UNKNOWN) {
throw new ParserException("Mandatory element SeekID or SeekPosition not found"); throw new ParserException("Mandatory element SeekID or SeekPosition not found");
...@@ -355,7 +374,7 @@ public final class WebmExtractor implements Extractor { ...@@ -355,7 +374,7 @@ public final class WebmExtractor implements Extractor {
return; return;
case ID_CUES: case ID_CUES:
if (cuesState != CUES_STATE_BUILT) { if (cuesState != CUES_STATE_BUILT) {
extractorOutput.seekMap(buildCues()); extractorOutput.seekMap(buildSeekMap());
cuesState = CUES_STATE_BUILT; cuesState = CUES_STATE_BUILT;
} else { } else {
// We have already built the cues. Ignore. // We have already built the cues. Ignore.
...@@ -528,7 +547,7 @@ public final class WebmExtractor implements Extractor { ...@@ -528,7 +547,7 @@ public final class WebmExtractor implements Extractor {
/* package */ void floatElement(int id, double value) { /* package */ void floatElement(int id, double value) {
switch (id) { switch (id) {
case ID_DURATION: case ID_DURATION:
durationUs = scaleTimecodeToUs((long) value); durationTimecode = (long) value;
return; return;
case ID_SAMPLING_FREQUENCY: case ID_SAMPLING_FREQUENCY:
trackFormat.sampleRate = (int) value; trackFormat.sampleRate = (int) value;
...@@ -865,19 +884,19 @@ public final class WebmExtractor implements Extractor { ...@@ -865,19 +884,19 @@ public final class WebmExtractor implements Extractor {
} }
/** /**
* Builds a {@link ChunkIndex} containing recently gathered Cues information. * Builds a {@link SeekMap} from the recently gathered Cues information.
* *
* @return The built {@link ChunkIndex}. * @return The built {@link SeekMap}. May be {@link SeekMap#UNSEEKABLE} if cues information was
* @throws ParserException If the index could not be built. * missing or incomplete.
*/ */
private ChunkIndex buildCues() throws ParserException { private SeekMap buildSeekMap() {
if (segmentContentPosition == UNKNOWN) { if (segmentContentPosition == UNKNOWN || durationUs == C.UNKNOWN_TIME_US
throw new ParserException("Segment start/end offsets unknown"); || cueTimesUs == null || cueTimesUs.size() == 0
} else if (durationUs == C.UNKNOWN_TIME_US) { || cueClusterPositions == null || cueClusterPositions.size() != cueTimesUs.size()) {
throw new ParserException("Duration unknown"); // Cues information is missing or incomplete.
} else if (cueTimesUs == null || cueClusterPositions == null cueTimesUs = null;
|| cueTimesUs.size() == 0 || cueTimesUs.size() != cueClusterPositions.size()) { cueClusterPositions = null;
throw new ParserException("Invalid/missing cue points"); return SeekMap.UNSEEKABLE;
} }
int cuePointsSize = cueTimesUs.size(); int cuePointsSize = cueTimesUs.size();
int[] sizes = new int[cuePointsSize]; int[] sizes = new int[cuePointsSize];
...@@ -927,8 +946,11 @@ public final class WebmExtractor implements Extractor { ...@@ -927,8 +946,11 @@ public final class WebmExtractor implements Extractor {
return false; return false;
} }
private long scaleTimecodeToUs(long unscaledTimecode) { private long scaleTimecodeToUs(long unscaledTimecode) throws ParserException {
return TimeUnit.NANOSECONDS.toMicros(unscaledTimecode * timecodeScale); if (timecodeScale == C.UNKNOWN_TIME_US) {
throw new ParserException("Can't scale timecode prior to timecodeScale being set.");
}
return Util.scaleLargeTimestamp(unscaledTimecode, timecodeScale, 1000);
} }
private static boolean isCodecSupported(String codecId) { private static boolean isCodecSupported(String codecId) {
...@@ -984,7 +1006,7 @@ public final class WebmExtractor implements Extractor { ...@@ -984,7 +1006,7 @@ public final class WebmExtractor implements Extractor {
} }
@Override @Override
public void floatElement(int id, double value) { public void floatElement(int id, double value) throws ParserException {
WebmExtractor.this.floatElement(id, value); WebmExtractor.this.floatElement(id, value);
} }
......
...@@ -80,8 +80,14 @@ import java.util.List; ...@@ -80,8 +80,14 @@ import java.util.List;
return this; return this;
} }
public StreamBuilder setInfo(int timecodeScale, long durationUs) { public StreamBuilder setInfo(int timecodeScale, long durationTimecode) {
info = createInfoElement(timecodeScale, durationUs); 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; return this;
} }
...@@ -177,7 +183,15 @@ import java.util.List; ...@@ -177,7 +183,15 @@ import java.util.List;
Assertions.checkNotNull(info); Assertions.checkNotNull(info);
EbmlElement tracks = element(0x1654AE6B, trackEntries.toArray(new EbmlElement[0])); EbmlElement tracks = element(0x1654AE6B, trackEntries.toArray(new EbmlElement[0]));
EbmlElement[] children;
if (cuePointCount == 0) {
children = new EbmlElement[2 + mediaSegments.size()];
System.arraycopy(mediaSegments.toArray(new EbmlElement[0]), 0, children, 2,
mediaSegments.size());
children[0] = info;
children[1] = tracks;
} else {
// Get the size of the initialization segment. // Get the size of the initialization segment.
EbmlElement[] cuePointElements = new EbmlElement[cuePointCount]; EbmlElement[] cuePointElements = new EbmlElement[cuePointCount];
for (int i = 0; i < cuePointCount; i++) { for (int i = 0; i < cuePointCount; i++) {
...@@ -193,12 +207,14 @@ import java.util.List; ...@@ -193,12 +207,14 @@ import java.util.List;
cues = element(0x1C53BB6B, cuePointElements); // Cues cues = element(0x1C53BB6B, cuePointElements); // Cues
// Build the top-level segment element. // Build the top-level segment element.
EbmlElement[] children = new EbmlElement[3 + mediaSegments.size()]; children = new EbmlElement[3 + mediaSegments.size()];
System.arraycopy(mediaSegments.toArray(new EbmlElement[0]), 0, children, 3, System.arraycopy(mediaSegments.toArray(new EbmlElement[0]), 0, children, 3,
mediaSegments.size()); mediaSegments.size());
children[0] = info; children[0] = info;
children[1] = tracks; children[1] = tracks;
children[2] = cues; children[2] = cues;
}
EbmlElement segmentElement = element(0x18538067, children); // Segment EbmlElement segmentElement = element(0x18538067, children); // Segment
// Serialize the EBML header and the top-level segment element. // Serialize the EBML header and the top-level segment element.
...@@ -221,12 +237,19 @@ import java.util.List; ...@@ -221,12 +237,19 @@ import java.util.List;
element(0x4285, (byte) (docTypeReadVersion & 0xFF))); // DocTypeReadVersion element(0x4285, (byte) (docTypeReadVersion & 0xFF))); // DocTypeReadVersion
} }
private EbmlElement createInfoElement(int timecodeScale, long durationUs) { private EbmlElement createInfoElement(int timecodeScale, long durationTimecode,
boolean durationFirst, boolean omitDefaultTimecodeScale) {
byte[] timecodeScaleBytes = getIntegerBytes(timecodeScale); byte[] timecodeScaleBytes = getIntegerBytes(timecodeScale);
byte[] durationBytes = getLongBytes(Double.doubleToLongBits(durationUs / 1000.0)); 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 return element(0x1549A966, // Info
element(0x2AD7B1, timecodeScaleBytes), // TimecodeScale durationFirst ? durationElement : timescaleElement,
element(0x4489, durationBytes)); // Duration durationFirst ? timescaleElement : durationElement);
} }
private static EbmlElement createVideoTrackEntry(String codecId, int pixelWidth, int pixelHeight, private static EbmlElement createVideoTrackEntry(String codecId, int pixelWidth, int pixelHeight,
......
...@@ -22,11 +22,13 @@ import com.google.android.exoplayer.MediaFormat; ...@@ -22,11 +22,13 @@ import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.ChunkIndex; import com.google.android.exoplayer.extractor.ChunkIndex;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.extractor.webm.StreamBuilder.ContentEncodingSettings; import com.google.android.exoplayer.extractor.webm.StreamBuilder.ContentEncodingSettings;
import com.google.android.exoplayer.testutil.FakeExtractorOutput; import com.google.android.exoplayer.testutil.FakeExtractorOutput;
import com.google.android.exoplayer.testutil.FakeTrackOutput; 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.MimeTypes;
import com.google.android.exoplayer.util.Util;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
...@@ -41,7 +43,7 @@ import java.util.UUID; ...@@ -41,7 +43,7 @@ import java.util.UUID;
public final class WebmExtractorTest extends InstrumentationTestCase { public final class WebmExtractorTest extends InstrumentationTestCase {
private static final int DEFAULT_TIMECODE_SCALE = 1000000; private static final int DEFAULT_TIMECODE_SCALE = 1000000;
private static final long TEST_DURATION_US = 9920000L; private static final long TEST_DURATION_TIMECODE = 9920L;
private static final int TEST_WIDTH = 1280; private static final int TEST_WIDTH = 1280;
private static final int TEST_HEIGHT = 720; private static final int TEST_HEIGHT = 720;
private static final int TEST_CHANNEL_COUNT = 1; private static final int TEST_CHANNEL_COUNT = 1;
...@@ -82,20 +84,21 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -82,20 +84,21 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
public void testReadInitializationSegment() throws IOException, InterruptedException { public void testReadInitializationSegment() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(1); .build(1);
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertIndex(DEFAULT_TIMECODE_SCALE, 1);
} }
public void testReadSegmentTwice() throws IOException, InterruptedException { public void testReadSegmentTwice() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(1); .build(1);
...@@ -103,54 +106,58 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -103,54 +106,58 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
extractor.seek(); extractor.seek();
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertIndex(DEFAULT_TIMECODE_SCALE, 1);
} }
public void testPrepareOpus() throws IOException, InterruptedException { public void testPrepareOpus() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, .addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE) TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.build(1); .build(1);
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_OPUS); assertTracksEnded();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_OPUS);
assertIndex(DEFAULT_TIMECODE_SCALE, 1);
} }
public void testPrepareVorbis() throws IOException, InterruptedException { public void testPrepareVorbis() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVorbisTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, getVorbisCodecPrivate()) .addVorbisTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, getVorbisCodecPrivate())
.build(1); .build(1);
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_VORBIS); assertTracksEnded();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_VORBIS);
assertIndex(DEFAULT_TIMECODE_SCALE, 1);
} }
public void testPrepareH264() throws IOException, InterruptedException { public void testPrepareH264() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(MATROSKA_DOC_TYPE) .setHeader(MATROSKA_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addH264Track(TEST_WIDTH, TEST_HEIGHT, TEST_H264_CODEC_PRIVATE) .addH264Track(TEST_WIDTH, TEST_HEIGHT, TEST_H264_CODEC_PRIVATE)
.build(1); .build(1);
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertH264VideoFormat(); assertTracksEnded();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertH264VideoFormat(DEFAULT_TIMECODE_SCALE);
assertIndex(DEFAULT_TIMECODE_SCALE, 1);
} }
public void testPrepareTwoTracks() throws IOException, InterruptedException { public void testPrepareTwoTracks() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, .addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE) TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
...@@ -158,16 +165,17 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -158,16 +165,17 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertTracksEnded();
assertEquals(2, extractorOutput.numberOfTracks); assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(); assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertAudioFormat(MimeTypes.AUDIO_OPUS); assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_OPUS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertIndex(DEFAULT_TIMECODE_SCALE, 1);
} }
public void testPrepareThreeTracks() throws IOException, InterruptedException { public void testPrepareThreeTracks() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addUnsupportedTrack() .addUnsupportedTrack()
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, .addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
...@@ -176,17 +184,18 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -176,17 +184,18 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertTracksEnded();
// Even though the input stream has 3 tracks, only 2 of them are supported and will be reported. // Even though the input stream has 3 tracks, only 2 of them are supported and will be reported.
assertEquals(2, extractorOutput.numberOfTracks); assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(); assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertAudioFormat(MimeTypes.AUDIO_OPUS); assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_OPUS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertIndex(DEFAULT_TIMECODE_SCALE, 1);
} }
public void testPrepareFourTracks() throws IOException, InterruptedException { public void testPrepareFourTracks() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addVorbisTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, getVorbisCodecPrivate()) .addVorbisTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, getVorbisCodecPrivate())
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
...@@ -196,26 +205,28 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -196,26 +205,28 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertTracksEnded();
// Even though the input stream has 4 supported tracks, only the first video and audio track // Even though the input stream has 4 supported tracks, only the first video and audio track
// will be reported. // will be reported.
assertEquals(2, extractorOutput.numberOfTracks); assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(); assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertAudioFormat(MimeTypes.AUDIO_VORBIS); assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_VORBIS);
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertIndex(DEFAULT_TIMECODE_SCALE, 1);
} }
public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException { public void testPrepareContentEncodingEncryption() throws IOException, InterruptedException {
ContentEncodingSettings settings = new StreamBuilder.ContentEncodingSettings(0, 1, 5, 1); ContentEncodingSettings settings = new StreamBuilder.ContentEncodingSettings(0, 1, 5, 1);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1); .build(1);
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US)); assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertIndex(DEFAULT_TIMECODE_SCALE, 1);
DrmInitData drmInitData = extractorOutput.drmInitData; DrmInitData drmInitData = extractorOutput.drmInitData;
assertNotNull(drmInitData); assertNotNull(drmInitData);
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(WIDEVINE_UUID)); android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(WIDEVINE_UUID));
...@@ -225,75 +236,91 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -225,75 +236,91 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
public void testPrepareThreeCuePoints() throws IOException, InterruptedException { public void testPrepareThreeCuePoints() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(3); .build(3);
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertIndex( assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
new IndexPoint(0, 0, 10000), assertIndex(DEFAULT_TIMECODE_SCALE, 3);
new IndexPoint(10000, 0, 10000),
new IndexPoint(20000, 0, TEST_DURATION_US - 20000));
} }
public void testPrepareCustomTimecodeScale() throws IOException, InterruptedException { 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() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(1000, TEST_DURATION_US) .setInfo(timecodeScale, TEST_DURATION_TIMECODE, omitTimecodeScaleIfDefault, afterDuration)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(3); .build(3);
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertIndex( assertVp9VideoFormat(timecodeScale);
new IndexPoint(0, 0, 10), assertIndex(timecodeScale, 3);
new IndexPoint(10, 0, 10),
new IndexPoint(20, 0, (TEST_DURATION_US / 1000) - 20));
} }
public void testPrepareNoCuePoints() throws IOException, InterruptedException { public void testPrepareNoCuesElement() throws IOException, InterruptedException {
byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
true /* keyframe */, false /* invisible */, media)
.build(0); .build(0);
try {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
fail();
} catch (ParserException exception) { assertTracksEnded();
assertEquals("Invalid/missing cue points", exception.getMessage()); assertIndexUnseekable();
}
} }
public void testAcceptsWebmDocType() throws IOException, InterruptedException { public void testAcceptsWebmDocType() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(1); .build(1);
// No exception is thrown.
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertTracksEnded();
} }
public void testAcceptsMatroskaDocType() throws IOException, InterruptedException { public void testAcceptsMatroskaDocType() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(MATROSKA_DOC_TYPE) .setHeader(MATROSKA_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(1); .build(1);
// No exception is thrown.
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertTracksEnded();
} }
public void testPrepareInvalidDocType() throws IOException, InterruptedException { public void testPrepareInvalidDocType() throws IOException, InterruptedException {
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader("webB") .setHeader("webB")
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.build(1); .build(1);
try { try {
...@@ -308,7 +335,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -308,7 +335,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
ContentEncodingSettings settings = new ContentEncodingSettings(1, 1, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(1, 1, 5, 1);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1); .build(1);
try { try {
...@@ -323,7 +350,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -323,7 +350,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 0, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 0, 5, 1);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1); .build(1);
try { try {
...@@ -339,7 +366,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -339,7 +366,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, new byte[0]); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 0, new byte[0]);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1); .build(1);
try { try {
...@@ -354,7 +381,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -354,7 +381,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 4, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 4, 1);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1); .build(1);
try { try {
...@@ -369,7 +396,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -369,7 +396,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 0); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 0);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.build(1); .build(1);
try { try {
...@@ -384,7 +411,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -384,7 +411,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */, .addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
true /* keyframe */, false /* invisible */, media) true /* keyframe */, false /* invisible */, media)
...@@ -392,7 +419,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -392,7 +419,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertSample(0, media, 0, true, false, null, getVideoOutput()); assertSample(0, media, 0, true, false, null, getVideoOutput());
} }
...@@ -403,7 +431,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -403,7 +431,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] unstrippedSampleBytes = TestUtil.joinByteArrays(strippedBytes, sampleBytes); byte[] unstrippedSampleBytes = TestUtil.joinByteArrays(strippedBytes, sampleBytes);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */, .addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
true /* keyframe */, false /* invisible */, sampleBytes) true /* keyframe */, false /* invisible */, sampleBytes)
...@@ -411,7 +439,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -411,7 +439,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertSample(0, unstrippedSampleBytes, 0, true, false, null, getVideoOutput()); assertSample(0, unstrippedSampleBytes, 0, true, false, null, getVideoOutput());
} }
...@@ -422,7 +451,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -422,7 +451,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] unstrippedSampleBytes = TestUtil.joinByteArrays(strippedBytes, sampleBytes); byte[] unstrippedSampleBytes = TestUtil.joinByteArrays(strippedBytes, sampleBytes);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */, .addSimpleBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
true /* keyframe */, false /* invisible */, sampleBytes) true /* keyframe */, false /* invisible */, sampleBytes)
...@@ -430,7 +459,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -430,7 +459,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertSample(0, unstrippedSampleBytes, 0, true, false, null, getVideoOutput()); assertSample(0, unstrippedSampleBytes, 0, true, false, null, getVideoOutput());
} }
...@@ -438,7 +468,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -438,7 +468,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, .addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE) TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
...@@ -450,9 +480,10 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -450,9 +480,10 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertTracksEnded();
assertEquals(2, extractorOutput.numberOfTracks); assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(); assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertAudioFormat(MimeTypes.AUDIO_OPUS); assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null, getVideoOutput()); assertSample(0, media, 0, true, false, null, getVideoOutput());
assertSample(0, media, 0, true, false, null, getAudioOutput()); assertSample(0, media, 0, true, false, null, getAudioOutput());
} }
...@@ -461,7 +492,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -461,7 +492,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addUnsupportedTrack() .addUnsupportedTrack()
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, .addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
...@@ -476,9 +507,10 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -476,9 +507,10 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertTracksEnded();
assertEquals(2, extractorOutput.numberOfTracks); assertEquals(2, extractorOutput.numberOfTracks);
assertVp9VideoFormat(); assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertAudioFormat(MimeTypes.AUDIO_OPUS); assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null, getVideoOutput()); assertSample(0, media, 0, true, false, null, getVideoOutput());
assertSample(0, media, 0, true, false, null, getAudioOutput()); assertSample(0, media, 0, true, false, null, getAudioOutput());
} }
...@@ -487,7 +519,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -487,7 +519,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, .addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY,
TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE) TEST_SEEK_PRE_ROLL, TEST_OPUS_CODEC_PRIVATE)
.addBlockMedia(2 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */, .addBlockMedia(2 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
...@@ -496,7 +528,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -496,7 +528,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_OPUS); assertTracksEnded();
assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_OPUS);
assertSample(0, media, 0, true, false, null, getAudioOutput()); assertSample(0, media, 0, true, false, null, getAudioOutput());
} }
...@@ -504,7 +537,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -504,7 +537,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */, .addBlockMedia(1 /* trackNumber */, 0 /* clusterTimecode */, 0 /* blockTimecode */,
false /* keyframe */, false /* invisible */, media) false /* keyframe */, false /* invisible */, media)
...@@ -512,7 +545,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -512,7 +545,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertSample(0, media, 0, false, false, null, getVideoOutput()); assertSample(0, media, 0, false, false, null, getVideoOutput());
} }
...@@ -521,7 +555,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -521,7 +555,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 1);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.addSimpleBlockEncryptedMedia(1 /* trackNumber */, 0 /* clusterTimecode */, .addSimpleBlockEncryptedMedia(1 /* trackNumber */, 0 /* clusterTimecode */,
0 /* blockTimecode */, true /* keyframe */, false /* invisible */, 0 /* blockTimecode */, true /* keyframe */, false /* invisible */,
...@@ -530,7 +564,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -530,7 +564,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertSample(0, media, 0, true, false, TEST_ENCRYPTION_KEY_ID, getVideoOutput()); assertSample(0, media, 0, true, false, TEST_ENCRYPTION_KEY_ID, getVideoOutput());
} }
...@@ -540,7 +575,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -540,7 +575,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 1); ContentEncodingSettings settings = new ContentEncodingSettings(0, 1, 5, 1);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, settings)
.addSimpleBlockEncryptedMedia(1 /* trackNumber */, 0 /* clusterTimecode */, .addSimpleBlockEncryptedMedia(1 /* trackNumber */, 0 /* clusterTimecode */,
0 /* blockTimecode */, true /* keyframe */, false /* invisible */, 0 /* blockTimecode */, true /* keyframe */, false /* invisible */,
...@@ -551,6 +586,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -551,6 +586,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
fail(); fail();
} catch (ParserException exception) { } catch (ParserException exception) {
assertTracksEnded();
assertEquals("Extension bit is set in signal byte", exception.getMessage()); assertEquals("Extension bit is set in signal byte", exception.getMessage());
} }
} }
...@@ -559,7 +595,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -559,7 +595,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addSimpleBlockMedia(1 /* trackNumber */, 12 /* clusterTimecode */, 13 /* blockTimecode */, .addSimpleBlockMedia(1 /* trackNumber */, 12 /* clusterTimecode */, 13 /* blockTimecode */,
false /* keyframe */, true /* invisible */, media) false /* keyframe */, true /* invisible */, media)
...@@ -567,15 +603,17 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -567,15 +603,17 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertSample(0, media, 25000, false, true, null, getVideoOutput()); assertSample(0, media, 25000, false, true, null, getVideoOutput());
} }
public void testReadSampleCustomTimescale() throws IOException, InterruptedException { public void testReadSampleCustomTimecodeScale() throws IOException, InterruptedException {
int timecodeScale = 1000;
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(1000, TEST_DURATION_US) .setInfo(timecodeScale, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addSimpleBlockMedia(1 /* trackNumber */, 12 /* clusterTimecode */, 13 /* blockTimecode */, .addSimpleBlockMedia(1 /* trackNumber */, 12 /* clusterTimecode */, 13 /* blockTimecode */,
false /* keyframe */, false /* invisible */, media) false /* keyframe */, false /* invisible */, media)
...@@ -583,7 +621,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -583,7 +621,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertVp9VideoFormat(timecodeScale);
assertSample(0, media, 25, false, false, null, getVideoOutput()); assertSample(0, media, 25, false, false, null, getVideoOutput());
} }
...@@ -591,7 +630,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -591,7 +630,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addVp9Track(TEST_WIDTH, TEST_HEIGHT, null) .addVp9Track(TEST_WIDTH, TEST_HEIGHT, null)
.addSimpleBlockMedia(1 /* trackNumber */, 13 /* clusterTimecode */, -12 /* blockTimecode */, .addSimpleBlockMedia(1 /* trackNumber */, 13 /* clusterTimecode */, -12 /* blockTimecode */,
true /* keyframe */, true /* invisible */, media) true /* keyframe */, true /* invisible */, media)
...@@ -599,7 +638,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -599,7 +638,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertVp9VideoFormat(); assertTracksEnded();
assertVp9VideoFormat(DEFAULT_TIMECODE_SCALE);
assertSample(0, media, 1000, true, true, null, getVideoOutput()); assertSample(0, media, 1000, true, true, null, getVideoOutput());
} }
...@@ -607,7 +647,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -607,7 +647,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(100); byte[] media = createFrameData(100);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, TEST_SEEK_PRE_ROLL, .addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, TEST_SEEK_PRE_ROLL,
TEST_OPUS_CODEC_PRIVATE, TEST_DEFAULT_DURATION_NS) TEST_OPUS_CODEC_PRIVATE, TEST_DEFAULT_DURATION_NS)
.addSimpleBlockMediaWithFixedSizeLacing(2 /* trackNumber */, 0 /* clusterTimecode */, .addSimpleBlockMediaWithFixedSizeLacing(2 /* trackNumber */, 0 /* clusterTimecode */,
...@@ -616,7 +656,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -616,7 +656,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_OPUS); assertTracksEnded();
assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_OPUS);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
long expectedTimeUs = i * TEST_DEFAULT_DURATION_NS / 1000; long expectedTimeUs = i * TEST_DEFAULT_DURATION_NS / 1000;
assertSample(i, Arrays.copyOfRange(media, i * 5, i * 5 + 5), expectedTimeUs, true, false, assertSample(i, Arrays.copyOfRange(media, i * 5, i * 5 + 5), expectedTimeUs, true, false,
...@@ -628,7 +669,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -628,7 +669,7 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
byte[] media = createFrameData(300); byte[] media = createFrameData(300);
byte[] data = new StreamBuilder() byte[] data = new StreamBuilder()
.setHeader(WEBM_DOC_TYPE) .setHeader(WEBM_DOC_TYPE)
.setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_US) .setInfo(DEFAULT_TIMECODE_SCALE, TEST_DURATION_TIMECODE)
.addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, TEST_SEEK_PRE_ROLL, .addOpusTrack(TEST_CHANNEL_COUNT, TEST_SAMPLE_RATE, TEST_CODEC_DELAY, TEST_SEEK_PRE_ROLL,
TEST_OPUS_CODEC_PRIVATE, TEST_DEFAULT_DURATION_NS) TEST_OPUS_CODEC_PRIVATE, TEST_DEFAULT_DURATION_NS)
.addSimpleBlockMediaWithXiphLacing(2 /* trackNumber */, 0 /* clusterTimecode */, .addSimpleBlockMediaWithXiphLacing(2 /* trackNumber */, 0 /* clusterTimecode */,
...@@ -637,7 +678,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -637,7 +678,8 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
TestUtil.consumeTestData(extractor, data); TestUtil.consumeTestData(extractor, data);
assertAudioFormat(MimeTypes.AUDIO_OPUS); assertTracksEnded();
assertAudioFormat(DEFAULT_TIMECODE_SCALE, MimeTypes.AUDIO_OPUS);
assertSample(0, Arrays.copyOfRange(media, 0, 256), 0 * TEST_DEFAULT_DURATION_NS / 1000, true, assertSample(0, Arrays.copyOfRange(media, 0, 256), 0 * TEST_DEFAULT_DURATION_NS / 1000, true,
false, null, getAudioOutput()); false, null, getAudioOutput());
assertSample(1, Arrays.copyOfRange(media, 256, 257), 1 * TEST_DEFAULT_DURATION_NS / 1000, true, assertSample(1, Arrays.copyOfRange(media, 256, 257), 1 * TEST_DEFAULT_DURATION_NS / 1000, true,
...@@ -656,22 +698,32 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -656,22 +698,32 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
return extractorOutput.trackOutputs.get(2); return extractorOutput.trackOutputs.get(2);
} }
private void assertVp9VideoFormat() { private void assertTracksEnded() {
assertTrue(extractorOutput.tracksEnded);
}
private void assertVp9VideoFormat(int timecodeScale) {
MediaFormat format = getVideoOutput().format; MediaFormat format = getVideoOutput().format;
assertEquals(Util.scaleLargeTimestamp(TEST_DURATION_TIMECODE, timecodeScale, 1000),
format.durationUs);
assertEquals(TEST_WIDTH, format.width); assertEquals(TEST_WIDTH, format.width);
assertEquals(TEST_HEIGHT, format.height); assertEquals(TEST_HEIGHT, format.height);
assertEquals(MimeTypes.VIDEO_VP9, format.mimeType); assertEquals(MimeTypes.VIDEO_VP9, format.mimeType);
} }
private void assertH264VideoFormat() { private void assertH264VideoFormat(int timecodeScale) {
MediaFormat format = getVideoOutput().format; MediaFormat format = getVideoOutput().format;
assertEquals(Util.scaleLargeTimestamp(TEST_DURATION_TIMECODE, timecodeScale, 1000),
format.durationUs);
assertEquals(TEST_WIDTH, format.width); assertEquals(TEST_WIDTH, format.width);
assertEquals(TEST_HEIGHT, format.height); assertEquals(TEST_HEIGHT, format.height);
assertEquals(MimeTypes.VIDEO_H264, format.mimeType); assertEquals(MimeTypes.VIDEO_H264, format.mimeType);
} }
private void assertAudioFormat(String expectedMimeType) { private void assertAudioFormat(int timecodeScale, String expectedMimeType) {
MediaFormat format = getAudioOutput().format; MediaFormat format = getAudioOutput().format;
assertEquals(Util.scaleLargeTimestamp(TEST_DURATION_TIMECODE, timecodeScale, 1000),
format.durationUs);
assertEquals(TEST_CHANNEL_COUNT, format.channelCount); assertEquals(TEST_CHANNEL_COUNT, format.channelCount);
assertEquals(TEST_SAMPLE_RATE, format.sampleRate); assertEquals(TEST_SAMPLE_RATE, format.sampleRate);
assertEquals(expectedMimeType, format.mimeType); assertEquals(expectedMimeType, format.mimeType);
...@@ -688,15 +740,25 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -688,15 +740,25 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
} }
} }
private void assertIndex(IndexPoint... indexPoints) { private void assertIndex(int timecodeScale, int cuePointCount) {
ChunkIndex index = (ChunkIndex) extractorOutput.seekMap; ChunkIndex index = (ChunkIndex) extractorOutput.seekMap;
assertEquals(indexPoints.length, index.length); assertEquals(cuePointCount, index.length);
for (int i = 0; i < indexPoints.length; i++) { for (int i = 0; i < cuePointCount - 1; i++) {
IndexPoint indexPoint = indexPoints[i]; assertEquals(Util.scaleLargeTimestamp(10 * i, timecodeScale, 1000), index.timesUs[i]);
assertEquals(indexPoint.timeUs, index.timesUs[i]); assertEquals(Util.scaleLargeTimestamp(10, timecodeScale, 1000), index.durationsUs[i]);
assertEquals(indexPoint.size, index.sizes[i]); assertEquals(0, index.sizes[i]);
assertEquals(indexPoint.durationUs, index.durationsUs[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]);
}
private void assertIndexUnseekable() {
assertEquals(SeekMap.UNSEEKABLE, extractorOutput.seekMap);
} }
private void assertSample(int index, byte[] expectedMedia, long timeUs, boolean keyframe, private void assertSample(int index, byte[] expectedMedia, long timeUs, boolean keyframe,
...@@ -732,19 +794,4 @@ public final class WebmExtractorTest extends InstrumentationTestCase { ...@@ -732,19 +794,4 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
return data; return data;
} }
/** Used by {@link #assertIndex(IndexPoint...)} to validate index elements. */
private static final class IndexPoint {
private final long timeUs;
private final int size;
private final long durationUs;
private IndexPoint(long timeUs, int size, long durationUs) {
this.timeUs = timeUs;
this.size = size;
this.durationUs = durationUs;
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment