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 DataSpec dataSpec;
private final DataSource.Factory dataSourceFactory; private final DataSource.Factory dataSourceFactory;
private final Format format; private final Format format;
@ -204,7 +199,12 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
@Deprecated @Deprecated
public SingleSampleMediaSource( public SingleSampleMediaSource(
Uri uri, DataSource.Factory dataSourceFactory, Format format, long durationUs) { 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.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.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
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.LoadErrorAction;
import com.google.android.exoplayer2.util.Assertions; 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 T chunkSource;
private final SequenceableLoader.Callback<ChunkSampleStream<T>> callback; private final SequenceableLoader.Callback<ChunkSampleStream<T>> callback;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final int minLoadableRetryCount; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final Loader loader; private final Loader loader;
private final ChunkHolder nextChunkHolder; private final ChunkHolder nextChunkHolder;
private final ArrayList<BaseMediaChunk> mediaChunks; private final ArrayList<BaseMediaChunk> mediaChunks;
@ -81,6 +83,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
/* package */ boolean loadingFinished; /* package */ boolean loadingFinished;
/** /**
* Constructs an instance.
*
* @param primaryTrackType The type of the primary track. One of the {@link C} {@code * @param primaryTrackType The type of the primary track. One of the {@link C} {@code
* TRACK_TYPE_*} constants. * TRACK_TYPE_*} constants.
* @param embeddedTrackTypes The types of any embedded tracks, or null. * @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 * @param minLoadableRetryCount The minimum number of times that the source should retry a load
* before propagating an error. * before propagating an error.
* @param eventDispatcher A dispatcher to notify of events. * @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( public ChunkSampleStream(
int primaryTrackType, int primaryTrackType,
int[] embeddedTrackTypes, int[] embeddedTrackTypes,
@ -103,13 +110,49 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
long positionUs, long positionUs,
int minLoadableRetryCount, int minLoadableRetryCount,
EventDispatcher eventDispatcher) { 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.primaryTrackType = primaryTrackType;
this.embeddedTrackTypes = embeddedTrackTypes; this.embeddedTrackTypes = embeddedTrackTypes;
this.embeddedTrackFormats = embeddedTrackFormats; this.embeddedTrackFormats = embeddedTrackFormats;
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
this.callback = callback; this.callback = callback;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.minLoadableRetryCount = minLoadableRetryCount; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
loader = new Loader("Loader:ChunkSampleStream"); loader = new Loader("Loader:ChunkSampleStream");
nextChunkHolder = new ChunkHolder(); nextChunkHolder = new ChunkHolder();
mediaChunks = new ArrayList<>(); mediaChunks = new ArrayList<>();
@ -439,12 +482,15 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
int lastChunkIndex = mediaChunks.size() - 1; int lastChunkIndex = mediaChunks.size() - 1;
boolean cancelable = boolean cancelable =
bytesLoaded == 0 || !isMediaChunk || !haveReadFromMediaChunk(lastChunkIndex); bytesLoaded == 0 || !isMediaChunk || !haveReadFromMediaChunk(lastChunkIndex);
boolean canceled = false; long blacklistDurationMs =
if (chunkSource.onChunkLoadError(loadable, cancelable, error)) { cancelable
if (!cancelable) { ? loadErrorHandlingPolicy.getBlacklistDurationMsFor(
Log.w(TAG, "Ignoring attempt to cancel non-cancelable load."); loadable.type, loadDurationMs, error, errorCount)
} else { : C.TIME_UNSET;
canceled = true; LoadErrorAction loadErrorAction = null;
if (chunkSource.onChunkLoadError(loadable, cancelable, error, blacklistDurationMs)) {
if (cancelable) {
loadErrorAction = Loader.DONT_RETRY;
if (isMediaChunk) { if (isMediaChunk) {
BaseMediaChunk removed = discardUpstreamMediaChunksFromIndex(lastChunkIndex); BaseMediaChunk removed = discardUpstreamMediaChunksFromIndex(lastChunkIndex);
Assertions.checkState(removed == loadable); Assertions.checkState(removed == loadable);
@ -452,8 +498,23 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
pendingResetPositionUs = lastSeekPositionUs; 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( eventDispatcher.loadError(
loadable.dataSpec, loadable.dataSpec,
loadable.getUri(), loadable.getUri(),
@ -471,10 +532,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
canceled); canceled);
if (canceled) { if (canceled) {
callback.onContinueLoadingRequested(this); callback.onContinueLoadingRequested(this);
return Loader.DONT_RETRY;
} else {
return Loader.RETRY;
} }
return loadErrorAction;
} }
// SequenceableLoader implementation // SequenceableLoader implementation
@ -521,7 +580,9 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
mediaChunk.init(mediaChunkOutput); mediaChunk.init(mediaChunkOutput);
mediaChunks.add(mediaChunk); mediaChunks.add(mediaChunk);
} }
long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount); long elapsedRealtimeMs =
loader.startLoading(
loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type));
eventDispatcher.loadStarted( eventDispatcher.loadStarted(
loadable.dataSpec, loadable.dataSpec,
loadable.dataSpec.uri, loadable.dataSpec.uri,

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.chunk; package com.google.android.exoplayer2.source.chunk;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SeekParameters;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -83,8 +84,8 @@ public interface ChunkSource {
/** /**
* Called when the {@link ChunkSampleStream} has finished loading a chunk obtained from this * Called when the {@link ChunkSampleStream} has finished loading a chunk obtained from this
* source. * 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. * @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 * Called when the {@link ChunkSampleStream} encounters an error loading a chunk obtained from
* this source. * 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 chunk The chunk whose load encountered the error.
* @param cancelable Whether the load can be canceled. * @param cancelable Whether the load can be canceled.
* @param e The error. * @param e The error.
* @return Whether the load should be canceled. Should always be false if {@code cancelable} is * @param blacklistDurationMs The duration for which the associated track may be blacklisted, or
* false. * {@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.C;
import com.google.android.exoplayer2.ParserException; 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.HttpDataSource.InvalidResponseCodeException;
import java.io.IOException; import java.io.IOException;
@ -31,6 +30,8 @@ public final class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPo
* streams. * streams.
*/ */
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_PROGRESSIVE_LIVE = 6; 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; 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 * Blacklists resources whose load error was an {@link InvalidResponseCodeException} with response
* code HTTP 404 or 410. The duration of the blacklisting is {@link * code HTTP 404 or 410. The duration of the blacklisting is {@link #DEFAULT_TRACK_BLACKLIST_MS}.
* ChunkedTrackBlacklistUtil#DEFAULT_TRACK_BLACKLIST_MS}.
*/ */
@Override @Override
public long getBlacklistDurationMsFor( public long getBlacklistDurationMsFor(
@ -69,7 +69,7 @@ public final class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPo
int responseCode = ((InvalidResponseCodeException) exception).responseCode; int responseCode = ((InvalidResponseCodeException) exception).responseCode;
return responseCode == 404 // HTTP 404 Not Found. return responseCode == 404 // HTTP 404 Not Found.
|| responseCode == 410 // HTTP 410 Gone. || responseCode == 410 // HTTP 410 Gone.
? ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS ? DEFAULT_TRACK_BLACKLIST_MS
: C.TIME_UNSET; : C.TIME_UNSET;
} }
return 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 android.net.Uri;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; 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.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer2.upstream.Loader.Loadable;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -34,56 +32,43 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public final class DefaultLoadErrorHandlingPolicyTest { 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 @Test
public void getBlacklistDurationMsFor_blacklist404() throws Exception { public void getBlacklistDurationMsFor_blacklist404() {
InvalidResponseCodeException exception = InvalidResponseCodeException exception =
new InvalidResponseCodeException(404, Collections.emptyMap(), new DataSpec(Uri.EMPTY)); new InvalidResponseCodeException(404, Collections.emptyMap(), new DataSpec(Uri.EMPTY));
assertThat(getDefaultPolicyBlacklistOutputFor(exception)) assertThat(getDefaultPolicyBlacklistOutputFor(exception))
.isEqualTo(ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS); .isEqualTo(DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_BLACKLIST_MS);
} }
@Test @Test
public void getBlacklistDurationMsFor_blacklist410() throws Exception { public void getBlacklistDurationMsFor_blacklist410() {
InvalidResponseCodeException exception = InvalidResponseCodeException exception =
new InvalidResponseCodeException(410, Collections.emptyMap(), new DataSpec(Uri.EMPTY)); new InvalidResponseCodeException(410, Collections.emptyMap(), new DataSpec(Uri.EMPTY));
assertThat(getDefaultPolicyBlacklistOutputFor(exception)) assertThat(getDefaultPolicyBlacklistOutputFor(exception))
.isEqualTo(ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS); .isEqualTo(DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_BLACKLIST_MS);
} }
@Test @Test
public void getBlacklistDurationMsFor_dontBlacklistUnexpectedHttpCodes() throws Exception { public void getBlacklistDurationMsFor_dontBlacklistUnexpectedHttpCodes() {
InvalidResponseCodeException exception = InvalidResponseCodeException exception =
new InvalidResponseCodeException(500, Collections.emptyMap(), new DataSpec(Uri.EMPTY)); new InvalidResponseCodeException(500, Collections.emptyMap(), new DataSpec(Uri.EMPTY));
assertThat(getDefaultPolicyBlacklistOutputFor(exception)).isEqualTo(C.TIME_UNSET); assertThat(getDefaultPolicyBlacklistOutputFor(exception)).isEqualTo(C.TIME_UNSET);
} }
@Test @Test
public void getBlacklistDurationMsFor_dontBlacklistUnexpectedExceptions() throws Exception { public void getBlacklistDurationMsFor_dontBlacklistUnexpectedExceptions() {
FileNotFoundException exception = new FileNotFoundException(); FileNotFoundException exception = new FileNotFoundException();
assertThat(getDefaultPolicyBlacklistOutputFor(exception)).isEqualTo(C.TIME_UNSET); assertThat(getDefaultPolicyBlacklistOutputFor(exception)).isEqualTo(C.TIME_UNSET);
} }
@Test @Test
public void getRetryDelayMsFor_dontRetryParserException() throws Exception { public void getRetryDelayMsFor_dontRetryParserException() {
assertThat(getDefaultPolicyRetryDelayOutputFor(new ParserException(), 1)) assertThat(getDefaultPolicyRetryDelayOutputFor(new ParserException(), 1))
.isEqualTo(C.TIME_UNSET); .isEqualTo(C.TIME_UNSET);
} }
@Test @Test
public void getRetryDelayMsFor_successiveRetryDelays() throws Exception { public void getRetryDelayMsFor_successiveRetryDelays() {
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 3)).isEqualTo(2000); assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 3)).isEqualTo(2000);
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 5)).isEqualTo(4000); assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 5)).isEqualTo(4000);
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 9)).isEqualTo(5000); 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.source.dash.manifest.Representation;
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.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower; import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
@ -62,7 +63,7 @@ import java.util.List;
/* package */ final int id; /* package */ final int id;
private final DashChunkSource.Factory chunkSourceFactory; private final DashChunkSource.Factory chunkSourceFactory;
private final @Nullable TransferListener transferListener; private final @Nullable TransferListener transferListener;
private final int minLoadableRetryCount; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final long elapsedRealtimeOffset; private final long elapsedRealtimeOffset;
private final LoaderErrorThrower manifestLoaderErrorThrower; private final LoaderErrorThrower manifestLoaderErrorThrower;
private final Allocator allocator; private final Allocator allocator;
@ -89,7 +90,7 @@ import java.util.List;
int periodIndex, int periodIndex,
DashChunkSource.Factory chunkSourceFactory, DashChunkSource.Factory chunkSourceFactory,
@Nullable TransferListener transferListener, @Nullable TransferListener transferListener,
int minLoadableRetryCount, LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher, EventDispatcher eventDispatcher,
long elapsedRealtimeOffset, long elapsedRealtimeOffset,
LoaderErrorThrower manifestLoaderErrorThrower, LoaderErrorThrower manifestLoaderErrorThrower,
@ -101,7 +102,7 @@ import java.util.List;
this.periodIndex = periodIndex; this.periodIndex = periodIndex;
this.chunkSourceFactory = chunkSourceFactory; this.chunkSourceFactory = chunkSourceFactory;
this.transferListener = transferListener; this.transferListener = transferListener;
this.minLoadableRetryCount = minLoadableRetryCount; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.elapsedRealtimeOffset = elapsedRealtimeOffset; this.elapsedRealtimeOffset = elapsedRealtimeOffset;
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
@ -612,7 +613,7 @@ import java.util.List;
this, this,
allocator, allocator,
positionUs, positionUs,
minLoadableRetryCount, loadErrorHandlingPolicy,
eventDispatcher); eventDispatcher);
synchronized (this) { synchronized (this) {
// The map is also accessed on the loading thread so synchronize access. // 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.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.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
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.LoadErrorAction;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower; 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 @Nullable ParsingLoadable.Parser<? extends DashManifest> manifestParser;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private int minLoadableRetryCount; private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long livePresentationDelayMs; private long livePresentationDelayMs;
private boolean livePresentationDelayOverridesManifest; private boolean livePresentationDelayOverridesManifest;
private boolean isCreateCalled; private boolean isCreateCalled;
@ -107,7 +109,7 @@ public final class DashMediaSource extends BaseMediaSource {
@Nullable DataSource.Factory manifestDataSourceFactory) { @Nullable DataSource.Factory manifestDataSourceFactory) {
this.chunkSourceFactory = Assertions.checkNotNull(chunkSourceFactory); this.chunkSourceFactory = Assertions.checkNotNull(chunkSourceFactory);
this.manifestDataSourceFactory = manifestDataSourceFactory; this.manifestDataSourceFactory = manifestDataSourceFactory;
minLoadableRetryCount = DEFAULT_MIN_LOADABLE_RETRY_COUNT; loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS; livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS;
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); 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 * Sets the minimum number of times to retry if a loading error occurs. See {@link
* {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}. * #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. * @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
* @return This factory, for convenience. * @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called. * @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) { 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); Assertions.checkState(!isCreateCalled);
this.minLoadableRetryCount = minLoadableRetryCount; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
return this; return this;
} }
@ -225,7 +247,7 @@ public final class DashMediaSource extends BaseMediaSource {
/* manifestParser= */ null, /* manifestParser= */ null,
chunkSourceFactory, chunkSourceFactory,
compositeSequenceableLoaderFactory, compositeSequenceableLoaderFactory,
minLoadableRetryCount, loadErrorHandlingPolicy,
livePresentationDelayMs, livePresentationDelayMs,
livePresentationDelayOverridesManifest, livePresentationDelayOverridesManifest,
tag); tag);
@ -266,7 +288,7 @@ public final class DashMediaSource extends BaseMediaSource {
manifestParser, manifestParser,
chunkSourceFactory, chunkSourceFactory,
compositeSequenceableLoaderFactory, compositeSequenceableLoaderFactory,
minLoadableRetryCount, loadErrorHandlingPolicy,
livePresentationDelayMs, livePresentationDelayMs,
livePresentationDelayOverridesManifest, livePresentationDelayOverridesManifest,
tag); 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 * 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. * 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 DataSource.Factory manifestDataSourceFactory;
private final DashChunkSource.Factory chunkSourceFactory; private final DashChunkSource.Factory chunkSourceFactory;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final int minLoadableRetryCount; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final long livePresentationDelayMs; private final long livePresentationDelayMs;
private final boolean livePresentationDelayOverridesManifest; private final boolean livePresentationDelayOverridesManifest;
private final EventDispatcher manifestEventDispatcher; private final EventDispatcher manifestEventDispatcher;
@ -379,7 +396,11 @@ public final class DashMediaSource extends BaseMediaSource {
DashChunkSource.Factory chunkSourceFactory, DashChunkSource.Factory chunkSourceFactory,
Handler eventHandler, Handler eventHandler,
MediaSourceEventListener eventListener) { MediaSourceEventListener eventListener) {
this(manifest, chunkSourceFactory, DEFAULT_MIN_LOADABLE_RETRY_COUNT, eventHandler, this(
manifest,
chunkSourceFactory,
DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT,
eventHandler,
eventListener); eventListener);
} }
@ -407,7 +428,7 @@ public final class DashMediaSource extends BaseMediaSource {
/* manifestParser= */ null, /* manifestParser= */ null,
chunkSourceFactory, chunkSourceFactory,
new DefaultCompositeSequenceableLoaderFactory(), new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount, new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
DEFAULT_LIVE_PRESENTATION_DELAY_MS, DEFAULT_LIVE_PRESENTATION_DELAY_MS,
/* livePresentationDelayOverridesManifest= */ false, /* livePresentationDelayOverridesManifest= */ false,
/* tag= */ null); /* tag= */ null);
@ -436,9 +457,14 @@ public final class DashMediaSource extends BaseMediaSource {
DashChunkSource.Factory chunkSourceFactory, DashChunkSource.Factory chunkSourceFactory,
Handler eventHandler, Handler eventHandler,
MediaSourceEventListener eventListener) { MediaSourceEventListener eventListener) {
this(manifestUri, manifestDataSourceFactory, chunkSourceFactory, this(
DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS, manifestUri,
eventHandler, eventListener); 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, manifestParser,
chunkSourceFactory, chunkSourceFactory,
new DefaultCompositeSequenceableLoaderFactory(), new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount, new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
livePresentationDelayMs == DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS livePresentationDelayMs == DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS
? DEFAULT_LIVE_PRESENTATION_DELAY_MS ? DEFAULT_LIVE_PRESENTATION_DELAY_MS
: livePresentationDelayMs, : livePresentationDelayMs,
@ -533,7 +559,7 @@ public final class DashMediaSource extends BaseMediaSource {
ParsingLoadable.Parser<? extends DashManifest> manifestParser, ParsingLoadable.Parser<? extends DashManifest> manifestParser,
DashChunkSource.Factory chunkSourceFactory, DashChunkSource.Factory chunkSourceFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
int minLoadableRetryCount, LoadErrorHandlingPolicy loadErrorHandlingPolicy,
long livePresentationDelayMs, long livePresentationDelayMs,
boolean livePresentationDelayOverridesManifest, boolean livePresentationDelayOverridesManifest,
@Nullable Object tag) { @Nullable Object tag) {
@ -543,7 +569,7 @@ public final class DashMediaSource extends BaseMediaSource {
this.manifestDataSourceFactory = manifestDataSourceFactory; this.manifestDataSourceFactory = manifestDataSourceFactory;
this.manifestParser = manifestParser; this.manifestParser = manifestParser;
this.chunkSourceFactory = chunkSourceFactory; this.chunkSourceFactory = chunkSourceFactory;
this.minLoadableRetryCount = minLoadableRetryCount; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.livePresentationDelayMs = livePresentationDelayMs; this.livePresentationDelayMs = livePresentationDelayMs;
this.livePresentationDelayOverridesManifest = livePresentationDelayOverridesManifest; this.livePresentationDelayOverridesManifest = livePresentationDelayOverridesManifest;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
@ -625,7 +651,7 @@ public final class DashMediaSource extends BaseMediaSource {
periodIndex, periodIndex,
chunkSourceFactory, chunkSourceFactory,
mediaTransferListener, mediaTransferListener,
minLoadableRetryCount, loadErrorHandlingPolicy,
periodEventDispatcher, periodEventDispatcher,
elapsedRealtimeOffsetMs, elapsedRealtimeOffsetMs,
manifestLoadErrorThrower, manifestLoadErrorThrower,
@ -735,7 +761,8 @@ public final class DashMediaSource extends BaseMediaSource {
} }
if (isManifestStale) { if (isManifestStale) {
if (staleManifestReloadAttempt++ < minLoadableRetryCount) { if (staleManifestReloadAttempt++
< loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)) {
scheduleManifestRefresh(getManifestLoadRetryDelayMillis()); scheduleManifestRefresh(getManifestLoadRetryDelayMillis());
} else { } else {
manifestFatalError = new DashManifestStaleException(); manifestFatalError = new DashManifestStaleException();
@ -1004,7 +1031,7 @@ public final class DashMediaSource extends BaseMediaSource {
startLoading( startLoading(
new ParsingLoadable<>(dataSource, manifestUri, C.DATA_TYPE_MANIFEST, manifestParser), new ParsingLoadable<>(dataSource, manifestUri, C.DATA_TYPE_MANIFEST, manifestParser),
manifestCallback, manifestCallback,
minLoadableRetryCount); loadErrorHandlingPolicy.getMinimumLoadableRetryCount(C.DATA_TYPE_MANIFEST));
} }
private long getManifestLoadRetryDelayMillis() { 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.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper; import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper;
import com.google.android.exoplayer2.source.chunk.ChunkHolder; 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.ContainerMediaChunk;
import com.google.android.exoplayer2.source.chunk.InitializationChunk; import com.google.android.exoplayer2.source.chunk.InitializationChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunk;
@ -380,7 +379,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
} }
@Override @Override
public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) { public boolean onChunkLoadError(
Chunk chunk, boolean cancelable, Exception e, long blacklistDurationMs) {
if (!cancelable) { if (!cancelable) {
return false; return false;
} }
@ -403,9 +403,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
} }
} }
} }
// Blacklist if appropriate. return blacklistDurationMs != C.TIME_UNSET
return ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection, && trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), blacklistDurationMs);
trackSelection.indexOf(chunk.trackFormat), e);
} }
// Internal methods. // 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.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.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.HlsMasterPlaylist.HlsUrl; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist; 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.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.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.UriUtil; import com.google.android.exoplayer2.util.UriUtil;
@ -429,7 +429,7 @@ import java.util.List;
seenExpectedPlaylistError |= expectedPlaylistUrl == url; seenExpectedPlaylistError |= expectedPlaylistUrl == url;
return !shouldBlacklist return !shouldBlacklist
|| trackSelection.blacklist( || trackSelection.blacklist(
trackSelectionIndex, ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS); trackSelectionIndex, DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_BLACKLIST_MS);
} }
// Private methods. // Private methods.

