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
aeb17e6a
authored
Nov 06, 2014
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
HLS improvements + steps towards ABR.
parent
9790430a
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
182 additions
and
113 deletions
library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java
library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java
library/src/main/java/com/google/android/exoplayer/hls/TsChunk.java
library/src/main/java/com/google/android/exoplayer/parser/ts/TsExtractor.java
library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java
View file @
aeb17e6a
...
...
@@ -45,7 +45,6 @@ public class HlsChunkSource {
private
final
HlsMasterPlaylist
masterPlaylist
;
private
final
HlsMediaPlaylistParser
mediaPlaylistParser
;
private
long
liveStartTimeUs
;
/* package */
HlsMediaPlaylist
mediaPlaylist
;
/* package */
boolean
mediaPlaylistWasLive
;
/* package */
long
lastMediaPlaylistLoadTimeMs
;
...
...
@@ -168,25 +167,22 @@ public class HlsChunkSource {
DataSpec
dataSpec
=
new
DataSpec
(
chunkUri
,
0
,
C
.
LENGTH_UNBOUNDED
,
null
);
long
startTimeUs
=
segment
.
startTimeUs
;
long
endTimeUs
=
startTimeUs
+
(
long
)
(
segment
.
durationSecs
*
1000000
);
long
startTimeUs
;
int
nextChunkMediaSequence
=
chunkMediaSequence
+
1
;
if
(
mediaPlaylistWasLive
)
{
if
(
queue
.
isEmpty
())
{
liveStartTimeUs
=
startTimeUs
;
startTimeUs
=
0
;
endTimeUs
-=
liveStartTimeUs
;
}
else
{
startTimeUs
-=
liveStartTimeUs
;
endTimeUs
-=
liveStartTimeUs
;
startTimeUs
=
queue
.
get
(
queue
.
size
()
-
1
).
endTimeUs
;
}
}
else
{
// Not live.
startTimeUs
=
segment
.
startTimeUs
;
if
(
chunkIndex
==
mediaPlaylist
.
segments
.
size
()
-
1
)
{
nextChunkMediaSequence
=
-
1
;
}
}
long
endTimeUs
=
startTimeUs
+
(
long
)
(
segment
.
durationSecs
*
1000000
);
DataSource
dataSource
;
if
(
encryptedDataSource
!=
null
)
{
...
...
@@ -194,9 +190,8 @@ public class HlsChunkSource {
}
else
{
dataSource
=
upstreamDataSource
;
}
out
.
chunk
=
new
TsChunk
(
dataSource
,
dataSpec
,
0
,
startTimeUs
,
endTimeUs
,
nextChunkMediaSequence
,
segment
.
discontinuity
);
out
.
chunk
=
new
TsChunk
(
dataSource
,
dataSpec
,
0
,
0
,
startTimeUs
,
endTimeUs
,
nextChunkMediaSequence
,
segment
.
discontinuity
,
false
);
}
private
boolean
shouldRerequestMediaPlaylist
()
{
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java
View file @
aeb17e6a
...
...
@@ -48,10 +48,11 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
private
static
final
long
MAX_SAMPLE_INTERLEAVING_OFFSET_US
=
5000000
;
private
static
final
int
NO_RESET_PENDING
=
-
1
;
private
final
TsExtractor
extractor
;
private
final
TsExtractor
.
SamplePool
samplePool
;
private
final
LoadControl
loadControl
;
private
final
HlsChunkSource
chunkSource
;
private
final
HlsChunkOperationHolder
currentLoadableHolder
;
private
final
LinkedList
<
TsExtractor
>
extractors
;
private
final
LinkedList
<
TsChunk
>
mediaChunks
;
private
final
List
<
TsChunk
>
readOnlyHlsChunks
;
private
final
int
bufferSizeContribution
;
...
...
@@ -84,7 +85,8 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
this
.
bufferSizeContribution
=
bufferSizeContribution
;
this
.
frameAccurateSeeking
=
frameAccurateSeeking
;
this
.
remainingReleaseCount
=
downstreamRendererCount
;
extractor
=
new
TsExtractor
();
samplePool
=
new
TsExtractor
.
SamplePool
();
extractors
=
new
LinkedList
<
TsExtractor
>();
currentLoadableHolder
=
new
HlsChunkOperationHolder
();
mediaChunks
=
new
LinkedList
<
TsChunk
>();
readOnlyHlsChunks
=
Collections
.
unmodifiableList
(
mediaChunks
);
...
...
@@ -100,6 +102,10 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
loadControl
.
register
(
this
,
bufferSizeContribution
);
}
continueBufferingInternal
();
if
(
extractors
.
isEmpty
())
{
return
false
;
}
TsExtractor
extractor
=
extractors
.
get
(
0
);
if
(
extractor
.
isPrepared
())
{
trackCount
=
extractor
.
getTrackCount
();
trackEnabledStates
=
new
boolean
[
trackCount
];
...
...
@@ -171,39 +177,38 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
}
TsChunk
mediaChunk
=
mediaChunks
.
getFirst
();
int
currentVariant
=
mediaChunk
.
variantIndex
;
TsExtractor
extractor
;
if
(
extractors
.
isEmpty
())
{
extractor
=
new
TsExtractor
(
mediaChunk
.
startTimeUs
,
samplePool
);
extractors
.
addLast
(
extractor
);
if
(
mediaChunk
.
discardFromFirstKeyframes
)
{
extractor
.
discardFromNextKeyframes
();
}
}
else
{
extractor
=
extractors
.
getLast
();
}
if
(
mediaChunk
.
isReadFinished
()
&&
mediaChunks
.
size
()
>
1
)
{
discardDownstreamHlsChunk
();
mediaChunk
=
mediaChunks
.
getFirst
();
}
boolean
haveSufficientSamples
=
false
;
if
(
mediaChunk
.
hasPendingDiscontinuity
())
{
if
(
extractor
.
hasSamples
())
{
// There are samples from before the discontinuity yet to be read from the extractor, so
// we don't want to reset the extractor yet.
haveSufficientSamples
=
true
;
}
else
{
extractor
.
reset
(
mediaChunk
.
startTimeUs
);
mediaChunk
.
clearPendingDiscontinuity
();
if
(
pendingDiscontinuities
==
null
)
{
// We're not prepared yet.
}
else
{
for
(
int
i
=
0
;
i
<
pendingDiscontinuities
.
length
;
i
++)
{
pendingDiscontinuities
[
i
]
=
true
;
}
}
if
(
mediaChunk
.
discontinuity
||
mediaChunk
.
variantIndex
!=
currentVariant
)
{
extractor
=
new
TsExtractor
(
mediaChunk
.
startTimeUs
,
samplePool
);
extractors
.
addLast
(
extractor
);
}
if
(
mediaChunk
.
discardFromFirstKeyframes
)
{
extractor
.
discardFromNextKeyframes
();
}
}
if
(!
mediaChunk
.
hasPendingDiscontinuity
())
{
// Allow the extractor to consume from the current chunk.
NonBlockingInputStream
inputStream
=
mediaChunk
.
getNonBlockingInputStream
();
haveSufficientSamples
=
extractor
.
consumeUntil
(
inputStream
,
downstreamPositionUs
+
MAX_SAMPLE_INTERLEAVING_OFFSET_US
);
// Allow the extractor to consume from the current chunk.
NonBlockingInputStream
inputStream
=
mediaChunk
.
getNonBlockingInputStream
();
boolean
haveSufficientSamples
=
extractor
.
consumeUntil
(
inputStream
,
downstreamPositionUs
+
MAX_SAMPLE_INTERLEAVING_OFFSET_US
);
if
(!
haveSufficientSamples
)
{
// If we can't read any more, then we always say we have sufficient samples.
if
(!
haveSufficientSamples
)
{
haveSufficientSamples
=
mediaChunk
.
isLastChunk
()
&&
mediaChunk
.
isReadFinished
();
}
haveSufficientSamples
=
mediaChunk
.
isLastChunk
()
&&
mediaChunk
.
isReadFinished
();
}
if
(!
haveSufficientSamples
&&
currentLoadableException
!=
null
)
{
...
...
@@ -223,7 +228,28 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
return
DISCONTINUITY_READ
;
}
if
(
onlyReadDiscontinuity
||
isPendingReset
()
||
!
extractor
.
isPrepared
())
{
if
(
onlyReadDiscontinuity
||
isPendingReset
())
{
return
NOTHING_READ
;
}
if
(
extractors
.
isEmpty
())
{
return
NOTHING_READ
;
}
TsExtractor
extractor
=
extractors
.
getFirst
();
while
(
extractors
.
size
()
>
1
&&
!
extractor
.
hasSamples
())
{
// We're finished reading from the extractor for all tracks, and so can discard it.
extractors
.
removeFirst
().
clear
();
extractor
=
extractors
.
getFirst
();
}
int
extractorIndex
=
0
;
while
(
extractors
.
size
()
>
extractorIndex
+
1
&&
!
extractor
.
hasSamples
(
track
))
{
// We're finished reading from the extractor for this particular track, so advance to the
// next one for the current read.
extractor
=
extractors
.
get
(++
extractorIndex
);
}
if
(!
extractor
.
isPrepared
())
{
return
NOTHING_READ
;
}
...
...
@@ -265,9 +291,9 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
if
(
mediaChunk
==
null
)
{
restartFrom
(
positionUs
);
}
else
{
discardExtractors
();
discardDownstreamHlsChunks
(
mediaChunk
);
mediaChunk
.
reset
();
extractor
.
reset
(
mediaChunk
.
startTimeUs
);
mediaChunk
.
resetReadPosition
();
updateLoadControl
();
}
}
...
...
@@ -494,13 +520,20 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
TsChunk
mediaChunk
=
(
TsChunk
)
currentLoadable
;
mediaChunks
.
add
(
mediaChunk
);
if
(
isPendingReset
())
{
extractor
.
reset
(
mediaChunk
.
startTimeUs
);
discardExtractors
(
);
pendingResetPositionUs
=
NO_RESET_PENDING
;
}
}
loader
.
startLoading
(
currentLoadable
,
this
);
}
private
void
discardExtractors
()
{
for
(
int
i
=
0
;
i
<
extractors
.
size
();
i
++)
{
extractors
.
get
(
i
).
clear
();
}
extractors
.
clear
();
}
/**
* Discards downstream media chunks until {@code untilChunk} if found. {@code untilChunk} is not
* itself discarded. Null can be passed to discard all media chunks.
...
...
library/src/main/java/com/google/android/exoplayer/hls/TsChunk.java
View file @
aeb17e6a
...
...
@@ -24,6 +24,10 @@ import com.google.android.exoplayer.upstream.DataSpec;
public
final
class
TsChunk
extends
HlsChunk
{
/**
* The index of the variant in the master playlist.
*/
public
final
int
variantIndex
;
/**
* The start time of the media contained by the chunk.
*/
public
final
long
startTimeUs
;
...
...
@@ -38,44 +42,38 @@ public final class TsChunk extends HlsChunk {
/**
* The encoding discontinuity indicator.
*/
private
final
boolean
discontinuity
;
private
boolean
pendingDiscontinuity
;
public
final
boolean
discontinuity
;
/**
* For each track, whether samples from the first keyframe (inclusive) should be discarded.
*/
public
final
boolean
discardFromFirstKeyframes
;
/**
* @param dataSource A {@link DataSource} for loading the data.
* @param dataSpec Defines the data to be loaded.
* @param trigger The reason for this chunk being selected.
* @param variantIndex The index of the variant in the master playlist.
* @param startTimeUs The start time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param nextChunkIndex The index of the next chunk, or -1 if this is the last chunk.
* @param discontinuity The encoding discontinuity indicator.
* @param discardFromFirstKeyframes For each contained media stream, whether samples from the
* first keyframe (inclusive) should be discarded.
*/
public
TsChunk
(
DataSource
dataSource
,
DataSpec
dataSpec
,
int
trigger
,
long
startTimeUs
,
long
endTimeUs
,
int
nextChunkIndex
,
boolean
discontinuity
)
{
public
TsChunk
(
DataSource
dataSource
,
DataSpec
dataSpec
,
int
trigger
,
int
variantIndex
,
long
startTimeUs
,
long
endTimeUs
,
int
nextChunkIndex
,
boolean
discontinuity
,
boolean
discardFromFirstKeyframes
)
{
super
(
dataSource
,
dataSpec
,
trigger
);
this
.
variantIndex
=
variantIndex
;
this
.
startTimeUs
=
startTimeUs
;
this
.
endTimeUs
=
endTimeUs
;
this
.
nextChunkIndex
=
nextChunkIndex
;
this
.
discontinuity
=
discontinuity
;
this
.
pendingDiscontinuity
=
discontinuity
;
this
.
discardFromFirstKeyframes
=
discardFromFirstKeyframes
;
}
public
boolean
isLastChunk
()
{
return
nextChunkIndex
==
-
1
;
}
public
void
reset
()
{
resetReadPosition
();
pendingDiscontinuity
=
discontinuity
;
}
public
boolean
hasPendingDiscontinuity
()
{
return
pendingDiscontinuity
;
}
public
void
clearPendingDiscontinuity
()
{
pendingDiscontinuity
=
false
;
}
}
library/src/main/java/com/google/android/exoplayer/parser/ts/TsExtractor.java
View file @
aeb17e6a
...
...
@@ -23,11 +23,13 @@ import com.google.android.exoplayer.util.CodecSpecificDataUtil;
import
com.google.android.exoplayer.util.MimeTypes
;
import
android.annotation.SuppressLint
;
import
android.media.MediaCodec
;
import
android.media.MediaExtractor
;
import
android.util.Log
;
import
android.util.Pair
;
import
android.util.SparseArray
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.LinkedList
;
import
java.util.Queue
;
...
...
@@ -47,26 +49,27 @@ public final class TsExtractor {
private
static
final
int
TS_STREAM_TYPE_H264
=
0x1B
;
private
static
final
int
TS_STREAM_TYPE_ID3
=
0x15
;
private
static
final
int
DEFAULT_BUFFER_SEGMENT_SIZE
=
64
*
1024
;
private
final
BitsArray
tsPacketBuffer
;
private
final
SparseArray
<
PesPayloadReader
>
pesPayloadReaders
;
// Indexed by streamType
private
final
SparseArray
<
TsPayloadReader
>
tsPayloadReaders
;
// Indexed by pid
private
final
Queue
<
Sample
>
samples
Pool
;
private
final
SamplePool
sample
Pool
;
private
boolean
prepared
;
/* package */
boolean
pending
TimestampOffsetUpdate
;
/* package */
long
pendingTimestampOffsetUs
;
/* package */
boolean
pending
FirstSampleTimestampAdjustment
;
/* package */
long
firstSampleTimestamp
;
/* package */
long
sampleTimestampOffsetUs
;
/* package */
long
largestParsedTimestampUs
;
/* package */
boolean
discardFromNextKeyframes
;
public
TsExtractor
()
{
public
TsExtractor
(
long
firstSampleTimestamp
,
SamplePool
samplePool
)
{
this
.
firstSampleTimestamp
=
firstSampleTimestamp
;
this
.
samplePool
=
samplePool
;
pendingFirstSampleTimestampAdjustment
=
true
;
tsPacketBuffer
=
new
BitsArray
();
pesPayloadReaders
=
new
SparseArray
<
PesPayloadReader
>();
tsPayloadReaders
=
new
SparseArray
<
TsPayloadReader
>();
tsPayloadReaders
.
put
(
TS_PAT_PID
,
new
PatReader
());
samplesPool
=
new
LinkedList
<
Sample
>();
largestParsedTimestampUs
=
Long
.
MIN_VALUE
;
}
...
...
@@ -105,22 +108,19 @@ public final class TsExtractor {
}
/**
*
Resets the extractor's internal state
.
*
Flushes any pending or incomplete samples, returning them to the sample pool
.
*/
public
void
reset
(
long
nextSampleTimestampUs
)
{
prepared
=
false
;
tsPacketBuffer
.
reset
();
tsPayloadReaders
.
clear
();
tsPayloadReaders
.
put
(
TS_PAT_PID
,
new
PatReader
());
// Clear each reader before discarding it, so as to recycle any queued Sample objects.
public
void
clear
()
{
for
(
int
i
=
0
;
i
<
pesPayloadReaders
.
size
();
i
++)
{
pesPayloadReaders
.
valueAt
(
i
).
clear
();
}
pesPayloadReaders
.
clear
();
// Configure for subsequent read operations.
pendingTimestampOffsetUpdate
=
true
;
pendingTimestampOffsetUs
=
nextSampleTimestampUs
;
largestParsedTimestampUs
=
Long
.
MIN_VALUE
;
}
/**
* For each track, whether to discard samples from the next keyframe (inclusive).
*/
public
void
discardFromNextKeyframes
()
{
discardFromNextKeyframes
=
true
;
}
/**
...
...
@@ -153,31 +153,43 @@ public final class TsExtractor {
*/
public
boolean
getSample
(
int
track
,
SampleHolder
out
)
{
Assertions
.
checkState
(
prepared
);
Queue
<
Sample
>
queue
=
pesPayloadReaders
.
valueAt
(
track
).
sample
s
Queue
;
Queue
<
Sample
>
queue
=
pesPayloadReaders
.
valueAt
(
track
).
sampleQueue
;
if
(
queue
.
isEmpty
())
{
return
false
;
}
Sample
sample
=
queue
.
remove
();
convert
(
sample
,
out
);
recycleSamp
le
(
sample
);
samplePool
.
recyc
le
(
sample
);
return
true
;
}
/**
* Whether samples are available for reading from {@link #getSample(int, SampleHolder)}.
* Whether samples are available for reading from {@link #getSample(int, SampleHolder)} for any
* track.
*
* @return True if samples are available for reading from {@link #getSample(int, SampleHolder)}
.
* False otherwise.
* @return True if samples are available for reading from {@link #getSample(int, SampleHolder)}
*
for any track.
False otherwise.
*/
public
boolean
hasSamples
()
{
for
(
int
i
=
0
;
i
<
pesPayloadReaders
.
size
();
i
++)
{
if
(
!
pesPayloadReaders
.
valueAt
(
i
).
samplesQueue
.
isEmpty
(
))
{
if
(
hasSamples
(
i
))
{
return
true
;
}
}
return
false
;
}
/**
* Whether samples are available for reading from {@link #getSample(int, SampleHolder)} for the
* specified track.
*
* @return True if samples are available for reading from {@link #getSample(int, SampleHolder)}
* for the specified track. False otherwise.
*/
public
boolean
hasSamples
(
int
track
)
{
return
!
pesPayloadReaders
.
valueAt
(
track
).
sampleQueue
.
isEmpty
();
}
private
boolean
checkPrepared
()
{
int
pesPayloadReaderCount
=
pesPayloadReaders
.
size
();
if
(
pesPayloadReaderCount
==
0
)
{
...
...
@@ -251,18 +263,6 @@ public final class TsExtractor {
out
.
timeUs
=
in
.
timeUs
;
}
/* package */
Sample
getSample
()
{
if
(
samplesPool
.
isEmpty
())
{
return
new
Sample
(
DEFAULT_BUFFER_SEGMENT_SIZE
);
}
return
samplesPool
.
remove
();
}
/* package */
void
recycleSample
(
Sample
sample
)
{
sample
.
reset
();
samplesPool
.
add
(
sample
);
}
/**
* Parses payload data.
*/
...
...
@@ -484,12 +484,14 @@ public final class TsExtractor {
*/
private
abstract
class
PesPayloadReader
{
public
final
Queue
<
Sample
>
sample
s
Queue
;
public
final
Queue
<
Sample
>
sampleQueue
;
private
MediaFormat
mediaFormat
;
private
boolean
foundFirstKeyframe
;
private
boolean
foundLastKeyframe
;
protected
PesPayloadReader
()
{
this
.
sample
s
Queue
=
new
LinkedList
<
Sample
>();
this
.
sampleQueue
=
new
LinkedList
<
Sample
>();
}
public
boolean
hasMediaFormat
()
{
...
...
@@ -507,8 +509,8 @@ public final class TsExtractor {
public
abstract
void
read
(
BitsArray
pesBuffer
,
int
pesPayloadSize
,
long
pesTimeUs
);
public
void
clear
()
{
while
(!
sample
s
Queue
.
isEmpty
())
{
recycleSample
(
samples
Queue
.
remove
());
while
(!
sampleQueue
.
isEmpty
())
{
samplePool
.
recycle
(
sample
Queue
.
remove
());
}
}
...
...
@@ -520,17 +522,31 @@ public final class TsExtractor {
* @param sampleTimeUs The sample time stamp.
*/
protected
void
addSample
(
BitsArray
buffer
,
int
sampleSize
,
long
sampleTimeUs
,
int
flags
)
{
Sample
sample
=
getSample
();
Sample
sample
=
samplePool
.
get
();
addToSample
(
sample
,
buffer
,
sampleSize
);
sample
.
flags
=
flags
;
sample
.
timeUs
=
sampleTimeUs
;
addSample
(
sample
);
}
@SuppressLint
(
"InlinedApi"
)
protected
void
addSample
(
Sample
sample
)
{
boolean
isKeyframe
=
(
sample
.
flags
&
MediaCodec
.
BUFFER_FLAG_SYNC_FRAME
)
!=
0
;
if
(
isKeyframe
)
{
if
(!
foundFirstKeyframe
)
{
foundFirstKeyframe
=
true
;
}
if
(
discardFromNextKeyframes
)
{
foundLastKeyframe
=
true
;
}
}
adjustTimestamp
(
sample
);
largestParsedTimestampUs
=
Math
.
max
(
largestParsedTimestampUs
,
sample
.
timeUs
);
samplesQueue
.
add
(
sample
);
if
(
foundFirstKeyframe
&&
!
foundLastKeyframe
)
{
largestParsedTimestampUs
=
Math
.
max
(
largestParsedTimestampUs
,
sample
.
timeUs
);
sampleQueue
.
add
(
sample
);
}
else
{
samplePool
.
recycle
(
sample
);
}
}
protected
void
addToSample
(
Sample
sample
,
BitsArray
buffer
,
int
size
)
{
...
...
@@ -542,9 +558,9 @@ public final class TsExtractor {
}
private
void
adjustTimestamp
(
Sample
sample
)
{
if
(
pending
TimestampOffsetUpdate
)
{
sampleTimestampOffsetUs
=
pendingTimestampOffsetUs
-
sample
.
timeUs
;
pending
TimestampOffsetUpdate
=
false
;
if
(
pending
FirstSampleTimestampAdjustment
)
{
sampleTimestampOffsetUs
=
firstSampleTimestamp
-
sample
.
timeUs
;
pending
FirstSampleTimestampAdjustment
=
false
;
}
sample
.
timeUs
+=
sampleTimestampOffsetUs
;
}
...
...
@@ -583,7 +599,7 @@ public final class TsExtractor {
if
(
currentSample
!=
null
)
{
addSample
(
currentSample
);
}
currentSample
=
getSample
();
currentSample
=
samplePool
.
get
();
pesPayloadSize
-=
readOneH264Frame
(
pesBuffer
,
false
);
currentSample
.
timeUs
=
pesTimeUs
;
...
...
@@ -615,7 +631,7 @@ public final class TsExtractor {
public
void
clear
()
{
super
.
clear
();
if
(
currentSample
!=
null
)
{
recycleSamp
le
(
currentSample
);
samplePool
.
recyc
le
(
currentSample
);
currentSample
=
null
;
}
}
...
...
@@ -742,8 +758,35 @@ public final class TsExtractor {
}
/**
* Simplified version of SampleHolder for internal buffering.
*/
* A pool from which the extractor can obtain sample objects for internal use.
*/
public
static
class
SamplePool
{
private
static
final
int
DEFAULT_BUFFER_SEGMENT_SIZE
=
64
*
1024
;
private
final
ArrayList
<
Sample
>
samples
;
public
SamplePool
()
{
samples
=
new
ArrayList
<
Sample
>();
}
/* package */
Sample
get
()
{
if
(
samples
.
isEmpty
())
{
return
new
Sample
(
DEFAULT_BUFFER_SEGMENT_SIZE
);
}
return
samples
.
remove
(
samples
.
size
()
-
1
);
}
/* package */
void
recycle
(
Sample
sample
)
{
sample
.
reset
();
samples
.
add
(
sample
);
}
}
/**
* Simplified version of SampleHolder for internal buffering.
*/
private
static
class
Sample
{
public
byte
[]
data
;
...
...
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