Use LoadErrorHandlingPolicy in SsMediaSource and DashMediaSource

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=208020030
This commit is contained in:
aquilescanta 2018-08-09 02:51:04 -07:00 committed by Oliver Woodman
parent 4ebc455c09
commit d5c7bff643
15 changed files with 227 additions and 220 deletions

View File

@ -178,11 +178,6 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
}
/**
* The default minimum number of times to retry loading data prior to failing.
*/
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
private final DataSpec dataSpec;
private final DataSource.Factory dataSourceFactory;
private final Format format;
@ -204,7 +199,12 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
@Deprecated
public SingleSampleMediaSource(
Uri uri, DataSource.Factory dataSourceFactory, Format format, long durationUs) {
this(uri, dataSourceFactory, format, durationUs, DEFAULT_MIN_LOADABLE_RETRY_COUNT);
this(
uri,
dataSourceFactory,
format,
durationUs,
DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT);
}
/**

View File

@ -27,6 +27,8 @@ import com.google.android.exoplayer2.source.SampleQueue;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SequenceableLoader;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
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.util.Assertions;
@ -64,7 +66,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
private final T chunkSource;
private final SequenceableLoader.Callback<ChunkSampleStream<T>> callback;
private final EventDispatcher eventDispatcher;
private final int minLoadableRetryCount;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final Loader loader;
private final ChunkHolder nextChunkHolder;
private final ArrayList<BaseMediaChunk> mediaChunks;
@ -81,6 +83,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
/* package */ boolean loadingFinished;
/**
* Constructs an instance.
*
* @param primaryTrackType The type of the primary track. One of the {@link C} {@code
* TRACK_TYPE_*} constants.
* @param embeddedTrackTypes The types of any embedded tracks, or null.
@ -92,7 +96,10 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
* @param minLoadableRetryCount The minimum number of times that the source should retry a load
* before propagating an error.
* @param eventDispatcher A dispatcher to notify of events.
* @deprecated Use {@link #ChunkSampleStream(int, int[], Format[], ChunkSource, Callback,
* Allocator, long, LoadErrorHandlingPolicy, EventDispatcher)} instead.
*/
@Deprecated
public ChunkSampleStream(
int primaryTrackType,
int[] embeddedTrackTypes,
@ -103,13 +110,49 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
long positionUs,
int minLoadableRetryCount,
EventDispatcher eventDispatcher) {
this(
primaryTrackType,
embeddedTrackTypes,
embeddedTrackFormats,
chunkSource,
callback,
allocator,
positionUs,
new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
eventDispatcher);
}
/**
* Constructs an instance.
*
* @param primaryTrackType The type of the primary track. One of the {@link C} {@code
* TRACK_TYPE_*} constants.
* @param embeddedTrackTypes The types of any embedded tracks, or null.
* @param embeddedTrackFormats The formats of the embedded tracks, or null.
* @param chunkSource A {@link ChunkSource} from which chunks to load are obtained.
* @param callback An {@link Callback} for the stream.
* @param allocator An {@link Allocator} from which allocations can be obtained.
* @param positionUs The position from which to start loading media.
* @param loadErrorHandlingPolicy The {@link LoadErrorHandlingPolicy}.
* @param eventDispatcher A dispatcher to notify of events.
*/
public ChunkSampleStream(
int primaryTrackType,
int[] embeddedTrackTypes,
Format[] embeddedTrackFormats,
T chunkSource,
Callback<ChunkSampleStream<T>> callback,
Allocator allocator,
long positionUs,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher) {
this.primaryTrackType = primaryTrackType;
this.embeddedTrackTypes = embeddedTrackTypes;
this.embeddedTrackFormats = embeddedTrackFormats;
this.chunkSource = chunkSource;
this.callback = callback;
this.eventDispatcher = eventDispatcher;
this.minLoadableRetryCount = minLoadableRetryCount;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
loader = new Loader("Loader:ChunkSampleStream");
nextChunkHolder = new ChunkHolder();
mediaChunks = new ArrayList<>();
@ -439,12 +482,15 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
int lastChunkIndex = mediaChunks.size() - 1;
boolean cancelable =
bytesLoaded == 0 || !isMediaChunk || !haveReadFromMediaChunk(lastChunkIndex);
boolean canceled = false;
if (chunkSource.onChunkLoadError(loadable, cancelable, error)) {
if (!cancelable) {
Log.w(TAG, "Ignoring attempt to cancel non-cancelable load.");
} else {
canceled = true;
long blacklistDurationMs =
cancelable
? loadErrorHandlingPolicy.getBlacklistDurationMsFor(
loadable.type, loadDurationMs, error, errorCount)
: C.TIME_UNSET;
LoadErrorAction loadErrorAction = null;
if (chunkSource.onChunkLoadError(loadable, cancelable, error, blacklistDurationMs)) {
if (cancelable) {
loadErrorAction = Loader.DONT_RETRY;
if (isMediaChunk) {
BaseMediaChunk removed = discardUpstreamMediaChunksFromIndex(lastChunkIndex);
Assertions.checkState(removed == loadable);
@ -452,8 +498,23 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
pendingResetPositionUs = lastSeekPositionUs;
}
}
} else {
Log.w(TAG, "Ignoring attempt to cancel non-cancelable load.");
}
}
if (loadErrorAction == null) {
// The load was not cancelled. Either the load must be retried or the error propagated.
long retryDelayMs =
loadErrorHandlingPolicy.getRetryDelayMsFor(
loadable.type, loadDurationMs, error, errorCount);
loadErrorAction =
retryDelayMs != C.TIME_UNSET
? Loader.createRetryAction(/* resetErrorCount= */ false, retryDelayMs)
: Loader.DONT_RETRY_FATAL;
}
boolean canceled = !loadErrorAction.isRetry();
eventDispatcher.loadError(
loadable.dataSpec,
loadable.getUri(),
@ -471,10 +532,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
canceled);
if (canceled) {
callback.onContinueLoadingRequested(this);
return Loader.DONT_RETRY;
} else {
return Loader.RETRY;
}
return loadErrorAction;
}
// SequenceableLoader implementation
@ -521,7 +580,9 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
mediaChunk.init(mediaChunkOutput);
mediaChunks.add(mediaChunk);
}
long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount);
long elapsedRealtimeMs =
loader.startLoading(
loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type));
eventDispatcher.loadStarted(
loadable.dataSpec,
loadable.dataSpec.uri,

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.chunk;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.SeekParameters;
import java.io.IOException;
import java.util.List;
@ -83,8 +84,8 @@ public interface ChunkSource {
/**
* Called when the {@link ChunkSampleStream} has finished loading a chunk obtained from this
* source.
* <p>
* This method should only be called when the source is enabled.
*
* <p>This method should only be called when the source is enabled.
*
* @param chunk The chunk whose load has been completed.
*/
@ -93,15 +94,15 @@ public interface ChunkSource {
/**
* Called when the {@link ChunkSampleStream} encounters an error loading a chunk obtained from
* this source.
* <p>
* This method should only be called when the source is enabled.
*
* <p>This method should only be called when the source is enabled.
*
* @param chunk The chunk whose load encountered the error.
* @param cancelable Whether the load can be canceled.
* @param e The error.
* @return Whether the load should be canceled. Should always be false if {@code cancelable} is
* false.
* @param blacklistDurationMs The duration for which the associated track may be blacklisted, or
* {@link C#TIME_UNSET} if the track may not be blacklisted.
* @return Whether the load should be canceled. Must be false if {@code cancelable} is false.
*/
boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e);
boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e, long blacklistDurationMs);
}

