diff --git a/extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java b/extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java index 96ce46f4f2..2ab807f899 100644 --- a/extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java +++ b/extensions/opus/src/main/java/com/google/android/exoplayer/ext/opus/LibopusAudioTrackRenderer.java @@ -126,24 +126,19 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo } @Override - protected int doPrepare(long positionUs) throws ExoPlaybackException { - try { - boolean sourcePrepared = source.prepare(positionUs); - if (!sourcePrepared) { - return TrackRenderer.STATE_UNPREPARED; - } - } catch (IOException e) { - throw new ExoPlaybackException(e); + protected int doPrepare(long positionUs) { + boolean sourcePrepared = source.prepare(positionUs); + if (!sourcePrepared) { + return TrackRenderer.STATE_UNPREPARED; } - - for (int i = 0; i < source.getTrackCount(); i++) { + int trackCount = source.getTrackCount(); + for (int i = 0; i < trackCount; i++) { if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_OPUS) || source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.AUDIO_WEBM)) { trackIndex = i; return TrackRenderer.STATE_PREPARED; } } - return TrackRenderer.STATE_IGNORE; } @@ -152,42 +147,49 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo if (outputStreamEnded) { return; } - try { - sourceIsReady = source.continueBuffering(trackIndex, positionUs); - checkForDiscontinuity(); - if (format == null) { - readFormat(); - } else { - // Create the decoder. - 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 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(); + sourceIsReady = source.continueBuffering(trackIndex, positionUs); + checkForDiscontinuity(); - // Queue input buffers. - while (feedInputBuffer()) {} + // Try and read a format if we don't have one already. + 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. + 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 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) { notifyAudioTrackInitializationError(e); throw new ExoPlaybackException(e); @@ -197,8 +199,6 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo } catch (OpusDecoderException e) { notifyDecoderError(e); throw new ExoPlaybackException(e); - } catch (IOException e) { - throw new ExoPlaybackException(e); } } @@ -249,7 +249,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo } } - private boolean feedInputBuffer() throws IOException, OpusDecoderException { + private boolean feedInputBuffer() throws OpusDecoderException { if (inputStreamEnded) { return false; } @@ -291,7 +291,7 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo return true; } - private void checkForDiscontinuity() throws IOException { + private void checkForDiscontinuity() { if (decoder == null) { return; } @@ -394,12 +394,23 @@ public class LibopusAudioTrackRenderer extends TrackRenderer implements MediaClo } } - private void readFormat() throws IOException { - int result = source.readData(trackIndex, currentPositionUs, formatHolder, null, false); + @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); if (result == SampleSource.FORMAT_READ) { format = formatHolder.format; audioTrack.reconfigure(format.getFrameworkMediaFormatV16()); + return true; } + return false; } @Override diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java index b6b3cc06a1..4c8745d006 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java @@ -153,23 +153,18 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { @Override protected int doPrepare(long positionUs) throws ExoPlaybackException { - try { - boolean sourcePrepared = source.prepare(positionUs); - if (!sourcePrepared) { - return TrackRenderer.STATE_UNPREPARED; - } - } catch (IOException e) { - throw new ExoPlaybackException(e); + boolean sourcePrepared = source.prepare(positionUs); + if (!sourcePrepared) { + return TrackRenderer.STATE_UNPREPARED; } - - for (int i = 0; i < source.getTrackCount(); i++) { + int trackCount = source.getTrackCount(); + for (int i = 0; i < trackCount; i++) { if (source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.VIDEO_VP9) || source.getTrackInfo(i).mimeType.equalsIgnoreCase(MimeTypes.VIDEO_WEBM)) { trackIndex = i; return TrackRenderer.STATE_PREPARED; } } - return TrackRenderer.STATE_IGNORE; } @@ -178,28 +173,29 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { if (outputStreamEnded) { return; } - try { - sourceIsReady = source.continueBuffering(trackIndex, 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); + sourceIsReady = source.continueBuffering(trackIndex, positionUs); + checkForDiscontinuity(positionUs); - // Queue input buffers. - while (feedInputBuffer(positionUs)) {} - } + // Try and read a format if we don't have one already. + 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) { notifyDecoderError(e); throw new ExoPlaybackException(e); - } catch (IOException e) { - throw new ExoPlaybackException(e); } } @@ -294,7 +290,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { surface.unlockCanvasAndPost(canvas); } - private boolean feedInputBuffer(long positionUs) throws IOException, VpxDecoderException { + private boolean feedInputBuffer(long positionUs) throws VpxDecoderException { if (inputStreamEnded) { return false; } @@ -334,7 +330,7 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { return true; } - private void checkForDiscontinuity(long positionUs) throws IOException { + private void checkForDiscontinuity(long positionUs) { if (decoder == null) { return; } @@ -417,11 +413,22 @@ public class LibvpxVideoTrackRenderer extends TrackRenderer { 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); if (result == SampleSource.FORMAT_READ) { format = formatHolder.format; + return true; } + return false; } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java index f926073740..dfcf7660a8 100644 --- a/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/DummyTrackRenderer.java @@ -25,7 +25,7 @@ package com.google.android.exoplayer; public class DummyTrackRenderer extends TrackRenderer { @Override - protected int doPrepare(long positionUs) throws ExoPlaybackException { + protected int doPrepare(long positionUs) { return STATE_IGNORE; } @@ -49,6 +49,11 @@ public class DummyTrackRenderer extends TrackRenderer { throw new IllegalStateException(); } + @Override + protected void maybeThrowError() { + throw new IllegalStateException(); + } + @Override protected long getDurationUs() { throw new IllegalStateException(); diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java index 17e69bf1b7..4737a36919 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java @@ -266,10 +266,12 @@ import java.util.List; private void incrementalPrepareInternal() throws ExoPlaybackException { long operationStartTimeMs = SystemClock.elapsedRealtime(); boolean prepared = true; - for (int i = 0; i < renderers.length; i++) { - if (renderers[i].getState() == TrackRenderer.STATE_UNPREPARED) { - int state = renderers[i].prepare(positionUs); + for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) { + TrackRenderer renderer = renderers[rendererIndex]; + if (renderer.getState() == TrackRenderer.STATE_UNPREPARED) { + int state = renderer.prepare(positionUs); if (state == TrackRenderer.STATE_UNPREPARED) { + renderer.maybeThrowError(); prepared = false; } } @@ -414,7 +416,14 @@ import java.util.List; // invocation of this method. renderer.doSomeWork(positionUs, elapsedRealtimeUs); 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) { // We've already encountered a track for which the buffered position is unknown. Hence the diff --git a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java index 02d599aca4..6cf393c0e7 100644 --- a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java @@ -76,6 +76,7 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe private final long fileDescriptorOffset; private final long fileDescriptorLength; + private IOException preparationError; private MediaExtractor extractor; private TrackInfo[] trackInfos; private boolean prepared; @@ -128,13 +129,22 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe } @Override - public boolean prepare(long positionUs) throws IOException { + public boolean prepare(long positionUs) { if (!prepared) { + if (preparationError != null) { + return false; + } + extractor = new MediaExtractor(); - if (context != null) { - extractor.setDataSource(context, uri, headers); - } else { - extractor.setDataSource(fileDescriptor, fileDescriptorOffset, fileDescriptorLength); + try { + if (context != null) { + extractor.setDataSource(context, uri, headers); + } else { + extractor.setDataSource(fileDescriptor, fileDescriptorOffset, fileDescriptorLength); + } + } catch (IOException e) { + preparationError = e; + return false; } trackStates = new int[extractor.getTrackCount()]; @@ -232,6 +242,13 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe trackStates[track] = TRACK_STATE_DISABLED; } + @Override + public void maybeThrowError() throws IOException { + if (preparationError != null) { + throw preparationError; + } + } + @Override public void seekToUs(long positionUs) { Assertions.checkState(prepared); diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java index 7ebfd795e9..f54e8947c0 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -245,17 +245,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { } @Override - protected int doPrepare(long positionUs) throws ExoPlaybackException { - try { - boolean sourcePrepared = source.prepare(positionUs); - if (!sourcePrepared) { - return TrackRenderer.STATE_UNPREPARED; - } - } catch (IOException e) { - throw new ExoPlaybackException(e); + protected int doPrepare(long positionUs) { + boolean sourcePrepared = source.prepare(positionUs); + if (!sourcePrepared) { + return TrackRenderer.STATE_UNPREPARED; } - - for (int i = 0; i < source.getTrackCount(); i++) { + int trackCount = source.getTrackCount(); + for (int i = 0; i < trackCount; i++) { // 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 // of the actual samples (e.g. audio/mp4a-latm and video/avc). @@ -264,7 +260,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { return TrackRenderer.STATE_PREPARED; } } - return TrackRenderer.STATE_IGNORE; } @@ -489,39 +484,35 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { @Override protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { - try { - sourceState = source.continueBuffering(trackIndex, positionUs) - ? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState) - : SOURCE_STATE_NOT_READY; - checkForDiscontinuity(positionUs); - if (format == null) { - readFormat(positionUs); - } - if (codec == null && shouldInitCodec()) { - maybeInitCodec(); - } - if (codec != null) { - TraceUtil.beginSection("drainAndFeed"); - while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {} - if (feedInputBuffer(positionUs, true)) { - while (feedInputBuffer(positionUs, false)) {} - } - TraceUtil.endSection(); - } - codecCounters.ensureUpdated(); - } catch (IOException e) { - throw new ExoPlaybackException(e); + sourceState = source.continueBuffering(trackIndex, positionUs) + ? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState) + : SOURCE_STATE_NOT_READY; + checkForDiscontinuity(positionUs); + if (format == null) { + readFormat(positionUs); } + if (codec == null && shouldInitCodec()) { + maybeInitCodec(); + } + if (codec != null) { + TraceUtil.beginSection("drainAndFeed"); + while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {} + if (feedInputBuffer(positionUs, true)) { + while (feedInputBuffer(positionUs, false)) {} + } + TraceUtil.endSection(); + } + 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); if (result == SampleSource.FORMAT_READ) { onInputFormatChanged(formatHolder); } } - private void checkForDiscontinuity(long positionUs) throws IOException, ExoPlaybackException { + private void checkForDiscontinuity(long positionUs) throws ExoPlaybackException { if (codec == null) { return; } @@ -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 * {@link #doSomeWork(long, long)}. 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. */ - private boolean feedInputBuffer(long positionUs, boolean firstFeed) - throws IOException, ExoPlaybackException { + private boolean feedInputBuffer(long positionUs, boolean firstFeed) throws ExoPlaybackException { if (inputStreamEnded || codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) { // The input stream has ended, or we need to re-initialize the codec but are still waiting @@ -785,6 +774,15 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { return false; } + @Override + protected void maybeThrowError() throws ExoPlaybackException { + try { + source.maybeThrowError(); + } catch (IOException e) { + throw new ExoPlaybackException(e); + } + } + @Override protected boolean isEnded() { return outputStreamEnded; diff --git a/library/src/main/java/com/google/android/exoplayer/SampleSource.java b/library/src/main/java/com/google/android/exoplayer/SampleSource.java index 3463dc9fcf..539983d200 100644 --- a/library/src/main/java/com/google/android/exoplayer/SampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/SampleSource.java @@ -76,9 +76,8 @@ public interface SampleSource { * * @param positionUs The player's current playback position. * @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. @@ -116,6 +115,14 @@ public interface SampleSource { */ 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. * @@ -123,9 +130,8 @@ public interface SampleSource { * @param positionUs The current playback position. * @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. - * @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. @@ -147,10 +153,9 @@ public interface SampleSource { * {@link #DISCONTINUITY_READ} or {@link #NOTHING_READ} can be returned. * @return The result, which can be {@link #SAMPLE_READ}, {@link #FORMAT_READ}, * {@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, - SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException; + SampleHolder sampleHolder, boolean onlyReadDiscontinuity); /** * Seeks to the specified time in microseconds. diff --git a/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java index cb4b866f15..ffc02caf4e 100644 --- a/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java @@ -107,6 +107,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent { * * @param positionUs The player's current playback position. * @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 { Assertions.checkState(state == TrackRenderer.STATE_UNPREPARED); @@ -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 * then {@link #start} must be called immediately after this method returns (unless a * {@link ExoPlaybackException} is thrown). + * @throws ExoPlaybackException If an error occurs. */ /* package */ final void enable(long positionUs, boolean joining) throws ExoPlaybackException { Assertions.checkState(state == TrackRenderer.STATE_PREPARED); @@ -164,6 +166,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent { /** * Starts the renderer, meaning that calls to {@link #doSomeWork(long, long)} will cause the * track to be rendered. + * + * @throws ExoPlaybackException If an error occurs. */ /* package */ final void start() throws ExoPlaybackException { Assertions.checkState(state == TrackRenderer.STATE_ENABLED); @@ -184,6 +188,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent { /** * Stops the renderer. + * + * @throws ExoPlaybackException If an error occurs. */ /* package */ final void stop() throws ExoPlaybackException { Assertions.checkState(state == TrackRenderer.STATE_STARTED); @@ -204,6 +210,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent { /** * Disable the renderer. + * + * @throws ExoPlaybackException If an error occurs. */ /* package */ final void disable() throws ExoPlaybackException { Assertions.checkState(state == TrackRenderer.STATE_ENABLED); @@ -224,6 +232,8 @@ public abstract class TrackRenderer implements ExoPlayerComponent { /** * Releases the renderer. + * + * @throws ExoPlaybackException If an error occurs. */ /* package */ final void release() throws ExoPlaybackException { Assertions.checkState(state != TrackRenderer.STATE_ENABLED @@ -297,6 +307,15 @@ public abstract class TrackRenderer implements ExoPlayerComponent { protected abstract void doSomeWork(long positionUs, long elapsedRealtimeUs) 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. *

diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java index 4837f0a5d5..b9302bc84d 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSampleSource.java @@ -188,23 +188,18 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load } @Override - public boolean continueBuffering(int track, long positionUs) throws IOException { + public boolean continueBuffering(int track, long positionUs) { Assertions.checkState(state == STATE_ENABLED); Assertions.checkState(track == 0); downstreamPositionUs = positionUs; chunkSource.continueBuffering(positionUs); updateLoadControl(); - - boolean haveSamples = !sampleQueue.isEmpty(); - if (!haveSamples) { - maybeThrowLoadableException(); - } - return loadingFinished || haveSamples; + return loadingFinished || !sampleQueue.isEmpty(); } @Override 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(track == 0); downstreamPositionUs = positionUs; @@ -219,7 +214,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load } if (isPendingReset()) { - maybeThrowLoadableException(); return NOTHING_READ; } @@ -252,7 +246,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load if (loadingFinished) { return END_OF_STREAM; } - maybeThrowLoadableException(); return NOTHING_READ; } @@ -263,7 +256,6 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load return SAMPLE_READ; } - maybeThrowLoadableException(); return NOTHING_READ; } @@ -295,15 +287,12 @@ public class ChunkSampleSource implements SampleSource, SampleSourceReader, Load pendingDiscontinuity = true; } - private void maybeThrowLoadableException() throws IOException { + @Override + public void maybeThrowError() throws IOException { if (currentLoadableException != null && currentLoadableExceptionCount > minLoadableRetryCount) { throw currentLoadableException; - } - if (sampleQueue.isEmpty() && currentLoadableHolder.chunk == null) { - IOException chunkSourceException = chunkSource.getError(); - if (chunkSourceException != null) { - throw chunkSourceException; - } + } else if (currentLoadableHolder.chunk == null) { + chunkSource.maybeThrowError(); } } diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java index 88306bc8f4..1723f201d1 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkSource.java @@ -94,13 +94,12 @@ public interface ChunkSource { long playbackPositionUs, ChunkOperationHolder out); /** - * If the {@link ChunkSource} is currently unable to provide chunks through - * {@link ChunkSource#getChunkOperation}, then this method returns the underlying cause. Returns - * null otherwise. + * If the source is currently having difficulty providing chunks, then this method throws the + * underlying error. Otherwise does nothing. * - * @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 diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/MultiTrackChunkSource.java b/library/src/main/java/com/google/android/exoplayer/chunk/MultiTrackChunkSource.java index 84f91c5465..549ab66f96 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/MultiTrackChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/MultiTrackChunkSource.java @@ -89,8 +89,8 @@ public class MultiTrackChunkSource implements ChunkSource, ExoPlayerComponent { } @Override - public IOException getError() { - return null; + public void maybeThrowError() throws IOException { + selectedSource.maybeThrowError(); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java index 8ee6c9adad..a0d6678ef1 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java @@ -21,7 +21,6 @@ import com.google.android.exoplayer.TrackInfo; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSpec; -import java.io.IOException; import java.util.List; /** @@ -94,8 +93,8 @@ public class SingleSampleChunkSource implements ChunkSource { } @Override - public IOException getError() { - return null; + public void maybeThrowError() { + // Do nothing. } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index 0b0da1e8f4..e2d63580dc 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -514,9 +514,12 @@ public class DashChunkSource implements ChunkSource { } @Override - public IOException getError() { - return fatalError != null ? fatalError - : (manifestFetcher != null ? manifestFetcher.getError() : null); + public void maybeThrowError() throws IOException { + if (fatalError != null) { + throw fatalError; + } else if (manifestFetcher != null) { + manifestFetcher.maybeThrowError(); + } } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java index 37e2ccba18..a365c73626 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/ExtractorSampleSource.java @@ -172,7 +172,7 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, } @Override - public boolean prepare(long positionUs) throws IOException { + public boolean prepare(long positionUs) { if (prepared) { return true; } @@ -198,10 +198,9 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, } prepared = true; return true; - } else { - maybeThrowLoadableException(); - return false; } + + return false; } @Override @@ -246,7 +245,7 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, } @Override - public boolean continueBuffering(int track, long playbackPositionUs) throws IOException { + public boolean continueBuffering(int track, long playbackPositionUs) { Assertions.checkState(prepared); Assertions.checkState(trackEnabledStates[track]); downstreamPositionUs = playbackPositionUs; @@ -258,16 +257,12 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, if (isPendingReset()) { return false; } - if (sampleQueues.valueAt(track).isEmpty()) { - maybeThrowLoadableException(); - return false; - } - return true; + return !sampleQueues.valueAt(track).isEmpty(); } @Override public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder, - SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException { + SampleHolder sampleHolder, boolean onlyReadDiscontinuity) { downstreamPositionUs = playbackPositionUs; if (pendingDiscontinuities[track]) { @@ -276,7 +271,6 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, } if (onlyReadDiscontinuity || isPendingReset()) { - maybeThrowLoadableException(); return NOTHING_READ; } @@ -304,10 +298,27 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, return END_OF_STREAM; } - maybeThrowLoadableException(); return NOTHING_READ; } + @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) { Assertions.checkState(prepared); @@ -496,23 +507,6 @@ public class ExtractorSampleSource implements SampleSource, SampleSourceReader, 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() { return new ExtractingLoadable(uri, dataSource, extractor, allocator, requestedBufferSize, 0); } diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java index b3a2dbdc34..c745dba20b 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsSampleSource.java @@ -125,7 +125,7 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader } @Override - public boolean prepare(long positionUs) throws IOException { + public boolean prepare(long positionUs) { if (prepared) { return true; } @@ -162,7 +162,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader downstreamPositionUs = positionUs; } maybeStartLoading(); - maybeThrowLoadableException(); return false; } @@ -218,7 +217,7 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader } @Override - public boolean continueBuffering(int track, long playbackPositionUs) throws IOException { + public boolean continueBuffering(int track, long playbackPositionUs) { Assertions.checkState(prepared); Assertions.checkState(trackEnabledStates[track]); downstreamPositionUs = playbackPositionUs; @@ -232,7 +231,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader if (isPendingReset() || extractors.isEmpty()) { return false; } - for (int extractorIndex = 0; extractorIndex < extractors.size(); extractorIndex++) { HlsExtractorWrapper extractor = extractors.get(extractorIndex); if (!extractor.isPrepared()) { @@ -242,13 +240,12 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader return true; } } - maybeThrowLoadableException(); return false; } @Override public int readData(int track, long playbackPositionUs, MediaFormatHolder formatHolder, - SampleHolder sampleHolder, boolean onlyReadDiscontinuity) throws IOException { + SampleHolder sampleHolder, boolean onlyReadDiscontinuity) { Assertions.checkState(prepared); downstreamPositionUs = playbackPositionUs; @@ -262,13 +259,11 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader } if (isPendingReset()) { - maybeThrowLoadableException(); return NOTHING_READ; } HlsExtractorWrapper extractor = getCurrentExtractor(); if (!extractor.isPrepared()) { - maybeThrowLoadableException(); return NOTHING_READ; } @@ -290,7 +285,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader // next one for the current read. extractor = extractors.get(++extractorIndex); if (!extractor.isPrepared()) { - maybeThrowLoadableException(); return NOTHING_READ; } } @@ -313,10 +307,16 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader return END_OF_STREAM; } - maybeThrowLoadableException(); return NOTHING_READ; } + @Override + public void maybeThrowError() throws IOException { + if (currentLoadableException != null && currentLoadableExceptionCount > minLoadableRetryCount) { + throw currentLoadableException; + } + } + @Override public void seekToUs(long positionUs) { Assertions.checkState(prepared); @@ -361,6 +361,8 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader } } + // Loader.Callback implementation. + @Override public void onLoadCompleted(Loadable loadable) { Assertions.checkState(loadable == currentLoadable); @@ -412,6 +414,8 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader maybeStartLoading(); } + // Internal stuff. + /** * Gets the current extractor from which samples should be read. *

@@ -455,12 +459,6 @@ public class HlsSampleSource implements SampleSource, SampleSourceReader, Loader return false; } - private void maybeThrowLoadableException() throws IOException { - if (currentLoadableException != null && currentLoadableExceptionCount > minLoadableRetryCount) { - throw currentLoadableException; - } - } - private void restartFrom(long positionUs) { pendingResetPositionUs = positionUs; loadingFinished = false; diff --git a/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java index 6376f88a61..b1722e2596 100644 --- a/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java @@ -90,16 +90,13 @@ public class MetadataTrackRenderer extends TrackRenderer implements Callback } @Override - protected int doPrepare(long positionUs) throws ExoPlaybackException { - try { - boolean sourcePrepared = source.prepare(positionUs); - if (!sourcePrepared) { - return TrackRenderer.STATE_UNPREPARED; - } - } catch (IOException e) { - throw new ExoPlaybackException(e); + protected int doPrepare(long positionUs) { + boolean sourcePrepared = source.prepare(positionUs); + if (!sourcePrepared) { + return TrackRenderer.STATE_UNPREPARED; } - 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)) { trackIndex = i; return TrackRenderer.STATE_PREPARED; @@ -128,29 +125,21 @@ public class MetadataTrackRenderer extends TrackRenderer implements Callback @Override protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { - try { - 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); - } + source.continueBuffering(trackIndex, positionUs); if (!inputStreamEnded && pendingMetadata == null) { - try { int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); if (result == SampleSource.SAMPLE_READ) { 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(); } else if (result == SampleSource.END_OF_STREAM) { 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) { @@ -159,6 +148,15 @@ public class MetadataTrackRenderer extends TrackRenderer implements Callback } } + @Override + protected void maybeThrowError() throws ExoPlaybackException { + try { + source.maybeThrowError(); + } catch (IOException e) { + throw new ExoPlaybackException(e); + } + } + @Override protected void onDisabled() { pendingMetadata = null; diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java index 91121c59fd..aca3994e22 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java @@ -324,9 +324,12 @@ public class SmoothStreamingChunkSource implements ChunkSource { } @Override - public IOException getError() { - return fatalError != null ? fatalError - : (manifestFetcher != null ? manifestFetcher.getError() : null); + public void maybeThrowError() throws IOException { + if (fatalError != null) { + throw fatalError; + } else { + manifestFetcher.maybeThrowError(); + } } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java index 9544dd8910..27019c82ab 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java @@ -151,17 +151,14 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { } @Override - protected int doPrepare(long positionUs) throws ExoPlaybackException { - try { - boolean sourcePrepared = source.prepare(positionUs); - if (!sourcePrepared) { - return TrackRenderer.STATE_UNPREPARED; - } - } catch (IOException e) { - throw new ExoPlaybackException(e); + protected int doPrepare(long positionUs) { + boolean sourcePrepared = source.prepare(positionUs); + if (!sourcePrepared) { + return TrackRenderer.STATE_UNPREPARED; } + int trackCount = source.getTrackCount(); 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)) { parserIndex = i; trackIndex = j; @@ -197,11 +194,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { @Override protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { - try { - source.continueBuffering(trackIndex, positionUs); - } catch (IOException e) { - throw new ExoPlaybackException(e); - } + source.continueBuffering(trackIndex, positionUs); if (nextSubtitle == null) { try { @@ -240,17 +233,13 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { if (!inputStreamEnded && nextSubtitle == null && !parserHelper.isParsing()) { // Try and read the next subtitle from the source. - try { - SampleHolder sampleHolder = parserHelper.getSampleHolder(); - sampleHolder.clearData(); - int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); - if (result == SampleSource.SAMPLE_READ) { - parserHelper.startParseOperation(); - } else if (result == SampleSource.END_OF_STREAM) { - inputStreamEnded = true; - } - } catch (IOException e) { - throw new ExoPlaybackException(e); + SampleHolder sampleHolder = parserHelper.getSampleHolder(); + sampleHolder.clearData(); + int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); + if (result == SampleSource.SAMPLE_READ) { + parserHelper.startParseOperation(); + } else if (result == SampleSource.END_OF_STREAM) { + inputStreamEnded = true; } } } @@ -271,6 +260,15 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { source.release(); } + @Override + protected void maybeThrowError() throws ExoPlaybackException { + try { + source.maybeThrowError(); + } catch (IOException e) { + throw new ExoPlaybackException(e); + } + } + @Override protected long getDurationUs() { return source.getTrackInfo(trackIndex).durationUs; diff --git a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java index 45bf83694c..8f81dc62dd 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java @@ -92,16 +92,13 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { } @Override - protected int doPrepare(long positionUs) throws ExoPlaybackException { - try { - boolean sourcePrepared = source.prepare(positionUs); - if (!sourcePrepared) { - return TrackRenderer.STATE_UNPREPARED; - } - } catch (IOException e) { - throw new ExoPlaybackException(e); + protected int doPrepare(long positionUs) { + boolean sourcePrepared = source.prepare(positionUs); + if (!sourcePrepared) { + return TrackRenderer.STATE_UNPREPARED; } - 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)) { trackIndex = i; return TrackRenderer.STATE_PREPARED; @@ -133,13 +130,7 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { @Override protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { - try { - 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); - } + source.continueBuffering(trackIndex, positionUs); if (isSamplePending()) { maybeParsePendingSample(positionUs); @@ -147,17 +138,11 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { int result = inputStreamEnded ? SampleSource.END_OF_STREAM : SampleSource.SAMPLE_READ; while (!isSamplePending() && result == SampleSource.SAMPLE_READ) { - try { - result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); - if (result == SampleSource.SAMPLE_READ) { - maybeParsePendingSample(positionUs); - } else if (result == SampleSource.END_OF_STREAM) { - 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); + result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); + if (result == SampleSource.SAMPLE_READ) { + maybeParsePendingSample(positionUs); + } else if (result == SampleSource.END_OF_STREAM) { + inputStreamEnded = true; } } @@ -181,6 +166,15 @@ public class Eia608TrackRenderer extends TrackRenderer implements Callback { source.disable(trackIndex); } + @Override + protected void maybeThrowError() throws ExoPlaybackException { + try { + source.maybeThrowError(); + } catch (IOException e) { + throw new ExoPlaybackException(e); + } + } + @Override protected long getDurationUs() { return source.getTrackInfo(trackIndex).durationUs; diff --git a/library/src/main/java/com/google/android/exoplayer/util/ManifestFetcher.java b/library/src/main/java/com/google/android/exoplayer/util/ManifestFetcher.java index 267d8ff6ae..c1b73d11cf 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/ManifestFetcher.java +++ b/library/src/main/java/com/google/android/exoplayer/util/ManifestFetcher.java @@ -186,17 +186,17 @@ public class ManifestFetcher implements Loader.Callback { } /** - * Gets the error that affected the most recent attempt to load the manifest, or null if the - * most recent attempt was successful. + * Throws the error that affected the most recent attempt to load the manifest. Does nothing if + * 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() { - if (loadExceptionCount <= 1) { - // Don't report an exception until at least 1 retry attempt has been made. - return null; + public void maybeThrowError() throws IOException { + // Don't throw an exception until at least 1 retry attempt has been made. + if (loadException == null || loadExceptionCount <= 1) { + return; } - return loadException; + throw loadException; } /**