diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java index 674e265739..774e91eee8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java @@ -384,8 +384,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { multiSession, /* useDrmSessionsForClearContentTrackTypes= */ new int[0], /* playClearSamplesWithoutKeys= */ false, - new DefaultLoadErrorHandlingPolicy( - initialDrmRequestRetryCount, /* locationExclusionEnabled= */ false), + new DefaultLoadErrorHandlingPolicy(initialDrmRequestRetryCount), DEFAULT_SESSION_KEEPALIVE_MS); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicy.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicy.java index 147d188104..ff8e60ff5c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicy.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicy.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.upstream; import static java.lang.Math.min; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.upstream.HttpDataSource.CleartextNotPermittedException; @@ -45,7 +46,6 @@ public class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPolicy { private static final int DEFAULT_BEHAVIOR_MIN_LOADABLE_RETRY_COUNT = -1; private final int minimumLoadableRetryCount; - private final boolean locationExclusionEnabled; /** * Creates an instance with default behavior. @@ -54,72 +54,48 @@ public class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPolicy { * #DEFAULT_MIN_LOADABLE_RETRY_COUNT_PROGRESSIVE_LIVE} for {@code dataType} {@link * C#DATA_TYPE_MEDIA_PROGRESSIVE_LIVE}. For other {@code dataType} values, it will return {@link * #DEFAULT_MIN_LOADABLE_RETRY_COUNT}. - * - *
Exclusion of both fallback types {@link #FALLBACK_TYPE_TRACK} and {@link - * #FALLBACK_TYPE_TRACK} is enabled by default. */ public DefaultLoadErrorHandlingPolicy() { - this(DEFAULT_BEHAVIOR_MIN_LOADABLE_RETRY_COUNT, /* locationExclusionEnabled= */ true); - } - - /** @deprecated Use {@link #DefaultLoadErrorHandlingPolicy(int, boolean)} instead. */ - @Deprecated - public DefaultLoadErrorHandlingPolicy(int minimumLoadableRetryCount) { - this(minimumLoadableRetryCount, /* locationExclusionEnabled= */ true); + this(DEFAULT_BEHAVIOR_MIN_LOADABLE_RETRY_COUNT); } /** * Creates an instance with the given value for {@link #getMinimumLoadableRetryCount(int)}. * * @param minimumLoadableRetryCount See {@link #getMinimumLoadableRetryCount}. - * @param locationExclusionEnabled Whether location exclusion is enabled. */ - public DefaultLoadErrorHandlingPolicy( - int minimumLoadableRetryCount, boolean locationExclusionEnabled) { + public DefaultLoadErrorHandlingPolicy(int minimumLoadableRetryCount) { this.minimumLoadableRetryCount = minimumLoadableRetryCount; - this.locationExclusionEnabled = locationExclusionEnabled; } /** * Returns the fallback selection. * *
The exclusion duration is given by {@link #DEFAULT_TRACK_EXCLUSION_MS} or {@link - * #DEFAULT_LOCATION_EXCLUSION_MS}, if the load error was an {@link InvalidResponseCodeException} - * with an HTTP response code indicating an unrecoverable error, or {@link C#TIME_UNSET} - * otherwise. + * #DEFAULT_LOCATION_EXCLUSION_MS} if the load error was not an {@link + * #isRecoverableError(IOException) recoverable error}. In case of a recoverable error null is + * returned to disable exclusion but retry the same load instead. * - *
If alternative locations are advertised by the {@link - * LoadErrorHandlingPolicy.FallbackOptions}, {@link #FALLBACK_TYPE_LOCATION} is selected until all - * locations are excluded, {@link #FALLBACK_TYPE_TRACK} otherwise. + *
If alternative locations {@link + * LoadErrorHandlingPolicy.FallbackOptions#isFallbackAvailable(int) are advertised}, {@link + * #FALLBACK_TYPE_LOCATION} is selected until all locations are excluded, {@link + * #FALLBACK_TYPE_TRACK} otherwise. */ @Override + @Nullable public FallbackSelection getFallbackSelectionFor( FallbackOptions fallbackOptions, LoadErrorInfo loadErrorInfo) { - @FallbackType int fallbackType = FALLBACK_TYPE_TRACK; - boolean fallbackAvailable = - fallbackOptions.numberOfTracks - fallbackOptions.numberOfExcludedTracks > 1; - if (locationExclusionEnabled - && fallbackOptions.numberOfLocations - fallbackOptions.numberOfExcludedLocations > 1) { - fallbackType = FALLBACK_TYPE_LOCATION; - fallbackAvailable = true; + if (isRecoverableError(loadErrorInfo.exception)) { + // Don't fallback. Retry the same load again. + return null; } - long exclusionDurationMs = C.TIME_UNSET; - IOException exception = loadErrorInfo.exception; - if (fallbackAvailable && exception instanceof InvalidResponseCodeException) { - int responseCode = ((InvalidResponseCodeException) exception).responseCode; - exclusionDurationMs = - responseCode == 403 // HTTP 403 Forbidden. - || responseCode == 404 // HTTP 404 Not Found. - || responseCode == 410 // HTTP 410 Gone. - || responseCode == 416 // HTTP 416 Range Not Satisfiable. - || responseCode == 500 // HTTP 500 Internal Server Error. - || responseCode == 503 // HTTP 503 Service Unavailable. - ? (fallbackType == FALLBACK_TYPE_TRACK - ? DEFAULT_TRACK_EXCLUSION_MS - : DEFAULT_LOCATION_EXCLUSION_MS) - : C.TIME_UNSET; + // Prefer location fallbacks to track fallbacks, when both are available. + if (fallbackOptions.isFallbackAvailable(FALLBACK_TYPE_LOCATION)) { + return new FallbackSelection(FALLBACK_TYPE_LOCATION, DEFAULT_LOCATION_EXCLUSION_MS); + } else if (fallbackOptions.isFallbackAvailable(FALLBACK_TYPE_TRACK)) { + return new FallbackSelection(FALLBACK_TYPE_TRACK, DEFAULT_TRACK_EXCLUSION_MS); } - return new FallbackSelection(fallbackType, exclusionDurationMs); + return null; } /** @@ -153,4 +129,24 @@ public class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPolicy { return minimumLoadableRetryCount; } } + + /** + * Returns whether the error is considered recoverable. + * + * @param exception The exception. + * @return Whether the error is considered an recoverable error. + */ + protected boolean isRecoverableError(IOException exception) { + if (!(exception instanceof InvalidResponseCodeException)) { + return true; + } + InvalidResponseCodeException invalidResponseCodeException = + (InvalidResponseCodeException) exception; + return invalidResponseCodeException.responseCode != 403 // HTTP 403 Forbidden. + && invalidResponseCodeException.responseCode != 404 // HTTP 404 Not Found. + && invalidResponseCodeException.responseCode != 410 // HTTP 410 Gone. + && invalidResponseCodeException.responseCode != 416 // HTTP 416 Range Not Satisfiable. + && invalidResponseCodeException.responseCode != 500 // HTTP 500 Internal Server Error. + && invalidResponseCodeException.responseCode != 503; // HTTP 503 Service Unavailable. + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/LoadErrorHandlingPolicy.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/LoadErrorHandlingPolicy.java index 3306ac7191..6ab21f5528 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/LoadErrorHandlingPolicy.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/LoadErrorHandlingPolicy.java @@ -15,7 +15,10 @@ */ package com.google.android.exoplayer2.upstream; +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + import androidx.annotation.IntDef; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.source.MediaLoadData; @@ -109,20 +112,28 @@ public interface LoadErrorHandlingPolicy { this.numberOfTracks = numberOfTracks; this.numberOfExcludedTracks = numberOfExcludedTracks; } + + /** Returns whether a fallback is available for the given {@link FallbackType fallback type}. */ + public boolean isFallbackAvailable(@FallbackType int type) { + return type == FALLBACK_TYPE_LOCATION + ? numberOfLocations - numberOfExcludedLocations > 1 + : numberOfTracks - numberOfExcludedTracks > 1; + } } /** The selection of a fallback option determining the fallback behaviour on load error. */ final class FallbackSelection { /** The {@link FallbackType fallback type} to use. */ @FallbackType public final int type; - /** - * The exclusion duration of the {@link #type} in milliseconds, or {@link C#TIME_UNSET} to - * disable exclusion of any fallback type. - */ + /** The exclusion duration of the {@link #type}, in milliseconds. */ public final long exclusionDurationMs; - /** Creates an instance with the given values. */ + /** + * Creates an instance with the given values. The exclusion duration, in milliseconds, needs to + * be a positive integer. + */ public FallbackSelection(@FallbackType int type, long exclusionDurationMs) { + checkArgument(exclusionDurationMs >= 0); this.type = type; this.exclusionDurationMs = exclusionDurationMs; } @@ -130,20 +141,18 @@ public interface LoadErrorHandlingPolicy { /** * Returns the {@link FallbackSelection fallback selection} that determines the exclusion - * behaviour on load error. + * behaviour on load error. If null is returned the caller will disable exclusion. * - *
If {@link FallbackSelection#exclusionDurationMs} is {@link C#TIME_UNSET}, exclusion is - * disabled for any fallback type, regardless of the value of the {@link FallbackSelection#type - * selected fallback type}. - * - *
If {@link FallbackSelection#type} is of a type that is not advertised as available by the - * {@link FallbackOptions}, exclusion is disabled for any fallback type. + *
If {@link FallbackSelection#type} is of a type that is not {@link + * FallbackOptions#isFallbackAvailable(int) advertised as available}, then the caller will disable + * exclusion as if null had been returned. * * @param fallbackOptions The available fallback options. * @param loadErrorInfo A {@link LoadErrorInfo} holding information about the load error. - * @return The fallback selection indicating whether to apply exclusion, and if so for which type - * and how long the resource should be excluded. + * @return The fallback selection indicating which exclusion type to apply and for how long the + * resource should be excluded. Returning null indicates to disable exclusion. */ + @Nullable FallbackSelection getFallbackSelectionFor( FallbackOptions fallbackOptions, LoadErrorInfo loadErrorInfo); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicyTest.java b/library/core/src/test/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicyTest.java index aa8a6e2305..22f4e4d778 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicyTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/upstream/DefaultLoadErrorHandlingPolicyTest.java @@ -16,13 +16,13 @@ package com.google.android.exoplayer2.upstream; import static com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy.DEFAULT_LOCATION_EXCLUSION_MS; -import static com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy.DEFAULT_MIN_LOADABLE_RETRY_COUNT; import static com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy.DEFAULT_TRACK_EXCLUSION_MS; import static com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.FALLBACK_TYPE_LOCATION; import static com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK; import static com.google.common.truth.Truth.assertThat; import android.net.Uri; +import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; @@ -56,6 +56,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { public void getFallbackSelectionFor_responseCode403() { InvalidResponseCodeException exception = buildInvalidResponseCodeException(403, "Forbidden"); + @Nullable LoadErrorHandlingPolicy.FallbackSelection defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( exception, @@ -83,6 +84,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { public void getFallbackSelectionFor_responseCode404() { InvalidResponseCodeException exception = buildInvalidResponseCodeException(404, "Not found"); + @Nullable LoadErrorHandlingPolicy.FallbackSelection defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( exception, @@ -111,6 +113,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { public void getFallbackSelectionFor_responseCode410() { InvalidResponseCodeException exception = buildInvalidResponseCodeException(410, "Gone"); + @Nullable LoadErrorHandlingPolicy.FallbackSelection defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( exception, @@ -140,6 +143,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { InvalidResponseCodeException exception = buildInvalidResponseCodeException(500, "Internal server error"); + @Nullable LoadErrorHandlingPolicy.FallbackSelection defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( exception, @@ -169,6 +173,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { InvalidResponseCodeException exception = buildInvalidResponseCodeException(503, "Service unavailable"); + @Nullable LoadErrorHandlingPolicy.FallbackSelection defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( exception, @@ -197,6 +202,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { public void getFallbackSelectionFor_dontExcludeUnexpectedHttpCodes() { InvalidResponseCodeException exception = buildInvalidResponseCodeException(418, "I'm a teapot"); + @Nullable LoadErrorHandlingPolicy.FallbackSelection defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( exception, @@ -205,8 +211,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { /* numberOfTracks= */ 10, /* numberOfExcludedTracks= */ 0); - assertThat(defaultPolicyFallbackSelection.type).isEqualTo(FALLBACK_TYPE_TRACK); - assertThat(defaultPolicyFallbackSelection.exclusionDurationMs).isEqualTo(C.TIME_UNSET); + assertThat(defaultPolicyFallbackSelection).isNull(); defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( @@ -215,14 +220,14 @@ public final class DefaultLoadErrorHandlingPolicyTest { /* numberOfExcludedLocations= */ 0, /* numberOfTracks= */ 4, /* numberOfExcludedTracks= */ 1); - assertThat(defaultPolicyFallbackSelection.type).isEqualTo(FALLBACK_TYPE_LOCATION); - assertThat(defaultPolicyFallbackSelection.exclusionDurationMs).isEqualTo(C.TIME_UNSET); + assertThat(defaultPolicyFallbackSelection).isNull(); } @Test public void getFallbackSelectionFor_dontExcludeUnexpectedExceptions() { IOException exception = new IOException(); + @Nullable LoadErrorHandlingPolicy.FallbackSelection defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( exception, @@ -231,8 +236,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { /* numberOfTracks= */ 10, /* numberOfExcludedTracks= */ 0); - assertThat(defaultPolicyFallbackSelection.type).isEqualTo(FALLBACK_TYPE_TRACK); - assertThat(defaultPolicyFallbackSelection.exclusionDurationMs).isEqualTo(C.TIME_UNSET); + assertThat(defaultPolicyFallbackSelection).isNull(); defaultPolicyFallbackSelection = getDefaultPolicyFallbackSelection( @@ -241,26 +245,7 @@ public final class DefaultLoadErrorHandlingPolicyTest { /* numberOfExcludedLocations= */ 0, /* numberOfTracks= */ 4, /* numberOfExcludedTracks= */ 1); - assertThat(defaultPolicyFallbackSelection.type).isEqualTo(FALLBACK_TYPE_LOCATION); - assertThat(defaultPolicyFallbackSelection.exclusionDurationMs).isEqualTo(C.TIME_UNSET); - } - - @Test - public void getFallbackSelectionFor_disabledLocationExclusion_useTrackExclusion() { - InvalidResponseCodeException exception = buildInvalidResponseCodeException(404, "Not found"); - - LoadErrorHandlingPolicy.FallbackSelection defaultPolicyFallbackSelection = - getDefaultPolicyFallbackSelection( - exception, - /* numberOfLocations= */ 2, - /* numberOfExcludedLocations= */ 0, - /* numberOfTracks= */ 4, - /* numberOfExcludedTracks= */ 1, - new DefaultLoadErrorHandlingPolicy( - DEFAULT_MIN_LOADABLE_RETRY_COUNT, /* locationExclusionEnabled= */ false)); - assertThat(defaultPolicyFallbackSelection.type).isEqualTo(FALLBACK_TYPE_TRACK); - assertThat(defaultPolicyFallbackSelection.exclusionDurationMs) - .isEqualTo(DEFAULT_TRACK_EXCLUSION_MS); + assertThat(defaultPolicyFallbackSelection).isNull(); } @Test @@ -279,28 +264,13 @@ public final class DefaultLoadErrorHandlingPolicyTest { assertThat(getDefaultPolicyRetryDelayOutputFor(new IOException(), 9)).isEqualTo(5000); } + @Nullable private static LoadErrorHandlingPolicy.FallbackSelection getDefaultPolicyFallbackSelection( IOException exception, int numberOfLocations, int numberOfExcludedLocations, int numberOfTracks, int numberOfExcludedTracks) { - return getDefaultPolicyFallbackSelection( - exception, - numberOfLocations, - numberOfExcludedLocations, - numberOfTracks, - numberOfExcludedTracks, - new DefaultLoadErrorHandlingPolicy()); - } - - private static LoadErrorHandlingPolicy.FallbackSelection getDefaultPolicyFallbackSelection( - IOException exception, - int numberOfLocations, - int numberOfExcludedLocations, - int numberOfTracks, - int numberOfExcludedTracks, - DefaultLoadErrorHandlingPolicy defaultLoadErrorHandlingPolicy) { LoadErrorInfo loadErrorInfo = new LoadErrorInfo( PLACEHOLDER_LOAD_EVENT_INFO, @@ -310,7 +280,8 @@ public final class DefaultLoadErrorHandlingPolicyTest { LoadErrorHandlingPolicy.FallbackOptions fallbackOptions = new LoadErrorHandlingPolicy.FallbackOptions( numberOfLocations, numberOfExcludedLocations, numberOfTracks, numberOfExcludedTracks); - return defaultLoadErrorHandlingPolicy.getFallbackSelectionFor(fallbackOptions, loadErrorInfo); + return new DefaultLoadErrorHandlingPolicy() + .getFallbackSelectionFor(fallbackOptions, loadErrorInfo); } private static long getDefaultPolicyRetryDelayOutputFor(IOException exception, int errorCount) { diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java index 3757a5b123..3c5bd1373d 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.dash; import static com.google.android.exoplayer2.trackselection.TrackSelectionUtil.createFallbackOptions; +import static com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK; import static java.lang.Math.max; import static java.lang.Math.min; @@ -490,10 +491,11 @@ public class DefaultDashChunkSource implements DashChunkSource { // No more alternative tracks remaining. return false; } + @Nullable LoadErrorHandlingPolicy.FallbackSelection fallbackSelection = loadErrorHandlingPolicy.getFallbackSelectionFor(fallbackOptions, loadErrorInfo); - return fallbackSelection.type == LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK - && fallbackSelection.exclusionDurationMs != C.TIME_UNSET + return fallbackSelection != null + && fallbackSelection.type == FALLBACK_TYPE_TRACK && trackSelection.blacklist( trackSelection.indexOf(chunk.trackFormat), fallbackSelection.exclusionDurationMs); } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSourceTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSourceTest.java index c90324b299..b5673bd7ee 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSourceTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSourceTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.net.Uri; import android.os.SystemClock; +import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; @@ -186,10 +187,11 @@ public class DefaultDashChunkSourceTest { DefaultLoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() { @Override + @Nullable public FallbackSelection getFallbackSelectionFor( FallbackOptions fallbackOptions, LoadErrorInfo loadErrorInfo) { // Never exclude, neither tracks nor locations. - return new FallbackSelection(FALLBACK_TYPE_TRACK, C.TIME_UNSET); + return null; } }; DashChunkSource chunkSource = createDashChunkSource(/* numberOfTracks= */ 2); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index b1104ba8f9..c489aaa1c2 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -562,13 +562,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } long exclusionDurationMs = C.TIME_UNSET; if (!forceRetry) { + @Nullable LoadErrorHandlingPolicy.FallbackSelection fallbackSelection = loadErrorHandlingPolicy.getFallbackSelectionFor( createFallbackOptions(chunkSource.getTrackSelection()), loadErrorInfo); - exclusionDurationMs = - fallbackSelection.type == LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK - ? fallbackSelection.exclusionDurationMs - : C.TIME_UNSET; + if (fallbackSelection != null + && fallbackSelection.type == LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK) { + exclusionDurationMs = fallbackSelection.exclusionDurationMs; + } } return chunkSource.onPlaylistError(playlistUrl, exclusionDurationMs); } @@ -909,11 +910,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; LoadErrorInfo loadErrorInfo = new LoadErrorInfo(loadEventInfo, mediaLoadData, error, errorCount); LoadErrorAction loadErrorAction; + @Nullable LoadErrorHandlingPolicy.FallbackSelection fallbackSelection = loadErrorHandlingPolicy.getFallbackSelectionFor( createFallbackOptions(chunkSource.getTrackSelection()), loadErrorInfo); - if (fallbackSelection.type == LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK - && fallbackSelection.exclusionDurationMs != C.TIME_UNSET) { + if (fallbackSelection != null + && fallbackSelection.type == LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK) { exclusionSucceeded = chunkSource.maybeExcludeTrack(loadable, fallbackSelection.exclusionDurationMs); } diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java index 6e2fa78af7..27cdb80396 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java @@ -290,12 +290,13 @@ public class DefaultSsChunkSource implements SsChunkSource { boolean cancelable, LoadErrorHandlingPolicy.LoadErrorInfo loadErrorInfo, LoadErrorHandlingPolicy loadErrorHandlingPolicy) { + @Nullable FallbackSelection fallbackSelection = loadErrorHandlingPolicy.getFallbackSelectionFor( createFallbackOptions(trackSelection), loadErrorInfo); return cancelable + && fallbackSelection != null && fallbackSelection.type == LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK - && fallbackSelection.exclusionDurationMs != C.TIME_UNSET && trackSelection.blacklist( trackSelection.indexOf(chunk.trackFormat), fallbackSelection.exclusionDurationMs); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java index 81261b5b6c..8db69b3dc7 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java @@ -177,8 +177,7 @@ public class FakeAdaptiveMediaPeriod positionUs, DrmSessionManager.DRM_UNSUPPORTED, new DrmSessionEventListener.EventDispatcher(), - new DefaultLoadErrorHandlingPolicy( - /* minimumLoadableRetryCount= */ 3, /* locationExclusionEnabled= */ true), + new DefaultLoadErrorHandlingPolicy(/* minimumLoadableRetryCount= */ 3), mediaSourceEventDispatcher); streams[i] = sampleStream; sampleStreams.add(sampleStream);