View File

@ -1,99 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.source.chunk;
import android.util.Log;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
/**
* Helper class for blacklisting tracks in a {@link TrackSelection} when 404 (Not Found) and 410
* (Gone) HTTP response codes are encountered.
*/
public final class ChunkedTrackBlacklistUtil {
/**
* The default duration for which a track is blacklisted in milliseconds.
*/
public static final long DEFAULT_TRACK_BLACKLIST_MS = 60000;
private static final String TAG = "ChunkedTrackBlacklist";
/**
* Blacklists {@code trackSelectionIndex} in {@code trackSelection} for
* {@link #DEFAULT_TRACK_BLACKLIST_MS} if {@code e} is an {@link InvalidResponseCodeException}
* with {@link InvalidResponseCodeException#responseCode} equal to 404 or 410. Else does nothing.
* Note that blacklisting will fail if the track is the only non-blacklisted track in the
* selection.
*
* @param trackSelection The track selection.
* @param trackSelectionIndex The index in the selection to consider blacklisting.
* @param e The error to inspect.
* @return Whether the track was blacklisted in the selection.
*/
public static boolean maybeBlacklistTrack(TrackSelection trackSelection, int trackSelectionIndex,
Exception e) {
return maybeBlacklistTrack(trackSelection, trackSelectionIndex, e, DEFAULT_TRACK_BLACKLIST_MS);
}
/**
* Blacklists {@code trackSelectionIndex} in {@code trackSelection} for
* {@code blacklistDurationMs} if calling {@link #shouldBlacklist(Exception)} for {@code e}
* returns true. Else does nothing. Note that blacklisting will fail if the track is the only
* non-blacklisted track in the selection.
*
* @param trackSelection The track selection.
* @param trackSelectionIndex The index in the selection to consider blacklisting.
* @param e The error to inspect.
* @param blacklistDurationMs The duration to blacklist the track for, if it is blacklisted.
* @return Whether the track was blacklisted.
*/
public static boolean maybeBlacklistTrack(TrackSelection trackSelection, int trackSelectionIndex,
Exception e, long blacklistDurationMs) {
if (shouldBlacklist(e)) {
boolean blacklisted = trackSelection.blacklist(trackSelectionIndex, blacklistDurationMs);
int responseCode = ((InvalidResponseCodeException) e).responseCode;
if (blacklisted) {
Log.w(TAG, "Blacklisted: duration=" + blacklistDurationMs + ", responseCode="
+ responseCode + ", format=" + trackSelection.getFormat(trackSelectionIndex));
} else {
Log.w(TAG, "Blacklisting failed (cannot blacklist last enabled track): responseCode="
+ responseCode + ", format=" + trackSelection.getFormat(trackSelectionIndex));
}
return blacklisted;
}
return false;
}
/**
* Returns whether a loading error is an {@link InvalidResponseCodeException} with
* {@link InvalidResponseCodeException#responseCode} equal to 404 or 410.
*
* @param e The loading error.
* @return Wheter the loading error is an {@link InvalidResponseCodeException} with
* {@link InvalidResponseCodeException#responseCode} equal to 404 or 410.
*/
public static boolean shouldBlacklist(Exception e) {
if (e instanceof InvalidResponseCodeException) {
int responseCode = ((InvalidResponseCodeException) e).responseCode;
return responseCode == 404 || responseCode == 410;
}
return false;
}
private ChunkedTrackBlacklistUtil() {}
}

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.upstream;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil;
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
import java.io.IOException;
@ -31,6 +30,8 @@ public final class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPo
* streams.
*/
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_PROGRESSIVE_LIVE = 6;
/** The default duration for which a track is blacklisted in milliseconds. */
public static final long DEFAULT_TRACK_BLACKLIST_MS = 60000;
private static final int DEFAULT_BEHAVIOR_MIN_LOADABLE_RETRY_COUNT = -1;
@ -59,8 +60,7 @@ public final class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPo
/**
* Blacklists resources whose load error was an {@link InvalidResponseCodeException} with response
* code HTTP 404 or 410. The duration of the blacklisting is {@link
* ChunkedTrackBlacklistUtil#DEFAULT_TRACK_BLACKLIST_MS}.
* code HTTP 404 or 410. The duration of the blacklisting is {@link #DEFAULT_TRACK_BLACKLIST_MS}.
*/
@Override
public long getBlacklistDurationMsFor(
@ -69,7 +69,7 @@ public final class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPo
int responseCode = ((InvalidResponseCodeException) exception).responseCode;
return responseCode == 404 // HTTP 404 Not Found.
|| responseCode == 410 // HTTP 410 Gone.
? ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS
? DEFAULT_TRACK_BLACKLIST_MS
: C.TIME_UNSET;
}
return C.TIME_UNSET;

