diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index 48bc652f460..4cb8faef318 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -119,6 +119,7 @@ public NoAdaptationSetException(String message) { private final long liveEdgeLatencyUs; private final long elapsedRealtimeOffsetUs; private final long[] availableRangeValues; + private final boolean live; private MediaPresentationDescription currentManifest; private ExposedTrack enabledTrack; @@ -260,7 +261,8 @@ dataSource, adaptiveFormatEvaluator, new SystemClock(), liveEdgeLatencyMs * 1000 this.evaluation = new Evaluation(); this.availableRangeValues = new long[2]; periodHolders = new SparseArray<>(); - tracks = new ArrayList(); + tracks = new ArrayList<>(); + live = initialManifest.dynamic; } // ChunkSource implementation. @@ -375,7 +377,7 @@ public final void getChunkOperation(List queue, long seekP availableRange.getCurrentBoundsUs(availableRangeValues); if (queue.isEmpty()) { - if (currentManifest.dynamic) { + if (live) { if (startAtLiveEdge) { // We want live streams to start at the live edge instead of the beginning of the // manifest @@ -403,18 +405,16 @@ public final void getChunkOperation(List queue, long seekP return; } - if (currentManifest.dynamic) { - long nextSegmentStartTimeUs = previous.endTimeUs; - if (nextSegmentStartTimeUs < availableRangeValues[0]) { - // This is before the first chunk in the current manifest. - fatalError = new BehindLiveWindowException(); - return; - } else if (nextSegmentStartTimeUs >= availableRangeValues[1]) { - // This chunk is beyond the last chunk in the current manifest. If the index is bounded - // 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; - } + long nextSegmentStartTimeUs = previous.endTimeUs; + if (live && nextSegmentStartTimeUs < availableRangeValues[0]) { + // This is before the first chunk in the current manifest. + fatalError = new BehindLiveWindowException(); + return; + } else if (currentManifest.dynamic && nextSegmentStartTimeUs >= availableRangeValues[1]) { + // This chunk is beyond the last chunk in the current manifest. If the index is bounded + // 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; } startingNewPeriod = false; @@ -545,7 +545,7 @@ public void adaptiveTrack(MediaPresentationDescription manifest, int periodIndex representationFormats[i] = format; } Arrays.sort(representationFormats, new DecreasingBandwidthComparator()); - long trackDurationUs = manifest.dynamic ? C.UNKNOWN_TIME_US : manifest.duration * 1000; + long trackDurationUs = live ? C.UNKNOWN_TIME_US : manifest.duration * 1000; String mediaMimeType = getMediaMimeType(maxHeightRepresentationFormat); if (mediaMimeType == null) { Log.w(TAG, "Skipped adaptive track (unknown media mime type)"); diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java index 23659a5bafb..c12a1044de5 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java @@ -221,7 +221,7 @@ public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist pla } public long getDurationUs() { - return live ? C.UNKNOWN_TIME_US : durationUs; + return durationUs; } /** @@ -551,7 +551,7 @@ private void setMediaPlaylist(int variantIndex, HlsMediaPlaylist mediaPlaylist) variantLastPlaylistLoadTimesMs[variantIndex] = SystemClock.elapsedRealtime(); variantPlaylists[variantIndex] = mediaPlaylist; live |= mediaPlaylist.live; - durationUs = mediaPlaylist.durationUs; + durationUs = live ? C.UNKNOWN_TIME_US : mediaPlaylist.durationUs; } /** diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java index 8c06486579c..26895118d9b 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer.smoothstreaming; import com.google.android.exoplayer.BehindLiveWindowException; +import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.chunk.Chunk; import com.google.android.exoplayer.chunk.ChunkExtractorWrapper; @@ -69,6 +70,7 @@ public class SmoothStreamingChunkSource implements ChunkSource, private final ManifestFetcher manifestFetcher; private final DrmInitData.Mapped drmInitData; private final FormatEvaluator adaptiveFormatEvaluator; + private final boolean live; // The tracks exposed by this source. private final ArrayList tracks; @@ -80,7 +82,7 @@ public class SmoothStreamingChunkSource implements ChunkSource, private boolean prepareCalled; private SmoothStreamingManifest currentManifest; private int currentManifestChunkOffset; - private boolean currentManifestFinished; + private boolean needManifestRefresh; private ExposedTrack enabledTrack; private IOException fatalError; @@ -135,6 +137,7 @@ private SmoothStreamingChunkSource(ManifestFetcher mani tracks = new ArrayList<>(); extractorWrappers = new SparseArray<>(); mediaFormats = new SparseArray<>(); + live = initialManifest.isLive; ProtectionElement protectionElement = initialManifest.protectionElement; if (protectionElement != null) { @@ -221,10 +224,10 @@ public void continueBuffering(long playbackPositionUs) { } } currentManifest = newManifest; - currentManifestFinished = false; + needManifestRefresh = false; } - if (currentManifestFinished && (SystemClock.elapsedRealtime() + if (needManifestRefresh && (SystemClock.elapsedRealtime() > manifestFetcher.getManifestLoadStartTimestamp() + MINIMUM_MANIFEST_REFRESH_PERIOD_MS)) { manifestFetcher.requestRefresh(); } @@ -265,14 +268,15 @@ public final void getChunkOperation(List queue, long seekP StreamElement streamElement = currentManifest.streamElements[enabledTrack.elementIndex]; if (streamElement.chunkCount == 0) { - // The manifest is currently empty for this stream. - currentManifestFinished = true; + if (currentManifest.isLive) { + needManifestRefresh = true; + } return; } int chunkIndex; if (queue.isEmpty()) { - if (currentManifest.isLive) { + if (live) { seekPositionUs = getLiveSeekPosition(currentManifest, liveEdgeLatencyUs); } chunkIndex = streamElement.getChunkIndex(seekPositionUs); @@ -281,19 +285,19 @@ public final void getChunkOperation(List queue, long seekP chunkIndex = previous.isLastChunk ? -1 : previous.chunkIndex + 1 - currentManifestChunkOffset; } - if (currentManifest.isLive) { - if (chunkIndex < 0) { - // This is before the first chunk in the current manifest. - fatalError = new BehindLiveWindowException(); - return; - } else if (chunkIndex >= streamElement.chunkCount) { + if (live && chunkIndex < 0) { + // This is before the first chunk in the current manifest. + fatalError = new BehindLiveWindowException(); + return; + } else if (currentManifest.isLive) { + if (chunkIndex >= streamElement.chunkCount) { // This is beyond the last chunk in the current manifest. - currentManifestFinished = true; + needManifestRefresh = true; return; } else if (chunkIndex == streamElement.chunkCount - 1) { // This is the last chunk in the current manifest. Mark the manifest as being finished, // but continue to return the final chunk. - currentManifestFinished = true; + needManifestRefresh = true; } } @@ -388,7 +392,7 @@ private MediaFormat initManifestTrack(SmoothStreamingManifest manifest, int elem } // Build the media format. - long durationUs = manifest.durationUs; + long durationUs = live ? C.UNKNOWN_TIME_US : manifest.durationUs; StreamElement element = manifest.streamElements[elementIndex]; Format format = element.tracks[trackIndex].format; byte[][] csdArray = element.tracks[trackIndex].csd;