Change signature of ChunkSource.onChunkLoadError

A no-op change that changes the signature of the onChunkLoadError method of the ChunkSource. Implementors can get the exclusion duration directly from the LoadErrorHndlingPolicy instead of receiving it as an argument of the callback.

PiperOrigin-RevId: 381102935
This commit is contained in:
bachinger 2021-06-23 21:26:32 +01:00 committed by Oliver Woodman
parent ed471fae96
commit 1cb4fb290f
6 changed files with 170 additions and 16 deletions

View File

@ -516,13 +516,9 @@ public class ChunkSampleStream<T extends ChunkSource>
LoadErrorInfo loadErrorInfo = LoadErrorInfo loadErrorInfo =
new LoadErrorInfo(loadEventInfo, mediaLoadData, error, errorCount); new LoadErrorInfo(loadEventInfo, mediaLoadData, error, errorCount);
long exclusionDurationMs =
cancelable
? loadErrorHandlingPolicy.getExclusionDurationMsFor(
LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK, loadErrorInfo)
: C.TIME_UNSET;
@Nullable LoadErrorAction loadErrorAction = null; @Nullable LoadErrorAction loadErrorAction = null;
if (chunkSource.onChunkLoadError(loadable, cancelable, error, exclusionDurationMs)) { if (chunkSource.onChunkLoadError(
loadable, cancelable, loadErrorInfo, loadErrorHandlingPolicy)) {
if (cancelable) { if (cancelable) {
loadErrorAction = Loader.DONT_RETRY; loadErrorAction = Loader.DONT_RETRY;
if (isMediaChunk) { if (isMediaChunk) {

View File

@ -15,8 +15,8 @@
*/ */
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 com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -104,15 +104,19 @@ public interface ChunkSource {
* *
* @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 loadErrorInfo The load error info.
* @param exclusionDurationMs The duration for which the associated track may be excluded, or * @param loadErrorHandlingPolicy The load error handling policy to customize the behaviour of
* {@link C#TIME_UNSET} if the track may not be excluded. * handling the load error.
* @return Whether the load should be canceled so that a replacement chunk can be loaded instead. * @return Whether the load should be canceled so that a replacement chunk can be loaded instead.
* Must be {@code false} if {@code cancelable} is {@code false}. If {@code true}, {@link * Must be {@code false} if {@code cancelable} is {@code false}. If {@code true}, {@link
* #getNextChunk(long, long, List, ChunkHolder)} will be called to obtain the replacement * #getNextChunk(long, long, List, ChunkHolder)} will be called to obtain the replacement
* chunk. * chunk.
*/ */
boolean onChunkLoadError(Chunk chunk, boolean cancelable, Exception e, long exclusionDurationMs); boolean onChunkLoadError(
Chunk chunk,
boolean cancelable,
LoadErrorHandlingPolicy.LoadErrorInfo loadErrorInfo,
LoadErrorHandlingPolicy loadErrorHandlingPolicy);
/** Releases any held resources. */ /** Releases any held resources. */
void release(); void release();

View File

@ -46,6 +46,7 @@ import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
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.HttpDataSource.InvalidResponseCodeException; import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
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.Util; import com.google.android.exoplayer2.util.Util;
@ -455,7 +456,10 @@ public class DefaultDashChunkSource implements DashChunkSource {
@Override @Override
public boolean onChunkLoadError( public boolean onChunkLoadError(
Chunk chunk, boolean cancelable, Exception e, long exclusionDurationMs) { Chunk chunk,
boolean cancelable,
LoadErrorHandlingPolicy.LoadErrorInfo loadErrorInfo,
LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
if (!cancelable) { if (!cancelable) {
return false; return false;
} }
@ -465,8 +469,8 @@ public class DefaultDashChunkSource implements DashChunkSource {
// Workaround for missing segment at the end of the period // Workaround for missing segment at the end of the period
if (!manifest.dynamic if (!manifest.dynamic
&& chunk instanceof MediaChunk && chunk instanceof MediaChunk
&& e instanceof InvalidResponseCodeException && loadErrorInfo.exception instanceof InvalidResponseCodeException
&& ((InvalidResponseCodeException) e).responseCode == 404) { && ((InvalidResponseCodeException) loadErrorInfo.exception).responseCode == 404) {
RepresentationHolder representationHolder = RepresentationHolder representationHolder =
representationHolders[trackSelection.indexOf(chunk.trackFormat)]; representationHolders[trackSelection.indexOf(chunk.trackFormat)];
long segmentCount = representationHolder.getSegmentCount(); long segmentCount = representationHolder.getSegmentCount();
@ -478,6 +482,9 @@ public class DefaultDashChunkSource implements DashChunkSource {
} }
} }
} }
long exclusionDurationMs =
loadErrorHandlingPolicy.getExclusionDurationMsFor(
LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK, loadErrorInfo);
return exclusionDurationMs != C.TIME_UNSET return exclusionDurationMs != C.TIME_UNSET
&& trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), exclusionDurationMs); && trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), exclusionDurationMs);
} }

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.dash; package com.google.android.exoplayer2.source.dash;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.net.Uri; import android.net.Uri;
@ -23,6 +24,8 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaLoadData;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.chunk.BundledChunkExtractor; import com.google.android.exoplayer2.source.chunk.BundledChunkExtractor;
import com.google.android.exoplayer2.source.chunk.ChunkHolder; import com.google.android.exoplayer2.source.chunk.ChunkHolder;
@ -30,10 +33,18 @@ import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
import com.google.android.exoplayer2.testutil.FakeDataSource; import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection; import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.HttpDataSource;
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.util.Assertions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.annotation.internal.DoNotInstrument; import org.robolectric.annotation.internal.DoNotInstrument;
@ -132,4 +143,129 @@ public class DefaultDashChunkSourceTest {
assertThat(output.chunk.dataSpec.flags & DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED) assertThat(output.chunk.dataSpec.flags & DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED)
.isEqualTo(0); .isEqualTo(0);
} }
@Test
public void onChunkLoadError_trackExclusionEnabled_requestReplacementChunkAsLongAsAvailable()
throws Exception {
DefaultLoadErrorHandlingPolicy loadErrorHandlingPolicy =
new DefaultLoadErrorHandlingPolicy() {
@Override
public long getExclusionDurationMsFor(int fallbackType, LoadErrorInfo loadErrorInfo) {
// Try to exclude tracks only.
return fallbackType == FALLBACK_TYPE_LOCATION
? C.TIME_UNSET
: super.getExclusionDurationMsFor(fallbackType, loadErrorInfo);
}
};
int numberOfTracks = 2;
DashChunkSource chunkSource = createDashChunkSource(numberOfTracks);
ChunkHolder output = new ChunkHolder();
for (int i = 0; i < numberOfTracks; i++) {
chunkSource.getNextChunk(
/* playbackPositionUs= */ 0,
/* loadPositionUs= */ 0,
/* queue= */ ImmutableList.of(),
output);
boolean alternativeTrackAvailable =
chunkSource.onChunkLoadError(
checkNotNull(output.chunk),
/* cancelable= */ true,
createFakeLoadErrorInfo(
output.chunk.dataSpec, /* httpResponseCode= */ 404, /* errorCount= */ 1),
loadErrorHandlingPolicy);
// Expect true except for the last track remaining.
assertThat(alternativeTrackAvailable).isEqualTo(i != numberOfTracks - 1);
}
}
@Test
public void onChunkLoadError_trackExclusionDisabled_neverRequestReplacementChunk()
throws Exception {
DefaultLoadErrorHandlingPolicy loadErrorHandlingPolicy =
new DefaultLoadErrorHandlingPolicy() {
@Override
public long getExclusionDurationMsFor(int fallbackType, LoadErrorInfo loadErrorInfo) {
// Never exclude, neither tracks nor locations.
return C.TIME_UNSET;
}
};
DashChunkSource chunkSource = createDashChunkSource(/* numberOfTracks= */ 2);
ChunkHolder output = new ChunkHolder();
chunkSource.getNextChunk(
/* playbackPositionUs= */ 0,
/* loadPositionUs= */ 0,
/* queue= */ ImmutableList.of(),
output);
boolean alternativeTrackAvailable =
chunkSource.onChunkLoadError(
checkNotNull(output.chunk),
/* cancelable= */ true,
createFakeLoadErrorInfo(
output.chunk.dataSpec, /* httpResponseCode= */ 404, /* errorCount= */ 1),
loadErrorHandlingPolicy);
assertThat(alternativeTrackAvailable).isFalse();
}
private DashChunkSource createDashChunkSource(int numberOfTracks) throws IOException {
Assertions.checkArgument(numberOfTracks < 6);
DashManifest manifest =
new DashManifestParser()
.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(
ApplicationProvider.getApplicationContext(), SAMPLE_MPD_VOD));
int[] adaptationSetIndices = new int[] {0};
int[] selectedTracks = new int[numberOfTracks];
Format[] formats = new Format[numberOfTracks];
for (int i = 0; i < numberOfTracks; i++) {
selectedTracks[i] = i;
formats[i] =
manifest
.getPeriod(0)
.adaptationSets
.get(adaptationSetIndices[0])
.representations
.get(i)
.format;
}
AdaptiveTrackSelection adaptiveTrackSelection =
new AdaptiveTrackSelection(
new TrackGroup(formats),
selectedTracks,
new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build());
return new DefaultDashChunkSource(
BundledChunkExtractor.FACTORY,
new LoaderErrorThrower.Dummy(),
manifest,
/* periodIndex= */ 0,
/* adaptationSetIndices= */ adaptationSetIndices,
adaptiveTrackSelection,
C.TRACK_TYPE_VIDEO,
new FakeDataSource(),
/* elapsedRealtimeOffsetMs= */ 0,
/* maxSegmentsPerLoad= */ 1,
/* enableEventMessageTrack= */ false,
/* closedCaptionFormats */ ImmutableList.of(),
/* playerTrackEmsgHandler= */ null);
}
private LoadErrorHandlingPolicy.LoadErrorInfo createFakeLoadErrorInfo(
DataSpec dataSpec, int httpResponseCode, int errorCount) {
LoadEventInfo loadEventInfo =
new LoadEventInfo(/* loadTaskId= */ 0, dataSpec, SystemClock.elapsedRealtime());
MediaLoadData mediaLoadData = new MediaLoadData(C.DATA_TYPE_MEDIA);
HttpDataSource.InvalidResponseCodeException invalidResponseCodeException =
new HttpDataSource.InvalidResponseCodeException(
httpResponseCode,
/* responseMessage= */ null,
ImmutableMap.of(),
dataSpec,
new byte[0]);
return new LoadErrorHandlingPolicy.LoadErrorInfo(
loadEventInfo, mediaLoadData, invalidResponseCodeException, errorCount);
}
} }

View File

@ -37,6 +37,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.
import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
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.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.Assertions; import com.google.android.exoplayer2.util.Assertions;
@ -282,7 +283,13 @@ public class DefaultSsChunkSource implements SsChunkSource {
@Override @Override
public boolean onChunkLoadError( public boolean onChunkLoadError(
Chunk chunk, boolean cancelable, Exception e, long exclusionDurationMs) { Chunk chunk,
boolean cancelable,
LoadErrorHandlingPolicy.LoadErrorInfo loadErrorInfo,
LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
long exclusionDurationMs =
loadErrorHandlingPolicy.getExclusionDurationMsFor(
LoadErrorHandlingPolicy.FALLBACK_TYPE_TRACK, loadErrorInfo);
return cancelable return cancelable
&& exclusionDurationMs != C.TIME_UNSET && exclusionDurationMs != C.TIME_UNSET
&& trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), exclusionDurationMs); && trackSelection.blacklist(trackSelection.indexOf(chunk.trackFormat), exclusionDurationMs);

View File

@ -30,6 +30,7 @@ import com.google.android.exoplayer2.testutil.FakeDataSet.FakeData.Segment;
import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
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.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
@ -156,7 +157,10 @@ public class FakeChunkSource implements ChunkSource {
@Override @Override
public boolean onChunkLoadError( public boolean onChunkLoadError(
Chunk chunk, boolean cancelable, Exception e, long exclusionDurationMs) { Chunk chunk,
boolean cancelable,
LoadErrorHandlingPolicy.LoadErrorInfo loadErrorInfo,
LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
return false; return false;
} }