mirror of
https://github.com/androidx/media.git
synced 2025-05-03 21:57:46 +08:00
Defer retries for progressive live audio streams
Issue: #1606 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=183058160
This commit is contained in:
parent
5dff21e5de
commit
a1274591b1
@ -38,6 +38,8 @@
|
|||||||
sub-streams, by allowing injection of custom `CompositeSequenceableLoader`
|
sub-streams, by allowing injection of custom `CompositeSequenceableLoader`
|
||||||
factories through `DashMediaSource.Factory`, `HlsMediaSource.Factory`,
|
factories through `DashMediaSource.Factory`, `HlsMediaSource.Factory`,
|
||||||
`SsMediaSource.Factory`, and `MergingMediaSource`.
|
`SsMediaSource.Factory`, and `MergingMediaSource`.
|
||||||
|
* Play out existing buffer before retrying for progressive live streams
|
||||||
|
([#1606](https://github.com/google/ExoPlayer/issues/1606)).
|
||||||
* Add `ExoPlayer.setSeekParameters` for controlling how seek operations are
|
* Add `ExoPlayer.setSeekParameters` for controlling how seek operations are
|
||||||
performed. The `SeekParameters` class contains defaults for exact seeking and
|
performed. The `SeekParameters` class contains defaults for exact seeking and
|
||||||
seeking to the closest sync points before, either side or after specified seek
|
seeking to the closest sync points before, either side or after specified seek
|
||||||
|
@ -111,6 +111,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
private long lastSeekPositionUs;
|
private long lastSeekPositionUs;
|
||||||
private long pendingResetPositionUs;
|
private long pendingResetPositionUs;
|
||||||
|
private boolean pendingDeferredRetry;
|
||||||
|
|
||||||
private int extractedSamplesCountAtStartOfLoad;
|
private int extractedSamplesCountAtStartOfLoad;
|
||||||
private boolean loadingFinished;
|
private boolean loadingFinished;
|
||||||
@ -259,6 +260,7 @@ import java.util.Arrays;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (enabledTrackCount == 0) {
|
if (enabledTrackCount == 0) {
|
||||||
|
pendingDeferredRetry = false;
|
||||||
notifyDiscontinuity = false;
|
notifyDiscontinuity = false;
|
||||||
if (loader.isLoading()) {
|
if (loader.isLoading()) {
|
||||||
// Discard as much as we can synchronously.
|
// Discard as much as we can synchronously.
|
||||||
@ -299,7 +301,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean continueLoading(long playbackPositionUs) {
|
public boolean continueLoading(long playbackPositionUs) {
|
||||||
if (loadingFinished || (prepared && enabledTrackCount == 0)) {
|
if (loadingFinished || pendingDeferredRetry || (prepared && enabledTrackCount == 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean continuedLoading = loadCondition.open();
|
boolean continuedLoading = loadCondition.open();
|
||||||
@ -361,6 +363,7 @@ import java.util.Arrays;
|
|||||||
return positionUs;
|
return positionUs;
|
||||||
}
|
}
|
||||||
// We were unable to seek within the buffer, so need to reset.
|
// We were unable to seek within the buffer, so need to reset.
|
||||||
|
pendingDeferredRetry = false;
|
||||||
pendingResetPositionUs = positionUs;
|
pendingResetPositionUs = positionUs;
|
||||||
loadingFinished = false;
|
loadingFinished = false;
|
||||||
if (loader.isLoading()) {
|
if (loader.isLoading()) {
|
||||||
@ -404,6 +407,8 @@ import java.util.Arrays;
|
|||||||
formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs);
|
formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs);
|
||||||
if (result == C.RESULT_BUFFER_READ) {
|
if (result == C.RESULT_BUFFER_READ) {
|
||||||
maybeNotifyTrackFormat(track);
|
maybeNotifyTrackFormat(track);
|
||||||
|
} else if (result == C.RESULT_NOTHING_READ) {
|
||||||
|
maybeStartDeferredRetry(track);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -424,6 +429,8 @@ import java.util.Arrays;
|
|||||||
}
|
}
|
||||||
if (skipCount > 0) {
|
if (skipCount > 0) {
|
||||||
maybeNotifyTrackFormat(track);
|
maybeNotifyTrackFormat(track);
|
||||||
|
} else {
|
||||||
|
maybeStartDeferredRetry(track);
|
||||||
}
|
}
|
||||||
return skipCount;
|
return skipCount;
|
||||||
}
|
}
|
||||||
@ -441,6 +448,23 @@ import java.util.Arrays;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maybeStartDeferredRetry(int track) {
|
||||||
|
if (!pendingDeferredRetry
|
||||||
|
|| !trackIsAudioVideoFlags[track]
|
||||||
|
|| sampleQueues[track].hasNextSample()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingResetPositionUs = 0;
|
||||||
|
pendingDeferredRetry = false;
|
||||||
|
notifyDiscontinuity = true;
|
||||||
|
lastSeekPositionUs = 0;
|
||||||
|
extractedSamplesCountAtStartOfLoad = 0;
|
||||||
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
|
sampleQueue.reset();
|
||||||
|
}
|
||||||
|
callback.onContinueLoadingRequested(this);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean suppressRead() {
|
private boolean suppressRead() {
|
||||||
return notifyDiscontinuity || isPendingReset();
|
return notifyDiscontinuity || isPendingReset();
|
||||||
}
|
}
|
||||||
@ -523,9 +547,9 @@ import java.util.Arrays;
|
|||||||
}
|
}
|
||||||
int extractedSamplesCount = getExtractedSamplesCount();
|
int extractedSamplesCount = getExtractedSamplesCount();
|
||||||
boolean madeProgress = extractedSamplesCount > extractedSamplesCountAtStartOfLoad;
|
boolean madeProgress = extractedSamplesCount > extractedSamplesCountAtStartOfLoad;
|
||||||
configureRetry(loadable); // May reset the sample queues.
|
return configureRetry(loadable, extractedSamplesCount)
|
||||||
extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount();
|
? (madeProgress ? Loader.RETRY_RESET_ERROR_COUNT : Loader.RETRY)
|
||||||
return madeProgress ? Loader.RETRY_RESET_ERROR_COUNT : Loader.RETRY;
|
: Loader.DONT_RETRY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractorOutput implementation. Called by the loading thread.
|
// ExtractorOutput implementation. Called by the loading thread.
|
||||||
@ -636,23 +660,47 @@ import java.util.Arrays;
|
|||||||
elapsedRealtimeMs);
|
elapsedRealtimeMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureRetry(ExtractingLoadable loadable) {
|
/**
|
||||||
|
* Called to configure a retry when a load error occurs.
|
||||||
|
*
|
||||||
|
* @param loadable The current loadable for which the error was encountered.
|
||||||
|
* @param currentExtractedSampleCount The current number of samples that have been extracted into
|
||||||
|
* the sample queues.
|
||||||
|
* @return Whether the loader should retry with the current loadable. False indicates a deferred
|
||||||
|
* retry.
|
||||||
|
*/
|
||||||
|
private boolean configureRetry(ExtractingLoadable loadable, int currentExtractedSampleCount) {
|
||||||
if (length != C.LENGTH_UNSET
|
if (length != C.LENGTH_UNSET
|
||||||
|| (seekMap != null && seekMap.getDurationUs() != C.TIME_UNSET)) {
|
|| (seekMap != null && seekMap.getDurationUs() != C.TIME_UNSET)) {
|
||||||
// We're playing an on-demand stream. Resume the current loadable, which will
|
// We're playing an on-demand stream. Resume the current loadable, which will
|
||||||
// request data starting from the point it left off.
|
// request data starting from the point it left off.
|
||||||
|
extractedSamplesCountAtStartOfLoad = currentExtractedSampleCount;
|
||||||
|
return true;
|
||||||
|
} else if (prepared && !suppressRead()) {
|
||||||
|
// We're playing a stream of unknown length and duration. Assume it's live, and therefore that
|
||||||
|
// the data at the uri is a continuously shifting window of the latest available media. For
|
||||||
|
// this case there's no way to continue loading from where a previous load finished, so it's
|
||||||
|
// necessary to load from the start whenever commencing a new load. Deferring the retry until
|
||||||
|
// we run out of buffered data makes for a much better user experience. See:
|
||||||
|
// https://github.com/google/ExoPlayer/issues/1606.
|
||||||
|
// Note that the suppressRead() check means only a single deferred retry can occur without
|
||||||
|
// progress being made. Any subsequent failures without progress will go through the else
|
||||||
|
// block below.
|
||||||
|
pendingDeferredRetry = true;
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// We're playing a stream of unknown length and duration. Assume it's live, and
|
// This is the same case as above, except in this case there's no value in deferring the retry
|
||||||
// therefore that the data at the uri is a continuously shifting window of the latest
|
// because there's no buffered data to be read. This case also covers an on-demand stream with
|
||||||
// available media. For this case there's no way to continue loading from where a
|
// unknown length that has yet to be prepared. This case cannot be disambiguated from the live
|
||||||
// previous load finished, so it's necessary to load from the start whenever commencing
|
// stream case, so we have no option but to load from the start.
|
||||||
// a new load.
|
|
||||||
lastSeekPositionUs = 0;
|
|
||||||
notifyDiscontinuity = prepared;
|
notifyDiscontinuity = prepared;
|
||||||
|
lastSeekPositionUs = 0;
|
||||||
|
extractedSamplesCountAtStartOfLoad = 0;
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
sampleQueue.reset();
|
sampleQueue.reset();
|
||||||
}
|
}
|
||||||
loadable.setLoadPosition(0, 0);
|
loadable.setLoadPosition(0, 0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user