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
a568fbdd
authored
Jan 04, 2019
by
andrewlewis
Committed by
Oliver Woodman
Jan 15, 2019
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Parse frame rate from 'mdta' metadata
PiperOrigin-RevId: 227813461
parent
4847889a
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
406 additions
and
59 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 @
a568fbdd
...
@@ -1181,6 +1181,37 @@ public final class Format implements Parcelable {
...
@@ -1181,6 +1181,37 @@ public final class Format implements Parcelable {
metadata
);
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
)
{
public
Format
copyWithDrmInitData
(
@Nullable
DrmInitData
drmInitData
)
{
return
new
Format
(
return
new
Format
(
id
,
id
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
View file @
a568fbdd
...
@@ -22,8 +22,8 @@ import java.util.ArrayList;
...
@@ -22,8 +22,8 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.List
;
@SuppressWarnings
(
"ConstantField"
)
@SuppressWarnings
(
{
"ConstantField"
,
"ConstantCaseForConstants"
}
)
/* package*/
abstract
class
Atom
{
/* package
*/
abstract
class
Atom
{
/**
/**
* Size of an atom header, in bytes.
* Size of an atom header, in bytes.
...
@@ -130,6 +130,7 @@ import java.util.List;
...
@@ -130,6 +130,7 @@ import java.util.List;
public
static
final
int
TYPE_sawb
=
Util
.
getIntegerCodeForString
(
"sawb"
);
public
static
final
int
TYPE_sawb
=
Util
.
getIntegerCodeForString
(
"sawb"
);
public
static
final
int
TYPE_udta
=
Util
.
getIntegerCodeForString
(
"udta"
);
public
static
final
int
TYPE_udta
=
Util
.
getIntegerCodeForString
(
"udta"
);
public
static
final
int
TYPE_meta
=
Util
.
getIntegerCodeForString
(
"meta"
);
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_ilst
=
Util
.
getIntegerCodeForString
(
"ilst"
);
public
static
final
int
TYPE_mean
=
Util
.
getIntegerCodeForString
(
"mean"
);
public
static
final
int
TYPE_mean
=
Util
.
getIntegerCodeForString
(
"mean"
);
public
static
final
int
TYPE_name
=
Util
.
getIntegerCodeForString
(
"name"
);
public
static
final
int
TYPE_name
=
Util
.
getIntegerCodeForString
(
"name"
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
a568fbdd
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.mp4;
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.mp4;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
MimeTypes
.
getMimeTypeFromMp4ObjectType
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
MimeTypes
.
getMimeTypeFromMp4ObjectType
;
import
android.support.annotation.Nullable
;
import
android.util.Pair
;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
...
@@ -39,7 +40,7 @@ import java.util.Collections;
...
@@ -39,7 +40,7 @@ import java.util.Collections;
import
java.util.List
;
import
java.util.List
;
/** Utility methods for parsing MP4 format atom payloads according to ISO 14496-12. */
/** Utility methods for parsing MP4 format atom payloads according to ISO 14496-12. */
@SuppressWarnings
(
"ConstantField"
)
@SuppressWarnings
(
{
"ConstantField"
,
"ConstantCaseForConstants"
}
)
/* package */
final
class
AtomParsers
{
/* package */
final
class
AtomParsers
{
private
static
final
String
TAG
=
"AtomParsers"
;
private
static
final
String
TAG
=
"AtomParsers"
;
...
@@ -51,6 +52,7 @@ import java.util.List;
...
@@ -51,6 +52,7 @@ import java.util.List;
private
static
final
int
TYPE_subt
=
Util
.
getIntegerCodeForString
(
"subt"
);
private
static
final
int
TYPE_subt
=
Util
.
getIntegerCodeForString
(
"subt"
);
private
static
final
int
TYPE_clcp
=
Util
.
getIntegerCodeForString
(
"clcp"
);
private
static
final
int
TYPE_clcp
=
Util
.
getIntegerCodeForString
(
"clcp"
);
private
static
final
int
TYPE_meta
=
Util
.
getIntegerCodeForString
(
"meta"
);
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
* 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;
...
@@ -77,7 +79,7 @@ import java.util.List;
DrmInitData
drmInitData
,
boolean
ignoreEditLists
,
boolean
isQuickTime
)
DrmInitData
drmInitData
,
boolean
ignoreEditLists
,
boolean
isQuickTime
)
throws
ParserException
{
throws
ParserException
{
Atom
.
ContainerAtom
mdia
=
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_mdia
);
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
)
{
if
(
trackType
==
C
.
TRACK_TYPE_UNKNOWN
)
{
return
null
;
return
null
;
}
}
...
@@ -485,6 +487,7 @@ import java.util.List;
...
@@ -485,6 +487,7 @@ import java.util.List;
* @param isQuickTime True for QuickTime media. False otherwise.
* @param isQuickTime True for QuickTime media. False otherwise.
* @return Parsed metadata, or null.
* @return Parsed metadata, or null.
*/
*/
@Nullable
public
static
Metadata
parseUdta
(
Atom
.
LeafAtom
udtaAtom
,
boolean
isQuickTime
)
{
public
static
Metadata
parseUdta
(
Atom
.
LeafAtom
udtaAtom
,
boolean
isQuickTime
)
{
if
(
isQuickTime
)
{
if
(
isQuickTime
)
{
// Meta boxes are regular boxes rather than full boxes in QuickTime. For now, don't try and
// 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;
...
@@ -499,14 +502,69 @@ import java.util.List;
int
atomType
=
udtaData
.
readInt
();
int
atomType
=
udtaData
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_meta
)
{
if
(
atomType
==
Atom
.
TYPE_meta
)
{
udtaData
.
setPosition
(
atomPosition
);
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
;
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
);
meta
.
skipBytes
(
Atom
.
FULL_HEADER_SIZE
);
while
(
meta
.
getPosition
()
<
limit
)
{
while
(
meta
.
getPosition
()
<
limit
)
{
int
atomPosition
=
meta
.
getPosition
();
int
atomPosition
=
meta
.
getPosition
();
...
@@ -516,11 +574,12 @@ import java.util.List;
...
@@ -516,11 +574,12 @@ import java.util.List;
meta
.
setPosition
(
atomPosition
);
meta
.
setPosition
(
atomPosition
);
return
parseIlst
(
meta
,
atomPosition
+
atomSize
);
return
parseIlst
(
meta
,
atomPosition
+
atomSize
);
}
}
meta
.
s
kipBytes
(
atomSize
-
Atom
.
HEADER_SIZE
);
meta
.
s
etPosition
(
atomPosition
+
atomSize
);
}
}
return
null
;
return
null
;
}
}
@Nullable
private
static
Metadata
parseIlst
(
ParsableByteArray
ilst
,
int
limit
)
{
private
static
Metadata
parseIlst
(
ParsableByteArray
ilst
,
int
limit
)
{
ilst
.
skipBytes
(
Atom
.
HEADER_SIZE
);
ilst
.
skipBytes
(
Atom
.
HEADER_SIZE
);
ArrayList
<
Metadata
.
Entry
>
entries
=
new
ArrayList
<>();
ArrayList
<
Metadata
.
Entry
>
entries
=
new
ArrayList
<>();
...
@@ -610,19 +669,22 @@ import java.util.List;
...
@@ -610,19 +669,22 @@ import java.util.List;
* Parses an hdlr atom.
* Parses an hdlr atom.
*
*
* @param hdlr The hdlr atom to decode.
* @param hdlr The hdlr atom to decode.
* @return The
track typ
e.
* @return The
handler valu
e.
*/
*/
private
static
int
parseHdlr
(
ParsableByteArray
hdlr
)
{
private
static
int
parseHdlr
(
ParsableByteArray
hdlr
)
{
hdlr
.
setPosition
(
Atom
.
FULL_HEADER_SIZE
+
4
);
hdlr
.
setPosition
(
Atom
.
FULL_HEADER_SIZE
+
4
);
int
trackType
=
hdlr
.
readInt
();
return
hdlr
.
readInt
();
if
(
trackType
==
TYPE_soun
)
{
}
/** Returns the track type for a given handler value. */
private
static
int
getTrackTypeForHdlr
(
int
hdlr
)
{
if
(
hdlr
==
TYPE_soun
)
{
return
C
.
TRACK_TYPE_AUDIO
;
return
C
.
TRACK_TYPE_AUDIO
;
}
else
if
(
trackType
==
TYPE_vide
)
{
}
else
if
(
hdlr
==
TYPE_vide
)
{
return
C
.
TRACK_TYPE_VIDEO
;
return
C
.
TRACK_TYPE_VIDEO
;
}
else
if
(
trackType
==
TYPE_text
||
trackType
==
TYPE_sbtl
||
trackType
==
TYPE_subt
}
else
if
(
hdlr
==
TYPE_text
||
hdlr
==
TYPE_sbtl
||
hdlr
==
TYPE_subt
||
hdlr
==
TYPE_clcp
)
{
||
trackType
==
TYPE_clcp
)
{
return
C
.
TRACK_TYPE_TEXT
;
return
C
.
TRACK_TYPE_TEXT
;
}
else
if
(
trackType
==
TYPE_meta
)
{
}
else
if
(
hdlr
==
TYPE_meta
)
{
return
C
.
TRACK_TYPE_METADATA
;
return
C
.
TRACK_TYPE_METADATA
;
}
else
{
}
else
{
return
C
.
TRACK_TYPE_UNKNOWN
;
return
C
.
TRACK_TYPE_UNKNOWN
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntry.java
0 → 100644
View file @
a568fbdd
/*
* 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 @
a568fbdd
...
@@ -16,6 +16,9 @@
...
@@ -16,6 +16,9 @@
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
android.support.annotation.Nullable
;
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.Metadata
;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
...
@@ -25,10 +28,9 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
...
@@ -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.Log
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
import
java.nio.ByteBuffer
;
/**
/** Utilities for handling metadata in MP4. */
* Parses metadata items stored in ilst atoms.
*/
/* package */
final
class
MetadataUtil
{
/* package */
final
class
MetadataUtil
{
private
static
final
String
TAG
=
"MetadataUtil"
;
private
static
final
String
TAG
=
"MetadataUtil"
;
...
@@ -106,17 +108,64 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -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_COPYRIGHT
=
0xA9
;
private
static
final
int
TYPE_TOP_BYTE_REPLACEMENT
=
0xFD
;
// Truncated value of \uFFFD.
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
()
{}
private
MetadataUtil
()
{}
/**
/**
* Parses a single ilst element from a {@link ParsableByteArray}. The element is read starting
* Returns a {@link Format} that is the same as the input format but includes information from the
* from the current position of the {@link ParsableByteArray}, and the position is advanced by the
* specified sources of metadata.
* size of the element. The position is advanced even if the element's type is unrecognized.
*/
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.
* @param ilst Holds the data to be parsed.
* @return The parsed element, or null if the element's type was not recognized.
* @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
position
=
ilst
.
getPosition
();
int
endPosition
=
position
+
ilst
.
readInt
();
int
endPosition
=
position
+
ilst
.
readInt
();
int
type
=
ilst
.
readInt
();
int
type
=
ilst
.
readInt
();
...
@@ -187,7 +236,36 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -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
type
,
String
id
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
...
@@ -200,7 +278,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -200,7 +278,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
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
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
if
(
atomType
==
Atom
.
TYPE_data
)
{
...
@@ -212,7 +291,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -212,7 +291,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
return
null
;
}
}
private
static
@Nullable
Id3Frame
parseUint8Attribute
(
@Nullable
private
static
Id3Frame
parseUint8Attribute
(
int
type
,
int
type
,
String
id
,
String
id
,
ParsableByteArray
data
,
ParsableByteArray
data
,
...
@@ -231,7 +311,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -231,7 +311,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
return
null
;
}
}
private
static
@Nullable
TextInformationFrame
parseIndexAndCountAttribute
(
@Nullable
private
static
TextInformationFrame
parseIndexAndCountAttribute
(
int
type
,
String
attributeName
,
ParsableByteArray
data
)
{
int
type
,
String
attributeName
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
...
@@ -251,8 +332,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -251,8 +332,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
return
null
;
}
}
private
static
@Nullable
TextInformationFrame
parseStandardGenreAttribute
(
@Nullable
ParsableByteArray
data
)
{
private
static
TextInformationFrame
parseStandardGenreAttribute
(
ParsableByteArray
data
)
{
int
genreCode
=
parseUint8AttributeValue
(
data
);
int
genreCode
=
parseUint8AttributeValue
(
data
);
String
genreString
=
(
0
<
genreCode
&&
genreCode
<=
STANDARD_GENRES
.
length
)
String
genreString
=
(
0
<
genreCode
&&
genreCode
<=
STANDARD_GENRES
.
length
)
?
STANDARD_GENRES
[
genreCode
-
1
]
:
null
;
?
STANDARD_GENRES
[
genreCode
-
1
]
:
null
;
...
@@ -263,7 +344,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -263,7 +344,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
return
null
;
}
}
private
static
@Nullable
ApicFrame
parseCoverArt
(
ParsableByteArray
data
)
{
@Nullable
private
static
ApicFrame
parseCoverArt
(
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
if
(
atomType
==
Atom
.
TYPE_data
)
{
...
@@ -287,8 +369,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -287,8 +369,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
return
null
;
}
}
private
static
@Nullable
Id3Frame
parseInternalAttribute
(
@Nullable
ParsableByteArray
data
,
int
endPosition
)
{
private
static
Id3Frame
parseInternalAttribute
(
ParsableByteArray
data
,
int
endPosition
)
{
String
domain
=
null
;
String
domain
=
null
;
String
name
=
null
;
String
name
=
null
;
int
dataAtomPosition
=
-
1
;
int
dataAtomPosition
=
-
1
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
View file @
a568fbdd
...
@@ -75,7 +75,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -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_ATOM_PAYLOAD
=
1
;
private
static
final
int
STATE_READING_SAMPLE
=
2
;
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 "
);
private
static
final
int
BRAND_QUICKTIME
=
Util
.
getIntegerCodeForString
(
"qt "
);
/**
/**
...
@@ -377,15 +377,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -377,15 +377,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
long
durationUs
=
C
.
TIME_UNSET
;
long
durationUs
=
C
.
TIME_UNSET
;
List
<
Mp4Track
>
tracks
=
new
ArrayList
<>();
List
<
Mp4Track
>
tracks
=
new
ArrayList
<>();
Metadata
metadata
=
null
;
// Process metadata.
Metadata
udtaMetadata
=
null
;
GaplessInfoHolder
gaplessInfoHolder
=
new
GaplessInfoHolder
();
GaplessInfoHolder
gaplessInfoHolder
=
new
GaplessInfoHolder
();
Atom
.
LeafAtom
udta
=
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_udta
);
Atom
.
LeafAtom
udta
=
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_udta
);
if
(
udta
!=
null
)
{
if
(
udta
!=
null
)
{
m
etadata
=
AtomParsers
.
parseUdta
(
udta
,
isQuickTime
);
udtaM
etadata
=
AtomParsers
.
parseUdta
(
udta
,
isQuickTime
);
if
(
m
etadata
!=
null
)
{
if
(
udtaM
etadata
!=
null
)
{
gaplessInfoHolder
.
setFromMetadata
(
m
etadata
);
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
;
boolean
ignoreEditLists
=
(
flags
&
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
)
!=
0
;
ArrayList
<
TrackSampleTable
>
trackSampleTables
=
ArrayList
<
TrackSampleTable
>
trackSampleTables
=
...
@@ -401,15 +407,9 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -401,15 +407,9 @@ public final class Mp4Extractor implements Extractor, SeekMap {
// Allow ten source samples per output sample, like the platform extractor.
// Allow ten source samples per output sample, like the platform extractor.
int
maxInputSize
=
trackSampleTable
.
maximumSize
+
3
*
10
;
int
maxInputSize
=
trackSampleTable
.
maximumSize
+
3
*
10
;
Format
format
=
track
.
format
.
copyWithMaxInputSize
(
maxInputSize
);
Format
format
=
track
.
format
.
copyWithMaxInputSize
(
maxInputSize
);
if
(
track
.
type
==
C
.
TRACK_TYPE_AUDIO
)
{
format
=
if
(
gaplessInfoHolder
.
hasGaplessInfo
())
{
MetadataUtil
.
getFormatWithMetadata
(
format
=
format
.
copyWithGaplessInfo
(
gaplessInfoHolder
.
encoderDelay
,
track
.
type
,
format
,
udtaMetadata
,
mdtaMetadata
,
gaplessInfoHolder
);
gaplessInfoHolder
.
encoderPadding
);
}
if
(
metadata
!=
null
)
{
format
=
format
.
copyWithMetadata
(
metadata
);
}
}
mp4Track
.
trackOutput
.
format
(
format
);
mp4Track
.
trackOutput
.
format
(
format
);
durationUs
=
durationUs
=
...
@@ -716,24 +716,37 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -716,24 +716,37 @@ public final class Mp4Extractor implements Extractor, SeekMap {
return
false
;
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
)
{
private
static
boolean
shouldParseLeafAtom
(
int
atom
)
{
return
atom
==
Atom
.
TYPE_mdhd
||
atom
==
Atom
.
TYPE_mvhd
||
atom
==
Atom
.
TYPE_hdlr
return
atom
==
Atom
.
TYPE_mdhd
||
atom
==
Atom
.
TYPE_stsd
||
atom
==
Atom
.
TYPE_stts
||
atom
==
Atom
.
TYPE_stss
||
atom
==
Atom
.
TYPE_mvhd
||
atom
==
Atom
.
TYPE_ctts
||
atom
==
Atom
.
TYPE_elst
||
atom
==
Atom
.
TYPE_stsc
||
atom
==
Atom
.
TYPE_hdlr
||
atom
==
Atom
.
TYPE_stsz
||
atom
==
Atom
.
TYPE_stz2
||
atom
==
Atom
.
TYPE_stco
||
atom
==
Atom
.
TYPE_stsd
||
atom
==
Atom
.
TYPE_co64
||
atom
==
Atom
.
TYPE_tkhd
||
atom
==
Atom
.
TYPE_ftyp
||
atom
==
Atom
.
TYPE_stts
||
atom
==
Atom
.
TYPE_udta
;
||
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
)
{
private
static
boolean
shouldParseContainerAtom
(
int
atom
)
{
return
atom
==
Atom
.
TYPE_moov
||
atom
==
Atom
.
TYPE_trak
||
atom
==
Atom
.
TYPE_mdia
return
atom
==
Atom
.
TYPE_moov
||
atom
==
Atom
.
TYPE_minf
||
atom
==
Atom
.
TYPE_stbl
||
atom
==
Atom
.
TYPE_edts
;
||
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
{
private
static
final
class
Mp4Track
{
...
...
library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntryTest.java
0 → 100644
View file @
a568fbdd
/*
* 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