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
eea57d0d
authored
Jun 15, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Enhance WebM extractor tests
parent
854fa928
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
782 additions
and
560 deletions
library/src/test/java/com/google/android/exoplayer/extractor/webm/StreamBuilder.java
library/src/test/java/com/google/android/exoplayer/extractor/webm/WebmExtractorTest.java
library/src/test/java/com/google/android/exoplayer/extractor/webm/StreamBuilder.java
0 → 100644
View file @
eea57d0d
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer
.
extractor
.
webm
;
import
com.google.android.exoplayer.util.Assertions
;
import
java.nio.ByteBuffer
;
import
java.util.Arrays
;
import
java.util.LinkedList
;
import
java.util.List
;
/**
* Provides byte arrays containing WebM data for {@link WebmExtractorTest}.
*/
/* package */
final
class
StreamBuilder
{
/** Used by {@link #addVp9Track} to create a Track header with Encryption. */
public
static
final
class
ContentEncodingSettings
{
private
final
int
order
;
private
final
int
scope
;
private
final
int
type
;
private
final
int
algorithm
;
private
final
int
aesCipherMode
;
public
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
;
}
}
public
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
;
}
public
static
final
byte
[]
TEST_ENCRYPTION_KEY_ID
=
{
0x00
,
0x01
,
0x02
,
0x03
};
public
static
final
byte
[]
TEST_INITIALIZATION_VECTOR
=
{
0x00
,
0x01
,
0x02
,
0x03
,
0x04
,
0x05
,
0x06
,
0x07
};
private
static
final
int
NO_VALUE
=
-
1
;
private
EbmlElement
header
;
private
EbmlElement
info
;
private
List
<
EbmlElement
>
trackEntries
;
private
List
<
EbmlElement
>
mediaSegments
;
public
StreamBuilder
()
{
trackEntries
=
new
LinkedList
<>();
mediaSegments
=
new
LinkedList
<>();
}
public
StreamBuilder
setHeader
(
String
docType
)
{
header
=
createEbmlElement
(
1
,
docType
,
2
);
return
this
;
}
public
StreamBuilder
setInfo
(
int
timecodeScale
,
long
durationUs
)
{
info
=
createInfoElement
(
timecodeScale
,
durationUs
);
return
this
;
}
public
StreamBuilder
addVp9Track
(
int
width
,
int
height
,
ContentEncodingSettings
contentEncodingSettings
)
{
trackEntries
.
add
(
createVideoTrackEntry
(
"V_VP9"
,
width
,
height
,
contentEncodingSettings
,
null
));
return
this
;
}
public
StreamBuilder
addOpusTrack
(
int
channelCount
,
int
sampleRate
,
int
codecDelay
,
int
seekPreRoll
,
byte
[]
codecPrivate
)
{
trackEntries
.
add
(
createAudioTrackEntry
(
"A_OPUS"
,
channelCount
,
sampleRate
,
codecPrivate
,
codecDelay
,
seekPreRoll
));
return
this
;
}
public
StreamBuilder
addVorbisTrack
(
int
channelCount
,
int
sampleRate
,
byte
[]
codecPrivate
)
{
trackEntries
.
add
(
createAudioTrackEntry
(
"A_VORBIS"
,
channelCount
,
sampleRate
,
codecPrivate
,
NO_VALUE
,
NO_VALUE
));
return
this
;
}
public
StreamBuilder
addUnsupportedTrack
()
{
trackEntries
.
add
(
element
(
0xAE
,
// TrackEntry
element
(
0x86
,
"D_WEBVTT/metadata"
.
getBytes
()),
// CodecID
element
(
0xD7
,
(
byte
)
0x03
),
// TrackNumber
element
(
0x83
,
(
byte
)
0x11
)));
// TrackType
return
this
;
}
public
StreamBuilder
addSimpleBlockEncryptedMedia
(
int
trackNumber
,
int
clusterTimecode
,
int
blockTimecode
,
boolean
keyframe
,
boolean
invisible
,
boolean
validSignalByte
,
byte
[]
data
)
{
byte
flags
=
(
byte
)
((
keyframe
?
0x80
:
0x00
)
|
(
invisible
?
0x08
:
0x00
));
EbmlElement
simpleBlockElement
=
createSimpleBlock
(
trackNumber
,
blockTimecode
,
flags
,
true
,
validSignalByte
,
data
);
mediaSegments
.
add
(
createCluster
(
clusterTimecode
,
simpleBlockElement
));
return
this
;
}
public
StreamBuilder
addSimpleBlockMedia
(
int
trackNumber
,
int
clusterTimecode
,
int
blockTimecode
,
boolean
keyframe
,
boolean
invisible
,
byte
[]
data
)
{
byte
flags
=
(
byte
)
((
keyframe
?
0x80
:
0x00
)
|
(
invisible
?
0x08
:
0x00
));
EbmlElement
simpleBlockElement
=
createSimpleBlock
(
trackNumber
,
blockTimecode
,
flags
,
false
,
true
,
data
);
mediaSegments
.
add
(
createCluster
(
clusterTimecode
,
simpleBlockElement
));
return
this
;
}
public
StreamBuilder
addBlockMedia
(
int
trackNumber
,
int
clusterTimecode
,
int
blockTimecode
,
boolean
keyframe
,
boolean
invisible
,
byte
[]
data
)
{
byte
flags
=
(
byte
)
(
invisible
?
0x08
:
0x00
);
EbmlElement
blockElement
=
createBlock
(
trackNumber
,
blockTimecode
,
keyframe
,
flags
,
data
);
mediaSegments
.
add
(
createCluster
(
clusterTimecode
,
blockElement
));
return
this
;
}
/**
* Serializes the constructed stream to a {@code byte[]} using the specified number of cue points.
*/
public
byte
[]
build
(
int
cuePointCount
)
{
Assertions
.
checkNotNull
(
header
);
Assertions
.
checkNotNull
(
info
);
EbmlElement
tracks
=
element
(
0x1654AE6B
,
trackEntries
.
toArray
(
new
EbmlElement
[
0
]));
// Get the size of the initialization segment.
EbmlElement
[]
cuePointElements
=
new
EbmlElement
[
cuePointCount
];
for
(
int
i
=
0
;
i
<
cuePointCount
;
i
++)
{
cuePointElements
[
i
]
=
createCuePointElement
(
10
*
i
,
0
);
}
EbmlElement
cues
=
element
(
0x1C53BB6B
,
cuePointElements
);
// Cues
long
initializationSegmentSize
=
info
.
getSize
()
+
tracks
.
getSize
()
+
cues
.
getSize
();
// Recreate the initialization segment using its size as an offset.
for
(
int
i
=
0
;
i
<
cuePointCount
;
i
++)
{
cuePointElements
[
i
]
=
createCuePointElement
(
10
*
i
,
(
int
)
initializationSegmentSize
);
}
cues
=
element
(
0x1C53BB6B
,
cuePointElements
);
// Cues
// Build the top-level segment element.
EbmlElement
[]
children
=
new
EbmlElement
[
3
+
mediaSegments
.
size
()];
System
.
arraycopy
(
mediaSegments
.
toArray
(
new
EbmlElement
[
0
]),
0
,
children
,
3
,
mediaSegments
.
size
());
children
[
0
]
=
info
;
children
[
1
]
=
tracks
;
children
[
2
]
=
cues
;
EbmlElement
segmentElement
=
element
(
0x18538067
,
children
);
// Segment
// Serialize the EBML header and the top-level segment element.
return
EbmlElement
.
serialize
(
header
,
segmentElement
);
}
private
static
EbmlElement
createCuePointElement
(
int
cueTime
,
int
cueClusterPosition
)
{
byte
[]
positionBytes
=
getLongBytes
(
cueClusterPosition
);
return
element
(
0xBB
,
// CuePoint
element
(
0xB3
,
(
byte
)
(
cueTime
&
0xFF
)),
// CueTime
element
(
0xB7
,
// CueTrackPositions
element
(
0xF1
,
positionBytes
)));
// CueClusterPosition
}
private
static
EbmlElement
createEbmlElement
(
int
ebmlReadVersion
,
String
docType
,
int
docTypeReadVersion
)
{
return
element
(
0x1A45DFA3
,
// EBML
element
(
0x42F7
,
(
byte
)
(
ebmlReadVersion
&
0xFF
)),
// EBMLReadVersion
element
(
0x4282
,
docType
.
getBytes
()),
// DocType
element
(
0x4285
,
(
byte
)
(
docTypeReadVersion
&
0xFF
)));
// DocTypeReadVersion
}
private
EbmlElement
createInfoElement
(
int
timecodeScale
,
long
durationUs
)
{
byte
[]
timecodeScaleBytes
=
getIntegerBytes
(
timecodeScale
);
byte
[]
durationBytes
=
getLongBytes
(
Double
.
doubleToLongBits
(
durationUs
/
1000.0
));
return
element
(
0x1549A966
,
// Info
element
(
0x2AD7B1
,
timecodeScaleBytes
),
// TimecodeScale
element
(
0x4489
,
durationBytes
));
// Duration
}
private
static
EbmlElement
createVideoTrackEntry
(
String
codecId
,
int
pixelWidth
,
int
pixelHeight
,
ContentEncodingSettings
contentEncodingSettings
,
byte
[]
codecPrivate
)
{
byte
[]
widthBytes
=
getIntegerBytes
(
pixelWidth
);
byte
[]
heightBytes
=
getIntegerBytes
(
pixelHeight
);
EbmlElement
contentEncodingSettingsElement
;
if
(
contentEncodingSettings
!=
null
)
{
contentEncodingSettingsElement
=
element
(
0x6D80
,
// ContentEncodings
element
(
0x6240
,
// ContentEncoding
// ContentEncodingOrder
element
(
0x5031
,
(
byte
)
(
contentEncodingSettings
.
order
&
0xFF
)),
// ContentEncodingScope
element
(
0x5032
,
(
byte
)
(
contentEncodingSettings
.
scope
&
0xFF
)),
// ContentEncodingType
element
(
0x5033
,
(
byte
)
(
contentEncodingSettings
.
type
&
0xFF
)),
element
(
0x5035
,
// ContentEncryption
// ContentEncAlgo
element
(
0x47E1
,
(
byte
)
(
contentEncodingSettings
.
algorithm
&
0xFF
)),
element
(
0x47E2
,
TEST_ENCRYPTION_KEY_ID
),
// ContentEncKeyID
element
(
0x47E7
,
// ContentEncAESSettings
// AESSettingsCipherMode
element
(
0x47E8
,
(
byte
)
(
contentEncodingSettings
.
aesCipherMode
&
0xFF
))))));
}
else
{
contentEncodingSettingsElement
=
empty
();
}
EbmlElement
codecPrivateElement
;
if
(
codecPrivate
!=
null
)
{
codecPrivateElement
=
element
(
0x63A2
,
codecPrivate
);
// CodecPrivate
}
else
{
codecPrivateElement
=
empty
();
}
return
element
(
0xAE
,
// TrackEntry
element
(
0x86
,
codecId
.
getBytes
()),
// CodecID
element
(
0xD7
,
(
byte
)
0x01
),
// TrackNumber
element
(
0x83
,
(
byte
)
0x01
),
// TrackType
contentEncodingSettingsElement
,
element
(
0xE0
,
// Video
element
(
0xB0
,
widthBytes
[
2
],
widthBytes
[
3
]),
element
(
0xBA
,
heightBytes
[
2
],
heightBytes
[
3
])),
codecPrivateElement
);
}
private
static
EbmlElement
createAudioTrackEntry
(
String
codecId
,
int
channelCount
,
int
sampleRate
,
byte
[]
codecPrivate
,
int
codecDelay
,
int
seekPreRoll
)
{
byte
channelCountByte
=
(
byte
)
(
channelCount
&
0xFF
);
byte
[]
sampleRateDoubleBytes
=
getLongBytes
(
Double
.
doubleToLongBits
(
sampleRate
));
return
element
(
0xAE
,
// TrackEntry
element
(
0x86
,
codecId
.
getBytes
()),
// CodecID
element
(
0xD7
,
(
byte
)
0x02
),
// TrackNumber
element
(
0x83
,
(
byte
)
0x02
),
// TrackType
// CodecDelay
codecDelay
!=
NO_VALUE
?
element
(
0x56AA
,
getIntegerBytes
(
codecDelay
))
:
empty
(),
// SeekPreRoll
seekPreRoll
!=
NO_VALUE
?
element
(
0x56BB
,
getIntegerBytes
(
seekPreRoll
))
:
empty
(),
element
(
0xE1
,
// Audio
element
(
0x9F
,
channelCountByte
),
// Channels
element
(
0xB5
,
sampleRateDoubleBytes
)),
// SamplingFrequency
element
(
0x63A2
,
codecPrivate
));
// CodecPrivate
}
private
static
EbmlElement
createCluster
(
int
timecode
,
EbmlElement
blockGroupOrSimpleBlock
)
{
return
element
(
0x1F43B675
,
// Cluster
element
(
0xE7
,
getIntegerBytes
(
timecode
)),
// Timecode
blockGroupOrSimpleBlock
);
}
private
static
EbmlElement
createSimpleBlock
(
int
trackNumber
,
int
timecode
,
int
flags
,
boolean
encrypted
,
boolean
validSignalByte
,
byte
[]
data
)
{
byte
[]
trackNumberBytes
=
getIntegerBytes
(
trackNumber
);
byte
[]
timeBytes
=
getIntegerBytes
(
timecode
);
byte
[]
simpleBlockBytes
=
createByteArray
(
0x40
,
trackNumberBytes
[
3
],
// Track number size=2
timeBytes
[
2
],
timeBytes
[
3
],
flags
);
// Timecode and flags
if
(
encrypted
)
{
simpleBlockBytes
=
joinByteArrays
(
simpleBlockBytes
,
createByteArray
(
validSignalByte
?
0x01
:
0x80
),
Arrays
.
copyOfRange
(
TEST_INITIALIZATION_VECTOR
,
0
,
8
));
}
return
element
(
0xA3
,
// SimpleBlock
joinByteArrays
(
simpleBlockBytes
,
data
));
}
private
static
EbmlElement
createBlock
(
int
trackNumber
,
int
timecode
,
boolean
keyframe
,
int
flags
,
byte
[]
data
)
{
byte
[]
trackNumberBytes
=
getIntegerBytes
(
trackNumber
);
byte
[]
timeBytes
=
getIntegerBytes
(
timecode
);
byte
[]
blockBytes
=
createByteArray
(
0x40
,
trackNumberBytes
[
3
],
// Track number size=2
timeBytes
[
2
],
timeBytes
[
3
],
flags
);
// Timecode and flags
EbmlElement
block
=
element
(
0xA1
,
// Block
joinByteArrays
(
blockBytes
,
data
));
EbmlElement
referenceBlock
=
keyframe
?
empty
()
:
element
(
0xFB
,
(
byte
)
0x00
);
// ReferenceBlock
return
element
(
0xA0
,
// BlockGroup
referenceBlock
,
block
);
}
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
;
}
private
static
byte
[]
getIntegerBytes
(
int
value
)
{
return
createByteArray
(
(
value
&
0xFF000000
)
>>
24
,
(
value
&
0x00FF0000
)
>>
16
,
(
value
&
0x0000FF00
)
>>
8
,
(
value
&
0x000000FF
));
}
private
static
byte
[]
getLongBytes
(
long
value
)
{
byte
[]
result
=
new
byte
[
8
];
for
(
int
i
=
0
;
i
<
8
;
i
++)
{
result
[
7
-
i
]
=
(
byte
)
((
value
>>
(
8
*
i
))
&
0xFF
);
}
return
result
;
}
/** @see EbmlElement#EbmlElement(int, EbmlElement...) */
private
static
EbmlElement
element
(
int
type
,
EbmlElement
...
childElements
)
{
return
new
EbmlElement
(
type
,
childElements
);
}
/** @see EbmlElement#EbmlElement(int, byte...) */
private
static
EbmlElement
element
(
int
type
,
byte
...
payload
)
{
return
new
EbmlElement
(
type
,
payload
);
}
/** @see EbmlElement#EbmlElement() */
private
static
EbmlElement
empty
()
{
return
new
EbmlElement
();
}
/** Represents a WebM EBML element that can be serialized as a byte array. */
private
static
final
class
EbmlElement
{
/** Returns a byte[] containing the concatenation of the data from all {@code elements}. */
public
static
byte
[]
serialize
(
EbmlElement
...
elements
)
{
int
size
=
0
;
for
(
EbmlElement
element
:
elements
)
{
size
+=
element
.
getSize
();
}
ByteBuffer
buffer
=
ByteBuffer
.
allocate
(
size
);
for
(
EbmlElement
element
:
elements
)
{
element
.
getData
(
buffer
);
}
return
buffer
.
array
();
}
private
final
int
id
;
private
final
EbmlElement
[]
childElements
;
private
final
byte
[]
payload
;
/** Creates an element containing the specified {@code childElements}. */
EbmlElement
(
int
id
,
EbmlElement
...
childElements
)
{
this
.
id
=
id
;
this
.
childElements
=
childElements
;
payload
=
null
;
}
/** Creates an element containing the specified {@code payload}. */
EbmlElement
(
int
id
,
byte
...
payload
)
{
this
.
id
=
id
;
this
.
payload
=
payload
;
childElements
=
null
;
}
/** Creates a completely empty element that will contribute no bytes to the output. */
EbmlElement
()
{
id
=
NO_VALUE
;
payload
=
null
;
childElements
=
null
;
}
private
long
getSize
()
{
if
(
id
==
NO_VALUE
)
{
return
0
;
}
long
payloadSize
=
getPayloadSize
();
return
getIdLength
()
+
getVIntLength
(
payloadSize
)
+
payloadSize
;
}
private
long
getPayloadSize
()
{
if
(
payload
!=
null
)
{
return
payload
.
length
;
}
int
payloadSize
=
0
;
for
(
EbmlElement
element
:
childElements
)
{
payloadSize
+=
element
.
getSize
();
}
return
payloadSize
;
}
private
void
getData
(
ByteBuffer
byteBuffer
)
{
if
(
id
==
NO_VALUE
)
{
return
;
}
putId
(
byteBuffer
);
putVInt
(
byteBuffer
,
getPayloadSize
());
if
(
payload
!=
null
)
{
byteBuffer
.
put
(
payload
);
}
else
{
for
(
EbmlElement
atom
:
childElements
)
{
atom
.
getData
(
byteBuffer
);
}
}
}
private
int
getIdLength
()
{
if
(
id
==
NO_VALUE
)
{
return
0
;
}
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
if
(
id
<
1
<<
(
7
*
i
+
8
))
{
return
i
+
1
;
}
}
throw
new
IllegalArgumentException
();
}
private
static
int
getVIntLength
(
long
vInt
)
{
for
(
int
i
=
1
;
i
<
9
;
i
++)
{
if
(
vInt
<
1
<<
(
7
*
i
))
{
return
i
;
}
}
throw
new
IllegalArgumentException
();
}
private
void
putId
(
ByteBuffer
byteBuffer
)
{
int
length
=
getIdLength
();
for
(
int
i
=
length
-
1
;
i
>=
0
;
i
--)
{
byteBuffer
.
put
((
byte
)
((
id
>>
(
i
*
8
))
&
0xFF
));
}
}
private
static
void
putVInt
(
ByteBuffer
byteBuffer
,
long
vInt
)
{
int
vIntLength
=
getVIntLength
(
vInt
);
vInt
|=
1
<<
(
vIntLength
*
7
);
for
(
int
i
=
vIntLength
-
1
;
i
>=
0
;
i
--)
{
byteBuffer
.
put
((
byte
)
((
vInt
>>
(
i
*
8
))
&
0xFF
));
}
}
}
}
library/src/test/java/com/google/android/exoplayer/extractor/webm/WebmExtractorTest.java
View file @
eea57d0d
...
...
@@ -15,6 +15,8 @@
*/
package
com
.
google
.
android
.
exoplayer
.
extractor
.
webm
;
import
static
com
.
google
.
android
.
exoplayer
.
extractor
.
webm
.
StreamBuilder
.
TEST_ENCRYPTION_KEY_ID
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.ParserException
;
...
...
@@ -26,6 +28,7 @@ import com.google.android.exoplayer.extractor.ExtractorInput;
import
com.google.android.exoplayer.extractor.ExtractorOutput
;
import
com.google.android.exoplayer.extractor.SeekMap
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.extractor.webm.StreamBuilder.ContentEncodingSettings
;
import
com.google.android.exoplayer.testutil.FakeDataSource
;
import
com.google.android.exoplayer.upstream.DataSource
;
import
com.google.android.exoplayer.upstream.DataSpec
;
...
...
@@ -37,7 +40,6 @@ import android.test.InstrumentationTestCase;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.util.Arrays
;
import
java.util.UUID
;
/**
...
...
@@ -45,32 +47,21 @@ import java.util.UUID;
*/
public
class
WebmExtractorTest
extends
InstrumentationTestCase
{
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
int
TEST_CODEC_DELAY
=
6500000
;
private
static
final
int
TEST_SEEK_PRE_ROLL
=
80000000
;
private
static
final
String
TEST_VORBIS_CODEC_PRIVATE
=
"webm/vorbis_codec_private"
;
private
static
final
int
TEST_VORBIS_INFO_SIZE
=
30
;
private
static
final
int
TEST_VORBIS_BOOKS_SIZE
=
4140
;
private
static
final
byte
[]
TEST_
ENCRYPTION_KEY_ID
=
{
0x00
,
0x01
,
0x02
,
0x03
};
private
static
final
byte
[]
TEST_
OPUS_CODEC_PRIVATE
=
new
byte
[]
{
0
,
0
};
private
static
final
UUID
WIDEVINE_UUID
=
new
UUID
(
0xEDEF8BA979D64ACE
L
,
0xA3C827DCD51D21ED
L
);
private
static
final
UUID
ZERO_UUID
=
new
UUID
(
0
,
0
);
private
static
final
byte
[]
TEST_INITIALIZATION_VECTOR
=
{
0x00
,
0x01
,
0x02
,
0x03
,
0x04
,
0x05
,
0x06
,
0x07
};
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
ID_DUMMY
=
3
;
private
static
final
String
WEBM_DOC_TYPE
=
"webm"
;
private
WebmExtractor
extractor
;
private
TestExtractorOutput
extractorOutput
;
...
...
@@ -95,68 +86,113 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
public
void
testReadInitializationSegment
()
throws
IOException
,
InterruptedException
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
null
));
assertVideoFormat
();
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
build
(
1
);
consume
(
data
);
assertVp9VideoFormat
();
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
}
public
void
testPrepareOpus
()
throws
IOException
,
InterruptedException
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_OPUS
},
null
));
assertAudioFormat
(
ID_OPUS
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addOpusTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
TEST_CODEC_DELAY
,
TEST_SEEK_PRE_ROLL
,
TEST_OPUS_CODEC_PRIVATE
)
.
build
(
1
);
consume
(
data
);
assertAudioFormat
(
MimeTypes
.
AUDIO_OPUS
);
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
}
public
void
testPrepareVorbis
()
throws
IOException
,
InterruptedException
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VORBIS
},
null
));
assertAudioFormat
(
ID_VORBIS
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVorbisTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
getVorbisCodecPrivate
())
.
build
(
1
);
consume
(
data
);
assertAudioFormat
(
MimeTypes
.
AUDIO_VORBIS
);
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
}
public
void
testPrepareTwoTracks
()
throws
IOException
,
InterruptedException
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
,
ID_OPUS
},
null
));
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addOpusTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
TEST_CODEC_DELAY
,
TEST_SEEK_PRE_ROLL
,
TEST_OPUS_CODEC_PRIVATE
)
.
build
(
1
);
consume
(
data
);
assertEquals
(
2
,
extractorOutput
.
numberOfTracks
);
assertVideoFormat
();
assertAudioFormat
(
ID
_OPUS
);
assertV
p9V
ideoFormat
();
assertAudioFormat
(
MimeTypes
.
AUDIO
_OPUS
);
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
}
public
void
testPrepareThreeTracks
()
throws
IOException
,
InterruptedException
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
,
ID_DUMMY
,
ID_OPUS
},
null
));
// Eventhough the input stream has 3 tracks, only 2 of them are supported and will be reported.
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addUnsupportedTrack
()
.
addOpusTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
TEST_CODEC_DELAY
,
TEST_SEEK_PRE_ROLL
,
TEST_OPUS_CODEC_PRIVATE
)
.
build
(
1
);
consume
(
data
);
// Even though the input stream has 3 tracks, only 2 of them are supported and will be reported.
assertEquals
(
2
,
extractorOutput
.
numberOfTracks
);
assertVideoFormat
();
assertAudioFormat
(
ID
_OPUS
);
assertV
p9V
ideoFormat
();
assertAudioFormat
(
MimeTypes
.
AUDIO
_OPUS
);
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
}
public
void
testPrepareFourTracks
()
throws
IOException
,
InterruptedException
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
,
ID_VORBIS
,
ID_VP9
,
ID_OPUS
},
null
));
// Eventhough the input stream has 4 supported tracks, only the first video and audio track will
// be reported.
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addVorbisTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
getVorbisCodecPrivate
())
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addOpusTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
TEST_CODEC_DELAY
,
TEST_SEEK_PRE_ROLL
,
TEST_OPUS_CODEC_PRIVATE
)
.
build
(
1
);
consume
(
data
);
// Even though the input stream has 4 supported tracks, only the first video and audio track
// will be reported.
assertEquals
(
2
,
extractorOutput
.
numberOfTracks
);
assertVideoFormat
();
assertAudioFormat
(
ID
_VORBIS
);
assertV
p9V
ideoFormat
();
assertAudioFormat
(
MimeTypes
.
AUDIO
_VORBIS
);
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
}
public
void
testPrepareContentEncodingEncryption
()
throws
IOException
,
InterruptedException
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
1
);
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
settings
));
assertVideoFormat
();
ContentEncodingSettings
settings
=
new
StreamBuilder
.
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
1
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
settings
)
.
build
(
1
);
consume
(
data
);
assertVp9VideoFormat
();
assertIndex
(
new
IndexPoint
(
0
,
0
,
TEST_DURATION_US
));
DrmInitData
drmInitData
=
extractorOutput
.
drmInitData
;
assertNotNull
(
drmInitData
);
...
...
@@ -165,10 +201,15 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
public
void
testPrepareThreeCuePoints
()
throws
IOException
,
InterruptedException
{
consume
(
createInitializationSegment
(
3
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
null
));
assertVideoFormat
();
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
build
(
3
);
consume
(
data
);
assertVp9VideoFormat
();
assertIndex
(
new
IndexPoint
(
0
,
0
,
10000
),
new
IndexPoint
(
10000
,
0
,
10000
),
...
...
@@ -176,8 +217,15 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
public
void
testPrepareCustomTimecodeScale
()
throws
IOException
,
InterruptedException
{
consume
(
createInitializationSegment
(
3
,
0
,
true
,
1000
,
new
int
[]
{
ID_VP9
},
null
));
assertVideoFormat
();
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
1000
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
build
(
3
);
consume
(
data
);
assertVp9VideoFormat
();
assertIndex
(
new
IndexPoint
(
0
,
0
,
10
),
new
IndexPoint
(
10
,
0
,
10
),
...
...
@@ -185,21 +233,38 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
public
void
testPrepareNoCuePoints
()
throws
IOException
,
InterruptedException
{
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
build
(
0
);
try
{
consume
(
createInitializationSegment
(
0
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
null
));
consume
(
data
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"Invalid/missing cue points"
,
exception
.
getMessage
());
}
}
public
void
testAcceptsWebmDocType
()
throws
IOException
,
InterruptedException
{
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
build
(
1
);
// No exception is thrown.
consume
(
data
);
}
public
void
testPrepareInvalidDocType
()
throws
IOException
,
InterruptedException
{
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
"webB"
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
build
(
1
);
try
{
consume
(
createInitializationSegment
(
1
,
0
,
false
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
null
));
consume
(
data
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"DocType webB not supported"
,
exception
.
getMessage
());
...
...
@@ -208,10 +273,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareInvalidContentEncodingOrder
()
throws
IOException
,
InterruptedException
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
1
,
1
,
1
,
5
,
1
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
settings
)
.
build
(
1
);
try
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
settings
));
consume
(
data
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"ContentEncodingOrder 1 not supported"
,
exception
.
getMessage
());
...
...
@@ -220,10 +288,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareInvalidContentEncodingScope
()
throws
IOException
,
InterruptedException
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
0
,
1
,
5
,
1
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
settings
)
.
build
(
1
);
try
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
settings
));
consume
(
data
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"ContentEncodingScope 0 not supported"
,
exception
.
getMessage
());
...
...
@@ -232,10 +303,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareInvalidContentEncodingType
()
throws
IOException
,
InterruptedException
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
0
,
5
,
1
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
settings
)
.
build
(
1
);
try
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
settings
));
consume
(
data
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"ContentEncodingType 0 not supported"
,
exception
.
getMessage
());
...
...
@@ -244,10 +318,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareInvalidContentEncAlgo
()
throws
IOException
,
InterruptedException
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
4
,
1
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
settings
)
.
build
(
1
);
try
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
settings
));
consume
(
data
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"ContentEncAlgo 4 not supported"
,
exception
.
getMessage
());
...
...
@@ -256,10 +333,13 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
testPrepareInvalidAESSettingsCipherMode
()
throws
IOException
,
InterruptedException
{
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
0
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
settings
)
.
build
(
1
);
try
{
consume
(
createInitializationSegment
(
1
,
0
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
settings
));
consume
(
data
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"AESSettingsCipherMode 0 not supported"
,
exception
.
getMessage
());
...
...
@@ -267,110 +347,136 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
public
void
testReadSampleKeyframe
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
false
,
false
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
null
),
mediaSegment
.
clusterBytes
);
consume
(
testInputData
);
assertVideoFormat
();
assertSample
(
mediaSegment
,
0
,
true
,
false
,
false
,
videoOutput
);
byte
[]
media
=
createFrameData
(
100
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addSimpleBlockMedia
(
1
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
media
)
.
build
(
1
);
consume
(
data
);
assertVp9VideoFormat
();
assertSample
(
media
,
0
,
true
,
false
,
false
,
videoOutput
);
}
public
void
testReadTwoTrackSamples
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegmentAudio
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
false
,
false
,
2
);
MediaSegment
mediaSegmentVideo
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
false
,
false
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegmentAudio
.
clusterBytes
.
length
+
mediaSegmentVideo
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
,
ID_OPUS
},
null
),
mediaSegmentVideo
.
clusterBytes
,
mediaSegmentAudio
.
clusterBytes
);
consume
(
testInputData
);
byte
[]
media
=
createFrameData
(
100
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addOpusTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
TEST_CODEC_DELAY
,
TEST_SEEK_PRE_ROLL
,
TEST_OPUS_CODEC_PRIVATE
)
.
addSimpleBlockMedia
(
1
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
media
)
.
addSimpleBlockMedia
(
2
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
media
)
.
build
(
1
);
consume
(
data
);
assertEquals
(
2
,
extractorOutput
.
numberOfTracks
);
assertVideoFormat
();
assertAudioFormat
(
ID
_OPUS
);
assertSample
(
media
SegmentVideo
,
0
,
true
,
false
,
false
,
videoOutput
);
assertSample
(
media
SegmentAudio
,
0
,
true
,
false
,
false
,
audioOutput
);
assertV
p9V
ideoFormat
();
assertAudioFormat
(
MimeTypes
.
AUDIO
_OPUS
);
assertSample
(
media
,
0
,
true
,
false
,
false
,
videoOutput
);
assertSample
(
media
,
0
,
true
,
false
,
false
,
audioOutput
);
}
public
void
testReadTwoTrackSamplesWithSkippedTrack
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegmentAudio
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
false
,
false
,
2
);
MediaSegment
mediaSegmentVideo
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
false
,
false
,
1
);
MediaSegment
mediaSegmentDummy
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
false
,
false
,
17
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegmentAudio
.
clusterBytes
.
length
+
mediaSegmentVideo
.
clusterBytes
.
length
+
mediaSegmentDummy
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_DUMMY
,
ID_VP9
,
ID_OPUS
},
null
),
mediaSegmentVideo
.
clusterBytes
,
mediaSegmentDummy
.
clusterBytes
,
mediaSegmentAudio
.
clusterBytes
);
consume
(
testInputData
);
byte
[]
media
=
createFrameData
(
100
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addUnsupportedTrack
()
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addOpusTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
TEST_CODEC_DELAY
,
TEST_SEEK_PRE_ROLL
,
TEST_OPUS_CODEC_PRIVATE
)
.
addSimpleBlockMedia
(
1
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
media
)
.
addSimpleBlockMedia
(
2
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
media
)
.
addSimpleBlockMedia
(
17
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
media
)
.
build
(
1
);
consume
(
data
);
assertEquals
(
2
,
extractorOutput
.
numberOfTracks
);
assertVideoFormat
();
assertAudioFormat
(
ID
_OPUS
);
assertSample
(
media
SegmentVideo
,
0
,
true
,
false
,
false
,
videoOutput
);
assertSample
(
media
SegmentAudio
,
0
,
true
,
false
,
false
,
audioOutput
);
assertV
p9V
ideoFormat
();
assertAudioFormat
(
MimeTypes
.
AUDIO
_OPUS
);
assertSample
(
media
,
0
,
true
,
false
,
false
,
videoOutput
);
assertSample
(
media
,
0
,
true
,
false
,
false
,
audioOutput
);
}
public
void
testReadBlock
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
false
,
false
,
false
,
2
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_OPUS
},
null
),
mediaSegment
.
clusterBytes
);
consume
(
testInputData
);
assertAudioFormat
(
ID_OPUS
);
assertSample
(
mediaSegment
,
0
,
true
,
false
,
false
,
audioOutput
);
byte
[]
media
=
createFrameData
(
100
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addOpusTrack
(
TEST_CHANNEL_COUNT
,
TEST_SAMPLE_RATE
,
TEST_CODEC_DELAY
,
TEST_SEEK_PRE_ROLL
,
TEST_OPUS_CODEC_PRIVATE
)
.
addBlockMedia
(
2
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
media
)
.
build
(
1
);
consume
(
data
);
assertAudioFormat
(
MimeTypes
.
AUDIO_OPUS
);
assertSample
(
media
,
0
,
true
,
false
,
false
,
audioOutput
);
}
public
void
testReadBlockNonKeyframe
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
false
,
false
,
false
,
false
,
false
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
null
),
mediaSegment
.
clusterBytes
);
consume
(
testInputData
);
assertVideoFormat
();
assertSample
(
mediaSegment
,
0
,
false
,
false
,
false
,
videoOutput
);
byte
[]
media
=
createFrameData
(
100
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addBlockMedia
(
1
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
false
/* keyframe */
,
false
/* invisible */
,
media
)
.
build
(
1
);
consume
(
data
);
assertVp9VideoFormat
();
assertSample
(
media
,
0
,
false
,
false
,
false
,
videoOutput
);
}
public
void
testReadEncryptedFrame
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
true
,
true
,
1
);
byte
[]
media
=
createFrameData
(
100
);
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
settings
),
mediaSegment
.
clusterBytes
);
consume
(
testInputData
);
assertVideoFormat
();
assertSample
(
mediaSegment
,
0
,
true
,
false
,
true
,
videoOutput
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
settings
)
.
addSimpleBlockEncryptedMedia
(
1
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
true
/* validSignalByte */
,
media
)
.
build
(
1
);
consume
(
data
);
assertVp9VideoFormat
();
assertSample
(
media
,
0
,
true
,
false
,
true
,
videoOutput
);
}
public
void
testReadEncryptedFrameWithInvalidSignalByte
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
0
,
0
,
true
,
false
,
true
,
true
,
false
,
1
);
byte
[]
media
=
createFrameData
(
100
);
ContentEncodingSettings
settings
=
new
ContentEncodingSettings
(
0
,
1
,
1
,
5
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
settings
),
mediaSegment
.
clusterBytes
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
settings
)
.
addSimpleBlockEncryptedMedia
(
1
/* trackNumber */
,
0
/* clusterTimecode */
,
0
/* blockTimecode */
,
true
/* keyframe */
,
false
/* invisible */
,
false
/* validSignalByte */
,
media
)
.
build
(
1
);
try
{
consume
(
testInputD
ata
);
consume
(
d
ata
);
fail
();
}
catch
(
ParserException
exception
)
{
assertEquals
(
"Extension bit is set in signal byte"
,
exception
.
getMessage
());
...
...
@@ -378,41 +484,51 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
public
void
testReadSampleInvisible
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
12
,
13
,
false
,
true
,
true
,
false
,
false
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
null
),
mediaSegment
.
clusterBytes
);
consume
(
testInputData
);
assertVideoFormat
();
assertSample
(
mediaSegment
,
25000
,
false
,
true
,
false
,
videoOutput
);
byte
[]
media
=
createFrameData
(
100
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addSimpleBlockMedia
(
1
/* trackNumber */
,
12
/* clusterTimecode */
,
13
/* blockTimecode */
,
false
/* keyframe */
,
true
/* invisible */
,
media
)
.
build
(
1
);
consume
(
data
);
assertVp9VideoFormat
();
assertSample
(
media
,
25000
,
false
,
true
,
false
,
videoOutput
);
}
public
void
testReadSampleCustomTimescale
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
12
,
13
,
false
,
false
,
true
,
false
,
false
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
1000
,
new
int
[]
{
ID_VP9
},
null
),
mediaSegment
.
clusterBytes
);
consume
(
testInputData
);
assertVideoFormat
();
assertSample
(
mediaSegment
,
25
,
false
,
false
,
false
,
videoOutput
);
byte
[]
media
=
createFrameData
(
100
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
1000
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addSimpleBlockMedia
(
1
/* trackNumber */
,
12
/* clusterTimecode */
,
13
/* blockTimecode */
,
false
/* keyframe */
,
false
/* invisible */
,
media
)
.
build
(
1
);
consume
(
data
);
assertVp9VideoFormat
();
assertSample
(
media
,
25
,
false
,
false
,
false
,
videoOutput
);
}
public
void
testReadSampleNegativeSimpleBlockTimecode
()
throws
IOException
,
InterruptedException
{
MediaSegment
mediaSegment
=
createMediaSegment
(
100
,
13
,
-
12
,
true
,
true
,
true
,
false
,
false
,
1
);
byte
[]
testInputData
=
joinByteArrays
(
createInitializationSegment
(
1
,
mediaSegment
.
clusterBytes
.
length
,
true
,
DEFAULT_TIMECODE_SCALE
,
new
int
[]
{
ID_VP9
},
null
),
mediaSegment
.
clusterBytes
);
consume
(
testInputData
);
assertVideoFormat
();
assertSample
(
mediaSegment
,
1000
,
true
,
true
,
false
,
videoOutput
);
byte
[]
media
=
createFrameData
(
100
);
byte
[]
data
=
new
StreamBuilder
()
.
setHeader
(
WEBM_DOC_TYPE
)
.
setInfo
(
DEFAULT_TIMECODE_SCALE
,
TEST_DURATION_US
)
.
addVp9Track
(
TEST_WIDTH
,
TEST_HEIGHT
,
null
)
.
addSimpleBlockMedia
(
1
/* trackNumber */
,
13
/* clusterTimecode */
,
-
12
/* blockTimecode */
,
true
/* keyframe */
,
true
/* invisible */
,
media
)
.
build
(
1
);
consume
(
data
);
assertVp9VideoFormat
();
assertSample
(
media
,
1000
,
true
,
true
,
false
,
videoOutput
);
}
private
void
consume
(
byte
[]
data
)
throws
IOException
,
InterruptedException
{
...
...
@@ -431,25 +547,25 @@ public class WebmExtractorTest extends InstrumentationTestCase {
return
input
;
}
private
void
assertVideoFormat
()
{
private
void
assertV
p9V
ideoFormat
()
{
MediaFormat
format
=
videoOutput
.
format
;
assertEquals
(
TEST_WIDTH
,
format
.
width
);
assertEquals
(
TEST_HEIGHT
,
format
.
height
);
assertEquals
(
MimeTypes
.
VIDEO_VP9
,
format
.
mimeType
);
}
private
void
assertAudioFormat
(
int
codecId
)
{
private
void
assertAudioFormat
(
String
expectedMimeType
)
{
MediaFormat
format
=
audioOutput
.
format
;
assertEquals
(
TEST_CHANNEL_COUNT
,
format
.
channelCount
);
assertEquals
(
TEST_SAMPLE_RATE
,
format
.
sampleRate
);
if
(
codecId
==
ID_OPUS
)
{
assertEquals
(
MimeTypes
.
AUDIO_OPUS
,
format
.
mimeType
);
assertEquals
(
expectedMimeType
,
format
.
mimeType
);
if
(
MimeTypes
.
AUDIO_OPUS
.
equals
(
expectedMimeType
))
{
assertEquals
(
3
,
format
.
initializationData
.
size
());
assertEquals
(
TEST_OPUS_CODEC_PRIVATE_SIZE
,
format
.
initializationData
.
get
(
0
).
length
);
android
.
test
.
MoreAsserts
.
assertEquals
(
TEST_OPUS_CODEC_PRIVATE
,
format
.
initializationData
.
get
(
0
));
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
);
}
else
if
(
MimeTypes
.
AUDIO_VORBIS
.
equals
(
expectedMimeType
))
{
assertEquals
(
2
,
format
.
initializationData
.
size
());
assertEquals
(
TEST_VORBIS_INFO_SIZE
,
format
.
initializationData
.
get
(
0
).
length
);
assertEquals
(
TEST_VORBIS_BOOKS_SIZE
,
format
.
initializationData
.
get
(
1
).
length
);
...
...
@@ -467,237 +583,21 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
}
private
void
assertSample
(
MediaSegment
mediaSegment
,
int
timeUs
,
boolean
keyframe
,
private
void
assertSample
(
byte
[]
expectedMedia
,
int
timeUs
,
boolean
keyframe
,
boolean
invisible
,
boolean
encrypted
,
TestTrackOutput
output
)
{
byte
[]
expectedOutput
=
mediaSegment
.
data
;
if
(
encrypted
)
{
expectedOutput
=
joinByteArrays
(
new
byte
[]
{(
byte
)
TEST_INITIALIZATION_VECTOR
.
length
},
TEST_INITIALIZATION_VECTOR
,
expectedOutput
);
expectedMedia
=
StreamBuilder
.
joinByteArrays
(
new
byte
[]
{(
byte
)
StreamBuilder
.
TEST_INITIALIZATION_VECTOR
.
length
},
StreamBuilder
.
TEST_INITIALIZATION_VECTOR
,
expectedMedia
);
}
a
ssertTrue
(
Arrays
.
equals
(
expectedOutput
,
output
.
sampleData
)
);
a
ndroid
.
test
.
MoreAsserts
.
assertEquals
(
expectedMedia
,
output
.
sampleData
);
assertEquals
(
timeUs
,
output
.
sampleTimeUs
);
assertEquals
(
keyframe
,
(
output
.
sampleFlags
&
C
.
SAMPLE_FLAG_SYNC
)
!=
0
);
assertEquals
(
invisible
,
(
output
.
sampleFlags
&
C
.
SAMPLE_FLAG_DECODE_ONLY
)
!=
0
);
assertEquals
(
encrypted
,
(
output
.
sampleFlags
&
C
.
SAMPLE_FLAG_ENCRYPTED
)
!=
0
);
}
private
byte
[]
createInitializationSegment
(
int
cuePoints
,
int
mediaSegmentSize
,
boolean
docTypeIsWebm
,
int
timecodeScale
,
int
[]
codecIds
,
ContentEncodingSettings
contentEncodingSettings
)
{
byte
[]
tracksElement
=
createTracksElement
(
codecIds
,
contentEncodingSettings
);
byte
[]
infoElement
=
createInfoElement
(
timecodeScale
);
byte
[]
cuesElement
=
createCuesElement
(
CUE_POINT_ELEMENT_BYTE_SIZE
*
cuePoints
);
int
initalizationSegmentSize
=
infoElement
.
length
+
tracksElement
.
length
+
cuesElement
.
length
+
CUE_POINT_ELEMENT_BYTE_SIZE
*
cuePoints
;
byte
[]
segmentElement
=
createSegmentElement
(
initalizationSegmentSize
+
mediaSegmentSize
);
byte
[]
bytes
=
joinByteArrays
(
createEbmlElement
(
1
,
docTypeIsWebm
,
2
),
segmentElement
,
infoElement
,
tracksElement
,
cuesElement
);
for
(
int
i
=
0
;
i
<
cuePoints
;
i
++)
{
bytes
=
joinByteArrays
(
bytes
,
createCuePointElement
(
10
*
i
,
initalizationSegmentSize
));
}
return
bytes
;
}
private
byte
[]
createTracksElement
(
int
[]
codecIds
,
ContentEncodingSettings
contentEncodingSettings
)
{
byte
[]
trackBytes
=
new
byte
[
0
];
for
(
int
codecId
:
codecIds
)
{
switch
(
codecId
)
{
case
ID_VP9:
trackBytes
=
joinByteArrays
(
trackBytes
,
createVideoTrackEntry
(
true
,
TEST_WIDTH
,
TEST_HEIGHT
,
contentEncodingSettings
));
break
;
case
ID_OPUS:
trackBytes
=
joinByteArrays
(
trackBytes
,
createOpusAudioTrackEntry
(
TEST_CHANNEL_COUNT
));
break
;
case
ID_VORBIS:
trackBytes
=
joinByteArrays
(
trackBytes
,
createVorbisAudioTrackEntry
(
TEST_CHANNEL_COUNT
));
break
;
case
ID_DUMMY:
trackBytes
=
joinByteArrays
(
trackBytes
,
createUnsupportedTrackEntry
());
break
;
}
}
byte
[]
tracksSize
=
getIntegerBytes
(
trackBytes
.
length
);
byte
[]
tracksHeader
=
createByteArray
(
0x16
,
0x54
,
0xAE
,
0x6B
,
// Tracks
0x01
,
0x00
,
0x00
,
0x00
,
tracksSize
[
0
],
tracksSize
[
1
],
tracksSize
[
2
],
tracksSize
[
3
]);
return
joinByteArrays
(
tracksHeader
,
trackBytes
);
}
private
static
MediaSegment
createMediaSegment
(
int
dataLength
,
int
clusterTimecode
,
int
blockTimecode
,
boolean
keyframe
,
boolean
invisible
,
boolean
simple
,
boolean
encrypted
,
boolean
validSignalByte
,
int
trackNumber
)
{
byte
[]
data
=
createFrameData
(
dataLength
);
byte
[]
blockBytes
;
if
(
simple
)
{
blockBytes
=
createSimpleBlockElement
(
data
.
length
,
blockTimecode
,
keyframe
,
invisible
,
true
,
encrypted
,
validSignalByte
,
trackNumber
);
}
else
{
blockBytes
=
createBlockElement
(
data
.
length
,
blockTimecode
,
keyframe
,
invisible
,
true
,
trackNumber
);
}
byte
[]
clusterBytes
=
createClusterElement
(
blockBytes
.
length
+
data
.
length
,
clusterTimecode
);
return
new
MediaSegment
(
joinByteArrays
(
clusterBytes
,
blockBytes
,
data
),
data
);
}
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
[]
createVideoTrackEntry
(
boolean
codecIsVp9
,
int
pixelWidth
,
int
pixelHeight
,
ContentEncodingSettings
contentEncodingSettings
)
{
byte
[]
widthBytes
=
getIntegerBytes
(
pixelWidth
);
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
(
0xAE
,
// TrackEntry
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x45
,
// size=69
0x86
,
// CodecID
0x85
,
0x56
,
0x5F
,
0x56
,
0x50
,
codecIsVp9
?
0x39
:
0x30
,
// size=5 value=V_VP9/0
0xD7
,
// TrackNumber
0x81
,
0x01
,
// size=1 value=1
0x83
,
// TrackType
0x81
,
0x01
,
// size=1 value=1
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
(
0xAE
,
// TrackEntry
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x1E
,
// size=30
0x86
,
// CodecID
0x85
,
0x56
,
0x5F
,
0x56
,
0x50
,
codecIsVp9
?
0x39
:
0x30
,
// size=5 value=V_VP9/0
0xD7
,
// TrackNumber
0x81
,
0x01
,
// size=1 value=1
0x83
,
// TrackType
0x81
,
0x01
,
// size=1 value=1
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
[]
createOpusAudioTrackEntry
(
int
channelCount
)
{
byte
[]
channelCountBytes
=
getIntegerBytes
(
channelCount
);
return
createByteArray
(
0xAE
,
// TrackEntry
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x36
,
// size=54
0x86
,
// CodecID
0x86
,
0x41
,
0x5F
,
0x4F
,
0x50
,
0x55
,
0x53
,
// size=6 value=A_OPUS
0xD7
,
// TrackNumber
0x81
,
0x02
,
// size=1 value=2
0x83
,
// TrackType
0x81
,
0x02
,
// size=1 value=2
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
[]
createVorbisAudioTrackEntry
(
int
channelCount
)
{
byte
[]
channelCountBytes
=
getIntegerBytes
(
channelCount
);
byte
[]
tracksElement
=
createByteArray
(
0xAE
,
// TrackEntry
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x10
,
0x99
,
// size=4249 (42+4207)
0x86
,
// CodecID
0x88
,
0x41
,
0x5f
,
0x56
,
0x4f
,
0x52
,
0x42
,
0x49
,
0x53
,
// size=8 value=A_VORBIS
0xD7
,
// TrackNumber
0x81
,
0x02
,
// size=1 value=2
0x83
,
// TrackType
0x81
,
0x02
,
// size=1 value=2
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
private
byte
[]
getVorbisCodecPrivate
()
{
byte
[]
codecPrivate
=
new
byte
[
4207
];
try
{
getInstrumentation
().
getContext
().
getResources
().
getAssets
().
open
(
TEST_VORBIS_CODEC_PRIVATE
)
...
...
@@ -705,99 +605,7 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
catch
(
IOException
e
)
{
fail
();
// should never happen
}
return
joinByteArrays
(
tracksElement
,
codecPrivate
);
}
private
static
byte
[]
createUnsupportedTrackEntry
()
{
return
createByteArray
(
0xAE
,
// TrackEntry
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x20
,
// size=32
0x86
,
// CodecID
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x11
,
// size =17
0x44
,
0x5f
,
0x57
,
0x45
,
0x42
,
0x56
,
0x54
,
0x54
,
0x2f
,
0x6d
,
0x65
,
0x74
,
0x61
,
0x64
,
0x61
,
0x74
,
0x61
,
// value=D_WEBVTT/metadata
0xD7
,
// TrackNumber
0x81
,
0x03
,
// size=1 value=3
0x83
,
// TrackType
0x81
,
0x11
);
// size=1 value=11
}
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
]);
}
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
,
boolean
encrypted
,
boolean
validSignalByte
,
int
trackNumber
)
{
byte
[]
sizeBytes
=
getIntegerBytes
(
size
+
5
+
(
encrypted
?
9
:
0
));
byte
[]
timeBytes
=
getIntegerBytes
(
timecode
);
byte
[]
trackNumberBytes
=
getIntegerBytes
(
trackNumber
);
byte
flags
=
(
byte
)
((
keyframe
?
0x80
:
0x00
)
|
(
invisible
?
0x08
:
0x00
)
|
(
noLacing
?
0x00
:
0x06
));
byte
[]
simpleBlock
=
createByteArray
(
0xA3
,
// SimpleBlock
0x01
,
0x00
,
0x00
,
0x00
,
sizeBytes
[
0
],
sizeBytes
[
1
],
sizeBytes
[
2
],
sizeBytes
[
3
],
0x40
,
trackNumberBytes
[
3
],
// Track number size=2
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
(
int
size
,
int
timecode
,
boolean
keyframe
,
boolean
invisible
,
boolean
noLacing
,
int
trackNumber
)
{
int
blockSize
=
size
+
5
;
byte
[]
blockSizeBytes
=
getIntegerBytes
(
blockSize
);
byte
[]
timeBytes
=
getIntegerBytes
(
timecode
);
byte
[]
trackNumberBytes
=
getIntegerBytes
(
trackNumber
);
// Size of blockgroup = id + size + size of reference block + length of data.
int
blockGroupElementSize
=
1
+
8
+
(
keyframe
?
0
:
3
)
+
blockSize
;
byte
[]
sizeBytes
=
getIntegerBytes
(
blockGroupElementSize
);
byte
flags
=
(
byte
)
((
invisible
?
0x08
:
0x00
)
|
(
noLacing
?
0x00
:
0x06
));
byte
[]
blockGroupHeader
=
createByteArray
(
0xA0
,
// BlockGroup
0x01
,
0x00
,
0x00
,
0x00
,
sizeBytes
[
0
],
sizeBytes
[
1
],
sizeBytes
[
2
],
sizeBytes
[
3
]);
byte
[]
referenceBlock
=
keyframe
?
new
byte
[
0
]
:
createByteArray
(
0xFB
,
// ReferenceBlock
0x81
,
0x00
);
// size=1 value=0
byte
[]
blockData
=
createByteArray
(
0xA1
,
// Block
0x01
,
0x00
,
0x00
,
0x00
,
blockSizeBytes
[
0
],
blockSizeBytes
[
1
],
blockSizeBytes
[
2
],
blockSizeBytes
[
3
],
0x40
,
trackNumberBytes
[
3
],
// Track number size=2
timeBytes
[
2
],
timeBytes
[
3
],
flags
);
// Timecode and flags
return
joinByteArrays
(
blockGroupHeader
,
referenceBlock
,
blockData
);
return
codecPrivate
;
}
private
static
byte
[]
createFrameData
(
int
size
)
{
...
...
@@ -808,35 +616,6 @@ public class WebmExtractorTest extends InstrumentationTestCase {
return
data
;
}
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
[]
data
;
private
MediaSegment
(
byte
[]
clusterBytes
,
byte
[]
data
)
{
this
.
clusterBytes
=
clusterBytes
;
this
.
data
=
data
;
}
}
/** Used by {@link #assertIndex(IndexPoint...)} to validate index elements. */
private
static
final
class
IndexPoint
{
...
...
@@ -852,26 +631,6 @@ public class WebmExtractorTest extends InstrumentationTestCase {
}
/** Used by {@link #createVideoTrackEntry} 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
;
}
}
/** Implements {@link ExtractorOutput} for test purposes. */
public
class
TestExtractorOutput
implements
ExtractorOutput
{
...
...
@@ -922,7 +681,8 @@ public class WebmExtractorTest extends InstrumentationTestCase {
InterruptedException
{
byte
[]
newData
=
new
byte
[
length
];
input
.
readFully
(
newData
,
0
,
length
);
sampleData
=
sampleData
==
null
?
newData
:
joinByteArrays
(
sampleData
,
newData
);
sampleData
=
sampleData
==
null
?
newData
:
StreamBuilder
.
joinByteArrays
(
sampleData
,
newData
);
return
length
;
}
...
...
@@ -930,7 +690,8 @@ public class WebmExtractorTest extends InstrumentationTestCase {
public
void
sampleData
(
ParsableByteArray
data
,
int
length
)
{
byte
[]
newData
=
new
byte
[
length
];
data
.
readBytes
(
newData
,
0
,
length
);
sampleData
=
sampleData
==
null
?
newData
:
joinByteArrays
(
sampleData
,
newData
);
sampleData
=
sampleData
==
null
?
newData
:
StreamBuilder
.
joinByteArrays
(
sampleData
,
newData
);
}
@Override
...
...
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