Skip to content

Commit

Permalink
Allow playback to continue even after SingleSampleMediaPeriod load er…
Browse files Browse the repository at this point in the history
…rors

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
  • Loading branch information
AquilesCanta authored and ojw28 committed Nov 7, 2017
1 parent ecaaed9 commit 54a2a69
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
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;
Expand All @@ -52,23 +51,28 @@
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;
this.minLoadableRetryCount = minLoadableRetryCount;
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");
Expand All @@ -85,7 +89,7 @@ public void prepare(Callback callback, long positionUs) {

@Override
public void maybeThrowPrepareError() throws IOException {
loader.maybeThrowError();
// Do nothing.
}

@Override
Expand Down Expand Up @@ -157,6 +161,7 @@ public void onLoadCompleted(SourceLoadable loadable, long elapsedRealtimeMs,
sampleSize = loadable.sampleSize;
sampleData = loadable.sampleData;
loadingFinished = true;
loadingSucceeded = true;
}

@Override
Expand All @@ -169,6 +174,11 @@ public void onLoadCanceled(SourceLoadable loadable, long elapsedRealtimeMs, long
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;
}

Expand Down Expand Up @@ -206,7 +216,9 @@ public boolean isReady() {

@Override
public void maybeThrowError() throws IOException {
loader.maybeThrowError();
if (!treatLoadErrorsAsEndOfStream) {
loader.maybeThrowError();
}
}

@Override
Expand All @@ -219,19 +231,19 @@ public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,59 @@ public interface EventListener {
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;
this.minLoadableRetryCount = minLoadableRetryCount;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
this.eventSourceId = eventSourceId;
this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream;
timeline = new SinglePeriodTimeline(durationUs, true);
}

Expand All @@ -98,7 +129,7 @@ public void maybeThrowSourceInfoRefreshError() throws IOException {
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
Expand Down

0 comments on commit 54a2a69

Please sign in to comment.