Allow playback to continue even after SingleSampleMediaPeriod load errors

This prevents users from having to check sideloaded subtitles URLs before
preparing a SingleSampleMediaSource with it.

Issue:#3140

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=174475274
This commit is contained in:
aquilescanta 2017-11-03 10:01:15 -07:00 committed by Oliver Woodman
parent ecaaed9674
commit 54a2a69b05
2 changed files with 60 additions and 17 deletions

View File

@ -27,7 +27,6 @@ import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.Loadable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.ArrayList;
@ -52,16 +51,20 @@ import java.util.Arrays;
private final int eventSourceId;
private final TrackGroupArray tracks;
private final ArrayList<SampleStreamImpl> sampleStreams;
// Package private to avoid thunk methods.
/* package */ final Loader loader;
/* package */ final Format format;
/* package */ final boolean treatLoadErrorsAsEndOfStream;
/* package */ boolean loadingFinished;
/* package */ boolean loadingSucceeded;
/* package */ byte[] sampleData;
/* package */ int sampleSize;
private int errorCount;
public SingleSampleMediaPeriod(Uri uri, DataSource.Factory dataSourceFactory, Format format,
int minLoadableRetryCount, Handler eventHandler, EventListener eventListener,
int eventSourceId) {
int eventSourceId, boolean treatLoadErrorsAsEndOfStream) {
this.uri = uri;
this.dataSourceFactory = dataSourceFactory;
this.format = format;
@ -69,6 +72,7 @@ import java.util.Arrays;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
this.eventSourceId = eventSourceId;
this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream;
tracks = new TrackGroupArray(new TrackGroup(format));
sampleStreams = new ArrayList<>();
loader = new Loader("Loader:SingleSampleMediaPeriod");
@ -85,7 +89,7 @@ import java.util.Arrays;
@Override
public void maybeThrowPrepareError() throws IOException {
loader.maybeThrowError();
// Do nothing.
}
@Override
@ -157,6 +161,7 @@ import java.util.Arrays;
sampleSize = loadable.sampleSize;
sampleData = loadable.sampleData;
loadingFinished = true;
loadingSucceeded = true;
}
@Override
@ -169,6 +174,11 @@ import java.util.Arrays;
public int onLoadError(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs,
IOException error) {
notifyLoadError(error);
errorCount++;
if (treatLoadErrorsAsEndOfStream && errorCount >= minLoadableRetryCount) {
loadingFinished = true;
return Loader.DONT_RETRY;
}
return Loader.RETRY;
}
@ -206,7 +216,9 @@ import java.util.Arrays;
@Override
public void maybeThrowError() throws IOException {
loader.maybeThrowError();
if (!treatLoadErrorsAsEndOfStream) {
loader.maybeThrowError();
}
}
@Override
@ -219,19 +231,19 @@ import java.util.Arrays;
formatHolder.format = format;
streamState = STREAM_STATE_SEND_SAMPLE;
return C.RESULT_FORMAT_READ;
}
Assertions.checkState(streamState == STREAM_STATE_SEND_SAMPLE);
if (!loadingFinished) {
return C.RESULT_NOTHING_READ;
} else {
buffer.timeUs = 0;
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
buffer.ensureSpaceForWrite(sampleSize);
buffer.data.put(sampleData, 0, sampleSize);
} else if (loadingFinished) {
if (loadingSucceeded) {
buffer.timeUs = 0;
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
buffer.ensureSpaceForWrite(sampleSize);
buffer.data.put(sampleData, 0, sampleSize);
} else {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
}
streamState = STREAM_STATE_END_OF_STREAM;
return C.RESULT_BUFFER_READ;
}
return C.RESULT_NOTHING_READ;
}
@Override

View File

@ -57,21 +57,51 @@ public final class SingleSampleMediaSource implements MediaSource {
private final Handler eventHandler;
private final EventListener eventListener;
private final int eventSourceId;
private final boolean treatLoadErrorsAsEndOfStream;
private final Timeline timeline;
/**
* @param uri The {@link Uri} of the media stream.
* @param dataSourceFactory The factory from which the {@link DataSource} to read the media will
* be obtained.
* @param format The {@link Format} associated with the output track.
* @param durationUs The duration of the media stream in microseconds.
*/
public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format,
long durationUs) {
this(uri, dataSourceFactory, format, durationUs, DEFAULT_MIN_LOADABLE_RETRY_COUNT);
}
/**
* @param uri The {@link Uri} of the media stream.
* @param dataSourceFactory The factory from which the {@link DataSource} to read the media will
* be obtained.
* @param format The {@link Format} associated with the output track.
* @param durationUs The duration of the media stream in microseconds.
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
*/
public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format,
long durationUs, int minLoadableRetryCount) {
this(uri, dataSourceFactory, format, durationUs, minLoadableRetryCount, null, null, 0);
this(uri, dataSourceFactory, format, durationUs, minLoadableRetryCount, null, null, 0, false);
}
/**
* @param uri The {@link Uri} of the media stream.
* @param dataSourceFactory The factory from which the {@link DataSource} to read the media will
* be obtained.
* @param format The {@link Format} associated with the output track.
* @param durationUs The duration of the media stream in microseconds.
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
* @param eventHandler A handler for events. May be null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param eventSourceId An identifier that gets passed to {@code eventListener} methods.
* @param treatLoadErrorsAsEndOfStream If true, load errors will not be propagated by sample
* streams, treating them as ended instead. If false, load errors will be propagated normally
* by {@link SampleStream#maybeThrowError()}.
*/
public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format,
long durationUs, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener,
int eventSourceId) {
int eventSourceId, boolean treatLoadErrorsAsEndOfStream) {
this.uri = uri;
this.dataSourceFactory = dataSourceFactory;
this.format = format;
@ -79,6 +109,7 @@ public final class SingleSampleMediaSource implements MediaSource {
this.eventHandler = eventHandler;
this.eventListener = eventListener;
this.eventSourceId = eventSourceId;
this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream;
timeline = new SinglePeriodTimeline(durationUs, true);
}
@ -98,7 +129,7 @@ public final class SingleSampleMediaSource implements MediaSource {
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkArgument(id.periodIndex == 0);
return new SingleSampleMediaPeriod(uri, dataSourceFactory, format, minLoadableRetryCount,
eventHandler, eventListener, eventSourceId);
eventHandler, eventListener, eventSourceId, treatLoadErrorsAsEndOfStream);
}
@Override