Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
4192ac56
authored
Mar 10, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
WebM Extractor support for Encrypted content.
parent
1ebaaaeb
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
351 additions
and
55 deletions
library/src/main/java/com/google/android/exoplayer/chunk/parser/webm/WebmExtractor.java
library/src/test/java/com/google/android/exoplayer/chunk/parser/webm/WebmExtractorTest.java
library/src/main/java/com/google/android/exoplayer/chunk/parser/webm/WebmExtractor.java
View file @
4192ac56
...
@@ -25,9 +25,13 @@ import com.google.android.exoplayer.upstream.NonBlockingInputStream;
...
@@ -25,9 +25,13 @@ import com.google.android.exoplayer.upstream.NonBlockingInputStream;
import
com.google.android.exoplayer.util.LongArray
;
import
com.google.android.exoplayer.util.LongArray
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
android.media.MediaCodec
;
import
android.media.MediaExtractor
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.UUID
;
import
java.util.UUID
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.TimeUnit
;
...
@@ -38,6 +42,8 @@ import java.util.concurrent.TimeUnit;
...
@@ -38,6 +42,8 @@ import java.util.concurrent.TimeUnit;
* <p>WebM is a subset of the EBML elements defined for Matroska. More information about EBML and
* <p>WebM is a subset of the EBML elements defined for Matroska. More information about EBML and
* Matroska is available <a href="http://www.matroska.org/technical/specs/index.html">here</a>.
* Matroska is available <a href="http://www.matroska.org/technical/specs/index.html">here</a>.
* More info about WebM is <a href="http://www.webmproject.org/code/specs/container/">here</a>.
* More info about WebM is <a href="http://www.webmproject.org/code/specs/container/">here</a>.
* RFC on encrypted WebM can be found
* <a href="http://wiki.webmproject.org/encryption/webm-encryption-rfc">here</a>.
*/
*/
public
final
class
WebmExtractor
implements
Extractor
{
public
final
class
WebmExtractor
implements
Extractor
{
...
@@ -47,6 +53,7 @@ public final class WebmExtractor implements Extractor {
...
@@ -47,6 +53,7 @@ public final class WebmExtractor implements Extractor {
private
static
final
String
CODEC_ID_OPUS
=
"A_OPUS"
;
private
static
final
String
CODEC_ID_OPUS
=
"A_OPUS"
;
private
static
final
int
VORBIS_MAX_INPUT_SIZE
=
8192
;
private
static
final
int
VORBIS_MAX_INPUT_SIZE
=
8192
;
private
static
final
int
OPUS_MAX_INPUT_SIZE
=
5760
;
private
static
final
int
OPUS_MAX_INPUT_SIZE
=
5760
;
private
static
final
int
BLOCK_COUNTER_SIZE
=
16
;
private
static
final
int
UNKNOWN
=
-
1
;
private
static
final
int
UNKNOWN
=
-
1
;
// Element IDs
// Element IDs
...
@@ -80,23 +87,31 @@ public final class WebmExtractor implements Extractor {
...
@@ -80,23 +87,31 @@ public final class WebmExtractor implements Extractor {
private
static
final
int
ID_CHANNELS
=
0x9F
;
private
static
final
int
ID_CHANNELS
=
0x9F
;
private
static
final
int
ID_SAMPLING_FREQUENCY
=
0xB5
;
private
static
final
int
ID_SAMPLING_FREQUENCY
=
0xB5
;
private
static
final
int
ID_CONTENT_ENCODINGS
=
0x6D80
;
private
static
final
int
ID_CONTENT_ENCODING
=
0x6240
;
private
static
final
int
ID_CONTENT_ENCODING_ORDER
=
0x5031
;
private
static
final
int
ID_CONTENT_ENCODING_SCOPE
=
0x5032
;
private
static
final
int
ID_CONTENT_ENCODING_TYPE
=
0x5033
;
private
static
final
int
ID_CONTENT_ENCRYPTION
=
0x5035
;
private
static
final
int
ID_CONTENT_ENCRYPTION_ALGORITHM
=
0x47E1
;
private
static
final
int
ID_CONTENT_ENCRYPTION_KEY_ID
=
0x47E2
;
private
static
final
int
ID_CONTENT_ENCRYPTION_AES_SETTINGS
=
0x47E7
;
private
static
final
int
ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE
=
0x47E8
;
private
static
final
int
ID_CUES
=
0x1C53BB6B
;
private
static
final
int
ID_CUES
=
0x1C53BB6B
;
private
static
final
int
ID_CUE_POINT
=
0xBB
;
private
static
final
int
ID_CUE_POINT
=
0xBB
;
private
static
final
int
ID_CUE_TIME
=
0xB3
;
private
static
final
int
ID_CUE_TIME
=
0xB3
;
private
static
final
int
ID_CUE_TRACK_POSITIONS
=
0xB7
;
private
static
final
int
ID_CUE_TRACK_POSITIONS
=
0xB7
;
private
static
final
int
ID_CUE_CLUSTER_POSITION
=
0xF1
;
private
static
final
int
ID_CUE_CLUSTER_POSITION
=
0xF1
;
// SimpleBlock Lacing Values
private
static
final
int
LACING_NONE
=
0
;
private
static
final
int
LACING_NONE
=
0
;
private
static
final
int
LACING_XIPH
=
1
;
private
static
final
int
LACING_FIXED
=
2
;
private
static
final
int
LACING_EBML
=
3
;
private
static
final
int
READ_TERMINATING_RESULTS
=
RESULT_NEED_MORE_DATA
|
RESULT_END_OF_STREAM
private
static
final
int
READ_TERMINATING_RESULTS
=
RESULT_NEED_MORE_DATA
|
RESULT_END_OF_STREAM
|
RESULT_READ_SAMPLE
|
RESULT_NEED_SAMPLE_HOLDER
;
|
RESULT_READ_SAMPLE
|
RESULT_NEED_SAMPLE_HOLDER
;
private
final
EbmlReader
reader
;
private
final
EbmlReader
reader
;
private
final
byte
[]
simpleBlockTimecodeAndFlags
=
new
byte
[
3
];
private
final
byte
[]
simpleBlockTimecodeAndFlags
=
new
byte
[
3
];
private
final
HashMap
<
UUID
,
byte
[]>
psshInfo
=
new
HashMap
<
UUID
,
byte
[]>();
private
SampleHolder
sampleHolder
;
private
SampleHolder
sampleHolder
;
private
int
readResults
;
private
int
readResults
;
...
@@ -113,7 +128,9 @@ public final class WebmExtractor implements Extractor {
...
@@ -113,7 +128,9 @@ public final class WebmExtractor implements Extractor {
private
String
codecId
;
private
String
codecId
;
private
long
codecDelayNs
;
private
long
codecDelayNs
;
private
long
seekPreRollNs
;
private
long
seekPreRollNs
;
private
boolean
seenAudioTrack
;
private
boolean
isAudioTrack
;
private
boolean
hasContentEncryption
;
private
byte
[]
encryptionKeyId
;
private
long
cuesSizeBytes
=
UNKNOWN
;
private
long
cuesSizeBytes
=
UNKNOWN
;
private
long
clusterTimecodeUs
=
UNKNOWN
;
private
long
clusterTimecodeUs
=
UNKNOWN
;
private
long
simpleBlockTimecodeUs
=
UNKNOWN
;
private
long
simpleBlockTimecodeUs
=
UNKNOWN
;
...
@@ -183,8 +200,7 @@ public final class WebmExtractor implements Extractor {
...
@@ -183,8 +200,7 @@ public final class WebmExtractor implements Extractor {
@Override
@Override
public
Map
<
UUID
,
byte
[]>
getPsshInfo
()
{
public
Map
<
UUID
,
byte
[]>
getPsshInfo
()
{
// TODO: Parse pssh data from Webm streams.
return
psshInfo
.
isEmpty
()
?
null
:
psshInfo
;
return
null
;
}
}
/* package */
int
getElementType
(
int
id
)
{
/* package */
int
getElementType
(
int
id
)
{
...
@@ -197,6 +213,10 @@ public final class WebmExtractor implements Extractor {
...
@@ -197,6 +213,10 @@ public final class WebmExtractor implements Extractor {
case
ID_TRACK_ENTRY:
case
ID_TRACK_ENTRY:
case
ID_AUDIO:
case
ID_AUDIO:
case
ID_VIDEO:
case
ID_VIDEO:
case
ID_CONTENT_ENCODINGS:
case
ID_CONTENT_ENCODING:
case
ID_CONTENT_ENCRYPTION:
case
ID_CONTENT_ENCRYPTION_AES_SETTINGS:
case
ID_CUES:
case
ID_CUES:
case
ID_CUE_POINT:
case
ID_CUE_POINT:
case
ID_CUE_TRACK_POSITIONS:
case
ID_CUE_TRACK_POSITIONS:
...
@@ -211,12 +231,18 @@ public final class WebmExtractor implements Extractor {
...
@@ -211,12 +231,18 @@ public final class WebmExtractor implements Extractor {
case
ID_CODEC_DELAY:
case
ID_CODEC_DELAY:
case
ID_SEEK_PRE_ROLL:
case
ID_SEEK_PRE_ROLL:
case
ID_CHANNELS:
case
ID_CHANNELS:
case
ID_CONTENT_ENCODING_ORDER:
case
ID_CONTENT_ENCODING_SCOPE:
case
ID_CONTENT_ENCODING_TYPE:
case
ID_CONTENT_ENCRYPTION_ALGORITHM:
case
ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE:
case
ID_CUE_TIME:
case
ID_CUE_TIME:
case
ID_CUE_CLUSTER_POSITION:
case
ID_CUE_CLUSTER_POSITION:
return
EbmlReader
.
TYPE_UNSIGNED_INT
;
return
EbmlReader
.
TYPE_UNSIGNED_INT
;
case
ID_DOC_TYPE:
case
ID_DOC_TYPE:
case
ID_CODEC_ID:
case
ID_CODEC_ID:
return
EbmlReader
.
TYPE_STRING
;
return
EbmlReader
.
TYPE_STRING
;
case
ID_CONTENT_ENCRYPTION_KEY_ID:
case
ID_SIMPLE_BLOCK:
case
ID_SIMPLE_BLOCK:
case
ID_BLOCK:
case
ID_BLOCK:
case
ID_CODEC_PRIVATE:
case
ID_CODEC_PRIVATE:
...
@@ -245,6 +271,12 @@ public final class WebmExtractor implements Extractor {
...
@@ -245,6 +271,12 @@ public final class WebmExtractor implements Extractor {
cueTimesUs
=
new
LongArray
();
cueTimesUs
=
new
LongArray
();
cueClusterPositions
=
new
LongArray
();
cueClusterPositions
=
new
LongArray
();
break
;
break
;
case
ID_CONTENT_ENCODING:
// TODO: check and fail if more than one content encoding is present.
break
;
case
ID_CONTENT_ENCRYPTION:
hasContentEncryption
=
true
;
break
;
default
:
default
:
// pass
// pass
}
}
...
@@ -256,17 +288,25 @@ public final class WebmExtractor implements Extractor {
...
@@ -256,17 +288,25 @@ public final class WebmExtractor implements Extractor {
case
ID_CUES:
case
ID_CUES:
buildCues
();
buildCues
();
return
false
;
return
false
;
case
ID_VIDEO:
case
ID_CONTENT_ENCODING:
buildVideoFormat
();
if
(!
hasContentEncryption
)
{
// We found a ContentEncoding other than Encryption.
throw
new
ParserException
(
"Found an unsupported ContentEncoding"
);
}
if
(
encryptionKeyId
==
null
)
{
throw
new
ParserException
(
"Encrypted Track found but ContentEncKeyID was not found"
);
}
// Widevine.
psshInfo
.
put
(
new
UUID
(
0xEDEF8BA979D64ACE
L
,
0xA3C827DCD51D21ED
L
),
encryptionKeyId
);
return
true
;
return
true
;
case
ID_AUDIO:
case
ID_AUDIO:
seen
AudioTrack
=
true
;
is
AudioTrack
=
true
;
return
true
;
return
true
;
case
ID_TRACK_ENTRY:
case
ID_TRACK_ENTRY:
if
(
seenAudioTrack
)
{
if
(
isAudioTrack
)
{
// Audio format has to be built here since codec private may not be available at the end
// of ID_AUDIO.
buildAudioFormat
();
buildAudioFormat
();
}
else
{
buildVideoFormat
();
}
}
return
true
;
return
true
;
default
:
default
:
...
@@ -306,6 +346,37 @@ public final class WebmExtractor implements Extractor {
...
@@ -306,6 +346,37 @@ public final class WebmExtractor implements Extractor {
case
ID_CHANNELS:
case
ID_CHANNELS:
channelCount
=
(
int
)
value
;
channelCount
=
(
int
)
value
;
break
;
break
;
case
ID_CONTENT_ENCODING_ORDER:
// This extractor only supports one ContentEncoding element and hence the order has to be 0.
if
(
value
!=
0
)
{
throw
new
ParserException
(
"ContentEncodingOrder "
+
value
+
" not supported"
);
}
break
;
case
ID_CONTENT_ENCODING_SCOPE:
// This extractor only supports the scope of all frames (since that's the only scope used
// for Encryption).
if
(
value
!=
1
)
{
throw
new
ParserException
(
"ContentEncodingScope "
+
value
+
" not supported"
);
}
break
;
case
ID_CONTENT_ENCODING_TYPE:
// This extractor only supports Encrypted ContentEncodingType.
if
(
value
!=
1
)
{
throw
new
ParserException
(
"ContentEncodingType "
+
value
+
" not supported"
);
}
break
;
case
ID_CONTENT_ENCRYPTION_ALGORITHM:
// Only the value 5 (AES) is allowed according to the WebM specification.
if
(
value
!=
5
)
{
throw
new
ParserException
(
"ContentEncAlgo "
+
value
+
" not supported"
);
}
break
;
case
ID_CONTENT_ENCRYPTION_AES_SETTINGS_CIPHER_MODE:
// Only the value 1 is allowed according to the WebM specification.
if
(
value
!=
1
)
{
throw
new
ParserException
(
"AESSettingsCipherMode "
+
value
+
" not supported"
);
}
break
;
case
ID_CUE_TIME:
case
ID_CUE_TIME:
cueTimesUs
.
add
(
scaleTimecodeToUs
(
value
));
cueTimesUs
.
add
(
scaleTimecodeToUs
(
value
));
break
;
break
;
...
@@ -397,22 +468,48 @@ public final class WebmExtractor implements Extractor {
...
@@ -397,22 +468,48 @@ public final class WebmExtractor implements Extractor {
}
}
boolean
invisible
=
(
simpleBlockTimecodeAndFlags
[
2
]
&
0x08
)
==
0x08
;
boolean
invisible
=
(
simpleBlockTimecodeAndFlags
[
2
]
&
0x08
)
==
0x08
;
int
lacing
=
(
simpleBlockTimecodeAndFlags
[
2
]
&
0x06
)
>>
1
;
int
lacing
=
(
simpleBlockTimecodeAndFlags
[
2
]
&
0x06
)
>>
1
;
if
(
lacing
!=
LACING_NONE
)
{
// Validate lacing and set info into sample holder.
throw
new
ParserException
(
"Lacing mode "
+
lacing
+
" not supported"
);
switch
(
lacing
)
{
}
case
LACING_NONE:
long
elementEndOffsetBytes
=
elementOffsetBytes
+
headerSizeBytes
+
contentsSizeBytes
;
long
elementEndOffsetBytes
=
elementOffsetBytes
+
headerSizeBytes
+
contentsSizeBytes
;
simpleBlockTimecodeUs
=
clusterTimecodeUs
+
timecodeUs
;
simpleBlockTimecodeUs
=
clusterTimecodeUs
+
timecodeUs
;
sampleHolder
.
flags
=
keyframe
?
C
.
SAMPLE_FLAG_SYNC
:
0
;
sampleHolder
.
flags
=
keyframe
?
C
.
SAMPLE_FLAG_SYNC
:
0
;
sampleHolder
.
decodeOnly
=
invisible
;
sampleHolder
.
decodeOnly
=
invisible
;
sampleHolder
.
timeUs
=
clusterTimecodeUs
+
timecodeUs
;
sampleHolder
.
timeUs
=
clusterTimecodeUs
+
timecodeUs
;
sampleHolder
.
size
=
(
int
)
(
elementEndOffsetBytes
-
reader
.
getBytesRead
());
sampleHolder
.
size
=
(
int
)
(
elementEndOffsetBytes
-
reader
.
getBytesRead
());
break
;
case
LACING_EBML:
if
(
hasContentEncryption
)
{
case
LACING_FIXED:
byte
[]
signalByte
=
new
byte
[
1
];
case
LACING_XIPH:
reader
.
readBytes
(
inputStream
,
signalByte
,
1
);
default
:
sampleHolder
.
size
-=
1
;
throw
new
ParserException
(
"Lacing mode "
+
lacing
+
" not supported"
);
// First bit of the signalByte (extension bit) must be 0.
if
((
signalByte
[
0
]
&
0x80
)
!=
0
)
{
throw
new
ParserException
(
"Extension bit is set in signal byte"
);
}
boolean
isEncrypted
=
(
signalByte
[
0
]
&
0x01
)
==
0x01
;
byte
[]
iv
=
null
;
if
(
isEncrypted
)
{
iv
=
sampleHolder
.
cryptoInfo
.
iv
;
if
(
iv
==
null
||
iv
.
length
!=
BLOCK_COUNTER_SIZE
)
{
iv
=
new
byte
[
BLOCK_COUNTER_SIZE
];
}
reader
.
readBytes
(
inputStream
,
iv
,
8
);
// The container has only 8 bytes of IV.
sampleHolder
.
size
-=
8
;
sampleHolder
.
flags
|=
MediaExtractor
.
SAMPLE_FLAG_ENCRYPTED
;
}
int
[]
numBytesOfClearData
=
sampleHolder
.
cryptoInfo
.
numBytesOfClearData
;
if
(
numBytesOfClearData
==
null
||
numBytesOfClearData
.
length
!=
1
)
{
numBytesOfClearData
=
new
int
[
1
];
}
numBytesOfClearData
[
0
]
=
isEncrypted
?
0
:
sampleHolder
.
size
;
int
[]
numBytesOfEncryptedData
=
sampleHolder
.
cryptoInfo
.
numBytesOfEncryptedData
;
if
(
numBytesOfEncryptedData
==
null
||
numBytesOfEncryptedData
.
length
!=
1
)
{
numBytesOfEncryptedData
=
new
int
[
1
];
}
numBytesOfEncryptedData
[
0
]
=
isEncrypted
?
sampleHolder
.
size
:
0
;
sampleHolder
.
cryptoInfo
.
set
(
1
,
numBytesOfClearData
,
numBytesOfEncryptedData
,
encryptionKeyId
,
iv
,
isEncrypted
?
MediaCodec
.
CRYPTO_MODE_AES_CTR
:
MediaCodec
.
CRYPTO_MODE_UNENCRYPTED
);
}
}
if
(
sampleHolder
.
data
==
null
||
sampleHolder
.
data
.
capacity
()
<
sampleHolder
.
size
)
{
if
(
sampleHolder
.
data
==
null
||
sampleHolder
.
data
.
capacity
()
<
sampleHolder
.
size
)
{
...
@@ -432,6 +529,10 @@ public final class WebmExtractor implements Extractor {
...
@@ -432,6 +529,10 @@ public final class WebmExtractor implements Extractor {
codecPrivate
=
new
byte
[
contentsSizeBytes
];
codecPrivate
=
new
byte
[
contentsSizeBytes
];
reader
.
readBytes
(
inputStream
,
codecPrivate
,
contentsSizeBytes
);
reader
.
readBytes
(
inputStream
,
codecPrivate
,
contentsSizeBytes
);
break
;
break
;
case
ID_CONTENT_ENCRYPTION_KEY_ID:
encryptionKeyId
=
new
byte
[
contentsSizeBytes
];
reader
.
readBytes
(
inputStream
,
encryptionKeyId
,
contentsSizeBytes
);
break
;
default
:
default
:
// pass
// pass
}
}
...
...
library/src/test/java/com/google/android/exoplayer/chunk/parser/webm/WebmExtractorTest.java
View file @
4192ac56
...
@@ -24,11 +24,15 @@ import com.google.android.exoplayer.upstream.ByteArrayNonBlockingInputStream;
...
@@ -24,11 +24,15 @@ import com.google.android.exoplayer.upstream.ByteArrayNonBlockingInputStream;
import
com.google.android.exoplayer.upstream.NonBlockingInputStream
;
import
com.google.android.exoplayer.upstream.NonBlockingInputStream
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
android.media.MediaCodec
;
import
android.media.MediaExtractor
;
import
android.test.InstrumentationTestCase
;
import
android.test.InstrumentationTestCase
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Map
;
import
java.util.UUID
;
public
class
WebmExtractorTest
extends
InstrumentationTestCase
{
public
class
WebmExtractorTest
extends
InstrumentationTestCase
{
...
@@ -50,6 +54,12 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -50,6 +54,12 @@ public class WebmExtractorTest extends InstrumentationTestCase {
private
static
final
String
TEST_VORBIS_CODEC_PRIVATE
=
"webm/vorbis_codec_private"
;
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_INFO_SIZE
=
30
;
private
static
final
int
TEST_VORBIS_BOOKS_SIZE
=
4140
;
private
static
final
int
TEST_VORBIS_BOOKS_SIZE
=
4140
;
private
static
final
byte
[]
TEST_ENCRYPTION_KEY_ID
=
{
0x00
,
0x01
,
0x02
,
0x03
};
private
static
final
UUID
WIDEVINE_UUID
=
new
UUID
(
0xEDEF8BA979D64ACE
L
,
0xA3C827DCD51D21ED
L
);
// First 8 bytes of IV come from the container, last 8 bytes are always initialized to 0.
private
static
final
byte
[]
TEST_INITIALIZATION_VECTOR
=
{
0x00
,
0x01
,
0x02
,
0x03
,
0x04
,
0x05
,
0x06
,
0x07
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
};
private
static
final
int
ID_VP9
=
0
;
private
static
final
int
ID_VP9
=
0
;
private
static
final
int
ID_OPUS
=
1
;
private
static
final
int
ID_OPUS
=
1
;
...
@@ -71,7 +81,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -71,7 +81,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepare
()
throws
ParserException
{
public
void
testPrepare
()
throws
ParserException
{
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
));
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
null
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertFormat
();
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
...
@@ -79,7 +89,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -79,7 +89,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareOpus
()
throws
ParserException
{
public
void
testPrepareOpus
()
throws
ParserException
{
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_OPUS
));
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_OPUS
,
null
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertAudioFormat
(
ID_OPUS
);
assertAudioFormat
(
ID_OPUS
);
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
...
@@ -87,15 +97,28 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -87,15 +97,28 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareVorbis
()
throws
ParserException
{
public
void
testPrepareVorbis
()
throws
ParserException
{
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VORBIS
));
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VORBIS
,
null
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertAudioFormat
(
ID_VORBIS
);
assertAudioFormat
(
ID_VORBIS
);
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
}
}
public
void
testPrepareContentEncodingEncryption
()
throws
ParserException
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
1
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
settings
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
Map
<
UUID
,
byte
[]>
psshInfo
=
extractor
.
getPsshInfo
();
assertNotNull
(
psshInfo
);
assertTrue
(
psshInfo
.
containsKey
(
WIDEVINE_UUID
));
android
.
test
.
MoreAsserts
.
assertEquals
(
TEST_ENCRYPTION_KEY_ID
,
psshInfo
.
get
(
WIDEVINE_UUID
));
}
public
void
testPrepareThreeCuePoints
()
throws
ParserException
{
public
void
testPrepareThreeCuePoints
()
throws
ParserException
{
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
3
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
));
createInitializationSegment
(
3
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
null
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertFormat
();
assertIndex
(
assertIndex
(
...
@@ -106,7 +129,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -106,7 +129,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareCustomTimecodeScale
()
throws
ParserException
{
public
void
testPrepareCustomTimecodeScale
()
throws
ParserException
{
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
3
,
0
,
true
,
1000
,
ID_VP9
));
createInitializationSegment
(
3
,
0
,
true
,
1000
,
ID_VP9
,
null
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertFormat
();
assertIndex
(
assertIndex
(
...
@@ -117,7 +140,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -117,7 +140,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareNoCuePoints
()
{
public
void
testPrepareNoCuePoints
()
{
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
0
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
));
createInitializationSegment
(
0
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
null
));
try
{
try
{
extractor
.
read
(
testInputStream
,
sampleHolder
);
extractor
.
read
(
testInputStream
,
sampleHolder
);
fail
();
fail
();
...
@@ -128,7 +151,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -128,7 +151,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareInvalidDocType
()
{
public
void
testPrepareInvalidDocType
()
{
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
false
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
));
createInitializationSegment
(
1
,
0
,
false
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
null
));
try
{
try
{
extractor
.
read
(
testInputStream
,
sampleHolder
);
extractor
.
read
(
testInputStream
,
sampleHolder
);
fail
();
fail
();
...
@@ -137,68 +160,158 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -137,68 +160,158 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
}
}
}
public
void
testPrepareInvalidContentEncodingOrder
()
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
1
,
1
,
1
,
5
,
1
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
settings
));
try
{
extractor
.
read
(
testInputStream
,
sampleHolder
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"ContentEncodingOrder 1 not supported"
,
exception
.
getMessage
());
}
}
public
void
testPrepareInvalidContentEncodingScope
()
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
0
,
1
,
5
,
1
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
settings
));
try
{
extractor
.
read
(
testInputStream
,
sampleHolder
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"ContentEncodingScope 0 not supported"
,
exception
.
getMessage
());
}
}
public
void
testPrepareInvalidContentEncodingType
()
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
0
,
5
,
1
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
settings
));
try
{
extractor
.
read
(
testInputStream
,
sampleHolder
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"ContentEncodingType 0 not supported"
,
exception
.
getMessage
());
}
}
public
void
testPrepareInvalidContentEncAlgo
()
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
4
,
1
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
settings
));
try
{
extractor
.
read
(
testInputStream
,
sampleHolder
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"ContentEncAlgo 4 not supported"
,
exception
.
getMessage
());
}
}
public
void
testPrepareInvalidAESSettingsCipherMode
()
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
0
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
settings
));
try
{
extractor
.
read
(
testInputStream
,
sampleHolder
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"AESSettingsCipherMode 0 not supported"
,
exception
.
getMessage
());
}
}
public
void
testReadSampleKeyframe
()
throws
ParserException
{
public
void
testReadSampleKeyframe
()
throws
ParserException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
);
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
false
,
false
);
byte
[]
testInputData
=
joinByteArrays
(
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
),
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
null
),
mediaSegment
.
clusterBytes
);
mediaSegment
.
clusterBytes
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertFormat
();
assertSample
(
mediaSegment
,
0
,
true
,
false
);
assertSample
(
mediaSegment
,
0
,
true
,
false
,
false
);
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
}
}
public
void
testReadBlock
()
throws
ParserException
{
public
void
testReadBlock
()
throws
ParserException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
false
);
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
false
,
false
,
false
);
byte
[]
testInputData
=
joinByteArrays
(
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_OPUS
),
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_OPUS
,
null
),
mediaSegment
.
clusterBytes
);
mediaSegment
.
clusterBytes
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertAudioFormat
(
ID_OPUS
);
assertAudioFormat
(
ID_OPUS
);
assertSample
(
mediaSegment
,
0
,
true
,
false
);
assertSample
(
mediaSegment
,
0
,
true
,
false
,
false
);
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
}
public
void
testReadEncryptedFrame
()
throws
ParserException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
true
,
true
);
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
settings
),
mediaSegment
.
clusterBytes
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertSample
(
mediaSegment
,
0
,
true
,
false
,
true
);
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
}
}
public
void
testReadEncryptedFrameWithInvalidSignalByte
()
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
true
,
false
);
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
settings
),
mediaSegment
.
clusterBytes
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
try
{
extractor
.
read
(
testInputStream
,
sampleHolder
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"Extension bit is set in signal byte"
,
exception
.
getMessage
());
}
}
public
void
testReadSampleInvisible
()
throws
ParserException
{
public
void
testReadSampleInvisible
()
throws
ParserException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
12
,
13
,
false
,
true
,
true
);
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
12
,
13
,
false
,
true
,
true
,
false
,
false
);
byte
[]
testInputData
=
joinByteArrays
(
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
),
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
null
),
mediaSegment
.
clusterBytes
);
mediaSegment
.
clusterBytes
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertFormat
();
assertSample
(
mediaSegment
,
25000
,
false
,
true
);
assertSample
(
mediaSegment
,
25000
,
false
,
true
,
false
);
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
}
}
public
void
testReadSampleCustomTimescale
()
throws
ParserException
{
public
void
testReadSampleCustomTimescale
()
throws
ParserException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
12
,
13
,
false
,
false
,
true
);
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
12
,
13
,
false
,
false
,
true
,
false
,
false
);
byte
[]
testInputData
=
joinByteArrays
(
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
1000
,
ID_VP9
),
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
1000
,
ID_VP9
,
null
),
mediaSegment
.
clusterBytes
);
mediaSegment
.
clusterBytes
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertFormat
();
assertSample
(
mediaSegment
,
25
,
false
,
false
);
assertSample
(
mediaSegment
,
25
,
false
,
false
,
false
);
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
}
}
public
void
testReadSampleNegativeSimpleBlockTimecode
()
throws
ParserException
{
public
void
testReadSampleNegativeSimpleBlockTimecode
()
throws
ParserException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
13
,
-
12
,
true
,
true
,
true
);
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
13
,
-
12
,
true
,
true
,
true
,
false
,
false
);
byte
[]
testInputData
=
joinByteArrays
(
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
),
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
ID_VP9
,
null
),
mediaSegment
.
clusterBytes
);
mediaSegment
.
clusterBytes
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
NonBlockingInputStream
testInputStream
=
new
ByteArrayNonBlockingInputStream
(
testInputData
);
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
EXPECTED_INIT_AND_SAMPLE_RESULT
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertFormat
();
assertFormat
();
assertSample
(
mediaSegment
,
1000
,
true
,
true
);
assertSample
(
mediaSegment
,
1000
,
true
,
true
,
false
);
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
assertEquals
(
WebmExtractor
.
RESULT_END_OF_STREAM
,
extractor
.
read
(
testInputStream
,
sampleHolder
));
}
}
...
@@ -241,23 +354,33 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -241,23 +354,33 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
}
private
void
assertSample
(
private
void
assertSample
(
MediaSegment
mediaSegment
,
int
timeUs
,
boolean
keyframe
,
boolean
invisible
)
{
MediaSegment
mediaSegment
,
int
timeUs
,
boolean
keyframe
,
boolean
invisible
,
boolean
encrypted
)
{
assertTrue
(
Arrays
.
equals
(
assertTrue
(
Arrays
.
equals
(
mediaSegment
.
videoBytes
,
Arrays
.
copyOf
(
sampleHolder
.
data
.
array
(),
sampleHolder
.
size
)));
mediaSegment
.
videoBytes
,
Arrays
.
copyOf
(
sampleHolder
.
data
.
array
(),
sampleHolder
.
size
)));
assertEquals
(
timeUs
,
sampleHolder
.
timeUs
);
assertEquals
(
timeUs
,
sampleHolder
.
timeUs
);
assertEquals
(
keyframe
,
(
sampleHolder
.
flags
&
C
.
SAMPLE_FLAG_SYNC
)
!=
0
);
assertEquals
(
keyframe
,
(
sampleHolder
.
flags
&
C
.
SAMPLE_FLAG_SYNC
)
!=
0
);
assertEquals
(
invisible
,
sampleHolder
.
decodeOnly
);
assertEquals
(
invisible
,
sampleHolder
.
decodeOnly
);
assertEquals
(
encrypted
,
(
sampleHolder
.
flags
&
MediaExtractor
.
SAMPLE_FLAG_ENCRYPTED
)
!=
0
);
if
(
encrypted
)
{
android
.
test
.
MoreAsserts
.
assertEquals
(
TEST_INITIALIZATION_VECTOR
,
sampleHolder
.
cryptoInfo
.
iv
);
assertEquals
(
MediaCodec
.
CRYPTO_MODE_AES_CTR
,
sampleHolder
.
cryptoInfo
.
mode
);
assertEquals
(
1
,
sampleHolder
.
cryptoInfo
.
numSubSamples
);
assertEquals
(
100
,
sampleHolder
.
cryptoInfo
.
numBytesOfEncryptedData
[
0
]);
assertEquals
(
0
,
sampleHolder
.
cryptoInfo
.
numBytesOfClearData
[
0
]);
}
}
}
private
byte
[]
createInitializationSegment
(
private
byte
[]
createInitializationSegment
(
int
cuePoints
,
int
mediaSegmentSize
,
boolean
docTypeIsWebm
,
int
timecodeScale
,
int
cuePoints
,
int
mediaSegmentSize
,
boolean
docTypeIsWebm
,
int
timecodeScale
,
int
codecId
)
{
int
codecId
,
ContentEncodingSettings
contentEncodingSettings
)
{
int
initalizationSegmentSize
=
INFO_ELEMENT_BYTE_SIZE
+
TRACKS_ELEMENT_BYTE_SIZE
int
initalizationSegmentSize
=
INFO_ELEMENT_BYTE_SIZE
+
TRACKS_ELEMENT_BYTE_SIZE
+
CUES_ELEMENT_BYTE_SIZE
+
CUE_POINT_ELEMENT_BYTE_SIZE
*
cuePoints
;
+
CUES_ELEMENT_BYTE_SIZE
+
CUE_POINT_ELEMENT_BYTE_SIZE
*
cuePoints
;
byte
[]
tracksElement
=
null
;
byte
[]
tracksElement
=
null
;
switch
(
codecId
)
{
switch
(
codecId
)
{
case
ID_VP9:
case
ID_VP9:
tracksElement
=
createTracksElementWithVideo
(
true
,
TEST_WIDTH
,
TEST_HEIGHT
);
tracksElement
=
createTracksElementWithVideo
(
true
,
TEST_WIDTH
,
TEST_HEIGHT
,
contentEncodingSettings
);
break
;
break
;
case
ID_OPUS:
case
ID_OPUS:
tracksElement
=
createTracksElementWithOpusAudio
(
TEST_CHANNEL_COUNT
);
tracksElement
=
createTracksElementWithOpusAudio
(
TEST_CHANNEL_COUNT
);
...
@@ -278,12 +401,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -278,12 +401,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
}
private
static
MediaSegment
createMediaSegment
(
int
videoBytesLength
,
int
clusterTimecode
,
private
static
MediaSegment
createMediaSegment
(
int
videoBytesLength
,
int
clusterTimecode
,
int
blockTimecode
,
boolean
keyframe
,
boolean
invisible
,
boolean
isSimple
)
{
int
blockTimecode
,
boolean
keyframe
,
boolean
invisible
,
boolean
simple
,
boolean
encrypted
,
boolean
validSignalByte
)
{
byte
[]
videoBytes
=
createVideoBytes
(
videoBytesLength
);
byte
[]
videoBytes
=
createVideoBytes
(
videoBytesLength
);
byte
[]
blockBytes
;
byte
[]
blockBytes
;
if
(
isS
imple
)
{
if
(
s
imple
)
{
blockBytes
=
createSimpleBlockElement
(
videoBytes
.
length
,
blockTimecode
,
blockBytes
=
createSimpleBlockElement
(
videoBytes
.
length
,
blockTimecode
,
keyframe
,
invisible
,
true
);
keyframe
,
invisible
,
true
,
encrypted
,
validSignalByte
);
}
else
{
}
else
{
blockBytes
=
createBlockElement
(
videoBytes
.
length
,
blockTimecode
,
invisible
,
true
);
blockBytes
=
createBlockElement
(
videoBytes
.
length
,
blockTimecode
,
invisible
,
true
);
}
}
...
@@ -338,9 +462,52 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -338,9 +462,52 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
}
private
static
byte
[]
createTracksElementWithVideo
(
private
static
byte
[]
createTracksElementWithVideo
(
boolean
codecIsVp9
,
int
pixelWidth
,
int
pixelHeight
)
{
boolean
codecIsVp9
,
int
pixelWidth
,
int
pixelHeight
,
ContentEncodingSettings
contentEncodingSettings
)
{
byte
[]
widthBytes
=
getIntegerBytes
(
pixelWidth
);
byte
[]
widthBytes
=
getIntegerBytes
(
pixelWidth
);
byte
[]
heightBytes
=
getIntegerBytes
(
pixelHeight
);
byte
[]
heightBytes
=
getIntegerBytes
(
pixelHeight
);
if
(
contentEncodingSettings
!=
null
)
{
byte
[]
orderBytes
=
getIntegerBytes
(
contentEncodingSettings
.
order
);
byte
[]
scopeBytes
=
getIntegerBytes
(
contentEncodingSettings
.
scope
);
byte
[]
typeBytes
=
getIntegerBytes
(
contentEncodingSettings
.
type
);
byte
[]
algorithmBytes
=
getIntegerBytes
(
contentEncodingSettings
.
algorithm
);
byte
[]
cipherModeBytes
=
getIntegerBytes
(
contentEncodingSettings
.
aesCipherMode
);
return
createByteArray
(
0x16
,
0x54
,
0xAE
,
0x6B
,
// Tracks
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x48
,
// size=72
0xAE
,
// TrackEntry
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x3F
,
// size=63
0x86
,
// CodecID
0x85
,
0x56
,
0x5F
,
0x56
,
0x50
,
codecIsVp9
?
0x39
:
0x30
,
// size=5 value=V_VP9/0
0x6D
,
0x80
,
// ContentEncodings
0xA4
,
// size=36
0x62
,
0x40
,
// ContentEncoding
0xA1
,
// size=33
0x50
,
0x31
,
// ContentEncodingOrder
0x81
,
orderBytes
[
3
],
0x50
,
0x32
,
// ContentEncodingScope
0x81
,
scopeBytes
[
3
],
0x50
,
0x33
,
// ContentEncodingType
0x81
,
typeBytes
[
3
],
0x50
,
0x35
,
// ContentEncryption
0x92
,
// size=18
0x47
,
0xE1
,
// ContentEncAlgo
0x81
,
algorithmBytes
[
3
],
0x47
,
0xE2
,
// ContentEncKeyID
0x84
,
// size=4
TEST_ENCRYPTION_KEY_ID
[
0
],
TEST_ENCRYPTION_KEY_ID
[
1
],
TEST_ENCRYPTION_KEY_ID
[
2
],
TEST_ENCRYPTION_KEY_ID
[
3
],
// value=binary
0x47
,
0xE7
,
// ContentEncAESSettings
0x84
,
// size=4
0x47
,
0xE8
,
// AESSettingsCipherMode
0x81
,
cipherModeBytes
[
3
],
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
}
else
{
return
createByteArray
(
return
createByteArray
(
0x16
,
0x54
,
0xAE
,
0x6B
,
// Tracks
0x16
,
0x54
,
0xAE
,
0x6B
,
// Tracks
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x24
,
// size=36
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x24
,
// size=36
...
@@ -355,6 +522,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -355,6 +522,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
0xBA
,
// PixelHeight
0xBA
,
// PixelHeight
0x82
,
heightBytes
[
2
],
heightBytes
[
3
]);
// size=2
0x82
,
heightBytes
[
2
],
heightBytes
[
3
]);
// size=2
}
}
}
private
static
byte
[]
createTracksElementWithOpusAudio
(
int
channelCount
)
{
private
static
byte
[]
createTracksElementWithOpusAudio
(
int
channelCount
)
{
byte
[]
channelCountBytes
=
getIntegerBytes
(
channelCount
);
byte
[]
channelCountBytes
=
getIntegerBytes
(
channelCount
);
...
@@ -438,16 +606,23 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -438,16 +606,23 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
}
private
static
byte
[]
createSimpleBlockElement
(
private
static
byte
[]
createSimpleBlockElement
(
int
size
,
int
timecode
,
boolean
keyframe
,
boolean
invisible
,
boolean
noLacing
)
{
int
size
,
int
timecode
,
boolean
keyframe
,
boolean
invisible
,
boolean
noLacing
,
byte
[]
sizeBytes
=
getIntegerBytes
(
size
+
4
);
boolean
encrypted
,
boolean
validSignalByte
)
{
byte
[]
sizeBytes
=
getIntegerBytes
(
size
+
4
+
(
encrypted
?
9
:
0
));
byte
[]
timeBytes
=
getIntegerBytes
(
timecode
);
byte
[]
timeBytes
=
getIntegerBytes
(
timecode
);
byte
flags
=
(
byte
)
byte
flags
=
(
byte
)
((
keyframe
?
0x80
:
0x00
)
|
(
invisible
?
0x08
:
0x00
)
|
(
noLacing
?
0x00
:
0x06
));
((
keyframe
?
0x80
:
0x00
)
|
(
invisible
?
0x08
:
0x00
)
|
(
noLacing
?
0x00
:
0x06
));
return
createByteArray
(
byte
[]
simpleBlock
=
createByteArray
(
0xA3
,
// SimpleBlock
0xA3
,
// SimpleBlock
0x01
,
0x00
,
0x00
,
0x00
,
sizeBytes
[
0
],
sizeBytes
[
1
],
sizeBytes
[
2
],
sizeBytes
[
3
],
0x01
,
0x00
,
0x00
,
0x00
,
sizeBytes
[
0
],
sizeBytes
[
1
],
sizeBytes
[
2
],
sizeBytes
[
3
],
0x81
,
// Track number value=1
0x81
,
// Track number value=1
timeBytes
[
2
],
timeBytes
[
3
],
flags
);
// Timecode and flags
timeBytes
[
2
],
timeBytes
[
3
],
flags
);
// Timecode and flags
if
(
encrypted
)
{
simpleBlock
=
joinByteArrays
(
simpleBlock
,
createByteArray
(
validSignalByte
?
0x01
:
0x80
),
Arrays
.
copyOfRange
(
TEST_INITIALIZATION_VECTOR
,
0
,
8
));
}
return
simpleBlock
;
}
}
private
static
byte
[]
createBlockElement
(
private
static
byte
[]
createBlockElement
(
...
@@ -520,4 +695,24 @@ public class WebmExtractorTest extends InstrumentationTestCase {
...
@@ -520,4 +695,24 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
}
/** Used by {@link createTracksElementWithVideo} to create a Track header with Encryption. */
private
static
final
class
ContentEncodingSettings
{
private
final
int
order
;
private
final
int
scope
;
private
final
int
type
;
private
final
int
algorithm
;
private
final
int
aesCipherMode
;
private
ContentEncodingSettings
(
int
order
,
int
scope
,
int
type
,
int
algorithm
,
int
aesCipherMode
)
{
this
.
order
=
order
;
this
.
scope
=
scope
;
this
.
type
=
type
;
this
.
algorithm
=
algorithm
;
this
.
aesCipherMode
=
aesCipherMode
;
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment