Commit d458b90c by aquilescanta Committed by Oliver Woodman

Parameterize load error handling in ExtractorMediaSource

Issue:#2844
Issue:#3370
Issue:#2981

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=206927295
parent d744c979
......@@ -496,9 +496,9 @@ public final class C {
/** A data type constant for ads loader data. */
public static final int DATA_TYPE_AD = 6;
/**
* A data type constant for progressive media live streams, typically containing media samples.
* A data type constant for live progressive media streams, typically containing media samples.
*/
public static final int DATA_TYPE_MEDIA_LIVE_STREAM = 7;
public static final int DATA_TYPE_MEDIA_PROGRESSIVE_LIVE = 7;
/**
* Applications or extensions may define custom {@code DATA_TYPE_*} constants greater than or
* equal to this value.
......
......@@ -37,6 +37,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.Loader.Loadable;
......@@ -79,7 +80,7 @@ import java.util.Arrays;
private final Uri uri;
private final DataSource dataSource;
private final int minLoadableRetryCount;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final EventDispatcher eventDispatcher;
private final Listener listener;
private final Allocator allocator;
......@@ -98,7 +99,7 @@ import java.util.Arrays;
private int[] sampleQueueTrackIds;
private boolean sampleQueuesBuilt;
private boolean prepared;
private int actualMinLoadableRetryCount;
private int dataType;
private boolean seenFirstTrackSelection;
private boolean notifyDiscontinuity;
......@@ -124,7 +125,7 @@ import java.util.Arrays;
* @param uri The {@link Uri} of the media stream.
* @param dataSource The data source to read the media.
* @param extractors The extractors to use to read the data source.
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
* @param loadErrorHandlingPolicy The {@link LoadErrorHandlingPolicy}.
* @param eventDispatcher A dispatcher to notify of events.
* @param listener A listener to notify when information about the period changes.
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
......@@ -137,7 +138,7 @@ import java.util.Arrays;
Uri uri,
DataSource dataSource,
Extractor[] extractors,
int minLoadableRetryCount,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher,
Listener listener,
Allocator allocator,
......@@ -145,7 +146,7 @@ import java.util.Arrays;
int continueLoadingCheckIntervalBytes) {
this.uri = uri;
this.dataSource = dataSource;
this.minLoadableRetryCount = minLoadableRetryCount;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.eventDispatcher = eventDispatcher;
this.listener = listener;
this.allocator = allocator;
......@@ -154,31 +155,16 @@ import java.util.Arrays;
loader = new Loader("Loader:ExtractorMediaPeriod");
extractorHolder = new ExtractorHolder(extractors, this);
loadCondition = new ConditionVariable();
maybeFinishPrepareRunnable = new Runnable() {
@Override
public void run() {
maybeFinishPrepare();
}
};
onContinueLoadingRequestedRunnable = new Runnable() {
@Override
public void run() {
if (!released) {
callback.onContinueLoadingRequested(ExtractorMediaPeriod.this);
}
}
};
maybeFinishPrepareRunnable = this::maybeFinishPrepare;
onContinueLoadingRequestedRunnable =
() -> callback.onContinueLoadingRequested(ExtractorMediaPeriod.this);
handler = new Handler();
sampleQueueTrackIds = new int[0];
sampleQueues = new SampleQueue[0];
pendingResetPositionUs = C.TIME_UNSET;
length = C.LENGTH_UNSET;
durationUs = C.TIME_UNSET;
// Assume on-demand for MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA, until prepared.
actualMinLoadableRetryCount =
minLoadableRetryCount == ExtractorMediaSource.MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA
? ExtractorMediaSource.DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND
: minLoadableRetryCount;
dataType = C.DATA_TYPE_MEDIA;
eventDispatcher.mediaPeriodCreated();
}
......@@ -404,7 +390,7 @@ import java.util.Arrays;
}
/* package */ void maybeThrowError() throws IOException {
loader.maybeThrowError(actualMinLoadableRetryCount);
loader.maybeThrowError(loadErrorHandlingPolicy.getMinimumLoadableRetryCount(dataType));
}
/* package */ int readData(int track, FormatHolder formatHolder, DecoderInputBuffer buffer,
......@@ -543,19 +529,20 @@ import java.util.Arrays;
IOException error,
int errorCount) {
copyLengthFromLoader(loadable);
LoadErrorAction retryAction;
if (isLoadableExceptionFatal(error)) {
retryAction = Loader.DONT_RETRY_FATAL;
} else {
LoadErrorAction loadErrorAction;
long retryDelayMs =
loadErrorHandlingPolicy.getRetryDelayMsFor(dataType, durationUs, error, errorCount);
if (retryDelayMs == C.TIME_UNSET) {
loadErrorAction = Loader.DONT_RETRY_FATAL;
} else /* the load should be retried */ {
int extractedSamplesCount = getExtractedSamplesCount();
boolean madeProgress = extractedSamplesCount > extractedSamplesCountAtStartOfLoad;
retryAction =
loadErrorAction =
configureRetry(loadable, extractedSamplesCount)
? (madeProgress ? Loader.RETRY_RESET_ERROR_COUNT : Loader.RETRY)
? Loader.createRetryAction(/* resetErrorCount= */ madeProgress, retryDelayMs)
: Loader.DONT_RETRY;
}
boolean wasCanceled =
retryAction == Loader.DONT_RETRY || retryAction == Loader.DONT_RETRY_FATAL;
eventDispatcher.loadError(
loadable.dataSpec,
loadable.dataSource.getLastOpenedUri(),
......@@ -570,8 +557,8 @@ import java.util.Arrays;
loadDurationMs,
loadable.dataSource.getBytesRead(),
error,
wasCanceled);
return retryAction;
!loadErrorAction.isRetry());
return loadErrorAction;
}
// ExtractorOutput implementation. Called by the loading thread.
......@@ -639,10 +626,10 @@ import java.util.Arrays;
haveAudioVideoTracks |= isAudioVideo;
}
tracks = new TrackGroupArray(trackArray);
if (minLoadableRetryCount == ExtractorMediaSource.MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA
&& length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET) {
actualMinLoadableRetryCount = ExtractorMediaSource.DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE;
}
dataType =
length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET
? C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE
: C.DATA_TYPE_MEDIA;
prepared = true;
listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable());
callback.onPrepared(this);
......@@ -655,8 +642,8 @@ import java.util.Arrays;
}
private void startLoading() {
ExtractingLoadable loadable = new ExtractingLoadable(uri, dataSource, extractorHolder,
loadCondition);
ExtractingLoadable loadable =
new ExtractingLoadable(uri, dataSource, extractorHolder, loadCondition);
if (prepared) {
Assertions.checkState(isPendingReset());
if (durationUs != C.TIME_UNSET && pendingResetPositionUs >= durationUs) {
......@@ -669,7 +656,9 @@ import java.util.Arrays;
pendingResetPositionUs = C.TIME_UNSET;
}
extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount();
long elapsedRealtimeMs = loader.startLoading(loadable, this, actualMinLoadableRetryCount);
long elapsedRealtimeMs =
loader.startLoading(
loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(dataType));
eventDispatcher.loadStarted(
loadable.dataSpec,
loadable.dataSpec.uri,
......@@ -772,10 +761,6 @@ import java.util.Arrays;
return pendingResetPositionUs != C.TIME_UNSET;
}
private static boolean isLoadableExceptionFatal(IOException e) {
return e instanceof UnrecognizedInputFormatException;
}
private final class SampleStreamImpl implements SampleStream {
private final int track;
......@@ -807,9 +792,7 @@ import java.util.Arrays;
}
/**
* Loads the media stream and extracts sample data from it.
*/
/** Loads the media stream and extracts sample data from it. */
/* package */ final class ExtractingLoadable implements Loadable {
private final Uri uri;
......@@ -825,7 +808,10 @@ import java.util.Arrays;
private DataSpec dataSpec;
private long length;
public ExtractingLoadable(Uri uri, DataSource dataSource, ExtractorHolder extractorHolder,
public ExtractingLoadable(
Uri uri,
DataSource dataSource,
ExtractorHolder extractorHolder,
ConditionVariable loadCondition) {
this.uri = Assertions.checkNotNull(uri);
this.dataSource = new StatsDataSource(dataSource);
......@@ -837,11 +823,7 @@ import java.util.Arrays;
dataSpec = new DataSpec(uri, positionHolder.position, C.LENGTH_UNSET, customCacheKey);
}
public void setLoadPosition(long position, long timeUs) {
positionHolder.position = position;
seekTimeUs = timeUs;
pendingExtractorSeek = true;
}
// Loadable implementation.
@Override
public void cancelLoad() {
......@@ -886,6 +868,13 @@ import java.util.Arrays;
}
}
// Internal methods.
private void setLoadPosition(long position, long timeUs) {
positionHolder.position = position;
seekTimeUs = timeUs;
pendingExtractorSeek = true;
}
}
/**
......@@ -950,7 +939,5 @@ import java.util.Arrays;
extractor = null;
}
}
}
}
......@@ -27,6 +27,8 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
......@@ -44,6 +46,7 @@ import java.io.IOException;
*/
public final class ExtractorMediaSource extends BaseMediaSource
implements ExtractorMediaPeriod.Listener {
/**
* Listener of {@link ExtractorMediaSource} events.
*
......@@ -76,7 +79,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
private @Nullable ExtractorsFactory extractorsFactory;
private @Nullable String customCacheKey;
private @Nullable Object tag;
private int minLoadableRetryCount;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private int continueLoadingCheckIntervalBytes;
private boolean isCreateCalled;
......@@ -87,7 +90,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
*/
public Factory(DataSource.Factory dataSourceFactory) {
this.dataSourceFactory = dataSourceFactory;
minLoadableRetryCount = MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA;
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
continueLoadingCheckIntervalBytes = DEFAULT_LOADING_CHECK_INTERVAL_BYTES;
}
......@@ -138,16 +141,36 @@ public final class ExtractorMediaSource extends BaseMediaSource
}
/**
* Sets the minimum number of times to retry if a loading error occurs. The default value is
* {@link #MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA}.
* Sets the minimum number of times to retry if a loading error occurs. See {@link
* #setLoadErrorHandlingPolicy} for the default value.
*
* <p>Calling this method is equivalent to calling {@link #setLoadErrorHandlingPolicy} with
* {@link DefaultLoadErrorHandlingPolicy(int)
* DefaultLoadErrorHandlingPolicy(minLoadableRetryCount)}
*
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
* @deprecated Use {@link #setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy)} instead.
*/
@Deprecated
public Factory setMinLoadableRetryCount(int minLoadableRetryCount) {
return setLoadErrorHandlingPolicy(new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount));
}
/**
* Sets the {@link LoadErrorHandlingPolicy}. The default value is created by calling {@link
* DefaultLoadErrorHandlingPolicy()}.
*
* <p>Calling this method overrides any calls to {@link #setMinLoadableRetryCount(int)}.
*
* @param loadErrorHandlingPolicy A {@link LoadErrorHandlingPolicy}.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
Assertions.checkState(!isCreateCalled);
this.minLoadableRetryCount = minLoadableRetryCount;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
return this;
}
......@@ -184,7 +207,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
uri,
dataSourceFactory,
extractorsFactory,
minLoadableRetryCount,
loadErrorHandlingPolicy,
customCacheKey,
continueLoadingCheckIntervalBytes,
tag);
......@@ -211,21 +234,6 @@ public final class ExtractorMediaSource extends BaseMediaSource
}
/**
* The default minimum number of times to retry loading prior to failing for on-demand streams.
*/
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND = 3;
/** The default minimum number of times to retry loading prior to failing for live streams. */
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE = 6;
/**
* Value for {@code minLoadableRetryCount} that causes the loader to retry {@link
* #DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE} times for live streams and {@link
* #DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND} for on-demand streams.
*/
public static final int MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA = -1;
/**
* The default number of bytes that should be loaded between each each invocation of {@link
* MediaPeriod.Callback#onContinueLoadingRequested(SequenceableLoader)}.
*/
......@@ -234,7 +242,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
private final Uri uri;
private final DataSource.Factory dataSourceFactory;
private final ExtractorsFactory extractorsFactory;
private final int minLoadableRetryCount;
private final LoadErrorHandlingPolicy loadableLoadErrorHandlingPolicy;
private final String customCacheKey;
private final int continueLoadingCheckIntervalBytes;
private final @Nullable Object tag;
......@@ -283,8 +291,14 @@ public final class ExtractorMediaSource extends BaseMediaSource
Handler eventHandler,
EventListener eventListener,
String customCacheKey) {
this(uri, dataSourceFactory, extractorsFactory, MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA, eventHandler,
eventListener, customCacheKey, DEFAULT_LOADING_CHECK_INTERVAL_BYTES);
this(
uri,
dataSourceFactory,
extractorsFactory,
eventHandler,
eventListener,
customCacheKey,
DEFAULT_LOADING_CHECK_INTERVAL_BYTES);
}
/**
......@@ -293,7 +307,6 @@ public final class ExtractorMediaSource extends BaseMediaSource
* @param extractorsFactory A factory for {@link Extractor}s to process the media stream. If the
* possible formats are known, pass a factory that instantiates extractors for those formats.
* Otherwise, pass a {@link DefaultExtractorsFactory} to use default extractors.
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
* @param eventHandler A handler for events. May be null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param customCacheKey A custom key that uniquely identifies the original stream. Used for cache
......@@ -307,7 +320,6 @@ public final class ExtractorMediaSource extends BaseMediaSource
Uri uri,
DataSource.Factory dataSourceFactory,
ExtractorsFactory extractorsFactory,
int minLoadableRetryCount,
Handler eventHandler,
EventListener eventListener,
String customCacheKey,
......@@ -316,7 +328,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
uri,
dataSourceFactory,
extractorsFactory,
minLoadableRetryCount,
new DefaultLoadErrorHandlingPolicy(),
customCacheKey,
continueLoadingCheckIntervalBytes,
/* tag= */ null);
......@@ -329,14 +341,14 @@ public final class ExtractorMediaSource extends BaseMediaSource
Uri uri,
DataSource.Factory dataSourceFactory,
ExtractorsFactory extractorsFactory,
int minLoadableRetryCount,
LoadErrorHandlingPolicy loadableLoadErrorHandlingPolicy,
@Nullable String customCacheKey,
int continueLoadingCheckIntervalBytes,
@Nullable Object tag) {
this.uri = uri;
this.dataSourceFactory = dataSourceFactory;
this.extractorsFactory = extractorsFactory;
this.minLoadableRetryCount = minLoadableRetryCount;
this.loadableLoadErrorHandlingPolicy = loadableLoadErrorHandlingPolicy;
this.customCacheKey = customCacheKey;
this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
this.timelineDurationUs = C.TIME_UNSET;
......@@ -368,7 +380,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
uri,
dataSource,
extractorsFactory.createExtractors(),
minLoadableRetryCount,
loadableLoadErrorHandlingPolicy,
createEventDispatcher(id),
this,
allocator,
......
......@@ -26,12 +26,26 @@ public final class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPo
/** The default minimum number of times to retry loading data prior to propagating the error. */
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
/**
* The default minimum number of times to retry loading prior to failing for progressive live
* streams.
*/
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_PROGRESSIVE_LIVE = 6;
private static final int DEFAULT_BEHAVIOR_MIN_LOADABLE_RETRY_COUNT = -1;
private final int minimumLoadableRetryCount;
/** Creates an instance that returns the default values. */
/**
* Creates an instance with default behavior.
*
* <p>{@link #getMinimumLoadableRetryCount} will return {@link
* #DEFAULT_MIN_LOADABLE_RETRY_COUNT_PROGRESSIVE_LIVE} for {@code dataType} {@link
* C#DATA_TYPE_MEDIA_PROGRESSIVE_LIVE}. For other {@code dataType} values, it will return {@link
* #DEFAULT_MIN_LOADABLE_RETRY_COUNT}.
*/
public DefaultLoadErrorHandlingPolicy() {
this(DEFAULT_MIN_LOADABLE_RETRY_COUNT);
this(DEFAULT_BEHAVIOR_MIN_LOADABLE_RETRY_COUNT);
}
/**
......@@ -73,9 +87,18 @@ public final class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPo
: Math.min((errorCount - 1) * 1000, 5000);
}
/** Returns {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}. */
/**
* See {@link #DefaultLoadErrorHandlingPolicy()} and {@link #DefaultLoadErrorHandlingPolicy(int)}
* for documentation about the behavior of this method.
*/
@Override
public int getMinimumLoadableRetryCount(int dataType) {
return minimumLoadableRetryCount;
if (minimumLoadableRetryCount == DEFAULT_BEHAVIOR_MIN_LOADABLE_RETRY_COUNT) {
return dataType == C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE
? DEFAULT_MIN_LOADABLE_RETRY_COUNT_PROGRESSIVE_LIVE
: DEFAULT_MIN_LOADABLE_RETRY_COUNT;
} else {
return minimumLoadableRetryCount;
}
}
}
......@@ -33,6 +33,8 @@ import java.io.IOException;
* int)} defines whether the load is retried. Errors whose load is not retried are propagated. Load
* errors whose load is retried are propagated according to {@link
* #getMinimumLoadableRetryCount(int)}.
*
* <p>Methods are invoked on the playback thread.
*/
public interface LoadErrorHandlingPolicy {
......
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