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
3bcd9ca6
authored
Jun 26, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Simplify HLS MP3 support.
parent
0c577ce2
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
123 additions
and
140 deletions
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/VbriSeeker.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/XingSeeker.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/AdtsReader.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/MpaReader.java → library/src/main/java/com/google/android/exoplayer/extractor/ts/MpegAudioReader.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
library/src/main/java/com/google/android/exoplayer/util/CodecSpecificDataUtil.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/MpegAudioHeader.java → library/src/main/java/com/google/android/exoplayer/util/MpegAudioHeader.java
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
View file @
3bcd9ca6
...
@@ -43,8 +43,11 @@ import java.util.Locale;
...
@@ -43,8 +43,11 @@ import java.util.Locale;
}
}
public
static
final
Sample
[]
YOUTUBE_DASH_MP4
=
new
Sample
[]
{
public
static
final
Sample
[]
YOUTUBE_DASH_MP4
=
new
Sample
[]
{
new
Sample
(
"XXXXXXXXX"
,
new
Sample
(
"Google Glass"
,
"http://178.33.229.111/live/mp4:Videolina/playlist.m3u8"
,
PlayerActivity
.
TYPE_HLS
),
"http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&"
+
"ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7."
+
"8506521BFC350652163895D4C26DEE124209AA9E&key=ik0"
,
PlayerActivity
.
TYPE_DASH
),
new
Sample
(
"Google Play"
,
new
Sample
(
"Google Play"
,
"http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?"
"http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&"
+
"as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&"
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java
View file @
3bcd9ca6
...
@@ -24,7 +24,7 @@ import com.google.android.exoplayer.extractor.ExtractorOutput;
...
@@ -24,7 +24,7 @@ import com.google.android.exoplayer.extractor.ExtractorOutput;
import
com.google.android.exoplayer.extractor.PositionHolder
;
import
com.google.android.exoplayer.extractor.PositionHolder
;
import
com.google.android.exoplayer.extractor.SeekMap
;
import
com.google.android.exoplayer.extractor.SeekMap
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.util.M
imeTypes
;
import
com.google.android.exoplayer.util.M
pegAudioHeader
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
...
@@ -43,20 +43,10 @@ public final class Mp3Extractor implements Extractor {
...
@@ -43,20 +43,10 @@ public final class Mp3Extractor implements Extractor {
/** Mask that includes the audio header values that must match between frames. */
/** Mask that includes the audio header values that must match between frames. */
private
static
final
int
HEADER_MASK
=
0xFFFE0C00
;
private
static
final
int
HEADER_MASK
=
0xFFFE0C00
;
private
static
final
int
ID3_TAG
=
Util
.
getIntegerCodeForString
(
"ID3"
);
private
static
final
int
ID3_TAG
=
Util
.
getIntegerCodeForString
(
"ID3"
);
public
static
final
String
[]
MIME_TYPE_BY_LAYER
=
new
String
[]
{
MimeTypes
.
AUDIO_MPEG_L1
,
MimeTypes
.
AUDIO_MPEG_L2
,
MimeTypes
.
AUDIO_MPEG
};
private
static
final
int
XING_HEADER
=
Util
.
getIntegerCodeForString
(
"Xing"
);
private
static
final
int
XING_HEADER
=
Util
.
getIntegerCodeForString
(
"Xing"
);
private
static
final
int
INFO_HEADER
=
Util
.
getIntegerCodeForString
(
"Info"
);
private
static
final
int
INFO_HEADER
=
Util
.
getIntegerCodeForString
(
"Info"
);
private
static
final
int
VBRI_HEADER
=
Util
.
getIntegerCodeForString
(
"VBRI"
);
private
static
final
int
VBRI_HEADER
=
Util
.
getIntegerCodeForString
(
"VBRI"
);
/**
* Theoretical maximum frame size for an MPEG audio stream, which occurs when playing a Layer 2
* MPEG 2.5 audio stream at 16 kb/s (with padding). The size is 1152 sample/frame *
* 160000 bit/s / (8000 sample/s * 8 bit/byte) + 1 padding byte/frame = 2881 byte/frame.
* The next power of two size is 4 KiB.
*/
public
static
final
int
MAX_FRAME_SIZE_BYTES
=
4096
;
private
final
BufferingInput
inputBuffer
;
private
final
BufferingInput
inputBuffer
;
private
final
ParsableByteArray
scratch
;
private
final
ParsableByteArray
scratch
;
private
final
MpegAudioHeader
synchronizedHeader
;
private
final
MpegAudioHeader
synchronizedHeader
;
...
@@ -74,7 +64,7 @@ public final class Mp3Extractor implements Extractor {
...
@@ -74,7 +64,7 @@ public final class Mp3Extractor implements Extractor {
/** Constructs a new {@link Mp3Extractor}. */
/** Constructs a new {@link Mp3Extractor}. */
public
Mp3Extractor
()
{
public
Mp3Extractor
()
{
inputBuffer
=
new
BufferingInput
(
MAX_FRAME_SIZE_BYTES
*
3
);
inputBuffer
=
new
BufferingInput
(
M
pegAudioHeader
.
M
AX_FRAME_SIZE_BYTES
*
3
);
scratch
=
new
ParsableByteArray
(
4
);
scratch
=
new
ParsableByteArray
(
4
);
synchronizedHeader
=
new
MpegAudioHeader
();
synchronizedHeader
=
new
MpegAudioHeader
();
}
}
...
@@ -255,10 +245,9 @@ public final class Mp3Extractor implements Extractor {
...
@@ -255,10 +245,9 @@ public final class Mp3Extractor implements Extractor {
if
(
seeker
==
null
)
{
if
(
seeker
==
null
)
{
setupSeeker
(
extractorInput
,
headerPosition
);
setupSeeker
(
extractorInput
,
headerPosition
);
extractorOutput
.
seekMap
(
seeker
);
extractorOutput
.
seekMap
(
seeker
);
trackOutput
.
format
(
MediaFormat
.
createAudioFormat
(
trackOutput
.
format
(
MediaFormat
.
createAudioFormat
(
synchronizedHeader
.
mimeType
,
MIME_TYPE_BY_LAYER
[
synchronizedHeader
.
layerIndex
],
MAX_FRAME_SIZE_BYTES
,
MpegAudioHeader
.
MAX_FRAME_SIZE_BYTES
,
seeker
.
getDurationUs
(),
synchronizedHeader
.
channels
,
seeker
.
getDurationUs
(),
synchronizedHeader
.
channels
,
synchronizedHeader
.
sampleRate
,
synchronizedHeader
.
sampleRate
,
Collections
.<
byte
[]>
emptyList
()));
Collections
.<
byte
[]>
emptyList
()));
}
}
return
headerPosition
;
return
headerPosition
;
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp3/VbriSeeker.java
View file @
3bcd9ca6
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
extractor
.
mp3
;
package
com
.
google
.
android
.
exoplayer
.
extractor
.
mp3
;
import
com.google.android.exoplayer.util.MpegAudioHeader
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp3/XingSeeker.java
View file @
3bcd9ca6
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer
.
extractor
.
mp3
;
package
com
.
google
.
android
.
exoplayer
.
extractor
.
mp3
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.util.MpegAudioHeader
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
...
...
library/src/main/java/com/google/android/exoplayer/extractor/ts/AdtsReader.java
View file @
3bcd9ca6
...
@@ -130,7 +130,7 @@ import java.util.Collections;
...
@@ -130,7 +130,7 @@ import java.util.Collections;
* Locates the next sync word, advancing the position to the byte that immediately follows it.
* Locates the next sync word, advancing the position to the byte that immediately follows it.
* If a sync word was not located, the position is advanced to the limit.
* If a sync word was not located, the position is advanced to the limit.
*
*
* @param pesBuffer The buffer
in which to search for the sync wor
d.
* @param pesBuffer The buffer
whose position should be advance
d.
* @return True if a sync word position was found. False otherwise.
* @return True if a sync word position was found. False otherwise.
*/
*/
private
boolean
skipToNextSync
(
ParsableByteArray
pesBuffer
)
{
private
boolean
skipToNextSync
(
ParsableByteArray
pesBuffer
)
{
...
...
library/src/main/java/com/google/android/exoplayer/extractor/ts/Mp
a
Reader.java
→
library/src/main/java/com/google/android/exoplayer/extractor/ts/Mp
egAudio
Reader.java
View file @
3bcd9ca6
...
@@ -18,8 +18,7 @@ package com.google.android.exoplayer.extractor.ts;
...
@@ -18,8 +18,7 @@ package com.google.android.exoplayer.extractor.ts;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.extractor.mp3.Mp3Extractor
;
import
com.google.android.exoplayer.util.MpegAudioHeader
;
import
com.google.android.exoplayer.extractor.mp3.MpegAudioHeader
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
java.util.Collections
;
import
java.util.Collections
;
...
@@ -27,7 +26,7 @@ import java.util.Collections;
...
@@ -27,7 +26,7 @@ import java.util.Collections;
/**
/**
* Parses a continuous MPEG Audio byte stream and extracts individual frames.
* Parses a continuous MPEG Audio byte stream and extracts individual frames.
*/
*/
/* package */
public
class
Mpa
Reader
extends
ElementaryStreamReader
{
/* package */
class
MpegAudio
Reader
extends
ElementaryStreamReader
{
private
static
final
int
STATE_FINDING_HEADER
=
0
;
private
static
final
int
STATE_FINDING_HEADER
=
0
;
private
static
final
int
STATE_READING_HEADER
=
1
;
private
static
final
int
STATE_READING_HEADER
=
1
;
...
@@ -36,33 +35,35 @@ import java.util.Collections;
...
@@ -36,33 +35,35 @@ import java.util.Collections;
private
static
final
int
HEADER_SIZE
=
4
;
private
static
final
int
HEADER_SIZE
=
4
;
private
final
ParsableByteArray
headerScratch
;
private
final
ParsableByteArray
headerScratch
;
private
final
MpegAudioHeader
header
;
private
int
state
;
private
int
state
;
private
int
bytesRead
;
private
int
frameBytesRead
;
private
boolean
hasOutputFormat
;
// Used
to find th
e header.
// Used
when finding the fram
e header.
private
boolean
lastByteWasFF
;
private
boolean
lastByteWasFF
;
// Used when parsing the header.
// Parsed from the frame header.
private
boolean
hasOutputFormat
;
private
long
frameDurationUs
;
private
long
frameDurationUs
;
private
int
sampl
eSize
;
private
int
fram
eSize
;
//
Used when reading the samples
.
//
The timestamp to attach to the next sample in the current packet
.
private
long
timeUs
;
private
long
timeUs
;
public
Mp
a
Reader
(
TrackOutput
output
)
{
public
Mp
egAudio
Reader
(
TrackOutput
output
)
{
super
(
output
);
super
(
output
);
state
=
STATE_FINDING_HEADER
;
state
=
STATE_FINDING_HEADER
;
// The first byte of an MPEG Audio frame header is always 0xFF.
// The first byte of an MPEG Audio frame header is always 0xFF.
headerScratch
=
new
ParsableByteArray
(
4
);
headerScratch
=
new
ParsableByteArray
(
4
);
headerScratch
.
data
[
0
]
=
(
byte
)
0xFF
;
headerScratch
.
data
[
0
]
=
(
byte
)
0xFF
;
header
=
new
MpegAudioHeader
();
}
}
@Override
@Override
public
void
seek
()
{
public
void
seek
()
{
state
=
STATE_FINDING_HEADER
;
state
=
STATE_FINDING_HEADER
;
b
ytesRead
=
0
;
frameB
ytesRead
=
0
;
lastByteWasFF
=
false
;
lastByteWasFF
=
false
;
}
}
...
@@ -74,19 +75,13 @@ import java.util.Collections;
...
@@ -74,19 +75,13 @@ import java.util.Collections;
while
(
data
.
bytesLeft
()
>
0
)
{
while
(
data
.
bytesLeft
()
>
0
)
{
switch
(
state
)
{
switch
(
state
)
{
case
STATE_FINDING_HEADER:
case
STATE_FINDING_HEADER:
if
(
findHeader
(
data
))
{
findHeader
(
data
);
state
=
STATE_READING_HEADER
;
}
break
;
break
;
case
STATE_READING_HEADER:
case
STATE_READING_HEADER:
if
(
readHeaderRemainder
(
data
))
{
readHeaderRemainder
(
data
);
state
=
STATE_READING_FRAME
;
}
break
;
break
;
case
STATE_READING_FRAME:
case
STATE_READING_FRAME:
if
(
readFrame
(
data
))
{
readFrameRemainder
(
data
);
state
=
STATE_FINDING_HEADER
;
}
break
;
break
;
}
}
}
}
...
@@ -100,78 +95,83 @@ import java.util.Collections;
...
@@ -100,78 +95,83 @@ import java.util.Collections;
/**
/**
* Attempts to locate the start of the next frame header.
* Attempts to locate the start of the next frame header.
* <p>
* <p>
* If a frame header is located then t
rue is returned. The first two bytes of the header will hav
e
* If a frame header is located then t
he state is changed to {@link #STATE_READING_HEADER}, th
e
*
been written into {@link #headerScratch}, and the position of the source will have been
*
first two bytes of the header are written into {@link #headerScratch}, and the position of the
* advanced to the byte that immediately follows these two bytes.
*
source is
advanced to the byte that immediately follows these two bytes.
* <p>
* <p>
* If a frame header is not located then the position of the source
will have been advanced to the
* If a frame header is not located then the position of the source
is advanced to the limit, and
*
limit, and
the method should be called again with the next source to continue the search.
* the method should be called again with the next source to continue the search.
*
*
* @param source The source from which to read.
* @param source The source from which to read.
* @return True if the frame header was located. False otherwise.
*/
*/
private
boolean
findHeader
(
ParsableByteArray
source
)
{
private
void
findHeader
(
ParsableByteArray
source
)
{
byte
[]
mpaD
ata
=
source
.
data
;
byte
[]
d
ata
=
source
.
data
;
int
startOffset
=
source
.
getPosition
();
int
startOffset
=
source
.
getPosition
();
int
endOffset
=
source
.
limit
();
int
endOffset
=
source
.
limit
();
for
(
int
i
=
startOffset
;
i
<
endOffset
;
i
++)
{
for
(
int
i
=
startOffset
;
i
<
endOffset
;
i
++)
{
boolean
byteIsFF
=
(
mpaD
ata
[
i
]
&
0xFF
)
==
0xFF
;
boolean
byteIsFF
=
(
d
ata
[
i
]
&
0xFF
)
==
0xFF
;
boolean
found
=
lastByteWasFF
&&
(
mpaData
[
i
]
&
0xF0
)
==
0xF
0
;
boolean
found
=
lastByteWasFF
&&
(
data
[
i
]
&
0xE0
)
==
0xE
0
;
lastByteWasFF
=
byteIsFF
;
lastByteWasFF
=
byteIsFF
;
if
(
found
)
{
if
(
found
)
{
source
.
setPosition
(
i
+
1
);
source
.
setPosition
(
i
+
1
);
// Reset lastByteWasFF for next time.
// Reset lastByteWasFF for next time.
lastByteWasFF
=
false
;
lastByteWasFF
=
false
;
headerScratch
.
data
[
0
]
=
(
byte
)
0xFF
;
headerScratch
.
data
[
1
]
=
data
[
i
]
;
headerScratch
.
data
[
1
]
=
mpaData
[
i
]
;
frameBytesRead
=
2
;
bytesRead
=
2
;
state
=
STATE_READING_HEADER
;
return
true
;
return
;
}
}
}
}
source
.
setPosition
(
endOffset
);
source
.
setPosition
(
endOffset
);
return
false
;
}
}
/**
/**
* Attempts to read the remaining two bytes of the frame header.
* Attempts to read the remaining two bytes of the frame header.
* <p>
* <p>
* If a frame header is read in full then t
rue is returned. The media format will have been output
* If a frame header is read in full then t
he state is changed to {@link #STATE_READING_FRAME},
*
if this has not previously occurred, the four header bytes will have been output as sampl
e
*
the media format is output if this has not previously occurred, the four header bytes ar
e
*
data, and the position of the source will have been
advanced to the byte that immediately
*
output as sample data, and the position of the source is
advanced to the byte that immediately
* follows the header.
* follows the header.
* <p>
* <p>
* If a frame header is not read in full then the position of the source will have been advanced
* If a frame header is read in full but cannot be parsed then the state is changed to
* to the limit, and the method should be called again with the next source to continue the read.
* {@link #STATE_READING_HEADER}.
* <p>
* If a frame header is not read in full then the position of the source is advanced to the limit,
* and the method should be called again with the next source to continue the read.
*
*
* @param source The source from which to read.
* @param source The source from which to read.
* @return True if the frame header was read in full. False otherwise.
*/
*/
private
boolean
readHeaderRemainder
(
ParsableByteArray
source
)
{
private
void
readHeaderRemainder
(
ParsableByteArray
source
)
{
int
bytesToRead
=
Math
.
min
(
source
.
bytesLeft
(),
HEADER_SIZE
-
bytesRead
);
int
bytesToRead
=
Math
.
min
(
source
.
bytesLeft
(),
HEADER_SIZE
-
frameBytesRead
);
source
.
readBytes
(
headerScratch
.
data
,
bytesRead
,
bytesToRead
);
source
.
readBytes
(
headerScratch
.
data
,
frameBytesRead
,
bytesToRead
);
bytesRead
+=
bytesToRead
;
frameBytesRead
+=
bytesToRead
;
if
(
bytesRead
<
HEADER_SIZE
)
{
if
(
frameBytesRead
<
HEADER_SIZE
)
{
return
false
;
// We haven't read the whole header yet.
return
;
}
}
headerScratch
.
setPosition
(
0
);
boolean
parsedHeader
=
MpegAudioHeader
.
populateHeader
(
headerScratch
.
readInt
(),
header
);
if
(!
parsedHeader
)
{
// We thought we'd located a frame header, but we hadn't.
frameBytesRead
=
0
;
state
=
STATE_READING_HEADER
;
return
;
}
frameSize
=
header
.
frameSize
;
if
(!
hasOutputFormat
)
{
if
(!
hasOutputFormat
)
{
headerScratch
.
setPosition
(
0
);
frameDurationUs
=
(
C
.
MICROS_PER_SECOND
*
header
.
samplesPerFrame
)
/
header
.
sampleRate
;
int
headerInt
=
headerScratch
.
readInt
();
MediaFormat
mediaFormat
=
MediaFormat
.
createAudioFormat
(
header
.
mimeType
,
MpegAudioHeader
synchronizedHeader
=
new
MpegAudioHeader
();
MpegAudioHeader
.
MAX_FRAME_SIZE_BYTES
,
C
.
UNKNOWN_TIME_US
,
header
.
channels
,
MpegAudioHeader
.
populateHeader
(
headerInt
,
synchronizedHeader
);
header
.
sampleRate
,
Collections
.<
byte
[]>
emptyList
());
MediaFormat
mediaFormat
=
MediaFormat
.
createAudioFormat
(
Mp3Extractor
.
MIME_TYPE_BY_LAYER
[
synchronizedHeader
.
layerIndex
],
Mp3Extractor
.
MAX_FRAME_SIZE_BYTES
,
C
.
UNKNOWN_TIME_US
,
synchronizedHeader
.
channels
,
synchronizedHeader
.
sampleRate
,
Collections
.<
byte
[]>
emptyList
());
output
.
format
(
mediaFormat
);
output
.
format
(
mediaFormat
);
hasOutputFormat
=
true
;
hasOutputFormat
=
true
;
frameDurationUs
=
(
C
.
MICROS_PER_SECOND
*
synchronizedHeader
.
samplesPerFrame
)
/
mediaFormat
.
sampleRate
;
sampleSize
=
synchronizedHeader
.
frameSize
;
}
}
headerScratch
.
setPosition
(
0
);
headerScratch
.
setPosition
(
0
);
output
.
sampleData
(
headerScratch
,
HEADER_SIZE
);
output
.
sampleData
(
headerScratch
,
HEADER_SIZE
);
return
true
;
state
=
STATE_READING_FRAME
;
}
}
/**
/**
...
@@ -185,20 +185,20 @@ import java.util.Collections;
...
@@ -185,20 +185,20 @@ import java.util.Collections;
* limit, and the method should be called again with the next source to continue the read.
* limit, and the method should be called again with the next source to continue the read.
*
*
* @param source The source from which to read.
* @param source The source from which to read.
* @return True if the frame was read in full. False otherwise.
*/
*/
private
boolean
readFrame
(
ParsableByteArray
source
)
{
private
void
readFrameRemainder
(
ParsableByteArray
source
)
{
int
bytesToRead
=
Math
.
min
(
source
.
bytesLeft
(),
sampleSize
-
b
ytesRead
);
int
bytesToRead
=
Math
.
min
(
source
.
bytesLeft
(),
frameSize
-
frameB
ytesRead
);
output
.
sampleData
(
source
,
bytesToRead
);
output
.
sampleData
(
source
,
bytesToRead
);
bytesRead
+=
bytesToRead
;
frameBytesRead
+=
bytesToRead
;
if
(
bytesRead
<
sampleSize
)
{
if
(
frameBytesRead
<
frameSize
)
{
return
false
;
// We haven't read the whole of the frame yet.
return
;
}
}
output
.
sampleMetadata
(
timeUs
,
C
.
SAMPLE_FLAG_SYNC
,
sampl
eSize
,
0
,
null
);
output
.
sampleMetadata
(
timeUs
,
C
.
SAMPLE_FLAG_SYNC
,
fram
eSize
,
0
,
null
);
timeUs
+=
frameDurationUs
;
timeUs
+=
frameDurationUs
;
b
ytesRead
=
0
;
frameB
ytesRead
=
0
;
return
true
;
state
=
STATE_FINDING_HEADER
;
}
}
}
}
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
View file @
3bcd9ca6
...
@@ -354,10 +354,10 @@ public final class TsExtractor implements Extractor, SeekMap {
...
@@ -354,10 +354,10 @@ public final class TsExtractor implements Extractor, SeekMap {
ElementaryStreamReader
pesPayloadReader
=
null
;
ElementaryStreamReader
pesPayloadReader
=
null
;
switch
(
streamType
)
{
switch
(
streamType
)
{
case
TS_STREAM_TYPE_MPA:
case
TS_STREAM_TYPE_MPA:
pesPayloadReader
=
new
Mp
a
Reader
(
output
.
track
(
TS_STREAM_TYPE_MPA
));
pesPayloadReader
=
new
Mp
egAudio
Reader
(
output
.
track
(
TS_STREAM_TYPE_MPA
));
break
;
break
;
case
TS_STREAM_TYPE_MPA_LSF:
case
TS_STREAM_TYPE_MPA_LSF:
pesPayloadReader
=
new
Mp
a
Reader
(
output
.
track
(
TS_STREAM_TYPE_MPA_LSF
));
pesPayloadReader
=
new
Mp
egAudio
Reader
(
output
.
track
(
TS_STREAM_TYPE_MPA_LSF
));
break
;
break
;
case
TS_STREAM_TYPE_AAC:
case
TS_STREAM_TYPE_AAC:
pesPayloadReader
=
new
AdtsReader
(
output
.
track
(
TS_STREAM_TYPE_AAC
));
pesPayloadReader
=
new
AdtsReader
(
output
.
track
(
TS_STREAM_TYPE_AAC
));
...
...
library/src/main/java/com/google/android/exoplayer/util/CodecSpecificDataUtil.java
View file @
3bcd9ca6
...
@@ -41,22 +41,6 @@ public final class CodecSpecificDataUtil {
...
@@ -41,22 +41,6 @@ public final class CodecSpecificDataUtil {
private
CodecSpecificDataUtil
()
{}
private
CodecSpecificDataUtil
()
{}
/**
* Gets the sample rate index.
*
* @param sampleRate The sample rate in Hz.
* @return The sample rate index.
*/
public
static
int
getSampleRateIndex
(
int
sampleRate
)
{
int
sampleRateIndex
=
0
;
for
(;
sampleRateIndex
<
AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE
.
length
;
sampleRateIndex
++)
{
if
(
AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE
[
sampleRateIndex
]
==
sampleRate
)
{
return
sampleRateIndex
;
}
}
return
-
1
;
}
/**
/**
* Parses an AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
* Parses an AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
*
*
...
@@ -65,24 +49,13 @@ public final class CodecSpecificDataUtil {
...
@@ -65,24 +49,13 @@ public final class CodecSpecificDataUtil {
*/
*/
public
static
Pair
<
Integer
,
Integer
>
parseAacAudioSpecificConfig
(
byte
[]
audioSpecificConfig
)
{
public
static
Pair
<
Integer
,
Integer
>
parseAacAudioSpecificConfig
(
byte
[]
audioSpecificConfig
)
{
int
audioObjectType
=
(
audioSpecificConfig
[
0
]
>>
3
)
&
0x1F
;
int
audioObjectType
=
(
audioSpecificConfig
[
0
]
>>
3
)
&
0x1F
;
if
(
audioObjectType
<
31
)
{
int
byteOffset
=
audioObjectType
==
5
||
audioObjectType
==
29
?
1
:
0
;
int
byteOffset
=
audioObjectType
==
5
||
audioObjectType
==
29
?
1
:
0
;
int
frequencyIndex
=
(
audioSpecificConfig
[
byteOffset
]
&
0x7
)
<<
1
int
frequencyIndex
=
(
audioSpecificConfig
[
byteOffset
]
&
0x7
)
<<
1
|
|
((
audioSpecificConfig
[
byteOffset
+
1
]
>>
7
)
&
0x1
);
((
audioSpecificConfig
[
byteOffset
+
1
]
>>
7
)
&
0x1
);
Assertions
.
checkState
(
frequencyIndex
<
13
);
Assertions
.
checkState
(
frequencyIndex
<
13
);
int
sampleRate
=
AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE
[
frequencyIndex
];
int
sampleRate
=
AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE
[
frequencyIndex
];
int
channelCount
=
(
audioSpecificConfig
[
byteOffset
+
1
]
>>
3
)
&
0xF
;
int
channelCount
=
AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE
[
return
Pair
.
create
(
sampleRate
,
channelCount
);
(
audioSpecificConfig
[
byteOffset
+
1
]
>>
3
)
&
0xF
];
return
Pair
.
create
(
sampleRate
,
channelCount
);
}
else
{
int
frequencyIndex
=
(
audioSpecificConfig
[
1
]
&
0x1E
)
>>
1
;
Assertions
.
checkState
(
frequencyIndex
<
13
);
int
sampleRate
=
AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE
[
frequencyIndex
];
int
channelCount
=
AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE
[
(
audioSpecificConfig
[
1
]
&
0x01
)
<<
3
|
((
audioSpecificConfig
[
2
]
>>
5
)
&
0x07
)];
return
Pair
.
create
(
sampleRate
,
channelCount
);
}
}
}
/**
/**
...
...
library/src/main/java/com/google/android/exoplayer/
extractor/mp3
/MpegAudioHeader.java
→
library/src/main/java/com/google/android/exoplayer/
util
/MpegAudioHeader.java
View file @
3bcd9ca6
...
@@ -13,11 +13,23 @@
...
@@ -13,11 +13,23 @@
* See the License for the specific language governing permissions and
* See the License for the specific language governing permissions and
* limitations under the License.
* limitations under the License.
*/
*/
package
com
.
google
.
android
.
exoplayer
.
extractor
.
mp3
;
package
com
.
google
.
android
.
exoplayer
.
util
;
/** Parsed MPEG audio frame header. */
/**
* Representation of an MPEG audio frame header.
*/
public
final
class
MpegAudioHeader
{
public
final
class
MpegAudioHeader
{
/**
* Theoretical maximum frame size for an MPEG audio stream, which occurs when playing a Layer 2
* MPEG 2.5 audio stream at 16 kb/s (with padding). The size is 1152 sample/frame *
* 160000 bit/s / (8000 sample/s * 8 bit/byte) + 1 padding byte/frame = 2881 byte/frame.
* The next power of two size is 4 KiB.
*/
public
static
final
int
MAX_FRAME_SIZE_BYTES
=
4096
;
private
static
final
String
[]
MIME_TYPE_BY_LAYER
=
new
String
[]
{
MimeTypes
.
AUDIO_MPEG_L1
,
MimeTypes
.
AUDIO_MPEG_L2
,
MimeTypes
.
AUDIO_MPEG
};
private
static
final
int
[]
SAMPLING_RATE_V1
=
{
44100
,
48000
,
32000
};
private
static
final
int
[]
SAMPLING_RATE_V1
=
{
44100
,
48000
,
32000
};
private
static
final
int
[]
BITRATE_V1_L1
=
private
static
final
int
[]
BITRATE_V1_L1
=
{
32
,
64
,
96
,
128
,
160
,
192
,
224
,
256
,
288
,
320
,
352
,
384
,
416
,
448
};
{
32
,
64
,
96
,
128
,
160
,
192
,
224
,
256
,
288
,
320
,
352
,
384
,
416
,
448
};
...
@@ -30,7 +42,9 @@ public final class MpegAudioHeader {
...
@@ -30,7 +42,9 @@ public final class MpegAudioHeader {
private
static
final
int
[]
BITRATE_V2
=
private
static
final
int
[]
BITRATE_V2
=
{
8
,
16
,
24
,
32
,
40
,
48
,
56
,
64
,
80
,
96
,
112
,
128
,
144
,
160
};
{
8
,
16
,
24
,
32
,
40
,
48
,
56
,
64
,
80
,
96
,
112
,
128
,
144
,
160
};
/** Returns the size of the frame associated with {@code header}, or -1 if it is invalid. */
/**
* Returns the size of the frame associated with {@code header}, or -1 if it is invalid.
*/
public
static
int
getFrameSize
(
int
header
)
{
public
static
int
getFrameSize
(
int
header
)
{
if
((
header
&
0xFFE00000
)
!=
0xFFE00000
)
{
if
((
header
&
0xFFE00000
)
!=
0xFFE00000
)
{
return
-
1
;
return
-
1
;
...
@@ -92,35 +106,37 @@ public final class MpegAudioHeader {
...
@@ -92,35 +106,37 @@ public final class MpegAudioHeader {
}
}
/**
/**
*
Returns the header represented by {@code header}, if it is valid; {@code null} otherwise
.
*
Parses {@code headerData}, populating {@code header} with the parsed data
.
*
*
* @param headerData Header data to parse.
* @param headerData Header data to parse.
* @param header Header to populate with data from {@code headerData}.
* @param header Header to populate with data from {@code headerData}.
* @return True if the header was populated. False otherwise, indicating that {@code headerData}
* is not a valid MPEG audio header.
*/
*/
public
static
void
populateHeader
(
int
headerData
,
MpegAudioHeader
header
)
{
public
static
boolean
populateHeader
(
int
headerData
,
MpegAudioHeader
header
)
{
if
((
headerData
&
0xFFE00000
)
!=
0xFFE00000
)
{
if
((
headerData
&
0xFFE00000
)
!=
0xFFE00000
)
{
return
;
return
false
;
}
}
int
version
=
(
headerData
>>>
19
)
&
3
;
int
version
=
(
headerData
>>>
19
)
&
3
;
if
(
version
==
1
)
{
if
(
version
==
1
)
{
return
;
return
false
;
}
}
int
layer
=
(
headerData
>>>
17
)
&
3
;
int
layer
=
(
headerData
>>>
17
)
&
3
;
if
(
layer
==
0
)
{
if
(
layer
==
0
)
{
return
;
return
false
;
}
}
int
bitrateIndex
=
(
headerData
>>>
12
)
&
15
;
int
bitrateIndex
=
(
headerData
>>>
12
)
&
15
;
if
(
bitrateIndex
==
0
||
bitrateIndex
==
0xF
)
{
if
(
bitrateIndex
==
0
||
bitrateIndex
==
0xF
)
{
// Disallow "free" bitrate.
// Disallow "free" bitrate.
return
;
return
false
;
}
}
int
samplingRateIndex
=
(
headerData
>>>
10
)
&
3
;
int
samplingRateIndex
=
(
headerData
>>>
10
)
&
3
;
if
(
samplingRateIndex
==
3
)
{
if
(
samplingRateIndex
==
3
)
{
return
;
return
false
;
}
}
int
sampleRate
=
SAMPLING_RATE_V1
[
samplingRateIndex
];
int
sampleRate
=
SAMPLING_RATE_V1
[
samplingRateIndex
];
...
@@ -154,16 +170,16 @@ public final class MpegAudioHeader {
...
@@ -154,16 +170,16 @@ public final class MpegAudioHeader {
}
}
}
}
String
mimeType
=
MIME_TYPE_BY_LAYER
[
3
-
layer
];
int
channels
=
((
headerData
>>
6
)
&
3
)
==
3
?
1
:
2
;
int
channels
=
((
headerData
>>
6
)
&
3
)
==
3
?
1
:
2
;
int
layerIndex
=
3
-
layer
;
header
.
setValues
(
version
,
mimeType
,
frameSize
,
sampleRate
,
channels
,
bitrate
,
samplesPerFrame
);
header
.
setValues
(
return
true
;
version
,
layerIndex
,
frameSize
,
sampleRate
,
channels
,
bitrate
,
samplesPerFrame
);
}
}
/** MPEG audio header version. */
/** MPEG audio header version. */
public
int
version
;
public
int
version
;
/**
MPEG audio layer index, starting at zero
. */
/**
The mime type
. */
public
int
layerIndex
;
public
String
mimeType
;
/** Size of the frame associated with this header, in bytes. */
/** Size of the frame associated with this header, in bytes. */
public
int
frameSize
;
public
int
frameSize
;
/** Sample rate in samples per second. */
/** Sample rate in samples per second. */
...
@@ -175,10 +191,10 @@ public final class MpegAudioHeader {
...
@@ -175,10 +191,10 @@ public final class MpegAudioHeader {
/** Number of samples stored in the frame. */
/** Number of samples stored in the frame. */
public
int
samplesPerFrame
;
public
int
samplesPerFrame
;
private
void
setValues
(
int
version
,
int
layerIndex
,
int
frameSize
,
int
sampleRate
,
int
channels
,
private
void
setValues
(
int
version
,
String
mimeType
,
int
frameSize
,
int
bitrate
,
int
samplesPerFrame
)
{
int
sampleRate
,
int
channels
,
int
bitrate
,
int
samplesPerFrame
)
{
this
.
version
=
version
;
this
.
version
=
version
;
this
.
layerIndex
=
layerIndex
;
this
.
mimeType
=
mimeType
;
this
.
frameSize
=
frameSize
;
this
.
frameSize
=
frameSize
;
this
.
sampleRate
=
sampleRate
;
this
.
sampleRate
=
sampleRate
;
this
.
channels
=
channels
;
this
.
channels
=
channels
;
...
...
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