View File

@ -20,9 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil;
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer2.upstream.Loader.Loadable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
@ -34,56 +32,43 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public final class DefaultLoadErrorHandlingPolicyTest {
private static final Loadable DUMMY_LOADABLE =
new Loadable() {
@Override
public void cancelLoad() {
// Do nothing.
}
@Override
public void load() throws IOException, InterruptedException {
// Do nothing.
}
};
@Test
public void getBlacklistDurationMsFor_blacklist404() throws Exception {
public void getBlacklistDurationMsFor_blacklist404() {
InvalidResponseCodeException exception =
new InvalidResponseCodeException(404, Collections.emptyMap(), new DataSpec(Uri.EMPTY));
assertThat(getDefaultPolicyBlacklistOutputFor(exception))
.isEqualTo(ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
.isEqualTo(DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_BLACKLIST_MS);
}
@Test
public void getBlacklistDurationMsFor_blacklist410() throws Exception {
public void getBlacklistDurationMsFor_blacklist410() {
InvalidResponseCodeException exception =
new InvalidResponseCodeException(410, Collections.emptyMap(), new DataSpec(Uri.EMPTY));
assertThat(getDefaultPolicyBlacklistOutputFor(exception))
.isEqualTo(ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
.isEqualTo(DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_BLACKLIST_MS);
}
@Test
public void getBlacklistDurationMsFor_dontBlacklistUnexpectedHttpCodes() throws Exception {
public void getBlacklistDurationMsFor_dontBlacklistUnexpectedHttpCodes() {
InvalidResponseCodeException exception =
new InvalidResponseCodeException(500, Collections.emptyMap(), new DataSpec(Uri.EMPTY));
assertThat(getDefaultPolicyBlacklistOutputFor(exception)).isEqualTo(C.TIME_UNSET);
}
@Test
public void getBlacklistDurationMsFor_dontBlacklistUnexpectedExceptions() throws Exception {
public void getBlacklistDurationMsFor_dontBlacklistUnexpectedExceptions() {
FileNotFoundException exception = new FileNotFoundException();
assertThat(getDefaultPolicyBlacklistOutputFor(exception)).isEqualTo(C.TIME_UNSET);
}
@Test
public void getRetryDelayMsFor_dontRetryParserException() throws Exception {
public void getRetryDelayMsFor_dontRetryParserException() {
assertThat(getDefaultPolicyRetryDelayOutputFor(new ParserException(), 1))
.isEqualTo(C.TIME_UNSET);
}
@Test
public void getRetryDelayMsFor_successiveRetryDelays() throws Exception {
public void getRetryDelayMsFor_successiveRetryDelays() {
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 3)).isEqualTo(2000);
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 5)).isEqualTo(4000);
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 9)).isEqualTo(5000);

View File

@ -42,6 +42,7 @@ import com.google.android.exoplayer2.source.dash.manifest.Period;
import com.google.android.exoplayer2.source.dash.manifest.Representation;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.MimeTypes;
@ -62,7 +63,7 @@ import java.util.List;
/* package */ final int id;
private final DashChunkSource.Factory chunkSourceFactory;
private final @Nullable TransferListener transferListener;
private final int minLoadableRetryCount;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final long elapsedRealtimeOffset;
private final LoaderErrorThrower manifestLoaderErrorThrower;
private final Allocator allocator;
@ -89,7 +90,7 @@ import java.util.List;
int periodIndex,
DashChunkSource.Factory chunkSourceFactory,
@Nullable TransferListener transferListener,
int minLoadableRetryCount,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher,
long elapsedRealtimeOffset,
LoaderErrorThrower manifestLoaderErrorThrower,
@ -101,7 +102,7 @@ import java.util.List;
this.periodIndex = periodIndex;
this.chunkSourceFactory = chunkSourceFactory;
this.transferListener = transferListener;
this.minLoadableRetryCount = minLoadableRetryCount;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.eventDispatcher = eventDispatcher;
this.elapsedRealtimeOffset = elapsedRealtimeOffset;
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
@ -612,7 +613,7 @@ import java.util.List;
this,
allocator,
positionUs,
minLoadableRetryCount,
loadErrorHandlingPolicy,
eventDispatcher);
synchronized (this) {
// The map is also accessed on the loading thread so synchronize access.

View File

@ -43,6 +43,8 @@ import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement;
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.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
@ -77,7 +79,7 @@ public final class DashMediaSource extends BaseMediaSource {
private @Nullable ParsingLoadable.Parser<? extends DashManifest> manifestParser;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private int minLoadableRetryCount;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long livePresentationDelayMs;
private boolean livePresentationDelayOverridesManifest;
private boolean isCreateCalled;
@ -107,7 +109,7 @@ public final class DashMediaSource extends BaseMediaSource {
@Nullable DataSource.Factory manifestDataSourceFactory) {
this.chunkSourceFactory = Assertions.checkNotNull(chunkSourceFactory);
this.manifestDataSourceFactory = manifestDataSourceFactory;
minLoadableRetryCount = DEFAULT_MIN_LOADABLE_RETRY_COUNT;
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS;
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
}
@ -128,16 +130,36 @@ public final class DashMediaSource extends BaseMediaSource {
}
/**
* Sets the minimum number of times to retry if a loading error occurs. The default value is
* {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}.
* 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;
}
@ -225,7 +247,7 @@ public final class DashMediaSource extends BaseMediaSource {
/* manifestParser= */ null,
chunkSourceFactory,
compositeSequenceableLoaderFactory,
minLoadableRetryCount,
loadErrorHandlingPolicy,
livePresentationDelayMs,
livePresentationDelayOverridesManifest,
tag);
@ -266,7 +288,7 @@ public final class DashMediaSource extends BaseMediaSource {
manifestParser,
chunkSourceFactory,
compositeSequenceableLoaderFactory,
minLoadableRetryCount,
loadErrorHandlingPolicy,
livePresentationDelayMs,
livePresentationDelayOverridesManifest,
tag);
@ -294,11 +316,6 @@ public final class DashMediaSource extends BaseMediaSource {
}
}
/**
* The default minimum number of times to retry loading data prior to failing.
*/
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
/**
* The default presentation delay for live streams. The presentation delay is the duration by
* which the default start position precedes the end of the live window.
@ -328,7 +345,7 @@ public final class DashMediaSource extends BaseMediaSource {
private final DataSource.Factory manifestDataSourceFactory;
private final DashChunkSource.Factory chunkSourceFactory;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final int minLoadableRetryCount;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final long livePresentationDelayMs;
private final boolean livePresentationDelayOverridesManifest;
private final EventDispatcher manifestEventDispatcher;
@ -379,7 +396,11 @@ public final class DashMediaSource extends BaseMediaSource {
DashChunkSource.Factory chunkSourceFactory,
Handler eventHandler,
MediaSourceEventListener eventListener) {
this(manifest, chunkSourceFactory, DEFAULT_MIN_LOADABLE_RETRY_COUNT, eventHandler,
this(
manifest,
chunkSourceFactory,
DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT,
eventHandler,
eventListener);
}
@ -407,7 +428,7 @@ public final class DashMediaSource extends BaseMediaSource {
/* manifestParser= */ null,
chunkSourceFactory,
new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount,
new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
DEFAULT_LIVE_PRESENTATION_DELAY_MS,
/* livePresentationDelayOverridesManifest= */ false,
/* tag= */ null);
@ -436,9 +457,14 @@ public final class DashMediaSource extends BaseMediaSource {
DashChunkSource.Factory chunkSourceFactory,
Handler eventHandler,
MediaSourceEventListener eventListener) {
this(manifestUri, manifestDataSourceFactory, chunkSourceFactory,
DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS,
eventHandler, eventListener);
this(
manifestUri,
manifestDataSourceFactory,
chunkSourceFactory,
DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT,
DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS,
eventHandler,
eventListener);
}
/**
@ -515,7 +541,7 @@ public final class DashMediaSource extends BaseMediaSource {
manifestParser,
chunkSourceFactory,
new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount,
new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
livePresentationDelayMs == DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS
? DEFAULT_LIVE_PRESENTATION_DELAY_MS
: livePresentationDelayMs,
@ -533,7 +559,7 @@ public final class DashMediaSource extends BaseMediaSource {
ParsingLoadable.Parser<? extends DashManifest> manifestParser,
DashChunkSource.Factory chunkSourceFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
int minLoadableRetryCount,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
long livePresentationDelayMs,
boolean livePresentationDelayOverridesManifest,
@Nullable Object tag) {
@ -543,7 +569,7 @@ public final class DashMediaSource extends BaseMediaSource {
this.manifestDataSourceFactory = manifestDataSourceFactory;
this.manifestParser = manifestParser;
this.chunkSourceFactory = chunkSourceFactory;
this.minLoadableRetryCount = minLoadableRetryCount;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.livePresentationDelayMs = livePresentationDelayMs;
this.livePresentationDelayOverridesManifest = livePresentationDelayOverridesManifest;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
@ -625,7 +651,7 @@ public final class DashMediaSource extends BaseMediaSource {
periodIndex,
chunkSourceFactory,
mediaTransferListener,
minLoadableRetryCount,
loadErrorHandlingPolicy,
periodEventDispatcher,
elapsedRealtimeOffsetMs,
manifestLoadErrorThrower,
@ -735,7 +761,8 @@ public final class DashMediaSource extends BaseMediaSource {
}
if (isManifestStale) {
if (staleManifestReloadAttempt++ < minLoadableRetryCount) {
if (staleManifestReloadAttempt++
< loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)) {
scheduleManifestRefresh(getManifestLoadRetryDelayMillis());
} else {
manifestFatalError = new DashManifestStaleException();
@ -1004,7 +1031,7 @@ public final class DashMediaSource extends BaseMediaSource {
startLoading(
new ParsingLoadable<>(dataSource, manifestUri, C.DATA_TYPE_MANIFEST, manifestParser),
manifestCallback,
minLoadableRetryCount);
loadErrorHandlingPolicy.getMinimumLoadableRetryCount(C.DATA_TYPE_MANIFEST));
}
private long getManifestLoadRetryDelayMillis() {

View File

@ -34,7 +34,6 @@ import com.google.android.exoplayer2.source.chunk.BaseMediaChunkIterator;
import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper;
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil;
import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk;
import com.google.android.exoplayer2.source.chunk.InitializationChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunk;
@ -380,7 +379,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
@Override
public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) {
public boolean onChunkLoadError(
Chunk chunk, boolean cancelable, Exception e, long blacklistDurationMs) {
if (!cancelable) {
return false;
}
@ -403,9 +403,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
}
}
// Blacklist if appropriate.
return ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection,
trackSelection.indexOf(chunk.trackFormat), e);
return blacklistDurationMs != C.TIME_UNSET
&& trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), blacklistDurationMs);
}
// Internal methods.

View File

@ -23,7 +23,6 @@ import com.google.android.exoplayer2.Format;
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.ChunkedTrackBlacklistUtil;
import com.google.android.exoplayer2.source.chunk.DataChunk;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
@ -33,6 +32,7 @@ import com.google.android.exoplayer2.trackselection.BaseTrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.UriUtil;
@ -429,7 +429,7 @@ import java.util.List;
seenExpectedPlaylistError |= expectedPlaylistUrl == url;
return !shouldBlacklist
|| trackSelection.blacklist(
trackSelectionIndex, ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
trackSelectionIndex, DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_BLACKLIST_MS);
}
// Private methods.

View File

@ -21,11 +21,11 @@ import android.os.SystemClock;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil;
import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
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.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
@ -621,7 +621,7 @@ public final class DefaultHlsPlaylistTracker
*/
private boolean blacklistPlaylist() {
blacklistUntilMs =
SystemClock.elapsedRealtime() + ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS;
SystemClock.elapsedRealtime() + DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_BLACKLIST_MS;
return primaryHlsUrl == playlistUrl && !maybeSelectNewPrimaryUrl();
}
}

View File

@ -27,7 +27,6 @@ import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper;
import com.google.android.exoplayer2.source.chunk.ChunkHolder;
import com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil;
import com.google.android.exoplayer2.source.chunk.ContainerMediaChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunk;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
@ -246,9 +245,11 @@ public class DefaultSsChunkSource implements SsChunkSource {
}
@Override
public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) {
return cancelable && ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection,
trackSelection.indexOf(chunk.trackFormat), e);
public boolean onChunkLoadError(
Chunk chunk, boolean cancelable, Exception e, long blacklistDurationMs) {
return cancelable
&& blacklistDurationMs != C.TIME_UNSET
&& trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), blacklistDurationMs);
}
// Private methods.

