Commit a671ebd0 by aquilescanta Committed by Oliver Woodman

Add live media playlist refresh requests when live edge is reached

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=131931868
parent 860c6588
...@@ -29,7 +29,6 @@ import com.google.android.exoplayer2.extractor.ts.TsExtractor; ...@@ -29,7 +29,6 @@ import com.google.android.exoplayer2.extractor.ts.TsExtractor;
import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.chunk.Chunk; import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil; import com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil;
import com.google.android.exoplayer2.source.chunk.DataChunk; import com.google.android.exoplayer2.source.chunk.DataChunk;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
...@@ -49,9 +48,44 @@ import java.util.Arrays; ...@@ -49,9 +48,44 @@ import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
/** /**
* A temporary test source of HLS chunks. * Source of Hls(possibly adaptive) chunks.
*/ */
public class HlsChunkSource { /* package */ class HlsChunkSource {
/**
* Chunk holder that allows the scheduling of retries.
*/
public static final class HlsChunkHolder {
public HlsChunkHolder() {
clear();
}
/**
* The chunk.
*/
public Chunk chunk;
/**
* Indicates that the end of the stream has been reached.
*/
public boolean endOfStream;
/**
* Milliseconds to wait before retrying.
*/
public long retryInMs;
/**
* Clears the holder.
*/
public void clear() {
chunk = null;
endOfStream = false;
retryInMs = C.TIME_UNSET;
}
}
/** /**
* The default time for which a media playlist should be blacklisted. * The default time for which a media playlist should be blacklisted.
...@@ -173,9 +207,10 @@ public class HlsChunkSource { ...@@ -173,9 +207,10 @@ public class HlsChunkSource {
/** /**
* Returns the next chunk to load. * Returns the next chunk to load.
* <p> * <p>
* If a chunk is available then {@link ChunkHolder#chunk} is set. If the end of the stream has * If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream has
* been reached then {@link ChunkHolder#endOfStream} is set. If a chunk is not available but the * been reached then {@link HlsChunkHolder#endOfStream} is set. If a chunk is not available but
* end of the stream has not been reached, the {@link ChunkHolder} is not modified. * the end of the stream has not been reached, {@link HlsChunkHolder#retryInMs} is set to contain
* the amount of milliseconds to wait before retrying.
* *
* @param previous The most recently loaded media chunk. * @param previous The most recently loaded media chunk.
* @param playbackPositionUs The current playback position. If {@code previous} is null then this * @param playbackPositionUs The current playback position. If {@code previous} is null then this
...@@ -183,7 +218,7 @@ public class HlsChunkSource { ...@@ -183,7 +218,7 @@ public class HlsChunkSource {
* should be interpreted as a seek position. * should be interpreted as a seek position.
* @param out A holder to populate. * @param out A holder to populate.
*/ */
public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, ChunkHolder out) { public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChunkHolder out) {
int oldVariantIndex = previous == null ? C.INDEX_UNSET int oldVariantIndex = previous == null ? C.INDEX_UNSET
: trackGroup.indexOf(previous.trackFormat); : trackGroup.indexOf(previous.trackFormat);
...@@ -206,8 +241,13 @@ public class HlsChunkSource { ...@@ -206,8 +241,13 @@ public class HlsChunkSource {
int chunkMediaSequence; int chunkMediaSequence;
if (live) { if (live) {
if (previous == null) { if (previous == null) {
chunkMediaSequence = Util.binarySearchFloor(mediaPlaylist.segments, playbackPositionUs, // When playling a live stream, the starting chunk will be the third counting from the live
true, true) + mediaPlaylist.mediaSequence; // edge.
chunkMediaSequence = Math.max(0, mediaPlaylist.segments.size() - 3)
+ mediaPlaylist.mediaSequence;
// TODO: Bring this back for live window seeking.
// chunkMediaSequence = Util.binarySearchFloor(mediaPlaylist.segments, playbackPositionUs,
// true, true) + mediaPlaylist.mediaSequence;
} else { } else {
chunkMediaSequence = getLiveNextChunkSequenceNumber(previous.chunkIndex, oldVariantIndex, chunkMediaSequence = getLiveNextChunkSequenceNumber(previous.chunkIndex, oldVariantIndex,
newVariantIndex); newVariantIndex);
...@@ -233,9 +273,16 @@ public class HlsChunkSource { ...@@ -233,9 +273,16 @@ public class HlsChunkSource {
if (chunkIndex >= mediaPlaylist.segments.size()) { if (chunkIndex >= mediaPlaylist.segments.size()) {
if (!mediaPlaylist.live) { if (!mediaPlaylist.live) {
out.endOfStream = true; out.endOfStream = true;
} else if (shouldRerequestLiveMediaPlaylist(newVariantIndex)) { } else /* Live */ {
out.chunk = newMediaPlaylistChunk(newVariantIndex, long msToRerequestLiveMediaPlaylist = msToRerequestLiveMediaPlaylist(newVariantIndex);
trackSelection.getSelectionReason(), trackSelection.getSelectionData()); if (msToRerequestLiveMediaPlaylist <= 0) {
out.chunk = newMediaPlaylistChunk(newVariantIndex,
trackSelection.getSelectionReason(), trackSelection.getSelectionData());
} else {
// 10 milliseconds are added to the wait to make sure the playlist is refreshed when
// getNextChunk() is called.
out.retryInMs = msToRerequestLiveMediaPlaylist + 10;
}
} }
return; return;
} }
...@@ -417,12 +464,12 @@ public class HlsChunkSource { ...@@ -417,12 +464,12 @@ public class HlsChunkSource {
// Private methods. // Private methods.
private boolean shouldRerequestLiveMediaPlaylist(int variantIndex) { private long msToRerequestLiveMediaPlaylist(int variantIndex) {
HlsMediaPlaylist mediaPlaylist = variantPlaylists[variantIndex]; HlsMediaPlaylist mediaPlaylist = variantPlaylists[variantIndex];
long timeSinceLastMediaPlaylistLoadMs = long timeSinceLastMediaPlaylistLoadMs =
SystemClock.elapsedRealtime() - variantLastPlaylistLoadTimesMs[variantIndex]; SystemClock.elapsedRealtime() - variantLastPlaylistLoadTimesMs[variantIndex];
// Don't re-request media playlist more often than one-half of the target duration. // Don't re-request media playlist more often than one-half of the target duration.
return timeSinceLastMediaPlaylistLoadMs >= (mediaPlaylist.targetDurationSecs * 1000) / 2; return (mediaPlaylist.targetDurationSecs * 1000) / 2 - timeSinceLastMediaPlaylistLoadMs;
} }
private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex, int trackSelectionReason, private MediaPlaylistChunk newMediaPlaylistChunk(int variantIndex, int trackSelectionReason,
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.hls; package com.google.android.exoplayer2.source.hls;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.text.TextUtils; import android.text.TextUtils;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -60,6 +61,7 @@ import java.util.List; ...@@ -60,6 +61,7 @@ import java.util.List;
private final IdentityHashMap<SampleStream, Integer> streamWrapperIndices; private final IdentityHashMap<SampleStream, Integer> streamWrapperIndices;
private final PtsTimestampAdjusterProvider timestampAdjusterProvider; private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
private final HlsPlaylistParser manifestParser; private final HlsPlaylistParser manifestParser;
private final Handler continueLoadingHandler;
private final Loader manifestFetcher; private final Loader manifestFetcher;
private final long preparePositionUs; private final long preparePositionUs;
...@@ -72,10 +74,11 @@ import java.util.List; ...@@ -72,10 +74,11 @@ import java.util.List;
private HlsSampleStreamWrapper[] sampleStreamWrappers; private HlsSampleStreamWrapper[] sampleStreamWrappers;
private HlsSampleStreamWrapper[] enabledSampleStreamWrappers; private HlsSampleStreamWrapper[] enabledSampleStreamWrappers;
private CompositeSequenceableLoader sequenceableLoader; private CompositeSequenceableLoader sequenceableLoader;
private Runnable continueLoadingRunnable;
public HlsMediaPeriod(Uri manifestUri, DataSource.Factory dataSourceFactory, public HlsMediaPeriod(Uri manifestUri, DataSource.Factory dataSourceFactory,
int minLoadableRetryCount, EventDispatcher eventDispatcher, int minLoadableRetryCount, EventDispatcher eventDispatcher,
MediaSource.Listener sourceListener, Callback callback, Allocator allocator, MediaSource.Listener sourceListener, final Callback callback, Allocator allocator,
long positionUs) { long positionUs) {
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
...@@ -86,8 +89,15 @@ import java.util.List; ...@@ -86,8 +89,15 @@ import java.util.List;
streamWrapperIndices = new IdentityHashMap<>(); streamWrapperIndices = new IdentityHashMap<>();
timestampAdjusterProvider = new PtsTimestampAdjusterProvider(); timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
manifestParser = new HlsPlaylistParser(); manifestParser = new HlsPlaylistParser();
continueLoadingHandler = new Handler();
manifestFetcher = new Loader("Loader:ManifestFetcher"); manifestFetcher = new Loader("Loader:ManifestFetcher");
preparePositionUs = positionUs; preparePositionUs = positionUs;
continueLoadingRunnable = new Runnable() {
@Override
public void run() {
callback.onContinueLoadingRequested(HlsMediaPeriod.this);
}
};
ParsingLoadable<HlsPlaylist> loadable = new ParsingLoadable<>( ParsingLoadable<HlsPlaylist> loadable = new ParsingLoadable<>(
dataSourceFactory.createDataSource(), manifestUri, C.DATA_TYPE_MANIFEST, manifestParser); dataSourceFactory.createDataSource(), manifestUri, C.DATA_TYPE_MANIFEST, manifestParser);
...@@ -96,6 +106,7 @@ import java.util.List; ...@@ -96,6 +106,7 @@ import java.util.List;
} }
public void release() { public void release() {
continueLoadingHandler.removeCallbacksAndMessages(null);
manifestFetcher.release(); manifestFetcher.release();
for (HlsSampleStreamWrapper sampleStreamWrapper : sampleStreamWrappers) { for (HlsSampleStreamWrapper sampleStreamWrapper : sampleStreamWrappers) {
sampleStreamWrapper.release(); sampleStreamWrapper.release();
...@@ -287,6 +298,12 @@ import java.util.List; ...@@ -287,6 +298,12 @@ import java.util.List;
} }
@Override @Override
public void onContinueLoadingRequiredInMs(final HlsSampleStreamWrapper sampleStreamWrapper,
long delayMs) {
continueLoadingHandler.postDelayed(continueLoadingRunnable, delayMs);
}
@Override
public void onContinueLoadingRequested(HlsSampleStreamWrapper sampleStreamWrapper) { public void onContinueLoadingRequested(HlsSampleStreamWrapper sampleStreamWrapper) {
if (trackGroups == null) { if (trackGroups == null) {
// Still preparing. // Still preparing.
......
...@@ -30,7 +30,6 @@ import com.google.android.exoplayer2.source.SequenceableLoader; ...@@ -30,7 +30,6 @@ import com.google.android.exoplayer2.source.SequenceableLoader;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.chunk.Chunk; import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
...@@ -56,6 +55,12 @@ import java.util.LinkedList; ...@@ -56,6 +55,12 @@ import java.util.LinkedList;
*/ */
void onPrepared(); void onPrepared();
/**
* Called to schedule a {@link #continueLoading(long)} call.
*/
void onContinueLoadingRequiredInMs(HlsSampleStreamWrapper sampleStreamSource,
long delayMs);
} }
private static final int PRIMARY_TYPE_NONE = 0; private static final int PRIMARY_TYPE_NONE = 0;
...@@ -72,7 +77,7 @@ import java.util.LinkedList; ...@@ -72,7 +77,7 @@ import java.util.LinkedList;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final Loader loader; private final Loader loader;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final ChunkHolder nextChunkHolder; private final HlsChunkSource.HlsChunkHolder nextChunkHolder;
private final SparseArray<DefaultTrackOutput> sampleQueues; private final SparseArray<DefaultTrackOutput> sampleQueues;
private final LinkedList<HlsMediaChunk> mediaChunks; private final LinkedList<HlsMediaChunk> mediaChunks;
...@@ -121,7 +126,7 @@ import java.util.LinkedList; ...@@ -121,7 +126,7 @@ import java.util.LinkedList;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
loader = new Loader("Loader:HlsSampleStreamWrapper"); loader = new Loader("Loader:HlsSampleStreamWrapper");
nextChunkHolder = new ChunkHolder(); nextChunkHolder = new HlsChunkSource.HlsChunkHolder();
sampleQueues = new SparseArray<>(); sampleQueues = new SparseArray<>();
mediaChunks = new LinkedList<>(); mediaChunks = new LinkedList<>();
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
...@@ -310,6 +315,7 @@ import java.util.LinkedList; ...@@ -310,6 +315,7 @@ import java.util.LinkedList;
nextChunkHolder); nextChunkHolder);
boolean endOfStream = nextChunkHolder.endOfStream; boolean endOfStream = nextChunkHolder.endOfStream;
Chunk loadable = nextChunkHolder.chunk; Chunk loadable = nextChunkHolder.chunk;
long retryInMs = nextChunkHolder.retryInMs;
nextChunkHolder.clear(); nextChunkHolder.clear();
if (endOfStream) { if (endOfStream) {
...@@ -318,6 +324,8 @@ import java.util.LinkedList; ...@@ -318,6 +324,8 @@ import java.util.LinkedList;
} }
if (loadable == null) { if (loadable == null) {
Assertions.checkState(retryInMs != C.TIME_UNSET && chunkSource.isLive());
callback.onContinueLoadingRequiredInMs(this, retryInMs);
return false; return false;
} }
......
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