View File

@ -21,11 +21,11 @@ import android.os.SystemClock;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; 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.HlsDataSourceFactory;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl; 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.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.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
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.LoadErrorAction;
@ -621,7 +621,7 @@ public final class DefaultHlsPlaylistTracker
*/ */
private boolean blacklistPlaylist() { private boolean blacklistPlaylist() {
blacklistUntilMs = blacklistUntilMs =
SystemClock.elapsedRealtime() + ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS; SystemClock.elapsedRealtime() + DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_BLACKLIST_MS;
return primaryHlsUrl == playlistUrl && !maybeSelectNewPrimaryUrl(); 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.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper; import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper;
import com.google.android.exoplayer2.source.chunk.ChunkHolder; 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.ContainerMediaChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunk;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
@ -246,9 +245,11 @@ public class DefaultSsChunkSource implements SsChunkSource {
} }
@Override @Override
public boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e) { public boolean onChunkLoadError(
return cancelable && ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection, Chunk chunk, boolean cancelable, Exception e, long blacklistDurationMs) {
trackSelection.indexOf(chunk.trackFormat), e); return cancelable
&& blacklistDurationMs != C.TIME_UNSET
&& trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), blacklistDurationMs);
} }
// Private methods. // 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.source.smoothstreaming.manifest.SsManifest.ProtectionElement;
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.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower; import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import java.io.IOException; import java.io.IOException;
@ -48,7 +49,7 @@ import java.util.ArrayList;
private final SsChunkSource.Factory chunkSourceFactory; private final SsChunkSource.Factory chunkSourceFactory;
private final @Nullable TransferListener transferListener; private final @Nullable TransferListener transferListener;
private final LoaderErrorThrower manifestLoaderErrorThrower; private final LoaderErrorThrower manifestLoaderErrorThrower;
private final int minLoadableRetryCount; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final Allocator allocator; private final Allocator allocator;
private final TrackGroupArray trackGroups; private final TrackGroupArray trackGroups;
@ -66,14 +67,14 @@ import java.util.ArrayList;
SsChunkSource.Factory chunkSourceFactory, SsChunkSource.Factory chunkSourceFactory,
@Nullable TransferListener transferListener, @Nullable TransferListener transferListener,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
int minLoadableRetryCount, LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher, EventDispatcher eventDispatcher,
LoaderErrorThrower manifestLoaderErrorThrower, LoaderErrorThrower manifestLoaderErrorThrower,
Allocator allocator) { Allocator allocator) {
this.chunkSourceFactory = chunkSourceFactory; this.chunkSourceFactory = chunkSourceFactory;
this.transferListener = transferListener; this.transferListener = transferListener;
this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower;
this.minLoadableRetryCount = minLoadableRetryCount; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.allocator = allocator; this.allocator = allocator;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
@ -238,7 +239,7 @@ import java.util.ArrayList;
this, this,
allocator, allocator,
positionUs, positionUs,
minLoadableRetryCount, loadErrorHandlingPolicy,
eventDispatcher); 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.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.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
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.LoadErrorAction;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower; 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 @Nullable ParsingLoadable.Parser<? extends SsManifest> manifestParser;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private int minLoadableRetryCount; private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private long livePresentationDelayMs; private long livePresentationDelayMs;
private boolean isCreateCalled; private boolean isCreateCalled;
private @Nullable Object tag; private @Nullable Object tag;
@ -94,7 +96,7 @@ public final class SsMediaSource extends BaseMediaSource
@Nullable DataSource.Factory manifestDataSourceFactory) { @Nullable DataSource.Factory manifestDataSourceFactory) {
this.chunkSourceFactory = Assertions.checkNotNull(chunkSourceFactory); this.chunkSourceFactory = Assertions.checkNotNull(chunkSourceFactory);
this.manifestDataSourceFactory = manifestDataSourceFactory; this.manifestDataSourceFactory = manifestDataSourceFactory;
minLoadableRetryCount = DEFAULT_MIN_LOADABLE_RETRY_COUNT; loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS; livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS;
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); 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 * Sets the minimum number of times to retry if a loading error occurs. See {@link
* {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}. * #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. * @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
* @return This factory, for convenience. * @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called. * @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) { 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); Assertions.checkState(!isCreateCalled);
this.minLoadableRetryCount = minLoadableRetryCount; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
return this; return this;
} }
@ -193,7 +215,7 @@ public final class SsMediaSource extends BaseMediaSource
/* manifestParser= */ null, /* manifestParser= */ null,
chunkSourceFactory, chunkSourceFactory,
compositeSequenceableLoaderFactory, compositeSequenceableLoaderFactory,
minLoadableRetryCount, loadErrorHandlingPolicy,
livePresentationDelayMs, livePresentationDelayMs,
tag); tag);
} }
@ -233,7 +255,7 @@ public final class SsMediaSource extends BaseMediaSource
manifestParser, manifestParser,
chunkSourceFactory, chunkSourceFactory,
compositeSequenceableLoaderFactory, compositeSequenceableLoaderFactory,
minLoadableRetryCount, loadErrorHandlingPolicy,
livePresentationDelayMs, livePresentationDelayMs,
tag); 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 * 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. * 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 DataSource.Factory manifestDataSourceFactory;
private final SsChunkSource.Factory chunkSourceFactory; private final SsChunkSource.Factory chunkSourceFactory;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final int minLoadableRetryCount; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final long livePresentationDelayMs; private final long livePresentationDelayMs;
private final EventDispatcher manifestEventDispatcher; private final EventDispatcher manifestEventDispatcher;
private final ParsingLoadable.Parser<? extends SsManifest> manifestParser; private final ParsingLoadable.Parser<? extends SsManifest> manifestParser;
@ -317,8 +335,12 @@ public final class SsMediaSource extends BaseMediaSource
SsChunkSource.Factory chunkSourceFactory, SsChunkSource.Factory chunkSourceFactory,
Handler eventHandler, Handler eventHandler,
MediaSourceEventListener eventListener) { MediaSourceEventListener eventListener) {
this(manifest, chunkSourceFactory, DEFAULT_MIN_LOADABLE_RETRY_COUNT, this(
eventHandler, eventListener); manifest,
chunkSourceFactory,
DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT,
eventHandler,
eventListener);
} }
/** /**
@ -345,7 +367,7 @@ public final class SsMediaSource extends BaseMediaSource
/* manifestParser= */ null, /* manifestParser= */ null,
chunkSourceFactory, chunkSourceFactory,
new DefaultCompositeSequenceableLoaderFactory(), new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount, new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
DEFAULT_LIVE_PRESENTATION_DELAY_MS, DEFAULT_LIVE_PRESENTATION_DELAY_MS,
/* tag= */ null); /* tag= */ null);
if (eventHandler != null && eventListener != null) { if (eventHandler != null && eventListener != null) {
@ -372,8 +394,13 @@ public final class SsMediaSource extends BaseMediaSource
SsChunkSource.Factory chunkSourceFactory, SsChunkSource.Factory chunkSourceFactory,
Handler eventHandler, Handler eventHandler,
MediaSourceEventListener eventListener) { MediaSourceEventListener eventListener) {
this(manifestUri, manifestDataSourceFactory, chunkSourceFactory, this(
DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_PRESENTATION_DELAY_MS, eventHandler, manifestUri,
manifestDataSourceFactory,
chunkSourceFactory,
DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT,
DEFAULT_LIVE_PRESENTATION_DELAY_MS,
eventHandler,
eventListener); eventListener);
} }
@ -438,7 +465,7 @@ public final class SsMediaSource extends BaseMediaSource
manifestParser, manifestParser,
chunkSourceFactory, chunkSourceFactory,
new DefaultCompositeSequenceableLoaderFactory(), new DefaultCompositeSequenceableLoaderFactory(),
minLoadableRetryCount, new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount),
livePresentationDelayMs, livePresentationDelayMs,
/* tag= */ null); /* tag= */ null);
if (eventHandler != null && eventListener != null) { if (eventHandler != null && eventListener != null) {
@ -453,7 +480,7 @@ public final class SsMediaSource extends BaseMediaSource
ParsingLoadable.Parser<? extends SsManifest> manifestParser, ParsingLoadable.Parser<? extends SsManifest> manifestParser,
SsChunkSource.Factory chunkSourceFactory, SsChunkSource.Factory chunkSourceFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
int minLoadableRetryCount, LoadErrorHandlingPolicy loadErrorHandlingPolicy,
long livePresentationDelayMs, long livePresentationDelayMs,
@Nullable Object tag) { @Nullable Object tag) {
Assertions.checkState(manifest == null || !manifest.isLive); Assertions.checkState(manifest == null || !manifest.isLive);
@ -463,7 +490,7 @@ public final class SsMediaSource extends BaseMediaSource
this.manifestParser = manifestParser; this.manifestParser = manifestParser;
this.chunkSourceFactory = chunkSourceFactory; this.chunkSourceFactory = chunkSourceFactory;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
this.minLoadableRetryCount = minLoadableRetryCount; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.livePresentationDelayMs = livePresentationDelayMs; this.livePresentationDelayMs = livePresentationDelayMs;
this.manifestEventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); this.manifestEventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
this.tag = tag; this.tag = tag;
@ -506,7 +533,7 @@ public final class SsMediaSource extends BaseMediaSource
chunkSourceFactory, chunkSourceFactory,
mediaTransferListener, mediaTransferListener,
compositeSequenceableLoaderFactory, compositeSequenceableLoaderFactory,
minLoadableRetryCount, loadErrorHandlingPolicy,
eventDispatcher, eventDispatcher,
manifestLoaderErrorThrower, manifestLoaderErrorThrower,
allocator); allocator);
@ -668,7 +695,9 @@ public final class SsMediaSource extends BaseMediaSource
private void startLoadingManifest() { private void startLoadingManifest() {
ParsingLoadable<SsManifest> loadable = new ParsingLoadable<>(manifestDataSource, ParsingLoadable<SsManifest> loadable = new ParsingLoadable<>(manifestDataSource,
manifestUri, C.DATA_TYPE_MANIFEST, manifestParser); 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( manifestEventDispatcher.loadStarted(
loadable.dataSpec, loadable.dataSpec.uri, loadable.type, elapsedRealtimeMs); loadable.dataSpec, loadable.dataSpec.uri, loadable.type, elapsedRealtimeMs);
} }

View File

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