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
8caaf0b5
authored
Oct 26, 2016
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Big cleanup of mp4 metadata extraction
parent
1b39d21e
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
369 additions
and
402 deletions
demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java
library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
library/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
library/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Util.java
library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java
demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java
View file @
8caaf0b5
...
...
@@ -154,6 +154,18 @@ import java.util.Locale;
}
Log
.
d
(
TAG
,
" ]"
);
}
// Log metadata for at most one of the tracks selected for the renderer.
if
(
trackSelection
!=
null
)
{
for
(
int
selectionIndex
=
0
;
selectionIndex
<
trackSelection
.
length
();
selectionIndex
++)
{
Metadata
metadata
=
trackSelection
.
getFormat
(
selectionIndex
).
metadata
;
if
(
metadata
!=
null
)
{
Log
.
d
(
TAG
,
" Metadata ["
);
printMetadata
(
metadata
,
" "
);
Log
.
d
(
TAG
,
" ]"
);
break
;
}
}
}
Log
.
d
(
TAG
,
" ]"
);
}
}
...
...
@@ -184,7 +196,7 @@ import java.util.Locale;
@Override
public
void
onMetadata
(
Metadata
metadata
)
{
Log
.
d
(
TAG
,
"onMetadata ["
);
printMetadata
(
metadata
);
printMetadata
(
metadata
,
" "
);
Log
.
d
(
TAG
,
"]"
);
}
...
...
@@ -208,13 +220,8 @@ import java.util.Locale;
@Override
public
void
onAudioInputFormatChanged
(
Format
format
)
{
boolean
hasMetadata
=
format
.
metadata
!=
null
;
Log
.
d
(
TAG
,
"audioFormatChanged ["
+
getSessionTimeString
()
+
", "
+
getFormatString
(
format
)
+
(
hasMetadata
?
""
:
"]"
));
if
(
hasMetadata
)
{
printMetadata
(
format
.
metadata
);
Log
.
d
(
TAG
,
"]"
);
}
+
"]"
);
}
@Override
...
...
@@ -335,35 +342,35 @@ import java.util.Locale;
Log
.
e
(
TAG
,
"internalError ["
+
getSessionTimeString
()
+
", "
+
type
+
"]"
,
e
);
}
private
void
printMetadata
(
Metadata
metadata
)
{
private
void
printMetadata
(
Metadata
metadata
,
String
prefix
)
{
for
(
int
i
=
0
;
i
<
metadata
.
length
();
i
++)
{
Metadata
.
Entry
entry
=
metadata
.
get
(
i
);
if
(
entry
instanceof
TxxxFrame
)
{
TxxxFrame
txxxFrame
=
(
TxxxFrame
)
entry
;
Log
.
d
(
TAG
,
String
.
format
(
"
%s: description=%s, value=%s"
,
txxxFrame
.
id
,
Log
.
d
(
TAG
,
prefix
+
String
.
format
(
"
%s: description=%s, value=%s"
,
txxxFrame
.
id
,
txxxFrame
.
description
,
txxxFrame
.
value
));
}
else
if
(
entry
instanceof
PrivFrame
)
{
PrivFrame
privFrame
=
(
PrivFrame
)
entry
;
Log
.
d
(
TAG
,
String
.
format
(
"
%s: owner=%s"
,
privFrame
.
id
,
privFrame
.
owner
));
Log
.
d
(
TAG
,
prefix
+
String
.
format
(
"
%s: owner=%s"
,
privFrame
.
id
,
privFrame
.
owner
));
}
else
if
(
entry
instanceof
GeobFrame
)
{
GeobFrame
geobFrame
=
(
GeobFrame
)
entry
;
Log
.
d
(
TAG
,
String
.
format
(
"
%s: mimeType=%s, filename=%s, description=%s"
,
Log
.
d
(
TAG
,
prefix
+
String
.
format
(
"
%s: mimeType=%s, filename=%s, description=%s"
,
geobFrame
.
id
,
geobFrame
.
mimeType
,
geobFrame
.
filename
,
geobFrame
.
description
));
}
else
if
(
entry
instanceof
ApicFrame
)
{
ApicFrame
apicFrame
=
(
ApicFrame
)
entry
;
Log
.
d
(
TAG
,
String
.
format
(
"
%s: mimeType=%s, description=%s"
,
Log
.
d
(
TAG
,
prefix
+
String
.
format
(
"
%s: mimeType=%s, description=%s"
,
apicFrame
.
id
,
apicFrame
.
mimeType
,
apicFrame
.
description
));
}
else
if
(
entry
instanceof
TextInformationFrame
)
{
TextInformationFrame
textInformationFrame
=
(
TextInformationFrame
)
entry
;
Log
.
d
(
TAG
,
String
.
format
(
"
%s: description=%s"
,
textInformationFrame
.
id
,
Log
.
d
(
TAG
,
prefix
+
String
.
format
(
"
%s: description=%s"
,
textInformationFrame
.
id
,
textInformationFrame
.
description
));
}
else
if
(
entry
instanceof
CommentFrame
)
{
CommentFrame
commentFrame
=
(
CommentFrame
)
entry
;
Log
.
d
(
TAG
,
String
.
format
(
"
%s: language=%s description=%s"
,
commentFrame
.
id
,
commentFrame
.
language
,
commentFrame
.
description
));
Log
.
d
(
TAG
,
prefix
+
String
.
format
(
"
%s: language=%s description=%s"
,
commentFrame
.
id
,
commentFrame
.
language
,
commentFrame
.
description
,
commentFrame
.
text
));
}
else
if
(
entry
instanceof
Id3Frame
)
{
Id3Frame
id3Frame
=
(
Id3Frame
)
entry
;
Log
.
d
(
TAG
,
String
.
format
(
"
%s"
,
id3Frame
.
id
));
Log
.
d
(
TAG
,
prefix
+
String
.
format
(
"
%s"
,
id3Frame
.
id
));
}
}
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
View file @
8caaf0b5
...
...
@@ -132,7 +132,6 @@ import java.util.List;
public
static
final
int
TYPE_vp08
=
Util
.
getIntegerCodeForString
(
"vp08"
);
public
static
final
int
TYPE_vp09
=
Util
.
getIntegerCodeForString
(
"vp09"
);
public
static
final
int
TYPE_vpcC
=
Util
.
getIntegerCodeForString
(
"vpcC"
);
public
static
final
int
TYPE_DASHES
=
Util
.
getIntegerCodeForString
(
"----"
);
public
final
int
type
;
...
...
@@ -299,7 +298,7 @@ import java.util.List;
* @return The corresponding four character string.
*/
public
static
String
getAtomTypeString
(
int
type
)
{
return
""
+
(
char
)
(
type
>>
24
)
return
""
+
(
char
)
(
(
type
>>
24
)
&
0xFF
)
+
(
char
)
((
type
>>
16
)
&
0xFF
)
+
(
char
)
((
type
>>
8
)
&
0xFF
)
+
(
char
)
(
type
&
0xFF
);
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
8caaf0b5
...
...
@@ -24,11 +24,6 @@ import com.google.android.exoplayer2.audio.Ac3Util;
import
com.google.android.exoplayer2.drm.DrmInitData
;
import
com.google.android.exoplayer2.extractor.GaplessInfoHolder
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.id3.BinaryFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
import
com.google.android.exoplayer2.metadata.id3.Id3Frame
;
import
com.google.android.exoplayer2.metadata.id3.Id3Util
;
import
com.google.android.exoplayer2.metadata.id3.TextInformationFrame
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.CodecSpecificDataUtil
;
import
com.google.android.exoplayer2.util.MimeTypes
;
...
...
@@ -418,334 +413,43 @@ import java.util.List;
ParsableByteArray
udtaData
=
udtaAtom
.
data
;
udtaData
.
setPosition
(
Atom
.
HEADER_SIZE
);
while
(
udtaData
.
bytesLeft
()
>=
Atom
.
HEADER_SIZE
)
{
int
atomPosition
=
udtaData
.
getPosition
();
int
atomSize
=
udtaData
.
readInt
();
int
atomType
=
udtaData
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_meta
)
{
udtaData
.
setPosition
(
udtaData
.
getPosition
()
-
Atom
.
HEADER_SIZE
);
udtaData
.
setLimit
(
udtaData
.
getPosition
()
+
atomSize
);
return
parseMetaAtom
(
udtaData
);
udtaData
.
setPosition
(
atomPosition
);
return
parseMetaAtom
(
udtaData
,
atomPosition
+
atomSize
);
}
udtaData
.
skipBytes
(
atomSize
-
Atom
.
HEADER_SIZE
);
}
return
null
;
}
private
static
Metadata
parseMetaAtom
(
ParsableByteArray
data
)
{
da
ta
.
skipBytes
(
Atom
.
FULL_HEADER_SIZE
);
ParsableByteArray
ilst
=
new
ParsableByteArray
();
while
(
data
.
bytesLeft
()
>=
Atom
.
HEADER_SIZE
)
{
int
payloadSize
=
data
.
readInt
()
-
Atom
.
HEADER_SIZE
;
int
atomType
=
da
ta
.
readInt
();
private
static
Metadata
parseMetaAtom
(
ParsableByteArray
meta
,
int
limit
)
{
me
ta
.
skipBytes
(
Atom
.
FULL_HEADER_SIZE
);
while
(
meta
.
getPosition
()
<
limit
)
{
int
atomPosition
=
meta
.
getPosition
();
int
atomSize
=
meta
.
readInt
()
;
int
atomType
=
me
ta
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_ilst
)
{
ilst
.
reset
(
data
.
data
,
data
.
getPosition
()
+
payloadSize
);
ilst
.
setPosition
(
data
.
getPosition
());
Metadata
metadata
=
parseIlst
(
ilst
);
if
(
metadata
!=
null
)
{
return
metadata
;
}
meta
.
setPosition
(
atomPosition
);
return
parseIlst
(
meta
,
atomPosition
+
atomSize
);
}
data
.
skipBytes
(
payloadSize
);
meta
.
skipBytes
(
atomSize
-
Atom
.
HEADER_SIZE
);
}
return
null
;
}
private
static
Metadata
parseIlst
(
ParsableByteArray
ilst
)
{
private
static
Metadata
parseIlst
(
ParsableByteArray
ilst
,
int
limit
)
{
ilst
.
skipBytes
(
Atom
.
HEADER_SIZE
);
ArrayList
<
Metadata
.
Entry
>
entries
=
new
ArrayList
<>();
while
(
ilst
.
bytesLeft
()
>
0
)
{
int
position
=
ilst
.
getPosition
();
int
endPosition
=
position
+
ilst
.
readInt
();
int
type
=
ilst
.
readInt
();
parseIlstElement
(
ilst
,
type
,
endPosition
,
entries
);
ilst
.
setPosition
(
endPosition
);
}
return
entries
.
isEmpty
()
?
null
:
new
Metadata
(
entries
);
}
private
static
final
String
P1
=
"\u00a9"
;
private
static
final
String
P2
=
"\ufffd"
;
private
static
final
int
TYPE_NAME_1
=
Util
.
getIntegerCodeForString
(
P1
+
"nam"
);
private
static
final
int
TYPE_NAME_2
=
Util
.
getIntegerCodeForString
(
P2
+
"nam"
);
private
static
final
int
TYPE_NAME_3
=
Util
.
getIntegerCodeForString
(
P1
+
"trk"
);
private
static
final
int
TYPE_NAME_4
=
Util
.
getIntegerCodeForString
(
P2
+
"trk"
);
private
static
final
int
TYPE_COMMENT_1
=
Util
.
getIntegerCodeForString
(
P1
+
"cmt"
);
private
static
final
int
TYPE_COMMENT_2
=
Util
.
getIntegerCodeForString
(
P2
+
"cmt"
);
private
static
final
int
TYPE_YEAR_1
=
Util
.
getIntegerCodeForString
(
P1
+
"day"
);
private
static
final
int
TYPE_YEAR_2
=
Util
.
getIntegerCodeForString
(
P2
+
"day"
);
private
static
final
int
TYPE_ARTIST_1
=
Util
.
getIntegerCodeForString
(
P1
+
"ART"
);
private
static
final
int
TYPE_ARTIST_2
=
Util
.
getIntegerCodeForString
(
P2
+
"ART"
);
private
static
final
int
TYPE_ENCODER_1
=
Util
.
getIntegerCodeForString
(
P1
+
"too"
);
private
static
final
int
TYPE_ENCODER_2
=
Util
.
getIntegerCodeForString
(
P2
+
"too"
);
private
static
final
int
TYPE_ALBUM_1
=
Util
.
getIntegerCodeForString
(
P1
+
"alb"
);
private
static
final
int
TYPE_ALBUM_2
=
Util
.
getIntegerCodeForString
(
P2
+
"alb"
);
private
static
final
int
TYPE_COMPOSER_1
=
Util
.
getIntegerCodeForString
(
P1
+
"com"
);
private
static
final
int
TYPE_COMPOSER_2
=
Util
.
getIntegerCodeForString
(
P2
+
"com"
);
private
static
final
int
TYPE_COMPOSER_3
=
Util
.
getIntegerCodeForString
(
P1
+
"wrt"
);
private
static
final
int
TYPE_COMPOSER_4
=
Util
.
getIntegerCodeForString
(
P2
+
"wrt"
);
private
static
final
int
TYPE_LYRICS_1
=
Util
.
getIntegerCodeForString
(
P1
+
"lyr"
);
private
static
final
int
TYPE_LYRICS_2
=
Util
.
getIntegerCodeForString
(
P2
+
"lyr"
);
private
static
final
int
TYPE_GENRE_1
=
Util
.
getIntegerCodeForString
(
P1
+
"gen"
);
private
static
final
int
TYPE_GENRE_2
=
Util
.
getIntegerCodeForString
(
P2
+
"gen"
);
private
static
final
int
TYPE_STANDARD_GENRE
=
Util
.
getIntegerCodeForString
(
"gnre"
);
private
static
final
int
TYPE_GROUPING_1
=
Util
.
getIntegerCodeForString
(
P1
+
"grp"
);
private
static
final
int
TYPE_GROUPING_2
=
Util
.
getIntegerCodeForString
(
P2
+
"grp"
);
private
static
final
int
TYPE_DISK_NUMBER
=
Util
.
getIntegerCodeForString
(
"disk"
);
private
static
final
int
TYPE_TRACK_NUMBER
=
Util
.
getIntegerCodeForString
(
"trkn"
);
private
static
final
int
TYPE_TEMPO
=
Util
.
getIntegerCodeForString
(
"tmpo"
);
private
static
final
int
TYPE_COMPILATION
=
Util
.
getIntegerCodeForString
(
"cpil"
);
private
static
final
int
TYPE_ALBUM_ARTIST
=
Util
.
getIntegerCodeForString
(
"aART"
);
private
static
final
int
TYPE_SORT_TRACK_NAME
=
Util
.
getIntegerCodeForString
(
"sonm"
);
private
static
final
int
TYPE_SORT_ALBUM
=
Util
.
getIntegerCodeForString
(
"soal"
);
private
static
final
int
TYPE_SORT_ARTIST
=
Util
.
getIntegerCodeForString
(
"soar"
);
private
static
final
int
TYPE_SORT_ALBUM_ARTIST
=
Util
.
getIntegerCodeForString
(
"soaa"
);
private
static
final
int
TYPE_SORT_COMPOSER
=
Util
.
getIntegerCodeForString
(
"soco"
);
private
static
final
int
TYPE_SORT_SHOW
=
Util
.
getIntegerCodeForString
(
"sosn"
);
private
static
final
int
TYPE_GAPLESS_ALBUM
=
Util
.
getIntegerCodeForString
(
"pgap"
);
private
static
final
int
TYPE_SHOW
=
Util
.
getIntegerCodeForString
(
"tvsh"
);
// TBD: covr = cover art, various account and iTunes specific attributes, more TV attributes
private
static
void
parseIlstElement
(
ParsableByteArray
ilst
,
int
type
,
int
endPosition
,
List
<
Metadata
.
Entry
>
builder
)
{
if
(
type
==
TYPE_NAME_1
||
type
==
TYPE_NAME_2
||
type
==
TYPE_NAME_3
||
type
==
TYPE_NAME_4
)
{
parseTextAttribute
(
builder
,
"TIT2"
,
ilst
);
}
else
if
(
type
==
TYPE_COMMENT_1
||
type
==
TYPE_COMMENT_2
)
{
parseCommentAttribute
(
builder
,
"COMM"
,
ilst
);
}
else
if
(
type
==
TYPE_YEAR_1
||
type
==
TYPE_YEAR_2
)
{
parseTextAttribute
(
builder
,
"TDRC"
,
ilst
);
}
else
if
(
type
==
TYPE_ARTIST_1
||
type
==
TYPE_ARTIST_2
)
{
parseTextAttribute
(
builder
,
"TPE1"
,
ilst
);
}
else
if
(
type
==
TYPE_ENCODER_1
||
type
==
TYPE_ENCODER_2
)
{
parseTextAttribute
(
builder
,
"TSSE"
,
ilst
);
}
else
if
(
type
==
TYPE_ALBUM_1
||
type
==
TYPE_ALBUM_2
)
{
parseTextAttribute
(
builder
,
"TALB"
,
ilst
);
}
else
if
(
type
==
TYPE_COMPOSER_1
||
type
==
TYPE_COMPOSER_2
||
type
==
TYPE_COMPOSER_3
||
type
==
TYPE_COMPOSER_4
)
{
parseTextAttribute
(
builder
,
"TCOM"
,
ilst
);
}
else
if
(
type
==
TYPE_LYRICS_1
||
type
==
TYPE_LYRICS_2
)
{
parseTextAttribute
(
builder
,
"lyrics"
,
ilst
);
}
else
if
(
type
==
TYPE_STANDARD_GENRE
)
{
parseStandardGenreAttribute
(
builder
,
"TCON"
,
ilst
);
}
else
if
(
type
==
TYPE_GENRE_1
||
type
==
TYPE_GENRE_2
)
{
parseTextAttribute
(
builder
,
"TCON"
,
ilst
);
}
else
if
(
type
==
TYPE_GROUPING_1
||
type
==
TYPE_GROUPING_2
)
{
parseTextAttribute
(
builder
,
"TIT1"
,
ilst
);
}
else
if
(
type
==
TYPE_DISK_NUMBER
)
{
parseIndexAndCountAttribute
(
builder
,
"TPOS"
,
ilst
,
endPosition
);
}
else
if
(
type
==
TYPE_TRACK_NUMBER
)
{
parseIndexAndCountAttribute
(
builder
,
"TRCK"
,
ilst
,
endPosition
);
}
else
if
(
type
==
TYPE_TEMPO
)
{
parseIntegerAttribute
(
builder
,
"TBPM"
,
ilst
);
}
else
if
(
type
==
TYPE_COMPILATION
)
{
parseBooleanAttribute
(
builder
,
"TCMP"
,
ilst
);
}
else
if
(
type
==
TYPE_ALBUM_ARTIST
)
{
parseTextAttribute
(
builder
,
"TPE2"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_TRACK_NAME
)
{
parseTextAttribute
(
builder
,
"TSOT"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_ALBUM
)
{
parseTextAttribute
(
builder
,
"TSO2"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_ARTIST
)
{
parseTextAttribute
(
builder
,
"TSOA"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_ALBUM_ARTIST
)
{
parseTextAttribute
(
builder
,
"TSOP"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_COMPOSER
)
{
parseTextAttribute
(
builder
,
"TSOC"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_SHOW
)
{
parseTextAttribute
(
builder
,
"sortShow"
,
ilst
);
}
else
if
(
type
==
TYPE_GAPLESS_ALBUM
)
{
parseBooleanAttribute
(
builder
,
"gaplessAlbum"
,
ilst
);
}
else
if
(
type
==
TYPE_SHOW
)
{
parseTextAttribute
(
builder
,
"show"
,
ilst
);
}
else
if
(
type
==
Atom
.
TYPE_DASHES
)
{
parseExtendedAttribute
(
builder
,
ilst
,
endPosition
);
}
}
private
static
void
parseTextAttribute
(
List
<
Metadata
.
Entry
>
builder
,
String
attributeName
,
ParsableByteArray
ilst
)
{
int
length
=
ilst
.
readInt
()
-
Atom
.
FULL_HEADER_SIZE
;
int
key
=
ilst
.
readInt
();
ilst
.
skipBytes
(
4
);
if
(
key
==
Atom
.
TYPE_data
)
{
ilst
.
skipBytes
(
4
);
String
value
=
ilst
.
readNullTerminatedString
(
length
-
4
);
Id3Frame
frame
=
new
TextInformationFrame
(
attributeName
,
value
);
builder
.
add
(
frame
);
}
else
{
ilst
.
skipBytes
(
length
);
}
}
private
static
void
parseCommentAttribute
(
List
<
Metadata
.
Entry
>
builder
,
String
attributeName
,
ParsableByteArray
ilst
)
{
int
length
=
ilst
.
readInt
()
-
Atom
.
FULL_HEADER_SIZE
;
int
key
=
ilst
.
readInt
();
ilst
.
skipBytes
(
4
);
if
(
key
==
Atom
.
TYPE_data
)
{
ilst
.
skipBytes
(
4
);
String
value
=
ilst
.
readNullTerminatedString
(
length
-
4
);
Id3Frame
frame
=
new
CommentFrame
(
"eng"
,
attributeName
,
value
);
builder
.
add
(
frame
);
}
else
{
ilst
.
skipBytes
(
length
);
}
}
private
static
void
parseBooleanAttribute
(
List
<
Metadata
.
Entry
>
builder
,
String
attributeName
,
ParsableByteArray
ilst
)
{
int
length
=
ilst
.
readInt
()
-
Atom
.
FULL_HEADER_SIZE
;
int
key
=
ilst
.
readInt
();
ilst
.
skipBytes
(
4
);
if
(
key
==
Atom
.
TYPE_data
)
{
Object
value
=
parseDataBox
(
ilst
,
length
);
if
(
value
instanceof
Integer
)
{
int
n
=
(
Integer
)
value
;
String
s
=
n
==
0
?
"0"
:
"1"
;
Id3Frame
frame
=
new
TextInformationFrame
(
attributeName
,
s
);
builder
.
add
(
frame
);
}
}
else
{
ilst
.
skipBytes
(
length
);
}
}
private
static
void
parseIntegerAttribute
(
List
<
Metadata
.
Entry
>
builder
,
String
attributeName
,
ParsableByteArray
ilst
)
{
int
length
=
ilst
.
readInt
()
-
Atom
.
FULL_HEADER_SIZE
;
int
key
=
ilst
.
readInt
();
ilst
.
skipBytes
(
4
);
if
(
key
==
Atom
.
TYPE_data
)
{
Object
value
=
parseDataBox
(
ilst
,
length
);
if
(
value
instanceof
Integer
)
{
int
n
=
(
Integer
)
value
;
String
s
=
""
+
n
;
Id3Frame
frame
=
new
TextInformationFrame
(
attributeName
,
s
);
builder
.
add
(
frame
);
}
}
else
{
ilst
.
skipBytes
(
length
);
}
}
private
static
void
parseIndexAndCountAttribute
(
List
<
Metadata
.
Entry
>
builder
,
String
attributeName
,
ParsableByteArray
ilst
,
int
endPosition
)
{
int
length
=
ilst
.
readInt
()
-
Atom
.
FULL_HEADER_SIZE
;
int
key
=
ilst
.
readInt
();
ilst
.
skipBytes
(
4
);
if
(
key
==
Atom
.
TYPE_data
)
{
Object
value
=
parseDataBox
(
ilst
,
length
);
if
(
value
instanceof
byte
[])
{
byte
[]
bytes
=
(
byte
[])
value
;
if
(
bytes
.
length
==
8
)
{
int
index
=
(
bytes
[
2
]
<<
8
)
+
(
bytes
[
3
]
&
0xFF
);
int
count
=
(
bytes
[
4
]
<<
8
)
+
(
bytes
[
5
]
&
0xFF
);
if
(
index
>
0
)
{
String
s
=
""
+
index
;
if
(
count
>
0
)
{
s
=
s
+
"/"
+
count
;
}
Id3Frame
frame
=
new
TextInformationFrame
(
attributeName
,
s
);
builder
.
add
(
frame
);
}
}
while
(
ilst
.
getPosition
()
<
limit
)
{
Metadata
.
Entry
entry
=
MetadataUtil
.
parseIlstElement
(
ilst
);
if
(
entry
!=
null
)
{
entries
.
add
(
entry
);
}
}
else
{
ilst
.
skipBytes
(
length
);
}
}
private
static
void
parseStandardGenreAttribute
(
List
<
Metadata
.
Entry
>
builder
,
String
attributeName
,
ParsableByteArray
ilst
)
{
int
length
=
ilst
.
readInt
()
-
Atom
.
FULL_HEADER_SIZE
;
int
key
=
ilst
.
readInt
();
ilst
.
skipBytes
(
4
);
if
(
key
==
Atom
.
TYPE_data
)
{
Object
value
=
parseDataBox
(
ilst
,
length
);
if
(
value
instanceof
byte
[])
{
byte
[]
bytes
=
(
byte
[])
value
;
if
(
bytes
.
length
==
2
)
{
int
code
=
(
bytes
[
0
]
<<
8
)
+
(
bytes
[
1
]
&
0xFF
);
String
s
=
Id3Util
.
decodeGenre
(
code
);
if
(
s
!=
null
)
{
Id3Frame
frame
=
new
TextInformationFrame
(
attributeName
,
s
);
builder
.
add
(
frame
);
}
}
}
}
else
{
ilst
.
skipBytes
(
length
);
}
}
private
static
void
parseExtendedAttribute
(
List
<
Metadata
.
Entry
>
builder
,
ParsableByteArray
ilst
,
int
endPosition
)
{
String
domain
=
null
;
String
name
=
null
;
Object
value
=
null
;
while
(
ilst
.
getPosition
()
<
endPosition
)
{
int
length
=
ilst
.
readInt
()
-
Atom
.
FULL_HEADER_SIZE
;
int
key
=
ilst
.
readInt
();
ilst
.
skipBytes
(
4
);
if
(
key
==
Atom
.
TYPE_mean
)
{
domain
=
ilst
.
readNullTerminatedString
(
length
);
}
else
if
(
key
==
Atom
.
TYPE_name
)
{
name
=
ilst
.
readNullTerminatedString
(
length
);
}
else
if
(
key
==
Atom
.
TYPE_data
)
{
value
=
parseDataBox
(
ilst
,
length
);
}
else
{
ilst
.
skipBytes
(
length
);
}
}
if
(
value
!=
null
)
{
if
(
Util
.
areEqual
(
domain
,
"com.apple.iTunes"
))
{
String
s
=
new
String
((
byte
[])
value
);
Id3Frame
frame
=
new
CommentFrame
(
"eng"
,
name
,
s
);
builder
.
add
(
frame
);
}
else
if
(
domain
!=
null
&&
name
!=
null
)
{
String
extendedName
=
domain
+
"."
+
name
;
if
(
value
instanceof
String
)
{
Id3Frame
frame
=
new
TextInformationFrame
(
extendedName
,
(
String
)
value
);
builder
.
add
(
frame
);
}
else
if
(
value
instanceof
Integer
)
{
Id3Frame
frame
=
new
TextInformationFrame
(
extendedName
,
value
.
toString
());
builder
.
add
(
frame
);
}
else
if
(
value
instanceof
byte
[])
{
byte
[]
bb
=
(
byte
[])
value
;
Id3Frame
frame
=
new
BinaryFrame
(
extendedName
,
bb
);
builder
.
add
(
frame
);
}
}
}
}
private
static
Object
parseDataBox
(
ParsableByteArray
ilst
,
int
length
)
{
int
versionAndFlags
=
ilst
.
readInt
();
int
flags
=
versionAndFlags
&
0xFFFFFF
;
boolean
isText
=
(
flags
==
1
);
boolean
isData
=
(
flags
==
0
);
boolean
isImageData
=
(
flags
==
0xD
);
boolean
isInteger
=
(
flags
==
21
);
int
dataLength
=
length
-
4
;
if
(
isText
)
{
return
ilst
.
readNullTerminatedString
(
dataLength
);
}
else
if
(
isInteger
)
{
if
(
dataLength
==
1
)
{
return
ilst
.
readUnsignedByte
();
}
else
if
(
dataLength
==
2
)
{
return
ilst
.
readUnsignedShort
();
}
else
{
ilst
.
skipBytes
(
dataLength
);
return
null
;
}
}
else
if
(
isData
)
{
byte
[]
bytes
=
new
byte
[
dataLength
];
ilst
.
readBytes
(
bytes
,
0
,
dataLength
);
return
bytes
;
}
else
{
ilst
.
skipBytes
(
dataLength
);
return
null
;
}
return
entries
.
isEmpty
()
?
null
:
new
Metadata
(
entries
);
}
/**
...
...
@@ -756,12 +460,9 @@ import java.util.List;
*/
private
static
long
parseMvhd
(
ParsableByteArray
mvhd
)
{
mvhd
.
setPosition
(
Atom
.
HEADER_SIZE
);
int
fullAtom
=
mvhd
.
readInt
();
int
version
=
Atom
.
parseFullAtomVersion
(
fullAtom
);
mvhd
.
skipBytes
(
version
==
0
?
8
:
16
);
return
mvhd
.
readUnsignedInt
();
}
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
0 → 100644
View file @
8caaf0b5
/*
* Copyright (C) 2016 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.util.Log
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
import
com.google.android.exoplayer2.metadata.id3.Id3Frame
;
import
com.google.android.exoplayer2.metadata.id3.TextInformationFrame
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
/**
* Parses metadata items stored in ilst atoms.
*/
/* package */
final
class
MetadataUtil
{
private
static
final
String
TAG
=
"MetadataUtil"
;
// Codes that start with the copyright character (omitted) and have equivalent ID3 frames.
private
static
final
int
SHORT_TYPE_NAME_1
=
Util
.
getIntegerCodeForString
(
"nam"
);
private
static
final
int
SHORT_TYPE_NAME_2
=
Util
.
getIntegerCodeForString
(
"trk"
);
private
static
final
int
SHORT_TYPE_COMMENT
=
Util
.
getIntegerCodeForString
(
"cmt"
);
private
static
final
int
SHORT_TYPE_YEAR
=
Util
.
getIntegerCodeForString
(
"day"
);
private
static
final
int
SHORT_TYPE_ARTIST
=
Util
.
getIntegerCodeForString
(
"ART"
);
private
static
final
int
SHORT_TYPE_ENCODER
=
Util
.
getIntegerCodeForString
(
"too"
);
private
static
final
int
SHORT_TYPE_ALBUM
=
Util
.
getIntegerCodeForString
(
"alb"
);
private
static
final
int
SHORT_TYPE_COMPOSER_1
=
Util
.
getIntegerCodeForString
(
"com"
);
private
static
final
int
SHORT_TYPE_COMPOSER_2
=
Util
.
getIntegerCodeForString
(
"wrt"
);
private
static
final
int
SHORT_TYPE_LYRICS
=
Util
.
getIntegerCodeForString
(
"lyr"
);
private
static
final
int
SHORT_TYPE_GENRE
=
Util
.
getIntegerCodeForString
(
"gen"
);
// Codes that have equivalent ID3 frames.
private
static
final
int
TYPE_COVER_ART
=
Util
.
getIntegerCodeForString
(
"covr"
);
private
static
final
int
TYPE_GENRE
=
Util
.
getIntegerCodeForString
(
"gnre"
);
private
static
final
int
TYPE_GROUPING
=
Util
.
getIntegerCodeForString
(
"grp"
);
private
static
final
int
TYPE_DISK_NUMBER
=
Util
.
getIntegerCodeForString
(
"disk"
);
private
static
final
int
TYPE_TRACK_NUMBER
=
Util
.
getIntegerCodeForString
(
"trkn"
);
private
static
final
int
TYPE_TEMPO
=
Util
.
getIntegerCodeForString
(
"tmpo"
);
private
static
final
int
TYPE_COMPILATION
=
Util
.
getIntegerCodeForString
(
"cpil"
);
private
static
final
int
TYPE_ALBUM_ARTIST
=
Util
.
getIntegerCodeForString
(
"aART"
);
private
static
final
int
TYPE_SORT_TRACK_NAME
=
Util
.
getIntegerCodeForString
(
"sonm"
);
private
static
final
int
TYPE_SORT_ALBUM
=
Util
.
getIntegerCodeForString
(
"soal"
);
private
static
final
int
TYPE_SORT_ARTIST
=
Util
.
getIntegerCodeForString
(
"soar"
);
private
static
final
int
TYPE_SORT_ALBUM_ARTIST
=
Util
.
getIntegerCodeForString
(
"soaa"
);
private
static
final
int
TYPE_SORT_COMPOSER
=
Util
.
getIntegerCodeForString
(
"soco"
);
// Types that do not have equivalent ID3 frames.
private
static
final
int
TYPE_RATING
=
Util
.
getIntegerCodeForString
(
"rtng"
);
private
static
final
int
TYPE_GAPLESS_ALBUM
=
Util
.
getIntegerCodeForString
(
"pgap"
);
private
static
final
int
TYPE_TV_SORT_SHOW
=
Util
.
getIntegerCodeForString
(
"sosn"
);
private
static
final
int
TYPE_TV_SHOW
=
Util
.
getIntegerCodeForString
(
"tvsh"
);
// Type for items that are intended for internal use by the player.
private
static
final
int
TYPE_INTERNAL
=
Util
.
getIntegerCodeForString
(
"----"
);
// Standard genres.
private
static
final
String
[]
STANDARD_GENRES
=
new
String
[]
{
// These are the official ID3v1 genres.
"Blues"
,
"Classic Rock"
,
"Country"
,
"Dance"
,
"Disco"
,
"Funk"
,
"Grunge"
,
"Hip-Hop"
,
"Jazz"
,
"Metal"
,
"New Age"
,
"Oldies"
,
"Other"
,
"Pop"
,
"R&B"
,
"Rap"
,
"Reggae"
,
"Rock"
,
"Techno"
,
"Industrial"
,
"Alternative"
,
"Ska"
,
"Death Metal"
,
"Pranks"
,
"Soundtrack"
,
"Euro-Techno"
,
"Ambient"
,
"Trip-Hop"
,
"Vocal"
,
"Jazz+Funk"
,
"Fusion"
,
"Trance"
,
"Classical"
,
"Instrumental"
,
"Acid"
,
"House"
,
"Game"
,
"Sound Clip"
,
"Gospel"
,
"Noise"
,
"AlternRock"
,
"Bass"
,
"Soul"
,
"Punk"
,
"Space"
,
"Meditative"
,
"Instrumental Pop"
,
"Instrumental Rock"
,
"Ethnic"
,
"Gothic"
,
"Darkwave"
,
"Techno-Industrial"
,
"Electronic"
,
"Pop-Folk"
,
"Eurodance"
,
"Dream"
,
"Southern Rock"
,
"Comedy"
,
"Cult"
,
"Gangsta"
,
"Top 40"
,
"Christian Rap"
,
"Pop/Funk"
,
"Jungle"
,
"Native American"
,
"Cabaret"
,
"New Wave"
,
"Psychadelic"
,
"Rave"
,
"Showtunes"
,
"Trailer"
,
"Lo-Fi"
,
"Tribal"
,
"Acid Punk"
,
"Acid Jazz"
,
"Polka"
,
"Retro"
,
"Musical"
,
"Rock & Roll"
,
"Hard Rock"
,
// These were made up by the authors of Winamp but backported into the ID3 spec.
"Folk"
,
"Folk-Rock"
,
"National Folk"
,
"Swing"
,
"Fast Fusion"
,
"Bebob"
,
"Latin"
,
"Revival"
,
"Celtic"
,
"Bluegrass"
,
"Avantgarde"
,
"Gothic Rock"
,
"Progressive Rock"
,
"Psychedelic Rock"
,
"Symphonic Rock"
,
"Slow Rock"
,
"Big Band"
,
"Chorus"
,
"Easy Listening"
,
"Acoustic"
,
"Humour"
,
"Speech"
,
"Chanson"
,
"Opera"
,
"Chamber Music"
,
"Sonata"
,
"Symphony"
,
"Booty Bass"
,
"Primus"
,
"Porn Groove"
,
"Satire"
,
"Slow Jam"
,
"Club"
,
"Tango"
,
"Samba"
,
"Folklore"
,
"Ballad"
,
"Power Ballad"
,
"Rhythmic Soul"
,
"Freestyle"
,
"Duet"
,
"Punk Rock"
,
"Drum Solo"
,
"A capella"
,
"Euro-House"
,
"Dance Hall"
,
// These were also invented by the Winamp folks but ignored by the ID3 authors.
"Goa"
,
"Drum & Bass"
,
"Club-House"
,
"Hardcore"
,
"Terror"
,
"Indie"
,
"BritPop"
,
"Negerpunk"
,
"Polsk Punk"
,
"Beat"
,
"Christian Gangsta Rap"
,
"Heavy Metal"
,
"Black Metal"
,
"Crossover"
,
"Contemporary Christian"
,
"Christian Rock"
,
"Merengue"
,
"Salsa"
,
"Thrash Metal"
,
"Anime"
,
"Jpop"
,
"Synthpop"
};
private
static
final
String
LANGUAGE_UNDEFINED
=
"und"
;
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.
*
* @param ilst Holds the data to be parsed.
* @return The parsed element, or null if the element's type was not recognized.
*/
public
static
Metadata
.
Entry
parseIlstElement
(
ParsableByteArray
ilst
)
{
int
position
=
ilst
.
getPosition
();
int
endPosition
=
position
+
ilst
.
readInt
();
int
type
=
ilst
.
readInt
();
int
typeTopByte
=
(
type
>>
24
)
&
0xFF
;
try
{
if
(
typeTopByte
==
'\
u00A9
'
/* Copyright char */
||
typeTopByte
==
'\
uFFFD
'
/* Replacement char */
)
{
int
shortType
=
type
&
0x00FFFFFF
;
if
(
shortType
==
SHORT_TYPE_COMMENT
)
{
return
parseCommentAttribute
(
type
,
ilst
);
}
else
if
(
shortType
==
SHORT_TYPE_NAME_1
||
shortType
==
SHORT_TYPE_NAME_2
)
{
return
parseTextAttribute
(
type
,
"TIT2"
,
ilst
);
}
else
if
(
shortType
==
SHORT_TYPE_COMPOSER_1
||
shortType
==
SHORT_TYPE_COMPOSER_2
)
{
return
parseTextAttribute
(
type
,
"TCOM"
,
ilst
);
}
else
if
(
shortType
==
SHORT_TYPE_YEAR
)
{
return
parseTextAttribute
(
type
,
"TDRC"
,
ilst
);
}
else
if
(
shortType
==
SHORT_TYPE_ARTIST
)
{
return
parseTextAttribute
(
type
,
"TPE1"
,
ilst
);
}
else
if
(
shortType
==
SHORT_TYPE_ENCODER
)
{
return
parseTextAttribute
(
type
,
"TSSE"
,
ilst
);
}
else
if
(
shortType
==
SHORT_TYPE_ALBUM
)
{
return
parseTextAttribute
(
type
,
"TALB"
,
ilst
);
}
else
if
(
shortType
==
SHORT_TYPE_LYRICS
)
{
return
parseTextAttribute
(
type
,
"USLT"
,
ilst
);
}
else
if
(
shortType
==
SHORT_TYPE_GENRE
)
{
return
parseTextAttribute
(
type
,
"TCON"
,
ilst
);
}
else
if
(
shortType
==
TYPE_GROUPING
)
{
return
parseTextAttribute
(
type
,
"TIT1"
,
ilst
);
}
}
else
if
(
type
==
TYPE_GENRE
)
{
return
parseStandardGenreAttribute
(
ilst
);
}
else
if
(
type
==
TYPE_DISK_NUMBER
)
{
return
parseIndexAndCountAttribute
(
type
,
"TPOS"
,
ilst
);
}
else
if
(
type
==
TYPE_TRACK_NUMBER
)
{
return
parseIndexAndCountAttribute
(
type
,
"TRCK"
,
ilst
);
}
else
if
(
type
==
TYPE_TEMPO
)
{
return
parseUint8Attribute
(
type
,
"TBPM"
,
ilst
,
true
,
false
);
}
else
if
(
type
==
TYPE_COMPILATION
)
{
return
parseUint8Attribute
(
type
,
"TCMP"
,
ilst
,
true
,
true
);
}
else
if
(
type
==
TYPE_COVER_ART
)
{
return
parseCoverArt
(
ilst
);
}
else
if
(
type
==
TYPE_ALBUM_ARTIST
)
{
return
parseTextAttribute
(
type
,
"TPE2"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_TRACK_NAME
)
{
return
parseTextAttribute
(
type
,
"TSOT"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_ALBUM
)
{
return
parseTextAttribute
(
type
,
"TSO2"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_ARTIST
)
{
return
parseTextAttribute
(
type
,
"TSOA"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_ALBUM_ARTIST
)
{
return
parseTextAttribute
(
type
,
"TSOP"
,
ilst
);
}
else
if
(
type
==
TYPE_SORT_COMPOSER
)
{
return
parseTextAttribute
(
type
,
"TSOC"
,
ilst
);
}
else
if
(
type
==
TYPE_RATING
)
{
return
parseUint8Attribute
(
type
,
"ITUNESADVISORY"
,
ilst
,
false
,
false
);
}
else
if
(
type
==
TYPE_GAPLESS_ALBUM
)
{
return
parseUint8Attribute
(
type
,
"ITUNESGAPLESS"
,
ilst
,
false
,
true
);
}
else
if
(
type
==
TYPE_TV_SORT_SHOW
)
{
return
parseTextAttribute
(
type
,
"TVSHOWSORT"
,
ilst
);
}
else
if
(
type
==
TYPE_TV_SHOW
)
{
return
parseTextAttribute
(
type
,
"TVSHOW"
,
ilst
);
}
else
if
(
type
==
TYPE_INTERNAL
)
{
return
parseInternalAttribute
(
ilst
,
endPosition
);
}
Log
.
d
(
TAG
,
"Skipped unknown metadata entry: "
+
Atom
.
getAtomTypeString
(
type
));
return
null
;
}
finally
{
ilst
.
setPosition
(
endPosition
);
}
}
private
static
TextInformationFrame
parseTextAttribute
(
int
type
,
String
id
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
data
.
skipBytes
(
8
);
// version (1), flags (3), empty (4)
String
value
=
data
.
readNullTerminatedString
(
atomSize
-
16
);
return
new
TextInformationFrame
(
id
,
value
);
}
Log
.
w
(
TAG
,
"Failed to parse text attribute: "
+
Atom
.
getAtomTypeString
(
type
));
return
null
;
}
private
static
CommentFrame
parseCommentAttribute
(
int
type
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
data
.
skipBytes
(
8
);
// version (1), flags (3), empty (4)
String
value
=
data
.
readNullTerminatedString
(
atomSize
-
16
);
return
new
CommentFrame
(
LANGUAGE_UNDEFINED
,
value
,
value
);
}
Log
.
w
(
TAG
,
"Failed to parse comment attribute: "
+
Atom
.
getAtomTypeString
(
type
));
return
null
;
}
private
static
Id3Frame
parseUint8Attribute
(
int
type
,
String
id
,
ParsableByteArray
data
,
boolean
isTextInformationFrame
,
boolean
isBoolean
)
{
int
value
=
parseUint8AttributeValue
(
data
);
if
(
isBoolean
)
{
value
=
Math
.
min
(
1
,
value
);
}
if
(
value
>=
0
)
{
return
isTextInformationFrame
?
new
TextInformationFrame
(
id
,
Integer
.
toString
(
value
))
:
new
CommentFrame
(
LANGUAGE_UNDEFINED
,
id
,
Integer
.
toString
(
value
));
}
Log
.
w
(
TAG
,
"Failed to parse uint8 attribute: "
+
Atom
.
getAtomTypeString
(
type
));
return
null
;
}
private
static
TextInformationFrame
parseIndexAndCountAttribute
(
int
type
,
String
attributeName
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
&&
atomSize
>=
22
)
{
data
.
skipBytes
(
10
);
// version (1), flags (3), empty (4), empty (2)
int
index
=
data
.
readUnsignedShort
();
if
(
index
>
0
)
{
String
description
=
""
+
index
;
int
count
=
data
.
readUnsignedShort
();
if
(
count
>
0
)
{
description
+=
"/"
+
count
;
}
return
new
TextInformationFrame
(
attributeName
,
description
);
}
}
Log
.
w
(
TAG
,
"Failed to parse index/count attribute: "
+
Atom
.
getAtomTypeString
(
type
));
return
null
;
}
private
static
TextInformationFrame
parseStandardGenreAttribute
(
ParsableByteArray
data
)
{
int
genreCode
=
parseUint8AttributeValue
(
data
);
String
genreString
=
(
0
<
genreCode
&&
genreCode
<=
STANDARD_GENRES
.
length
)
?
STANDARD_GENRES
[
genreCode
-
1
]
:
null
;
if
(
genreString
!=
null
)
{
return
new
TextInformationFrame
(
"TCON"
,
genreString
);
}
Log
.
w
(
TAG
,
"Failed to parse standard genre code"
);
return
null
;
}
private
static
ApicFrame
parseCoverArt
(
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
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
;
if
(
mimeType
==
null
)
{
Log
.
w
(
TAG
,
"Unrecognized cover art flags: "
+
flags
);
return
null
;
}
data
.
skipBytes
(
4
);
// empty (4)
byte
[]
pictureData
=
new
byte
[
atomSize
-
16
];
data
.
readBytes
(
pictureData
,
0
,
pictureData
.
length
);
return
new
ApicFrame
(
mimeType
,
null
,
3
/* Cover (front) */
,
pictureData
);
}
Log
.
w
(
TAG
,
"Failed to parse cover art attribute"
);
return
null
;
}
private
static
Id3Frame
parseInternalAttribute
(
ParsableByteArray
data
,
int
endPosition
)
{
String
domain
=
null
;
String
name
=
null
;
int
dataAtomPosition
=
-
1
;
int
dataAtomSize
=
-
1
;
while
(
data
.
getPosition
()
<
endPosition
)
{
int
atomPosition
=
data
.
getPosition
();
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
data
.
skipBytes
(
4
);
// version (1), flags (3)
if
(
atomType
==
Atom
.
TYPE_mean
)
{
domain
=
data
.
readNullTerminatedString
(
atomSize
-
12
);
}
else
if
(
atomType
==
Atom
.
TYPE_name
)
{
name
=
data
.
readNullTerminatedString
(
atomSize
-
12
);
}
else
{
if
(
atomType
==
Atom
.
TYPE_data
)
{
dataAtomPosition
=
atomPosition
;
dataAtomSize
=
atomSize
;
}
data
.
skipBytes
(
atomSize
-
12
);
}
}
if
(!
"com.apple.iTunes"
.
equals
(
domain
)
||
!
"iTunSMPB"
.
equals
(
name
)
||
dataAtomPosition
==
-
1
)
{
// We're only interested in iTunSMPB.
return
null
;
}
data
.
setPosition
(
dataAtomPosition
);
data
.
skipBytes
(
16
);
// size (4), type (4), version (1), flags (3), empty (4)
String
value
=
data
.
readNullTerminatedString
(
dataAtomSize
-
16
);
return
new
CommentFrame
(
LANGUAGE_UNDEFINED
,
name
,
value
);
}
private
static
int
parseUint8AttributeValue
(
ParsableByteArray
data
)
{
data
.
skipBytes
(
4
);
// atomSize
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
data
.
skipBytes
(
8
);
// version (1), flags (3), empty (4)
return
data
.
readUnsignedByte
();
}
Log
.
w
(
TAG
,
"Failed to parse uint8 attribute value"
);
return
-
1
;
}
}
library/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Util.java
deleted
100644 → 0
View file @
1b39d21e
/*
* Copyright (C) 2016 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
.
metadata
.
id3
;
/**
* ID3 utility methods.
*/
public
final
class
Id3Util
{
private
static
final
String
[]
STANDARD_GENRES
=
new
String
[]
{
// These are the official ID3v1 genres.
"Blues"
,
"Classic Rock"
,
"Country"
,
"Dance"
,
"Disco"
,
"Funk"
,
"Grunge"
,
"Hip-Hop"
,
"Jazz"
,
"Metal"
,
"New Age"
,
"Oldies"
,
"Other"
,
"Pop"
,
"R&B"
,
"Rap"
,
"Reggae"
,
"Rock"
,
"Techno"
,
"Industrial"
,
"Alternative"
,
"Ska"
,
"Death Metal"
,
"Pranks"
,
"Soundtrack"
,
"Euro-Techno"
,
"Ambient"
,
"Trip-Hop"
,
"Vocal"
,
"Jazz+Funk"
,
"Fusion"
,
"Trance"
,
"Classical"
,
"Instrumental"
,
"Acid"
,
"House"
,
"Game"
,
"Sound Clip"
,
"Gospel"
,
"Noise"
,
"AlternRock"
,
"Bass"
,
"Soul"
,
"Punk"
,
"Space"
,
"Meditative"
,
"Instrumental Pop"
,
"Instrumental Rock"
,
"Ethnic"
,
"Gothic"
,
"Darkwave"
,
"Techno-Industrial"
,
"Electronic"
,
"Pop-Folk"
,
"Eurodance"
,
"Dream"
,
"Southern Rock"
,
"Comedy"
,
"Cult"
,
"Gangsta"
,
"Top 40"
,
"Christian Rap"
,
"Pop/Funk"
,
"Jungle"
,
"Native American"
,
"Cabaret"
,
"New Wave"
,
"Psychadelic"
,
"Rave"
,
"Showtunes"
,
"Trailer"
,
"Lo-Fi"
,
"Tribal"
,
"Acid Punk"
,
"Acid Jazz"
,
"Polka"
,
"Retro"
,
"Musical"
,
"Rock & Roll"
,
"Hard Rock"
,
// These were made up by the authors of Winamp but backported into the ID3 spec.
"Folk"
,
"Folk-Rock"
,
"National Folk"
,
"Swing"
,
"Fast Fusion"
,
"Bebob"
,
"Latin"
,
"Revival"
,
"Celtic"
,
"Bluegrass"
,
"Avantgarde"
,
"Gothic Rock"
,
"Progressive Rock"
,
"Psychedelic Rock"
,
"Symphonic Rock"
,
"Slow Rock"
,
"Big Band"
,
"Chorus"
,
"Easy Listening"
,
"Acoustic"
,
"Humour"
,
"Speech"
,
"Chanson"
,
"Opera"
,
"Chamber Music"
,
"Sonata"
,
"Symphony"
,
"Booty Bass"
,
"Primus"
,
"Porn Groove"
,
"Satire"
,
"Slow Jam"
,
"Club"
,
"Tango"
,
"Samba"
,
"Folklore"
,
"Ballad"
,
"Power Ballad"
,
"Rhythmic Soul"
,
"Freestyle"
,
"Duet"
,
"Punk Rock"
,
"Drum Solo"
,
"A capella"
,
"Euro-House"
,
"Dance Hall"
,
// These were also invented by the Winamp folks but ignored by the ID3 authors.
"Goa"
,
"Drum & Bass"
,
"Club-House"
,
"Hardcore"
,
"Terror"
,
"Indie"
,
"BritPop"
,
"Negerpunk"
,
"Polsk Punk"
,
"Beat"
,
"Christian Gangsta Rap"
,
"Heavy Metal"
,
"Black Metal"
,
"Crossover"
,
"Contemporary Christian"
,
"Christian Rock"
,
"Merengue"
,
"Salsa"
,
"Thrash Metal"
,
"Anime"
,
"Jpop"
,
"Synthpop"
};
private
Id3Util
()
{}
public
static
String
decodeGenre
(
int
code
)
{
return
(
0
<
code
&&
code
<=
STANDARD_GENRES
.
length
)
?
STANDARD_GENRES
[
code
-
1
]
:
null
;
}
}
library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java
View file @
8caaf0b5
...
...
@@ -300,9 +300,9 @@ public final class ParsableByteArray {
*/
public
int
readLittleEndianInt
()
{
return
(
data
[
position
++]
&
0xFF
)
|
(
data
[
position
++]
&
0xFF
)
<<
8
|
(
data
[
position
++]
&
0xFF
)
<<
16
|
(
data
[
position
++]
&
0xFF
)
<<
24
;
|
(
data
[
position
++]
&
0xFF
)
<<
8
|
(
data
[
position
++]
&
0xFF
)
<<
16
|
(
data
[
position
++]
&
0xFF
)
<<
24
;
}
/**
...
...
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