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
821d4fb1
authored
Dec 18, 2019
by
olly
Committed by
Oliver Woodman
Dec 18, 2019
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add NonNull at package level for extractor.mp4
PiperOrigin-RevId: 286191078
parent
7a03e8ed
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
150 additions
and
100 deletions
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntry.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackFragment.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/package-info.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
821d4fb1
...
...
@@ -125,14 +125,16 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
Pair
<
Long
,
String
>
mdhdData
=
parseMdhd
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_mdhd
).
data
);
StsdData
stsdData
=
parseStsd
(
stbl
.
getLeafAtomOfType
(
Atom
.
TYPE_stsd
).
data
,
tkhdData
.
id
,
tkhdData
.
rotationDegrees
,
mdhdData
.
second
,
drmInitData
,
isQuickTime
);
long
[]
editListDurations
=
null
;
long
[]
editListMediaTimes
=
null
;
@Nullable
long
[]
editListDurations
=
null
;
@Nullable
long
[]
editListMediaTimes
=
null
;
if
(!
ignoreEditLists
)
{
@Nullable
Pair
<
long
[],
long
[]>
edtsData
=
parseEdts
(
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_edts
));
if
(
edtsData
!=
null
)
{
editListDurations
=
edtsData
.
first
;
editListMediaTimes
=
edtsData
.
second
;
@Nullable
Atom
.
ContainerAtom
edtsAtom
=
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_edts
);
if
(
edtsAtom
!=
null
)
{
@Nullable
Pair
<
long
[],
long
[]>
edtsData
=
parseEdts
(
edtsAtom
);
if
(
edtsData
!=
null
)
{
editListDurations
=
edtsData
.
first
;
editListMediaTimes
=
edtsData
.
second
;
}
}
}
return
stsdData
.
format
==
null
?
null
...
...
@@ -154,11 +156,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
Track
track
,
Atom
.
ContainerAtom
stblAtom
,
GaplessInfoHolder
gaplessInfoHolder
)
throws
ParserException
{
SampleSizeBox
sampleSizeBox
;
Atom
.
LeafAtom
stszAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stsz
);
@Nullable
Atom
.
LeafAtom
stszAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stsz
);
if
(
stszAtom
!=
null
)
{
sampleSizeBox
=
new
StszSampleSizeBox
(
stszAtom
);
}
else
{
Atom
.
LeafAtom
stz2Atom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stz2
);
@Nullable
Atom
.
LeafAtom
stz2Atom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stz2
);
if
(
stz2Atom
==
null
)
{
throw
new
ParserException
(
"Track has no sample table size information"
);
}
...
...
@@ -179,7 +181,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
// Entries are byte offsets of chunks.
boolean
chunkOffsetsAreLongs
=
false
;
Atom
.
LeafAtom
chunkOffsetsAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stco
);
@Nullable
Atom
.
LeafAtom
chunkOffsetsAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stco
);
if
(
chunkOffsetsAtom
==
null
)
{
chunkOffsetsAreLongs
=
true
;
chunkOffsetsAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_co64
);
...
...
@@ -190,11 +192,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
// Entries are (number of samples, timestamp delta between those samples).
ParsableByteArray
stts
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stts
).
data
;
// Entries are the indices of samples that are synchronization samples.
Atom
.
LeafAtom
stssAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stss
);
ParsableByteArray
stss
=
stssAtom
!=
null
?
stssAtom
.
data
:
null
;
@Nullable
Atom
.
LeafAtom
stssAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stss
);
@Nullable
ParsableByteArray
stss
=
stssAtom
!=
null
?
stssAtom
.
data
:
null
;
// Entries are (number of samples, timestamp offset).
Atom
.
LeafAtom
cttsAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_ctts
);
ParsableByteArray
ctts
=
cttsAtom
!=
null
?
cttsAtom
.
data
:
null
;
@Nullable
Atom
.
LeafAtom
cttsAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_ctts
);
@Nullable
ParsableByteArray
ctts
=
cttsAtom
!=
null
?
cttsAtom
.
data
:
null
;
// Prepare to read chunk information.
ChunkIterator
chunkIterator
=
new
ChunkIterator
(
stsc
,
chunkOffsets
,
chunkOffsetsAreLongs
);
...
...
@@ -542,9 +544,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
*/
@Nullable
public
static
Metadata
parseMdtaFromMeta
(
Atom
.
ContainerAtom
meta
)
{
Atom
.
LeafAtom
hdlrAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
);
Atom
.
LeafAtom
keysAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_keys
);
Atom
.
LeafAtom
ilstAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_ilst
);
@Nullable
Atom
.
LeafAtom
hdlrAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
);
@Nullable
Atom
.
LeafAtom
keysAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_keys
);
@Nullable
Atom
.
LeafAtom
ilstAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_ilst
);
if
(
hdlrAtom
==
null
||
keysAtom
==
null
||
ilstAtom
==
null
...
...
@@ -575,6 +577,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
int
keyIndex
=
ilst
.
readInt
()
-
1
;
if
(
keyIndex
>=
0
&&
keyIndex
<
keyNames
.
length
)
{
String
key
=
keyNames
[
keyIndex
];
@Nullable
Metadata
.
Entry
entry
=
MetadataUtil
.
parseMdtaMetadataEntryFromIlst
(
ilst
,
atomPosition
+
atomSize
,
key
);
if
(
entry
!=
null
)
{
...
...
@@ -609,7 +612,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
ilst
.
skipBytes
(
Atom
.
HEADER_SIZE
);
ArrayList
<
Metadata
.
Entry
>
entries
=
new
ArrayList
<>();
while
(
ilst
.
getPosition
()
<
limit
)
{
Metadata
.
Entry
entry
=
MetadataUtil
.
parseIlstElement
(
ilst
);
@Nullable
Metadata
.
Entry
entry
=
MetadataUtil
.
parseIlstElement
(
ilst
);
if
(
entry
!=
null
)
{
entries
.
add
(
entry
);
}
...
...
@@ -817,12 +820,18 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
return
out
;
}
private
static
void
parseTextSampleEntry
(
ParsableByteArray
parent
,
int
atomType
,
int
position
,
int
atomSize
,
int
trackId
,
String
language
,
StsdData
out
)
throws
ParserException
{
private
static
void
parseTextSampleEntry
(
ParsableByteArray
parent
,
int
atomType
,
int
position
,
int
atomSize
,
int
trackId
,
String
language
,
StsdData
out
)
{
parent
.
setPosition
(
position
+
Atom
.
HEADER_SIZE
+
StsdData
.
STSD_HEADER_SIZE
);
// Default values.
List
<
byte
[]>
initializationData
=
null
;
@Nullable
List
<
byte
[]>
initializationData
=
null
;
long
subsampleOffsetUs
=
Format
.
OFFSET_SAMPLE_RELATIVE
;
String
mimeType
;
...
...
@@ -934,7 +943,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
initializationData
=
hevcConfig
.
initializationData
;
out
.
nalUnitLengthFieldLength
=
hevcConfig
.
nalUnitLengthFieldLength
;
}
else
if
(
childAtomType
==
Atom
.
TYPE_dvcC
||
childAtomType
==
Atom
.
TYPE_dvvC
)
{
DolbyVisionConfig
dolbyVisionConfig
=
DolbyVisionConfig
.
parse
(
parent
);
@Nullable
DolbyVisionConfig
dolbyVisionConfig
=
DolbyVisionConfig
.
parse
(
parent
);
if
(
dolbyVisionConfig
!=
null
)
{
codecs
=
dolbyVisionConfig
.
codecs
;
mimeType
=
MimeTypes
.
VIDEO_DOLBY_VISION
;
...
...
@@ -1021,11 +1030,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
*/
@Nullable
private
static
Pair
<
long
[],
long
[]>
parseEdts
(
Atom
.
ContainerAtom
edtsAtom
)
{
Atom
.
LeafAtom
elst
;
if
(
e
dtsAtom
==
null
||
(
elst
=
edtsAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_elst
))
==
null
)
{
@Nullable
Atom
.
LeafAtom
elstAtom
=
edtsAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_elst
)
;
if
(
e
lstAtom
==
null
)
{
return
null
;
}
ParsableByteArray
elstData
=
elst
.
data
;
ParsableByteArray
elstData
=
elst
Atom
.
data
;
elstData
.
setPosition
(
Atom
.
HEADER_SIZE
);
int
fullAtom
=
elstData
.
readInt
();
int
version
=
Atom
.
parseFullAtomVersion
(
fullAtom
);
...
...
@@ -1328,8 +1337,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
int
childPosition
=
position
+
Atom
.
HEADER_SIZE
;
int
schemeInformationBoxPosition
=
C
.
POSITION_UNSET
;
int
schemeInformationBoxSize
=
0
;
String
schemeType
=
null
;
Integer
dataFormat
=
null
;
@Nullable
String
schemeType
=
null
;
@Nullable
Integer
dataFormat
=
null
;
while
(
childPosition
-
position
<
size
)
{
parent
.
setPosition
(
childPosition
);
int
childAtomSize
=
parent
.
readInt
();
...
...
@@ -1352,9 +1361,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
Assertions
.
checkStateNotNull
(
dataFormat
,
"frma atom is mandatory"
);
Assertions
.
checkState
(
schemeInformationBoxPosition
!=
C
.
POSITION_UNSET
,
"schi atom is mandatory"
);
TrackEncryptionBox
encryptionBox
=
parseSchiFromParent
(
parent
,
schemeInformationBoxPosition
,
schemeInformationBoxSize
,
schemeType
);
Assertions
.
checkStateNotNull
(
encryptionBox
,
"tenc atom is mandatory"
);
TrackEncryptionBox
encryptionBox
=
Assertions
.
checkStateNotNull
(
parseSchiFromParent
(
parent
,
schemeInformationBoxPosition
,
schemeInformationBoxSize
,
schemeType
),
"tenc atom is mandatory"
);
return
Pair
.
create
(
dataFormat
,
encryptionBox
);
}
else
{
return
null
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
View file @
821d4fb1
...
...
@@ -55,6 +55,7 @@ import java.util.Arrays;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.UUID
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/** Extracts data from the FMP4 container format. */
@SuppressWarnings
(
"ConstantField"
)
...
...
@@ -155,14 +156,14 @@ public class FragmentedMp4Extractor implements Extractor {
private
int
atomType
;
private
long
atomSize
;
private
int
atomHeaderBytesRead
;
private
ParsableByteArray
atomData
;
@Nullable
private
ParsableByteArray
atomData
;
private
long
endOfMdatPosition
;
private
int
pendingMetadataSampleBytes
;
private
long
pendingSeekTimeUs
;
private
long
durationUs
;
private
long
segmentIndexEarliestPresentationTimeUs
;
private
TrackBundle
currentTrackBundle
;
@Nullable
private
TrackBundle
currentTrackBundle
;
private
int
sampleSize
;
private
int
sampleBytesWritten
;
private
int
sampleCurrentNalBytesRemaining
;
...
...
@@ -170,7 +171,7 @@ public class FragmentedMp4Extractor implements Extractor {
private
boolean
isAc4HeaderRequired
;
// Extractor output.
private
ExtractorOutput
extractorOutput
;
@MonotonicNonNull
private
ExtractorOutput
extractorOutput
;
private
TrackOutput
[]
emsgTrackOutputs
;
private
TrackOutput
[]
cea608TrackOutputs
;
...
...
@@ -495,6 +496,7 @@ public class FragmentedMp4Extractor implements Extractor {
for
(
int
i
=
0
;
i
<
moovContainerChildrenSize
;
i
++)
{
Atom
.
ContainerAtom
atom
=
moov
.
containerChildren
.
get
(
i
);
if
(
atom
.
type
==
Atom
.
TYPE_trak
)
{
@Nullable
Track
track
=
modifyTrack
(
AtomParsers
.
parseTrak
(
...
...
@@ -712,7 +714,7 @@ public class FragmentedMp4Extractor implements Extractor {
private
static
void
parseTraf
(
ContainerAtom
traf
,
SparseArray
<
TrackBundle
>
trackBundleArray
,
@Flags
int
flags
,
byte
[]
extendedTypeScratch
)
throws
ParserException
{
LeafAtom
tfhd
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfhd
);
TrackBundle
trackBundle
=
parseTfhd
(
tfhd
.
data
,
trackBundleArray
);
@Nullable
TrackBundle
trackBundle
=
parseTfhd
(
tfhd
.
data
,
trackBundleArray
);
if
(
trackBundle
==
null
)
{
return
;
}
...
...
@@ -721,33 +723,34 @@ public class FragmentedMp4Extractor implements Extractor {
long
decodeTime
=
fragment
.
nextFragmentDecodeTime
;
trackBundle
.
reset
();
LeafAtom
tfdtAtom
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfdt
);
@Nullable
LeafAtom
tfdtAtom
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfdt
);
if
(
tfdtAtom
!=
null
&&
(
flags
&
FLAG_WORKAROUND_IGNORE_TFDT_BOX
)
==
0
)
{
decodeTime
=
parseTfdt
(
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfdt
).
data
);
}
parseTruns
(
traf
,
trackBundle
,
decodeTime
,
flags
);
TrackEncryptionBox
encryptionBox
=
trackBundle
.
track
.
getSampleDescriptionEncryptionBox
(
fragment
.
header
.
sampleDescriptionIndex
);
@Nullable
TrackEncryptionBox
encryptionBox
=
trackBundle
.
track
.
getSampleDescriptionEncryptionBox
(
fragment
.
header
.
sampleDescriptionIndex
);
LeafAtom
saiz
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_saiz
);
@Nullable
LeafAtom
saiz
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_saiz
);
if
(
saiz
!=
null
)
{
parseSaiz
(
encryptionBox
,
saiz
.
data
,
fragment
);
}
LeafAtom
saio
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_saio
);
@Nullable
LeafAtom
saio
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_saio
);
if
(
saio
!=
null
)
{
parseSaio
(
saio
.
data
,
fragment
);
}
LeafAtom
senc
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_senc
);
@Nullable
LeafAtom
senc
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_senc
);
if
(
senc
!=
null
)
{
parseSenc
(
senc
.
data
,
fragment
);
}
LeafAtom
sbgp
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_sbgp
);
LeafAtom
sgpd
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_sgpd
);
@Nullable
LeafAtom
sbgp
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_sbgp
);
@Nullable
LeafAtom
sgpd
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_sgpd
);
if
(
sbgp
!=
null
&&
sgpd
!=
null
)
{
parseSgpd
(
sbgp
.
data
,
sgpd
.
data
,
encryptionBox
!=
null
?
encryptionBox
.
schemeType
:
null
,
fragment
);
...
...
@@ -863,13 +866,14 @@ public class FragmentedMp4Extractor implements Extractor {
* @return The {@link TrackBundle} to which the {@link TrackFragment} belongs, or null if the tfhd
* does not refer to any {@link TrackBundle}.
*/
@Nullable
private
static
TrackBundle
parseTfhd
(
ParsableByteArray
tfhd
,
SparseArray
<
TrackBundle
>
trackBundles
)
{
tfhd
.
setPosition
(
Atom
.
HEADER_SIZE
);
int
fullAtom
=
tfhd
.
readInt
();
int
atomFlags
=
Atom
.
parseFullAtomFlags
(
fullAtom
);
int
trackId
=
tfhd
.
readInt
();
TrackBundle
trackBundle
=
getTrackBundle
(
trackBundles
,
trackId
);
@Nullable
TrackBundle
trackBundle
=
getTrackBundle
(
trackBundles
,
trackId
);
if
(
trackBundle
==
null
)
{
return
null
;
}
...
...
@@ -1053,8 +1057,12 @@ public class FragmentedMp4Extractor implements Extractor {
out
.
fillEncryptionData
(
senc
);
}
private
static
void
parseSgpd
(
ParsableByteArray
sbgp
,
ParsableByteArray
sgpd
,
String
schemeType
,
TrackFragment
out
)
throws
ParserException
{
private
static
void
parseSgpd
(
ParsableByteArray
sbgp
,
ParsableByteArray
sgpd
,
@Nullable
String
schemeType
,
TrackFragment
out
)
throws
ParserException
{
sbgp
.
setPosition
(
Atom
.
HEADER_SIZE
);
int
sbgpFullAtom
=
sbgp
.
readInt
();
if
(
sbgp
.
readInt
()
!=
SAMPLE_GROUP_TYPE_seig
)
{
...
...
@@ -1216,7 +1224,7 @@ public class FragmentedMp4Extractor implements Extractor {
private
boolean
readSample
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
if
(
parserState
==
STATE_READING_SAMPLE_START
)
{
if
(
currentTrackBundle
==
null
)
{
TrackBundle
currentTrackBundle
=
getNextFragmentRun
(
trackBundles
);
@Nullable
TrackBundle
currentTrackBundle
=
getNextFragmentRun
(
trackBundles
);
if
(
currentTrackBundle
==
null
)
{
// We've run out of samples in the current mdat. Discard any trailing data and prepare to
// read the header of the next atom.
...
...
@@ -1388,6 +1396,7 @@ public class FragmentedMp4Extractor implements Extractor {
* Returns the {@link TrackBundle} whose fragment run has the earliest file position out of those
* yet to be consumed, or null if all have been consumed.
*/
@Nullable
private
static
TrackBundle
getNextFragmentRun
(
SparseArray
<
TrackBundle
>
trackBundles
)
{
TrackBundle
nextTrackBundle
=
null
;
long
nextTrackRunOffset
=
Long
.
MAX_VALUE
;
...
...
@@ -1410,7 +1419,7 @@ public class FragmentedMp4Extractor implements Extractor {
/** Returns DrmInitData from leaf atoms. */
private
static
DrmInitData
getDrmInitDataFromAtoms
(
List
<
Atom
.
LeafAtom
>
leafChildren
)
{
ArrayList
<
SchemeData
>
schemeDatas
=
null
;
@Nullable
ArrayList
<
SchemeData
>
schemeDatas
=
null
;
int
leafChildrenSize
=
leafChildren
.
size
();
for
(
int
i
=
0
;
i
<
leafChildrenSize
;
i
++)
{
LeafAtom
child
=
leafChildren
.
get
(
i
);
...
...
@@ -1419,7 +1428,7 @@ public class FragmentedMp4Extractor implements Extractor {
schemeDatas
=
new
ArrayList
<>();
}
byte
[]
psshData
=
child
.
data
.
data
;
UUID
uuid
=
PsshAtomUtil
.
parseUuid
(
psshData
);
@Nullable
UUID
uuid
=
PsshAtomUtil
.
parseUuid
(
psshData
);
if
(
uuid
==
null
)
{
Log
.
w
(
TAG
,
"Skipped pssh atom (failed to extract uuid)"
);
}
else
{
...
...
@@ -1496,9 +1505,10 @@ public class FragmentedMp4Extractor implements Extractor {
}
public
void
updateDrmInitData
(
DrmInitData
drmInitData
)
{
@Nullable
TrackEncryptionBox
encryptionBox
=
track
.
getSampleDescriptionEncryptionBox
(
fragment
.
header
.
sampleDescriptionIndex
);
String
schemeType
=
encryptionBox
!=
null
?
encryptionBox
.
schemeType
:
null
;
@Nullable
String
schemeType
=
encryptionBox
!=
null
?
encryptionBox
.
schemeType
:
null
;
output
.
format
(
track
.
format
.
copyWithDrmInitData
(
drmInitData
.
copyWithSchemeType
(
schemeType
)));
}
...
...
@@ -1595,7 +1605,7 @@ public class FragmentedMp4Extractor implements Extractor {
/** Skips the encryption data for the current sample. */
private
void
skipSampleEncryptionData
()
{
TrackEncryptionBox
encryptionBox
=
getEncryptionBoxIfEncrypted
();
@Nullable
TrackEncryptionBox
encryptionBox
=
getEncryptionBoxIfEncrypted
();
if
(
encryptionBox
==
null
)
{
return
;
}
...
...
@@ -1609,8 +1619,10 @@ public class FragmentedMp4Extractor implements Extractor {
}
}
@Nullable
private
TrackEncryptionBox
getEncryptionBoxIfEncrypted
()
{
int
sampleDescriptionIndex
=
fragment
.
header
.
sampleDescriptionIndex
;
@Nullable
TrackEncryptionBox
encryptionBox
=
fragment
.
trackEncryptionBox
!=
null
?
fragment
.
trackEncryptionBox
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntry.java
View file @
821d4fb1
...
...
@@ -47,8 +47,7 @@ public final class MdtaMetadataEntry implements Metadata.Entry {
private
MdtaMetadataEntry
(
Parcel
in
)
{
key
=
Util
.
castNonNull
(
in
.
readString
());
value
=
new
byte
[
in
.
readInt
()];
in
.
readByteArray
(
value
);
value
=
Util
.
castNonNull
(
in
.
createByteArray
());
localeIndicator
=
in
.
readInt
();
typeIndicator
=
in
.
readInt
();
}
...
...
@@ -88,7 +87,6 @@ public final class MdtaMetadataEntry implements Metadata.Entry {
@Override
public
void
writeToParcel
(
Parcel
dest
,
int
flags
)
{
dest
.
writeString
(
key
);
dest
.
writeInt
(
value
.
length
);
dest
.
writeByteArray
(
value
);
dest
.
writeInt
(
localeIndicator
);
dest
.
writeInt
(
typeIndicator
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
View file @
821d4fb1
...
...
@@ -325,8 +325,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
@Nullable
private
static
TextInformationFrame
parseStandardGenreAttribute
(
ParsableByteArray
data
)
{
int
genreCode
=
parseUint8AttributeValue
(
data
);
String
genreString
=
(
0
<
genreCode
&&
genreCode
<=
STANDARD_GENRES
.
length
)
?
STANDARD_GENRES
[
genreCode
-
1
]
:
null
;
@Nullable
String
genreString
=
(
0
<
genreCode
&&
genreCode
<=
STANDARD_GENRES
.
length
)
?
STANDARD_GENRES
[
genreCode
-
1
]
:
null
;
if
(
genreString
!=
null
)
{
return
new
TextInformationFrame
(
"TCON"
,
/* description= */
null
,
genreString
);
}
...
...
@@ -341,7 +344,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
if
(
atomType
==
Atom
.
TYPE_data
)
{
int
fullVersionInt
=
data
.
readInt
();
int
flags
=
Atom
.
parseFullAtomFlags
(
fullVersionInt
);
String
mimeType
=
flags
==
13
?
"image/jpeg"
:
flags
==
14
?
"image/png"
:
null
;
@Nullable
String
mimeType
=
flags
==
13
?
"image/jpeg"
:
flags
==
14
?
"image/png"
:
null
;
if
(
mimeType
==
null
)
{
Log
.
w
(
TAG
,
"Unrecognized cover art flags: "
+
flags
);
return
null
;
...
...
@@ -361,8 +364,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
@Nullable
private
static
Id3Frame
parseInternalAttribute
(
ParsableByteArray
data
,
int
endPosition
)
{
String
domain
=
null
;
String
name
=
null
;
@Nullable
String
domain
=
null
;
@Nullable
String
name
=
null
;
int
dataAtomPosition
=
-
1
;
int
dataAtomSize
=
-
1
;
while
(
data
.
getPosition
()
<
endPosition
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
View file @
821d4fb1
...
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.ParserException
;
...
...
@@ -42,6 +43,7 @@ import java.lang.annotation.RetentionPolicy;
import
java.util.ArrayDeque
;
import
java.util.ArrayList
;
import
java.util.List
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/**
* Extracts data from the MP4 container format.
...
...
@@ -105,7 +107,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private
int
atomType
;
private
long
atomSize
;
private
int
atomHeaderBytesRead
;
private
ParsableByteArray
atomData
;
@Nullable
private
ParsableByteArray
atomData
;
private
int
sampleTrackIndex
;
private
int
sampleBytesWritten
;
...
...
@@ -113,7 +115,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private
boolean
isAc4HeaderRequired
;
// Extractor outputs.
private
ExtractorOutput
extractorOutput
;
@MonotonicNonNull
private
ExtractorOutput
extractorOutput
;
private
Mp4Track
[]
tracks
;
private
long
[][]
accumulatedSampleSizes
;
private
int
firstVideoTrackIndex
;
...
...
@@ -290,8 +292,11 @@ public final class Mp4Extractor implements Extractor, SeekMap {
// The atom extends to the end of the file. Note that if the atom is within a container we can
// work out its size even if the input length is unknown.
long
endPosition
=
input
.
getLength
();
if
(
endPosition
==
C
.
LENGTH_UNSET
&&
!
containerAtoms
.
isEmpty
())
{
endPosition
=
containerAtoms
.
peek
().
endPosition
;
if
(
endPosition
==
C
.
LENGTH_UNSET
)
{
@Nullable
ContainerAtom
containerAtom
=
containerAtoms
.
peek
();
if
(
containerAtom
!=
null
)
{
endPosition
=
containerAtom
.
endPosition
;
}
}
if
(
endPosition
!=
C
.
LENGTH_UNSET
)
{
atomSize
=
endPosition
-
input
.
getPosition
()
+
atomHeaderBytesRead
;
...
...
@@ -386,17 +391,17 @@ public final class Mp4Extractor implements Extractor, SeekMap {
List
<
Mp4Track
>
tracks
=
new
ArrayList
<>();
// Process metadata.
Metadata
udtaMetadata
=
null
;
@Nullable
Metadata
udtaMetadata
=
null
;
GaplessInfoHolder
gaplessInfoHolder
=
new
GaplessInfoHolder
();
Atom
.
LeafAtom
udta
=
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_udta
);
@Nullable
Atom
.
LeafAtom
udta
=
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_udta
);
if
(
udta
!=
null
)
{
udtaMetadata
=
AtomParsers
.
parseUdta
(
udta
,
isQuickTime
);
if
(
udtaMetadata
!=
null
)
{
gaplessInfoHolder
.
setFromMetadata
(
udtaMetadata
);
}
}
Metadata
mdtaMetadata
=
null
;
Atom
.
ContainerAtom
meta
=
moov
.
getContainerAtomOfType
(
Atom
.
TYPE_meta
);
@Nullable
Metadata
mdtaMetadata
=
null
;
@Nullable
Atom
.
ContainerAtom
meta
=
moov
.
getContainerAtomOfType
(
Atom
.
TYPE_meta
);
if
(
meta
!=
null
)
{
mdtaMetadata
=
AtomParsers
.
parseMdtaFromMeta
(
meta
);
}
...
...
@@ -453,6 +458,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
if
(
atom
.
type
!=
Atom
.
TYPE_trak
)
{
continue
;
}
@Nullable
Track
track
=
AtomParsers
.
parseTrak
(
atom
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java
View file @
821d4fb1
...
...
@@ -49,8 +49,6 @@ public final class PsshAtomUtil {
* @param data The scheme specific data.
* @return The PSSH atom.
*/
// dereference of possibly-null reference keyId
@SuppressWarnings
({
"ParameterNotNullable"
,
"nullness:dereference.of.nullable"
})
public
static
byte
[]
buildPsshAtom
(
UUID
systemId
,
@Nullable
UUID
[]
keyIds
,
@Nullable
byte
[]
data
)
{
int
dataLength
=
data
!=
null
?
data
.
length
:
0
;
...
...
@@ -97,8 +95,9 @@ public final class PsshAtomUtil {
* @return The parsed UUID. Null if the input is not a valid PSSH atom, or if the PSSH atom has an
* unsupported version.
*/
public
static
@Nullable
UUID
parseUuid
(
byte
[]
atom
)
{
PsshAtom
parsedAtom
=
parsePsshAtom
(
atom
);
@Nullable
public
static
UUID
parseUuid
(
byte
[]
atom
)
{
@Nullable
PsshAtom
parsedAtom
=
parsePsshAtom
(
atom
);
if
(
parsedAtom
==
null
)
{
return
null
;
}
...
...
@@ -115,7 +114,7 @@ public final class PsshAtomUtil {
* an unsupported version.
*/
public
static
int
parseVersion
(
byte
[]
atom
)
{
PsshAtom
parsedAtom
=
parsePsshAtom
(
atom
);
@Nullable
PsshAtom
parsedAtom
=
parsePsshAtom
(
atom
);
if
(
parsedAtom
==
null
)
{
return
-
1
;
}
...
...
@@ -133,8 +132,9 @@ public final class PsshAtomUtil {
* @return The parsed scheme specific data. Null if the input is not a valid PSSH atom, or if the
* PSSH atom has an unsupported version, or if the PSSH atom does not match the passed UUID.
*/
public
static
@Nullable
byte
[]
parseSchemeSpecificData
(
byte
[]
atom
,
UUID
uuid
)
{
PsshAtom
parsedAtom
=
parsePsshAtom
(
atom
);
@Nullable
public
static
byte
[]
parseSchemeSpecificData
(
byte
[]
atom
,
UUID
uuid
)
{
@Nullable
PsshAtom
parsedAtom
=
parsePsshAtom
(
atom
);
if
(
parsedAtom
==
null
)
{
return
null
;
}
...
...
@@ -153,7 +153,8 @@ public final class PsshAtomUtil {
* has an unsupported version.
*/
// TODO: Support parsing of the key ids for version 1 PSSH atoms.
private
static
@Nullable
PsshAtom
parsePsshAtom
(
byte
[]
atom
)
{
@Nullable
private
static
PsshAtom
parsePsshAtom
(
byte
[]
atom
)
{
ParsableByteArray
atomData
=
new
ParsableByteArray
(
atom
);
if
(
atomData
.
limit
()
<
Atom
.
FULL_HEADER_SIZE
+
16
/* UUID */
+
4
/* DataSize */
)
{
// Data too short.
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackFragment.java
View file @
821d4fb1
...
...
@@ -15,19 +15,19 @@
*/
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
java.io.IOException
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/**
* A holder for information corresponding to a single fragment of an mp4 file.
*/
/* package */
final
class
TrackFragment
{
/**
* The default values for samples from the track fragment header.
*/
public
DefaultSampleValues
header
;
/** The default values for samples from the track fragment header. */
@MonotonicNonNull
public
DefaultSampleValues
header
;
/**
* The position (byte offset) of the start of fragment.
*/
...
...
@@ -81,20 +81,13 @@ import java.io.IOException;
* Undefined otherwise.
*/
public
boolean
[]
sampleHasSubsampleEncryptionTable
;
/**
* Fragment specific track encryption. May be null.
*/
public
TrackEncryptionBox
trackEncryptionBox
;
/**
* If {@link #definesEncryptionData} is true, indicates the length of the sample encryption data.
* Undefined otherwise.
*/
public
int
sampleEncryptionDataLength
;
/** Fragment specific track encryption. May be null. */
@Nullable
public
TrackEncryptionBox
trackEncryptionBox
;
/**
* If {@link #definesEncryptionData} is true, contains binary sample encryption data. Undefined
* otherwise.
*/
public
ParsableByteArray
sampleEncryptionData
;
public
final
ParsableByteArray
sampleEncryptionData
;
/**
* Whether {@link #sampleEncryptionData} needs populating with the actual encryption data.
*/
...
...
@@ -104,6 +97,17 @@ import java.io.IOException;
*/
public
long
nextFragmentDecodeTime
;
public
TrackFragment
()
{
trunDataPosition
=
new
long
[
0
];
trunLength
=
new
int
[
0
];
sampleSizeTable
=
new
int
[
0
];
sampleCompositionTimeOffsetTable
=
new
int
[
0
];
sampleDecodingTimeTable
=
new
long
[
0
];
sampleIsSyncFrameTable
=
new
boolean
[
0
];
sampleHasSubsampleEncryptionTable
=
new
boolean
[
0
];
sampleEncryptionData
=
new
ParsableByteArray
();
}
/**
* Resets the fragment.
* <p>
...
...
@@ -130,11 +134,11 @@ import java.io.IOException;
public
void
initTables
(
int
trunCount
,
int
sampleCount
)
{
this
.
trunCount
=
trunCount
;
this
.
sampleCount
=
sampleCount
;
if
(
trunLength
==
null
||
trunLength
.
length
<
trunCount
)
{
if
(
trunLength
.
length
<
trunCount
)
{
trunDataPosition
=
new
long
[
trunCount
];
trunLength
=
new
int
[
trunCount
];
}
if
(
sampleSizeTable
==
null
||
sampleSizeTable
.
length
<
sampleCount
)
{
if
(
sampleSizeTable
.
length
<
sampleCount
)
{
// Size the tables 25% larger than needed, so as to make future resize operations less
// likely. The choice of 25% is relatively arbitrary.
int
tableSize
=
(
sampleCount
*
125
)
/
100
;
...
...
@@ -148,18 +152,14 @@ import java.io.IOException;
/**
* Configures the fragment to be one that defines encryption data of the specified length.
* <p>
* {@link #definesEncryptionData} is set to true, {@link #sampleEncryptionDataLength} is set to
* the specified length, and {@link #sampleEncryptionData} is resized if necessary such that it
* is at least this length.
*
* <p>{@link #definesEncryptionData} is set to true, and the {@link ParsableByteArray#limit()
* limit} of {@link #sampleEncryptionData} is set to the specified length.
*
* @param length The length in bytes of the encryption data.
*/
public
void
initEncryptionData
(
int
length
)
{
if
(
sampleEncryptionData
==
null
||
sampleEncryptionData
.
limit
()
<
length
)
{
sampleEncryptionData
=
new
ParsableByteArray
(
length
);
}
sampleEncryptionDataLength
=
length
;
sampleEncryptionData
.
reset
(
length
);
definesEncryptionData
=
true
;
sampleEncryptionDataNeedsFill
=
true
;
}
...
...
@@ -170,7 +170,7 @@ import java.io.IOException;
* @param input An {@link ExtractorInput} from which to read the encryption data.
*/
public
void
fillEncryptionData
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
input
.
readFully
(
sampleEncryptionData
.
data
,
0
,
sampleEncryptionData
Length
);
input
.
readFully
(
sampleEncryptionData
.
data
,
0
,
sampleEncryptionData
.
limit
()
);
sampleEncryptionData
.
setPosition
(
0
);
sampleEncryptionDataNeedsFill
=
false
;
}
...
...
@@ -181,7 +181,7 @@ import java.io.IOException;
* @param source A source from which to read the encryption data.
*/
public
void
fillEncryptionData
(
ParsableByteArray
source
)
{
source
.
readBytes
(
sampleEncryptionData
.
data
,
0
,
sampleEncryptionData
Length
);
source
.
readBytes
(
sampleEncryptionData
.
data
,
0
,
sampleEncryptionData
.
limit
()
);
sampleEncryptionData
.
setPosition
(
0
);
sampleEncryptionDataNeedsFill
=
false
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/package-info.java
0 → 100644
View file @
821d4fb1
/*
* Copyright (C) 2019 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.
*/
@NonNullApi
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
com.google.android.exoplayer2.util.NonNullApi
;
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