mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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.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;
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
* 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
// 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 @@
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
@ -286,6 +297,12 @@ import java.util.List;
|
||||||
sourceListener.onSourceInfoRefreshed(timeline, playlist);
|
sourceListener.onSourceInfoRefreshed(timeline, playlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContinueLoadingRequiredInMs(final HlsSampleStreamWrapper sampleStreamWrapper,
|
||||||
|
long delayMs) {
|
||||||
|
continueLoadingHandler.postDelayed(continueLoadingRunnable, delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContinueLoadingRequested(HlsSampleStreamWrapper sampleStreamWrapper) {
|
public void onContinueLoadingRequested(HlsSampleStreamWrapper sampleStreamWrapper) {
|
||||||
if (trackGroups == null) {
|
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.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;
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadable == null) {
|
if (loadable == null) {
|
||||||
|
Assertions.checkState(retryInMs != C.TIME_UNSET && chunkSource.isLive());
|
||||||
|
callback.onContinueLoadingRequiredInMs(this, retryInMs);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue