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

View File

@ -57,21 +57,51 @@ public final class SingleSampleMediaSource implements MediaSource {
private final Handler eventHandler; private final Handler eventHandler;
private final EventListener eventListener; private final EventListener eventListener;
private final int eventSourceId; private final int eventSourceId;
private final boolean treatLoadErrorsAsEndOfStream;
private final Timeline timeline; 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, public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format,
long durationUs) { long durationUs) {
this(uri, dataSourceFactory, format, durationUs, DEFAULT_MIN_LOADABLE_RETRY_COUNT); 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, public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format,
long durationUs, int minLoadableRetryCount) { 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, public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format,
long durationUs, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, long durationUs, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener,
int eventSourceId) { int eventSourceId, boolean treatLoadErrorsAsEndOfStream) {
this.uri = uri; this.uri = uri;
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
this.format = format; this.format = format;
@ -79,6 +109,7 @@ public final class SingleSampleMediaSource implements MediaSource {
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
this.eventSourceId = eventSourceId; this.eventSourceId = eventSourceId;
this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream;
timeline = new SinglePeriodTimeline(durationUs, true); timeline = new SinglePeriodTimeline(durationUs, true);
} }
@ -98,7 +129,7 @@ public final class SingleSampleMediaSource implements MediaSource {
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkArgument(id.periodIndex == 0); Assertions.checkArgument(id.periodIndex == 0);
return new SingleSampleMediaPeriod(uri, dataSourceFactory, format, minLoadableRetryCount, return new SingleSampleMediaPeriod(uri, dataSourceFactory, format, minLoadableRetryCount,
eventHandler, eventListener, eventSourceId); eventHandler, eventListener, eventSourceId, treatLoadErrorsAsEndOfStream);
} }
@Override @Override