Skip to content

Commit

Permalink
Gracefully handle chunkful preparation without chunks.
Browse files Browse the repository at this point in the history
This situation happens if the first chunk to load is already behind the end
of the stream. In this case, the preparation never completes because
HlsSampleStreamWrapper gets stuck in a prepared=false and loadingFinished=true
state.

Gracefully handle this situation by attempting to load the last chunk if still
unprepared to ensure that track information is obtained as far as possible.
Otherwise, it wouldn't be possible to play anything even when seeking back.

Issue:#6314
PiperOrigin-RevId: 264599465
  • Loading branch information
tonihei committed Aug 23, 2019
1 parent f0aae7a commit 6a1331f
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 5 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
([#5407](https://github.com/google/ExoPlayer/issues/5407)).
* Deprecate `setTag` parameter of `Timeline.getWindow`. Tags will always be set.
* Support out-of-band HDR10+ metadata for VP9 in WebM/Matroska.
* Fix issue where HLS streams get stuck in infinite buffering state after
postroll ad ([#6314](https://github.com/google/ExoPlayer/issues/6314)).

### 2.10.4 ###

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,17 @@ public void setIsTimestampMaster(boolean isTimestampMaster) {
* media in previous periods still to be played.
* @param loadPositionUs The current load position relative to the period start in microseconds.
* @param queue The queue of buffered {@link HlsMediaChunk}s.
* @param allowEndOfStream Whether {@link HlsChunkHolder#endOfStream} is allowed to be set for
* non-empty media playlists. If {@code false}, the last available chunk is returned instead.
* If the media playlist is empty, {@link HlsChunkHolder#endOfStream} is always set.
* @param out A holder to populate.
*/
public void getNextChunk(
long playbackPositionUs, long loadPositionUs, List<HlsMediaChunk> queue, HlsChunkHolder out) {
long playbackPositionUs,
long loadPositionUs,
List<HlsMediaChunk> queue,
boolean allowEndOfStream,
HlsChunkHolder out) {
HlsMediaChunk previous = queue.isEmpty() ? null : queue.get(queue.size() - 1);
int oldTrackIndex = previous == null ? C.INDEX_UNSET : trackGroup.indexOf(previous.trackFormat);
long bufferedDurationUs = loadPositionUs - playbackPositionUs;
Expand Down Expand Up @@ -292,15 +299,20 @@ public void getNextChunk(
}

int segmentIndexInPlaylist = (int) (chunkMediaSequence - mediaPlaylist.mediaSequence);
if (segmentIndexInPlaylist >= mediaPlaylist.segments.size()) {
int availableSegmentCount = mediaPlaylist.segments.size();
if (segmentIndexInPlaylist >= availableSegmentCount) {
if (mediaPlaylist.hasEndTag) {
out.endOfStream = true;
if (allowEndOfStream || availableSegmentCount == 0) {
out.endOfStream = true;
return;
}
segmentIndexInPlaylist = availableSegmentCount - 1;
} else /* Live */ {
out.playlistUrl = selectedPlaylistUrl;
seenExpectedPlaylistError &= selectedPlaylistUrl.equals(expectedPlaylistUrl);
expectedPlaylistUrl = selectedPlaylistUrl;
return;
}
return;
}
// We have a valid playlist snapshot, we can discard any playlist errors at this point.
seenExpectedPlaylistError = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmSession;
Expand Down Expand Up @@ -232,6 +233,9 @@ public void prepareWithMasterPlaylistInfo(

public void maybeThrowPrepareError() throws IOException {
maybeThrowError();
if (loadingFinished && !prepared) {
throw new ParserException("Loading finished before preparation is complete.");
}
}

public TrackGroupArray getTrackGroups() {
Expand Down Expand Up @@ -608,7 +612,12 @@ public boolean continueLoading(long positionUs) {
? lastMediaChunk.endTimeUs
: Math.max(lastSeekPositionUs, lastMediaChunk.startTimeUs);
}
chunkSource.getNextChunk(positionUs, loadPositionUs, chunkQueue, nextChunkHolder);
chunkSource.getNextChunk(
positionUs,
loadPositionUs,
chunkQueue,
/* allowEndOfStream= */ prepared || !chunkQueue.isEmpty(),
nextChunkHolder);
boolean endOfStream = nextChunkHolder.endOfStream;
Chunk loadable = nextChunkHolder.chunk;
Uri playlistUrlToLoad = nextChunkHolder.playlistUrl;
Expand Down

0 comments on commit 6a1331f

Please sign in to comment.