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