Allow configuration of the Loader retry delay

Issue:#3370

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=201996109
This commit is contained in:
aquilescanta 2018-06-25 12:09:27 -07:00 committed by Oliver Woodman
parent eb8c686243
commit a1f89bec0d
9 changed files with 92 additions and 30 deletions

View File

@ -13,6 +13,9 @@
([#3972](https://github.com/google/ExoPlayer/issues/3972)). ([#3972](https://github.com/google/ExoPlayer/issues/3972)).
* Pass `BandwidthMeter` to `TrackSelection.Factory` which can be used to obtain * Pass `BandwidthMeter` to `TrackSelection.Factory` which can be used to obtain
bandwidth estimates in the future. Always null at the moment. bandwidth estimates in the future. Always null at the moment.
* Error handling:
* Allow configuration of the Loader retry delay
([#3370](https://github.com/google/ExoPlayer/issues/3370)).
* HLS: * HLS:
* Allow injection of custom playlist trackers. * Allow injection of custom playlist trackers.
* Pass HTTP response headers to `HlsExtractorFactory.createExtractor`. * Pass HTTP response headers to `HlsExtractorFactory.createExtractor`.

View File

@ -38,6 +38,7 @@ import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.Loader.Loadable; import com.google.android.exoplayer2.upstream.Loader.Loadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.ConditionVariable;
@ -532,7 +533,7 @@ import java.util.Arrays;
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) {
boolean isErrorFatal = isLoadableExceptionFatal(error); boolean isErrorFatal = isLoadableExceptionFatal(error);
eventDispatcher.loadError( eventDispatcher.loadError(

View File

@ -25,6 +25,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.Loader.Loadable; import com.google.android.exoplayer2.upstream.Loader.Loadable;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -228,7 +229,7 @@ import java.util.Arrays;
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) {
errorCount++; errorCount++;
boolean cancel = treatLoadErrorsAsEndOfStream && errorCount >= minLoadableRetryCount; boolean cancel = treatLoadErrorsAsEndOfStream && errorCount >= minLoadableRetryCount;

View File

@ -28,6 +28,7 @@ import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.SequenceableLoader;
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;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
@ -409,7 +410,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) {
long bytesLoaded = loadable.bytesLoaded(); long bytesLoaded = loadable.bytesLoaded();
boolean isMediaChunk = isMediaChunk(loadable); boolean isMediaChunk = isMediaChunk(loadable);

View File

@ -23,6 +23,7 @@ import android.os.SystemClock;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -110,12 +111,12 @@ public final class Loader implements LoaderErrorThrower {
* @param elapsedRealtimeMs {@link SystemClock#elapsedRealtime} when the error occurred. * @param elapsedRealtimeMs {@link SystemClock#elapsedRealtime} when the error occurred.
* @param loadDurationMs The duration of the load up to the point at which the error occurred. * @param loadDurationMs The duration of the load up to the point at which the error occurred.
* @param error The load error. * @param error The load error.
* @return The desired retry action. One of {@link Loader#RETRY}, {@link * @return The desired error handling action. One of {@link Loader#RETRY}, {@link
* Loader#RETRY_RESET_ERROR_COUNT}, {@link Loader#DONT_RETRY} and {@link * Loader#RETRY_RESET_ERROR_COUNT}, {@link Loader#DONT_RETRY}, {@link
* Loader#DONT_RETRY_FATAL}. * Loader#DONT_RETRY_FATAL} or a retry action created by {@link #createRetryAction}.
*/ */
@RetryAction LoadErrorAction onLoadError(
int onLoadError(T loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error); T loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error);
} }
/** /**
@ -130,15 +131,51 @@ public final class Loader implements LoaderErrorThrower {
} }
/** Actions that can be taken in response to a load error. */ /** Types of action that can be taken in response to a load error. */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({RETRY, RETRY_RESET_ERROR_COUNT, DONT_RETRY, DONT_RETRY_FATAL}) @IntDef({
public @interface RetryAction {} ACTION_TYPE_RETRY,
ACTION_TYPE_RETRY_AND_RESET_ERROR_COUNT,
ACTION_TYPE_DONT_RETRY,
ACTION_TYPE_DONT_RETRY_FATAL
})
private @interface RetryActionType {}
public static final int RETRY = 0; private static final int ACTION_TYPE_RETRY = 0;
public static final int RETRY_RESET_ERROR_COUNT = 1; private static final int ACTION_TYPE_RETRY_AND_RESET_ERROR_COUNT = 1;
public static final int DONT_RETRY = 2; private static final int ACTION_TYPE_DONT_RETRY = 2;
public static final int DONT_RETRY_FATAL = 3; private static final int ACTION_TYPE_DONT_RETRY_FATAL = 3;
/** Retries the load using the default delay. */
public static final LoadErrorAction RETRY =
createRetryAction(/* resetErrorCount= */ false, C.TIME_UNSET);
/** Retries the load using the default delay and resets the error count. */
public static final LoadErrorAction RETRY_RESET_ERROR_COUNT =
createRetryAction(/* resetErrorCount= */ true, C.TIME_UNSET);
/** Discards the failed loading task and ignores any errors that have occurred. */
public static final LoadErrorAction DONT_RETRY =
new LoadErrorAction(ACTION_TYPE_DONT_RETRY, C.TIME_UNSET);
/**
* Discards the failed load. The next call to {@link #maybeThrowError()} will throw the last load
* error.
*/
public static final LoadErrorAction DONT_RETRY_FATAL =
new LoadErrorAction(ACTION_TYPE_DONT_RETRY_FATAL, C.TIME_UNSET);
/**
* Action that can be taken in response to {@link Callback#onLoadError(Loadable, long, long,
* IOException)}.
*/
public static final class LoadErrorAction {
private final @RetryActionType int type;
private final long retryDelayMillis;
private LoadErrorAction(@RetryActionType int type, long retryDelayMillis) {
this.type = type;
this.retryDelayMillis = retryDelayMillis;
}
}
private final ExecutorService downloadExecutorService; private final ExecutorService downloadExecutorService;
@ -152,6 +189,19 @@ public final class Loader implements LoaderErrorThrower {
this.downloadExecutorService = Util.newSingleThreadExecutor(threadName); this.downloadExecutorService = Util.newSingleThreadExecutor(threadName);
} }
/**
* Creates a {@link LoadErrorAction} for retrying with the given parameters.
*
* @param resetErrorCount Whether the previous error count should be set to zero.
* @param retryDelayMillis The number of milliseconds to wait before retrying.
* @return A {@link LoadErrorAction} for retrying with the given parameters.
*/
public static LoadErrorAction createRetryAction(boolean resetErrorCount, long retryDelayMillis) {
return new LoadErrorAction(
resetErrorCount ? ACTION_TYPE_RETRY_AND_RESET_ERROR_COUNT : ACTION_TYPE_RETRY,
retryDelayMillis);
}
/** /**
* Starts loading a {@link Loadable}. * Starts loading a {@link Loadable}.
* *
@ -395,12 +445,16 @@ public final class Loader implements LoaderErrorThrower {
break; break;
case MSG_IO_EXCEPTION: case MSG_IO_EXCEPTION:
currentError = (IOException) msg.obj; currentError = (IOException) msg.obj;
int retryAction = callback.onLoadError(loadable, nowMs, durationMs, currentError); LoadErrorAction action = callback.onLoadError(loadable, nowMs, durationMs, currentError);
if (retryAction == DONT_RETRY_FATAL) { if (action.type == ACTION_TYPE_DONT_RETRY_FATAL) {
fatalError = currentError; fatalError = currentError;
} else if (retryAction != DONT_RETRY) { } else if (action.type != ACTION_TYPE_DONT_RETRY) {
errorCount = retryAction == RETRY_RESET_ERROR_COUNT ? 1 : errorCount + 1; errorCount =
start(getRetryDelayMillis()); action.type == ACTION_TYPE_RETRY_AND_RESET_ERROR_COUNT ? 1 : errorCount + 1;
start(
action.retryDelayMillis != C.TIME_UNSET
? action.retryDelayMillis
: getRetryDelayMillis());
} }
break; break;
default: default:

View File

@ -43,6 +43,7 @@ import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower; import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
@ -716,8 +717,7 @@ public final class DashMediaSource extends BaseMediaSource {
} }
} }
/* package */ @Loader.RetryAction /* package */ LoadErrorAction onManifestLoadError(
int onManifestLoadError(
ParsingLoadable<DashManifest> loadable, ParsingLoadable<DashManifest> loadable,
long elapsedRealtimeMs, long elapsedRealtimeMs,
long loadDurationMs, long loadDurationMs,
@ -745,8 +745,7 @@ public final class DashMediaSource extends BaseMediaSource {
onUtcTimestampResolved(loadable.getResult() - elapsedRealtimeMs); onUtcTimestampResolved(loadable.getResult() - elapsedRealtimeMs);
} }
/* package */ @Loader.RetryAction /* package */ LoadErrorAction onUtcTimestampLoadError(
int onUtcTimestampLoadError(
ParsingLoadable<Long> loadable, ParsingLoadable<Long> loadable,
long elapsedRealtimeMs, long elapsedRealtimeMs,
long loadDurationMs, long loadDurationMs,
@ -1172,7 +1171,7 @@ public final class DashMediaSource extends BaseMediaSource {
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
ParsingLoadable<DashManifest> loadable, ParsingLoadable<DashManifest> loadable,
long elapsedRealtimeMs, long elapsedRealtimeMs,
long loadDurationMs, long loadDurationMs,
@ -1197,7 +1196,7 @@ public final class DashMediaSource extends BaseMediaSource {
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
ParsingLoadable<Long> loadable, ParsingLoadable<Long> loadable,
long elapsedRealtimeMs, long elapsedRealtimeMs,
long loadDurationMs, long loadDurationMs,

View File

@ -40,6 +40,7 @@ import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUr
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;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -602,7 +603,7 @@ import java.util.Arrays;
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { Chunk loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) {
long bytesLoaded = loadable.bytesLoaded(); long bytesLoaded = loadable.bytesLoaded();
boolean isMediaChunk = isMediaChunk(loadable); boolean isMediaChunk = isMediaChunk(loadable);

View File

@ -27,6 +27,7 @@ import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUr
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.UriUtil; import com.google.android.exoplayer2.util.UriUtil;
@ -229,7 +230,7 @@ public final class DefaultHlsPlaylistTracker
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
ParsingLoadable<HlsPlaylist> loadable, ParsingLoadable<HlsPlaylist> loadable,
long elapsedRealtimeMs, long elapsedRealtimeMs,
long loadDurationMs, long loadDurationMs,
@ -485,7 +486,7 @@ public final class DefaultHlsPlaylistTracker
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
ParsingLoadable<HlsPlaylist> loadable, ParsingLoadable<HlsPlaylist> loadable,
long elapsedRealtimeMs, long elapsedRealtimeMs,
long loadDurationMs, long loadDurationMs,

View File

@ -41,6 +41,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsUtil;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower; import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
@ -540,7 +541,7 @@ public final class SsMediaSource extends BaseMediaSource
} }
@Override @Override
public @Loader.RetryAction int onLoadError( public LoadErrorAction onLoadError(
ParsingLoadable<SsManifest> loadable, ParsingLoadable<SsManifest> loadable,
long elapsedRealtimeMs, long elapsedRealtimeMs,
long loadDurationMs, long loadDurationMs,