mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Push tests.
This commit is contained in:
parent
462fea3eaf
commit
fbd0a57e5c
10
library/src/test/.classpath
Normal file
10
library/src/test/.classpath
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="src" path="java"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/ExoPlayerDemo"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
62
library/src/test/.project
Normal file
62
library/src/test/.project
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>ExoPlayerTests</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
<project>ExoPlayerLib</project>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
<linkedResources>
|
||||
<link>
|
||||
<name>libs/dexmaker-1.2.jar</name>
|
||||
<type>1</type>
|
||||
<locationURI>$%7BPARENT-3-PROJECT_LOC%7D/third_party/dexmaker/dexmaker-1.2.jar</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>libs/dexmaker-mockito-1.2.jar</name>
|
||||
<type>1</type>
|
||||
<locationURI>$%7BPARENT-3-PROJECT_LOC%7D/third_party/dexmaker/dexmaker-mockito-1.2.jar</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>libs/mockito-all-1.9.5.jar</name>
|
||||
<type>1</type>
|
||||
<locationURI>$%7BPARENT-3-PROJECT_LOC%7D/third_party/mockito/mockito-all-1.9.5.jar</locationURI>
|
||||
</link>
|
||||
</linkedResources>
|
||||
<filteredResources>
|
||||
<filter>
|
||||
<id>1425657306619</id>
|
||||
<name></name>
|
||||
<type>14</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.ui.ide.multiFilter</id>
|
||||
<arguments>1.0-name-matches-true-false-BUILD</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
</filteredResources>
|
||||
</projectDescription>
|
30
library/src/test/AndroidManifest.xml
Normal file
30
library/src/test/AndroidManifest.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.exoplayer.tests">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="21"/>
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner"/>
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:targetPackage="com.google.android.exoplayer.demo"
|
||||
android:name="android.test.InstrumentationTestRunner"/>
|
||||
|
||||
</manifest>
|
99
library/src/test/assets/dash/sample_mpd_1
Executable file
99
library/src/test/assets/dash/sample_mpd_1
Executable file
@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
availabilityStartTime="2014-06-19T23:07:42"
|
||||
minBufferTime="PT1.500S"
|
||||
minimumUpdatePeriod="PT5.000S"
|
||||
profiles="urn:mpeg:dash:profile:isoff-main:2011"
|
||||
timeShiftBufferDepth="PT129600.000S"
|
||||
type="dynamic"
|
||||
xmlns="urn:mpeg:DASH:schema:MPD:2011"
|
||||
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd"
|
||||
yt:earliestMediaSequence="1266404" >
|
||||
<Period start="PT6462826.784S" >
|
||||
<SegmentList
|
||||
presentationTimeOffset="34740095"
|
||||
startNumber="1292317"
|
||||
timescale="1000" >
|
||||
<SegmentTimeline>
|
||||
<S d="4804" />
|
||||
<S d="5338" />
|
||||
<S d="4938" />
|
||||
</SegmentTimeline>
|
||||
</SegmentList>
|
||||
<AdaptationSet
|
||||
mimeType="audio/mp4"
|
||||
subsegmentAlignment="true" >
|
||||
<Role
|
||||
schemeIdUri="urn:mpeg:DASH:role:2011"
|
||||
value="main" />
|
||||
<Representation
|
||||
id="141"
|
||||
audioSamplingRate="48000"
|
||||
bandwidth="272000"
|
||||
codecs="mp4a.40.2"
|
||||
startWithSAP="1" >
|
||||
<AudioChannelConfiguration
|
||||
schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"
|
||||
value="2" />
|
||||
<BaseURL>
|
||||
http://www.test.com/141
|
||||
</BaseURL>
|
||||
<SegmentList>
|
||||
<Initialization
|
||||
range="0-591"
|
||||
sourceURL="sq/0/clen/79480/lmt/1403219262956762/dur/4.805" />
|
||||
<SegmentURL media="sq/1292317/clen/77447/lmt/1409671169987621/dur/4.805" />
|
||||
<SegmentURL media="sq/1292318/clen/86958/lmt/1409671174832549/dur/5.339" />
|
||||
<SegmentURL media="sq/1292319/clen/85018/lmt/1409671179719956/dur/4.938" />
|
||||
</SegmentList>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet
|
||||
mimeType="video/mp4"
|
||||
subsegmentAlignment="true" >
|
||||
<Role
|
||||
schemeIdUri="urn:mpeg:DASH:role:2011"
|
||||
value="main" />
|
||||
<Representation
|
||||
id="135"
|
||||
bandwidth="1116000"
|
||||
codecs="avc1.42c01f"
|
||||
height="480"
|
||||
startWithSAP="1"
|
||||
width="854" >
|
||||
<BaseURL>
|
||||
http://www.test.com/135
|
||||
</BaseURL>
|
||||
<SegmentList>
|
||||
<Initialization
|
||||
range="0-671"
|
||||
sourceURL="sq/0/clen/1221137/lmt/1403219262956762/dur/4.805" />
|
||||
<SegmentURL media="sq/1292317/clen/1279915/lmt/1409671169987621/dur/4.805" />
|
||||
<SegmentURL media="sq/1292318/clen/1310650/lmt/1409671174832549/dur/5.339" />
|
||||
<SegmentURL media="sq/1292319/clen/1486558/lmt/1409671179719956/dur/4.938" />
|
||||
</SegmentList>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet
|
||||
lang="en"
|
||||
mimeType="text/vtt" >
|
||||
<Role
|
||||
schemeIdUri="urn:mpeg:DASH:role:2011"
|
||||
value="caption" />
|
||||
<Representation
|
||||
id="en"
|
||||
bandwidth="0"
|
||||
codecs="" >
|
||||
<BaseURL>
|
||||
http://www.test.com/vtt
|
||||
</BaseURL>
|
||||
<SegmentList>
|
||||
<SegmentURL media="sq/1292317" />
|
||||
<SegmentURL media="sq/1292318" />
|
||||
<SegmentURL media="sq/1292319" />
|
||||
</SegmentList>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
|
BIN
library/src/test/assets/webm/vorbis_codec_private
Normal file
BIN
library/src/test/assets/webm/vorbis_codec_private
Normal file
Binary file not shown.
1
library/src/test/assets/webvtt/empty
Normal file
1
library/src/test/assets/webvtt/empty
Normal file
@ -0,0 +1 @@
|
||||
|
8
library/src/test/assets/webvtt/typical
Normal file
8
library/src/test/assets/webvtt/typical
Normal file
@ -0,0 +1,8 @@
|
||||
WEBVTT
|
||||
X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000
|
||||
|
||||
00:00.000 --> 00:01.234
|
||||
This is the first subtitle.
|
||||
|
||||
00:02.345 --> 00:03.456
|
||||
This is the second subtitle.
|
10
library/src/test/assets/webvtt/typical_with_identifiers
Normal file
10
library/src/test/assets/webvtt/typical_with_identifiers
Normal file
@ -0,0 +1,10 @@
|
||||
WEBVTT
|
||||
X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000
|
||||
|
||||
1
|
||||
00:00.000 --> 00:01.234
|
||||
This is the first subtitle.
|
||||
|
||||
2
|
||||
00:02.345 --> 00:03.456
|
||||
This is the second subtitle.
|
14
library/src/test/assets/webvtt/typical_with_tags
Normal file
14
library/src/test/assets/webvtt/typical_with_tags
Normal file
@ -0,0 +1,14 @@
|
||||
WEBVTT
|
||||
X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000
|
||||
|
||||
00:00.000 --> 00:01.234
|
||||
This is the <i>first</i> subtitle.
|
||||
|
||||
00:02.345 --> 00:03.456
|
||||
This is the <b><i>second</b></i> subtitle.
|
||||
|
||||
00:04.000 --> 00:05.000
|
||||
This is the <c.red.caps>third</c> subtitle.
|
||||
|
||||
00:06.000 --> 00:07.000
|
||||
This is the <fourth> &subtitle.
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit test for {@link MediaFormat}.
|
||||
*/
|
||||
public class MediaFormatTest extends TestCase {
|
||||
|
||||
public void testConversionToFrameworkFormat() {
|
||||
if (Util.SDK_INT < 16) {
|
||||
// Test doesn't apply.
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] initData1 = new byte[] {1, 2, 3};
|
||||
byte[] initData2 = new byte[] {4, 5, 6};
|
||||
List<byte[]> initData = new ArrayList<byte[]>();
|
||||
initData.add(initData1);
|
||||
initData.add(initData2);
|
||||
|
||||
testConversionToFrameworkFormatV16(
|
||||
MediaFormat.createVideoFormat("video/xyz", 102400, 1280, 720, 1.5f, initData));
|
||||
testConversionToFrameworkFormatV16(
|
||||
MediaFormat.createAudioFormat("audio/xyz", 102400, 5, 44100, initData));
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private void testConversionToFrameworkFormatV16(MediaFormat format) {
|
||||
// Convert to a framework MediaFormat and back again.
|
||||
MediaFormat convertedFormat = MediaFormat.createFromFrameworkMediaFormatV16(
|
||||
format.getFrameworkMediaFormatV16());
|
||||
// Assert that we end up with an equivalent object to the one we started with.
|
||||
assertEquals(format.hashCode(), convertedFormat.hashCode());
|
||||
assertEquals(format, convertedFormat);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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.chunk.parser.webm;
|
||||
|
||||
import com.google.android.exoplayer.ParserException;
|
||||
import com.google.android.exoplayer.upstream.ByteArrayNonBlockingInputStream;
|
||||
import com.google.android.exoplayer.upstream.NonBlockingInputStream;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests {@link DefaultEbmlReader}.
|
||||
*/
|
||||
public class DefaultEbmlReaderTest extends TestCase {
|
||||
|
||||
private final EventCapturingEbmlEventHandler eventHandler =
|
||||
new EventCapturingEbmlEventHandler();
|
||||
|
||||
public void testNothing() {
|
||||
NonBlockingInputStream input = createTestInputStream();
|
||||
assertNoEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM);
|
||||
}
|
||||
|
||||
public void testMasterElement() {
|
||||
NonBlockingInputStream input =
|
||||
createTestInputStream(0x1A, 0x45, 0xDF, 0xA3, 0x84, 0x42, 0x85, 0x81, 0x01);
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onMasterElementStart(EventCapturingEbmlEventHandler.ID_EBML, 0, 5, 4);
|
||||
expected.onIntegerElement(EventCapturingEbmlEventHandler.ID_DOC_TYPE_READ_VERSION, 1);
|
||||
expected.onMasterElementEnd(EventCapturingEbmlEventHandler.ID_EBML);
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testMasterElementEmpty() {
|
||||
NonBlockingInputStream input = createTestInputStream(0x18, 0x53, 0x80, 0x67, 0x80);
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onMasterElementStart(EventCapturingEbmlEventHandler.ID_SEGMENT, 0, 5, 0);
|
||||
expected.onMasterElementEnd(EventCapturingEbmlEventHandler.ID_SEGMENT);
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testUnsignedIntegerElement() {
|
||||
// 0xFE is chosen because for signed integers it should be interpreted as -2
|
||||
NonBlockingInputStream input = createTestInputStream(0x42, 0xF7, 0x81, 0xFE);
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onIntegerElement(EventCapturingEbmlEventHandler.ID_EBML_READ_VERSION, 254);
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testUnsignedIntegerElementLarge() {
|
||||
NonBlockingInputStream input =
|
||||
createTestInputStream(0x42, 0xF7, 0x88, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onIntegerElement(EventCapturingEbmlEventHandler.ID_EBML_READ_VERSION, Long.MAX_VALUE);
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testUnsignedIntegerElementTooLargeBecomesNegative() {
|
||||
NonBlockingInputStream input =
|
||||
createTestInputStream(0x42, 0xF7, 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onIntegerElement(EventCapturingEbmlEventHandler.ID_EBML_READ_VERSION, -1);
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testStringElement() {
|
||||
NonBlockingInputStream input =
|
||||
createTestInputStream(0x42, 0x82, 0x86, 0x41, 0x62, 0x63, 0x31, 0x32, 0x33);
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onStringElement(EventCapturingEbmlEventHandler.ID_DOC_TYPE, "Abc123");
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testStringElementEmpty() {
|
||||
NonBlockingInputStream input = createTestInputStream(0x42, 0x82, 0x80);
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onStringElement(EventCapturingEbmlEventHandler.ID_DOC_TYPE, "");
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testFloatElementThreeBytes() {
|
||||
try {
|
||||
eventHandler.read(createTestInputStream(0x44, 0x89, 0x83, 0x3F, 0x80, 0x00));
|
||||
fail();
|
||||
} catch (IllegalStateException exception) {
|
||||
// Expected
|
||||
}
|
||||
assertNoEvents();
|
||||
}
|
||||
|
||||
public void testFloatElementFourBytes() {
|
||||
NonBlockingInputStream input =
|
||||
createTestInputStream(0x44, 0x89, 0x84, 0x3F, 0x80, 0x00, 0x00);
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onFloatElement(EventCapturingEbmlEventHandler.ID_DURATION, 1.0);
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testFloatElementEightBytes() {
|
||||
NonBlockingInputStream input =
|
||||
createTestInputStream(0x44, 0x89, 0x88, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.onFloatElement(EventCapturingEbmlEventHandler.ID_DURATION, -2.0);
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testBinaryElementReadBytes() {
|
||||
eventHandler.binaryElementHandler = EventCapturingEbmlEventHandler.HANDLER_READ_BYTES;
|
||||
NonBlockingInputStream input =
|
||||
createTestInputStream(0xA3, 0x88, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
|
||||
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.binaryElementHandler = EventCapturingEbmlEventHandler.HANDLER_READ_BYTES;
|
||||
expected.onBinaryElement(
|
||||
EventCapturingEbmlEventHandler.ID_SIMPLE_BLOCK, 0, 0, 8,
|
||||
createTestInputStream(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08));
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testBinaryElementReadVarint() {
|
||||
eventHandler.binaryElementHandler = EventCapturingEbmlEventHandler.HANDLER_READ_VARINT;
|
||||
NonBlockingInputStream input = createTestInputStream(0xA3, 0x82, 0x40, 0x2A);
|
||||
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.binaryElementHandler = EventCapturingEbmlEventHandler.HANDLER_READ_VARINT;
|
||||
expected.onBinaryElement(
|
||||
EventCapturingEbmlEventHandler.ID_SIMPLE_BLOCK, 0, 0, 0,
|
||||
createTestInputStream(0x40, 0x2A));
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testBinaryElementSkipBytes() {
|
||||
eventHandler.binaryElementHandler = EventCapturingEbmlEventHandler.HANDLER_SKIP_BYTES;
|
||||
NonBlockingInputStream input =
|
||||
createTestInputStream(0xA3, 0x88, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
|
||||
|
||||
EventCapturingEbmlEventHandler expected = new EventCapturingEbmlEventHandler();
|
||||
expected.binaryElementHandler = EventCapturingEbmlEventHandler.HANDLER_SKIP_BYTES;
|
||||
expected.onBinaryElement(
|
||||
EventCapturingEbmlEventHandler.ID_SIMPLE_BLOCK, 0, 0, 8,
|
||||
createTestInputStream(0, 0, 0, 0, 0, 0, 0, 0));
|
||||
assertEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM, expected.events);
|
||||
}
|
||||
|
||||
public void testBinaryElementDoNothing() {
|
||||
eventHandler.binaryElementHandler = EventCapturingEbmlEventHandler.HANDLER_DO_NOTHING;
|
||||
try {
|
||||
eventHandler.read(
|
||||
createTestInputStream(0xA3, 0x88, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08));
|
||||
fail();
|
||||
} catch (IllegalStateException exception) {
|
||||
// Expected
|
||||
}
|
||||
assertNoEvents();
|
||||
}
|
||||
|
||||
public void testBinaryElementNotEnoughBytes() {
|
||||
NonBlockingInputStream input = createTestInputStream(0xA3, 0x88, 0x01, 0x02, 0x03);
|
||||
assertNoEvents(input, EbmlReader.READ_RESULT_NEED_MORE_DATA);
|
||||
}
|
||||
|
||||
public void testUnknownElement() {
|
||||
NonBlockingInputStream input = createTestInputStream(0xEC, 0x81, 0x00);
|
||||
assertNoEvents(input, EbmlReader.READ_RESULT_END_OF_STREAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to build a {@link ByteArrayNonBlockingInputStream} quickly from zero or more
|
||||
* integer arguments.
|
||||
*
|
||||
* <p>Each argument must be able to cast to a byte value.
|
||||
*
|
||||
* @param data Zero or more integers with values between {@code 0x00} and {@code 0xFF}
|
||||
* @return A {@link ByteArrayNonBlockingInputStream} containing the given byte values
|
||||
*/
|
||||
private NonBlockingInputStream createTestInputStream(int... data) {
|
||||
byte[] bytes = new byte[data.length];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
bytes[i] = (byte) data[i];
|
||||
}
|
||||
return new ByteArrayNonBlockingInputStream(bytes);
|
||||
}
|
||||
|
||||
private void assertReads(NonBlockingInputStream input, int continues, int finalResult) {
|
||||
for (int i = 0; i < continues; i++) {
|
||||
assertEquals(EbmlReader.READ_RESULT_CONTINUE, eventHandler.read(input));
|
||||
}
|
||||
assertEquals(finalResult, eventHandler.read(input));
|
||||
}
|
||||
|
||||
private void assertNoEvents() {
|
||||
assertEvents(Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
private void assertEvents(List<String> events) {
|
||||
assertEquals(events.size(), eventHandler.events.size());
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
assertEquals(events.get(i), eventHandler.events.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertNoEvents(NonBlockingInputStream input, int finalResult) {
|
||||
assertReads(input, 0, finalResult);
|
||||
assertNoEvents();
|
||||
}
|
||||
|
||||
private void assertEvents(NonBlockingInputStream input, int finalResult, List<String> events) {
|
||||
assertReads(input, events.size(), finalResult);
|
||||
assertEvents(events);
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link EbmlEventHandler} which captures all event callbacks made by
|
||||
* {@link DefaultEbmlReader} for testing purposes.
|
||||
*/
|
||||
private static final class EventCapturingEbmlEventHandler implements EbmlEventHandler {
|
||||
|
||||
// Element IDs
|
||||
private static final int ID_EBML = 0x1A45DFA3;
|
||||
private static final int ID_EBML_READ_VERSION = 0x42F7;
|
||||
private static final int ID_DOC_TYPE = 0x4282;
|
||||
private static final int ID_DOC_TYPE_READ_VERSION = 0x4285;
|
||||
|
||||
private static final int ID_SEGMENT = 0x18538067;
|
||||
private static final int ID_DURATION = 0x4489;
|
||||
private static final int ID_SIMPLE_BLOCK = 0xA3;
|
||||
|
||||
// Various ways to handle things in onBinaryElement()
|
||||
private static final int HANDLER_DO_NOTHING = 0;
|
||||
private static final int HANDLER_READ_BYTES = 1;
|
||||
private static final int HANDLER_READ_VARINT = 2;
|
||||
private static final int HANDLER_SKIP_BYTES = 3;
|
||||
|
||||
private final EbmlReader reader = new DefaultEbmlReader();
|
||||
private final List<String> events = new ArrayList<String>();
|
||||
|
||||
private int binaryElementHandler;
|
||||
|
||||
private EventCapturingEbmlEventHandler() {
|
||||
reader.setEventHandler(this);
|
||||
}
|
||||
|
||||
private int read(NonBlockingInputStream inputStream) {
|
||||
try {
|
||||
return reader.read(inputStream);
|
||||
} catch (ParserException e) {
|
||||
// should never happen.
|
||||
fail();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getElementType(int id) {
|
||||
switch (id) {
|
||||
case ID_EBML:
|
||||
case ID_SEGMENT:
|
||||
return EbmlReader.TYPE_MASTER;
|
||||
case ID_EBML_READ_VERSION:
|
||||
case ID_DOC_TYPE_READ_VERSION:
|
||||
return EbmlReader.TYPE_UNSIGNED_INT;
|
||||
case ID_DOC_TYPE:
|
||||
return EbmlReader.TYPE_STRING;
|
||||
case ID_SIMPLE_BLOCK:
|
||||
return EbmlReader.TYPE_BINARY;
|
||||
case ID_DURATION:
|
||||
return EbmlReader.TYPE_FLOAT;
|
||||
default:
|
||||
return EbmlReader.TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMasterElementStart(
|
||||
int id, long elementOffset, int headerSize, long contentsSize) {
|
||||
events.add(formatEvent(id, "start elementOffset=" + elementOffset
|
||||
+ " headerSize=" + headerSize + " contentsSize=" + contentsSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMasterElementEnd(int id) {
|
||||
events.add(formatEvent(id, "end"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIntegerElement(int id, long value) {
|
||||
events.add(formatEvent(id, "integer=" + String.valueOf(value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFloatElement(int id, double value) {
|
||||
events.add(formatEvent(id, "float=" + String.valueOf(value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStringElement(int id, String value) {
|
||||
events.add(formatEvent(id, "string=" + value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBinaryElement(
|
||||
int id, long elementOffset, int headerSize, int contentsSize,
|
||||
NonBlockingInputStream inputStream) {
|
||||
switch (binaryElementHandler) {
|
||||
case HANDLER_READ_BYTES:
|
||||
byte[] bytes = new byte[contentsSize];
|
||||
reader.readBytes(inputStream, bytes, contentsSize);
|
||||
events.add(formatEvent(id, "bytes=" + Arrays.toString(bytes)));
|
||||
break;
|
||||
case HANDLER_READ_VARINT:
|
||||
long value = reader.readVarint(inputStream);
|
||||
events.add(formatEvent(id, "varint=" + String.valueOf(value)));
|
||||
break;
|
||||
case HANDLER_SKIP_BYTES:
|
||||
reader.skipBytes(inputStream, contentsSize);
|
||||
events.add(formatEvent(id, "skipped " + contentsSize + " byte(s)"));
|
||||
break;
|
||||
case HANDLER_DO_NOTHING:
|
||||
default:
|
||||
// pass
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String formatEvent(int id, String event) {
|
||||
return "[" + Integer.toHexString(id) + "] " + event;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,523 @@
|
||||
/*
|
||||
* 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.chunk.parser.webm;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.MediaFormat;
|
||||
import com.google.android.exoplayer.ParserException;
|
||||
import com.google.android.exoplayer.SampleHolder;
|
||||
import com.google.android.exoplayer.chunk.parser.SegmentIndex;
|
||||
import com.google.android.exoplayer.upstream.ByteArrayNonBlockingInputStream;
|
||||
import com.google.android.exoplayer.upstream.NonBlockingInputStream;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class WebmExtractorTest extends InstrumentationTestCase {
|
||||
|
||||
private static final int INFO_ELEMENT_BYTE_SIZE = 31;
|
||||
private static final int TRACKS_ELEMENT_BYTE_SIZE = 48;
|
||||
private static final int CUES_ELEMENT_BYTE_SIZE = 12;
|
||||
private static final int CUE_POINT_ELEMENT_BYTE_SIZE = 31;
|
||||
|
||||
private static final int DEFAULT_TIMECODE_SCALE = 1000000;
|
||||
|
||||
private static final long TEST_DURATION_US = 9920000L;
|
||||
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 long TEST_CODEC_DELAY = 6500000;
|
||||
private static final long TEST_SEEK_PRE_ROLL = 80000000;
|
||||
private static final int TEST_OPUS_CODEC_PRIVATE_SIZE = 2;
|
||||
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 int ID_VP9 = 0;
|
||||
private static final int ID_OPUS = 1;
|
||||
private static final int ID_VORBIS = 2;
|
||||
|
||||
private static final int EXPECTED_INIT_RESULT = WebmExtractor.RESULT_READ_INIT
|
||||
| WebmExtractor.RESULT_READ_INDEX | WebmExtractor.RESULT_END_OF_STREAM;
|
||||
private static final int EXPECTED_INIT_AND_SAMPLE_RESULT = WebmExtractor.RESULT_READ_INIT
|
||||
| WebmExtractor.RESULT_READ_INDEX | WebmExtractor.RESULT_READ_SAMPLE;
|
||||
|
||||
private final WebmExtractor extractor = new WebmExtractor();
|
||||
private final SampleHolder sampleHolder =
|
||||
new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
sampleHolder.data = ByteBuffer.allocate(1024);
|
||||
}
|
||||
|
||||
public void testPrepare() throws ParserException {
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(
|
||||
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9));
|
||||
assertEquals(EXPECTED_INIT_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertFormat();
|
||||
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
|
||||
}
|
||||
|
||||
public void testPrepareOpus() throws ParserException {
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(
|
||||
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_OPUS));
|
||||
assertEquals(EXPECTED_INIT_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertAudioFormat(ID_OPUS);
|
||||
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
|
||||
}
|
||||
|
||||
public void testPrepareVorbis() throws ParserException {
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(
|
||||
createInitializationSegment(1, 0, true, DEFAULT_TIMECODE_SCALE, ID_VORBIS));
|
||||
assertEquals(EXPECTED_INIT_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertAudioFormat(ID_VORBIS);
|
||||
assertIndex(new IndexPoint(0, 0, TEST_DURATION_US));
|
||||
}
|
||||
|
||||
public void testPrepareThreeCuePoints() throws ParserException {
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(
|
||||
createInitializationSegment(3, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9));
|
||||
assertEquals(EXPECTED_INIT_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertFormat();
|
||||
assertIndex(
|
||||
new IndexPoint(0, 0, 10000),
|
||||
new IndexPoint(10000, 0, 10000),
|
||||
new IndexPoint(20000, 0, TEST_DURATION_US - 20000));
|
||||
}
|
||||
|
||||
public void testPrepareCustomTimecodeScale() throws ParserException {
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(
|
||||
createInitializationSegment(3, 0, true, 1000, ID_VP9));
|
||||
assertEquals(EXPECTED_INIT_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertFormat();
|
||||
assertIndex(
|
||||
new IndexPoint(0, 0, 10),
|
||||
new IndexPoint(10, 0, 10),
|
||||
new IndexPoint(20, 0, (TEST_DURATION_US / 1000) - 20));
|
||||
}
|
||||
|
||||
public void testPrepareNoCuePoints() {
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(
|
||||
createInitializationSegment(0, 0, true, DEFAULT_TIMECODE_SCALE, ID_VP9));
|
||||
try {
|
||||
extractor.read(testInputStream, sampleHolder);
|
||||
fail();
|
||||
} catch (ParserException exception) {
|
||||
assertEquals("Invalid/missing cue points", exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testPrepareInvalidDocType() {
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(
|
||||
createInitializationSegment(1, 0, false, DEFAULT_TIMECODE_SCALE, ID_VP9));
|
||||
try {
|
||||
extractor.read(testInputStream, sampleHolder);
|
||||
fail();
|
||||
} catch (ParserException exception) {
|
||||
assertEquals("DocType webB not supported", exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testReadSampleKeyframe() throws ParserException {
|
||||
MediaSegment mediaSegment = createMediaSegment(100, 0, 0, true, false, true);
|
||||
byte[] testInputData = joinByteArrays(
|
||||
createInitializationSegment(
|
||||
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_VP9),
|
||||
mediaSegment.clusterBytes);
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(testInputData);
|
||||
assertEquals(EXPECTED_INIT_AND_SAMPLE_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertFormat();
|
||||
assertSample(mediaSegment, 0, true, false);
|
||||
assertEquals(WebmExtractor.RESULT_END_OF_STREAM, extractor.read(testInputStream, sampleHolder));
|
||||
}
|
||||
|
||||
public void testReadBlock() throws ParserException {
|
||||
MediaSegment mediaSegment = createMediaSegment(100, 0, 0, true, false, false);
|
||||
byte[] testInputData = joinByteArrays(
|
||||
createInitializationSegment(
|
||||
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_OPUS),
|
||||
mediaSegment.clusterBytes);
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(testInputData);
|
||||
assertEquals(EXPECTED_INIT_AND_SAMPLE_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertAudioFormat(ID_OPUS);
|
||||
assertSample(mediaSegment, 0, true, false);
|
||||
assertEquals(WebmExtractor.RESULT_END_OF_STREAM, extractor.read(testInputStream, sampleHolder));
|
||||
}
|
||||
|
||||
public void testReadSampleInvisible() throws ParserException {
|
||||
MediaSegment mediaSegment = createMediaSegment(100, 12, 13, false, true, true);
|
||||
byte[] testInputData = joinByteArrays(
|
||||
createInitializationSegment(
|
||||
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_VP9),
|
||||
mediaSegment.clusterBytes);
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(testInputData);
|
||||
assertEquals(EXPECTED_INIT_AND_SAMPLE_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertFormat();
|
||||
assertSample(mediaSegment, 25000, false, true);
|
||||
assertEquals(WebmExtractor.RESULT_END_OF_STREAM, extractor.read(testInputStream, sampleHolder));
|
||||
}
|
||||
|
||||
public void testReadSampleCustomTimescale() throws ParserException {
|
||||
MediaSegment mediaSegment = createMediaSegment(100, 12, 13, false, false, true);
|
||||
byte[] testInputData = joinByteArrays(
|
||||
createInitializationSegment(
|
||||
1, mediaSegment.clusterBytes.length, true, 1000, ID_VP9),
|
||||
mediaSegment.clusterBytes);
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(testInputData);
|
||||
assertEquals(EXPECTED_INIT_AND_SAMPLE_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertFormat();
|
||||
assertSample(mediaSegment, 25, false, false);
|
||||
assertEquals(WebmExtractor.RESULT_END_OF_STREAM, extractor.read(testInputStream, sampleHolder));
|
||||
}
|
||||
|
||||
public void testReadSampleNegativeSimpleBlockTimecode() throws ParserException {
|
||||
MediaSegment mediaSegment = createMediaSegment(100, 13, -12, true, true, true);
|
||||
byte[] testInputData = joinByteArrays(
|
||||
createInitializationSegment(
|
||||
1, mediaSegment.clusterBytes.length, true, DEFAULT_TIMECODE_SCALE, ID_VP9),
|
||||
mediaSegment.clusterBytes);
|
||||
NonBlockingInputStream testInputStream = new ByteArrayNonBlockingInputStream(testInputData);
|
||||
assertEquals(EXPECTED_INIT_AND_SAMPLE_RESULT, extractor.read(testInputStream, sampleHolder));
|
||||
assertFormat();
|
||||
assertSample(mediaSegment, 1000, true, true);
|
||||
assertEquals(WebmExtractor.RESULT_END_OF_STREAM, extractor.read(testInputStream, sampleHolder));
|
||||
}
|
||||
|
||||
private void assertFormat() {
|
||||
MediaFormat format = extractor.getFormat();
|
||||
assertEquals(TEST_WIDTH, format.width);
|
||||
assertEquals(TEST_HEIGHT, format.height);
|
||||
assertEquals(MimeTypes.VIDEO_VP9, format.mimeType);
|
||||
}
|
||||
|
||||
private void assertAudioFormat(int codecId) {
|
||||
MediaFormat format = extractor.getFormat();
|
||||
assertEquals(TEST_CHANNEL_COUNT, format.channelCount);
|
||||
assertEquals(TEST_SAMPLE_RATE, format.sampleRate);
|
||||
if (codecId == ID_OPUS) {
|
||||
assertEquals(MimeTypes.AUDIO_OPUS, format.mimeType);
|
||||
assertEquals(3, format.initializationData.size());
|
||||
assertEquals(TEST_OPUS_CODEC_PRIVATE_SIZE, format.initializationData.get(0).length);
|
||||
assertEquals(TEST_CODEC_DELAY, ByteBuffer.wrap(format.initializationData.get(1)).getLong());
|
||||
assertEquals(TEST_SEEK_PRE_ROLL, ByteBuffer.wrap(format.initializationData.get(2)).getLong());
|
||||
} else if (codecId == ID_VORBIS) {
|
||||
assertEquals(MimeTypes.AUDIO_VORBIS, format.mimeType);
|
||||
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 assertIndex(IndexPoint... indexPoints) {
|
||||
SegmentIndex index = extractor.getIndex();
|
||||
assertEquals(CUES_ELEMENT_BYTE_SIZE + CUE_POINT_ELEMENT_BYTE_SIZE * indexPoints.length,
|
||||
index.sizeBytes);
|
||||
assertEquals(indexPoints.length, index.length);
|
||||
for (int i = 0; i < indexPoints.length; i++) {
|
||||
IndexPoint indexPoint = indexPoints[i];
|
||||
assertEquals(indexPoint.timeUs, index.timesUs[i]);
|
||||
assertEquals(indexPoint.size, index.sizes[i]);
|
||||
assertEquals(indexPoint.durationUs, index.durationsUs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSample(
|
||||
MediaSegment mediaSegment, int timeUs, boolean keyframe, boolean invisible) {
|
||||
assertTrue(Arrays.equals(
|
||||
mediaSegment.videoBytes, Arrays.copyOf(sampleHolder.data.array(), sampleHolder.size)));
|
||||
assertEquals(timeUs, sampleHolder.timeUs);
|
||||
assertEquals(keyframe, (sampleHolder.flags & C.SAMPLE_FLAG_SYNC) != 0);
|
||||
assertEquals(invisible, sampleHolder.decodeOnly);
|
||||
}
|
||||
|
||||
private byte[] createInitializationSegment(
|
||||
int cuePoints, int mediaSegmentSize, boolean docTypeIsWebm, int timecodeScale,
|
||||
int codecId) {
|
||||
int initalizationSegmentSize = INFO_ELEMENT_BYTE_SIZE + TRACKS_ELEMENT_BYTE_SIZE
|
||||
+ CUES_ELEMENT_BYTE_SIZE + CUE_POINT_ELEMENT_BYTE_SIZE * cuePoints;
|
||||
byte[] tracksElement = null;
|
||||
switch (codecId) {
|
||||
case ID_VP9:
|
||||
tracksElement = createTracksElementWithVideo(true, TEST_WIDTH, TEST_HEIGHT);
|
||||
break;
|
||||
case ID_OPUS:
|
||||
tracksElement = createTracksElementWithOpusAudio(TEST_CHANNEL_COUNT);
|
||||
break;
|
||||
case ID_VORBIS:
|
||||
tracksElement = createTracksElementWithVorbisAudio(TEST_CHANNEL_COUNT);
|
||||
break;
|
||||
}
|
||||
byte[] bytes = joinByteArrays(createEbmlElement(1, docTypeIsWebm, 2),
|
||||
createSegmentElement(initalizationSegmentSize + mediaSegmentSize),
|
||||
createInfoElement(timecodeScale),
|
||||
tracksElement,
|
||||
createCuesElement(CUE_POINT_ELEMENT_BYTE_SIZE * cuePoints));
|
||||
for (int i = 0; i < cuePoints; i++) {
|
||||
bytes = joinByteArrays(bytes, createCuePointElement(10 * i, initalizationSegmentSize));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static MediaSegment createMediaSegment(int videoBytesLength, int clusterTimecode,
|
||||
int blockTimecode, boolean keyframe, boolean invisible, boolean isSimple) {
|
||||
byte[] videoBytes = createVideoBytes(videoBytesLength);
|
||||
byte[] blockBytes;
|
||||
if (isSimple) {
|
||||
blockBytes = createSimpleBlockElement(videoBytes.length, blockTimecode,
|
||||
keyframe, invisible, true);
|
||||
} else {
|
||||
blockBytes = createBlockElement(videoBytes.length, blockTimecode, invisible, true);
|
||||
}
|
||||
byte[] clusterBytes =
|
||||
createClusterElement(blockBytes.length + videoBytes.length, clusterTimecode);
|
||||
return new MediaSegment(joinByteArrays(clusterBytes, blockBytes, videoBytes), videoBytes);
|
||||
}
|
||||
|
||||
private static byte[] joinByteArrays(byte[]... byteArrays) {
|
||||
int length = 0;
|
||||
for (byte[] byteArray : byteArrays) {
|
||||
length += byteArray.length;
|
||||
}
|
||||
byte[] joined = new byte[length];
|
||||
length = 0;
|
||||
for (byte[] byteArray : byteArrays) {
|
||||
System.arraycopy(byteArray, 0, joined, length, byteArray.length);
|
||||
length += byteArray.length;
|
||||
}
|
||||
return joined;
|
||||
}
|
||||
|
||||
private static byte[] createEbmlElement(
|
||||
int ebmlReadVersion, boolean docTypeIsWebm, int docTypeReadVersion) {
|
||||
return createByteArray(
|
||||
0x1A, 0x45, 0xDF, 0xA3, // EBML
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, // size=15
|
||||
0x42, 0xF7, // EBMLReadVersion
|
||||
0x81, ebmlReadVersion, // size=1
|
||||
0x42, 0x82, // DocType
|
||||
0x84, 0x77, 0x65, 0x62, docTypeIsWebm ? 0x6D : 0x42, // size=4 value=webm/B
|
||||
0x42, 0x85, // DocTypeReadVersion
|
||||
0x81, docTypeReadVersion); // size=1
|
||||
}
|
||||
|
||||
private static byte[] createSegmentElement(int size) {
|
||||
byte[] sizeBytes = getIntegerBytes(size);
|
||||
return createByteArray(
|
||||
0x18, 0x53, 0x80, 0x67, // Segment
|
||||
0x01, 0x00, 0x00, 0x00, sizeBytes[0], sizeBytes[1], sizeBytes[2], sizeBytes[3]);
|
||||
}
|
||||
|
||||
private static byte[] createInfoElement(int timecodeScale) {
|
||||
byte[] scaleBytes = getIntegerBytes(timecodeScale);
|
||||
return createByteArray(
|
||||
0x15, 0x49, 0xA9, 0x66, // Info
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, // size=19
|
||||
0x2A, 0xD7, 0xB1, // TimecodeScale
|
||||
0x84, scaleBytes[0], scaleBytes[1], scaleBytes[2], scaleBytes[3], // size=4
|
||||
0x44, 0x89, // Duration
|
||||
0x88, 0x40, 0xC3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00); // size=8 value=9920.0
|
||||
}
|
||||
|
||||
private static byte[] createTracksElementWithVideo(
|
||||
boolean codecIsVp9, int pixelWidth, int pixelHeight) {
|
||||
byte[] widthBytes = getIntegerBytes(pixelWidth);
|
||||
byte[] heightBytes = getIntegerBytes(pixelHeight);
|
||||
return createByteArray(
|
||||
0x16, 0x54, 0xAE, 0x6B, // Tracks
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, // size=36
|
||||
0xAE, // TrackEntry
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, // size=27
|
||||
0x86, // CodecID
|
||||
0x85, 0x56, 0x5F, 0x56, 0x50, codecIsVp9 ? 0x39 : 0x30, // size=5 value=V_VP9/0
|
||||
0xE0, // Video
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // size=8
|
||||
0xB0, // PixelWidth
|
||||
0x82, widthBytes[2], widthBytes[3], // size=2
|
||||
0xBA, // PixelHeight
|
||||
0x82, heightBytes[2], heightBytes[3]); // size=2
|
||||
}
|
||||
|
||||
private static byte[] createTracksElementWithOpusAudio(int channelCount) {
|
||||
byte[] channelCountBytes = getIntegerBytes(channelCount);
|
||||
return createByteArray(
|
||||
0x16, 0x54, 0xAE, 0x6B, // Tracks
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, // size=57
|
||||
0xAE, // TrackEntry
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, // size=48
|
||||
0x86, // CodecID
|
||||
0x86, 0x41, 0x5F, 0x4F, 0x50, 0x55, 0x53, // size=6 value=A_OPUS
|
||||
0x56, 0xAA, // CodecDelay
|
||||
0x83, 0x63, 0x2E, 0xA0, // size=3 value=6500000
|
||||
0x56, 0xBB, // SeekPreRoll
|
||||
0x84, 0x04, 0xC4, 0xB4, 0x00, // size=4 value=80000000
|
||||
0xE1, // Audio
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, // size=13
|
||||
0x9F, // Channels
|
||||
0x81, channelCountBytes[3], // size=1
|
||||
0xB5, // SamplingFrequency
|
||||
0x88, 0x40, 0xE7, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, // size=8 value=48000
|
||||
0x63, 0xA2, // CodecPrivate
|
||||
0x82, 0x00, 0x00); // size=2
|
||||
}
|
||||
|
||||
private byte[] createTracksElementWithVorbisAudio(int channelCount) {
|
||||
byte[] channelCountBytes = getIntegerBytes(channelCount);
|
||||
byte[] tracksElement = createByteArray(
|
||||
0x16, 0x54, 0xAE, 0x6B, // Tracks
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x9C, // size=4252
|
||||
0xAE, // TrackEntry
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x93, // size=4243 (36+4207)
|
||||
0x86, // CodecID
|
||||
0x88, 0x41, 0x5f, 0x56, 0x4f, 0x52, 0x42, 0x49, 0x53, // size=8 value=A_VORBIS
|
||||
0xE1, // Audio
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, // size=13
|
||||
0x9F, // Channels
|
||||
0x81, channelCountBytes[3], // size=1
|
||||
0xB5, // SamplingFrequency
|
||||
0x88, 0x40, 0xE7, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, // size=8 value=48000
|
||||
0x63, 0xA2, // CodecPrivate
|
||||
0x50, 0x6F); // size=4207
|
||||
byte[] codecPrivate = new byte[4207];
|
||||
try {
|
||||
getInstrumentation().getContext().getResources().getAssets().open(TEST_VORBIS_CODEC_PRIVATE)
|
||||
.read(codecPrivate);
|
||||
} catch (IOException e) {
|
||||
fail(); // should never happen
|
||||
}
|
||||
return joinByteArrays(tracksElement, codecPrivate);
|
||||
}
|
||||
|
||||
private static byte[] createCuesElement(int size) {
|
||||
byte[] sizeBytes = getIntegerBytes(size);
|
||||
return createByteArray(
|
||||
0x1C, 0x53, 0xBB, 0x6B, // Cues
|
||||
0x01, 0x00, 0x00, 0x00, sizeBytes[0], sizeBytes[1], sizeBytes[2], sizeBytes[3]); // size=31
|
||||
}
|
||||
|
||||
private static byte[] createCuePointElement(int cueTime, int cueClusterPosition) {
|
||||
byte[] positionBytes = getIntegerBytes(cueClusterPosition);
|
||||
return createByteArray(
|
||||
0xBB, // CuePoint
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, // size=22
|
||||
0xB3, // CueTime
|
||||
0x81, cueTime, // size=1
|
||||
0xB7, // CueTrackPositions
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, // size=10
|
||||
0xF1, // CueClusterPosition
|
||||
0x88, 0x00, 0x00, 0x00, 0x00, positionBytes[0], positionBytes[1],
|
||||
positionBytes[2], positionBytes[3]); // size=8
|
||||
}
|
||||
|
||||
private static byte[] createClusterElement(int size, int timecode) {
|
||||
byte[] sizeBytes = getIntegerBytes(size);
|
||||
byte[] timeBytes = getIntegerBytes(timecode);
|
||||
return createByteArray(
|
||||
0x1F, 0x43, 0xB6, 0x75, // Cluster
|
||||
0x01, 0x00, 0x00, 0x00, sizeBytes[0], sizeBytes[1], sizeBytes[2], sizeBytes[3],
|
||||
0xE7, // Timecode
|
||||
0x84, timeBytes[0], timeBytes[1], timeBytes[2], timeBytes[3]); // size=4
|
||||
}
|
||||
|
||||
private static byte[] createSimpleBlockElement(
|
||||
int size, int timecode, boolean keyframe, boolean invisible, boolean noLacing) {
|
||||
byte[] sizeBytes = getIntegerBytes(size + 4);
|
||||
byte[] timeBytes = getIntegerBytes(timecode);
|
||||
byte flags = (byte)
|
||||
((keyframe ? 0x80 : 0x00) | (invisible ? 0x08 : 0x00) | (noLacing ? 0x00 : 0x06));
|
||||
return createByteArray(
|
||||
0xA3, // SimpleBlock
|
||||
0x01, 0x00, 0x00, 0x00, sizeBytes[0], sizeBytes[1], sizeBytes[2], sizeBytes[3],
|
||||
0x81, // Track number value=1
|
||||
timeBytes[2], timeBytes[3], flags); // Timecode and flags
|
||||
}
|
||||
|
||||
private static byte[] createBlockElement(
|
||||
int size, int timecode, boolean invisible, boolean noLacing) {
|
||||
int blockSize = size + 4;
|
||||
byte[] blockSizeBytes = getIntegerBytes(blockSize);
|
||||
byte[] timeBytes = getIntegerBytes(timecode);
|
||||
int blockElementSize = 1 + 8 + blockSize; // id + size + length of data
|
||||
byte[] sizeBytes = getIntegerBytes(blockElementSize);
|
||||
byte flags = (byte) ((invisible ? 0x08 : 0x00) | (noLacing ? 0x00 : 0x06));
|
||||
return createByteArray(
|
||||
0xA0, // BlockGroup
|
||||
0x01, 0x00, 0x00, 0x00, sizeBytes[0], sizeBytes[1], sizeBytes[2], sizeBytes[3],
|
||||
0xA1, // Block
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
blockSizeBytes[0], blockSizeBytes[1], blockSizeBytes[2], blockSizeBytes[3],
|
||||
0x81, // Track number value=1
|
||||
timeBytes[2], timeBytes[3], flags); // Timecode and flags
|
||||
}
|
||||
|
||||
private static byte[] createVideoBytes(int size) {
|
||||
byte[] videoBytes = new byte[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
videoBytes[i] = (byte) i;
|
||||
}
|
||||
return videoBytes;
|
||||
}
|
||||
|
||||
private static byte[] getIntegerBytes(int value) {
|
||||
return createByteArray(
|
||||
(value & 0xFF000000) >> 24,
|
||||
(value & 0x00FF0000) >> 16,
|
||||
(value & 0x0000FF00) >> 8,
|
||||
(value & 0x000000FF));
|
||||
}
|
||||
|
||||
private static byte[] createByteArray(int... intArray) {
|
||||
byte[] byteArray = new byte[intArray.length];
|
||||
for (int i = 0; i < byteArray.length; i++) {
|
||||
byteArray[i] = (byte) intArray[i];
|
||||
}
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
/** Used by {@link #createMediaSegment} to return both cluster and video bytes together. */
|
||||
private static final class MediaSegment {
|
||||
|
||||
private final byte[] clusterBytes;
|
||||
private final byte[] videoBytes;
|
||||
|
||||
private MediaSegment(byte[] clusterBytes, byte[] videoBytes) {
|
||||
this.clusterBytes = clusterBytes;
|
||||
this.videoBytes = videoBytes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.dash;
|
||||
|
||||
import com.google.android.exoplayer.MediaFormat;
|
||||
import com.google.android.exoplayer.chunk.Format;
|
||||
import com.google.android.exoplayer.dash.mpd.Representation;
|
||||
import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests {@link DashChunkSource}.
|
||||
*/
|
||||
public class DashChunkSourceTest extends TestCase {
|
||||
|
||||
public void testMaxVideoDimensions() {
|
||||
SingleSegmentBase segmentBase1 = new SingleSegmentBase("https://example.com/1.mp4");
|
||||
Format format1 = new Format("1", "video/mp4", 100, 200, -1, -1, 1000);
|
||||
Representation representation1 =
|
||||
Representation.newInstance(0, 0, null, 0, format1, segmentBase1);
|
||||
|
||||
SingleSegmentBase segmentBase2 = new SingleSegmentBase("https://example.com/2.mp4");
|
||||
Format format2 = new Format("2", "video/mp4", 400, 50, -1, -1, 1000);
|
||||
Representation representation2 =
|
||||
Representation.newInstance(0, 0, null, 0, format2, segmentBase2);
|
||||
|
||||
DashChunkSource chunkSource = new DashChunkSource(null, null, representation1, representation2);
|
||||
MediaFormat out = MediaFormat.createVideoFormat("video/h264", 1, 1, 1, 1, null);
|
||||
chunkSource.getMaxVideoDimensions(out);
|
||||
|
||||
assertEquals(400, out.getMaxVideoWidth());
|
||||
assertEquals(200, out.getMaxVideoHeight());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.dash.mpd;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MediaPresentationDescriptionParser}.
|
||||
*/
|
||||
public class MediaPresentationDescriptionParserTest extends InstrumentationTestCase {
|
||||
|
||||
private static final String SAMPLE_MPD_1 = "dash/sample_mpd_1";
|
||||
|
||||
public void testParseMediaPresentationDescription() throws IOException {
|
||||
MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser();
|
||||
InputStream inputStream =
|
||||
getInstrumentation().getContext().getResources().getAssets().open(SAMPLE_MPD_1);
|
||||
// Simple test to ensure that the sample manifest parses without throwing any exceptions.
|
||||
parser.parse("https://example.com/test.mpd", inputStream);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.dash.mpd;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link RangedUri}.
|
||||
*/
|
||||
public class RangedUriTest extends TestCase {
|
||||
|
||||
private static final String FULL_URI = "http://www.test.com/path/file.ext";
|
||||
|
||||
public void testMerge() {
|
||||
RangedUri rangeA = new RangedUri(null, FULL_URI, 0, 10);
|
||||
RangedUri rangeB = new RangedUri(null, FULL_URI, 10, 10);
|
||||
RangedUri expected = new RangedUri(null, FULL_URI, 0, 20);
|
||||
assertMerge(rangeA, rangeB, expected);
|
||||
}
|
||||
|
||||
public void testMergeUnbounded() {
|
||||
RangedUri rangeA = new RangedUri(null, FULL_URI, 0, 10);
|
||||
RangedUri rangeB = new RangedUri(null, FULL_URI, 10, -1);
|
||||
RangedUri expected = new RangedUri(null, FULL_URI, 0, -1);
|
||||
assertMerge(rangeA, rangeB, expected);
|
||||
}
|
||||
|
||||
public void testNonMerge() {
|
||||
// A and B do not overlap, so should not merge
|
||||
RangedUri rangeA = new RangedUri(null, FULL_URI, 0, 10);
|
||||
RangedUri rangeB = new RangedUri(null, FULL_URI, 11, 10);
|
||||
assertNonMerge(rangeA, rangeB);
|
||||
|
||||
// A and B do not overlap, so should not merge
|
||||
rangeA = new RangedUri(null, FULL_URI, 0, 10);
|
||||
rangeB = new RangedUri(null, FULL_URI, 11, -1);
|
||||
assertNonMerge(rangeA, rangeB);
|
||||
|
||||
// A and B are bounded but overlap, so should not merge
|
||||
rangeA = new RangedUri(null, FULL_URI, 0, 11);
|
||||
rangeB = new RangedUri(null, FULL_URI, 10, 10);
|
||||
assertNonMerge(rangeA, rangeB);
|
||||
|
||||
// A and B overlap due to unboundedness, so should not merge
|
||||
rangeA = new RangedUri(null, FULL_URI, 0, -1);
|
||||
rangeB = new RangedUri(null, FULL_URI, 10, -1);
|
||||
assertNonMerge(rangeA, rangeB);
|
||||
|
||||
}
|
||||
|
||||
private void assertMerge(RangedUri rangeA, RangedUri rangeB, RangedUri expected) {
|
||||
RangedUri merged = rangeA.attemptMerge(rangeB);
|
||||
assertEquals(expected, merged);
|
||||
merged = rangeB.attemptMerge(rangeA);
|
||||
assertEquals(expected, merged);
|
||||
}
|
||||
|
||||
private void assertNonMerge(RangedUri rangeA, RangedUri rangeB) {
|
||||
RangedUri merged = rangeA.attemptMerge(rangeB);
|
||||
assertNull(merged);
|
||||
merged = rangeB.attemptMerge(rangeA);
|
||||
assertNull(merged);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.dash.mpd;
|
||||
|
||||
import com.google.android.exoplayer.chunk.Format;
|
||||
import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Representation}.
|
||||
*/
|
||||
public class RepresentationTest extends TestCase {
|
||||
|
||||
public void testGetCacheKey() {
|
||||
String uri = "http://www.google.com";
|
||||
SegmentBase base = new SingleSegmentBase(new RangedUri(uri, null, 0, 1), 1, 0, uri, 1, 1);
|
||||
Format format = new Format("0", MimeTypes.VIDEO_MP4, 1920, 1080, 0, 0, 2500000);
|
||||
Representation representation = Representation.newInstance(-1, -1, "test_stream_1", 3,
|
||||
format, base);
|
||||
assertEquals("test_stream_1.0.3", representation.getCacheKey());
|
||||
|
||||
format = new Format("150", MimeTypes.VIDEO_MP4, 1920, 1080, 0, 0, 2500000);
|
||||
representation = Representation.newInstance(-1, -1, "test_stream_1", -1, format, base);
|
||||
assertEquals("test_stream_1.150.-1", representation.getCacheKey());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.dash.mpd;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link UrlTemplate}.
|
||||
*/
|
||||
public class UrlTemplateTest extends TestCase {
|
||||
|
||||
public void testRealExamples() {
|
||||
String template = "QualityLevels($Bandwidth$)/Fragments(video=$Time$,format=mpd-time-csf)";
|
||||
UrlTemplate urlTemplate = UrlTemplate.compile(template);
|
||||
String url = urlTemplate.buildUri("abc1", 10, 650000, 5000);
|
||||
assertEquals("QualityLevels(650000)/Fragments(video=5000,format=mpd-time-csf)", url);
|
||||
|
||||
template = "$RepresentationID$/$Number$";
|
||||
urlTemplate = UrlTemplate.compile(template);
|
||||
url = urlTemplate.buildUri("abc1", 10, 650000, 5000);
|
||||
assertEquals("abc1/10", url);
|
||||
|
||||
template = "chunk_ctvideo_cfm4s_rid$RepresentationID$_cn$Number$_w2073857842_mpd.m4s";
|
||||
urlTemplate = UrlTemplate.compile(template);
|
||||
url = urlTemplate.buildUri("abc1", 10, 650000, 5000);
|
||||
assertEquals("chunk_ctvideo_cfm4s_ridabc1_cn10_w2073857842_mpd.m4s", url);
|
||||
}
|
||||
|
||||
public void testFull() {
|
||||
String template = "$Bandwidth$_a_$RepresentationID$_b_$Time$_c_$Number$";
|
||||
UrlTemplate urlTemplate = UrlTemplate.compile(template);
|
||||
String url = urlTemplate.buildUri("abc1", 10, 650000, 5000);
|
||||
assertEquals("650000_a_abc1_b_5000_c_10", url);
|
||||
}
|
||||
|
||||
public void testFullWithDollarEscaping() {
|
||||
String template = "$$$Bandwidth$$$_a$$_$RepresentationID$_b_$Time$_c_$Number$$$";
|
||||
UrlTemplate urlTemplate = UrlTemplate.compile(template);
|
||||
String url = urlTemplate.buildUri("abc1", 10, 650000, 5000);
|
||||
assertEquals("$650000$_a$_abc1_b_5000_c_10$", url);
|
||||
}
|
||||
|
||||
public void testInvalidSubstitution() {
|
||||
String template = "$IllegalId$";
|
||||
try {
|
||||
UrlTemplate.compile(template);
|
||||
assertTrue(false);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.hls;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Test for {@link HlsMasterPlaylistParserTest}
|
||||
*/
|
||||
public class HlsMasterPlaylistParserTest extends TestCase {
|
||||
|
||||
public void testParseMasterPlaylist() {
|
||||
String playlistUrl = "https://example.com/test.m3u8";
|
||||
String playlistString = "#EXTM3U\n"
|
||||
+ "\n"
|
||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
|
||||
+ "http://example.com/low.m3u8\n"
|
||||
+ "\n"
|
||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
|
||||
+ "http://example.com/spaces_in_codecs.m3u8\n"
|
||||
+ "\n"
|
||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=2560000,RESOLUTION=384x160\n"
|
||||
+ "http://example.com/mid.m3u8\n"
|
||||
+ "\n"
|
||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=7680000\n"
|
||||
+ "http://example.com/hi.m3u8\n"
|
||||
+ "\n"
|
||||
+ "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n"
|
||||
+ "http://example.com/audio-only.m3u8";
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(
|
||||
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
||||
try {
|
||||
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUrl, inputStream);
|
||||
assertNotNull(playlist);
|
||||
assertEquals(HlsPlaylist.TYPE_MASTER, playlist.type);
|
||||
|
||||
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
|
||||
|
||||
List<Variant> variants = masterPlaylist.variants;
|
||||
assertNotNull(variants);
|
||||
assertEquals(5, variants.size());
|
||||
|
||||
assertEquals(0, variants.get(0).index);
|
||||
assertEquals(1280000, variants.get(0).bandwidth);
|
||||
assertNotNull(variants.get(0).codecs);
|
||||
assertEquals(2, variants.get(0).codecs.length);
|
||||
assertEquals("mp4a.40.2", variants.get(0).codecs[0]);
|
||||
assertEquals("avc1.66.30", variants.get(0).codecs[1]);
|
||||
assertEquals(304, variants.get(0).width);
|
||||
assertEquals(128, variants.get(0).height);
|
||||
assertEquals("http://example.com/low.m3u8", variants.get(0).url);
|
||||
|
||||
assertEquals(1, variants.get(1).index);
|
||||
assertEquals(1280000, variants.get(1).bandwidth);
|
||||
assertNotNull(variants.get(1).codecs);
|
||||
assertEquals(2, variants.get(1).codecs.length);
|
||||
assertEquals("mp4a.40.2", variants.get(1).codecs[0]);
|
||||
assertEquals("avc1.66.30", variants.get(1).codecs[1]);
|
||||
assertEquals("http://example.com/spaces_in_codecs.m3u8", variants.get(1).url);
|
||||
|
||||
assertEquals(2, variants.get(2).index);
|
||||
assertEquals(2560000, variants.get(2).bandwidth);
|
||||
assertEquals(null, variants.get(2).codecs);
|
||||
assertEquals(384, variants.get(2).width);
|
||||
assertEquals(160, variants.get(2).height);
|
||||
assertEquals("http://example.com/mid.m3u8", variants.get(2).url);
|
||||
|
||||
assertEquals(3, variants.get(3).index);
|
||||
assertEquals(7680000, variants.get(3).bandwidth);
|
||||
assertEquals(null, variants.get(3).codecs);
|
||||
assertEquals(-1, variants.get(3).width);
|
||||
assertEquals(-1, variants.get(3).height);
|
||||
assertEquals("http://example.com/hi.m3u8", variants.get(3).url);
|
||||
|
||||
assertEquals(4, variants.get(4).index);
|
||||
assertEquals(65000, variants.get(4).bandwidth);
|
||||
assertNotNull(variants.get(4).codecs);
|
||||
assertEquals(1, variants.get(4).codecs.length);
|
||||
assertEquals("mp4a.40.5", variants.get(4).codecs[0]);
|
||||
assertEquals(-1, variants.get(4).width);
|
||||
assertEquals(-1, variants.get(4).height);
|
||||
assertEquals("http://example.com/audio-only.m3u8", variants.get(4).url);
|
||||
} catch (IOException exception) {
|
||||
fail(exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.hls;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Test for {@link HlsMediaPlaylistParserTest}
|
||||
*/
|
||||
public class HlsMediaPlaylistParserTest extends TestCase {
|
||||
|
||||
public void testParseMediaPlaylist() {
|
||||
String playlistUrl = "https://example.com/test.m3u8";
|
||||
String playlistString = "#EXTM3U\n"
|
||||
+ "#EXT-X-VERSION:3\n"
|
||||
+ "#EXT-X-TARGETDURATION:8\n"
|
||||
+ "#EXT-X-MEDIA-SEQUENCE:2679\n"
|
||||
+ "#EXT-X-ALLOW-CACHE:YES\n"
|
||||
+ "\n"
|
||||
+ "#EXTINF:7.975,\n"
|
||||
+ "#EXT-X-BYTERANGE:51370@0\n"
|
||||
+ "https://priv.example.com/fileSequence2679.ts\n"
|
||||
+ "\n"
|
||||
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n"
|
||||
+ "#EXTINF:7.975,\n"
|
||||
+ "#EXT-X-BYTERANGE:51501@51370\n"
|
||||
+ "https://priv.example.com/fileSequence2680.ts\n"
|
||||
+ "\n"
|
||||
+ "#EXT-X-KEY:METHOD=NONE\n"
|
||||
+ "#EXTINF:7.941,\n"
|
||||
+ "#EXT-X-BYTERANGE:51501\n" // @102871
|
||||
+ "https://priv.example.com/fileSequence2681.ts\n"
|
||||
+ "\n"
|
||||
+ "#EXT-X-DISCONTINUITY\n"
|
||||
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n"
|
||||
+ "#EXTINF:7.975,\n"
|
||||
+ "#EXT-X-BYTERANGE:51740\n" // @154372
|
||||
+ "https://priv.example.com/fileSequence2682.ts\n"
|
||||
+ "\n"
|
||||
+ "#EXTINF:7.975,\n"
|
||||
+ "https://priv.example.com/fileSequence2683.ts\n"
|
||||
+ "#EXT-X-ENDLIST";
|
||||
InputStream inputStream = new ByteArrayInputStream(
|
||||
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
||||
try {
|
||||
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUrl, inputStream);
|
||||
assertNotNull(playlist);
|
||||
assertEquals(HlsPlaylist.TYPE_MEDIA, playlist.type);
|
||||
|
||||
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
|
||||
|
||||
assertEquals(2679, mediaPlaylist.mediaSequence);
|
||||
assertEquals(8, mediaPlaylist.targetDurationSecs);
|
||||
assertEquals(3, mediaPlaylist.version);
|
||||
assertEquals(false, mediaPlaylist.live);
|
||||
List<HlsMediaPlaylist.Segment> segments = mediaPlaylist.segments;
|
||||
assertNotNull(segments);
|
||||
assertEquals(5, segments.size());
|
||||
|
||||
assertEquals(false, segments.get(0).discontinuity);
|
||||
assertEquals(7.975, segments.get(0).durationSecs);
|
||||
assertEquals(null, segments.get(0).encryptionMethod);
|
||||
assertEquals(null, segments.get(0).encryptionKeyUri);
|
||||
assertEquals(null, segments.get(0).encryptionIV);
|
||||
assertEquals(51370, segments.get(0).byterangeLength);
|
||||
assertEquals(0, segments.get(0).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2679.ts", segments.get(0).url);
|
||||
|
||||
assertEquals(false, segments.get(1).discontinuity);
|
||||
assertEquals(7.975, segments.get(1).durationSecs);
|
||||
assertEquals("AES-128", segments.get(1).encryptionMethod);
|
||||
assertEquals("https://priv.example.com/key.php?r=2680", segments.get(1).encryptionKeyUri);
|
||||
assertEquals("0x1566B", segments.get(1).encryptionIV);
|
||||
assertEquals(51501, segments.get(1).byterangeLength);
|
||||
assertEquals(51370, segments.get(1).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2680.ts", segments.get(1).url);
|
||||
|
||||
assertEquals(false, segments.get(2).discontinuity);
|
||||
assertEquals(7.941, segments.get(2).durationSecs);
|
||||
assertEquals(HlsMediaPlaylist.ENCRYPTION_METHOD_NONE, segments.get(2).encryptionMethod);
|
||||
assertEquals(null, segments.get(2).encryptionKeyUri);
|
||||
assertEquals(null, segments.get(2).encryptionIV);
|
||||
assertEquals(51501, segments.get(2).byterangeLength);
|
||||
assertEquals(102871, segments.get(2).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2681.ts", segments.get(2).url);
|
||||
|
||||
assertEquals(true, segments.get(3).discontinuity);
|
||||
assertEquals(7.975, segments.get(3).durationSecs);
|
||||
assertEquals("AES-128", segments.get(3).encryptionMethod);
|
||||
assertEquals("https://priv.example.com/key.php?r=2682", segments.get(3).encryptionKeyUri);
|
||||
// 0xA7A == 2682.
|
||||
assertNotNull(segments.get(3).encryptionIV);
|
||||
assertEquals("A7A", segments.get(3).encryptionIV.toUpperCase(Locale.getDefault()));
|
||||
assertEquals(51740, segments.get(3).byterangeLength);
|
||||
assertEquals(154372, segments.get(3).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2682.ts", segments.get(3).url);
|
||||
|
||||
assertEquals(false, segments.get(4).discontinuity);
|
||||
assertEquals(7.975, segments.get(4).durationSecs);
|
||||
assertEquals("AES-128", segments.get(4).encryptionMethod);
|
||||
assertEquals("https://priv.example.com/key.php?r=2682", segments.get(4).encryptionKeyUri);
|
||||
// 0xA7A == 2682.
|
||||
assertNotNull(segments.get(4).encryptionIV);
|
||||
assertEquals("A7A", segments.get(4).encryptionIV.toUpperCase(Locale.getDefault()));
|
||||
assertEquals(C.LENGTH_UNBOUNDED, segments.get(4).byterangeLength);
|
||||
assertEquals(0, segments.get(4).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2683.ts", segments.get(4).url);
|
||||
} catch (IOException exception) {
|
||||
fail(exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.metadata;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Test for {@link Id3Parser}
|
||||
*/
|
||||
public class Id3ParserTest extends TestCase {
|
||||
|
||||
public void testParseTxxxFrames() {
|
||||
byte[] rawId3 = new byte[] { 73, 68, 51, 4, 0, 0, 0, 0, 0, 41, 84, 88, 88, 88, 0, 0, 0, 31,
|
||||
0, 0, 3, 0, 109, 100, 105, 97, 108, 111, 103, 95, 86, 73, 78, 68, 73, 67, 79, 49, 53, 50,
|
||||
55, 54, 54, 52, 95, 115, 116, 97, 114, 116, 0 };
|
||||
|
||||
Id3Parser parser = new Id3Parser();
|
||||
try {
|
||||
Map<String, Object> metadata = parser.parse(rawId3, rawId3.length);
|
||||
assertNotNull(metadata);
|
||||
assertEquals(1, metadata.size());
|
||||
TxxxMetadata txxx = (TxxxMetadata) metadata.get(TxxxMetadata.TYPE);
|
||||
assertNotNull(txxx);
|
||||
assertEquals("", txxx.description);
|
||||
assertEquals("mdialog_VINDICO1527664_start", txxx.value);
|
||||
} catch (Exception exception) {
|
||||
fail(exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.mp4;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests for {@link Mp4Util}.
|
||||
*/
|
||||
public class Mp4UtilTest extends TestCase {
|
||||
|
||||
private static final int TEST_PARTIAL_NAL_POSITION = 4;
|
||||
private static final int TEST_NAL_POSITION = 10;
|
||||
|
||||
public void testFindNalUnit() {
|
||||
byte[] data = buildTestData();
|
||||
|
||||
// Should find NAL unit.
|
||||
int result = Mp4Util.findNalUnit(data, 0, data.length);
|
||||
assertEquals(TEST_NAL_POSITION, result);
|
||||
// Should find NAL unit whose prefix ends one byte before the limit.
|
||||
result = Mp4Util.findNalUnit(data, 0, TEST_NAL_POSITION + 4);
|
||||
assertEquals(TEST_NAL_POSITION, result);
|
||||
// Shouldn't find NAL unit whose prefix ends at the limit (since the limit is exclusive).
|
||||
result = Mp4Util.findNalUnit(data, 0, TEST_NAL_POSITION + 3);
|
||||
assertEquals(TEST_NAL_POSITION + 3, result);
|
||||
// Should find NAL unit whose prefix starts at the offset.
|
||||
result = Mp4Util.findNalUnit(data, TEST_NAL_POSITION, data.length);
|
||||
assertEquals(TEST_NAL_POSITION, result);
|
||||
// Shouldn't find NAL unit whose prefix starts one byte past the offset.
|
||||
result = Mp4Util.findNalUnit(data, TEST_NAL_POSITION + 1, data.length);
|
||||
assertEquals(data.length, result);
|
||||
}
|
||||
|
||||
public void testFindNalUnitWithPrefix() {
|
||||
byte[] data = buildTestData();
|
||||
|
||||
// First byte of NAL unit in data1, rest in data2.
|
||||
boolean[] prefixFlags = new boolean[3];
|
||||
byte[] data1 = Arrays.copyOfRange(data, 0, TEST_NAL_POSITION + 1);
|
||||
byte[] data2 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 1, data.length);
|
||||
int result = Mp4Util.findNalUnit(data1, 0, data1.length, prefixFlags);
|
||||
assertEquals(data1.length, result);
|
||||
result = Mp4Util.findNalUnit(data2, 0, data2.length, prefixFlags);
|
||||
assertEquals(-1, result);
|
||||
assertPrefixFlagsCleared(prefixFlags);
|
||||
|
||||
// First three bytes of NAL unit in data1, rest in data2.
|
||||
prefixFlags = new boolean[3];
|
||||
data1 = Arrays.copyOfRange(data, 0, TEST_NAL_POSITION + 3);
|
||||
data2 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 3, data.length);
|
||||
result = Mp4Util.findNalUnit(data1, 0, data1.length, prefixFlags);
|
||||
assertEquals(data1.length, result);
|
||||
result = Mp4Util.findNalUnit(data2, 0, data2.length, prefixFlags);
|
||||
assertEquals(-3, result);
|
||||
assertPrefixFlagsCleared(prefixFlags);
|
||||
|
||||
// First byte of NAL unit in data1, second byte in data2, rest in data3.
|
||||
prefixFlags = new boolean[3];
|
||||
data1 = Arrays.copyOfRange(data, 0, TEST_NAL_POSITION + 1);
|
||||
data2 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 1, TEST_NAL_POSITION + 2);
|
||||
byte[] data3 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 2, data.length);
|
||||
result = Mp4Util.findNalUnit(data1, 0, data1.length, prefixFlags);
|
||||
assertEquals(data1.length, result);
|
||||
result = Mp4Util.findNalUnit(data2, 0, data2.length, prefixFlags);
|
||||
assertEquals(data2.length, result);
|
||||
result = Mp4Util.findNalUnit(data3, 0, data3.length, prefixFlags);
|
||||
assertEquals(-2, result);
|
||||
assertPrefixFlagsCleared(prefixFlags);
|
||||
|
||||
// NAL unit split with one byte in four arrays.
|
||||
prefixFlags = new boolean[3];
|
||||
data1 = Arrays.copyOfRange(data, 0, TEST_NAL_POSITION + 1);
|
||||
data2 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 1, TEST_NAL_POSITION + 2);
|
||||
data3 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 2, TEST_NAL_POSITION + 3);
|
||||
byte[] data4 = Arrays.copyOfRange(data, TEST_NAL_POSITION + 2, data.length);
|
||||
result = Mp4Util.findNalUnit(data1, 0, data1.length, prefixFlags);
|
||||
assertEquals(data1.length, result);
|
||||
result = Mp4Util.findNalUnit(data2, 0, data2.length, prefixFlags);
|
||||
assertEquals(data2.length, result);
|
||||
result = Mp4Util.findNalUnit(data3, 0, data3.length, prefixFlags);
|
||||
assertEquals(data3.length, result);
|
||||
result = Mp4Util.findNalUnit(data4, 0, data4.length, prefixFlags);
|
||||
assertEquals(-3, result);
|
||||
assertPrefixFlagsCleared(prefixFlags);
|
||||
|
||||
// NAL unit entirely in data2. data1 ends with partial prefix.
|
||||
prefixFlags = new boolean[3];
|
||||
data1 = Arrays.copyOfRange(data, 0, TEST_PARTIAL_NAL_POSITION + 2);
|
||||
data2 = Arrays.copyOfRange(data, TEST_PARTIAL_NAL_POSITION + 2, data.length);
|
||||
result = Mp4Util.findNalUnit(data1, 0, data1.length, prefixFlags);
|
||||
assertEquals(data1.length, result);
|
||||
result = Mp4Util.findNalUnit(data2, 0, data2.length, prefixFlags);
|
||||
assertEquals(4, result);
|
||||
assertPrefixFlagsCleared(prefixFlags);
|
||||
}
|
||||
|
||||
private static byte[] buildTestData() {
|
||||
byte[] data = new byte[20];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = (byte) 0xFF;
|
||||
}
|
||||
// Insert an incomplete NAL unit start code.
|
||||
data[TEST_PARTIAL_NAL_POSITION] = 0;
|
||||
data[TEST_PARTIAL_NAL_POSITION + 1] = 0;
|
||||
// Insert a complete NAL unit start code.
|
||||
data[TEST_NAL_POSITION] = 0;
|
||||
data[TEST_NAL_POSITION + 1] = 0;
|
||||
data[TEST_NAL_POSITION + 2] = 1;
|
||||
data[TEST_NAL_POSITION + 3] = 5;
|
||||
return data;
|
||||
}
|
||||
|
||||
private static void assertPrefixFlagsCleared(boolean[] flags) {
|
||||
assertEquals(false, flags[0] || flags[1] || flags[2]);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.testutil;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Utility methods for tests.
|
||||
*/
|
||||
public class Util {
|
||||
|
||||
private Util() {}
|
||||
|
||||
public static byte[] buildTestData(int length) {
|
||||
return buildTestData(length, length);
|
||||
}
|
||||
|
||||
public static byte[] buildTestData(int length, int seed) {
|
||||
Random random = new Random(seed);
|
||||
byte[] source = new byte[length];
|
||||
random.nextBytes(source);
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.text.webvtt;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Unit test for {@link WebvttParser}.
|
||||
*/
|
||||
public class WebvttParserTest extends InstrumentationTestCase {
|
||||
|
||||
private static final String TYPICAL_WEBVTT_FILE = "webvtt/typical";
|
||||
private static final String TYPICAL_WITH_IDS_WEBVTT_FILE = "webvtt/typical_with_identifiers";
|
||||
private static final String TYPICAL_WITH_TAGS_WEBVTT_FILE = "webvtt/typical_with_tags";
|
||||
private static final String EMPTY_WEBVTT_FILE = "webvtt/empty";
|
||||
|
||||
public void testParseNullWebvttFile() throws IOException {
|
||||
WebvttParser parser = new WebvttParser();
|
||||
InputStream inputStream =
|
||||
getInstrumentation().getContext().getResources().getAssets().open(EMPTY_WEBVTT_FILE);
|
||||
|
||||
try {
|
||||
parser.parse(inputStream, C.UTF8_NAME, 0);
|
||||
fail("Expected IOException");
|
||||
} catch (IOException expected) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseTypicalWebvttFile() throws IOException {
|
||||
WebvttParser parser = new WebvttParser();
|
||||
InputStream inputStream =
|
||||
getInstrumentation().getContext().getResources().getAssets().open(TYPICAL_WEBVTT_FILE);
|
||||
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
||||
|
||||
// test start time and event count
|
||||
long startTimeUs = 5000000;
|
||||
assertEquals(startTimeUs, subtitle.getStartTime());
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
|
||||
// test first cue
|
||||
assertEquals(startTimeUs, subtitle.getEventTime(0));
|
||||
assertEquals("This is the first subtitle.",
|
||||
subtitle.getText(subtitle.getEventTime(0)));
|
||||
assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1));
|
||||
|
||||
// test second cue
|
||||
assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2));
|
||||
assertEquals("This is the second subtitle.",
|
||||
subtitle.getText(subtitle.getEventTime(2)));
|
||||
assertEquals(startTimeUs + 3456000, subtitle.getEventTime(3));
|
||||
}
|
||||
|
||||
public void testParseTypicalWithIdsWebvttFile() throws IOException {
|
||||
WebvttParser parser = new WebvttParser();
|
||||
InputStream inputStream =
|
||||
getInstrumentation().getContext().getResources().getAssets()
|
||||
.open(TYPICAL_WITH_IDS_WEBVTT_FILE);
|
||||
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
||||
|
||||
// test start time and event count
|
||||
long startTimeUs = 5000000;
|
||||
assertEquals(startTimeUs, subtitle.getStartTime());
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
|
||||
// test first cue
|
||||
assertEquals(startTimeUs, subtitle.getEventTime(0));
|
||||
assertEquals("This is the first subtitle.",
|
||||
subtitle.getText(subtitle.getEventTime(0)));
|
||||
assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1));
|
||||
|
||||
// test second cue
|
||||
assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2));
|
||||
assertEquals("This is the second subtitle.",
|
||||
subtitle.getText(subtitle.getEventTime(2)));
|
||||
assertEquals(startTimeUs + 3456000, subtitle.getEventTime(3));
|
||||
}
|
||||
|
||||
public void testParseTypicalWithTagsWebvttFile() throws IOException {
|
||||
WebvttParser parser = new WebvttParser();
|
||||
InputStream inputStream =
|
||||
getInstrumentation().getContext().getResources().getAssets()
|
||||
.open(TYPICAL_WITH_TAGS_WEBVTT_FILE);
|
||||
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
||||
|
||||
// test start time and event count
|
||||
long startTimeUs = 5000000;
|
||||
assertEquals(startTimeUs, subtitle.getStartTime());
|
||||
assertEquals(8, subtitle.getEventTimeCount());
|
||||
|
||||
// test first cue
|
||||
assertEquals(startTimeUs, subtitle.getEventTime(0));
|
||||
assertEquals("This is the first subtitle.",
|
||||
subtitle.getText(subtitle.getEventTime(0)));
|
||||
assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1));
|
||||
|
||||
// test second cue
|
||||
assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2));
|
||||
assertEquals("This is the second subtitle.",
|
||||
subtitle.getText(subtitle.getEventTime(2)));
|
||||
assertEquals(startTimeUs + 3456000, subtitle.getEventTime(3));
|
||||
|
||||
// test third cue
|
||||
assertEquals(startTimeUs + 4000000, subtitle.getEventTime(4));
|
||||
assertEquals("This is the third subtitle.",
|
||||
subtitle.getText(subtitle.getEventTime(4)));
|
||||
assertEquals(startTimeUs + 5000000, subtitle.getEventTime(5));
|
||||
|
||||
// test fourth cue
|
||||
assertEquals(startTimeUs + 6000000, subtitle.getEventTime(6));
|
||||
assertEquals("This is the <fourth> &subtitle.",
|
||||
subtitle.getText(subtitle.getEventTime(6)));
|
||||
assertEquals(startTimeUs + 7000000, subtitle.getEventTime(7));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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.text.webvtt;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link WebvttSubtitle}.
|
||||
*/
|
||||
public class WebvttSubtitleTest extends TestCase {
|
||||
|
||||
private static final String FIRST_SUBTITLE_STRING = "This is the first subtitle.";
|
||||
private static final String SECOND_SUBTITLE_STRING = "This is the second subtitle.";
|
||||
private static final String FIRST_AND_SECOND_SUBTITLE_STRING =
|
||||
FIRST_SUBTITLE_STRING + SECOND_SUBTITLE_STRING;
|
||||
|
||||
private WebvttSubtitle emptySubtitle = new WebvttSubtitle(new String[] {}, 0, new long[] {});
|
||||
|
||||
private WebvttSubtitle simpleSubtitle = new WebvttSubtitle(
|
||||
new String[] {FIRST_SUBTITLE_STRING, SECOND_SUBTITLE_STRING}, 0,
|
||||
new long[] {1000000, 2000000, 3000000, 4000000});
|
||||
|
||||
private WebvttSubtitle overlappingSubtitle = new WebvttSubtitle(
|
||||
new String[] {FIRST_SUBTITLE_STRING, SECOND_SUBTITLE_STRING}, 0,
|
||||
new long[] {1000000, 3000000, 2000000, 4000000});
|
||||
|
||||
private WebvttSubtitle nestedSubtitle = new WebvttSubtitle(
|
||||
new String[] {FIRST_SUBTITLE_STRING, SECOND_SUBTITLE_STRING}, 0,
|
||||
new long[] {1000000, 4000000, 2000000, 3000000});
|
||||
|
||||
public void testEventCount() {
|
||||
assertEquals(0, emptySubtitle.getEventTimeCount());
|
||||
assertEquals(4, simpleSubtitle.getEventTimeCount());
|
||||
assertEquals(4, overlappingSubtitle.getEventTimeCount());
|
||||
assertEquals(4, nestedSubtitle.getEventTimeCount());
|
||||
}
|
||||
|
||||
public void testStartTime() {
|
||||
assertEquals(0, emptySubtitle.getStartTime());
|
||||
assertEquals(0, simpleSubtitle.getStartTime());
|
||||
assertEquals(0, overlappingSubtitle.getStartTime());
|
||||
assertEquals(0, nestedSubtitle.getStartTime());
|
||||
}
|
||||
|
||||
public void testLastEventTime() {
|
||||
assertEquals(-1, emptySubtitle.getLastEventTime());
|
||||
assertEquals(4000000, simpleSubtitle.getLastEventTime());
|
||||
assertEquals(4000000, overlappingSubtitle.getLastEventTime());
|
||||
assertEquals(4000000, nestedSubtitle.getLastEventTime());
|
||||
}
|
||||
|
||||
public void testSimpleSubtitleEventTimes() {
|
||||
testSubtitleEventTimesHelper(simpleSubtitle);
|
||||
}
|
||||
|
||||
public void testSimpleSubtitleEventIndices() {
|
||||
testSubtitleEventIndicesHelper(simpleSubtitle);
|
||||
}
|
||||
|
||||
public void testSimpleSubtitleText() {
|
||||
// Test before first subtitle
|
||||
assertNull(simpleSubtitle.getText(0));
|
||||
assertNull(simpleSubtitle.getText(500000));
|
||||
assertNull(simpleSubtitle.getText(999999));
|
||||
|
||||
// Test first subtitle
|
||||
assertEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getText(1000000));
|
||||
assertEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getText(1500000));
|
||||
assertEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getText(1999999));
|
||||
|
||||
// Test after first subtitle, before second subtitle
|
||||
assertNull(simpleSubtitle.getText(2000000));
|
||||
assertNull(simpleSubtitle.getText(2500000));
|
||||
assertNull(simpleSubtitle.getText(2999999));
|
||||
|
||||
// Test second subtitle
|
||||
assertEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getText(3000000));
|
||||
assertEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getText(3500000));
|
||||
assertEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getText(3999999));
|
||||
|
||||
// Test after second subtitle
|
||||
assertNull(simpleSubtitle.getText(4000000));
|
||||
assertNull(simpleSubtitle.getText(4500000));
|
||||
assertNull(simpleSubtitle.getText(Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
public void testOverlappingSubtitleEventTimes() {
|
||||
testSubtitleEventTimesHelper(overlappingSubtitle);
|
||||
}
|
||||
|
||||
public void testOverlappingSubtitleEventIndices() {
|
||||
testSubtitleEventIndicesHelper(overlappingSubtitle);
|
||||
}
|
||||
|
||||
public void testOverlappingSubtitleText() {
|
||||
// Test before first subtitle
|
||||
assertNull(overlappingSubtitle.getText(0));
|
||||
assertNull(overlappingSubtitle.getText(500000));
|
||||
assertNull(overlappingSubtitle.getText(999999));
|
||||
|
||||
// Test first subtitle
|
||||
assertEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getText(1000000));
|
||||
assertEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getText(1500000));
|
||||
assertEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getText(1999999));
|
||||
|
||||
// Test after first and second subtitle
|
||||
assertEquals(FIRST_AND_SECOND_SUBTITLE_STRING, overlappingSubtitle.getText(2000000));
|
||||
assertEquals(FIRST_AND_SECOND_SUBTITLE_STRING, overlappingSubtitle.getText(2500000));
|
||||
assertEquals(FIRST_AND_SECOND_SUBTITLE_STRING, overlappingSubtitle.getText(2999999));
|
||||
|
||||
// Test second subtitle
|
||||
assertEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getText(3000000));
|
||||
assertEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getText(3500000));
|
||||
assertEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getText(3999999));
|
||||
|
||||
// Test after second subtitle
|
||||
assertNull(overlappingSubtitle.getText(4000000));
|
||||
assertNull(overlappingSubtitle.getText(4500000));
|
||||
assertNull(overlappingSubtitle.getText(Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
public void testNestedSubtitleEventTimes() {
|
||||
testSubtitleEventTimesHelper(nestedSubtitle);
|
||||
}
|
||||
|
||||
public void testNestedSubtitleEventIndices() {
|
||||
testSubtitleEventIndicesHelper(nestedSubtitle);
|
||||
}
|
||||
|
||||
public void testNestedSubtitleText() {
|
||||
// Test before first subtitle
|
||||
assertNull(nestedSubtitle.getText(0));
|
||||
assertNull(nestedSubtitle.getText(500000));
|
||||
assertNull(nestedSubtitle.getText(999999));
|
||||
|
||||
// Test first subtitle
|
||||
assertEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getText(1000000));
|
||||
assertEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getText(1500000));
|
||||
assertEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getText(1999999));
|
||||
|
||||
// Test after first and second subtitle
|
||||
assertEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getText(2000000));
|
||||
assertEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getText(2500000));
|
||||
assertEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getText(2999999));
|
||||
|
||||
// Test first subtitle
|
||||
assertEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getText(3000000));
|
||||
assertEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getText(3500000));
|
||||
assertEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getText(3999999));
|
||||
|
||||
// Test after second subtitle
|
||||
assertNull(nestedSubtitle.getText(4000000));
|
||||
assertNull(nestedSubtitle.getText(4500000));
|
||||
assertNull(nestedSubtitle.getText(Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
private void testSubtitleEventTimesHelper(WebvttSubtitle subtitle) {
|
||||
assertEquals(1000000, subtitle.getEventTime(0));
|
||||
assertEquals(2000000, subtitle.getEventTime(1));
|
||||
assertEquals(3000000, subtitle.getEventTime(2));
|
||||
assertEquals(4000000, subtitle.getEventTime(3));
|
||||
}
|
||||
|
||||
private void testSubtitleEventIndicesHelper(WebvttSubtitle subtitle) {
|
||||
// Test first event
|
||||
assertEquals(0, subtitle.getNextEventTimeIndex(0));
|
||||
assertEquals(0, subtitle.getNextEventTimeIndex(500000));
|
||||
assertEquals(0, subtitle.getNextEventTimeIndex(999999));
|
||||
|
||||
// Test second event
|
||||
assertEquals(1, subtitle.getNextEventTimeIndex(1000000));
|
||||
assertEquals(1, subtitle.getNextEventTimeIndex(1500000));
|
||||
assertEquals(1, subtitle.getNextEventTimeIndex(1999999));
|
||||
|
||||
// Test third event
|
||||
assertEquals(2, subtitle.getNextEventTimeIndex(2000000));
|
||||
assertEquals(2, subtitle.getNextEventTimeIndex(2500000));
|
||||
assertEquals(2, subtitle.getNextEventTimeIndex(2999999));
|
||||
|
||||
// Test fourth event
|
||||
assertEquals(3, subtitle.getNextEventTimeIndex(3000000));
|
||||
assertEquals(3, subtitle.getNextEventTimeIndex(3500000));
|
||||
assertEquals(3, subtitle.getNextEventTimeIndex(3999999));
|
||||
|
||||
// Test null event (i.e. look for events after the last event)
|
||||
assertEquals(-1, subtitle.getNextEventTimeIndex(4000000));
|
||||
assertEquals(-1, subtitle.getNextEventTimeIndex(4500000));
|
||||
assertEquals(-1, subtitle.getNextEventTimeIndex(Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.upstream;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ByteArrayDataSource}.
|
||||
*/
|
||||
public class ByteArrayDataSourceTest extends TestCase {
|
||||
|
||||
private static final byte[] TEST_DATA = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
private static final byte[] TEST_DATA_ODD = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
|
||||
public void testFullReadSingleBytes() {
|
||||
readTestData(TEST_DATA, 0, C.LENGTH_UNBOUNDED, 1, 0, 1, false);
|
||||
}
|
||||
|
||||
public void testFullReadAllBytes() {
|
||||
readTestData(TEST_DATA, 0, C.LENGTH_UNBOUNDED, 100, 0, 100, false);
|
||||
}
|
||||
|
||||
public void testLimitReadSingleBytes() {
|
||||
// Limit set to the length of the data.
|
||||
readTestData(TEST_DATA, 0, TEST_DATA.length, 1, 0, 1, false);
|
||||
// And less.
|
||||
readTestData(TEST_DATA, 0, 6, 1, 0, 1, false);
|
||||
}
|
||||
|
||||
public void testFullReadTwoBytes() {
|
||||
// Try with the total data length an exact multiple of the size of each individual read.
|
||||
readTestData(TEST_DATA, 0, C.LENGTH_UNBOUNDED, 2, 0, 2, false);
|
||||
// And not.
|
||||
readTestData(TEST_DATA_ODD, 0, C.LENGTH_UNBOUNDED, 2, 0, 2, false);
|
||||
}
|
||||
|
||||
public void testLimitReadTwoBytes() {
|
||||
// Try with the limit an exact multiple of the size of each individual read.
|
||||
readTestData(TEST_DATA, 0, 6, 2, 0, 2, false);
|
||||
// And not.
|
||||
readTestData(TEST_DATA, 0, 7, 2, 0, 2, false);
|
||||
}
|
||||
|
||||
public void testReadFromValidOffsets() {
|
||||
// Read from an offset without bound.
|
||||
readTestData(TEST_DATA, 1, C.LENGTH_UNBOUNDED, 1, 0, 1, false);
|
||||
// And with bound.
|
||||
readTestData(TEST_DATA, 1, 6, 1, 0, 1, false);
|
||||
// Read from the last possible offset without bound.
|
||||
readTestData(TEST_DATA, TEST_DATA.length - 1, C.LENGTH_UNBOUNDED, 1, 0, 1, false);
|
||||
// And with bound.
|
||||
readTestData(TEST_DATA, TEST_DATA.length - 1, 1, 1, 0, 1, false);
|
||||
}
|
||||
|
||||
public void testReadFromInvalidOffsets() {
|
||||
// Read from first invalid offset and check failure without bound.
|
||||
readTestData(TEST_DATA, TEST_DATA.length, C.LENGTH_UNBOUNDED, 1, 0, 1, true);
|
||||
// And with bound.
|
||||
readTestData(TEST_DATA, TEST_DATA.length, 1, 1, 0, 1, true);
|
||||
}
|
||||
|
||||
public void testReadWithInvalidLength() {
|
||||
// Read more data than is available.
|
||||
readTestData(TEST_DATA, 0, TEST_DATA.length + 1, 1, 0, 1, true);
|
||||
// And with bound.
|
||||
readTestData(TEST_DATA, 1, TEST_DATA.length, 1, 0, 1, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests reading from a {@link ByteArrayDataSource} with various parameters.
|
||||
*
|
||||
* @param testData The data that the {@link ByteArrayDataSource} will wrap.
|
||||
* @param dataOffset The offset from which to read data.
|
||||
* @param dataLength The total length of data to read.
|
||||
* @param outputBufferLength The length of the target buffer for each read.
|
||||
* @param writeOffset The offset into {@code outputBufferLength} for each read.
|
||||
* @param maxReadLength The maximum length of each read.
|
||||
* @param expectFailOnOpen Whether it is expected that opening the source will fail.
|
||||
*/
|
||||
private void readTestData(byte[] testData, int dataOffset, int dataLength, int outputBufferLength,
|
||||
int writeOffset, int maxReadLength, boolean expectFailOnOpen) {
|
||||
int expectedFinalBytesRead =
|
||||
dataLength == C.LENGTH_UNBOUNDED ? (testData.length - dataOffset) : dataLength;
|
||||
ByteArrayDataSource dataSource = new ByteArrayDataSource(testData);
|
||||
boolean opened = false;
|
||||
try {
|
||||
// Open the source.
|
||||
long length = dataSource.open(new DataSpec(null, dataOffset, dataLength, null));
|
||||
opened = true;
|
||||
assertFalse(expectFailOnOpen);
|
||||
|
||||
// Verify the resolved length is as we expect.
|
||||
assertEquals(expectedFinalBytesRead, length);
|
||||
|
||||
byte[] outputBuffer = new byte[outputBufferLength];
|
||||
int accumulatedBytesRead = 0;
|
||||
while (true) {
|
||||
// Calculate a valid length for the next read, constraining by the specified output buffer
|
||||
// length, write offset and maximum write length input parameters.
|
||||
int requestedReadLength = Math.min(maxReadLength, outputBufferLength - writeOffset);
|
||||
assertTrue(requestedReadLength > 0);
|
||||
|
||||
int bytesRead = dataSource.read(outputBuffer, writeOffset, requestedReadLength);
|
||||
if (bytesRead != -1) {
|
||||
assertTrue(bytesRead > 0);
|
||||
assertTrue(bytesRead <= requestedReadLength);
|
||||
// Check the data read was correct.
|
||||
for (int i = 0; i < bytesRead; i++) {
|
||||
assertEquals(testData[dataOffset + accumulatedBytesRead + i],
|
||||
outputBuffer[writeOffset + i]);
|
||||
}
|
||||
// Check that we haven't read more data than we were expecting.
|
||||
accumulatedBytesRead += bytesRead;
|
||||
assertTrue(accumulatedBytesRead <= expectedFinalBytesRead);
|
||||
// If we haven't read all of the bytes the request should have been satisfied in full.
|
||||
assertTrue(accumulatedBytesRead == expectedFinalBytesRead
|
||||
|| bytesRead == requestedReadLength);
|
||||
} else {
|
||||
// We're done. Check we read the expected number of bytes.
|
||||
assertEquals(expectedFinalBytesRead, accumulatedBytesRead);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (expectFailOnOpen && !opened) {
|
||||
// Expected.
|
||||
return;
|
||||
}
|
||||
// Unexpected failure.
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.upstream;
|
||||
|
||||
import com.google.android.exoplayer.testutil.Util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DataSourceStream}.
|
||||
*/
|
||||
public class DataSourceStreamTest extends TestCase {
|
||||
|
||||
private static final int DATA_LENGTH = 1024;
|
||||
private static final int BUFFER_LENGTH = 128;
|
||||
|
||||
public void testGetLoadedData() throws IOException, InterruptedException {
|
||||
byte[] testData = Util.buildTestData(DATA_LENGTH);
|
||||
DataSource dataSource = new ByteArrayDataSource(testData);
|
||||
DataSpec dataSpec = new DataSpec(null, 0, DATA_LENGTH, null);
|
||||
DataSourceStream dataSourceStream = new DataSourceStream(dataSource, dataSpec,
|
||||
new BufferPool(BUFFER_LENGTH));
|
||||
|
||||
dataSourceStream.load();
|
||||
// Assert that the read and load positions are correct.
|
||||
assertEquals(0, dataSourceStream.getReadPosition());
|
||||
assertEquals(testData.length, dataSourceStream.getLoadPosition());
|
||||
|
||||
int halfTestDataLength = testData.length / 2;
|
||||
byte[] readData = new byte[testData.length];
|
||||
int bytesRead = dataSourceStream.read(readData, 0, halfTestDataLength);
|
||||
// Assert that the read position is updated correctly.
|
||||
assertEquals(halfTestDataLength, bytesRead);
|
||||
assertEquals(halfTestDataLength, dataSourceStream.getReadPosition());
|
||||
|
||||
bytesRead += dataSourceStream.read(readData, bytesRead, testData.length - bytesRead);
|
||||
// Assert that the read position was updated correctly.
|
||||
assertEquals(testData.length, bytesRead);
|
||||
assertEquals(testData.length, dataSourceStream.getReadPosition());
|
||||
// Assert that the data read using the two read calls either side of getLoadedData is correct.
|
||||
assertTrue(Arrays.equals(testData, readData));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tests for {@link ParsableByteArray}.
|
||||
*/
|
||||
public class ParsableByteArrayTest extends TestCase {
|
||||
|
||||
private static final byte[] ARRAY_ELEMENTS =
|
||||
new byte[] {0x0F, (byte) 0xFF, (byte) 0x42, (byte) 0x0F, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
private ParsableByteArray parsableByteArray;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
parsableByteArray = new ParsableByteArray(ARRAY_ELEMENTS.length);
|
||||
System.arraycopy(ARRAY_ELEMENTS, 0, parsableByteArray.data, 0, ARRAY_ELEMENTS.length);
|
||||
}
|
||||
|
||||
public void testReadInt() {
|
||||
// When reading a signed integer
|
||||
int value = parsableByteArray.readInt();
|
||||
|
||||
// Then the read value is equal to the array elements interpreted as an int.
|
||||
assertEquals((0xFF & ARRAY_ELEMENTS[0]) << 24 | (0xFF & ARRAY_ELEMENTS[1]) << 16
|
||||
| (0xFF & ARRAY_ELEMENTS[2]) << 8 | (0xFF & ARRAY_ELEMENTS[3]), value);
|
||||
}
|
||||
|
||||
public void testSkipBack() {
|
||||
// When reading an unsigned integer
|
||||
long value = parsableByteArray.readUnsignedInt();
|
||||
|
||||
// Then skipping back and reading gives the same value.
|
||||
parsableByteArray.skip(-4);
|
||||
assertEquals(value, parsableByteArray.readUnsignedInt());
|
||||
}
|
||||
|
||||
public void testReadingMovesPosition() {
|
||||
// Given an array at the start
|
||||
assertEquals(0, parsableByteArray.getPosition());
|
||||
|
||||
// When reading an integer, the position advances
|
||||
parsableByteArray.readUnsignedInt();
|
||||
assertEquals(4, parsableByteArray.getPosition());
|
||||
}
|
||||
|
||||
public void testOutOfBoundsThrows() {
|
||||
// Given an array at the end
|
||||
parsableByteArray.readUnsignedLongToLong();
|
||||
assertEquals(ARRAY_ELEMENTS.length, parsableByteArray.getPosition());
|
||||
|
||||
// Then reading more data throws.
|
||||
try {
|
||||
parsableByteArray.readUnsignedInt();
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
public void testModificationsAffectParsableArray() {
|
||||
// When modifying the wrapped byte array
|
||||
byte[] data = parsableByteArray.data;
|
||||
long readValue = parsableByteArray.readUnsignedInt();
|
||||
data[0] = (byte) (ARRAY_ELEMENTS[0] + 1);
|
||||
parsableByteArray.setPosition(0);
|
||||
|
||||
// Then the parsed value changes.
|
||||
assertFalse(parsableByteArray.readUnsignedInt() == readValue);
|
||||
}
|
||||
|
||||
public void testReadingUnsignedLongWithMsbSetThrows() {
|
||||
// Given an array with the most-significant bit set on the top byte
|
||||
byte[] data = parsableByteArray.data;
|
||||
data[0] = (byte) 0x80;
|
||||
|
||||
// Then reading an unsigned long throws.
|
||||
try {
|
||||
parsableByteArray.readUnsignedLongToLong();
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
public void testReadUnsignedFixedPoint1616() {
|
||||
// When reading the integer part of a 16.16 fixed point value
|
||||
int value = parsableByteArray.readUnsignedFixedPoint1616();
|
||||
|
||||
// Then the read value is equal to the array elements interpreted as a short.
|
||||
assertEquals((0xFF & ARRAY_ELEMENTS[0]) << 8 | (ARRAY_ELEMENTS[1] & 0xFF), value);
|
||||
assertEquals(4, parsableByteArray.getPosition());
|
||||
}
|
||||
|
||||
public void testReadingBytesReturnsCopy() {
|
||||
// When reading all the bytes back
|
||||
int length = parsableByteArray.limit();
|
||||
assertEquals(ARRAY_ELEMENTS.length, length);
|
||||
byte[] copy = new byte[length];
|
||||
parsableByteArray.readBytes(copy, 0, length);
|
||||
|
||||
// Then the array elements are the same.
|
||||
assertTrue(Arrays.equals(parsableByteArray.data, copy));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link UriUtil}.
|
||||
*/
|
||||
public class UriUtilTest extends TestCase {
|
||||
|
||||
/**
|
||||
* Tests normal usage of {@link UriUtil#resolve(String, String)}.
|
||||
* <p>
|
||||
* The test cases are taken from RFC-3986 5.4.1.
|
||||
*/
|
||||
public void testResolveNormal() {
|
||||
String base = "http://a/b/c/d;p?q";
|
||||
|
||||
assertEquals("g:h", UriUtil.resolve(base, "g:h"));
|
||||
assertEquals("http://a/b/c/g", UriUtil.resolve(base, "g"));
|
||||
assertEquals("http://a/b/c/g/", UriUtil.resolve(base, "g/"));
|
||||
assertEquals("http://a/g", UriUtil.resolve(base, "/g"));
|
||||
assertEquals("http://g", UriUtil.resolve(base, "//g"));
|
||||
assertEquals("http://a/b/c/d;p?y", UriUtil.resolve(base, "?y"));
|
||||
assertEquals("http://a/b/c/g?y", UriUtil.resolve(base, "g?y"));
|
||||
assertEquals("http://a/b/c/d;p?q#s", UriUtil.resolve(base, "#s"));
|
||||
assertEquals("http://a/b/c/g#s", UriUtil.resolve(base, "g#s"));
|
||||
assertEquals("http://a/b/c/g?y#s", UriUtil.resolve(base, "g?y#s"));
|
||||
assertEquals("http://a/b/c/;x", UriUtil.resolve(base, ";x"));
|
||||
assertEquals("http://a/b/c/g;x", UriUtil.resolve(base, "g;x"));
|
||||
assertEquals("http://a/b/c/g;x?y#s", UriUtil.resolve(base, "g;x?y#s"));
|
||||
assertEquals("http://a/b/c/d;p?q", UriUtil.resolve(base, ""));
|
||||
assertEquals("http://a/b/c/", UriUtil.resolve(base, "."));
|
||||
assertEquals("http://a/b/c/", UriUtil.resolve(base, "./"));
|
||||
assertEquals("http://a/b/", UriUtil.resolve(base, ".."));
|
||||
assertEquals("http://a/b/", UriUtil.resolve(base, "../"));
|
||||
assertEquals("http://a/b/g", UriUtil.resolve(base, "../g"));
|
||||
assertEquals("http://a/", UriUtil.resolve(base, "../.."));
|
||||
assertEquals("http://a/", UriUtil.resolve(base, "../../"));
|
||||
assertEquals("http://a/g", UriUtil.resolve(base, "../../g"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests abnormal usage of {@link UriUtil#resolve(String, String)}.
|
||||
* <p>
|
||||
* The test cases are taken from RFC-3986 5.4.2.
|
||||
*/
|
||||
public void testResolveAbnormal() {
|
||||
String base = "http://a/b/c/d;p?q";
|
||||
|
||||
assertEquals("http://a/g", UriUtil.resolve(base, "../../../g"));
|
||||
assertEquals("http://a/g", UriUtil.resolve(base, "../../../../g"));
|
||||
|
||||
assertEquals("http://a/g", UriUtil.resolve(base, "/./g"));
|
||||
assertEquals("http://a/g", UriUtil.resolve(base, "/../g"));
|
||||
assertEquals("http://a/b/c/g.", UriUtil.resolve(base, "g."));
|
||||
assertEquals("http://a/b/c/.g", UriUtil.resolve(base, ".g"));
|
||||
assertEquals("http://a/b/c/g..", UriUtil.resolve(base, "g.."));
|
||||
assertEquals("http://a/b/c/..g", UriUtil.resolve(base, "..g"));
|
||||
|
||||
assertEquals("http://a/b/g", UriUtil.resolve(base, "./../g"));
|
||||
assertEquals("http://a/b/c/g/", UriUtil.resolve(base, "./g/."));
|
||||
assertEquals("http://a/b/c/g/h", UriUtil.resolve(base, "g/./h"));
|
||||
assertEquals("http://a/b/c/h", UriUtil.resolve(base, "g/../h"));
|
||||
assertEquals("http://a/b/c/g;x=1/y", UriUtil.resolve(base, "g;x=1/./y"));
|
||||
assertEquals("http://a/b/c/y", UriUtil.resolve(base, "g;x=1/../y"));
|
||||
|
||||
assertEquals("http://a/b/c/g?y/./x", UriUtil.resolve(base, "g?y/./x"));
|
||||
assertEquals("http://a/b/c/g?y/../x", UriUtil.resolve(base, "g?y/../x"));
|
||||
assertEquals("http://a/b/c/g#s/./x", UriUtil.resolve(base, "g#s/./x"));
|
||||
assertEquals("http://a/b/c/g#s/../x", UriUtil.resolve(base, "g#s/../x"));
|
||||
|
||||
assertEquals("http:g", UriUtil.resolve(base, "http:g"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests additional abnormal usage of {@link UriUtil#resolve(String, String)}.
|
||||
*/
|
||||
public void testResolveAbnormalAdditional() {
|
||||
assertEquals("c:e", UriUtil.resolve("http://a/b", "c:d/../e"));
|
||||
assertEquals("a:c", UriUtil.resolve("a:b", "../c"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Util}.
|
||||
*/
|
||||
public class UtilTest extends TestCase {
|
||||
|
||||
public void testArrayBinarySearchFloor() {
|
||||
long[] values = new long[0];
|
||||
assertEquals(-1, Util.binarySearchFloor(values, 0, false, false));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 0, false, true));
|
||||
|
||||
values = new long[] {1, 3, 5};
|
||||
assertEquals(-1, Util.binarySearchFloor(values, 0, false, false));
|
||||
assertEquals(-1, Util.binarySearchFloor(values, 0, true, false));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 0, false, true));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 0, true, true));
|
||||
|
||||
assertEquals(-1, Util.binarySearchFloor(values, 1, false, false));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 1, true, false));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 1, false, true));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 1, true, true));
|
||||
|
||||
assertEquals(1, Util.binarySearchFloor(values, 4, false, false));
|
||||
assertEquals(1, Util.binarySearchFloor(values, 4, true, false));
|
||||
|
||||
assertEquals(1, Util.binarySearchFloor(values, 5, false, false));
|
||||
assertEquals(2, Util.binarySearchFloor(values, 5, true, false));
|
||||
|
||||
assertEquals(2, Util.binarySearchFloor(values, 6, false, false));
|
||||
assertEquals(2, Util.binarySearchFloor(values, 6, true, false));
|
||||
}
|
||||
|
||||
public void testListBinarySearchFloor() {
|
||||
List<Integer> values = new ArrayList<Integer>();
|
||||
assertEquals(-1, Util.binarySearchFloor(values, 0, false, false));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 0, false, true));
|
||||
|
||||
values.add(1);
|
||||
values.add(3);
|
||||
values.add(5);
|
||||
assertEquals(-1, Util.binarySearchFloor(values, 0, false, false));
|
||||
assertEquals(-1, Util.binarySearchFloor(values, 0, true, false));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 0, false, true));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 0, true, true));
|
||||
|
||||
assertEquals(-1, Util.binarySearchFloor(values, 1, false, false));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 1, true, false));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 1, false, true));
|
||||
assertEquals(0, Util.binarySearchFloor(values, 1, true, true));
|
||||
|
||||
assertEquals(1, Util.binarySearchFloor(values, 4, false, false));
|
||||
assertEquals(1, Util.binarySearchFloor(values, 4, true, false));
|
||||
|
||||
assertEquals(1, Util.binarySearchFloor(values, 5, false, false));
|
||||
assertEquals(2, Util.binarySearchFloor(values, 5, true, false));
|
||||
|
||||
assertEquals(2, Util.binarySearchFloor(values, 6, false, false));
|
||||
assertEquals(2, Util.binarySearchFloor(values, 6, true, false));
|
||||
}
|
||||
|
||||
public void testArrayBinarySearchCeil() {
|
||||
long[] values = new long[0];
|
||||
assertEquals(0, Util.binarySearchCeil(values, 0, false, false));
|
||||
assertEquals(-1, Util.binarySearchCeil(values, 0, false, true));
|
||||
|
||||
values = new long[] {1, 3, 5};
|
||||
assertEquals(0, Util.binarySearchCeil(values, 0, false, false));
|
||||
assertEquals(0, Util.binarySearchCeil(values, 0, true, false));
|
||||
|
||||
assertEquals(1, Util.binarySearchCeil(values, 1, false, false));
|
||||
assertEquals(0, Util.binarySearchCeil(values, 1, true, false));
|
||||
|
||||
assertEquals(1, Util.binarySearchCeil(values, 2, false, false));
|
||||
assertEquals(1, Util.binarySearchCeil(values, 2, true, false));
|
||||
|
||||
assertEquals(3, Util.binarySearchCeil(values, 5, false, false));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 5, true, false));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 5, false, true));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 5, true, true));
|
||||
|
||||
assertEquals(3, Util.binarySearchCeil(values, 6, false, false));
|
||||
assertEquals(3, Util.binarySearchCeil(values, 6, true, false));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 6, false, true));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 6, true, true));
|
||||
}
|
||||
|
||||
public void testListBinarySearchCeil() {
|
||||
List<Integer> values = new ArrayList<Integer>();
|
||||
assertEquals(0, Util.binarySearchCeil(values, 0, false, false));
|
||||
assertEquals(-1, Util.binarySearchCeil(values, 0, false, true));
|
||||
|
||||
values.add(1);
|
||||
values.add(3);
|
||||
values.add(5);
|
||||
assertEquals(0, Util.binarySearchCeil(values, 0, false, false));
|
||||
assertEquals(0, Util.binarySearchCeil(values, 0, true, false));
|
||||
|
||||
assertEquals(1, Util.binarySearchCeil(values, 1, false, false));
|
||||
assertEquals(0, Util.binarySearchCeil(values, 1, true, false));
|
||||
|
||||
assertEquals(1, Util.binarySearchCeil(values, 2, false, false));
|
||||
assertEquals(1, Util.binarySearchCeil(values, 2, true, false));
|
||||
|
||||
assertEquals(3, Util.binarySearchCeil(values, 5, false, false));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 5, true, false));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 5, false, true));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 5, true, true));
|
||||
|
||||
assertEquals(3, Util.binarySearchCeil(values, 6, false, false));
|
||||
assertEquals(3, Util.binarySearchCeil(values, 6, true, false));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 6, false, true));
|
||||
assertEquals(2, Util.binarySearchCeil(values, 6, true, true));
|
||||
}
|
||||
|
||||
public void testParseXsDuration() {
|
||||
assertEquals(150279L, Util.parseXsDuration("PT150.279S"));
|
||||
assertEquals(1500L, Util.parseXsDuration("PT1.500S"));
|
||||
}
|
||||
|
||||
public void testParseXsDateTime() throws ParseException {
|
||||
assertEquals(1403219262000L, Util.parseXsDateTime("2014-06-19T23:07:42"));
|
||||
assertEquals(1407322800000L, Util.parseXsDateTime("2014-08-06T11:00:00Z"));
|
||||
}
|
||||
|
||||
}
|
1
library/src/test/libs/.README.txt
Normal file
1
library/src/test/libs/.README.txt
Normal file
@ -0,0 +1 @@
|
||||
This file is needed to make sure the libs directory is present.
|
14
library/src/test/project.properties
Normal file
14
library/src/test/project.properties
Normal file
@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-21
|
2
library/src/test/res/.README.txt
Normal file
2
library/src/test/res/.README.txt
Normal file
@ -0,0 +1,2 @@
|
||||
This file is needed to make sure the res directory is present.
|
||||
The file is ignored by the Android toolchain because its name starts with a dot.
|
201
third_party/dexmaker/LICENSE
vendored
Normal file
201
third_party/dexmaker/LICENSE
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
BIN
third_party/dexmaker/dexmaker-1.2.jar
vendored
Normal file
BIN
third_party/dexmaker/dexmaker-1.2.jar
vendored
Normal file
Binary file not shown.
BIN
third_party/dexmaker/dexmaker-mockito-1.2.jar
vendored
Normal file
BIN
third_party/dexmaker/dexmaker-mockito-1.2.jar
vendored
Normal file
Binary file not shown.
21
third_party/mockito/LICENSE
vendored
Normal file
21
third_party/mockito/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2008-2010 Mockito contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
BIN
third_party/mockito/mockito-all-1.9.5.jar
vendored
Normal file
BIN
third_party/mockito/mockito-all-1.9.5.jar
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user