mirror of
https://github.com/samsonjs/media.git
synced 2026-04-10 12:05:47 +00:00
Add LoadErrorHandlingPolicy to customize blacklisting and backoff logic
Issue:#2844 Issue:#3370 Issue:#2981 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=204149284
This commit is contained in:
parent
095c6e4bf8
commit
32a91b5689
3 changed files with 231 additions and 11 deletions
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.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 com.google.android.exoplayer2.upstream.Loader.Loadable;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Defines how errors encountered by {@link Loader Loaders} are handled.
|
||||
*
|
||||
* <p>Loader clients may blacklist a resource when a load error occurs. Blacklisting works around
|
||||
* load errors by loading an alternative resource. Clients do not try blacklisting when a resource
|
||||
* does not have an alternative. When a resource does have valid alternatives, {@link
|
||||
* #getBlacklistDurationMsFor(T, long, IOException, int)} defines whether the resource should be
|
||||
* blacklisted. Blacklisting will succeed if any of the alternatives is not in the black list.
|
||||
*
|
||||
* <p>When blacklisting does not take place, {@link #getRetryDelayMsFor(T, long, IOException, int)}
|
||||
* defines whether the load is retried. Loader clients define when to propagate retry attempt
|
||||
* errors. Errors that are not retried are propagated.
|
||||
*
|
||||
* @param <T> The type of the object being loaded.
|
||||
*/
|
||||
public interface LoadErrorHandlingPolicy<T extends Loadable> {
|
||||
|
||||
/** Default implementation of {@link LoadErrorHandlingPolicy}. */
|
||||
LoadErrorHandlingPolicy<Loadable> DEFAULT =
|
||||
new LoadErrorHandlingPolicy<Loadable>() {
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
@Override
|
||||
public long getBlacklistDurationMsFor(
|
||||
Loadable loadable, long loadDurationMs, IOException exception, int errorCount) {
|
||||
if (exception instanceof InvalidResponseCodeException) {
|
||||
int responseCode = ((InvalidResponseCodeException) exception).responseCode;
|
||||
return responseCode == 404 // HTTP 404 Not Found.
|
||||
|| responseCode == 410 // HTTP 410 Gone.
|
||||
? ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS
|
||||
: C.TIME_UNSET;
|
||||
}
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retries for any exception that is not a subclass of {@link ParserException}. The retry
|
||||
* delay is calculated as {@code Math.min((errorCount - 1) * 1000, 5000)}.
|
||||
*/
|
||||
@Override
|
||||
public long getRetryDelayMsFor(
|
||||
Loadable loadable, long loadDurationMs, IOException exception, int errorCount) {
|
||||
return exception instanceof ParserException
|
||||
? C.TIME_UNSET
|
||||
: Math.min((errorCount - 1) * 1000, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
/** Returns {@link #DEFAULT}. */
|
||||
static <U extends Loadable> LoadErrorHandlingPolicy<U> getDefault() {
|
||||
@SuppressWarnings("unchecked") // Safe contravariant cast.
|
||||
LoadErrorHandlingPolicy<U> policy = (LoadErrorHandlingPolicy<U>) DEFAULT;
|
||||
return policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds for which a resource associated to a provided load error
|
||||
* should be blacklisted, or {@link C#TIME_UNSET} if the resource should not be blacklisted.
|
||||
*
|
||||
* @param loadable The loadable whose load failed.
|
||||
* @param loadDurationMs The duration in milliseconds of the load up to the point at which the
|
||||
* error occurred, including any previous attempts.
|
||||
* @param exception The load error.
|
||||
* @param errorCount The number of errors this load has encountered, including this one.
|
||||
* @return The blacklist duration in milliseconds, or {@link C#TIME_UNSET} if the resource should
|
||||
* not be blacklisted.
|
||||
*/
|
||||
long getBlacklistDurationMsFor(
|
||||
T loadable, long loadDurationMs, IOException exception, int errorCount);
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds to wait before attempting the load again, or {@link
|
||||
* C#TIME_UNSET} if the error is fatal and should not be retried.
|
||||
*
|
||||
* <p>{@link Loader} clients may ignore the retry delay returned by this method in order to wait
|
||||
* for a specific event before retrying. However, the load is retried if and only if this method
|
||||
* does not return {@link C#TIME_UNSET}.
|
||||
*
|
||||
* @param loadable The loadable whose load failed.
|
||||
* @param loadDurationMs The duration in milliseconds of the load up to the point at which the
|
||||
* error occurred, including any previous attempts.
|
||||
* @param exception The load error.
|
||||
* @param errorCount The number of errors this load has encountered, including this one.
|
||||
* @return The number of milliseconds to wait before attempting the load again, or {@link
|
||||
* C#TIME_UNSET} if the error is fatal and should not be retried.
|
||||
*/
|
||||
long getRetryDelayMsFor(T loadable, long loadDurationMs, IOException exception, int errorCount);
|
||||
}
|
||||
|
|
@ -75,27 +75,29 @@ public final class Loader implements LoaderErrorThrower {
|
|||
|
||||
/**
|
||||
* Called when a load has completed.
|
||||
* <p>
|
||||
* Note: There is guaranteed to be a memory barrier between {@link Loadable#load()} exiting and
|
||||
* this callback being called.
|
||||
*
|
||||
* <p>Note: There is guaranteed to be a memory barrier between {@link Loadable#load()} exiting
|
||||
* and this callback being called.
|
||||
*
|
||||
* @param loadable The loadable whose load has completed.
|
||||
* @param elapsedRealtimeMs {@link SystemClock#elapsedRealtime} when the load ended.
|
||||
* @param loadDurationMs The duration of the load.
|
||||
* @param loadDurationMs The duration in milliseconds of the load since {@link #startLoading}
|
||||
* was called.
|
||||
*/
|
||||
void onLoadCompleted(T loadable, long elapsedRealtimeMs, long loadDurationMs);
|
||||
|
||||
/**
|
||||
* Called when a load has been canceled.
|
||||
* <p>
|
||||
* Note: If the {@link Loader} has not been released then there is guaranteed to be a memory
|
||||
* barrier between {@link Loadable#load()} exiting and this callback being called. If the
|
||||
* {@link Loader} has been released then this callback may be called before
|
||||
* {@link Loadable#load()} exits.
|
||||
*
|
||||
* <p>Note: If the {@link Loader} has not been released then there is guaranteed to be a memory
|
||||
* barrier between {@link Loadable#load()} exiting and this callback being called. If the {@link
|
||||
* Loader} has been released then this callback may be called before {@link Loadable#load()}
|
||||
* exits.
|
||||
*
|
||||
* @param loadable The loadable whose load has been canceled.
|
||||
* @param elapsedRealtimeMs {@link SystemClock#elapsedRealtime} when the load was canceled.
|
||||
* @param loadDurationMs The duration of the load up to the point at which it was canceled.
|
||||
* @param loadDurationMs The duration in milliseconds of the load since {@link #startLoading}
|
||||
* was called up to the point at which it was canceled.
|
||||
* @param released True if the load was canceled because the {@link Loader} was released. False
|
||||
* otherwise.
|
||||
*/
|
||||
|
|
@ -109,7 +111,8 @@ public final class Loader implements LoaderErrorThrower {
|
|||
*
|
||||
* @param loadable The loadable whose load has encountered an error.
|
||||
* @param elapsedRealtimeMs {@link SystemClock#elapsedRealtime} when the error occurred.
|
||||
* @param loadDurationMs The duration of the load up to the point at which the error occurred.
|
||||
* @param loadDurationMs The duration in milliseconds of the load since {@link #startLoading}
|
||||
* was called up to the point at which the error occurred.
|
||||
* @param error The load error.
|
||||
* @param errorCount The number of errors this load has encountered, including this one.
|
||||
* @return The desired error handling action. One of {@link Loader#RETRY}, {@link
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.upstream;
|
||||
|
||||
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;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
/** Unit tests for {@link LoadErrorHandlingPolicy#DEFAULT}. */
|
||||
@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 {
|
||||
InvalidResponseCodeException exception =
|
||||
new InvalidResponseCodeException(404, Collections.emptyMap(), new DataSpec(Uri.EMPTY));
|
||||
assertThat(getDefaultPolicyBlacklistOutputFor(exception))
|
||||
.isEqualTo(ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBlacklistDurationMsFor_blacklist410() throws Exception {
|
||||
InvalidResponseCodeException exception =
|
||||
new InvalidResponseCodeException(410, Collections.emptyMap(), new DataSpec(Uri.EMPTY));
|
||||
assertThat(getDefaultPolicyBlacklistOutputFor(exception))
|
||||
.isEqualTo(ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBlacklistDurationMsFor_dontBlacklistUnexpectedHttpCodes() throws Exception {
|
||||
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 {
|
||||
FileNotFoundException exception = new FileNotFoundException();
|
||||
assertThat(getDefaultPolicyBlacklistOutputFor(exception)).isEqualTo(C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRetryDelayMsFor_dontRetryParserException() throws Exception {
|
||||
assertThat(getDefaultPolicyRetryDelayOutputFor(new ParserException(), 1))
|
||||
.isEqualTo(C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRetryDelayMsFor_successiveRetryDelays() throws Exception {
|
||||
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 3)).isEqualTo(2000);
|
||||
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 5)).isEqualTo(4000);
|
||||
assertThat(getDefaultPolicyRetryDelayOutputFor(new FileNotFoundException(), 9)).isEqualTo(5000);
|
||||
}
|
||||
|
||||
private static long getDefaultPolicyBlacklistOutputFor(IOException exception) {
|
||||
return LoadErrorHandlingPolicy.DEFAULT.getBlacklistDurationMsFor(
|
||||
DUMMY_LOADABLE, /* loadDurationMs= */ 1000, exception, /* errorCount= */ 1);
|
||||
}
|
||||
|
||||
private static long getDefaultPolicyRetryDelayOutputFor(IOException exception, int errorCount) {
|
||||
return LoadErrorHandlingPolicy.DEFAULT.getRetryDelayMsFor(
|
||||
DUMMY_LOADABLE, /* loadDurationMs= */ 1000, exception, errorCount);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue