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
39f8c775
authored
Nov 19, 2020
by
bachinger
Committed by
Oliver Woodman
Nov 19, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Replace cancelled HLS preload parts
Issue: #5011 PiperOrigin-RevId: 343277357
parent
31166d41
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
75 additions
and
12 deletions
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
View file @
39f8c775
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
java
.
lang
.
Math
.
max
;
import
static
java
.
lang
.
Math
.
max
;
import
android.net.Uri
;
import
android.net.Uri
;
...
@@ -39,7 +40,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
...
@@ -39,7 +40,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.DataSpec
;
import
com.google.android.exoplayer2.upstream.DataSpec
;
import
com.google.android.exoplayer2.upstream.TransferListener
;
import
com.google.android.exoplayer2.upstream.TransferListener
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.TimestampAdjuster
;
import
com.google.android.exoplayer2.util.TimestampAdjuster
;
import
com.google.android.exoplayer2.util.UriUtil
;
import
com.google.android.exoplayer2.util.UriUtil
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
...
@@ -84,7 +84,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -84,7 +84,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
endOfStream
=
false
;
endOfStream
=
false
;
playlistUrl
=
null
;
playlistUrl
=
null
;
}
}
}
}
/**
/**
...
@@ -223,6 +222,44 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -223,6 +222,44 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
/**
/**
* Checks whether the previous media chunk is a preload chunk that has been removed in the current
* playlist.
*
* @param previous The previous media chunk.
* @return True if the previous media chunk has been removed in the current playlist.
*/
public
boolean
isMediaChunkRemoved
(
HlsMediaChunk
previous
)
{
if
(!
previous
.
isPreload
)
{
return
false
;
}
Uri
playlistUrl
=
playlistUrls
[
trackGroup
.
indexOf
(
previous
.
trackFormat
)];
HlsMediaPlaylist
mediaPlaylist
=
checkNotNull
(
playlistTracker
.
getPlaylistSnapshot
(
playlistUrl
,
/* isForPlayback= */
false
));
int
segmentIndexInPlaylist
=
(
int
)
(
previous
.
chunkIndex
-
mediaPlaylist
.
mediaSequence
);
if
(
segmentIndexInPlaylist
<
0
)
{
// The segment of the previous chunk is not in the current playlist anymore.
return
false
;
}
List
<
HlsMediaPlaylist
.
Part
>
partsInCurrentPlaylist
=
segmentIndexInPlaylist
<
mediaPlaylist
.
segments
.
size
()
?
mediaPlaylist
.
segments
.
get
(
segmentIndexInPlaylist
).
parts
:
mediaPlaylist
.
trailingParts
;
if
(
previous
.
partIndex
>=
partsInCurrentPlaylist
.
size
())
{
// In case the part hinted in the previous playlist has been wrongly assigned to the then full
// but not yet terminated segment, we discard it regardless whether the URI is different or
// not. While this is theoretically possible and unspecified, it appears to be an edge case
// which we can avoid with a small inefficiency of discarding in vain. We could allow this
// here but, if the chunk is not discarded, it could create unpredictable problems later,
// because the media sequence in previous.chunkIndex does not match to the actual media
// sequence in the new playlist.
return
true
;
}
HlsMediaPlaylist
.
Part
publishedPart
=
partsInCurrentPlaylist
.
get
(
previous
.
partIndex
);
Uri
publishedUri
=
Uri
.
parse
(
UriUtil
.
resolve
(
mediaPlaylist
.
baseUri
,
publishedPart
.
url
));
return
!
Util
.
areEqual
(
publishedUri
,
previous
.
dataSpec
.
uri
);
}
/**
* Returns the next chunk to load.
* Returns the next chunk to load.
*
*
* <p>If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream
* <p>If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream
...
@@ -270,7 +307,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -270,7 +307,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
trackSelection
.
updateSelectedTrack
(
trackSelection
.
updateSelectedTrack
(
playbackPositionUs
,
bufferedDurationUs
,
timeToLiveEdgeUs
,
queue
,
mediaChunkIterators
);
playbackPositionUs
,
bufferedDurationUs
,
timeToLiveEdgeUs
,
queue
,
mediaChunkIterators
);
int
selectedTrackIndex
=
trackSelection
.
getSelectedIndexInTrackGroup
();
int
selectedTrackIndex
=
trackSelection
.
getSelectedIndexInTrackGroup
();
boolean
switchingTrack
=
oldTrackIndex
!=
selectedTrackIndex
;
boolean
switchingTrack
=
oldTrackIndex
!=
selectedTrackIndex
;
Uri
selectedPlaylistUrl
=
playlistUrls
[
selectedTrackIndex
];
Uri
selectedPlaylistUrl
=
playlistUrls
[
selectedTrackIndex
];
if
(!
playlistTracker
.
isSnapshotValid
(
selectedPlaylistUrl
))
{
if
(!
playlistTracker
.
isSnapshotValid
(
selectedPlaylistUrl
))
{
...
@@ -284,7 +320,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -284,7 +320,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
HlsMediaPlaylist
mediaPlaylist
=
HlsMediaPlaylist
mediaPlaylist
=
playlistTracker
.
getPlaylistSnapshot
(
selectedPlaylistUrl
,
/* isForPlayback= */
true
);
playlistTracker
.
getPlaylistSnapshot
(
selectedPlaylistUrl
,
/* isForPlayback= */
true
);
// playlistTracker snapshot is valid (checked by if() above), so mediaPlaylist must be non-null.
// playlistTracker snapshot is valid (checked by if() above), so mediaPlaylist must be non-null.
Assertions
.
checkNotNull
(
mediaPlaylist
);
checkNotNull
(
mediaPlaylist
);
independentSegments
=
mediaPlaylist
.
hasIndependentSegments
;
independentSegments
=
mediaPlaylist
.
hasIndependentSegments
;
updateLiveEdgeTimeUs
(
mediaPlaylist
);
updateLiveEdgeTimeUs
(
mediaPlaylist
);
...
@@ -306,7 +342,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -306,7 +342,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
playlistTracker
.
getPlaylistSnapshot
(
selectedPlaylistUrl
,
/* isForPlayback= */
true
);
playlistTracker
.
getPlaylistSnapshot
(
selectedPlaylistUrl
,
/* isForPlayback= */
true
);
// playlistTracker snapshot is valid (checked by if() above), so mediaPlaylist must be
// playlistTracker snapshot is valid (checked by if() above), so mediaPlaylist must be
// non-null.
// non-null.
Assertions
.
checkNotNull
(
mediaPlaylist
);
checkNotNull
(
mediaPlaylist
);
startOfPlaylistInPeriodUs
=
startOfPlaylistInPeriodUs
=
mediaPlaylist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
mediaPlaylist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
// Get the next segment/part without switching tracks.
// Get the next segment/part without switching tracks.
...
@@ -366,7 +402,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -366,7 +402,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if
(
out
.
chunk
!=
null
)
{
if
(
out
.
chunk
!=
null
)
{
return
;
return
;
}
}
out
.
chunk
=
out
.
chunk
=
HlsMediaChunk
.
createInstance
(
HlsMediaChunk
.
createInstance
(
extractorFactory
,
extractorFactory
,
...
@@ -399,7 +434,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -399,7 +434,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Segment
mediaSegment
=
mediaPlaylist
.
segments
.
get
(
segmentIndexInPlaylist
);
Segment
mediaSegment
=
mediaPlaylist
.
segments
.
get
(
segmentIndexInPlaylist
);
if
(
nextPartIndex
==
C
.
INDEX_UNSET
)
{
if
(
nextPartIndex
==
C
.
INDEX_UNSET
)
{
return
new
SegmentBaseHolder
(
mediaSegment
,
nextMediaSequence
,
nextPartIndex
);
return
new
SegmentBaseHolder
(
mediaSegment
,
nextMediaSequence
,
/* partIndex= */
C
.
INDEX_UNSET
);
}
}
if
(
nextPartIndex
<
mediaSegment
.
parts
.
size
())
{
if
(
nextPartIndex
<
mediaSegment
.
parts
.
size
())
{
...
@@ -417,6 +452,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -417,6 +452,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return
new
SegmentBaseHolder
(
return
new
SegmentBaseHolder
(
mediaPlaylist
.
trailingParts
.
get
(
0
),
nextMediaSequence
+
1
,
/* partIndex= */
0
);
mediaPlaylist
.
trailingParts
.
get
(
0
),
nextMediaSequence
+
1
,
/* partIndex= */
0
);
}
}
// End of stream.
return
null
;
return
null
;
}
}
...
@@ -430,8 +466,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -430,8 +466,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if
(
chunk
instanceof
EncryptionKeyChunk
)
{
if
(
chunk
instanceof
EncryptionKeyChunk
)
{
EncryptionKeyChunk
encryptionKeyChunk
=
(
EncryptionKeyChunk
)
chunk
;
EncryptionKeyChunk
encryptionKeyChunk
=
(
EncryptionKeyChunk
)
chunk
;
scratchSpace
=
encryptionKeyChunk
.
getDataHolder
();
scratchSpace
=
encryptionKeyChunk
.
getDataHolder
();
keyCache
.
put
(
keyCache
.
put
(
encryptionKeyChunk
.
dataSpec
.
uri
,
checkNotNull
(
encryptionKeyChunk
.
getResult
()));
encryptionKeyChunk
.
dataSpec
.
uri
,
Assertions
.
checkNotNull
(
encryptionKeyChunk
.
getResult
()));
}
}
}
}
...
@@ -499,7 +534,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -499,7 +534,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
HlsMediaPlaylist
playlist
=
HlsMediaPlaylist
playlist
=
playlistTracker
.
getPlaylistSnapshot
(
playlistUrl
,
/* isForPlayback= */
false
);
playlistTracker
.
getPlaylistSnapshot
(
playlistUrl
,
/* isForPlayback= */
false
);
// Playlist snapshot is valid (checked by if() above) so playlist must be non-null.
// Playlist snapshot is valid (checked by if() above) so playlist must be non-null.
Assertions
.
checkNotNull
(
playlist
);
checkNotNull
(
playlist
);
long
startOfPlaylistInPeriodUs
=
long
startOfPlaylistInPeriodUs
=
playlist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
playlist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
boolean
switchingTrack
=
trackIndex
!=
oldTrackIndex
;
boolean
switchingTrack
=
trackIndex
!=
oldTrackIndex
;
...
@@ -704,6 +739,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -704,6 +739,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public
final
HlsMediaPlaylist
.
SegmentBase
segmentBase
;
public
final
HlsMediaPlaylist
.
SegmentBase
segmentBase
;
public
final
long
mediaSequence
;
public
final
long
mediaSequence
;
public
final
int
partIndex
;
public
final
int
partIndex
;
public
final
boolean
isPreload
;
/** Creates a new instance. */
/** Creates a new instance. */
public
SegmentBaseHolder
(
public
SegmentBaseHolder
(
...
@@ -711,6 +747,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -711,6 +747,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this
.
segmentBase
=
segmentBase
;
this
.
segmentBase
=
segmentBase
;
this
.
mediaSequence
=
mediaSequence
;
this
.
mediaSequence
=
mediaSequence
;
this
.
partIndex
=
partIndex
;
this
.
partIndex
=
partIndex
;
this
.
isPreload
=
segmentBase
instanceof
HlsMediaPlaylist
.
Part
&&
((
HlsMediaPlaylist
.
Part
)
segmentBase
).
isPreload
;
}
}
}
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java
View file @
39f8c775
...
@@ -169,6 +169,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
...
@@ -169,6 +169,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
segmentEndTimeInPeriodUs
,
segmentEndTimeInPeriodUs
,
segmentBaseHolder
.
mediaSequence
,
segmentBaseHolder
.
mediaSequence
,
segmentBaseHolder
.
partIndex
,
segmentBaseHolder
.
partIndex
,
segmentBaseHolder
.
isPreload
,
discontinuitySequenceNumber
,
discontinuitySequenceNumber
,
mediaSegment
.
hasGapTag
,
mediaSegment
.
hasGapTag
,
isMasterTimestampSource
,
isMasterTimestampSource
,
...
@@ -204,6 +205,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
...
@@ -204,6 +205,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** The part index or {@link C#INDEX_UNSET} if the chunk is a full segment */
/** The part index or {@link C#INDEX_UNSET} if the chunk is a full segment */
public
final
int
partIndex
;
public
final
int
partIndex
;
/** Whether this chunk is a preload chunk. */
public
final
boolean
isPreload
;
@Nullable
private
final
DataSource
initDataSource
;
@Nullable
private
final
DataSource
initDataSource
;
@Nullable
private
final
DataSpec
initDataSpec
;
@Nullable
private
final
DataSpec
initDataSpec
;
@Nullable
private
final
HlsMediaChunkExtractor
previousExtractor
;
@Nullable
private
final
HlsMediaChunkExtractor
previousExtractor
;
...
@@ -247,6 +251,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
...
@@ -247,6 +251,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
long
endTimeUs
,
long
endTimeUs
,
long
chunkMediaSequence
,
long
chunkMediaSequence
,
int
partIndex
,
int
partIndex
,
boolean
isPreload
,
int
discontinuitySequenceNumber
,
int
discontinuitySequenceNumber
,
boolean
hasGapTag
,
boolean
hasGapTag
,
boolean
isMasterTimestampSource
,
boolean
isMasterTimestampSource
,
...
@@ -267,6 +272,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
...
@@ -267,6 +272,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
chunkMediaSequence
);
chunkMediaSequence
);
this
.
mediaSegmentEncrypted
=
mediaSegmentEncrypted
;
this
.
mediaSegmentEncrypted
=
mediaSegmentEncrypted
;
this
.
partIndex
=
partIndex
;
this
.
partIndex
=
partIndex
;
this
.
isPreload
=
isPreload
;
this
.
discontinuitySequenceNumber
=
discontinuitySequenceNumber
;
this
.
discontinuitySequenceNumber
=
discontinuitySequenceNumber
;
this
.
initDataSpec
=
initDataSpec
;
this
.
initDataSpec
=
initDataSpec
;
this
.
initDataSource
=
initDataSource
;
this
.
initDataSource
=
initDataSource
;
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
View file @
39f8c775
...
@@ -445,6 +445,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
...
@@ -445,6 +445,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
@Override
@Override
public
void
onPlaylistChanged
()
{
public
void
onPlaylistChanged
()
{
for
(
HlsSampleStreamWrapper
streamWrapper
:
sampleStreamWrappers
)
{
streamWrapper
.
onPlaylistUpdated
();
}
callback
.
onContinueLoadingRequested
(
this
);
callback
.
onContinueLoadingRequested
(
this
);
}
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
View file @
39f8c775
...
@@ -504,6 +504,16 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
...
@@ -504,6 +504,16 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return
true
;
return
true
;
}
}
/** Called when the playlist is updated. */
public
void
onPlaylistUpdated
()
{
if
(!
loadingFinished
&&
loader
.
isLoading
()
&&
!
mediaChunks
.
isEmpty
()
&&
chunkSource
.
isMediaChunkRemoved
(
Iterables
.
getLast
(
mediaChunks
)))
{
loader
.
cancelLoading
();
}
}
public
void
release
()
{
public
void
release
()
{
if
(
prepared
)
{
if
(
prepared
)
{
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
...
@@ -672,8 +682,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
...
@@ -672,8 +682,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/* allowEndOfStream= */
prepared
||
!
chunkQueue
.
isEmpty
(),
/* allowEndOfStream= */
prepared
||
!
chunkQueue
.
isEmpty
(),
nextChunkHolder
);
nextChunkHolder
);
boolean
endOfStream
=
nextChunkHolder
.
endOfStream
;
boolean
endOfStream
=
nextChunkHolder
.
endOfStream
;
Chunk
loadable
=
nextChunkHolder
.
chunk
;
@Nullable
Chunk
loadable
=
nextChunkHolder
.
chunk
;
Uri
playlistUrlToLoad
=
nextChunkHolder
.
playlistUrl
;
@Nullable
Uri
playlistUrlToLoad
=
nextChunkHolder
.
playlistUrl
;
nextChunkHolder
.
clear
();
nextChunkHolder
.
clear
();
if
(
endOfStream
)
{
if
(
endOfStream
)
{
...
@@ -727,6 +737,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
...
@@ -727,6 +737,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return
;
return
;
}
}
if
(!
readOnlyMediaChunks
.
isEmpty
()
&&
chunkSource
.
isMediaChunkRemoved
(
Iterables
.
getLast
(
readOnlyMediaChunks
)))
{
discardUpstream
(
mediaChunks
.
size
()
-
1
);
}
int
preferredQueueSize
=
chunkSource
.
getPreferredQueueSize
(
positionUs
,
readOnlyMediaChunks
);
int
preferredQueueSize
=
chunkSource
.
getPreferredQueueSize
(
positionUs
,
readOnlyMediaChunks
);
if
(
preferredQueueSize
<
mediaChunks
.
size
())
{
if
(
preferredQueueSize
<
mediaChunks
.
size
())
{
discardUpstream
(
preferredQueueSize
);
discardUpstream
(
preferredQueueSize
);
...
...
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