From d906e405a176ace5807daaf92ef5843fa4082d7b Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Fri, 2 Jan 2015 17:35:57 +0000 Subject: [PATCH] Propagate AudioTrack errors. --- .../exoplayer/demo/full/EventLogger.java | 5 ++++ .../demo/full/player/DemoPlayer.java | 8 +++++ .../Ac3PassthroughAudioTrackRenderer.java | 30 +++++++++++++++++-- .../exoplayer/FrameworkSampleSource.java | 3 +- .../MediaCodecAudioTrackRenderer.java | 28 +++++++++++++++-- .../android/exoplayer/audio/AudioTrack.java | 28 +++++++++++++---- 6 files changed, 91 insertions(+), 11 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/full/EventLogger.java b/demo/src/main/java/com/google/android/exoplayer/demo/full/EventLogger.java index 7ccecc1285..52b00246e5 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/full/EventLogger.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/full/EventLogger.java @@ -153,6 +153,11 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener printInternalError("audioTrackInitializationError", e); } + @Override + public void onAudioTrackWriteError(AudioTrack.WriteException e) { + printInternalError("audioTrackWriteError", e); + } + @Override public void onCryptoError(CryptoException e) { printInternalError("cryptoError", e); diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/full/player/DemoPlayer.java b/demo/src/main/java/com/google/android/exoplayer/demo/full/player/DemoPlayer.java index a426295b3c..2a2907fcdf 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/full/player/DemoPlayer.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/full/player/DemoPlayer.java @@ -108,6 +108,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi public interface InternalErrorListener { void onRendererInitializationError(Exception e); void onAudioTrackInitializationError(AudioTrack.InitializationException e); + void onAudioTrackWriteError(AudioTrack.WriteException e); void onDecoderInitializationError(DecoderInitializationException e); void onCryptoError(CryptoException e); void onUpstreamError(int sourceId, IOException e); @@ -434,6 +435,13 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi } } + @Override + public void onAudioTrackWriteError(AudioTrack.WriteException e) { + if (internalErrorListener != null) { + internalErrorListener.onAudioTrackWriteError(e); + } + } + @Override public void onCryptoError(CryptoException e) { if (internalErrorListener != null) { diff --git a/library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java index 932ed486b7..319e206ddf 100644 --- a/library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java @@ -51,6 +51,13 @@ public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer { */ void onAudioTrackInitializationError(AudioTrack.InitializationException e); + /** + * Invoked when an {@link AudioTrack} write fails. + * + * @param e The corresponding exception. + */ + void onAudioTrackWriteError(AudioTrack.WriteException e); + } /** @@ -188,7 +195,7 @@ public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer { } } - private void feedInputBuffer() throws IOException { + private void feedInputBuffer() throws IOException, ExoPlaybackException { if (!audioTrack.isInitialized() || inputStreamEnded) { return; } @@ -212,8 +219,14 @@ public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer { shouldReadInputBuffer = false; } - int handleBufferResult = - audioTrack.handleBuffer(sampleHolder.data, 0, sampleHolder.size, sampleHolder.timeUs); + int handleBufferResult; + try { + handleBufferResult = + audioTrack.handleBuffer(sampleHolder.data, 0, sampleHolder.size, sampleHolder.timeUs); + } catch (AudioTrack.WriteException e) { + notifyAudioTrackWriteError(e); + throw new ExoPlaybackException(e); + } // If we are out of sync, allow currentPositionUs to jump backwards. if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) { @@ -308,4 +321,15 @@ public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer { } } + private void notifyAudioTrackWriteError(final AudioTrack.WriteException e) { + if (eventHandler != null && eventListener != null) { + eventHandler.post(new Runnable() { + @Override + public void run() { + eventListener.onAudioTrackWriteError(e); + } + }); + } + } + } 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 716ef7dafb..b2bcc91bfa 100644 --- a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java @@ -190,7 +190,8 @@ public final class FrameworkSampleSource implements SampleSource { if (bufferedDurationUs == -1) { return TrackRenderer.UNKNOWN_TIME_US; } else { - return extractor.getSampleTime() + bufferedDurationUs; + long sampleTime = extractor.getSampleTime(); + return sampleTime == -1 ? TrackRenderer.END_OF_TRACK_US : sampleTime + bufferedDurationUs; } } diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java index 5c8a0056d6..b12cb6cb2a 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java @@ -47,6 +47,13 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { */ void onAudioTrackInitializationError(AudioTrack.InitializationException e); + /** + * Invoked when an {@link AudioTrack} write fails. + * + * @param e The corresponding exception. + */ + void onAudioTrackWriteError(AudioTrack.WriteException e); + } /** @@ -303,8 +310,14 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { } } - int handleBufferResult = audioTrack.handleBuffer( - buffer, bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs); + int handleBufferResult; + try { + handleBufferResult = audioTrack.handleBuffer( + buffer, bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs); + } catch (AudioTrack.WriteException e) { + notifyAudioTrackWriteError(e); + throw new ExoPlaybackException(e); + } // If we are out of sync, allow currentPositionUs to jump backwards. if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) { @@ -341,4 +354,15 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { } } + private void notifyAudioTrackWriteError(final AudioTrack.WriteException e) { + if (eventHandler != null && eventListener != null) { + eventHandler.post(new Runnable() { + @Override + public void run() { + eventListener.onAudioTrackWriteError(e); + } + }); + } + } + } diff --git a/library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java b/library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java index 9754d511e1..ccc3943b28 100644 --- a/library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java +++ b/library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java @@ -66,6 +66,21 @@ public final class AudioTrack { } + /** + * Thrown when a failure occurs writing to an {@link android.media.AudioTrack}. + */ + public static class WriteException extends Exception { + + /** The value returned from {@link android.media.AudioTrack#write(byte[], int, int)}. */ + public final int errorCode; + + public WriteException(int errorCode) { + super("AudioTrack write failed: " + errorCode); + this.errorCode = errorCode; + } + + } + /** Returned in the result of {@link #handleBuffer} if the buffer was discontinuous. */ public static final int RESULT_POSITION_DISCONTINUITY = 1; /** Returned in the result of {@link #handleBuffer} if the buffer can be released. */ @@ -359,8 +374,10 @@ public final class AudioTrack { * @return A bit field with {@link #RESULT_BUFFER_CONSUMED} if the buffer can be released, and * {@link #RESULT_POSITION_DISCONTINUITY} if the buffer was not contiguous with previously * written data. + * @throws WriteException If an error occurs writing the audio data. */ - public int handleBuffer(ByteBuffer buffer, int offset, int size, long presentationTimeUs) { + public int handleBuffer(ByteBuffer buffer, int offset, int size, long presentationTimeUs) + throws WriteException { if (size == 0) { return RESULT_BUFFER_CONSUMED; } @@ -422,9 +439,7 @@ public final class AudioTrack { if (bytesToWrite > 0) { bytesToWrite = Math.min(temporaryBufferSize, bytesToWrite); bytesWritten = audioTrack.write(temporaryBuffer, temporaryBufferOffset, bytesToWrite); - if (bytesWritten < 0) { - Log.w(TAG, "AudioTrack.write returned error code: " + bytesWritten); - } else { + if (bytesWritten >= 0) { temporaryBufferOffset += bytesWritten; } } @@ -432,12 +447,15 @@ public final class AudioTrack { bytesWritten = writeNonBlockingV21(audioTrack, buffer, temporaryBufferSize); } + if (bytesWritten < 0) { + throw new WriteException(bytesWritten); + } + temporaryBufferSize -= bytesWritten; submittedBytes += bytesWritten; if (temporaryBufferSize == 0) { result |= RESULT_BUFFER_CONSUMED; } - return result; }