mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Add live media playlist refresh requests when live edge is reached
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=131931868
This commit is contained in:
parent
860c6588c0
commit
a671ebd019
3 changed files with 90 additions and 18 deletions
|
|
@ -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.TrackGroup;
|
||||
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.DataChunk;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
|
||||
|
|
@ -49,9 +48,44 @@ import java.util.Arrays;
|
|||
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.
|
||||
|
|
@ -173,9 +207,10 @@ public class HlsChunkSource {
|
|||
/**
|
||||
* Returns the next chunk to load.
|
||||
* <p>
|
||||
* If a chunk is available then {@link ChunkHolder#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
|
||||
* end of the stream has not been reached, the {@link ChunkHolder} is not modified.
|
||||
* If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream has
|
||||
* been reached then {@link HlsChunkHolder#endOfStream} is set. If a chunk is not available but
|
||||
* 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 playbackPositionUs The current playback position. If {@code previous} is null then this
|
||||
|
|
@ -183,7 +218,7 @@ public class HlsChunkSource {
|
|||
* should be interpreted as a seek position.
|
||||
* @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
|
||||
: trackGroup.indexOf(previous.trackFormat);
|
||||
|
||||
|
|
@ -206,8 +241,13 @@ public class HlsChunkSource {
|
|||
int chunkMediaSequence;
|
||||
if (live) {
|
||||
if (previous == null) {
|
||||
chunkMediaSequence = Util.binarySearchFloor(mediaPlaylist.segments, playbackPositionUs,
|
||||
true, true) + mediaPlaylist.mediaSequence;
|
||||
// When playling a live stream, the starting chunk will be the third counting from the live
|
||||
// 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 {
|
||||
chunkMediaSequence = getLiveNextChunkSequenceNumber(previous.chunkIndex, oldVariantIndex,
|
||||
newVariantIndex);
|
||||
|
|
@ -233,9 +273,16 @@ public class HlsChunkSource {
|
|||
if (chunkIndex >= mediaPlaylist.segments.size()) {
|
||||
if (!mediaPlaylist.live) {
|
||||
out.endOfStream = true;
|
||||
} else if (shouldRerequestLiveMediaPlaylist(newVariantIndex)) {
|
||||
out.chunk = newMediaPlaylistChunk(newVariantIndex,
|
||||
trackSelection.getSelectionReason(), trackSelection.getSelectionData());
|
||||
} else /* Live */ {
|
||||
long msToRerequestLiveMediaPlaylist = msToRerequestLiveMediaPlaylist(newVariantIndex);
|
||||
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;
|
||||
}
|
||||
|
|
@ -417,12 +464,12 @@ public class HlsChunkSource {
|
|||
|
||||
// Private methods.
|
||||
|
||||
private boolean shouldRerequestLiveMediaPlaylist(int variantIndex) {
|
||||
private long msToRerequestLiveMediaPlaylist(int variantIndex) {
|
||||
HlsMediaPlaylist mediaPlaylist = variantPlaylists[variantIndex];
|
||||
long timeSinceLastMediaPlaylistLoadMs =
|
||||
SystemClock.elapsedRealtime() - variantLastPlaylistLoadTimesMs[variantIndex];
|
||||
// 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,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer2.source.hls;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
|
|
@ -60,6 +61,7 @@ import java.util.List;
|
|||
private final IdentityHashMap<SampleStream, Integer> streamWrapperIndices;
|
||||
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
|
||||
private final HlsPlaylistParser manifestParser;
|
||||
private final Handler continueLoadingHandler;
|
||||
private final Loader manifestFetcher;
|
||||
private final long preparePositionUs;
|
||||
|
||||
|
|
@ -72,10 +74,11 @@ import java.util.List;
|
|||
private HlsSampleStreamWrapper[] sampleStreamWrappers;
|
||||
private HlsSampleStreamWrapper[] enabledSampleStreamWrappers;
|
||||
private CompositeSequenceableLoader sequenceableLoader;
|
||||
private Runnable continueLoadingRunnable;
|
||||
|
||||
public HlsMediaPeriod(Uri manifestUri, DataSource.Factory dataSourceFactory,
|
||||
int minLoadableRetryCount, EventDispatcher eventDispatcher,
|
||||
MediaSource.Listener sourceListener, Callback callback, Allocator allocator,
|
||||
MediaSource.Listener sourceListener, final Callback callback, Allocator allocator,
|
||||
long positionUs) {
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
|
|
@ -86,8 +89,15 @@ import java.util.List;
|
|||
streamWrapperIndices = new IdentityHashMap<>();
|
||||
timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
|
||||
manifestParser = new HlsPlaylistParser();
|
||||
continueLoadingHandler = new Handler();
|
||||
manifestFetcher = new Loader("Loader:ManifestFetcher");
|
||||
preparePositionUs = positionUs;
|
||||
continueLoadingRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onContinueLoadingRequested(HlsMediaPeriod.this);
|
||||
}
|
||||
};
|
||||
|
||||
ParsingLoadable<HlsPlaylist> loadable = new ParsingLoadable<>(
|
||||
dataSourceFactory.createDataSource(), manifestUri, C.DATA_TYPE_MANIFEST, manifestParser);
|
||||
|
|
@ -96,6 +106,7 @@ import java.util.List;
|
|||
}
|
||||
|
||||
public void release() {
|
||||
continueLoadingHandler.removeCallbacksAndMessages(null);
|
||||
manifestFetcher.release();
|
||||
for (HlsSampleStreamWrapper sampleStreamWrapper : sampleStreamWrappers) {
|
||||
sampleStreamWrapper.release();
|
||||
|
|
@ -286,6 +297,12 @@ import java.util.List;
|
|||
sourceListener.onSourceInfoRefreshed(timeline, playlist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContinueLoadingRequiredInMs(final HlsSampleStreamWrapper sampleStreamWrapper,
|
||||
long delayMs) {
|
||||
continueLoadingHandler.postDelayed(continueLoadingRunnable, delayMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContinueLoadingRequested(HlsSampleStreamWrapper sampleStreamWrapper) {
|
||||
if (trackGroups == null) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import com.google.android.exoplayer2.source.SequenceableLoader;
|
|||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
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.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.Loader;
|
||||
|
|
@ -56,6 +55,12 @@ import java.util.LinkedList;
|
|||
*/
|
||||
void onPrepared();
|
||||
|
||||
/**
|
||||
* Called to schedule a {@link #continueLoading(long)} call.
|
||||
*/
|
||||
void onContinueLoadingRequiredInMs(HlsSampleStreamWrapper sampleStreamSource,
|
||||
long delayMs);
|
||||
|
||||
}
|
||||
|
||||
private static final int PRIMARY_TYPE_NONE = 0;
|
||||
|
|
@ -72,7 +77,7 @@ import java.util.LinkedList;
|
|||
private final int minLoadableRetryCount;
|
||||
private final Loader loader;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final ChunkHolder nextChunkHolder;
|
||||
private final HlsChunkSource.HlsChunkHolder nextChunkHolder;
|
||||
private final SparseArray<DefaultTrackOutput> sampleQueues;
|
||||
private final LinkedList<HlsMediaChunk> mediaChunks;
|
||||
|
||||
|
|
@ -121,7 +126,7 @@ import java.util.LinkedList;
|
|||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
loader = new Loader("Loader:HlsSampleStreamWrapper");
|
||||
nextChunkHolder = new ChunkHolder();
|
||||
nextChunkHolder = new HlsChunkSource.HlsChunkHolder();
|
||||
sampleQueues = new SparseArray<>();
|
||||
mediaChunks = new LinkedList<>();
|
||||
lastSeekPositionUs = positionUs;
|
||||
|
|
@ -310,6 +315,7 @@ import java.util.LinkedList;
|
|||
nextChunkHolder);
|
||||
boolean endOfStream = nextChunkHolder.endOfStream;
|
||||
Chunk loadable = nextChunkHolder.chunk;
|
||||
long retryInMs = nextChunkHolder.retryInMs;
|
||||
nextChunkHolder.clear();
|
||||
|
||||
if (endOfStream) {
|
||||
|
|
@ -318,6 +324,8 @@ import java.util.LinkedList;
|
|||
}
|
||||
|
||||
if (loadable == null) {
|
||||
Assertions.checkState(retryInMs != C.TIME_UNSET && chunkSource.isLive());
|
||||
callback.onContinueLoadingRequiredInMs(this, retryInMs);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue