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
e6ca2df5
authored
Sep 10, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Fix end-of-stream for live streams.
Issue: #764
parent
89fcafec
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
121 additions
and
101 deletions
library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java
library/src/main/java/com/google/android/exoplayer/chunk/ChunkOperationHolder.java
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java
library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java
library/src/main/java/com/google/android/exoplayer/chunk/MediaChunk.java
library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java
library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java
library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java
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/smoothstreaming/SmoothStreamingChunkSource.java
library/src/main/java/com/google/android/exoplayer/chunk/BaseMediaChunk.java
View file @
e6ca2df5
...
...
@@ -46,7 +46,6 @@ public abstract class BaseMediaChunk extends MediaChunk {
* @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 chunkIndex The index of the chunk.
* @param isLastChunk True if this is the last chunk in the media. False otherwise.
* @param isMediaFormatFinal True if {@link #getMediaFormat()} and {@link #getDrmInitData()} can
* be called at any time to obtain the media format and drm initialization data. False if
* these methods are only guaranteed to return correct data after the first sample data has
...
...
@@ -54,10 +53,8 @@ public abstract class BaseMediaChunk extends MediaChunk {
* @param parentId Identifier for a parent from which this chunk originates.
*/
public
BaseMediaChunk
(
DataSource
dataSource
,
DataSpec
dataSpec
,
int
trigger
,
Format
format
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
boolean
isLastChunk
,
boolean
isMediaFormatFinal
,
int
parentId
)
{
super
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
isLastChunk
,
parentId
);
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
boolean
isMediaFormatFinal
,
int
parentId
)
{
super
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
parentId
);
this
.
isMediaFormatFinal
=
isMediaFormatFinal
;
}
...
...
library/src/main/java/com/google/android/exoplayer/chunk/ChunkOperationHolder.java
View file @
e6ca2df5
...
...
@@ -16,8 +16,13 @@
package
com
.
google
.
android
.
exoplayer
.
chunk
;
/**
* Holds a chunk operation, which consists of a {@link Chunk} to load together with the number of
* {@link MediaChunk}s that should be retained on the queue.
* Holds a chunk operation, which consists of a either:
* <ul>
* <li>The number of {@link MediaChunk}s that should be retained on the queue ({@link #queueSize})
* together with the next {@link Chunk} to load ({@link #chunk}). {@link #chunk} may be null if the
* next chunk cannot be provided yet.</li>
* <li>A flag indicating that the end of the stream has been reached ({@link #endOfStream}).</li>
* </ul>
*/
public
final
class
ChunkOperationHolder
{
...
...
@@ -31,4 +36,18 @@ public final class ChunkOperationHolder {
*/
public
Chunk
chunk
;
/**
* Indicates that the end of the stream has been reached.
*/
public
boolean
endOfStream
;
/**
* Clears the holder.
*/
public
void
clear
()
{
queueSize
=
0
;
chunk
=
null
;
endOfStream
=
false
;
}
}
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java
View file @
e6ca2df5
...
...
@@ -325,10 +325,9 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
Chunk
currentLoadable
=
currentLoadableHolder
.
chunk
;
chunkSource
.
onChunkLoadCompleted
(
currentLoadable
);
if
(
isMediaChunk
(
currentLoadable
))
{
MediaChunk
mediaChunk
=
(
MediaChunk
)
currentLoadable
;
BaseMediaChunk
mediaChunk
=
(
Base
MediaChunk
)
currentLoadable
;
notifyLoadCompleted
(
currentLoadable
.
bytesLoaded
(),
mediaChunk
.
type
,
mediaChunk
.
trigger
,
mediaChunk
.
format
,
mediaChunk
.
startTimeUs
,
mediaChunk
.
endTimeUs
,
now
,
loadDurationMs
);
loadingFinished
=
((
BaseMediaChunk
)
currentLoadable
).
isLastChunk
;
}
else
{
notifyLoadCompleted
(
currentLoadable
.
bytesLoaded
(),
currentLoadable
.
type
,
currentLoadable
.
trigger
,
currentLoadable
.
format
,
-
1
,
-
1
,
now
,
loadDurationMs
);
...
...
@@ -408,9 +407,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
||
(
now
-
lastPerformedBufferOperation
>
2000
)))
{
// Perform the evaluation.
lastPerformedBufferOperation
=
now
;
currentLoadableHolder
.
queueSize
=
readOnlyMediaChunks
.
size
();
chunkSource
.
getChunkOperation
(
readOnlyMediaChunks
,
pendingResetPositionUs
,
downstreamPositionUs
,
currentLoadableHolder
);
doChunkOperation
();
boolean
chunksDiscarded
=
discardUpstreamMediaChunks
(
currentLoadableHolder
.
queueSize
);
// Update the next load position as appropriate.
if
(
currentLoadableHolder
.
chunk
==
null
)
{
...
...
@@ -447,8 +444,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
if
(
isPendingReset
())
{
return
pendingResetPositionUs
;
}
else
{
BaseMediaChunk
lastMediaChunk
=
mediaChunks
.
getLast
();
return
lastMediaChunk
.
isLastChunk
?
-
1
:
lastMediaChunk
.
endTimeUs
;
return
loadingFinished
?
-
1
:
mediaChunks
.
getLast
().
endTimeUs
;
}
}
...
...
@@ -464,9 +460,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
Chunk
backedOffChunk
=
currentLoadableHolder
.
chunk
;
if
(!
isMediaChunk
(
backedOffChunk
))
{
currentLoadableHolder
.
queueSize
=
readOnlyMediaChunks
.
size
();
chunkSource
.
getChunkOperation
(
readOnlyMediaChunks
,
pendingResetPositionUs
,
downstreamPositionUs
,
currentLoadableHolder
);
doChunkOperation
();
discardUpstreamMediaChunks
(
currentLoadableHolder
.
queueSize
);
if
(
currentLoadableHolder
.
chunk
==
backedOffChunk
)
{
// Chunk was unchanged. Resume loading.
...
...
@@ -491,9 +485,7 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
// and add it back again afterwards.
BaseMediaChunk
removedChunk
=
mediaChunks
.
removeLast
();
Assertions
.
checkState
(
backedOffChunk
==
removedChunk
);
currentLoadableHolder
.
queueSize
=
readOnlyMediaChunks
.
size
();
chunkSource
.
getChunkOperation
(
readOnlyMediaChunks
,
pendingResetPositionUs
,
downstreamPositionUs
,
currentLoadableHolder
);
doChunkOperation
();
mediaChunks
.
add
(
removedChunk
);
if
(
currentLoadableHolder
.
chunk
==
backedOffChunk
)
{
...
...
@@ -534,6 +526,19 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
}
/**
* Sets up the {@link #currentLoadableHolder}, passes it to the chunk source to cause it to be
* updated with the next operation, and updates {@link #loadingFinished} if the end of the stream
* is reached.
*/
private
void
doChunkOperation
()
{
currentLoadableHolder
.
endOfStream
=
false
;
currentLoadableHolder
.
queueSize
=
readOnlyMediaChunks
.
size
();
chunkSource
.
getChunkOperation
(
readOnlyMediaChunks
,
pendingResetPositionUs
,
downstreamPositionUs
,
currentLoadableHolder
);
loadingFinished
=
currentLoadableHolder
.
endOfStream
;
}
/**
* Discard upstream media chunks until the queue length is equal to the length specified.
*
* @param queueLength The desired length of the queue.
...
...
library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java
View file @
e6ca2df5
...
...
@@ -89,24 +89,19 @@ public interface ChunkSource {
* Updates the provided {@link ChunkOperationHolder} to contain the next operation that should
* be performed by the calling {@link ChunkSampleSource}.
* <p>
* The next operation comprises of a possibly shortened queue length (shortened if the
* implementation wishes for the caller to discard {@link MediaChunk}s from the queue), together
* with the next {@link Chunk} to load. The next chunk may be a {@link MediaChunk} to be added to
* the queue, or another {@link Chunk} type (e.g. to load initialization data), or null if the
* source is not able to provide a chunk in its current state.
* <p>
* This method should only be called when the source is enabled.
*
* @param queue A representation of the currently buffered {@link MediaChunk}s.
* @param seekPositionUs If the queue is empty, this parameter must specify the seek position. If
* the queue is non-empty then this parameter is ignored.
* @param playbackPositionUs The current playback position.
* @param out A holder for the next operation, whose {@link ChunkOperationHolder#queueSize} is
* initially equal to the length of the queue, and whose {@link ChunkOperationHolder#chunk} is
* initially equal to null or a {@link Chunk} previously supplied by the {@link ChunkSource}
* that the caller has not yet finished loading. In the latter case the chunk can either be
* replaced or left unchanged. Note that leaving the chunk unchanged is both preferred and
* more efficient than replacing it with a new but identical chunk.
* @param out A holder for the next operation, whose {@link ChunkOperationHolder#endOfStream} is
* initially set to false, whose {@link ChunkOperationHolder#queueSize} is initially equal to
* the length of the queue, and whose {@link ChunkOperationHolder#chunk} is initially equal to
* null or a {@link Chunk} previously supplied by the {@link ChunkSource} that the caller has
* not yet finished loading. In the latter case the chunk can either be replaced or left
* unchanged. Note that leaving the chunk unchanged is both preferred and more efficient than
* replacing it with a new but identical chunk.
*/
void
getChunkOperation
(
List
<?
extends
MediaChunk
>
queue
,
long
seekPositionUs
,
long
playbackPositionUs
,
ChunkOperationHolder
out
);
...
...
library/src/main/java/com/google/android/exoplayer/chunk/ContainerMediaChunk.java
View file @
e6ca2df5
...
...
@@ -53,7 +53,6 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
* @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 chunkIndex The index of the chunk.
* @param isLastChunk True if this is the last chunk in the media. False otherwise.
* @param sampleOffsetUs An offset to add to the sample timestamps parsed by the extractor.
* @param extractorWrapper A wrapped extractor to use for parsing the data.
* @param mediaFormat The {@link MediaFormat} of the chunk, if known. May be null if the data is
...
...
@@ -71,10 +70,10 @@ public class ContainerMediaChunk extends BaseMediaChunk implements SingleTrackOu
* @param parentId Identifier for a parent from which this chunk originates.
*/
public
ContainerMediaChunk
(
DataSource
dataSource
,
DataSpec
dataSpec
,
int
trigger
,
Format
format
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
boolean
isLastChunk
,
long
sampleOffsetUs
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
long
sampleOffsetUs
,
ChunkExtractorWrapper
extractorWrapper
,
MediaFormat
mediaFormat
,
int
adaptiveMaxWidth
,
int
adaptiveMaxHeight
,
DrmInitData
drmInitData
,
boolean
isMediaFormatFinal
,
int
parentId
)
{
super
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
isLastChunk
,
super
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
isMediaFormatFinal
,
parentId
);
this
.
extractorWrapper
=
extractorWrapper
;
this
.
sampleOffsetUs
=
sampleOffsetUs
;
...
...
library/src/main/java/com/google/android/exoplayer/chunk/MediaChunk.java
View file @
e6ca2df5
...
...
@@ -36,14 +36,10 @@ public abstract class MediaChunk extends Chunk {
* The chunk index.
*/
public
final
int
chunkIndex
;
/**
* True if this is the last chunk in the media. False otherwise.
*/
public
final
boolean
isLastChunk
;
public
MediaChunk
(
DataSource
dataSource
,
DataSpec
dataSpec
,
int
trigger
,
Format
format
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
boolean
isLastChunk
)
{
this
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
isLastChunk
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
)
{
this
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
Chunk
.
NO_PARENT_ID
);
}
...
...
@@ -55,17 +51,15 @@ public abstract class MediaChunk extends Chunk {
* @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 chunkIndex The index of the chunk.
* @param isLastChunk True if this is the last chunk in the media. False otherwise.
* @param parentId Identifier for a parent from which this chunk originates.
*/
public
MediaChunk
(
DataSource
dataSource
,
DataSpec
dataSpec
,
int
trigger
,
Format
format
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
boolean
isLastChunk
,
int
parentId
)
{
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
int
parentId
)
{
super
(
dataSource
,
dataSpec
,
Chunk
.
TYPE_MEDIA
,
trigger
,
format
,
parentId
);
Assertions
.
checkNotNull
(
format
);
this
.
startTimeUs
=
startTimeUs
;
this
.
endTimeUs
=
endTimeUs
;
this
.
chunkIndex
=
chunkIndex
;
this
.
isLastChunk
=
isLastChunk
;
}
}
library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java
View file @
e6ca2df5
...
...
@@ -84,6 +84,7 @@ public final class SingleSampleChunkSource implements ChunkSource {
long
playbackPositionUs
,
ChunkOperationHolder
out
)
{
if
(!
queue
.
isEmpty
())
{
// We've already provided the single sample.
out
.
endOfStream
=
true
;
return
;
}
out
.
chunk
=
initChunk
();
...
...
@@ -111,7 +112,7 @@ public final class SingleSampleChunkSource implements ChunkSource {
private
SingleSampleMediaChunk
initChunk
()
{
return
new
SingleSampleMediaChunk
(
dataSource
,
dataSpec
,
Chunk
.
TRIGGER_UNSPECIFIED
,
format
,
0
,
durationUs
,
0
,
true
,
mediaFormat
,
null
,
Chunk
.
NO_PARENT_ID
);
durationUs
,
0
,
mediaFormat
,
null
,
Chunk
.
NO_PARENT_ID
);
}
}
library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java
View file @
e6ca2df5
...
...
@@ -43,17 +43,16 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
* @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 chunkIndex The index of the chunk.
* @param isLastChunk True if this is the last chunk in the media. False otherwise.
* @param sampleFormat The format of the sample.
* @param sampleDrmInitData The {@link DrmInitData} for the sample. Null if the sample is not drm
* protected.
* @param parentId Identifier for a parent from which this chunk originates.
*/
public
SingleSampleMediaChunk
(
DataSource
dataSource
,
DataSpec
dataSpec
,
int
trigger
,
Format
format
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
boolean
isLastChunk
,
MediaFormat
sampleFormat
,
DrmInitData
sampleDrmInitData
,
int
parentId
)
{
super
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
isLastChunk
,
true
,
parentId
);
Format
format
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
MediaFormat
sampleFormat
,
DrmInitData
sampleDrmInitData
,
int
parentId
)
{
super
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
true
,
parentId
);
this
.
sampleFormat
=
sampleFormat
;
this
.
sampleDrmInitData
=
sampleDrmInitData
;
}
...
...
library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java
View file @
e6ca2df5
...
...
@@ -400,11 +400,6 @@ public class DashChunkSource implements ChunkSource, Output {
}
MediaChunk
previous
=
queue
.
get
(
out
.
queueSize
-
1
);
if
(
previous
.
isLastChunk
)
{
// We've reached the end of the stream.
return
;
}
long
nextSegmentStartTimeUs
=
previous
.
endTimeUs
;
if
(
live
&&
nextSegmentStartTimeUs
<
availableRangeValues
[
0
])
{
// This is before the first chunk in the current manifest.
...
...
@@ -415,6 +410,17 @@ public class DashChunkSource implements ChunkSource, Output {
// we'll need to wait until it's refreshed. If it's unbounded we just need to wait for a
// while before attempting to load the chunk.
return
;
}
else
if
(!
currentManifest
.
dynamic
)
{
// The current manifest isn't dynamic, so check whether we've reached the end of the stream.
PeriodHolder
lastPeriodHolder
=
periodHolders
.
valueAt
(
periodHolders
.
size
()
-
1
);
if
(
previous
.
parentId
==
lastPeriodHolder
.
localIndex
)
{
RepresentationHolder
representationHolder
=
lastPeriodHolder
.
representationHolders
.
get
(
previous
.
format
.
id
);
if
(
representationHolder
.
isLastSegment
(
previous
.
chunkIndex
))
{
out
.
endOfStream
=
true
;
return
;
}
}
}
startingNewPeriod
=
false
;
...
...
@@ -701,13 +707,8 @@ public class DashChunkSource implements ChunkSource, Output {
DataSource
dataSource
,
MediaFormat
mediaFormat
,
int
segmentNum
,
int
trigger
)
{
Representation
representation
=
representationHolder
.
representation
;
Format
format
=
representation
.
format
;
long
startTimeUs
=
representationHolder
.
getSegmentStartTimeUs
(
segmentNum
);
long
endTimeUs
=
representationHolder
.
getSegmentEndTimeUs
(
segmentNum
);
boolean
isLastSegment
=
!
currentManifest
.
dynamic
&&
periodHolders
.
valueAt
(
periodHolders
.
size
()
-
1
)
==
periodHolder
&&
representationHolder
.
isLastSegment
(
segmentNum
);
RangedUri
segmentUri
=
representationHolder
.
getSegmentUrl
(
segmentNum
);
DataSpec
dataSpec
=
new
DataSpec
(
segmentUri
.
getUri
(),
segmentUri
.
start
,
segmentUri
.
length
,
representation
.
getCacheKey
());
...
...
@@ -715,15 +716,15 @@ public class DashChunkSource implements ChunkSource, Output {
long
sampleOffsetUs
=
periodHolder
.
startTimeUs
-
representation
.
presentationTimeOffsetUs
;
if
(
mimeTypeIsRawText
(
format
.
mimeType
))
{
return
new
SingleSampleMediaChunk
(
dataSource
,
dataSpec
,
Chunk
.
TRIGGER_INITIAL
,
format
,
startTimeUs
,
endTimeUs
,
segmentNum
,
isLastSegment
,
startTimeUs
,
endTimeUs
,
segmentNum
,
MediaFormat
.
createTextFormat
(
format
.
mimeType
,
MediaFormat
.
NO_VALUE
,
format
.
language
),
null
,
periodHolder
.
localIndex
);
}
else
{
boolean
isMediaFormatFinal
=
(
mediaFormat
!=
null
);
return
new
ContainerMediaChunk
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
segmentNum
,
isLastSegment
,
sampleOffsetUs
,
representationHolder
.
extractorWrapper
,
mediaFormat
,
enabledTrack
.
adaptiveMaxWidth
,
enabledTrack
.
adaptiveMaxHeight
,
periodHolder
.
drmInitData
,
isMediaFormatFinal
,
periodHolder
.
localIndex
);
segmentNum
,
sampleOffsetUs
,
representationHolder
.
extractorWrapper
,
mediaFormat
,
enabledTrack
.
adaptiveMaxWidth
,
enabledTrack
.
adaptiveMaxHeight
,
periodHolder
.
drmInitData
,
isMediaFormatFinal
,
periodHolder
.
localIndex
);
}
}
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java
View file @
e6ca2df5
...
...
@@ -19,6 +19,7 @@ import com.google.android.exoplayer.C;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener
;
import
com.google.android.exoplayer.chunk.Chunk
;
import
com.google.android.exoplayer.chunk.ChunkOperationHolder
;
import
com.google.android.exoplayer.chunk.DataChunk
;
import
com.google.android.exoplayer.chunk.Format
;
import
com.google.android.exoplayer.extractor.Extractor
;
...
...
@@ -237,16 +238,18 @@ public class HlsChunkSource {
}
/**
* Returns the next {@link Chunk} that should be loaded.
* Updates the provided {@link ChunkOperationHolder} to contain the next operation that should
* be performed by the calling {@link HlsSampleSource}.
*
* @param previousTsChunk The previously loaded chunk that the next chunk should follow.
* @param seekPositionUs If there is no previous chunk, this parameter must specify the seek
* position. If there is a previous chunk then this parameter is ignored.
* @param playbackPositionUs The current playback position.
* @return The next chunk to load.
* @param out The holder to populate with the result. {@link ChunkOperationHolder#queueSize} is
* unused.
*/
public
Chunk
getChunkOperation
(
TsChunk
previousTsChunk
,
long
seekPositionUs
,
long
playbackPositionUs
)
{
public
void
getChunkOperation
(
TsChunk
previousTsChunk
,
long
seekPositionUs
,
long
playbackPositionUs
,
ChunkOperationHolder
out
)
{
int
nextVariantIndex
;
boolean
switchingVariantSpliced
;
if
(
adaptiveMode
==
ADAPTIVE_MODE_NONE
)
{
...
...
@@ -262,7 +265,8 @@ public class HlsChunkSource {
HlsMediaPlaylist
mediaPlaylist
=
variantPlaylists
[
nextVariantIndex
];
if
(
mediaPlaylist
==
null
)
{
// We don't have the media playlist for the next variant. Request it now.
return
newMediaPlaylistChunk
(
nextVariantIndex
);
out
.
chunk
=
newMediaPlaylistChunk
(
nextVariantIndex
);
return
;
}
selectedVariantIndex
=
nextVariantIndex
;
...
...
@@ -299,11 +303,12 @@ public class HlsChunkSource {
int
chunkIndex
=
chunkMediaSequence
-
mediaPlaylist
.
mediaSequence
;
if
(
chunkIndex
>=
mediaPlaylist
.
segments
.
size
())
{
if
(
mediaPlaylist
.
live
&&
shouldRerequestMediaPlaylist
(
nextVariantIndex
)
)
{
return
newMediaPlaylistChunk
(
nextVariantIndex
)
;
}
else
{
return
null
;
if
(
!
mediaPlaylist
.
live
)
{
out
.
endOfStream
=
true
;
}
else
if
(
shouldRerequestLiveMediaPlaylist
(
nextVariantIndex
))
{
out
.
chunk
=
newMediaPlaylistChunk
(
nextVariantIndex
)
;
}
return
;
}
HlsMediaPlaylist
.
Segment
segment
=
mediaPlaylist
.
segments
.
get
(
chunkIndex
);
...
...
@@ -314,8 +319,8 @@ public class HlsChunkSource {
Uri
keyUri
=
UriUtil
.
resolveToUri
(
mediaPlaylist
.
baseUri
,
segment
.
encryptionKeyUri
);
if
(!
keyUri
.
equals
(
encryptionKeyUri
))
{
// Encryption is specified and the key has changed.
Chunk
toReturn
=
newEncryptionKeyChunk
(
keyUri
,
segment
.
encryptionIV
,
selectedVariantIndex
);
return
toReturn
;
out
.
chunk
=
newEncryptionKeyChunk
(
keyUri
,
segment
.
encryptionIV
,
selectedVariantIndex
);
return
;
}
if
(!
Util
.
areEqual
(
segment
.
encryptionIV
,
encryptionIvString
))
{
setEncryptionData
(
keyUri
,
segment
.
encryptionIV
,
encryptionKey
);
...
...
@@ -342,7 +347,6 @@ public class HlsChunkSource {
startTimeUs
=
segment
.
startTimeUs
;
}
long
endTimeUs
=
startTimeUs
+
(
long
)
(
segment
.
durationSecs
*
C
.
MICROS_PER_SECOND
);
boolean
isLastChunk
=
!
mediaPlaylist
.
live
&&
chunkIndex
==
mediaPlaylist
.
segments
.
size
()
-
1
;
int
trigger
=
Chunk
.
TRIGGER_UNSPECIFIED
;
Format
format
=
variants
[
selectedVariantIndex
].
format
;
...
...
@@ -358,9 +362,8 @@ public class HlsChunkSource {
extractorWrapper
=
previousTsChunk
.
extractorWrapper
;
}
return
new
TsChunk
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkMediaSequence
,
isLastChunk
,
extractorWrapper
,
encryptionKey
,
encryptionIv
);
out
.
chunk
=
new
TsChunk
(
dataSource
,
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkMediaSequence
,
extractorWrapper
,
encryptionKey
,
encryptionIv
);
}
/**
...
...
@@ -492,7 +495,7 @@ public class HlsChunkSource {
return
lowestQualityEnabledVariantIndex
;
}
private
boolean
shouldRerequestMediaPlaylist
(
int
nextVariantIndex
)
{
private
boolean
shouldRerequest
Live
MediaPlaylist
(
int
nextVariantIndex
)
{
// Don't re-request media playlist more often than one-half of the target duration.
HlsMediaPlaylist
mediaPlaylist
=
variantPlaylists
[
nextVariantIndex
];
long
timeSinceLastMediaPlaylistLoadMs
=
...
...
library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java
View file @
e6ca2df5
...
...
@@ -25,6 +25,7 @@ import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import
com.google.android.exoplayer.TrackRenderer
;
import
com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener
;
import
com.google.android.exoplayer.chunk.Chunk
;
import
com.google.android.exoplayer.chunk.ChunkOperationHolder
;
import
com.google.android.exoplayer.chunk.Format
;
import
com.google.android.exoplayer.upstream.Loader
;
import
com.google.android.exoplayer.upstream.Loader.Loadable
;
...
...
@@ -58,6 +59,7 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
private
final
LinkedList
<
HlsExtractorWrapper
>
extractors
;
private
final
int
minLoadableRetryCount
;
private
final
int
bufferSizeContribution
;
private
final
ChunkOperationHolder
chunkOperationHolder
;
private
final
int
eventSourceId
;
private
final
LoadControl
loadControl
;
...
...
@@ -114,6 +116,7 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
this
.
eventSourceId
=
eventSourceId
;
this
.
pendingResetPositionUs
=
NO_RESET_PENDING
;
extractors
=
new
LinkedList
<>();
chunkOperationHolder
=
new
ChunkOperationHolder
();
}
@Override
...
...
@@ -383,7 +386,6 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
chunkSource
.
onChunkLoadCompleted
(
currentLoadable
);
if
(
isTsChunk
(
currentLoadable
))
{
Assertions
.
checkState
(
currentLoadable
==
currentTsLoadable
);
loadingFinished
=
currentTsLoadable
.
isLastChunk
;
previousTsLoadable
=
currentTsLoadable
;
notifyLoadCompleted
(
currentLoadable
.
bytesLoaded
(),
currentTsLoadable
.
type
,
currentTsLoadable
.
trigger
,
currentTsLoadable
.
format
,
currentTsLoadable
.
startTimeUs
,
...
...
@@ -521,8 +523,16 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
return
;
}
Chunk
nextLoadable
=
chunkSource
.
getChunkOperation
(
previousTsLoadable
,
pendingResetPositionUs
,
downstreamPositionUs
);
chunkSource
.
getChunkOperation
(
previousTsLoadable
,
pendingResetPositionUs
,
downstreamPositionUs
,
chunkOperationHolder
);
boolean
endOfStream
=
chunkOperationHolder
.
endOfStream
;
Chunk
nextLoadable
=
chunkOperationHolder
.
chunk
;
chunkOperationHolder
.
clear
();
if
(
endOfStream
)
{
loadingFinished
=
true
;
return
;
}
if
(
nextLoadable
==
null
)
{
return
;
}
...
...
@@ -557,9 +567,8 @@ public final class HlsSampleSource implements SampleSource, SampleSourceReader,
if
(
isPendingReset
())
{
return
pendingResetPositionUs
;
}
else
{
return
currentTsLoadable
!=
null
?
(
currentTsLoadable
.
isLastChunk
?
-
1
:
currentTsLoadable
.
endTimeUs
)
:
(
previousTsLoadable
.
isLastChunk
?
-
1
:
previousTsLoadable
.
endTimeUs
);
return
loadingFinished
?
-
1
:
currentTsLoadable
!=
null
?
currentTsLoadable
.
endTimeUs
:
previousTsLoadable
.
endTimeUs
;
}
}
...
...
library/src/main/java/com/google/android/exoplayer/hls/TsChunk.java
View file @
e6ca2df5
...
...
@@ -49,16 +49,15 @@ public final class TsChunk extends MediaChunk {
* @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 chunkIndex The index of the chunk.
* @param isLastChunk True if this is the last chunk in the media. False otherwise.
* @param extractorWrapper A wrapped extractor to parse samples from the data.
* @param encryptionKey For AES encryption chunks, the encryption key.
* @param encryptionIv For AES encryption chunks, the encryption initialization vector.
*/
public
TsChunk
(
DataSource
dataSource
,
DataSpec
dataSpec
,
int
trigger
,
Format
format
,
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
boolean
isLastChunk
,
HlsExtractorWrapper
extractorWrapper
,
byte
[]
encryptionKey
,
byte
[]
encryptionIv
)
{
long
startTimeUs
,
long
endTimeUs
,
int
chunkIndex
,
HlsExtractorWrapper
extractorWrapper
,
byte
[]
encryptionKey
,
byte
[]
encryptionIv
)
{
super
(
buildDataSource
(
dataSource
,
encryptionKey
,
encryptionIv
),
dataSpec
,
trigger
,
format
,
startTimeUs
,
endTimeUs
,
chunkIndex
,
isLastChunk
);
startTimeUs
,
endTimeUs
,
chunkIndex
);
this
.
extractorWrapper
=
extractorWrapper
;
// Note: this.dataSource and dataSource may be different.
this
.
isEncrypted
=
this
.
dataSource
instanceof
Aes128DataSource
;
...
...
library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java
View file @
e6ca2df5
...
...
@@ -270,6 +270,8 @@ public class SmoothStreamingChunkSource implements ChunkSource,
if
(
streamElement
.
chunkCount
==
0
)
{
if
(
currentManifest
.
isLive
)
{
needManifestRefresh
=
true
;
}
else
{
out
.
endOfStream
=
true
;
}
return
;
}
...
...
@@ -282,7 +284,7 @@ public class SmoothStreamingChunkSource implements ChunkSource,
chunkIndex
=
streamElement
.
getChunkIndex
(
seekPositionUs
);
}
else
{
MediaChunk
previous
=
queue
.
get
(
out
.
queueSize
-
1
);
chunkIndex
=
previous
.
isLastChunk
?
-
1
:
previous
.
chunkIndex
+
1
-
currentManifestChunkOffset
;
chunkIndex
=
previous
.
chunkIndex
+
1
-
currentManifestChunkOffset
;
}
if
(
live
&&
chunkIndex
<
0
)
{
...
...
@@ -299,10 +301,8 @@ public class SmoothStreamingChunkSource implements ChunkSource,
// but continue to return the final chunk.
needManifestRefresh
=
true
;
}
}
if
(
chunkIndex
==
-
1
)
{
// We've reached the end of the stream.
}
else
if
(
chunkIndex
>=
streamElement
.
chunkCount
)
{
out
.
endOfStream
=
true
;
return
;
}
...
...
@@ -317,9 +317,8 @@ public class SmoothStreamingChunkSource implements ChunkSource,
Uri
uri
=
streamElement
.
buildRequestUri
(
manifestTrackIndex
,
chunkIndex
);
Chunk
mediaChunk
=
newMediaChunk
(
selectedFormat
,
uri
,
null
,
extractorWrappers
.
get
(
manifestTrackKey
),
drmInitData
,
dataSource
,
currentAbsoluteChunkIndex
,
isLastChunk
,
chunkStartTimeUs
,
chunkEndTimeUs
,
evaluation
.
trigger
,
mediaFormats
.
get
(
manifestTrackKey
),
enabledTrack
.
adaptiveMaxWidth
,
enabledTrack
.
adaptiveMaxHeight
);
chunkStartTimeUs
,
chunkEndTimeUs
,
evaluation
.
trigger
,
mediaFormats
.
get
(
manifestTrackKey
),
enabledTrack
.
adaptiveMaxWidth
,
enabledTrack
.
adaptiveMaxHeight
);
out
.
chunk
=
mediaChunk
;
}
...
...
@@ -474,14 +473,14 @@ public class SmoothStreamingChunkSource implements ChunkSource,
private
static
MediaChunk
newMediaChunk
(
Format
formatInfo
,
Uri
uri
,
String
cacheKey
,
ChunkExtractorWrapper
extractorWrapper
,
DrmInitData
drmInitData
,
DataSource
dataSource
,
int
chunkIndex
,
boolean
isLast
,
long
chunkStartTimeUs
,
long
chunkEndTimeUs
,
int
trigger
,
MediaFormat
mediaFormat
,
int
adaptiveMaxWidth
,
int
adaptiveMaxHeight
)
{
int
chunkIndex
,
long
chunkStartTimeUs
,
long
chunkEndTimeUs
,
int
trigger
,
MediaFormat
mediaFormat
,
int
adaptiveMaxWidth
,
int
adaptiveMaxHeight
)
{
long
offset
=
0
;
DataSpec
dataSpec
=
new
DataSpec
(
uri
,
offset
,
-
1
,
cacheKey
);
// In SmoothStreaming each chunk contains sample timestamps relative to the start of the chunk.
// To convert them the absolute timestamps, we need to set sampleOffsetUs to -chunkStartTimeUs.
return
new
ContainerMediaChunk
(
dataSource
,
dataSpec
,
trigger
,
formatInfo
,
chunkStartTimeUs
,
chunkEndTimeUs
,
chunkIndex
,
isLast
,
chunkStartTimeUs
,
extractorWrapper
,
mediaFormat
,
chunkEndTimeUs
,
chunkIndex
,
chunkStartTimeUs
,
extractorWrapper
,
mediaFormat
,
adaptiveMaxWidth
,
adaptiveMaxHeight
,
drmInitData
,
true
,
Chunk
.
NO_PARENT_ID
);
}
...
...
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