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
005e98fc
authored
Aug 11, 2014
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Simplify parsing of encryption data + support SENC boxes.
Issue: #4
parent
8ec88402
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
79 additions
and
80 deletions
library/src/main/java/com/google/android/exoplayer/parser/mp4/Atom.java
library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java
library/src/main/java/com/google/android/exoplayer/parser/mp4/TrackFragment.java
library/src/main/java/com/google/android/exoplayer/parser/mp4/Atom.java
View file @
005e98fc
...
...
@@ -54,6 +54,7 @@ import java.util.List;
public
static
final
int
TYPE_frma
=
0x66726D61
;
public
static
final
int
TYPE_saiz
=
0x7361697A
;
public
static
final
int
TYPE_uuid
=
0x75756964
;
public
static
final
int
TYPE_senc
=
0x73656E63
;
public
final
int
type
;
...
...
library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java
View file @
005e98fc
...
...
@@ -93,7 +93,7 @@ public final class FragmentedMp4Extractor {
// Parser states
private
static
final
int
STATE_READING_ATOM_HEADER
=
0
;
private
static
final
int
STATE_READING_ATOM_PAYLOAD
=
1
;
private
static
final
int
STATE_READING_
CENC_AUXILIARY
_DATA
=
2
;
private
static
final
int
STATE_READING_
ENCRYPTION
_DATA
=
2
;
private
static
final
int
STATE_READING_SAMPLE
=
3
;
// Atom data offsets
...
...
@@ -130,6 +130,7 @@ public final class FragmentedMp4Extractor {
parsedAtoms
.
add
(
Atom
.
TYPE_pssh
);
parsedAtoms
.
add
(
Atom
.
TYPE_saiz
);
parsedAtoms
.
add
(
Atom
.
TYPE_uuid
);
parsedAtoms
.
add
(
Atom
.
TYPE_senc
);
PARSED_ATOMS
=
Collections
.
unmodifiableSet
(
parsedAtoms
);
}
...
...
@@ -162,8 +163,6 @@ public final class FragmentedMp4Extractor {
private
int
atomType
;
private
int
atomSize
;
private
ParsableByteArray
atomData
;
private
ParsableByteArray
cencAuxiliaryData
;
private
int
cencAuxiliaryBytesRead
;
private
int
pendingSeekTimeMs
;
private
int
sampleIndex
;
...
...
@@ -273,8 +272,8 @@ public final class FragmentedMp4Extractor {
case
STATE_READING_ATOM_PAYLOAD:
results
|=
readAtomPayload
(
inputStream
);
break
;
case
STATE_READING_
CENC_AUXILIARY
_DATA:
results
|=
read
CencAuxiliary
Data
(
inputStream
);
case
STATE_READING_
ENCRYPTION
_DATA:
results
|=
read
Encryption
Data
(
inputStream
);
break
;
default
:
results
|=
readOrSkipSample
(
inputStream
,
out
);
...
...
@@ -330,9 +329,6 @@ public final class FragmentedMp4Extractor {
rootAtomBytesRead
=
0
;
}
break
;
case
STATE_READING_CENC_AUXILIARY_DATA:
cencAuxiliaryBytesRead
=
0
;
break
;
}
parserState
=
state
;
}
...
...
@@ -354,12 +350,9 @@ public final class FragmentedMp4Extractor {
atomType
=
atomHeader
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_mdat
)
{
int
cencAuxSize
=
fragmentRun
.
auxiliarySampleInfoTotalSize
;
if
(
cencAuxSize
>
0
)
{
cencAuxiliaryData
=
new
ParsableByteArray
(
cencAuxSize
);
enterState
(
STATE_READING_CENC_AUXILIARY_DATA
);
if
(
fragmentRun
.
sampleEncryptionDataNeedsFill
)
{
enterState
(
STATE_READING_ENCRYPTION_DATA
);
}
else
{
cencAuxiliaryData
=
null
;
enterState
(
STATE_READING_SAMPLE
);
}
return
0
;
...
...
@@ -631,7 +624,7 @@ public final class FragmentedMp4Extractor {
if
(
childAtomType
==
Atom
.
TYPE_esds
)
{
initializationData
=
parseEsdsFromParent
(
parent
,
childStartPosition
);
// TODO: Do we really need to do this? See [redacted]
// Update sampleRate and
sampleRate
from the AudioSpecificConfig initialization data.
// Update sampleRate and
channelCount
from the AudioSpecificConfig initialization data.
Pair
<
Integer
,
Integer
>
audioSpecificConfig
=
CodecSpecificDataUtil
.
parseAudioSpecificConfig
(
initializationData
);
sampleRate
=
audioSpecificConfig
.
first
;
...
...
@@ -752,7 +745,7 @@ public final class FragmentedMp4Extractor {
parent
.
skip
(
13
);
// Start of AudioSpecificConfig (defined in 14496-3)
parent
.
skip
(
1
);
// AudioSpecificConfig tag
parent
.
skip
(
1
);
// AudioSpecificConfig tag
varIntByte
=
parent
.
readUnsignedByte
();
int
varInt
=
varIntByte
&
0x7F
;
while
(
varIntByte
>
127
)
{
...
...
@@ -789,26 +782,38 @@ public final class FragmentedMp4Extractor {
*/
private
static
void
parseTraf
(
Track
track
,
DefaultSampleValues
extendsDefaults
,
ContainerAtom
traf
,
TrackFragment
out
,
int
workaroundFlags
)
{
LeafAtom
saiz
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_saiz
);
if
(
saiz
!=
null
)
{
parseSaiz
(
saiz
.
getData
(),
out
);
}
LeafAtom
tfdtAtom
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfdt
);
long
decodeTime
=
tfdtAtom
==
null
?
0
:
parseTfdt
(
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfdt
).
getData
());
LeafAtom
tfhd
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfhd
);
DefaultSampleValues
fragmentHeader
=
parseTfhd
(
extendsDefaults
,
tfhd
.
getData
());
out
.
setSampleDescriptionIndex
(
fragmentHeader
.
sampleDescriptionIndex
);
LeafAtom
trun
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_trun
);
parseTrun
(
track
,
fragmentHeader
,
decodeTime
,
workaroundFlags
,
trun
.
getData
(),
out
);
TrackEncryptionBox
trackEncryptionBox
=
track
.
sampleDescriptionEncryptionBoxes
[
fragmentHeader
.
sampleDescriptionIndex
];
LeafAtom
saiz
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_saiz
);
if
(
saiz
!=
null
)
{
parseSaiz
(
trackEncryptionBox
,
saiz
.
getData
(),
out
);
}
LeafAtom
senc
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_senc
);
if
(
senc
!=
null
)
{
parseSenc
(
senc
.
getData
(),
out
);
}
LeafAtom
uuid
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_uuid
);
if
(
uuid
!=
null
)
{
parseUuid
(
uuid
.
getData
(),
out
);
}
}
private
static
void
parseSaiz
(
ParsableByteArray
saiz
,
TrackFragment
out
)
{
private
static
void
parseSaiz
(
TrackEncryptionBox
encryptionBox
,
ParsableByteArray
saiz
,
TrackFragment
out
)
{
int
vectorSize
=
encryptionBox
.
initializationVectorSize
;
saiz
.
setPosition
(
ATOM_HEADER_SIZE
);
int
fullAtom
=
saiz
.
readInt
();
int
flags
=
parseFullAtomFlags
(
fullAtom
);
...
...
@@ -818,19 +823,20 @@ public final class FragmentedMp4Extractor {
int
defaultSampleInfoSize
=
saiz
.
readUnsignedByte
();
int
sampleCount
=
saiz
.
readUnsignedIntToInt
();
int
totalSize
=
0
;
int
[]
sampleInfoSizes
=
new
int
[
sampleCount
];
boolean
[]
sampleHasSubsampleEncryptionTable
=
new
boolean
[
sampleCount
];
if
(
defaultSampleInfoSize
==
0
)
{
for
(
int
i
=
0
;
i
<
sampleCount
;
i
++)
{
sampleInfoSizes
[
i
]
=
saiz
.
readUnsignedByte
();
totalSize
+=
sampleInfoSizes
[
i
];
int
sampleInfoSize
=
saiz
.
readUnsignedByte
();
totalSize
+=
sampleInfoSize
;
sampleHasSubsampleEncryptionTable
[
i
]
=
sampleInfoSize
>
vectorSize
;
}
}
else
{
for
(
int
i
=
0
;
i
<
sampleCount
;
i
++)
{
sampleInfoSizes
[
i
]
=
defaultSampleInfoSize
;
totalSize
+=
defaultSampleInfoSize
;
}
boolean
subsampleEncryption
=
defaultSampleInfoSize
>
vectorSize
;
totalSize
+=
defaultSampleInfoSize
*
sampleCount
;
Arrays
.
fill
(
sampleHasSubsampleEncryptionTable
,
subsampleEncryption
);
}
out
.
setAuxiliarySampleInfoTables
(
totalSize
,
sampleInfoSizes
);
out
.
setSampleEncryptionData
(
sampleHasSubsampleEncryptionTable
,
new
ParsableByteArray
(
totalSize
),
true
);
}
/**
...
...
@@ -942,13 +948,8 @@ public final class FragmentedMp4Extractor {
}
sampleDecodingTimeTable
[
i
]
=
(
int
)
((
cumulativeTime
*
1000
)
/
timescale
);
sampleSizeTable
[
i
]
=
sampleSize
;
boolean
isSync
=
((
sampleFlags
>>
16
)
&
0x1
)
==
0
;
if
(
workaroundEveryVideoFrameIsSyncFrame
&&
i
!=
0
)
{
isSync
=
false
;
}
if
(
isSync
)
{
sampleIsSyncFrameTable
[
i
]
=
true
;
}
sampleIsSyncFrameTable
[
i
]
=
((
sampleFlags
>>
16
)
&
0x1
)
==
0
&&
(!
workaroundEveryVideoFrameIsSyncFrame
||
i
==
0
);
cumulativeTime
+=
sampleDuration
;
}
...
...
@@ -966,9 +967,19 @@ public final class FragmentedMp4Extractor {
return
;
}
// See "Portable encoding of audio-video objects: The Protected Interoperable File Format
// (PIFF), John A. Bocharov et al, Section 5.3.2.1."
int
fullAtom
=
uuid
.
readInt
();
// Except for the extended type, this box is identical to a SENC box. See "Portable encoding of
// audio-video objects: The Protected Interoperable File Format (PIFF), John A. Bocharov et al,
// Section 5.3.2.1."
parseSenc
(
uuid
,
16
,
out
);
}
private
static
void
parseSenc
(
ParsableByteArray
senc
,
TrackFragment
out
)
{
parseSenc
(
senc
,
0
,
out
);
}
private
static
void
parseSenc
(
ParsableByteArray
senc
,
int
offset
,
TrackFragment
out
)
{
senc
.
setPosition
(
ATOM_HEADER_SIZE
+
offset
);
int
fullAtom
=
senc
.
readInt
();
int
flags
=
parseFullAtomFlags
(
fullAtom
);
if
((
flags
&
0x01
/* override_track_encryption_box_parameters */
)
!=
0
)
{
...
...
@@ -977,15 +988,19 @@ public final class FragmentedMp4Extractor {
}
boolean
subsampleEncryption
=
(
flags
&
0x02
/* use_subsample_encryption */
)
!=
0
;
int
numberOfEntries
=
uuid
.
readUnsignedIntToInt
();
if
(
numberOfEntries
!=
out
.
length
)
{
throw
new
IllegalStateException
(
"Length mismatch: "
+
numberOfEntries
+
", "
+
out
.
length
);
int
sampleCount
=
senc
.
readUnsignedIntToInt
();
if
(
sampleCount
!=
out
.
length
)
{
throw
new
IllegalStateException
(
"Length mismatch: "
+
sampleCount
+
", "
+
out
.
length
);
}
int
sampleEncryptionDataLength
=
uuid
.
length
()
-
uuid
.
getPosition
();
boolean
[]
sampleHasSubsampleEncryptionTable
=
new
boolean
[
sampleCount
];
Arrays
.
fill
(
sampleHasSubsampleEncryptionTable
,
subsampleEncryption
);
int
sampleEncryptionDataLength
=
senc
.
length
()
-
senc
.
getPosition
();
ParsableByteArray
sampleEncryptionData
=
new
ParsableByteArray
(
sampleEncryptionDataLength
);
uuid
.
readBytes
(
sampleEncryptionData
.
getData
(),
0
,
sampleEncryptionData
.
length
());
out
.
setSmoothStreamingSampleEncryptionData
(
sampleEncryptionData
,
subsampleEncryption
);
senc
.
readBytes
(
sampleEncryptionData
.
getData
(),
0
,
sampleEncryptionDataLength
);
out
.
setSampleEncryptionData
(
sampleHasSubsampleEncryptionTable
,
sampleEncryptionData
,
false
);
}
/**
...
...
@@ -1044,17 +1059,14 @@ public final class FragmentedMp4Extractor {
return
new
SegmentIndex
(
atom
.
length
(),
sizes
,
offsets
,
durationsUs
,
timesUs
);
}
private
int
readCencAuxiliaryData
(
NonBlockingInputStream
inputStream
)
{
int
length
=
cencAuxiliaryData
.
length
();
int
bytesRead
=
inputStream
.
read
(
cencAuxiliaryData
.
getData
(),
cencAuxiliaryBytesRead
,
length
-
cencAuxiliaryBytesRead
);
if
(
bytesRead
==
-
1
)
{
return
RESULT_END_OF_STREAM
;
}
cencAuxiliaryBytesRead
+=
bytesRead
;
if
(
cencAuxiliaryBytesRead
<
length
)
{
private
int
readEncryptionData
(
NonBlockingInputStream
inputStream
)
{
ParsableByteArray
sampleEncryptionData
=
fragmentRun
.
sampleEncryptionData
;
int
sampleEncryptionDataLength
=
sampleEncryptionData
.
length
();
if
(
inputStream
.
getAvailableByteCount
()
<
sampleEncryptionDataLength
)
{
return
RESULT_NEED_MORE_DATA
;
}
inputStream
.
read
(
sampleEncryptionData
.
getData
(),
0
,
sampleEncryptionDataLength
);
fragmentRun
.
sampleEncryptionDataNeedsFill
=
false
;
enterState
(
STATE_READING_SAMPLE
);
return
0
;
}
...
...
@@ -1093,15 +1105,12 @@ public final class FragmentedMp4Extractor {
}
private
int
skipSample
(
NonBlockingInputStream
inputStream
,
int
sampleSize
)
{
ParsableByteArray
sampleEncryptionData
=
cencAuxiliaryData
!=
null
?
cencAuxiliaryData
:
fragmentRun
.
smoothStreamingSampleEncryptionData
;
ParsableByteArray
sampleEncryptionData
=
fragmentRun
.
sampleEncryptionData
;
if
(
sampleEncryptionData
!=
null
)
{
TrackEncryptionBox
encryptionBox
=
track
.
sampleDescriptionEncryptionBoxes
[
fragmentRun
.
sampleDescriptionIndex
];
int
vectorSize
=
encryptionBox
.
initializationVectorSize
;
boolean
subsampleEncryption
=
cencAuxiliaryData
!=
null
?
fragmentRun
.
auxiliarySampleInfoSizeTable
[
sampleIndex
]
>
vectorSize
:
fragmentRun
.
smoothStreamingUsesSubsampleEncryption
;
boolean
subsampleEncryption
=
fragmentRun
.
sampleHasSubsampleEncryptionTable
[
sampleIndex
];
sampleEncryptionData
.
skip
(
vectorSize
);
int
subsampleCount
=
subsampleEncryption
?
sampleEncryptionData
.
readUnsignedShort
()
:
1
;
if
(
subsampleEncryption
)
{
...
...
@@ -1128,13 +1137,11 @@ public final class FragmentedMp4Extractor {
out
.
flags
|=
MediaExtractor
.
SAMPLE_FLAG_SYNC
;
lastSyncSampleIndex
=
sampleIndex
;
}
if
(
out
.
allowDataBufferReplacement
&&
(
out
.
data
==
null
||
out
.
data
.
capacity
()
<
sampleSize
))
{
if
(
out
.
allowDataBufferReplacement
&&
(
out
.
data
==
null
||
out
.
data
.
capacity
()
<
sampleSize
))
{
outputData
=
ByteBuffer
.
allocate
(
sampleSize
);
out
.
data
=
outputData
;
}
ParsableByteArray
sampleEncryptionData
=
cencAuxiliaryData
!=
null
?
cencAuxiliaryData
:
fragmentRun
.
smoothStreamingSampleEncryptionData
;
ParsableByteArray
sampleEncryptionData
=
fragmentRun
.
sampleEncryptionData
;
if
(
sampleEncryptionData
!=
null
)
{
readSampleEncryptionData
(
sampleEncryptionData
,
out
);
}
...
...
@@ -1173,9 +1180,7 @@ public final class FragmentedMp4Extractor {
byte
[]
keyId
=
encryptionBox
.
keyId
;
boolean
isEncrypted
=
encryptionBox
.
isEncrypted
;
int
vectorSize
=
encryptionBox
.
initializationVectorSize
;
boolean
subsampleEncryption
=
cencAuxiliaryData
!=
null
?
fragmentRun
.
auxiliarySampleInfoSizeTable
[
sampleIndex
]
>
vectorSize
:
fragmentRun
.
smoothStreamingUsesSubsampleEncryption
;
boolean
subsampleEncryption
=
fragmentRun
.
sampleHasSubsampleEncryptionTable
[
sampleIndex
];
byte
[]
vector
=
out
.
cryptoInfo
.
iv
;
if
(
vector
==
null
||
vector
.
length
!=
16
)
{
...
...
library/src/main/java/com/google/android/exoplayer/parser/mp4/TrackFragment.java
View file @
005e98fc
...
...
@@ -27,12 +27,10 @@ package com.google.android.exoplayer.parser.mp4;
public
int
[]
sampleDecodingTimeTable
;
public
int
[]
sampleCompositionTimeOffsetTable
;
public
boolean
[]
sampleIsSyncFrameTable
;
public
boolean
[]
sampleHasSubsampleEncryptionTable
;
public
int
auxiliarySampleInfoTotalSize
;
public
int
[]
auxiliarySampleInfoSizeTable
;
public
boolean
smoothStreamingUsesSubsampleEncryption
;
public
ParsableByteArray
smoothStreamingSampleEncryptionData
;
public
ParsableByteArray
sampleEncryptionData
;
public
boolean
sampleEncryptionDataNeedsFill
;
public
void
setSampleDescriptionIndex
(
int
sampleDescriptionIndex
)
{
this
.
sampleDescriptionIndex
=
sampleDescriptionIndex
;
...
...
@@ -47,16 +45,11 @@ package com.google.android.exoplayer.parser.mp4;
this
.
length
=
sampleSizeTable
.
length
;
}
public
void
setAuxiliarySampleInfoTables
(
int
totalAuxiliarySampleInfoSize
,
int
[]
auxiliarySampleInfoSizeTable
)
{
this
.
auxiliarySampleInfoTotalSize
=
totalAuxiliarySampleInfoSize
;
this
.
auxiliarySampleInfoSizeTable
=
auxiliarySampleInfoSizeTable
;
}
public
void
setSmoothStreamingSampleEncryptionData
(
ParsableByteArray
data
,
boolean
usesSubsampleEncryption
)
{
this
.
smoothStreamingSampleEncryptionData
=
data
;
this
.
smoothStreamingUsesSubsampleEncryption
=
usesSubsampleEncryption
;
public
void
setSampleEncryptionData
(
boolean
[]
sampleHasSubsampleEncryptionTable
,
ParsableByteArray
sampleEncryptionData
,
boolean
sampleEncryptionDataNeedsFill
)
{
this
.
sampleHasSubsampleEncryptionTable
=
sampleHasSubsampleEncryptionTable
;
this
.
sampleEncryptionData
=
sampleEncryptionData
;
this
.
sampleEncryptionDataNeedsFill
=
sampleEncryptionDataNeedsFill
;
}
public
int
getSamplePresentationTime
(
int
index
)
{
...
...
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