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
4422e8a0
authored
Oct 27, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Further cleanup to FLV extractor
parent
f91ea903
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
221 additions
and
269 deletions
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
library/src/main/java/com/google/android/exoplayer/extractor/flv/AudioTagPayloadReader.java
library/src/main/java/com/google/android/exoplayer/extractor/flv/FlvExtractor.java
library/src/main/java/com/google/android/exoplayer/extractor/flv/ScriptTagPayloadReader.java
library/src/main/java/com/google/android/exoplayer/extractor/flv/TagPayloadReader.java
library/src/main/java/com/google/android/exoplayer/extractor/flv/VideoTagPayloadReader.java
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
View file @
4422e8a0
...
@@ -148,7 +148,6 @@ import java.util.Locale;
...
@@ -148,7 +148,6 @@ import java.util.Locale;
"http://demos.webmproject.org/exoplayer/glass_vp9_vorbis.webm"
,
PlayerActivity
.
TYPE_OTHER
),
"http://demos.webmproject.org/exoplayer/glass_vp9_vorbis.webm"
,
PlayerActivity
.
TYPE_OTHER
),
new
Sample
(
"Big Buck Bunny (FLV Video)"
,
new
Sample
(
"Big Buck Bunny (FLV Video)"
,
"http://vod.leasewebcdn.com/bbb.flv?ri=1024&rs=150&start=0"
,
PlayerActivity
.
TYPE_OTHER
),
"http://vod.leasewebcdn.com/bbb.flv?ri=1024&rs=150&start=0"
,
PlayerActivity
.
TYPE_OTHER
),
};
};
private
Samples
()
{}
private
Samples
()
{}
...
...
library/src/main/java/com/google/android/exoplayer/extractor/flv/AudioTagPayloadReader.java
View file @
4422e8a0
...
@@ -28,7 +28,7 @@ import android.util.Pair;
...
@@ -28,7 +28,7 @@ import android.util.Pair;
import
java.util.Collections
;
import
java.util.Collections
;
/**
/**
* Parses audio tags
of
from an FLV stream and extracts AAC frames.
* Parses audio tags from an FLV stream and extracts AAC frames.
*/
*/
/* package */
final
class
AudioTagPayloadReader
extends
TagPayloadReader
{
/* package */
final
class
AudioTagPayloadReader
extends
TagPayloadReader
{
...
@@ -59,29 +59,22 @@ import java.util.Collections;
...
@@ -59,29 +59,22 @@ import java.util.Collections;
@Override
@Override
protected
boolean
parseHeader
(
ParsableByteArray
data
)
throws
UnsupportedFormatException
{
protected
boolean
parseHeader
(
ParsableByteArray
data
)
throws
UnsupportedFormatException
{
// Parse audio data header, if it was not done, to extract information about the audio codec
// and audio configuration.
if
(!
hasParsedAudioDataHeader
)
{
if
(!
hasParsedAudioDataHeader
)
{
int
header
=
data
.
readUnsignedByte
();
int
header
=
data
.
readUnsignedByte
();
int
audioFormat
=
(
header
>>
4
)
&
0x0F
;
int
audioFormat
=
(
header
>>
4
)
&
0x0F
;
int
sampleRateIndex
=
(
header
>>
2
)
&
0x03
;
int
sampleRateIndex
=
(
header
>>
2
)
&
0x03
;
if
(
sampleRateIndex
<
0
||
sampleRateIndex
>=
AUDIO_SAMPLING_RATE_TABLE
.
length
)
{
if
(
sampleRateIndex
<
0
||
sampleRateIndex
>=
AUDIO_SAMPLING_RATE_TABLE
.
length
)
{
throw
new
UnsupportedFormatException
(
"Invalid sample rate
for the audio track"
);
throw
new
UnsupportedFormatException
(
"Invalid sample rate
index: "
+
sampleRateIndex
);
}
}
// TODO: Add support for MP3 and PCM.
if
(
audioFormat
!=
AUDIO_FORMAT_AAC
)
{
if
(
audioFormat
!=
AUDIO_FORMAT_AAC
)
{
// TODO: Adds support for MP3 and PCM
throw
new
UnsupportedFormatException
(
"Audio format not supported: "
+
audioFormat
);
if
(
audioFormat
!=
AUDIO_FORMAT_AAC
)
{
throw
new
UnsupportedFormatException
(
"Audio format not supported: "
+
audioFormat
);
}
}
}
hasParsedAudioDataHeader
=
true
;
hasParsedAudioDataHeader
=
true
;
}
else
{
}
else
{
// Skip header if it was parsed previously.
// Skip header if it was parsed previously.
data
.
skipBytes
(
1
);
data
.
skipBytes
(
1
);
}
}
// In all the cases we will be managing AAC format (otherwise an exception would be fired so we
// can just always return true.
return
true
;
return
true
;
}
}
...
...
library/src/main/java/com/google/android/exoplayer/extractor/flv/FlvExtractor.java
View file @
4422e8a0
...
@@ -21,63 +21,61 @@ import com.google.android.exoplayer.extractor.ExtractorInput;
...
@@ -21,63 +21,61 @@ import com.google.android.exoplayer.extractor.ExtractorInput;
import
com.google.android.exoplayer.extractor.ExtractorOutput
;
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.util.Assertions
;
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
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.io.IOException
;
/**
/**
* Facilitates the extraction of data from the FLV container format.
* Facilitates the extraction of data from the FLV container format.
*/
*/
public
final
class
FlvExtractor
implements
Extractor
,
SeekMap
{
public
final
class
FlvExtractor
implements
Extractor
,
SeekMap
{
// Header sizes
private
static
final
int
FLV_MIN_HEADER_SIZE
=
9
;
// Header sizes.
private
static
final
int
FLV_HEADER_SIZE
=
9
;
private
static
final
int
FLV_TAG_HEADER_SIZE
=
11
;
private
static
final
int
FLV_TAG_HEADER_SIZE
=
11
;
// Parser states.
// Parser states.
private
static
final
int
STATE_READING_TAG_HEADER
=
1
;
private
static
final
int
STATE_READING_FLV_HEADER
=
1
;
private
static
final
int
STATE_READING_SAMPLE
=
2
;
private
static
final
int
STATE_SKIPPING_TO_TAG_HEADER
=
2
;
private
static
final
int
STATE_READING_TAG_HEADER
=
3
;
private
static
final
int
STATE_READING_TAG_DATA
=
4
;
// Tag types
// Tag types
.
private
static
final
int
TAG_TYPE_AUDIO
=
8
;
private
static
final
int
TAG_TYPE_AUDIO
=
8
;
private
static
final
int
TAG_TYPE_VIDEO
=
9
;
private
static
final
int
TAG_TYPE_VIDEO
=
9
;
private
static
final
int
TAG_TYPE_SCRIPT_DATA
=
18
;
private
static
final
int
TAG_TYPE_SCRIPT_DATA
=
18
;
// FLV container identifier
// FLV container identifier
.
private
static
final
int
FLV_TAG
=
Util
.
getIntegerCodeForString
(
"FLV"
);
private
static
final
int
FLV_TAG
=
Util
.
getIntegerCodeForString
(
"FLV"
);
// Temporary buffers
// Temporary buffers
.
private
final
ParsableByteArray
scratch
;
private
final
ParsableByteArray
scratch
;
private
final
ParsableByteArray
headerBuffer
;
private
final
ParsableByteArray
headerBuffer
;
private
final
ParsableByteArray
tagHeaderBuffer
;
private
final
ParsableByteArray
tagHeaderBuffer
;
private
ParsableByteArray
tagData
;
private
final
ParsableByteArray
tagData
;
// Extractor outputs.
// Extractor outputs.
private
ExtractorOutput
extractorOutput
;
private
ExtractorOutput
extractorOutput
;
// State variables.
// State variables.
private
int
parserState
;
private
int
parserState
;
private
int
dataOffset
;
private
int
bytesToNextTagHeader
;
private
TagHeader
currentTagHeader
;
public
int
tagType
;
public
int
tagDataSize
;
public
long
tagTimestampUs
;
// Tags readers
// Tags readers
.
private
AudioTagPayloadReader
audioReader
;
private
AudioTagPayloadReader
audioReader
;
private
VideoTagPayloadReader
videoReader
;
private
VideoTagPayloadReader
videoReader
;
private
ScriptTagPayloadReader
metadataReader
;
private
ScriptTagPayloadReader
metadataReader
;
public
FlvExtractor
()
{
public
FlvExtractor
()
{
scratch
=
new
ParsableByteArray
(
4
);
scratch
=
new
ParsableByteArray
(
4
);
headerBuffer
=
new
ParsableByteArray
(
FLV_
MIN_
HEADER_SIZE
);
headerBuffer
=
new
ParsableByteArray
(
FLV_HEADER_SIZE
);
tagHeaderBuffer
=
new
ParsableByteArray
(
FLV_TAG_HEADER_SIZE
);
tagHeaderBuffer
=
new
ParsableByteArray
(
FLV_TAG_HEADER_SIZE
);
dataOffset
=
0
;
tagData
=
new
ParsableByteArray
();
currentTagHeader
=
new
TagHeader
();
parserState
=
STATE_READING_FLV_HEADER
;
}
@Override
public
void
init
(
ExtractorOutput
output
)
{
this
.
extractorOutput
=
output
;
}
}
@Override
@Override
...
@@ -112,151 +110,133 @@ public final class FlvExtractor implements Extractor, SeekMap {
...
@@ -112,151 +110,133 @@ public final class FlvExtractor implements Extractor, SeekMap {
}
}
@Override
@Override
public
void
init
(
ExtractorOutput
output
)
{
this
.
extractorOutput
=
output
;
}
@Override
public
void
seek
()
{
parserState
=
STATE_READING_FLV_HEADER
;
bytesToNextTagHeader
=
0
;
}
@Override
public
int
read
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
,
public
int
read
(
ExtractorInput
input
,
PositionHolder
seekPosition
)
throws
IOException
,
InterruptedException
{
InterruptedException
{
if
(
dataOffset
==
0
while
(
true
)
{
&&
!
readHeader
(
input
))
{
switch
(
parserState
)
{
return
RESULT_END_OF_INPUT
;
case
STATE_READING_FLV_HEADER:
}
if
(!
readFlvHeader
(
input
))
{
return
RESULT_END_OF_INPUT
;
try
{
}
while
(
true
)
{
break
;
if
(
parserState
==
STATE_READING_TAG_HEADER
)
{
case
STATE_SKIPPING_TO_TAG_HEADER:
skipToTagHeader
(
input
);
break
;
case
STATE_READING_TAG_HEADER:
if
(!
readTagHeader
(
input
))
{
if
(!
readTagHeader
(
input
))
{
return
RESULT_END_OF_INPUT
;
return
RESULT_END_OF_INPUT
;
}
}
}
else
{
break
;
return
readSample
(
input
);
case
STATE_READING_TAG_DATA:
}
if
(
readTagData
(
input
))
{
return
RESULT_CONTINUE
;
}
break
;
}
}
}
catch
(
AudioTagPayloadReader
.
UnsupportedFormatException
unsupportedTrack
)
{
unsupportedTrack
.
printStackTrace
();
return
RESULT_END_OF_INPUT
;
}
}
}
}
@Override
public
void
seek
()
{
dataOffset
=
0
;
}
/**
/**
* Reads FLV container header from the provided {@link ExtractorInput}.
* Reads an FLV container header from the provided {@link ExtractorInput}.
*
* @param input The {@link ExtractorInput} from which to read.
* @param input The {@link ExtractorInput} from which to read.
* @return True if header was read successfully.
Otherwise, false
.
* @return True if header was read successfully.
False if the end of stream was reached
.
* @throws IOException If an error occurred reading from the source.
* @throws IOException If an error occurred reading
or parsing data
from the source.
* @throws InterruptedException If the thread was interrupted.
* @throws InterruptedException If the thread was interrupted.
*/
*/
private
boolean
readHeader
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
private
boolean
readFlvHeader
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
try
{
if
(!
input
.
readFully
(
headerBuffer
.
data
,
0
,
FLV_HEADER_SIZE
,
true
))
{
input
.
readFully
(
headerBuffer
.
data
,
0
,
FLV_MIN_HEADER_SIZE
);
// We've reached the end of the stream.
headerBuffer
.
setPosition
(
0
);
headerBuffer
.
skipBytes
(
4
);
int
flags
=
headerBuffer
.
readUnsignedByte
();
boolean
hasAudio
=
(
flags
&
0x04
)
!=
0
;
boolean
hasVideo
=
(
flags
&
0x01
)
!=
0
;
if
(
hasAudio
&&
audioReader
==
null
)
{
audioReader
=
new
AudioTagPayloadReader
(
extractorOutput
.
track
(
TAG_TYPE_AUDIO
));
}
if
(
hasVideo
&&
videoReader
==
null
)
{
videoReader
=
new
VideoTagPayloadReader
(
extractorOutput
.
track
(
TAG_TYPE_VIDEO
));
}
if
(
metadataReader
==
null
)
{
metadataReader
=
new
ScriptTagPayloadReader
(
null
);
}
extractorOutput
.
endTracks
();
extractorOutput
.
seekMap
(
this
);
// Store payload start position and start extended header (if there is one)
dataOffset
=
headerBuffer
.
readInt
();
input
.
skipFully
(
dataOffset
-
FLV_MIN_HEADER_SIZE
);
parserState
=
STATE_READING_TAG_HEADER
;
}
catch
(
EOFException
eof
)
{
return
false
;
return
false
;
}
}
headerBuffer
.
setPosition
(
0
);
headerBuffer
.
skipBytes
(
4
);
int
flags
=
headerBuffer
.
readUnsignedByte
();
boolean
hasAudio
=
(
flags
&
0x04
)
!=
0
;
boolean
hasVideo
=
(
flags
&
0x01
)
!=
0
;
if
(
hasAudio
&&
audioReader
==
null
)
{
audioReader
=
new
AudioTagPayloadReader
(
extractorOutput
.
track
(
TAG_TYPE_AUDIO
));
}
if
(
hasVideo
&&
videoReader
==
null
)
{
videoReader
=
new
VideoTagPayloadReader
(
extractorOutput
.
track
(
TAG_TYPE_VIDEO
));
}
if
(
metadataReader
==
null
)
{
metadataReader
=
new
ScriptTagPayloadReader
(
null
);
}
extractorOutput
.
endTracks
();
extractorOutput
.
seekMap
(
this
);
// We need to skip any additional content in the FLV header, plus the 4 byte previous tag size.
bytesToNextTagHeader
=
headerBuffer
.
readInt
()
-
FLV_HEADER_SIZE
+
4
;
parserState
=
STATE_SKIPPING_TO_TAG_HEADER
;
return
true
;
return
true
;
}
}
/**
/**
* Skips over data to reach the next tag header.
*
* @param input The {@link ExtractorInput} from which to read.
* @throws IOException If an error occurred skipping data from the source.
* @throws InterruptedException If the thread was interrupted.
*/
private
void
skipToTagHeader
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
input
.
skipFully
(
bytesToNextTagHeader
);
bytesToNextTagHeader
=
0
;
parserState
=
STATE_READING_TAG_HEADER
;
}
/**
* Reads a tag header from the provided {@link ExtractorInput}.
* Reads a tag header from the provided {@link ExtractorInput}.
*
* @param input The {@link ExtractorInput} from which to read.
* @param input The {@link ExtractorInput} from which to read.
* @return True if tag header was read successfully. Otherwise, false.
* @return True if tag header was read successfully. Otherwise, false.
* @throws IOException If an error occurred reading from the source.
* @throws IOException If an error occurred reading
or parsing data
from the source.
* @throws InterruptedException If the thread was interrupted.
* @throws InterruptedException If the thread was interrupted.
* @throws TagPayloadReader.UnsupportedFormatException If payload of the tag is using a codec non
* supported codec.
*/
*/
private
boolean
readTagHeader
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
,
private
boolean
readTagHeader
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
TagPayloadReader
.
UnsupportedFormatException
{
if
(!
input
.
readFully
(
tagHeaderBuffer
.
data
,
0
,
FLV_TAG_HEADER_SIZE
,
true
))
{
try
{
// We've reached the end of the stream.
// skipping previous tag size field
input
.
skipFully
(
4
);
// Read the tag header from the input.
input
.
readFully
(
tagHeaderBuffer
.
data
,
0
,
FLV_TAG_HEADER_SIZE
);
tagHeaderBuffer
.
setPosition
(
0
);
int
type
=
tagHeaderBuffer
.
readUnsignedByte
();
int
dataSize
=
tagHeaderBuffer
.
readUnsignedInt24
();
long
timestamp
=
tagHeaderBuffer
.
readUnsignedInt24
();
timestamp
=
(
tagHeaderBuffer
.
readUnsignedByte
()
<<
24
)
|
timestamp
;
int
streamId
=
tagHeaderBuffer
.
readUnsignedInt24
();
currentTagHeader
.
type
=
type
;
currentTagHeader
.
dataSize
=
dataSize
;
currentTagHeader
.
timestamp
=
timestamp
*
1000
;
currentTagHeader
.
streamId
=
streamId
;
// Sanity checks.
Assertions
.
checkState
(
type
==
TAG_TYPE_AUDIO
||
type
==
TAG_TYPE_VIDEO
||
type
==
TAG_TYPE_SCRIPT_DATA
);
// Reuse tagData buffer to avoid lot of memory allocation (performance penalty).
if
(
tagData
==
null
||
dataSize
>
tagData
.
capacity
())
{
tagData
=
new
ParsableByteArray
(
dataSize
);
}
else
{
tagData
.
setPosition
(
0
);
}
tagData
.
setLimit
(
dataSize
);
parserState
=
STATE_READING_SAMPLE
;
}
catch
(
EOFException
eof
)
{
return
false
;
return
false
;
}
}
tagHeaderBuffer
.
setPosition
(
0
);
tagType
=
tagHeaderBuffer
.
readUnsignedByte
();
tagDataSize
=
tagHeaderBuffer
.
readUnsignedInt24
();
tagTimestampUs
=
tagHeaderBuffer
.
readUnsignedInt24
();
tagTimestampUs
=
((
tagHeaderBuffer
.
readUnsignedByte
()
<<
24
)
|
tagTimestampUs
)
*
1000L
;
tagHeaderBuffer
.
skipBytes
(
3
);
// streamId
parserState
=
STATE_READING_TAG_DATA
;
return
true
;
return
true
;
}
}
/**
/**
* Reads payload of an FLV tag from the provided {@link ExtractorInput}.
* Reads the body of a tag from the provided {@link ExtractorInput}.
*
* @param input The {@link ExtractorInput} from which to read.
* @param input The {@link ExtractorInput} from which to read.
* @return
One of {@link Extractor#RESULT_CONTINUE} and {@link Extractor#RESULT_END_OF_INPUT}
.
* @return
True if the data was consumed by a reader. False if it was skipped
.
* @throws IOException If an error occurred reading from the source.
* @throws IOException If an error occurred reading
or parsing data
from the source.
* @throws InterruptedException If the thread was interrupted.
* @throws InterruptedException If the thread was interrupted.
* @throws TagPayloadReader.UnsupportedFormatException If payload of the tag is using a codec non
* supported codec.
*/
*/
private
int
readSample
(
ExtractorInput
input
)
throws
IOException
,
private
boolean
readTagData
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
InterruptedException
,
AudioTagPayloadReader
.
UnsupportedFormatException
{
boolean
wasConsumed
=
true
;
if
(
tagData
!=
null
)
{
if
(
tagType
==
TAG_TYPE_AUDIO
&&
audioReader
!=
null
)
{
if
(!
input
.
readFully
(
tagData
.
data
,
0
,
currentTagHeader
.
dataSize
,
true
))
{
audioReader
.
consume
(
prepareTagData
(
input
),
tagTimestampUs
);
return
RESULT_END_OF_INPUT
;
}
else
if
(
tagType
==
TAG_TYPE_VIDEO
&&
videoReader
!=
null
)
{
}
videoReader
.
consume
(
prepareTagData
(
input
),
tagTimestampUs
);
tagData
.
setPosition
(
0
);
}
else
if
(
tagType
==
TAG_TYPE_SCRIPT_DATA
&&
metadataReader
!=
null
)
{
}
else
{
metadataReader
.
consume
(
prepareTagData
(
input
),
tagTimestampUs
);
input
.
skipFully
(
currentTagHeader
.
dataSize
);
return
RESULT_CONTINUE
;
}
// Pass payload to the right payload reader.
if
(
currentTagHeader
.
type
==
TAG_TYPE_AUDIO
&&
audioReader
!=
null
)
{
audioReader
.
consume
(
tagData
,
currentTagHeader
.
timestamp
);
}
else
if
(
currentTagHeader
.
type
==
TAG_TYPE_VIDEO
&&
videoReader
!=
null
)
{
videoReader
.
consume
(
tagData
,
currentTagHeader
.
timestamp
);
}
else
if
(
currentTagHeader
.
type
==
TAG_TYPE_SCRIPT_DATA
&&
metadataReader
!=
null
)
{
metadataReader
.
consume
(
tagData
,
currentTagHeader
.
timestamp
);
if
(
metadataReader
.
getDurationUs
()
!=
C
.
UNKNOWN_TIME_US
)
{
if
(
metadataReader
.
getDurationUs
()
!=
C
.
UNKNOWN_TIME_US
)
{
if
(
audioReader
!=
null
)
{
if
(
audioReader
!=
null
)
{
audioReader
.
setDurationUs
(
metadataReader
.
getDurationUs
());
audioReader
.
setDurationUs
(
metadataReader
.
getDurationUs
());
...
@@ -266,16 +246,28 @@ public final class FlvExtractor implements Extractor, SeekMap {
...
@@ -266,16 +246,28 @@ public final class FlvExtractor implements Extractor, SeekMap {
}
}
}
}
}
else
{
}
else
{
tagData
.
reset
();
input
.
skipFully
(
tagDataSize
);
wasConsumed
=
false
;
}
}
bytesToNextTagHeader
=
4
;
// There's a 4 byte previous tag size before the next header.
parserState
=
STATE_SKIPPING_TO_TAG_HEADER
;
return
wasConsumed
;
}
parserState
=
STATE_READING_TAG_HEADER
;
private
ParsableByteArray
prepareTagData
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
return
RESULT_CONTINUE
;
if
(
tagDataSize
>
tagData
.
capacity
())
{
tagData
.
reset
(
new
byte
[
Math
.
max
(
tagData
.
capacity
()
*
2
,
tagDataSize
)],
0
);
}
else
{
tagData
.
setPosition
(
0
);
}
tagData
.
setLimit
(
tagDataSize
);
input
.
readFully
(
tagData
.
data
,
0
,
tagDataSize
);
return
tagData
;
}
}
// SeekMap implementation.
// SeekMap implementation.
// TODO: Add seeking support
@Override
@Override
public
boolean
isSeekable
()
{
public
boolean
isSeekable
()
{
return
false
;
return
false
;
...
@@ -286,16 +278,4 @@ public final class FlvExtractor implements Extractor, SeekMap {
...
@@ -286,16 +278,4 @@ public final class FlvExtractor implements Extractor, SeekMap {
return
0
;
return
0
;
}
}
/**
* Defines header of a FLV tag
*/
final
class
TagHeader
{
public
int
type
;
public
int
dataSize
;
public
long
timestamp
;
public
int
streamId
;
}
}
}
library/src/main/java/com/google/android/exoplayer/extractor/flv/ScriptTagPayloadReader.java
View file @
4422e8a0
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer
.
extractor
.
flv
;
package
com
.
google
.
android
.
exoplayer
.
extractor
.
flv
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
...
@@ -55,17 +56,16 @@ import java.util.Map;
...
@@ -55,17 +56,16 @@ import java.util.Map;
}
}
@Override
@Override
protected
boolean
parseHeader
(
ParsableByteArray
data
)
throws
UnsupportedFormatException
{
protected
boolean
parseHeader
(
ParsableByteArray
data
)
{
return
true
;
return
true
;
}
}
@SuppressWarnings
(
"unchecked"
)
@Override
@Override
protected
void
parsePayload
(
ParsableByteArray
data
,
long
timeUs
)
{
protected
void
parsePayload
(
ParsableByteArray
data
,
long
timeUs
)
throws
ParserException
{
int
nameType
=
readAmfType
(
data
);
int
nameType
=
readAmfType
(
data
);
if
(
nameType
!=
AMF_TYPE_STRING
)
{
if
(
nameType
!=
AMF_TYPE_STRING
)
{
// Should never happen.
// Should never happen.
return
;
throw
new
ParserException
()
;
}
}
String
name
=
readAmfString
(
data
);
String
name
=
readAmfString
(
data
);
if
(!
NAME_METADATA
.
equals
(
name
))
{
if
(!
NAME_METADATA
.
equals
(
name
))
{
...
@@ -75,48 +75,27 @@ import java.util.Map;
...
@@ -75,48 +75,27 @@ import java.util.Map;
int
type
=
readAmfType
(
data
);
int
type
=
readAmfType
(
data
);
if
(
type
!=
AMF_TYPE_ECMA_ARRAY
)
{
if
(
type
!=
AMF_TYPE_ECMA_ARRAY
)
{
// Should never happen.
// Should never happen.
return
;
throw
new
ParserException
()
;
}
}
// Set the duration to the value contained in the metadata, if present.
// Set the duration to the value contained in the metadata, if present.
Map
<
String
,
Object
>
metadata
=
(
Map
<
String
,
Object
>)
readAmfData
(
data
,
type
);
Map
<
String
,
Object
>
metadata
=
readAmfEcmaArray
(
data
);
if
(
metadata
.
containsKey
(
KEY_DURATION
))
{
if
(
metadata
.
containsKey
(
KEY_DURATION
))
{
double
durationSeconds
=
(
double
)
metadata
.
get
(
KEY_DURATION
);
double
durationSeconds
=
(
double
)
metadata
.
get
(
KEY_DURATION
);
setDurationUs
((
long
)
(
durationSeconds
*
C
.
MICROS_PER_SECOND
));
setDurationUs
((
long
)
(
durationSeconds
*
C
.
MICROS_PER_SECOND
));
}
}
}
}
private
int
readAmfType
(
ParsableByteArray
data
)
{
private
static
int
readAmfType
(
ParsableByteArray
data
)
{
return
data
.
readUnsignedByte
();
return
data
.
readUnsignedByte
();
}
}
private
Object
readAmfData
(
ParsableByteArray
data
,
int
type
)
{
switch
(
type
)
{
case
AMF_TYPE_NUMBER:
return
readAmfDouble
(
data
);
case
AMF_TYPE_BOOLEAN:
return
readAmfBoolean
(
data
);
case
AMF_TYPE_STRING:
return
readAmfString
(
data
);
case
AMF_TYPE_OBJECT:
return
readAmfObject
(
data
);
case
AMF_TYPE_ECMA_ARRAY:
return
readAmfEcmaArray
(
data
);
case
AMF_TYPE_STRICT_ARRAY:
return
readAmfStrictArray
(
data
);
case
AMF_TYPE_DATE:
return
readAmfDate
(
data
);
default
:
return
null
;
}
}
/**
/**
* Read a boolean from an AMF encoded buffer.
* Read a boolean from an AMF encoded buffer.
*
*
* @param data The buffer from which to read.
* @param data The buffer from which to read.
* @return The value read from the buffer.
* @return The value read from the buffer.
*/
*/
private
Boolean
readAmfBoolean
(
ParsableByteArray
data
)
{
private
static
Boolean
readAmfBoolean
(
ParsableByteArray
data
)
{
return
data
.
readUnsignedByte
()
==
1
;
return
data
.
readUnsignedByte
()
==
1
;
}
}
...
@@ -126,7 +105,7 @@ import java.util.Map;
...
@@ -126,7 +105,7 @@ import java.util.Map;
* @param data The buffer from which to read.
* @param data The buffer from which to read.
* @return The value read from the buffer.
* @return The value read from the buffer.
*/
*/
private
Double
readAmfDouble
(
ParsableByteArray
data
)
{
private
static
Double
readAmfDouble
(
ParsableByteArray
data
)
{
return
Double
.
longBitsToDouble
(
data
.
readLong
());
return
Double
.
longBitsToDouble
(
data
.
readLong
());
}
}
...
@@ -136,7 +115,7 @@ import java.util.Map;
...
@@ -136,7 +115,7 @@ import java.util.Map;
* @param data The buffer from which to read.
* @param data The buffer from which to read.
* @return The value read from the buffer.
* @return The value read from the buffer.
*/
*/
private
String
readAmfString
(
ParsableByteArray
data
)
{
private
static
String
readAmfString
(
ParsableByteArray
data
)
{
int
size
=
data
.
readUnsignedShort
();
int
size
=
data
.
readUnsignedShort
();
int
position
=
data
.
getPosition
();
int
position
=
data
.
getPosition
();
data
.
skipBytes
(
size
);
data
.
skipBytes
(
size
);
...
@@ -149,9 +128,9 @@ import java.util.Map;
...
@@ -149,9 +128,9 @@ import java.util.Map;
* @param data The buffer from which to read.
* @param data The buffer from which to read.
* @return The value read from the buffer.
* @return The value read from the buffer.
*/
*/
private
Object
readAmfStrictArray
(
ParsableByteArray
data
)
{
private
static
ArrayList
<
Object
>
readAmfStrictArray
(
ParsableByteArray
data
)
{
long
count
=
data
.
readUnsigned
Int
();
int
count
=
data
.
readUnsignedIntTo
Int
();
ArrayList
<
Object
>
list
=
new
ArrayList
<>();
ArrayList
<
Object
>
list
=
new
ArrayList
<>(
count
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
int
type
=
readAmfType
(
data
);
int
type
=
readAmfType
(
data
);
list
.
add
(
readAmfData
(
data
,
type
));
list
.
add
(
readAmfData
(
data
,
type
));
...
@@ -165,7 +144,7 @@ import java.util.Map;
...
@@ -165,7 +144,7 @@ import java.util.Map;
* @param data The buffer from which to read.
* @param data The buffer from which to read.
* @return The value read from the buffer.
* @return The value read from the buffer.
*/
*/
private
Object
readAmfObject
(
ParsableByteArray
data
)
{
private
static
HashMap
<
String
,
Object
>
readAmfObject
(
ParsableByteArray
data
)
{
HashMap
<
String
,
Object
>
array
=
new
HashMap
<>();
HashMap
<
String
,
Object
>
array
=
new
HashMap
<>();
while
(
true
)
{
while
(
true
)
{
String
key
=
readAmfString
(
data
);
String
key
=
readAmfString
(
data
);
...
@@ -184,9 +163,9 @@ import java.util.Map;
...
@@ -184,9 +163,9 @@ import java.util.Map;
* @param data The buffer from which to read.
* @param data The buffer from which to read.
* @return The value read from the buffer.
* @return The value read from the buffer.
*/
*/
private
Object
readAmfEcmaArray
(
ParsableByteArray
data
)
{
private
static
HashMap
<
String
,
Object
>
readAmfEcmaArray
(
ParsableByteArray
data
)
{
long
count
=
data
.
readUnsigned
Int
();
int
count
=
data
.
readUnsignedIntTo
Int
();
HashMap
<
String
,
Object
>
array
=
new
HashMap
<>();
HashMap
<
String
,
Object
>
array
=
new
HashMap
<>(
count
);
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
String
key
=
readAmfString
(
data
);
String
key
=
readAmfString
(
data
);
int
type
=
readAmfType
(
data
);
int
type
=
readAmfType
(
data
);
...
@@ -201,10 +180,31 @@ import java.util.Map;
...
@@ -201,10 +180,31 @@ import java.util.Map;
* @param data The buffer from which to read.
* @param data The buffer from which to read.
* @return The value read from the buffer.
* @return The value read from the buffer.
*/
*/
private
Date
readAmfDate
(
ParsableByteArray
data
)
{
private
static
Date
readAmfDate
(
ParsableByteArray
data
)
{
Date
date
=
new
Date
((
long
)
readAmfDouble
(
data
).
doubleValue
());
Date
date
=
new
Date
((
long
)
readAmfDouble
(
data
).
doubleValue
());
data
.
readUnsignedShort
(
);
// Skip reserved bytes.
data
.
skipBytes
(
2
);
// Skip reserved bytes.
return
date
;
return
date
;
}
}
private
static
Object
readAmfData
(
ParsableByteArray
data
,
int
type
)
{
switch
(
type
)
{
case
AMF_TYPE_NUMBER:
return
readAmfDouble
(
data
);
case
AMF_TYPE_BOOLEAN:
return
readAmfBoolean
(
data
);
case
AMF_TYPE_STRING:
return
readAmfString
(
data
);
case
AMF_TYPE_OBJECT:
return
readAmfObject
(
data
);
case
AMF_TYPE_ECMA_ARRAY:
return
readAmfEcmaArray
(
data
);
case
AMF_TYPE_STRICT_ARRAY:
return
readAmfStrictArray
(
data
);
case
AMF_TYPE_DATE:
return
readAmfDate
(
data
);
default
:
return
null
;
}
}
}
}
library/src/main/java/com/google/android/exoplayer/extractor/flv/TagPayloadReader.java
View file @
4422e8a0
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer
.
extractor
.
flv
;
package
com
.
google
.
android
.
exoplayer
.
extractor
.
flv
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
...
@@ -27,7 +28,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
...
@@ -27,7 +28,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
/**
/**
* Thrown when the format is not supported.
* Thrown when the format is not supported.
*/
*/
public
static
final
class
UnsupportedFormatException
extends
Exception
{
public
static
final
class
UnsupportedFormatException
extends
Parser
Exception
{
public
UnsupportedFormatException
(
String
msg
)
{
public
UnsupportedFormatException
(
String
msg
)
{
super
(
msg
);
super
(
msg
);
...
@@ -79,8 +80,9 @@ import com.google.android.exoplayer.util.ParsableByteArray;
...
@@ -79,8 +80,9 @@ import com.google.android.exoplayer.util.ParsableByteArray;
*
*
* @param data The payload data to consume.
* @param data The payload data to consume.
* @param timeUs The timestamp associated with the payload.
* @param timeUs The timestamp associated with the payload.
* @throws ParserException If an error occurs parsing the data.
*/
*/
public
final
void
consume
(
ParsableByteArray
data
,
long
timeUs
)
throws
UnsupportedFormat
Exception
{
public
final
void
consume
(
ParsableByteArray
data
,
long
timeUs
)
throws
Parser
Exception
{
if
(
parseHeader
(
data
))
{
if
(
parseHeader
(
data
))
{
parsePayload
(
data
,
timeUs
);
parsePayload
(
data
,
timeUs
);
}
}
...
@@ -92,16 +94,17 @@ import com.google.android.exoplayer.util.ParsableByteArray;
...
@@ -92,16 +94,17 @@ import com.google.android.exoplayer.util.ParsableByteArray;
* @param data Buffer where the tag header is stored.
* @param data Buffer where the tag header is stored.
* @return True if the header was parsed successfully and the payload should be read. False
* @return True if the header was parsed successfully and the payload should be read. False
* otherwise.
* otherwise.
* @throws
UnsupportedFormatException
* @throws
ParserException If an error occurs parsing the header.
*/
*/
protected
abstract
boolean
parseHeader
(
ParsableByteArray
data
)
throws
UnsupportedFormat
Exception
;
protected
abstract
boolean
parseHeader
(
ParsableByteArray
data
)
throws
Parser
Exception
;
/**
/**
* Parses tag payload.
* Parses tag payload.
*
*
* @param data Buffer where tag payload is stored
* @param data Buffer where tag payload is stored
* @param timeUs Time position of the frame
* @param timeUs Time position of the frame
* @throws ParserException If an error occurs parsing the payload.
*/
*/
protected
abstract
void
parsePayload
(
ParsableByteArray
data
,
long
timeUs
);
protected
abstract
void
parsePayload
(
ParsableByteArray
data
,
long
timeUs
)
throws
ParserException
;
}
}
library/src/main/java/com/google/android/exoplayer/extractor/flv/VideoTagPayloadReader.java
View file @
4422e8a0
...
@@ -26,8 +26,6 @@ import com.google.android.exoplayer.util.NalUnitUtil;
...
@@ -26,8 +26,6 @@ import com.google.android.exoplayer.util.NalUnitUtil;
import
com.google.android.exoplayer.util.ParsableBitArray
;
import
com.google.android.exoplayer.util.ParsableBitArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
android.util.Log
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
...
@@ -35,24 +33,22 @@ import java.util.List;
...
@@ -35,24 +33,22 @@ import java.util.List;
* Parses video tags from an FLV stream and extracts H.264 nal units.
* Parses video tags from an FLV stream and extracts H.264 nal units.
*/
*/
/* package */
final
class
VideoTagPayloadReader
extends
TagPayloadReader
{
/* package */
final
class
VideoTagPayloadReader
extends
TagPayloadReader
{
private
static
final
String
TAG
=
"VideoTagPayloadReader"
;
// Video codec
// Video codec
.
private
static
final
int
VIDEO_CODEC_AVC
=
7
;
private
static
final
int
VIDEO_CODEC_AVC
=
7
;
// F
RAME TYPE
// F
rame types.
private
static
final
int
VIDEO_FRAME_KEYFRAME
=
1
;
private
static
final
int
VIDEO_FRAME_KEYFRAME
=
1
;
private
static
final
int
VIDEO_FRAME_VIDEO_INFO
=
5
;
private
static
final
int
VIDEO_FRAME_VIDEO_INFO
=
5
;
// P
ACKET TYPE
// P
acket types.
private
static
final
int
AVC_PACKET_TYPE_SEQUENCE_HEADER
=
0
;
private
static
final
int
AVC_PACKET_TYPE_SEQUENCE_HEADER
=
0
;
private
static
final
int
AVC_PACKET_TYPE_AVC_NALU
=
1
;
private
static
final
int
AVC_PACKET_TYPE_AVC_NALU
=
1
;
private
static
final
int
AVC_PACKET_TYPE_AVC_END_OF_SEQUENCE
=
2
;
// Temporary arrays.
// Temporary arrays.
private
final
ParsableByteArray
nalStartCode
;
private
final
ParsableByteArray
nalStartCode
;
private
final
ParsableByteArray
nalLength
;
private
final
ParsableByteArray
nalLength
;
private
int
nalUnit
s
Length
;
private
int
nalUnit
LengthField
Length
;
// State variables.
// State variables.
private
boolean
hasOutputFormat
;
private
boolean
hasOutputFormat
;
...
@@ -86,28 +82,17 @@ import java.util.List;
...
@@ -86,28 +82,17 @@ import java.util.List;
}
}
@Override
@Override
protected
void
parsePayload
(
ParsableByteArray
data
,
long
timeUs
)
{
protected
void
parsePayload
(
ParsableByteArray
data
,
long
timeUs
)
throws
ParserException
{
int
packetType
=
data
.
readUnsignedByte
();
int
packetType
=
data
.
readUnsignedByte
();
int
compositionTime
=
data
.
readUnsignedInt24
();
int
compositionTimeMs
=
data
.
readUnsignedInt24
();
// If there is a composition time, adjust timeUs accordingly
timeUs
+=
compositionTimeMs
*
1000L
;
// Note: compositionTime within AVCVIDEOPACKET is provided in milliseconds
// and timeUs is in microseconds.
if
(
compositionTime
>
0
)
{
timeUs
+=
compositionTime
*
1000
;
}
// Parse avc sequence header in case this was not done before.
// Parse avc sequence header in case this was not done before.
if
(
packetType
==
AVC_PACKET_TYPE_SEQUENCE_HEADER
&&
!
hasOutputFormat
)
{
if
(
packetType
==
AVC_PACKET_TYPE_SEQUENCE_HEADER
&&
!
hasOutputFormat
)
{
ParsableByteArray
videoSequence
=
new
ParsableByteArray
(
new
byte
[
data
.
bytesLeft
()]);
ParsableByteArray
videoSequence
=
new
ParsableByteArray
(
new
byte
[
data
.
bytesLeft
()]);
data
.
readBytes
(
videoSequence
.
data
,
0
,
data
.
bytesLeft
());
data
.
readBytes
(
videoSequence
.
data
,
0
,
data
.
bytesLeft
());
AvcSequenceHeaderData
avcData
;
AvcSequenceHeaderData
avcData
=
parseAvcCodecPrivate
(
videoSequence
);
try
{
nalUnitLengthFieldLength
=
avcData
.
nalUnitLengthFieldLength
;
avcData
=
parseAvcCodecPrivate
(
videoSequence
);
nalUnitsLength
=
avcData
.
nalUnitLengthFieldLength
;
}
catch
(
ParserException
e
)
{
e
.
printStackTrace
();
return
;
}
// Construct and output the format.
// Construct and output the format.
MediaFormat
mediaFormat
=
MediaFormat
.
createVideoFormat
(
MediaFormat
.
NO_VALUE
,
MediaFormat
mediaFormat
=
MediaFormat
.
createVideoFormat
(
MediaFormat
.
NO_VALUE
,
...
@@ -124,8 +109,7 @@ import java.util.List;
...
@@ -124,8 +109,7 @@ import java.util.List;
nalLengthData
[
0
]
=
0
;
nalLengthData
[
0
]
=
0
;
nalLengthData
[
1
]
=
0
;
nalLengthData
[
1
]
=
0
;
nalLengthData
[
2
]
=
0
;
nalLengthData
[
2
]
=
0
;
int
nalUnitLengthFieldLength
=
nalUnitsLength
;
int
nalUnitLengthFieldLengthDiff
=
4
-
nalUnitLengthFieldLength
;
int
nalUnitLengthFieldLengthDiff
=
4
-
nalUnitsLength
;
// NAL units are length delimited, but the decoder requires start code delimited units.
// NAL units are length delimited, but the decoder requires start code delimited units.
// Loop until we've written the sample to the track output, replacing length delimiters with
// Loop until we've written the sample to the track output, replacing length delimiters with
// start codes as we encounter them.
// start codes as we encounter them.
...
@@ -137,65 +121,58 @@ import java.util.List;
...
@@ -137,65 +121,58 @@ import java.util.List;
nalLength
.
setPosition
(
0
);
nalLength
.
setPosition
(
0
);
bytesToWrite
=
nalLength
.
readUnsignedIntToInt
();
bytesToWrite
=
nalLength
.
readUnsignedIntToInt
();
//
First, write nal start code (replacing length field by nal delimiter codes)
//
Write a start code for the current NAL unit.
nalStartCode
.
setPosition
(
0
);
nalStartCode
.
setPosition
(
0
);
output
.
sampleData
(
nalStartCode
,
4
);
output
.
sampleData
(
nalStartCode
,
4
);
bytesWritten
+=
4
;
bytesWritten
+=
4
;
//
Then write nal unit itsef
//
Write the payload of the NAL unit.
output
.
sampleData
(
data
,
bytesToWrite
);
output
.
sampleData
(
data
,
bytesToWrite
);
bytesWritten
+=
bytesToWrite
;
bytesWritten
+=
bytesToWrite
;
}
}
output
.
sampleMetadata
(
timeUs
,
frameType
==
VIDEO_FRAME_KEYFRAME
?
C
.
SAMPLE_FLAG_SYNC
:
0
,
output
.
sampleMetadata
(
timeUs
,
frameType
==
VIDEO_FRAME_KEYFRAME
?
C
.
SAMPLE_FLAG_SYNC
:
0
,
bytesWritten
,
0
,
null
);
bytesWritten
,
0
,
null
);
}
else
if
(
packetType
==
AVC_PACKET_TYPE_AVC_END_OF_SEQUENCE
)
{
Log
.
d
(
TAG
,
"End of seq!!!"
);
}
}
}
}
/**
/**
* Builds initialization data for a {@link MediaFormat} from H.264 (AVC) codec private data.
* Builds initialization data for a {@link MediaFormat} from H.264 (AVC) codec private data.
*
*
* @return The AvcSequenceHeader data with all the information needed to initialize
* @return The AvcSequenceHeader data needed to initialize the video codec.
* the video codec.
* @throws ParserException If the initialization data could not be built.
* @throws ParserException If the initialization data could not be built.
*/
*/
private
AvcSequenceHeaderData
parseAvcCodecPrivate
(
ParsableByteArray
buffer
)
private
AvcSequenceHeaderData
parseAvcCodecPrivate
(
ParsableByteArray
buffer
)
throws
ParserException
{
throws
ParserException
{
try
{
// TODO: Deduplicate with AtomParsers.parseAvcCFromParent.
// TODO: Deduplicate with AtomParsers.parseAvcCFromParent.
buffer
.
setPosition
(
4
);
buffer
.
setPosition
(
4
);
int
nalUnitLengthFieldLength
=
(
buffer
.
readUnsignedByte
()
&
0x03
)
+
1
;
int
nalUnitLengthFieldLength
=
(
buffer
.
readUnsignedByte
()
&
0x03
)
+
1
;
Assertions
.
checkState
(
nalUnitLengthFieldLength
!=
3
);
Assertions
.
checkState
(
nalUnitLengthFieldLength
!=
3
);
List
<
byte
[]>
initializationData
=
new
ArrayList
<>();
List
<
byte
[]>
initializationData
=
new
ArrayList
<>();
int
numSequenceParameterSets
=
buffer
.
readUnsignedByte
()
&
0x1F
;
int
numSequenceParameterSets
=
buffer
.
readUnsignedByte
()
&
0x1F
;
for
(
int
i
=
0
;
i
<
numSequenceParameterSets
;
i
++)
{
for
(
int
i
=
0
;
i
<
numSequenceParameterSets
;
i
++)
{
initializationData
.
add
(
NalUnitUtil
.
parseChildNalUnit
(
buffer
));
initializationData
.
add
(
NalUnitUtil
.
parseChildNalUnit
(
buffer
));
}
}
int
numPictureParameterSets
=
buffer
.
readUnsignedByte
();
int
numPictureParameterSets
=
buffer
.
readUnsignedByte
();
for
(
int
j
=
0
;
j
<
numPictureParameterSets
;
j
++)
{
for
(
int
j
=
0
;
j
<
numPictureParameterSets
;
j
++)
{
initializationData
.
add
(
NalUnitUtil
.
parseChildNalUnit
(
buffer
));
initializationData
.
add
(
NalUnitUtil
.
parseChildNalUnit
(
buffer
));
}
}
float
pixelWidthAspectRatio
=
1
;
int
width
=
MediaFormat
.
NO_VALUE
;
int
height
=
MediaFormat
.
NO_VALUE
;
if
(
numSequenceParameterSets
>
0
)
{
// Parse the first sequence parameter set to obtain pixelWidthAspectRatio.
ParsableBitArray
spsDataBitArray
=
new
ParsableBitArray
(
initializationData
.
get
(
0
));
// Skip the NAL header consisting of the nalUnitLengthField and the type (1 byte).
spsDataBitArray
.
setPosition
(
8
*
(
nalUnitLengthFieldLength
+
1
));
CodecSpecificDataUtil
.
SpsData
sps
=
CodecSpecificDataUtil
.
parseSpsNalUnit
(
spsDataBitArray
);
width
=
sps
.
width
;
height
=
sps
.
height
;
pixelWidthAspectRatio
=
sps
.
pixelWidthAspectRatio
;
}
return
new
AvcSequenceHeaderData
(
initializationData
,
nalUnitLengthFieldLength
,
float
pixelWidthAspectRatio
=
1
;
width
,
height
,
pixelWidthAspectRatio
);
int
width
=
MediaFormat
.
NO_VALUE
;
}
catch
(
ArrayIndexOutOfBoundsException
e
)
{
int
height
=
MediaFormat
.
NO_VALUE
;
throw
new
ParserException
(
"Error parsing AVC codec private"
);
if
(
numSequenceParameterSets
>
0
)
{
// Parse the first sequence parameter set to obtain pixelWidthAspectRatio.
ParsableBitArray
spsDataBitArray
=
new
ParsableBitArray
(
initializationData
.
get
(
0
));
// Skip the NAL header consisting of the nalUnitLengthField and the type (1 byte).
spsDataBitArray
.
setPosition
(
8
*
(
nalUnitLengthFieldLength
+
1
));
CodecSpecificDataUtil
.
SpsData
sps
=
CodecSpecificDataUtil
.
parseSpsNalUnit
(
spsDataBitArray
);
width
=
sps
.
width
;
height
=
sps
.
height
;
pixelWidthAspectRatio
=
sps
.
pixelWidthAspectRatio
;
}
}
return
new
AvcSequenceHeaderData
(
initializationData
,
nalUnitLengthFieldLength
,
width
,
height
,
pixelWidthAspectRatio
);
}
}
/**
/**
...
...
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