View File

@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.ProtectionElement;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.TransferListener;
import java.io.IOException;
@ -48,7 +49,7 @@ import java.util.ArrayList;
private final SsChunkSource.Factory chunkSourceFactory;
private final @Nullable TransferListener transferListener;
private final LoaderErrorThrower manifestLoaderErrorThrower;
private final int minLoadableRetryCount;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final EventDispatcher eventDispatcher;
private final Allocator allocator;
private final TrackGroupArray trackGroups;
@ -66,14 +67,14 @@ import java.util.ArrayList;
SsChunkSource.Factory chunkSourceFactory,
@Nullable TransferListener transferListener,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
int minLoadableRetryCount,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher,
LoaderErrorThrower manifestLoaderErrorThrower,
Allocator allocator) {
this.chunkSourceFactory = chunkSourceFactory;
this.transferListener = transferListener;
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
this.minLoadableRetryCount = minLoadableRetryCount;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.eventDispatcher = eventDispatcher;
this.allocator = allocator;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
@ -238,7 +239,7 @@ import java.util.ArrayList;
this,
allocator,
positionUs,
minLoadableRetryCount,
loadErrorHandlingPolicy,
eventDispatcher);
}

View File

@ -40,6 +40,8 @@ import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestP
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsUtil;
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.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
@ -65,7 +67,7 @@ public final class SsMediaSource extends BaseMediaSource
private @Nullable ParsingLoadable.Parser<? extends SsManifest> manifestParser;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private int minLoadableRetryCount;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long livePresentationDelayMs;
private boolean isCreateCalled;
private @Nullable Object tag;
@ -94,7 +96,7 @@ public final class SsMediaSource extends BaseMediaSource
@Nullable DataSource.Factory manifestDataSourceFactory) {
this.chunkSourceFactory = Assertions.checkNotNull(chunkSourceFactory);
this.manifestDataSourceFactory = manifestDataSourceFactory;
minLoadableRetryCount = DEFAULT_MIN_LOADABLE_RETRY_COUNT;
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS;
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
}
@ -114,16 +116,36 @@ public final class SsMediaSource extends BaseMediaSource
}
/**
* Sets the minimum number of times to retry if a loading error occurs. The default value is
* {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}.
* 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;
}
@ -193,7 +215,7 @@ public final class SsMediaSource extends BaseMediaSource
/* manifestParser= */ null,
chunkSourceFactory,
compositeSequenceableLoaderFactory,
minLoadableRetryCount,
loadErrorHandlingPolicy,
livePresentationDelayMs,
tag);
}
@ -233,7 +255,7 @@ public final class SsMediaSource extends BaseMediaSource
manifestParser,
chunkSourceFactory,
compositeSequenceableLoaderFactory,
minLoadableRetryCount,
loadErrorHandlingPolicy,
livePresentationDelayMs,
tag);
}
@ -261,10 +283,6 @@ public final class SsMediaSource extends BaseMediaSource
}
/**
* The default minimum number of times to retry loading data prior to failing.
*/
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
/**
* The default presentation delay for live streams. The presentation delay is the duration by
* which the default start position precedes the end of the live window.
@ -285,7 +303,7 @@ public final class SsMediaSource extends BaseMediaSource
private final DataSource.Factory manifestDataSourceFactory;
private final SsChunkSource.Factory chunkSourceFactory;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final int minLoadableRetryCount;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final long livePresentationDelayMs;
private final EventDispatcher manifestEventDispatcher;
private final ParsingLoadable.Parser<? extends SsManifest> manifestParser;
@ -317,8 +335,12 @@ public final class SsMediaSource extends BaseMediaSource
SsChunkSource.Factory chunkSourceFactory,
Handler eventHandler,
MediaSourceEventListener eventListener) {
this(manifest, chunkSourceFactory, DEFAULT_MIN_LOADABLE_RETRY_COUNT,
eventHandler, eventListener);
this(
manifest,
chunkSourceFactory,
DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT,
eventHandler,
eventListener);
}
/**
@ -345,7 +367,7 @@ public final class SsMediaSource extends BaseMediaSource
/* manifestParser= */ null,
chunkSourceFactory,
new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount,
new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
DEFAULT_LIVE_PRESENTATION_DELAY_MS,
/* tag= */ null);
if (eventHandler != null && eventListener != null) {
@ -372,8 +394,13 @@ public final class SsMediaSource extends BaseMediaSource
SsChunkSource.Factory chunkSourceFactory,
Handler eventHandler,
MediaSourceEventListener eventListener) {
this(manifestUri, manifestDataSourceFactory, chunkSourceFactory,
DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_PRESENTATION_DELAY_MS, eventHandler,
this(
manifestUri,
manifestDataSourceFactory,
chunkSourceFactory,
DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT,
DEFAULT_LIVE_PRESENTATION_DELAY_MS,
eventHandler,
eventListener);
}
@ -438,7 +465,7 @@ public final class SsMediaSource extends BaseMediaSource
manifestParser,
chunkSourceFactory,
new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount,
new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
livePresentationDelayMs,
/* tag= */ null);
if (eventHandler != null && eventListener != null) {
@ -453,7 +480,7 @@ public final class SsMediaSource extends BaseMediaSource
ParsingLoadable.Parser<? extends SsManifest> manifestParser,
SsChunkSource.Factory chunkSourceFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
int minLoadableRetryCount,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
long livePresentationDelayMs,
@Nullable Object tag) {
Assertions.checkState(manifest == null || !manifest.isLive);
@ -463,7 +490,7 @@ public final class SsMediaSource extends BaseMediaSource
this.manifestParser = manifestParser;
this.chunkSourceFactory = chunkSourceFactory;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
this.minLoadableRetryCount = minLoadableRetryCount;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.livePresentationDelayMs = livePresentationDelayMs;
this.manifestEventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
this.tag = tag;
@ -506,7 +533,7 @@ public final class SsMediaSource extends BaseMediaSource
chunkSourceFactory,
mediaTransferListener,
compositeSequenceableLoaderFactory,
minLoadableRetryCount,
loadErrorHandlingPolicy,
eventDispatcher,
manifestLoaderErrorThrower,
allocator);
@ -668,7 +695,9 @@ public final class SsMediaSource extends BaseMediaSource
private void startLoadingManifest() {
ParsingLoadable<SsManifest> loadable = new ParsingLoadable<>(manifestDataSource,
manifestUri, C.DATA_TYPE_MANIFEST, manifestParser);
long elapsedRealtimeMs = manifestLoader.startLoading(loadable, this, minLoadableRetryCount);
long elapsedRealtimeMs =
manifestLoader.startLoading(
loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type));
manifestEventDispatcher.loadStarted(
loadable.dataSpec, loadable.dataSpec.uri, loadable.type, elapsedRealtimeMs);
}

View File

@ -138,7 +138,8 @@ public final class FakeChunkSource implements ChunkSource {
}
@Override
public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) {
public boolean onChunkLoadError(
Chunk chunk, boolean cancelable, Exception e, long blacklistDurationMs) {
return false;
}