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
ed1f41db
authored
Jan 04, 2019
by
andrewlewis
Committed by
Oliver Woodman
Jan 08, 2019
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Parse frame rate from 'mdta' metadata
PiperOrigin-RevId: 227813461
parent
d834eeab
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
405 additions
and
58 deletions
library/core/src/main/java/com/google/android/exoplayer2/Format.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.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/test/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntryTest.java
library/core/src/main/java/com/google/android/exoplayer2/Format.java
View file @
ed1f41db
...
...
@@ -1181,6 +1181,37 @@ public final class Format implements Parcelable {
metadata
);
}
public
Format
copyWithFrameRate
(
float
frameRate
)
{
return
new
Format
(
id
,
label
,
containerMimeType
,
sampleMimeType
,
codecs
,
bitrate
,
maxInputSize
,
width
,
height
,
frameRate
,
rotationDegrees
,
pixelWidthHeightRatio
,
projectionData
,
stereoMode
,
colorInfo
,
channelCount
,
sampleRate
,
pcmEncoding
,
encoderDelay
,
encoderPadding
,
selectionFlags
,
language
,
accessibilityChannel
,
subsampleOffsetUs
,
initializationData
,
drmInitData
,
metadata
);
}
public
Format
copyWithDrmInitData
(
@Nullable
DrmInitData
drmInitData
)
{
return
new
Format
(
id
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
View file @
ed1f41db
...
...
@@ -22,7 +22,7 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.List
;
@SuppressWarnings
(
"ConstantField"
)
@SuppressWarnings
(
{
"ConstantField"
,
"ConstantCaseForConstants"
}
)
/* package */
abstract
class
Atom
{
/**
...
...
@@ -130,6 +130,7 @@ import java.util.List;
public
static
final
int
TYPE_sawb
=
Util
.
getIntegerCodeForString
(
"sawb"
);
public
static
final
int
TYPE_udta
=
Util
.
getIntegerCodeForString
(
"udta"
);
public
static
final
int
TYPE_meta
=
Util
.
getIntegerCodeForString
(
"meta"
);
public
static
final
int
TYPE_keys
=
Util
.
getIntegerCodeForString
(
"keys"
);
public
static
final
int
TYPE_ilst
=
Util
.
getIntegerCodeForString
(
"ilst"
);
public
static
final
int
TYPE_mean
=
Util
.
getIntegerCodeForString
(
"mean"
);
public
static
final
int
TYPE_name
=
Util
.
getIntegerCodeForString
(
"name"
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
ed1f41db
...
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.mp4;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
MimeTypes
.
getMimeTypeFromMp4ObjectType
;
import
android.support.annotation.Nullable
;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
...
...
@@ -39,7 +40,7 @@ import java.util.Collections;
import
java.util.List
;
/** Utility methods for parsing MP4 format atom payloads according to ISO 14496-12. */
@SuppressWarnings
(
"ConstantField"
)
@SuppressWarnings
(
{
"ConstantField"
,
"ConstantCaseForConstants"
}
)
/* package */
final
class
AtomParsers
{
private
static
final
String
TAG
=
"AtomParsers"
;
...
...
@@ -51,6 +52,7 @@ import java.util.List;
private
static
final
int
TYPE_subt
=
Util
.
getIntegerCodeForString
(
"subt"
);
private
static
final
int
TYPE_clcp
=
Util
.
getIntegerCodeForString
(
"clcp"
);
private
static
final
int
TYPE_meta
=
Util
.
getIntegerCodeForString
(
"meta"
);
private
static
final
int
TYPE_mdta
=
Util
.
getIntegerCodeForString
(
"mdta"
);
/**
* The threshold number of samples to trim from the start/end of an audio track when applying an
...
...
@@ -77,7 +79,7 @@ import java.util.List;
DrmInitData
drmInitData
,
boolean
ignoreEditLists
,
boolean
isQuickTime
)
throws
ParserException
{
Atom
.
ContainerAtom
mdia
=
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_mdia
);
int
trackType
=
parseHdlr
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
).
data
);
int
trackType
=
getTrackTypeForHdlr
(
parseHdlr
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
).
data
)
);
if
(
trackType
==
C
.
TRACK_TYPE_UNKNOWN
)
{
return
null
;
}
...
...
@@ -485,6 +487,7 @@ import java.util.List;
* @param isQuickTime True for QuickTime media. False otherwise.
* @return Parsed metadata, or null.
*/
@Nullable
public
static
Metadata
parseUdta
(
Atom
.
LeafAtom
udtaAtom
,
boolean
isQuickTime
)
{
if
(
isQuickTime
)
{
// Meta boxes are regular boxes rather than full boxes in QuickTime. For now, don't try and
...
...
@@ -499,14 +502,69 @@ import java.util.List;
int
atomType
=
udtaData
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_meta
)
{
udtaData
.
setPosition
(
atomPosition
);
return
parse
MetaAtom
(
udtaData
,
atomPosition
+
atomSize
);
return
parse
UdtaMeta
(
udtaData
,
atomPosition
+
atomSize
);
}
udtaData
.
s
kipBytes
(
atomSize
-
Atom
.
HEADER_SIZE
);
udtaData
.
s
etPosition
(
atomPosition
+
atomSize
);
}
return
null
;
}
private
static
Metadata
parseMetaAtom
(
ParsableByteArray
meta
,
int
limit
)
{
/**
* Parses a metadata meta atom if it contains metadata with handler 'mdta'.
*
* @param meta The metadata atom to decode.
* @return Parsed metadata, or null.
*/
@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
);
if
(
hdlrAtom
==
null
||
keysAtom
==
null
||
ilstAtom
==
null
||
AtomParsers
.
parseHdlr
(
hdlrAtom
.
data
)
!=
TYPE_mdta
)
{
// There isn't enough information to parse the metadata, or the handler type is unexpected.
return
null
;
}
// Parse metadata keys.
ParsableByteArray
keys
=
keysAtom
.
data
;
keys
.
setPosition
(
Atom
.
FULL_HEADER_SIZE
);
int
entryCount
=
keys
.
readInt
();
String
[]
keyNames
=
new
String
[
entryCount
];
for
(
int
i
=
0
;
i
<
entryCount
;
i
++)
{
int
entrySize
=
keys
.
readInt
();
keys
.
skipBytes
(
4
);
// keyNamespace
int
keySize
=
entrySize
-
8
;
keyNames
[
i
]
=
keys
.
readString
(
keySize
);
}
// Parse metadata items.
ParsableByteArray
ilst
=
ilstAtom
.
data
;
ilst
.
setPosition
(
Atom
.
HEADER_SIZE
);
ArrayList
<
Metadata
.
Entry
>
entries
=
new
ArrayList
<>();
while
(
ilst
.
bytesLeft
()
>
Atom
.
HEADER_SIZE
)
{
int
atomPosition
=
ilst
.
getPosition
();
int
atomSize
=
ilst
.
readInt
();
int
keyIndex
=
ilst
.
readInt
()
-
1
;
if
(
keyIndex
>=
0
&&
keyIndex
<
keyNames
.
length
)
{
String
key
=
keyNames
[
keyIndex
];
Metadata
.
Entry
entry
=
MetadataUtil
.
parseMdtaMetadataEntryFromIlst
(
ilst
,
atomPosition
+
atomSize
,
key
);
if
(
entry
!=
null
)
{
entries
.
add
(
entry
);
}
}
else
{
Log
.
w
(
TAG
,
"Skipped metadata with unknown key index: "
+
keyIndex
);
}
ilst
.
setPosition
(
atomPosition
+
atomSize
);
}
return
entries
.
isEmpty
()
?
null
:
new
Metadata
(
entries
);
}
@Nullable
private
static
Metadata
parseUdtaMeta
(
ParsableByteArray
meta
,
int
limit
)
{
meta
.
skipBytes
(
Atom
.
FULL_HEADER_SIZE
);
while
(
meta
.
getPosition
()
<
limit
)
{
int
atomPosition
=
meta
.
getPosition
();
...
...
@@ -516,11 +574,12 @@ import java.util.List;
meta
.
setPosition
(
atomPosition
);
return
parseIlst
(
meta
,
atomPosition
+
atomSize
);
}
meta
.
s
kipBytes
(
atomSize
-
Atom
.
HEADER_SIZE
);
meta
.
s
etPosition
(
atomPosition
+
atomSize
);
}
return
null
;
}
@Nullable
private
static
Metadata
parseIlst
(
ParsableByteArray
ilst
,
int
limit
)
{
ilst
.
skipBytes
(
Atom
.
HEADER_SIZE
);
ArrayList
<
Metadata
.
Entry
>
entries
=
new
ArrayList
<>();
...
...
@@ -610,19 +669,22 @@ import java.util.List;
* Parses an hdlr atom.
*
* @param hdlr The hdlr atom to decode.
* @return The
track typ
e.
* @return The
handler valu
e.
*/
private
static
int
parseHdlr
(
ParsableByteArray
hdlr
)
{
hdlr
.
setPosition
(
Atom
.
FULL_HEADER_SIZE
+
4
);
int
trackType
=
hdlr
.
readInt
();
if
(
trackType
==
TYPE_soun
)
{
return
hdlr
.
readInt
();
}
/** Returns the track type for a given handler value. */
private
static
int
getTrackTypeForHdlr
(
int
hdlr
)
{
if
(
hdlr
==
TYPE_soun
)
{
return
C
.
TRACK_TYPE_AUDIO
;
}
else
if
(
trackType
==
TYPE_vide
)
{
}
else
if
(
hdlr
==
TYPE_vide
)
{
return
C
.
TRACK_TYPE_VIDEO
;
}
else
if
(
trackType
==
TYPE_text
||
trackType
==
TYPE_sbtl
||
trackType
==
TYPE_subt
||
trackType
==
TYPE_clcp
)
{
}
else
if
(
hdlr
==
TYPE_text
||
hdlr
==
TYPE_sbtl
||
hdlr
==
TYPE_subt
||
hdlr
==
TYPE_clcp
)
{
return
C
.
TRACK_TYPE_TEXT
;
}
else
if
(
trackType
==
TYPE_meta
)
{
}
else
if
(
hdlr
==
TYPE_meta
)
{
return
C
.
TRACK_TYPE_METADATA
;
}
else
{
return
C
.
TRACK_TYPE_UNKNOWN
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntry.java
0 → 100644
View file @
ed1f41db
/*
* 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.
*/
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
android.os.Parcel
;
import
android.os.Parcelable
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
java.util.Arrays
;
/**
* Stores extensible metadata with handler type 'mdta'. See also the QuickTime File Format
* Specification.
*/
public
final
class
MdtaMetadataEntry
implements
Metadata
.
Entry
{
/** The metadata key name. */
public
final
String
key
;
/** The payload. The interpretation of the value depends on {@link #typeIndicator}. */
public
final
byte
[]
value
;
/** The four byte locale indicator. */
public
final
int
localeIndicator
;
/** The four byte type indicator. */
public
final
int
typeIndicator
;
/** Creates a new metadata entry for the specified metadata key/value. */
public
MdtaMetadataEntry
(
String
key
,
byte
[]
value
,
int
localeIndicator
,
int
typeIndicator
)
{
this
.
key
=
key
;
this
.
value
=
value
;
this
.
localeIndicator
=
localeIndicator
;
this
.
typeIndicator
=
typeIndicator
;
}
private
MdtaMetadataEntry
(
Parcel
in
)
{
key
=
in
.
readString
();
value
=
new
byte
[
in
.
readInt
()];
in
.
readByteArray
(
value
);
localeIndicator
=
in
.
readInt
();
typeIndicator
=
in
.
readInt
();
}
@Override
public
boolean
equals
(
@Nullable
Object
obj
)
{
if
(
this
==
obj
)
{
return
true
;
}
if
(
obj
==
null
||
getClass
()
!=
obj
.
getClass
())
{
return
false
;
}
MdtaMetadataEntry
other
=
(
MdtaMetadataEntry
)
obj
;
return
key
.
equals
(
other
.
key
)
&&
Arrays
.
equals
(
value
,
other
.
value
)
&&
localeIndicator
==
other
.
localeIndicator
&&
typeIndicator
==
other
.
typeIndicator
;
}
@Override
public
int
hashCode
()
{
int
result
=
17
;
result
=
31
*
result
+
key
.
hashCode
();
result
=
31
*
result
+
Arrays
.
hashCode
(
value
);
result
=
31
*
result
+
localeIndicator
;
result
=
31
*
result
+
typeIndicator
;
return
result
;
}
@Override
public
String
toString
()
{
return
"mdta: key="
+
key
;
}
// Parcelable implementation.
@Override
public
void
writeToParcel
(
Parcel
dest
,
int
flags
)
{
dest
.
writeString
(
key
);
dest
.
writeInt
(
value
.
length
);
dest
.
writeByteArray
(
value
);
dest
.
writeInt
(
localeIndicator
);
dest
.
writeInt
(
typeIndicator
);
}
@Override
public
int
describeContents
()
{
return
0
;
}
public
static
final
Parcelable
.
Creator
<
MdtaMetadataEntry
>
CREATOR
=
new
Parcelable
.
Creator
<
MdtaMetadataEntry
>()
{
@Override
public
MdtaMetadataEntry
createFromParcel
(
Parcel
in
)
{
return
new
MdtaMetadataEntry
(
in
);
}
@Override
public
MdtaMetadataEntry
[]
newArray
(
int
size
)
{
return
new
MdtaMetadataEntry
[
size
];
}
};
}
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
View file @
ed1f41db
...
...
@@ -16,6 +16,9 @@
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.GaplessInfoHolder
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
...
...
@@ -25,10 +28,9 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
import
java.nio.ByteBuffer
;
/**
* Parses metadata items stored in ilst atoms.
*/
/** Utilities for handling metadata in MP4. */
/* package */
final
class
MetadataUtil
{
private
static
final
String
TAG
=
"MetadataUtil"
;
...
...
@@ -106,17 +108,64 @@ import com.google.android.exoplayer2.util.Util;
private
static
final
int
TYPE_TOP_BYTE_COPYRIGHT
=
0xA9
;
private
static
final
int
TYPE_TOP_BYTE_REPLACEMENT
=
0xFD
;
// Truncated value of \uFFFD.
private
static
final
String
MDTA_KEY_ANDROID_CAPTURE_FPS
=
"com.android.capture.fps"
;
private
static
final
int
MDTA_TYPE_INDICATOR_FLOAT
=
23
;
private
MetadataUtil
()
{}
/**
* Parses a single ilst element from a {@link ParsableByteArray}. The element is read starting
* from the current position of the {@link ParsableByteArray}, and the position is advanced by the
* size of the element. The position is advanced even if the element's type is unrecognized.
* Returns a {@link Format} that is the same as the input format but includes information from the
* specified sources of metadata.
*/
public
static
Format
getFormatWithMetadata
(
int
trackType
,
Format
format
,
@Nullable
Metadata
udtaMetadata
,
@Nullable
Metadata
mdtaMetadata
,
GaplessInfoHolder
gaplessInfoHolder
)
{
if
(
trackType
==
C
.
TRACK_TYPE_AUDIO
)
{
if
(
gaplessInfoHolder
.
hasGaplessInfo
())
{
format
=
format
.
copyWithGaplessInfo
(
gaplessInfoHolder
.
encoderDelay
,
gaplessInfoHolder
.
encoderPadding
);
}
// We assume all udta metadata is associated with the audio track.
if
(
udtaMetadata
!=
null
)
{
format
=
format
.
copyWithMetadata
(
udtaMetadata
);
}
}
else
if
(
trackType
==
C
.
TRACK_TYPE_VIDEO
&&
mdtaMetadata
!=
null
)
{
// Populate only metadata keys that are known to be specific to video.
for
(
int
i
=
0
;
i
<
mdtaMetadata
.
length
();
i
++)
{
Metadata
.
Entry
entry
=
mdtaMetadata
.
get
(
i
);
if
(
entry
instanceof
MdtaMetadataEntry
)
{
MdtaMetadataEntry
mdtaMetadataEntry
=
(
MdtaMetadataEntry
)
entry
;
if
(
MDTA_KEY_ANDROID_CAPTURE_FPS
.
equals
(
mdtaMetadataEntry
.
key
)
&&
mdtaMetadataEntry
.
typeIndicator
==
MDTA_TYPE_INDICATOR_FLOAT
)
{
try
{
float
fps
=
ByteBuffer
.
wrap
(
mdtaMetadataEntry
.
value
).
asFloatBuffer
().
get
();
format
=
format
.
copyWithFrameRate
(
fps
);
format
=
format
.
copyWithMetadata
(
new
Metadata
(
mdtaMetadataEntry
));
}
catch
(
NumberFormatException
e
)
{
Log
.
w
(
TAG
,
"Ignoring invalid framerate"
);
}
}
}
}
}
return
format
;
}
/**
* Parses a single userdata ilst element from a {@link ParsableByteArray}. The element is read
* starting from the current position of the {@link ParsableByteArray}, and the position is
* advanced by the size of the element. The position is advanced even if the element's type is
* unrecognized.
*
* @param ilst Holds the data to be parsed.
* @return The parsed element, or null if the element's type was not recognized.
*/
public
static
@Nullable
Metadata
.
Entry
parseIlstElement
(
ParsableByteArray
ilst
)
{
@Nullable
public
static
Metadata
.
Entry
parseIlstElement
(
ParsableByteArray
ilst
)
{
int
position
=
ilst
.
getPosition
();
int
endPosition
=
position
+
ilst
.
readInt
();
int
type
=
ilst
.
readInt
();
...
...
@@ -187,7 +236,36 @@ import com.google.android.exoplayer2.util.Util;
}
}
private
static
@Nullable
TextInformationFrame
parseTextAttribute
(
/**
* Parses an 'mdta' metadata entry starting at the current position in an ilst box.
*
* @param ilst The ilst box.
* @param endPosition The end position of the entry in the ilst box.
* @param key The mdta metadata entry key for the entry.
* @return The parsed element, or null if the entry wasn't recognized.
*/
@Nullable
public
static
MdtaMetadataEntry
parseMdtaMetadataEntryFromIlst
(
ParsableByteArray
ilst
,
int
endPosition
,
String
key
)
{
int
atomPosition
;
while
((
atomPosition
=
ilst
.
getPosition
())
<
endPosition
)
{
int
atomSize
=
ilst
.
readInt
();
int
atomType
=
ilst
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
int
typeIndicator
=
ilst
.
readInt
();
int
localeIndicator
=
ilst
.
readInt
();
int
dataSize
=
atomSize
-
16
;
byte
[]
value
=
new
byte
[
dataSize
];
ilst
.
readBytes
(
value
,
0
,
dataSize
);
return
new
MdtaMetadataEntry
(
key
,
value
,
localeIndicator
,
typeIndicator
);
}
ilst
.
setPosition
(
atomPosition
+
atomSize
);
}
return
null
;
}
@Nullable
private
static
TextInformationFrame
parseTextAttribute
(
int
type
,
String
id
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
...
...
@@ -200,7 +278,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
CommentFrame
parseCommentAttribute
(
int
type
,
ParsableByteArray
data
)
{
@Nullable
private
static
CommentFrame
parseCommentAttribute
(
int
type
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
...
...
@@ -212,7 +291,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
Id3Frame
parseUint8Attribute
(
@Nullable
private
static
Id3Frame
parseUint8Attribute
(
int
type
,
String
id
,
ParsableByteArray
data
,
...
...
@@ -231,7 +311,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
TextInformationFrame
parseIndexAndCountAttribute
(
@Nullable
private
static
TextInformationFrame
parseIndexAndCountAttribute
(
int
type
,
String
attributeName
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
...
...
@@ -251,8 +332,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
TextInformationFrame
parseStandardGenreAttribute
(
ParsableByteArray
data
)
{
@Nullable
private
static
TextInformationFrame
parseStandardGenreAttribute
(
ParsableByteArray
data
)
{
int
genreCode
=
parseUint8AttributeValue
(
data
);
String
genreString
=
(
0
<
genreCode
&&
genreCode
<=
STANDARD_GENRES
.
length
)
?
STANDARD_GENRES
[
genreCode
-
1
]
:
null
;
...
...
@@ -263,7 +344,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
ApicFrame
parseCoverArt
(
ParsableByteArray
data
)
{
@Nullable
private
static
ApicFrame
parseCoverArt
(
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
...
...
@@ -287,8 +369,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
Id3Frame
parseInternalAttribute
(
ParsableByteArray
data
,
int
endPosition
)
{
@Nullable
private
static
Id3Frame
parseInternalAttribute
(
ParsableByteArray
data
,
int
endPosition
)
{
String
domain
=
null
;
String
name
=
null
;
int
dataAtomPosition
=
-
1
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
View file @
ed1f41db
...
...
@@ -75,7 +75,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private
static
final
int
STATE_READING_ATOM_PAYLOAD
=
1
;
private
static
final
int
STATE_READING_SAMPLE
=
2
;
/
/ Brand stored in the ftyp atom for QuickTime media.
/
** Brand stored in the ftyp atom for QuickTime media. */
private
static
final
int
BRAND_QUICKTIME
=
Util
.
getIntegerCodeForString
(
"qt "
);
/**
...
...
@@ -377,15 +377,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
long
durationUs
=
C
.
TIME_UNSET
;
List
<
Mp4Track
>
tracks
=
new
ArrayList
<>();
Metadata
metadata
=
null
;
// Process metadata.
Metadata
udtaMetadata
=
null
;
GaplessInfoHolder
gaplessInfoHolder
=
new
GaplessInfoHolder
();
Atom
.
LeafAtom
udta
=
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_udta
);
if
(
udta
!=
null
)
{
m
etadata
=
AtomParsers
.
parseUdta
(
udta
,
isQuickTime
);
if
(
m
etadata
!=
null
)
{
gaplessInfoHolder
.
setFromMetadata
(
m
etadata
);
udtaM
etadata
=
AtomParsers
.
parseUdta
(
udta
,
isQuickTime
);
if
(
udtaM
etadata
!=
null
)
{
gaplessInfoHolder
.
setFromMetadata
(
udtaM
etadata
);
}
}
Metadata
mdtaMetadata
=
null
;
Atom
.
ContainerAtom
meta
=
moov
.
getContainerAtomOfType
(
Atom
.
TYPE_meta
);
if
(
meta
!=
null
)
{
mdtaMetadata
=
AtomParsers
.
parseMdtaFromMeta
(
meta
);
}
boolean
ignoreEditLists
=
(
flags
&
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
)
!=
0
;
ArrayList
<
TrackSampleTable
>
trackSampleTables
=
...
...
@@ -401,15 +407,9 @@ public final class Mp4Extractor implements Extractor, SeekMap {
// Allow ten source samples per output sample, like the platform extractor.
int
maxInputSize
=
trackSampleTable
.
maximumSize
+
3
*
10
;
Format
format
=
track
.
format
.
copyWithMaxInputSize
(
maxInputSize
);
if
(
track
.
type
==
C
.
TRACK_TYPE_AUDIO
)
{
if
(
gaplessInfoHolder
.
hasGaplessInfo
())
{
format
=
format
.
copyWithGaplessInfo
(
gaplessInfoHolder
.
encoderDelay
,
gaplessInfoHolder
.
encoderPadding
);
}
if
(
metadata
!=
null
)
{
format
=
format
.
copyWithMetadata
(
metadata
);
}
}
format
=
MetadataUtil
.
getFormatWithMetadata
(
track
.
type
,
format
,
udtaMetadata
,
mdtaMetadata
,
gaplessInfoHolder
);
mp4Track
.
trackOutput
.
format
(
format
);
durationUs
=
...
...
@@ -716,24 +716,37 @@ public final class Mp4Extractor implements Extractor, SeekMap {
return
false
;
}
/**
* Returns whether the extractor should decode a leaf atom with type {@code atom}.
*/
/** Returns whether the extractor should decode a leaf atom with type {@code atom}. */
private
static
boolean
shouldParseLeafAtom
(
int
atom
)
{
return
atom
==
Atom
.
TYPE_mdhd
||
atom
==
Atom
.
TYPE_mvhd
||
atom
==
Atom
.
TYPE_hdlr
||
atom
==
Atom
.
TYPE_stsd
||
atom
==
Atom
.
TYPE_stts
||
atom
==
Atom
.
TYPE_stss
||
atom
==
Atom
.
TYPE_ctts
||
atom
==
Atom
.
TYPE_elst
||
atom
==
Atom
.
TYPE_stsc
||
atom
==
Atom
.
TYPE_stsz
||
atom
==
Atom
.
TYPE_stz2
||
atom
==
Atom
.
TYPE_stco
||
atom
==
Atom
.
TYPE_co64
||
atom
==
Atom
.
TYPE_tkhd
||
atom
==
Atom
.
TYPE_ftyp
||
atom
==
Atom
.
TYPE_udta
;
return
atom
==
Atom
.
TYPE_mdhd
||
atom
==
Atom
.
TYPE_mvhd
||
atom
==
Atom
.
TYPE_hdlr
||
atom
==
Atom
.
TYPE_stsd
||
atom
==
Atom
.
TYPE_stts
||
atom
==
Atom
.
TYPE_stss
||
atom
==
Atom
.
TYPE_ctts
||
atom
==
Atom
.
TYPE_elst
||
atom
==
Atom
.
TYPE_stsc
||
atom
==
Atom
.
TYPE_stsz
||
atom
==
Atom
.
TYPE_stz2
||
atom
==
Atom
.
TYPE_stco
||
atom
==
Atom
.
TYPE_co64
||
atom
==
Atom
.
TYPE_tkhd
||
atom
==
Atom
.
TYPE_ftyp
||
atom
==
Atom
.
TYPE_udta
||
atom
==
Atom
.
TYPE_keys
||
atom
==
Atom
.
TYPE_ilst
;
}
/**
* Returns whether the extractor should decode a container atom with type {@code atom}.
*/
/** Returns whether the extractor should decode a container atom with type {@code atom}. */
private
static
boolean
shouldParseContainerAtom
(
int
atom
)
{
return
atom
==
Atom
.
TYPE_moov
||
atom
==
Atom
.
TYPE_trak
||
atom
==
Atom
.
TYPE_mdia
||
atom
==
Atom
.
TYPE_minf
||
atom
==
Atom
.
TYPE_stbl
||
atom
==
Atom
.
TYPE_edts
;
return
atom
==
Atom
.
TYPE_moov
||
atom
==
Atom
.
TYPE_trak
||
atom
==
Atom
.
TYPE_mdia
||
atom
==
Atom
.
TYPE_minf
||
atom
==
Atom
.
TYPE_stbl
||
atom
==
Atom
.
TYPE_edts
||
atom
==
Atom
.
TYPE_meta
;
}
private
static
final
class
Mp4Track
{
...
...
library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntryTest.java
0 → 100644
View file @
ed1f41db
/*
* 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.
*/
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
android.os.Parcel
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
/** Test for {@link MdtaMetadataEntry}. */
@RunWith
(
RobolectricTestRunner
.
class
)
public
final
class
MdtaMetadataEntryTest
{
@Test
public
void
testParcelable
()
{
MdtaMetadataEntry
mdtaMetadataEntryToParcel
=
new
MdtaMetadataEntry
(
"test"
,
new
byte
[]
{
1
,
2
},
3
,
4
);
Parcel
parcel
=
Parcel
.
obtain
();
mdtaMetadataEntryToParcel
.
writeToParcel
(
parcel
,
0
);
parcel
.
setDataPosition
(
0
);
MdtaMetadataEntry
mdtaMetadataEntryFromParcel
=
MdtaMetadataEntry
.
CREATOR
.
createFromParcel
(
parcel
);
assertThat
(
mdtaMetadataEntryFromParcel
).
isEqualTo
(
mdtaMetadataEntryToParcel
);
parcel
.
recycle
();
}
}
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