Commit 4a29be49 by Oliver Woodman

Correctly use "current-manifest-live" v.s. "stream-is-live"

When a live stream ends, what typically happens is that the manifest
is refreshed and the refreshed version is not marked as live/dynamic.
When this happens we:

1. Don't want the duration of the track to change.
2. Still want to consider the possibility that we may have fallen behind
   the live window.
3. Don't want to allow futher manifest refreshes.

This change uses the right thing in the right place.
parent 143a4dee
......@@ -119,6 +119,7 @@ public class DashChunkSource implements ChunkSource, Output {
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 @@ public class DashChunkSource implements ChunkSource, Output {
this.evaluation = new Evaluation();
this.availableRangeValues = new long[2];
periodHolders = new SparseArray<>();
tracks = new ArrayList<ExposedTrack>();
tracks = new ArrayList<>();
live = initialManifest.dynamic;
}
// ChunkSource implementation.
......@@ -375,7 +377,7 @@ public class DashChunkSource implements ChunkSource, Output {
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 class DashChunkSource implements ChunkSource, Output {
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 class DashChunkSource implements ChunkSource, Output {
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)");
......
......@@ -221,7 +221,7 @@ public class HlsChunkSource {
}
public long getDurationUs() {
return live ? C.UNKNOWN_TIME_US : durationUs;
return durationUs;
}
/**
......@@ -551,7 +551,7 @@ public class HlsChunkSource {
variantLastPlaylistLoadTimesMs[variantIndex] = SystemClock.elapsedRealtime();
variantPlaylists[variantIndex] = mediaPlaylist;
live |= mediaPlaylist.live;
durationUs = mediaPlaylist.durationUs;
durationUs = live ? C.UNKNOWN_TIME_US : mediaPlaylist.durationUs;
}
/**
......
......@@ -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<SmoothStreamingManifest> manifestFetcher;
private final DrmInitData.Mapped drmInitData;
private final FormatEvaluator adaptiveFormatEvaluator;
private final boolean live;
// The tracks exposed by this source.
private final ArrayList<ExposedTrack> 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 @@ public class SmoothStreamingChunkSource implements ChunkSource,
tracks = new ArrayList<>();
extractorWrappers = new SparseArray<>();
mediaFormats = new SparseArray<>();
live = initialManifest.isLive;
ProtectionElement protectionElement = initialManifest.protectionElement;
if (protectionElement != null) {
......@@ -221,10 +224,10 @@ public class SmoothStreamingChunkSource implements ChunkSource,
}
}
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 class SmoothStreamingChunkSource implements ChunkSource,
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 class SmoothStreamingChunkSource implements ChunkSource,
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 @@ public class SmoothStreamingChunkSource implements ChunkSource,
}
// 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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment