Commit a2f10399 by Oliver Woodman

Improve error propagation

parent 5df6854f
Showing with 344 additions and 298 deletions
...@@ -126,24 +126,19 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo ...@@ -126,24 +126,19 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
} }
@Override @Override
protected int doPrepare(long positionUs) throws ExoPlaybackException { protected int doPrepare(long positionUs) {
try { boolean sourcePrepared = source.prepare(positionUs);
boolean sourcePrepared = source.prepare(positionUs); if (!sourcePrepared) {
if (!sourcePrepared) { return TrackRenderer.STATE_UNPREPARED;
return TrackRenderer.STATE_UNPREPARED;
}
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
int trackCount = source.getTrackCount();
for (int i = 0; i < source.getTrackCount(); i++) { for (int i = 0; i < trackCount; i++) {
if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_OPUS) if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_OPUS)
|| source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_WEBM)) { || source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_WEBM)) {
trackIndex = i; trackIndex = i;
return TrackRenderer.STATE_PREPARED; return TrackRenderer.STATE_PREPARED;
} }
} }
return TrackRenderer.STATE_IGNORE; return TrackRenderer.STATE_IGNORE;
} }
...@@ -152,42 +147,49 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo ...@@ -152,42 +147,49 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
if (outputStreamEnded) { if (outputStreamEnded) {
return; return;
} }
try { sourceIsReady = source.continueBuffering(trackIndex, positionUs);
sourceIsReady = source.continueBuffering(trackIndex, positionUs); checkForDiscontinuity();
checkForDiscontinuity();
if (format == null) { // Try and read a format if we don't have one already.
readFormat(); if (format == null && !readFormat(positionUs)) {
} else { // We can't make progress without one.
// Create the decoder. return;
if (decoder == null) { }
// For opus, the format can contain upto 3 entries in initializationData in the following
// exact order:
// 1) Opus Header Information (required)
// 2) Codec Delay in nanoseconds (required if Seek Preroll is present)
// 3) Seek Preroll in nanoseconds (required if Codec Delay is present)
List<byte[]> initializationData = format.initializationData;
if (initializationData.size() < 1) {
throw new ExoPlaybackException("Missing initialization data");
}
long codecDelayNs = -1;
long seekPreRollNs = -1;
if (initializationData.size() == 3) {
if (initializationData.get(1).length != Long.SIZE
|| initializationData.get(2).length != Long.SIZE) {
throw new ExoPlaybackException("Invalid Codec Delay or Seek Preroll");
}
codecDelayNs = ByteBuffer.wrap(initializationData.get(1)).getLong();
seekPreRollNs = ByteBuffer.wrap(initializationData.get(2)).getLong();
}
decoder =
new OpusDecoderWrapper(initializationData.get(0), codecDelayNs, seekPreRollNs);
decoder.start();
}
renderBuffer();
// Queue input buffers. // If we don't have a decoder yet, we need to instantiate one.
while (feedInputBuffer()) {} if (decoder == null) {
// For opus, the format can contain upto 3 entries in initializationData in the following
// exact order:
// 1) Opus Header Information (required)
// 2) Codec Delay in nanoseconds (required if Seek Preroll is present)
// 3) Seek Preroll in nanoseconds (required if Codec Delay is present)
List<byte[]> initializationData = format.initializationData;
if (initializationData.size() < 1) {
throw new ExoPlaybackException("Missing initialization data");
}
long codecDelayNs = -1;
long seekPreRollNs = -1;
if (initializationData.size() == 3) {
if (initializationData.get(1).length != Long.SIZE
|| initializationData.get(2).length != Long.SIZE) {
throw new ExoPlaybackException("Invalid Codec Delay or Seek Preroll");
}
codecDelayNs = ByteBuffer.wrap(initializationData.get(1)).getLong();
seekPreRollNs = ByteBuffer.wrap(initializationData.get(2)).getLong();
} }
try {
decoder = new OpusDecoderWrapper(initializationData.get(0), codecDelayNs, seekPreRollNs);
} catch (OpusDecoderException e) {
notifyDecoderError(e);
throw new ExoPlaybackException(e);
}
decoder.start();
}
// Rendering loop.
try {
renderBuffer();
while (feedInputBuffer()) {}
} catch (AudioTrack.InitializationException e) { } catch (AudioTrack.InitializationException e) {
notifyAudioTrackInitializationError(e); notifyAudioTrackInitializationError(e);
throw new ExoPlaybackException(e); throw new ExoPlaybackException(e);
...@@ -197,8 +199,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo ...@@ -197,8 +199,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
} catch (OpusDecoderException e) { } catch (OpusDecoderException e) {
notifyDecoderError(e); notifyDecoderError(e);
throw new ExoPlaybackException(e); throw new ExoPlaybackException(e);
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
} }
...@@ -249,7 +249,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo ...@@ -249,7 +249,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
} }
} }
private boolean feedInputBuffer() throws IOException, OpusDecoderException { private boolean feedInputBuffer() throws OpusDecoderException {
if (inputStreamEnded) { if (inputStreamEnded) {
return false; return false;
} }
...@@ -291,7 +291,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo ...@@ -291,7 +291,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
return true; return true;
} }
private void checkForDiscontinuity() throws IOException { private void checkForDiscontinuity() {
if (decoder == null) { if (decoder == null) {
return; return;
} }
...@@ -394,12 +394,23 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo ...@@ -394,12 +394,23 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo
} }
} }
private void readFormat() throws IOException { @Override
int result = source.readData(trackIndex, currentPositionUs, formatHolder, null, false); protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
private boolean readFormat(long positionUs) {
int result = source.readData(trackIndex, positionUs, formatHolder, null, false);
if (result == SampleSource.FORMAT_READ) { if (result == SampleSource.FORMAT_READ) {
format = formatHolder.format; format = formatHolder.format;
audioTrack.reconfigure(format.getFrameworkMediaFormatV16()); audioTrack.reconfigure(format.getFrameworkMediaFormatV16());
return true;
} }
return false;
} }
@Override @Override
......
...@@ -153,23 +153,18 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -153,23 +153,18 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
@Override @Override
protected int doPrepare(long positionUs) throws ExoPlaybackException { protected int doPrepare(long positionUs) throws ExoPlaybackException {
try { boolean sourcePrepared = source.prepare(positionUs);
boolean sourcePrepared = source.prepare(positionUs); if (!sourcePrepared) {
if (!sourcePrepared) { return TrackRenderer.STATE_UNPREPARED;
return TrackRenderer.STATE_UNPREPARED;
}
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
int trackCount = source.getTrackCount();
for (int i = 0; i < source.getTrackCount(); i++) { for (int i = 0; i < trackCount; i++) {
if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.VIDEO_VP9) if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.VIDEO_VP9)
|| source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.VIDEO_WEBM)) { || source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.VIDEO_WEBM)) {
trackIndex = i; trackIndex = i;
return TrackRenderer.STATE_PREPARED; return TrackRenderer.STATE_PREPARED;
} }
} }
return TrackRenderer.STATE_IGNORE; return TrackRenderer.STATE_IGNORE;
} }
...@@ -178,28 +173,29 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -178,28 +173,29 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
if (outputStreamEnded) { if (outputStreamEnded) {
return; return;
} }
try { sourceIsReady = source.continueBuffering(trackIndex, positionUs);
sourceIsReady = source.continueBuffering(trackIndex, positionUs); checkForDiscontinuity(positionUs);
checkForDiscontinuity(positionUs);
if (format == null) {
readFormat(positionUs);
} else {
// TODO: Add support for dynamic switching between one type of surface to another.
// Create the decoder.
if (decoder == null) {
decoder = new VpxDecoderWrapper(outputRgb);
decoder.start();
}
processOutputBuffer(positionUs, elapsedRealtimeUs);
// Queue input buffers. // Try and read a format if we don't have one already.
while (feedInputBuffer(positionUs)) {} if (format == null && !readFormat(positionUs)) {
} // We can't make progress without one.
return;
}
// If we don't have a decoder yet, we need to instantiate one.
// TODO: Add support for dynamic switching between one type of surface to another.
if (decoder == null) {
decoder = new VpxDecoderWrapper(outputRgb);
decoder.start();
}
// Rendering loop.
try {
processOutputBuffer(positionUs, elapsedRealtimeUs);
while (feedInputBuffer(positionUs)) {}
} catch (VpxDecoderException e) { } catch (VpxDecoderException e) {
notifyDecoderError(e); notifyDecoderError(e);
throw new ExoPlaybackException(e); throw new ExoPlaybackException(e);
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
} }
...@@ -294,7 +290,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -294,7 +290,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
surface.unlockCanvasAndPost(canvas); surface.unlockCanvasAndPost(canvas);
} }
private boolean feedInputBuffer(long positionUs) throws IOException, VpxDecoderException { private boolean feedInputBuffer(long positionUs) throws VpxDecoderException {
if (inputStreamEnded) { if (inputStreamEnded) {
return false; return false;
} }
...@@ -334,7 +330,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -334,7 +330,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
return true; return true;
} }
private void checkForDiscontinuity(long positionUs) throws IOException { private void checkForDiscontinuity(long positionUs) {
if (decoder == null) { if (decoder == null) {
return; return;
} }
...@@ -417,11 +413,22 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -417,11 +413,22 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer {
source.disable(trackIndex); source.disable(trackIndex);
} }
private void readFormat(long positionUs) throws IOException { @Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
private boolean readFormat(long positionUs) {
int result = source.readData(trackIndex, positionUs, formatHolder, null, false); int result = source.readData(trackIndex, positionUs, formatHolder, null, false);
if (result == SampleSource.FORMAT_READ) { if (result == SampleSource.FORMAT_READ) {
format = formatHolder.format; format = formatHolder.format;
return true;
} }
return false;
} }
@Override @Override
......
...@@ -25,7 +25,7 @@ package com.google.android.exoplayer; ...@@ -25,7 +25,7 @@ package com.google.android.exoplayer;
public class DummyTrackRenderer extends TrackRenderer { public class DummyTrackRenderer extends TrackRenderer {
@Override @Override
protected int doPrepare(long positionUs) throws ExoPlaybackException { protected int doPrepare(long positionUs) {
return STATE_IGNORE; return STATE_IGNORE;
} }
...@@ -50,6 +50,11 @@ public class DummyTrackRenderer extends TrackRenderer { ...@@ -50,6 +50,11 @@ public class DummyTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected void maybeThrowError() {
throw new IllegalStateException();
}
@Override
protected long getDurationUs() { protected long getDurationUs() {
throw new IllegalStateException(); throw new IllegalStateException();
} }
......
...@@ -266,10 +266,12 @@ import java.util.List; ...@@ -266,10 +266,12 @@ import java.util.List;
private void incrementalPrepareInternal() throws ExoPlaybackException { private void incrementalPrepareInternal() throws ExoPlaybackException {
long operationStartTimeMs = SystemClock.elapsedRealtime(); long operationStartTimeMs = SystemClock.elapsedRealtime();
boolean prepared = true; boolean prepared = true;
for (int i = 0; i < renderers.length; i++) { for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
if (renderers[i].getState() == TrackRenderer.STATE_UNPREPARED) { TrackRenderer renderer = renderers[rendererIndex];
int state = renderers[i].prepare(positionUs); if (renderer.getState() == TrackRenderer.STATE_UNPREPARED) {
int state = renderer.prepare(positionUs);
if (state == TrackRenderer.STATE_UNPREPARED) { if (state == TrackRenderer.STATE_UNPREPARED) {
renderer.maybeThrowError();
prepared = false; prepared = false;
} }
} }
...@@ -414,7 +416,14 @@ import java.util.List; ...@@ -414,7 +416,14 @@ import java.util.List;
// invocation of this method. // invocation of this method.
renderer.doSomeWork(positionUs, elapsedRealtimeUs); renderer.doSomeWork(positionUs, elapsedRealtimeUs);
allRenderersEnded = allRenderersEnded && renderer.isEnded(); allRenderersEnded = allRenderersEnded && renderer.isEnded();
allRenderersReadyOrEnded = allRenderersReadyOrEnded && rendererReadyOrEnded(renderer);
// Determine whether the renderer is ready (or ended). If it's not, throw an error that's
// preventing the renderer from making progress, if such an error exists.
boolean rendererReadyOrEnded = rendererReadyOrEnded(renderer);
if (!rendererReadyOrEnded) {
renderer.maybeThrowError();
}
allRenderersReadyOrEnded = allRenderersReadyOrEnded && rendererReadyOrEnded;
if (bufferedPositionUs == TrackRenderer.UNKNOWN_TIME_US) { if (bufferedPositionUs == TrackRenderer.UNKNOWN_TIME_US) {
// We've already encountered a track for which the buffered position is unknown. Hence the // We've already encountered a track for which the buffered position is unknown. Hence the
......
...@@ -76,6 +76,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe ...@@ -76,6 +76,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
private final long fileDescriptorOffset; private final long fileDescriptorOffset;
private final long fileDescriptorLength; private final long fileDescriptorLength;
private IOException preparationError;
private MediaExtractor extractor; private MediaExtractor extractor;
private TrackInfo[] trackInfos; private TrackInfo[] trackInfos;
private boolean prepared; private boolean prepared;
...@@ -128,13 +129,22 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe ...@@ -128,13 +129,22 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
} }
@Override @Override
public boolean prepare(long positionUs) throws IOException { public boolean prepare(long positionUs) {
if (!prepared) { if (!prepared) {
if (preparationError != null) {
return false;
}
extractor = new MediaExtractor(); extractor = new MediaExtractor();
if (context != null) { try {
extractor.setDataSource(context, uri, headers); if (context != null) {
} else { extractor.setDataSource(context, uri, headers);
extractor.setDataSource(fileDescriptor, fileDescriptorOffset, fileDescriptorLength); } else {
extractor.setDataSource(fileDescriptor, fileDescriptorOffset, fileDescriptorLength);
}
} catch (IOException e) {
preparationError = e;
return false;
} }
trackStates = new int[extractor.getTrackCount()]; trackStates = new int[extractor.getTrackCount()];
...@@ -233,6 +243,13 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe ...@@ -233,6 +243,13 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
} }
@Override @Override
public void maybeThrowError() throws IOException {
if (preparationError != null) {
throw preparationError;
}
}
@Override
public void seekToUs(long positionUs) { public void seekToUs(long positionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
seekToUsInternal(positionUs, false); seekToUsInternal(positionUs, false);
......
...@@ -245,17 +245,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -245,17 +245,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected int doPrepare(long positionUs) throws ExoPlaybackException { protected int doPrepare(long positionUs) {
try { boolean sourcePrepared = source.prepare(positionUs);
boolean sourcePrepared = source.prepare(positionUs); if (!sourcePrepared) {
if (!sourcePrepared) { return TrackRenderer.STATE_UNPREPARED;
return TrackRenderer.STATE_UNPREPARED;
}
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
int trackCount = source.getTrackCount();
for (int i = 0; i < source.getTrackCount(); i++) { for (int i = 0; i < trackCount; i++) {
// TODO: Right now this is getting the mime types of the container format // TODO: Right now this is getting the mime types of the container format
// (e.g. audio/mp4 and video/mp4 for fragmented mp4). It needs to be getting the mime types // (e.g. audio/mp4 and video/mp4 for fragmented mp4). It needs to be getting the mime types
// of the actual samples (e.g. audio/mp4a-latm and video/avc). // of the actual samples (e.g. audio/mp4a-latm and video/avc).
...@@ -264,7 +260,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -264,7 +260,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
return TrackRenderer.STATE_PREPARED; return TrackRenderer.STATE_PREPARED;
} }
} }
return TrackRenderer.STATE_IGNORE; return TrackRenderer.STATE_IGNORE;
} }
...@@ -489,39 +484,35 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -489,39 +484,35 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
try { sourceState = source.continueBuffering(trackIndex, positionUs)
sourceState = source.continueBuffering(trackIndex, positionUs) ? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState)
? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState) : SOURCE_STATE_NOT_READY;
: SOURCE_STATE_NOT_READY; checkForDiscontinuity(positionUs);
checkForDiscontinuity(positionUs); if (format == null) {
if (format == null) { readFormat(positionUs);
readFormat(positionUs); }
} if (codec == null && shouldInitCodec()) {
if (codec == null && shouldInitCodec()) { maybeInitCodec();
maybeInitCodec(); }
} if (codec != null) {
if (codec != null) { TraceUtil.beginSection("drainAndFeed");
TraceUtil.beginSection("drainAndFeed"); while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {}
while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {} if (feedInputBuffer(positionUs, true)) {
if (feedInputBuffer(positionUs, true)) { while (feedInputBuffer(positionUs, false)) {}
while (feedInputBuffer(positionUs, false)) {}
}
TraceUtil.endSection();
} }
codecCounters.ensureUpdated(); TraceUtil.endSection();
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
codecCounters.ensureUpdated();
} }
private void readFormat(long positionUs) throws IOException, ExoPlaybackException { private void readFormat(long positionUs) throws ExoPlaybackException {
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.FORMAT_READ) { if (result == SampleSource.FORMAT_READ) {
onInputFormatChanged(formatHolder); onInputFormatChanged(formatHolder);
} }
} }
private void checkForDiscontinuity(long positionUs) throws IOException, ExoPlaybackException { private void checkForDiscontinuity(long positionUs) throws ExoPlaybackException {
if (codec == null) { if (codec == null) {
return; return;
} }
...@@ -560,11 +551,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -560,11 +551,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
* @param firstFeed True if this is the first call to this method from the current invocation of * @param firstFeed True if this is the first call to this method from the current invocation of
* {@link #doSomeWork(long, long)}. False otherwise. * {@link #doSomeWork(long, long)}. False otherwise.
* @return True if it may be possible to feed more input data. False otherwise. * @return True if it may be possible to feed more input data. False otherwise.
* @throws IOException If an error occurs reading data from the upstream source.
* @throws ExoPlaybackException If an error occurs feeding the input buffer. * @throws ExoPlaybackException If an error occurs feeding the input buffer.
*/ */
private boolean feedInputBuffer(long positionUs, boolean firstFeed) private boolean feedInputBuffer(long positionUs, boolean firstFeed) throws ExoPlaybackException {
throws IOException, ExoPlaybackException {
if (inputStreamEnded if (inputStreamEnded
|| codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) { || codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) {
// The input stream has ended, or we need to re-initialize the codec but are still waiting // The input stream has ended, or we need to re-initialize the codec but are still waiting
...@@ -786,6 +775,15 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -786,6 +775,15 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected boolean isEnded() { protected boolean isEnded() {
return outputStreamEnded; return outputStreamEnded;
} }
......
...@@ -76,9 +76,8 @@ public interface SampleSource { ...@@ -76,9 +76,8 @@ public interface SampleSource {
* *
* @param positionUs The player's current playback position. * @param positionUs The player's current playback position.
* @return True if the source was prepared successfully, false otherwise. * @return True if the source was prepared successfully, false otherwise.
* @throws IOException If an error occurred preparing the source.
*/ */
public boolean prepare(long positionUs) throws IOException; public boolean prepare(long positionUs);
/** /**
* Returns the number of tracks exposed by the source. * Returns the number of tracks exposed by the source.
...@@ -117,15 +116,22 @@ public interface SampleSource { ...@@ -117,15 +116,22 @@ public interface SampleSource {
public void disable(int track); public void disable(int track);
/** /**
* If the source is currently having difficulty preparing or loading samples, then this method
* throws the underlying error. Otherwise does nothing.
*
* @throws IOException The underlying error.
*/
public void maybeThrowError() throws IOException;
/**
* Indicates to the source that it should still be buffering data for the specified track. * Indicates to the source that it should still be buffering data for the specified track.
* *
* @param track The track to continue buffering. * @param track The track to continue buffering.
* @param positionUs The current playback position. * @param positionUs The current playback position.
* @return True if the track has available samples, or if the end of the stream has been * @return True if the track has available samples, or if the end of the stream has been
* reached. False if more data needs to be buffered for samples to become available. * reached. False if more data needs to be buffered for samples to become available.
* @throws IOException If an error occurred reading from the source.
*/ */
public boolean continueBuffering(int track, long positionUs) throws IOException; public boolean continueBuffering(int track, long positionUs);
/** /**
* Attempts to read either a sample, a new format or or a discontinuity from the source. * Attempts to read either a sample, a new format or or a discontinuity from the source.
...@@ -147,10 +153,9 @@ public interface SampleSource { ...@@ -147,10 +153,9 @@ public interface SampleSource {
* {@link #DISCONTINUITY_READ} or {@link #NOTHING_READ} can be returned. * {@link #DISCONTINUITY_READ} or {@link #NOTHING_READ} can be returned.
* @return The result, which can be {@link #SAMPLE_READ}, {@link #FORMAT_READ}, * @return The result, which can be {@link #SAMPLE_READ}, {@link #FORMAT_READ},
* {@link #DISCONTINUITY_READ}, {@link #NOTHING_READ} or {@link #END_OF_STREAM}. * {@link #DISCONTINUITY_READ}, {@link #NOTHING_READ} or {@link #END_OF_STREAM}.
* @throws IOException If an error occurred reading from the source.
*/ */
public int readData(int track, long positionUs, MediaFormatHolder formatHolder, public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException; SampleHolder sampleHolder, boolean onlyReadDiscontinuity);
/** /**
* Seeks to the specified time in microseconds. * Seeks to the specified time in microseconds.
......
...@@ -107,6 +107,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -107,6 +107,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* *
* @param positionUs The player's current playback position. * @param positionUs The player's current playback position.
* @return The current state (one of the STATE_* constants), for convenience. * @return The current state (one of the STATE_* constants), for convenience.
* @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final int prepare(long positionUs) throws ExoPlaybackException { /* package */ final int prepare(long positionUs) throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED); Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED);
...@@ -139,6 +140,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -139,6 +140,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* @param joining Whether this renderer is being enabled to join an ongoing playback. If true * @param joining Whether this renderer is being enabled to join an ongoing playback. If true
* then {@link #start} must be called immediately after this method returns (unless a * then {@link #start} must be called immediately after this method returns (unless a
* {@link ExoPlaybackException} is thrown). * {@link ExoPlaybackException} is thrown).
* @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void enable(long positionUs, boolean joining) throws ExoPlaybackException { /* package */ final void enable(long positionUs, boolean joining) throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_PREPARED); Assertions.checkState(state == TrackRenderer.STATE_PREPARED);
...@@ -164,6 +166,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -164,6 +166,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
/** /**
* Starts the renderer, meaning that calls to {@link #doSomeWork(long, long)} will cause the * Starts the renderer, meaning that calls to {@link #doSomeWork(long, long)} will cause the
* track to be rendered. * track to be rendered.
*
* @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void start() throws ExoPlaybackException { /* package */ final void start() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_ENABLED); Assertions.checkState(state == TrackRenderer.STATE_ENABLED);
...@@ -184,6 +188,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -184,6 +188,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
/** /**
* Stops the renderer. * Stops the renderer.
*
* @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void stop() throws ExoPlaybackException { /* package */ final void stop() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_STARTED); Assertions.checkState(state == TrackRenderer.STATE_STARTED);
...@@ -204,6 +210,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -204,6 +210,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
/** /**
* Disable the renderer. * Disable the renderer.
*
* @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void disable() throws ExoPlaybackException { /* package */ final void disable() throws ExoPlaybackException {
Assertions.checkState(state == TrackRenderer.STATE_ENABLED); Assertions.checkState(state == TrackRenderer.STATE_ENABLED);
...@@ -224,6 +232,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -224,6 +232,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
/** /**
* Releases the renderer. * Releases the renderer.
*
* @throws ExoPlaybackException If an error occurs.
*/ */
/* package */ final void release() throws ExoPlaybackException { /* package */ final void release() throws ExoPlaybackException {
Assertions.checkState(state != TrackRenderer.STATE_ENABLED Assertions.checkState(state != TrackRenderer.STATE_ENABLED
...@@ -298,6 +308,15 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -298,6 +308,15 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
throws ExoPlaybackException; throws ExoPlaybackException;
/** /**
* Throws an error that's preventing the renderer from making progress or buffering more data at
* this point in time.
*
* @throws ExoPlaybackException An error that's preventing the renderer from making progress or
* buffering more data.
*/
protected abstract void maybeThrowError() throws ExoPlaybackException;
/**
* Returns the duration of the media being rendered. * Returns the duration of the media being rendered.
* <p> * <p>
* This method may be called when the renderer is in the following states: * This method may be called when the renderer is in the following states:
......
...@@ -188,23 +188,18 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load ...@@ -188,23 +188,18 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
} }
@Override @Override
public boolean continueBuffering(int track, long positionUs) throws IOException { public boolean continueBuffering(int track, long positionUs) {
Assertions.checkState(state == STATE_ENABLED); Assertions.checkState(state == STATE_ENABLED);
Assertions.checkState(track == 0); Assertions.checkState(track == 0);
downstreamPositionUs = positionUs; downstreamPositionUs = positionUs;
chunkSource.continueBuffering(positionUs); chunkSource.continueBuffering(positionUs);
updateLoadControl(); updateLoadControl();
return loadingFinished || !sampleQueue.isEmpty();
boolean haveSamples = !sampleQueue.isEmpty();
if (!haveSamples) {
maybeThrowLoadableException();
}
return loadingFinished || haveSamples;
} }
@Override @Override
public int readData(int track, long positionUs, MediaFormatHolder formatHolder, public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException { SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
Assertions.checkState(state == STATE_ENABLED); Assertions.checkState(state == STATE_ENABLED);
Assertions.checkState(track == 0); Assertions.checkState(track == 0);
downstreamPositionUs = positionUs; downstreamPositionUs = positionUs;
...@@ -219,7 +214,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load ...@@ -219,7 +214,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
} }
if (isPendingReset()) { if (isPendingReset()) {
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
...@@ -252,7 +246,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load ...@@ -252,7 +246,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
if (loadingFinished) { if (loadingFinished) {
return END_OF_STREAM; return END_OF_STREAM;
} }
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
...@@ -263,7 +256,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load ...@@ -263,7 +256,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
return SAMPLE_READ; return SAMPLE_READ;
} }
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
...@@ -295,15 +287,12 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load ...@@ -295,15 +287,12 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load
pendingDiscontinuity = true; pendingDiscontinuity = true;
} }
private void maybeThrowLoadableException() throws IOException { @Override
public void maybeThrowError() throws IOException {
if (currentLoadableException != null && currentLoadableExceptionCount > minLoadableRetryCount) { if (currentLoadableException != null && currentLoadableExceptionCount > minLoadableRetryCount) {
throw currentLoadableException; throw currentLoadableException;
} } else if (currentLoadableHolder.chunk == null) {
if (sampleQueue.isEmpty() && currentLoadableHolder.chunk == null) { chunkSource.maybeThrowError();
IOException chunkSourceException = chunkSource.getError();
if (chunkSourceException != null) {
throw chunkSourceException;
}
} }
} }
......
...@@ -94,13 +94,12 @@ public interface ChunkSource { ...@@ -94,13 +94,12 @@ public interface ChunkSource {
long playbackPositionUs, ChunkOperationHolder out); long playbackPositionUs, ChunkOperationHolder out);
/** /**
* If the {@link ChunkSource} is currently unable to provide chunks through * If the source is currently having difficulty providing chunks, then this method throws the
* {@link ChunkSource#getChunkOperation}, then this method returns the underlying cause. Returns * underlying error. Otherwise does nothing.
* null otherwise.
* *
* @return An {@link IOException}, or null. * @throws IOException The underlying error.
*/ */
IOException getError(); void maybeThrowError() throws IOException;
/** /**
* Invoked when the {@link ChunkSampleSource} has finished loading a chunk obtained from this * Invoked when the {@link ChunkSampleSource} has finished loading a chunk obtained from this
......
...@@ -89,8 +89,8 @@ public class MultiTrackChunkSource implements ChunkSource, ExoPlayerComponent { ...@@ -89,8 +89,8 @@ public class MultiTrackChunkSource implements ChunkSource, ExoPlayerComponent {
} }
@Override @Override
public IOException getError() { public void maybeThrowError() throws IOException {
return null; selectedSource.maybeThrowError();
} }
@Override @Override
......
...@@ -21,7 +21,6 @@ import com.google.android.exoplayer.TrackInfo; ...@@ -21,7 +21,6 @@ import com.google.android.exoplayer.TrackInfo;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import java.io.IOException;
import java.util.List; import java.util.List;
/** /**
...@@ -94,8 +93,8 @@ public class SingleSampleChunkSource implements ChunkSource { ...@@ -94,8 +93,8 @@ public class SingleSampleChunkSource implements ChunkSource {
} }
@Override @Override
public IOException getError() { public void maybeThrowError() {
return null; // Do nothing.
} }
@Override @Override
......
...@@ -514,9 +514,12 @@ public class DashChunkSource implements ChunkSource { ...@@ -514,9 +514,12 @@ public class DashChunkSource implements ChunkSource {
} }
@Override @Override
public IOException getError() { public void maybeThrowError() throws IOException {
return fatalError != null ? fatalError if (fatalError != null) {
: (manifestFetcher != null ? manifestFetcher.getError() : null); throw fatalError;
} else if (manifestFetcher != null) {
manifestFetcher.maybeThrowError();
}
} }
@Override @Override
......
...@@ -172,7 +172,7 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, ...@@ -172,7 +172,7 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader,
} }
@Override @Override
public boolean prepare(long positionUs) throws IOException { public boolean prepare(long positionUs) {
if (prepared) { if (prepared) {
return true; return true;
} }
...@@ -198,10 +198,9 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, ...@@ -198,10 +198,9 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader,
} }
prepared = true; prepared = true;
return true; return true;
} else {
maybeThrowLoadableException();
return false;
} }
return false;
} }
@Override @Override
...@@ -246,7 +245,7 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, ...@@ -246,7 +245,7 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader,
} }
@Override @Override
public boolean continueBuffering(int track, long playbackPositionUs) throws IOException { public boolean continueBuffering(int track, long playbackPositionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
Assertions.checkState(trackEnabledStates[track]); Assertions.checkState(trackEnabledStates[track]);
downstreamPositionUs = playbackPositionUs; downstreamPositionUs = playbackPositionUs;
...@@ -258,16 +257,12 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, ...@@ -258,16 +257,12 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader,
if (isPendingReset()) { if (isPendingReset()) {
return false; return false;
} }
if (sampleQueues.valueAt(track).isEmpty()) { return !sampleQueues.valueAt(track).isEmpty();
maybeThrowLoadableException();
return false;
}
return true;
} }
@Override @Override
public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder, public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder,
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException { SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
downstreamPositionUs = playbackPositionUs; downstreamPositionUs = playbackPositionUs;
if (pendingDiscontinuities[track]) { if (pendingDiscontinuities[track]) {
...@@ -276,7 +271,6 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, ...@@ -276,7 +271,6 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader,
} }
if (onlyReadDiscontinuity || isPendingReset()) { if (onlyReadDiscontinuity || isPendingReset()) {
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
...@@ -304,11 +298,28 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, ...@@ -304,11 +298,28 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader,
return END_OF_STREAM; return END_OF_STREAM;
} }
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
@Override @Override
public void maybeThrowError() throws IOException {
if (currentLoadableException == null) {
return;
}
int minLoadableRetryCountForMedia;
if (minLoadableRetryCount != MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA) {
minLoadableRetryCountForMedia = minLoadableRetryCount;
} else {
minLoadableRetryCountForMedia = seekMap != null && !seekMap.isSeekable()
? DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE
: DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND;
}
if (currentLoadableExceptionCount > minLoadableRetryCountForMedia) {
throw currentLoadableException;
}
}
@Override
public void seekToUs(long positionUs) { public void seekToUs(long positionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
Assertions.checkState(enabledTrackCount > 0); Assertions.checkState(enabledTrackCount > 0);
...@@ -496,23 +507,6 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, ...@@ -496,23 +507,6 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader,
loader.startLoading(loadable, this); loader.startLoading(loadable, this);
} }
private void maybeThrowLoadableException() throws IOException {
if (currentLoadableException == null) {
return;
}
int minLoadableRetryCountForMedia;
if (minLoadableRetryCount != MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA) {
minLoadableRetryCountForMedia = minLoadableRetryCount;
} else {
minLoadableRetryCountForMedia = seekMap != null && !seekMap.isSeekable()
? DEFAULT_MIN_LOADABLE_RETRY_COUNT_LIVE
: DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND;
}
if (currentLoadableExceptionCount > minLoadableRetryCountForMedia) {
throw currentLoadableException;
}
}
private ExtractingLoadable createLoadableFromStart() { private ExtractingLoadable createLoadableFromStart() {
return new ExtractingLoadable(uri, dataSource, extractor, allocator, requestedBufferSize, 0); return new ExtractingLoadable(uri, dataSource, extractor, allocator, requestedBufferSize, 0);
} }
......
...@@ -125,7 +125,7 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -125,7 +125,7 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
} }
@Override @Override
public boolean prepare(long positionUs) throws IOException { public boolean prepare(long positionUs) {
if (prepared) { if (prepared) {
return true; return true;
} }
...@@ -162,7 +162,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -162,7 +162,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
downstreamPositionUs = positionUs; downstreamPositionUs = positionUs;
} }
maybeStartLoading(); maybeStartLoading();
maybeThrowLoadableException();
return false; return false;
} }
...@@ -218,7 +217,7 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -218,7 +217,7 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
} }
@Override @Override
public boolean continueBuffering(int track, long playbackPositionUs) throws IOException { public boolean continueBuffering(int track, long playbackPositionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
Assertions.checkState(trackEnabledStates[track]); Assertions.checkState(trackEnabledStates[track]);
downstreamPositionUs = playbackPositionUs; downstreamPositionUs = playbackPositionUs;
...@@ -232,7 +231,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -232,7 +231,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
if (isPendingReset() || extractors.isEmpty()) { if (isPendingReset() || extractors.isEmpty()) {
return false; return false;
} }
for (int extractorIndex = 0; extractorIndex < extractors.size(); extractorIndex++) { for (int extractorIndex = 0; extractorIndex < extractors.size(); extractorIndex++) {
HlsExtractorWrapper extractor = extractors.get(extractorIndex); HlsExtractorWrapper extractor = extractors.get(extractorIndex);
if (!extractor.isPrepared()) { if (!extractor.isPrepared()) {
...@@ -242,13 +240,12 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -242,13 +240,12 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
return true; return true;
} }
} }
maybeThrowLoadableException();
return false; return false;
} }
@Override @Override
public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder, public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder,
SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException { SampleHolder sampleHolder, boolean onlyReadDiscontinuity) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
downstreamPositionUs = playbackPositionUs; downstreamPositionUs = playbackPositionUs;
...@@ -262,13 +259,11 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -262,13 +259,11 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
} }
if (isPendingReset()) { if (isPendingReset()) {
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
HlsExtractorWrapper extractor = getCurrentExtractor(); HlsExtractorWrapper extractor = getCurrentExtractor();
if (!extractor.isPrepared()) { if (!extractor.isPrepared()) {
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
...@@ -290,7 +285,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -290,7 +285,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
// next one for the current read. // next one for the current read.
extractor = extractors.get(++extractorIndex); extractor = extractors.get(++extractorIndex);
if (!extractor.isPrepared()) { if (!extractor.isPrepared()) {
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
} }
...@@ -313,11 +307,17 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -313,11 +307,17 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
return END_OF_STREAM; return END_OF_STREAM;
} }
maybeThrowLoadableException();
return NOTHING_READ; return NOTHING_READ;
} }
@Override @Override
public void maybeThrowError() throws IOException {
if (currentLoadableException != null && currentLoadableExceptionCount > minLoadableRetryCount) {
throw currentLoadableException;
}
}
@Override
public void seekToUs(long positionUs) { public void seekToUs(long positionUs) {
Assertions.checkState(prepared); Assertions.checkState(prepared);
Assertions.checkState(enabledTrackCount > 0); Assertions.checkState(enabledTrackCount > 0);
...@@ -361,6 +361,8 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -361,6 +361,8 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
} }
} }
// Loader.Callback implementation.
@Override @Override
public void onLoadCompleted(Loadable loadable) { public void onLoadCompleted(Loadable loadable) {
Assertions.checkState(loadable == currentLoadable); Assertions.checkState(loadable == currentLoadable);
...@@ -412,6 +414,8 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -412,6 +414,8 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
maybeStartLoading(); maybeStartLoading();
} }
// Internal stuff.
/** /**
* Gets the current extractor from which samples should be read. * Gets the current extractor from which samples should be read.
* <p> * <p>
...@@ -455,12 +459,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader ...@@ -455,12 +459,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader
return false; return false;
} }
private void maybeThrowLoadableException() throws IOException {
if (currentLoadableException != null && currentLoadableExceptionCount > minLoadableRetryCount) {
throw currentLoadableException;
}
}
private void restartFrom(long positionUs) { private void restartFrom(long positionUs) {
pendingResetPositionUs = positionUs; pendingResetPositionUs = positionUs;
loadingFinished = false; loadingFinished = false;
......
...@@ -90,16 +90,13 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback ...@@ -90,16 +90,13 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback
} }
@Override @Override
protected int doPrepare(long positionUs) throws ExoPlaybackException { protected int doPrepare(long positionUs) {
try { boolean sourcePrepared = source.prepare(positionUs);
boolean sourcePrepared = source.prepare(positionUs); if (!sourcePrepared) {
if (!sourcePrepared) { return TrackRenderer.STATE_UNPREPARED;
return TrackRenderer.STATE_UNPREPARED;
}
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
for (int i = 0; i < source.getTrackCount(); i++) { int trackCount = source.getTrackCount();
for (int i = 0; i < trackCount; i++) {
if (metadataParser.canParse(source.getTrackInfo(i).mimeType)) { if (metadataParser.canParse(source.getTrackInfo(i).mimeType)) {
trackIndex = i; trackIndex = i;
return TrackRenderer.STATE_PREPARED; return TrackRenderer.STATE_PREPARED;
...@@ -128,29 +125,21 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback ...@@ -128,29 +125,21 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) protected void doSomeWork(long positionUs, long elapsedRealtimeUs)
throws ExoPlaybackException { throws ExoPlaybackException {
try { source.continueBuffering(trackIndex, positionUs);
source.continueBuffering(trackIndex, positionUs);
} catch (IOException e) {
// TODO: This should be propagated, but in the current design propagation may occur too
// early. See [Internal b/22291244].
// throw new ExoPlaybackException(e);
}
if (!inputStreamEnded && pendingMetadata == null) { if (!inputStreamEnded && pendingMetadata == null) {
try {
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false);
if (result == SampleSource.SAMPLE_READ) { if (result == SampleSource.SAMPLE_READ) {
pendingMetadataTimestamp = sampleHolder.timeUs; pendingMetadataTimestamp = sampleHolder.timeUs;
pendingMetadata = metadataParser.parse(sampleHolder.data.array(), sampleHolder.size); try {
pendingMetadata = metadataParser.parse(sampleHolder.data.array(), sampleHolder.size);
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
sampleHolder.data.clear(); sampleHolder.data.clear();
} else if (result == SampleSource.END_OF_STREAM) { } else if (result == SampleSource.END_OF_STREAM) {
inputStreamEnded = true; inputStreamEnded = true;
} }
} catch (IOException e) {
// TODO: This should be propagated, but in the current design propagation may occur too
// early. See [Internal b/22291244].
// throw new ExoPlaybackException(e);
}
} }
if (pendingMetadata != null && pendingMetadataTimestamp <= positionUs) { if (pendingMetadata != null && pendingMetadataTimestamp <= positionUs) {
...@@ -160,6 +149,15 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback ...@@ -160,6 +149,15 @@ public class MetadataTrackRenderer<T> extends TrackRenderer implements Callback
} }
@Override @Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected void onDisabled() { protected void onDisabled() {
pendingMetadata = null; pendingMetadata = null;
source.disable(trackIndex); source.disable(trackIndex);
......
...@@ -324,9 +324,12 @@ public class SmoothStreamingChunkSource implements ChunkSource { ...@@ -324,9 +324,12 @@ public class SmoothStreamingChunkSource implements ChunkSource {
} }
@Override @Override
public IOException getError() { public void maybeThrowError() throws IOException {
return fatalError != null ? fatalError if (fatalError != null) {
: (manifestFetcher != null ? manifestFetcher.getError() : null); throw fatalError;
} else {
manifestFetcher.maybeThrowError();
}
} }
@Override @Override
......
...@@ -151,17 +151,14 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { ...@@ -151,17 +151,14 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected int doPrepare(long positionUs) throws ExoPlaybackException { protected int doPrepare(long positionUs) {
try { boolean sourcePrepared = source.prepare(positionUs);
boolean sourcePrepared = source.prepare(positionUs); if (!sourcePrepared) {
if (!sourcePrepared) { return TrackRenderer.STATE_UNPREPARED;
return TrackRenderer.STATE_UNPREPARED;
}
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
int trackCount = source.getTrackCount();
for (int i = 0; i < subtitleParsers.length; i++) { for (int i = 0; i < subtitleParsers.length; i++) {
for (int j = 0; j < source.getTrackCount(); j++) { for (int j = 0; j < trackCount; j++) {
if (subtitleParsers[i].canParse(source.getTrackInfo(j).mimeType)) { if (subtitleParsers[i].canParse(source.getTrackInfo(j).mimeType)) {
parserIndex = i; parserIndex = i;
trackIndex = j; trackIndex = j;
...@@ -197,11 +194,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { ...@@ -197,11 +194,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
try { source.continueBuffering(trackIndex, positionUs);
source.continueBuffering(trackIndex, positionUs);
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
if (nextSubtitle == null) { if (nextSubtitle == null) {
try { try {
...@@ -240,17 +233,13 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { ...@@ -240,17 +233,13 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
if (!inputStreamEnded && nextSubtitle == null && !parserHelper.isParsing()) { if (!inputStreamEnded && nextSubtitle == null && !parserHelper.isParsing()) {
// Try and read the next subtitle from the source. // Try and read the next subtitle from the source.
try { SampleHolder sampleHolder = parserHelper.getSampleHolder();
SampleHolder sampleHolder = parserHelper.getSampleHolder(); sampleHolder.clearData();
sampleHolder.clearData(); int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false);
int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); if (result == SampleSource.SAMPLE_READ) {
if (result == SampleSource.SAMPLE_READ) { parserHelper.startParseOperation();
parserHelper.startParseOperation(); } else if (result == SampleSource.END_OF_STREAM) {
} else if (result == SampleSource.END_OF_STREAM) { inputStreamEnded = true;
inputStreamEnded = true;
}
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
} }
} }
...@@ -272,6 +261,15 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { ...@@ -272,6 +261,15 @@ public class TextTrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected long getDurationUs() { protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs; return source.getTrackInfo(trackIndex).durationUs;
} }
......
...@@ -92,16 +92,13 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { ...@@ -92,16 +92,13 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected int doPrepare(long positionUs) throws ExoPlaybackException { protected int doPrepare(long positionUs) {
try { boolean sourcePrepared = source.prepare(positionUs);
boolean sourcePrepared = source.prepare(positionUs); if (!sourcePrepared) {
if (!sourcePrepared) { return TrackRenderer.STATE_UNPREPARED;
return TrackRenderer.STATE_UNPREPARED;
}
} catch (IOException e) {
throw new ExoPlaybackException(e);
} }
for (int i = 0; i < source.getTrackCount(); i++) { int trackCount = source.getTrackCount();
for (int i = 0; i < trackCount; i++) {
if (eia608Parser.canParse(source.getTrackInfo(i).mimeType)) { if (eia608Parser.canParse(source.getTrackInfo(i).mimeType)) {
trackIndex = i; trackIndex = i;
return TrackRenderer.STATE_PREPARED; return TrackRenderer.STATE_PREPARED;
...@@ -133,13 +130,7 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { ...@@ -133,13 +130,7 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback {
@Override @Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
try { source.continueBuffering(trackIndex, positionUs);
source.continueBuffering(trackIndex, positionUs);
} catch (IOException e) {
// TODO: This should be propagated, but in the current design propagation may occur too
// early. See [Internal b/22291244].
// throw new ExoPlaybackException(e);
}
if (isSamplePending()) { if (isSamplePending()) {
maybeParsePendingSample(positionUs); maybeParsePendingSample(positionUs);
...@@ -147,17 +138,11 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { ...@@ -147,17 +138,11 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback {
int result = inputStreamEnded ? SampleSource.END_OF_STREAM : SampleSource.SAMPLE_READ; int result = inputStreamEnded ? SampleSource.END_OF_STREAM : SampleSource.SAMPLE_READ;
while (!isSamplePending() && result == SampleSource.SAMPLE_READ) { while (!isSamplePending() && result == SampleSource.SAMPLE_READ) {
try { result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false);
result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); if (result == SampleSource.SAMPLE_READ) {
if (result == SampleSource.SAMPLE_READ) { maybeParsePendingSample(positionUs);
maybeParsePendingSample(positionUs); } else if (result == SampleSource.END_OF_STREAM) {
} else if (result == SampleSource.END_OF_STREAM) { inputStreamEnded = true;
inputStreamEnded = true;
}
} catch (IOException e) {
// TODO: This should be propagated, but in the current design propagation may occur too
// early. See [Internal b/22291244].
// throw new ExoPlaybackException(e);
} }
} }
...@@ -182,6 +167,15 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { ...@@ -182,6 +167,15 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected void maybeThrowError() throws ExoPlaybackException {
try {
source.maybeThrowError();
} catch (IOException e) {
throw new ExoPlaybackException(e);
}
}
@Override
protected long getDurationUs() { protected long getDurationUs() {
return source.getTrackInfo(trackIndex).durationUs; return source.getTrackInfo(trackIndex).durationUs;
} }
......
...@@ -186,17 +186,17 @@ public class ManifestFetcher<T> implements Loader.Callback { ...@@ -186,17 +186,17 @@ public class ManifestFetcher<T> implements Loader.Callback {
} }
/** /**
* Gets the error that affected the most recent attempt to load the manifest, or null if the * Throws the error that affected the most recent attempt to load the manifest. Does nothing if
* most recent attempt was successful. * the most recent attempt was successful.
* *
* @return The error, or null if the most recent attempt was successful. * @throws IOException The error that affected the most recent attempt to load the manifest.
*/ */
public IOException getError() { public void maybeThrowError() throws IOException {
if (loadExceptionCount <= 1) { // Don't throw an exception until at least 1 retry attempt has been made.
// Don't report an exception until at least 1 retry attempt has been made. if (loadException == null || loadExceptionCount <= 1) {
return null; return;
} }
return loadException; throw loadException;
} }
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment