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
c06f844e
authored
Jun 25, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
SUPER!
parent
ed856b2a
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
161 additions
and
259 deletions
library/src/main/java/com/google/android/exoplayer/extractor/mp3/Mp3Extractor.java
library/src/main/java/com/google/android/exoplayer/extractor/mp3/MpegAudioHeader.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/mp3/Mp3Extractor.java
View file @
c06f844e
...
@@ -43,7 +43,7 @@ public final class Mp3Extractor implements Extractor {
...
@@ -43,7 +43,7 @@ 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"
);
p
rivate
static
final
String
[]
MIME_TYPE_BY_LAYER
=
p
ublic
static
final
String
[]
MIME_TYPE_BY_LAYER
=
new
String
[]
{
MimeTypes
.
AUDIO_MPEG_L1
,
MimeTypes
.
AUDIO_MPEG_L2
,
MimeTypes
.
AUDIO_MPEG
};
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"
);
...
@@ -55,7 +55,7 @@ public final class Mp3Extractor implements Extractor {
...
@@ -55,7 +55,7 @@ public final class Mp3Extractor implements Extractor {
* 160000 bit/s / (8000 sample/s * 8 bit/byte) + 1 padding byte/frame = 2881 byte/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.
* The next power of two size is 4 KiB.
*/
*/
p
rivate
static
final
int
MAX_FRAME_SIZE_BYTES
=
4096
;
p
ublic
static
final
int
MAX_FRAME_SIZE_BYTES
=
4096
;
private
final
BufferingInput
inputBuffer
;
private
final
BufferingInput
inputBuffer
;
private
final
ParsableByteArray
scratch
;
private
final
ParsableByteArray
scratch
;
...
...
library/src/main/java/com/google/android/exoplayer/extractor/mp3/MpegAudioHeader.java
View file @
c06f844e
...
@@ -16,7 +16,7 @@
...
@@ -16,7 +16,7 @@
package
com
.
google
.
android
.
exoplayer
.
extractor
.
mp3
;
package
com
.
google
.
android
.
exoplayer
.
extractor
.
mp3
;
/** Parsed MPEG audio frame header. */
/** Parsed MPEG audio frame header. */
/* package */
final
class
MpegAudioHeader
{
public
final
class
MpegAudioHeader
{
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
=
...
...
library/src/main/java/com/google/android/exoplayer/extractor/ts/AdtsReader.java
View file @
c06f844e
...
@@ -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
whose position should be advance
d.
* @param pesBuffer The buffer
in which to search for the sync wor
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/MpaReader.java
View file @
c06f844e
...
@@ -18,285 +18,187 @@ package com.google.android.exoplayer.extractor.ts;
...
@@ -18,285 +18,187 @@ 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.util.CodecSpecificDataUtil
;
import
com.google.android.exoplayer.extractor.mp3.Mp3Extractor
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.extractor.mp3.MpegAudioHeader
;
import
com.google.android.exoplayer.util.ParsableBitArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
android.util.Pair
;
import
java.util.Collections
;
import
java.util.Collections
;
/**
/**
* Parses a continuous MPEG Audio byte stream and extracts individual
* Parses a continuous MPEG Audio byte stream and extracts individual frames.
* frames.
*/
*/
/* package */
public
class
MpaReader
extends
ElementaryStreamReader
{
/* package */
public
class
MpaReader
extends
ElementaryStreamReader
{
private
static
final
int
STATE_FINDING_SYNC
=
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
;
private
static
final
int
STATE_READING_SAMPLE
=
2
;
private
static
final
int
STATE_READING_FRAME
=
2
;
private
static
final
int
HEADER_SIZE
=
4
;
private
static
final
int
CRC_SIZE
=
2
;
private
final
ParsableBitArray
mpaScratch
;
private
int
state
;
private
int
bytesRead
;
// Used to find the header.
private
boolean
hasCrc
;
// Used when parsing the header.
private
static
final
int
HEADER_SIZE
=
4
;
private
boolean
hasOutputFormat
;
private
long
frameDurationUs
;
private
int
sampleSize
;
// Used when reading the samples.
private
final
ParsableByteArray
headerScratch
;
private
long
timeUs
;
//
private
int
state
;
/**
private
int
bytesRead
;
* sampling rates in hertz:
*
* @index MPEG Version ID
* @index sampling rate index
*/
private
static
final
int
[][]
MPA_SAMPLING_RATES
=
new
int
[][]
{
// Used to find the header.
{
11025
,
12000
,
8000
},
// MPEG 2.5
private
boolean
lastByteWasFF
;
{
0
,
0
,
0
},
// reserved
{
22050
,
24000
,
16000
},
// MPEG 2
{
44100
,
48000
,
32000
}
// MPEG 1
};
/**
// Used when parsing the header.
* bitrates:
private
boolean
hasOutputFormat
;
*
private
long
frameDurationUs
;
* @index LSF
private
int
sampleSize
;
* @index Layer
* @index bitrate index
*/
private
static
final
int
[][][]
MPA_BITRATES
=
new
int
[][][]
{
// Used when reading the samples.
{
// MPEG 1
private
long
timeUs
;
// Layer1
{
0
,
32
,
64
,
96
,
128
,
160
,
192
,
224
,
256
,
288
,
320
,
352
,
384
,
416
,
448
},
// Layer2
{
0
,
32
,
48
,
56
,
64
,
80
,
96
,
112
,
128
,
160
,
192
,
224
,
256
,
320
,
384
},
// Layer3
{
0
,
32
,
40
,
48
,
56
,
64
,
80
,
96
,
112
,
128
,
160
,
192
,
224
,
256
,
320
}
},
{
// MPEG 2, 2.5
// Layer1
{
0
,
32
,
48
,
56
,
64
,
80
,
96
,
112
,
128
,
144
,
160
,
176
,
192
,
224
,
256
},
// Layer2
{
0
,
8
,
16
,
24
,
32
,
40
,
48
,
56
,
64
,
80
,
96
,
112
,
128
,
144
,
160
},
// Layer3
{
0
,
8
,
16
,
24
,
32
,
40
,
48
,
56
,
64
,
80
,
96
,
112
,
128
,
144
,
160
}
}
};
/**
public
MpaReader
(
TrackOutput
output
)
{
* Samples per Frame:
super
(
output
);
*
state
=
STATE_FINDING_HEADER
;
* @index LSF
// The first byte of an MPEG Audio frame header is always 0xFF.
* @index Layer
headerScratch
=
new
ParsableByteArray
(
4
);
*/
headerScratch
.
data
[
0
]
=
(
byte
)
0xFF
;
}
private
static
final
int
[][]
MPA_SAMPLES_PER_FRAME
=
new
int
[][]
{
@Override
{
// MPEG 1
public
void
seek
()
{
384
,
// Layer1
state
=
STATE_FINDING_HEADER
;
1152
,
// Layer2
bytesRead
=
0
;
1152
// Layer3
lastByteWasFF
=
false
;
},
}
{
// MPEG 2, 2.5
384
,
// Layer1
1152
,
// Layer2
576
// Layer3
}
};
/**
@Override
* Coefficients (samples per frame / 8):
public
void
consume
(
ParsableByteArray
data
,
long
pesTimeUs
,
boolean
startOfPacket
)
{
*
if
(
startOfPacket
)
{
* @index = LSF
timeUs
=
pesTimeUs
;
* @index = Layer
*/
private
static
final
int
[][]
MPA_COEFFICIENTS
=
new
int
[][]
{
{
// MPEG 1
12
,
// Layer1
144
,
// Layer2
144
// Layer3
},
{
// MPEG 2, 2.5
12
,
// Layer1
144
,
// Layer2
72
// Layer3
}
};
/**
* slot size per layer:
*
* @index = Layer
*/
private
static
final
int
[]
MPA_SLOT_SIZE
=
new
int
[]
{
4
,
// Layer1
1
,
// Layer2
1
// Layer3
};
public
MpaReader
(
TrackOutput
output
)
{
super
(
output
);
mpaScratch
=
new
ParsableBitArray
(
new
byte
[
HEADER_SIZE
+
CRC_SIZE
]);
state
=
STATE_FINDING_SYNC
;
}
}
while
(
data
.
bytesLeft
()
>
0
)
{
@Override
switch
(
state
)
{
public
void
consume
(
ParsableByteArray
data
,
long
pesTimeUs
,
boolean
startOfPacket
)
{
case
STATE_FINDING_HEADER:
if
(
startOfPacket
)
{
if
(
findHeader
(
data
))
{
timeUs
=
pesTimeUs
;
state
=
STATE_READING_HEADER
;
}
}
while
(
data
.
bytesLeft
()
>
0
)
{
break
;
switch
(
state
)
{
case
STATE_READING_HEADER:
case
STATE_FINDING_SYNC:
if
(
readHeaderRemainder
(
data
))
{
if
(
skipToNextSync
(
data
))
{
state
=
STATE_READING_FRAME
;
bytesRead
=
0
;
}
state
=
STATE_READING_HEADER
;
break
;
}
case
STATE_READING_FRAME:
break
;
if
(
readFrame
(
data
))
{
case
STATE_READING_HEADER:
state
=
STATE_FINDING_HEADER
;
int
targetLength
=
hasCrc
?
HEADER_SIZE
+
CRC_SIZE
:
HEADER_SIZE
;
}
if
(
continueRead
(
data
,
mpaScratch
.
data
,
targetLength
))
{
break
;
parseHeader
();
}
bytesRead
=
targetLength
;
state
=
STATE_READING_SAMPLE
;
}
break
;
case
STATE_READING_SAMPLE:
int
bytesToRead
=
Math
.
min
(
data
.
bytesLeft
(),
sampleSize
-
bytesRead
);
output
.
sampleData
(
data
,
bytesToRead
);
bytesRead
+=
bytesToRead
;
if
(
bytesRead
==
sampleSize
)
{
output
.
sampleMetadata
(
timeUs
,
C
.
SAMPLE_FLAG_SYNC
,
sampleSize
,
0
,
null
);
timeUs
+=
frameDurationUs
;
bytesRead
=
0
;
state
=
STATE_FINDING_SYNC
;
}
break
;
}
}
}
}
}
@Override
public
void
packetFinished
()
{
@Override
// Do nothing.
public
void
packetFinished
()
{
}
// Do nothing.
}
/**
* Continues a read from the provided {@code source} into a given {@code target}. It's assumed
/**
* that the data should be written into {@code target} starting from an offset of zero.
* Attempts to locate the start of the next frame header.
*
* <p>
* @param source The source from which to read.
* If a frame header is located then true is returned. The first two bytes of the header will have
* @param target The target into which data is to be read.
* been written into {@link #headerScratch}, and the position of the source will have been
* @param targetLength The target length of the read.
* advanced to the byte that immediately follows these two bytes.
* @return Whether the target length was reached.
* <p>
*/
* If a frame header is not located then the position of the source will have been advanced to the
private
boolean
continueRead
(
ParsableByteArray
source
,
byte
[]
target
,
int
targetLength
)
{
* limit, and the method should be called again with the next source to continue the search.
int
bytesToRead
=
Math
.
min
(
source
.
bytesLeft
(),
targetLength
-
bytesRead
);
*
source
.
readBytes
(
target
,
bytesRead
,
bytesToRead
);
* @param source The source from which to read.
bytesRead
+=
bytesToRead
;
* @return True if the frame header was located. False otherwise.
return
bytesRead
==
targetLength
;
*/
private
boolean
findHeader
(
ParsableByteArray
source
)
{
byte
[]
mpaData
=
source
.
data
;
int
startOffset
=
source
.
getPosition
();
int
endOffset
=
source
.
limit
();
for
(
int
i
=
startOffset
;
i
<
endOffset
;
i
++)
{
boolean
byteIsFF
=
(
mpaData
[
i
]
&
0xFF
)
==
0xFF
;
boolean
found
=
lastByteWasFF
&&
(
mpaData
[
i
]
&
0xF0
)
==
0xF0
;
lastByteWasFF
=
byteIsFF
;
if
(
found
)
{
source
.
setPosition
(
i
+
1
);
// Reset lastByteWasFF for next time.
lastByteWasFF
=
false
;
headerScratch
.
data
[
0
]
=
(
byte
)
0xFF
;
headerScratch
.
data
[
1
]
=
mpaData
[
i
];
bytesRead
=
2
;
return
true
;
}
}
}
source
.
setPosition
(
endOffset
);
/**
return
false
;
* 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.
*
/**
* @param pesBuffer The buffer whose position should be advanced.
* Attempts to read the remaining two bytes of the frame header.
* @return True if a sync word position was found. False otherwise.
* <p>
*/
* If a frame header is read in full then true is returned. The media format will have been output
private
boolean
skipToNextSync
(
ParsableByteArray
pesBuffer
)
{
* if this has not previously occurred, the four header bytes will have been output as sample
byte
[]
mpaData
=
pesBuffer
.
data
;
* data, and the position of the source will have been advanced to the byte that immediately
int
startOffset
=
pesBuffer
.
getPosition
();
* follows the header.
int
endOffset
=
pesBuffer
.
limit
();
* <p>
for
(
int
i
=
startOffset
;
i
<
endOffset
-
1
;
i
++)
{
* If a frame header is not read in full then the position of the source will have been advanced
int
syncBits
=
((
mpaData
[
i
]
&
0xFF
)
<<
8
)
|
(
mpaData
[
i
+
1
]
&
0xFF
);
* to the limit, and the method should be called again with the next source to continue the read.
if
((
syncBits
&
0xFFF0
)
==
0xFFF0
)
{
*
hasCrc
=
(
mpaData
[
i
+
1
]
&
0x1
)
==
0
;
* @param source The source from which to read.
pesBuffer
.
setPosition
(
i
);
* @return True if the frame header was read in full. False otherwise.
return
true
;
*/
}
private
boolean
readHeaderRemainder
(
ParsableByteArray
source
)
{
}
int
bytesToRead
=
Math
.
min
(
source
.
bytesLeft
(),
HEADER_SIZE
-
bytesRead
);
pesBuffer
.
setPosition
(
endOffset
);
source
.
readBytes
(
headerScratch
.
data
,
bytesRead
,
bytesToRead
);
return
false
;
bytesRead
+=
bytesToRead
;
if
(
bytesRead
<
HEADER_SIZE
)
{
return
false
;
}
}
/**
if
(!
hasOutputFormat
)
{
* Calculates MPEG Audio frame size
headerScratch
.
setPosition
(
0
);
*
int
headerInt
=
headerScratch
.
readInt
();
* @param layer The MPEG layer
MpegAudioHeader
synchronizedHeader
=
new
MpegAudioHeader
();
* @param LSF Low Sample rate Format (MPEG 2)
MpegAudioHeader
.
populateHeader
(
headerInt
,
synchronizedHeader
);
* @param bitrate The bitrate in bits per second
MediaFormat
mediaFormat
=
MediaFormat
.
createAudioFormat
(
* @param samplesPerSec The sampling rate in hertz
Mp3Extractor
.
MIME_TYPE_BY_LAYER
[
synchronizedHeader
.
layerIndex
],
Mp3Extractor
.
MAX_FRAME_SIZE_BYTES
,
* @param -paddingSize
C
.
UNKNOWN_TIME_US
,
synchronizedHeader
.
channels
,
synchronizedHeader
.
sampleRate
,
* @return Frame size in bytes
Collections
.<
byte
[]>
emptyList
());
*/
output
.
format
(
mediaFormat
);
private
static
int
CalcMpaFrameSize
(
int
layer
,
int
LSF
,
int
bitrate
,
int
samplesPerSec
,
int
paddingSize
)
{
hasOutputFormat
=
true
;
return
(
int
)(
Math
.
floor
(
MPA_COEFFICIENTS
[
LSF
][
layer
]
*
bitrate
/
samplesPerSec
)
+
paddingSize
)
*
MPA_SLOT_SIZE
[
layer
];
frameDurationUs
=
(
C
.
MICROS_PER_SECOND
*
synchronizedHeader
.
samplesPerFrame
)
/
mediaFormat
.
sampleRate
;
sampleSize
=
synchronizedHeader
.
frameSize
;
}
}
/**
headerScratch
.
setPosition
(
0
);
* Parses the sample header.
output
.
sampleData
(
headerScratch
,
HEADER_SIZE
);
*/
return
true
;
private
void
parseHeader
()
{
}
int
headerLength
=
hasCrc
?
HEADER_SIZE
+
CRC_SIZE
:
HEADER_SIZE
;
/**
if
(!
hasOutputFormat
)
{
* Attempts to read the remainder of the frame.
mpaScratch
.
setPosition
(
0
);
* <p>
mpaScratch
.
skipBits
(
12
);
* If a frame is read in full then true is returned. The frame will have been output, and the
int
isLSF
=
(!
mpaScratch
.
readBit
())
?
1
:
0
;
* position of the source will have been advanced to the byte that immediately follows the end of
int
layer
=
mpaScratch
.
readBits
(
2
)
^
3
;
* the frame.
mpaScratch
.
skipBits
(
1
);
* <p>
int
audioObjectType
=
32
+
layer
;
* If a frame is not read in full then the position of the source will have been advanced to the
int
bitRate
=
MPA_BITRATES
[
isLSF
][
layer
][
mpaScratch
.
readBits
(
4
)];
* limit, and the method should be called again with the next source to continue the read.
int
sampleRate
=
MPA_SAMPLING_RATES
[
3
-
isLSF
][
mpaScratch
.
readBits
(
2
)];
*
int
sampleRateIndex
=
CodecSpecificDataUtil
.
getSampleRateIndex
(
sampleRate
);
* @param source The source from which to read.
int
paddingBit
=
(
mpaScratch
.
readBit
())
?
1
:
0
;
* @return True if the frame was read in full. False otherwise.
mpaScratch
.
skipBits
(
1
);
*/
int
channelConfig
=
mpaScratch
.
readBits
(
2
)
==
3
?
1
:
2
;
private
boolean
readFrame
(
ParsableByteArray
source
)
{
int
bytesToRead
=
Math
.
min
(
source
.
bytesLeft
(),
sampleSize
-
bytesRead
);
byte
[]
audioSpecificConfig
=
CodecSpecificDataUtil
.
buildAudioSpecificConfig
(
output
.
sampleData
(
source
,
bytesToRead
);
audioObjectType
,
sampleRateIndex
,
channelConfig
);
bytesRead
+=
bytesToRead
;
Pair
<
Integer
,
Integer
>
audioParams
=
CodecSpecificDataUtil
.
parseAudioSpecificConfig
(
if
(
bytesRead
<
sampleSize
)
{
audioSpecificConfig
);
return
false
;
// need to investigate how to detect if the mpeg decoder supports Layers other than Layer III
MediaFormat
mediaFormat
=
MediaFormat
.
createAudioFormat
(
/*isLSF == 1 ?*/
MimeTypes
.
AUDIO_MPEG
/* : MimeTypes.AUDIO_MP1L2*/
,
MediaFormat
.
NO_VALUE
,
audioParams
.
second
,
audioParams
.
first
,
Collections
.
singletonList
(
audioSpecificConfig
));
output
.
format
(
mediaFormat
);
hasOutputFormat
=
true
;
frameDurationUs
=
(
C
.
MICROS_PER_SECOND
*
MPA_SAMPLES_PER_FRAME
[
isLSF
][
layer
])
/
mediaFormat
.
sampleRate
;
sampleSize
=
CalcMpaFrameSize
(
layer
,
isLSF
,
bitRate
*
1000
,
sampleRate
,
paddingBit
);
}
mpaScratch
.
setPosition
(
0
);
ParsableByteArray
header
=
new
ParsableByteArray
(
mpaScratch
.
data
,
headerLength
);
output
.
sampleData
(
header
,
headerLength
);
}
}
/* (non-Javadoc)
output
.
sampleMetadata
(
timeUs
,
C
.
SAMPLE_FLAG_SYNC
,
sampleSize
,
0
,
null
);
* @see com.google.android.exoplayer.extractor.ts.ElementaryStreamReader#seek()
timeUs
+=
frameDurationUs
;
*/
bytesRead
=
0
;
@Override
return
true
;
public
void
seek
()
{
}
// TODO(olly): Auto-generated method stub
}
}
}
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