diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java b/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java index 6372bbedf2..711ef521e7 100644 --- a/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java +++ b/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java @@ -63,6 +63,19 @@ public final class AudioTrack { */ public interface Listener { + /** + * Called when the audio track has been initialized with the specified {@code audioSessionId}. + * + * @param audioSessionId The audio session id. + */ + void onAudioSessionId(int audioSessionId); + + /** + * Called when the audio track handles a buffer whose timestamp is discontinuous with the last + * buffer handled since it was reset. + */ + void onPositionDiscontinuity(); + /** * Called when the audio track underruns. * @@ -74,13 +87,6 @@ public final class AudioTrack { */ void onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); - /** - * Called when the audio track has been initialized with the specified {@code audioSessionId}. - * - * @param audioSessionId The audio session id. - */ - void onAudioSessionId(int audioSessionId); - } /** @@ -144,15 +150,6 @@ public final class AudioTrack { } - /** - * 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. - */ - public static final int RESULT_BUFFER_CONSUMED = 2; - /** * Returned by {@link #getCurrentPositionUs} when the position is not set. */ @@ -591,24 +588,21 @@ public final class AudioTrack { * Attempts to write data from a {@link ByteBuffer} to the audio track, starting from its current * position and ending at its limit (exclusive). The position of the {@link ByteBuffer} is * advanced by the number of bytes that were successfully written. + * {@link Listener#onPositionDiscontinuity()} will be called if {@code presentationTimeUs} is + * discontinuous with the last buffer handled since the track was reset. *

- * Returns a bit field containing {@link #RESULT_BUFFER_CONSUMED} if the data was written in full, - * and {@link #RESULT_POSITION_DISCONTINUITY} if the buffer was discontinuous with previously - * written data. - *

- * If the data was not written in full then the same {@link ByteBuffer} must be provided to - * subsequent calls until it has been fully consumed, except in the case of an interleaving call - * to {@link #configure} or {@link #reset}. + * Returns whether the data was written in full. If the data was not written in full then the same + * {@link ByteBuffer} must be provided to subsequent calls until it has been fully consumed, + * except in the case of an interleaving call to {@link #reset()} (or an interleaving call to + * {@link #configure(String, int, int, int, int)} that caused the track to be reset). * * @param buffer The buffer containing audio data to play back. * @param presentationTimeUs Presentation timestamp of the next buffer in microseconds. - * @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. + * @return Whether the buffer was consumed fully. * @throws InitializationException If an error occurs initializing the track. * @throws WriteException If an error occurs writing the audio data. */ - public int handleBuffer(ByteBuffer buffer, long presentationTimeUs) + public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs) throws InitializationException, WriteException { if (!isInitialized()) { initialize(); @@ -623,12 +617,12 @@ public final class AudioTrack { long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs; listener.onUnderrun(bufferSize, C.usToMs(bufferSizeUs), elapsedSinceLastFeedMs); } - int result = writeBuffer(buffer, presentationTimeUs); + boolean result = writeBuffer(buffer, presentationTimeUs); lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime(); return result; } - private int writeBuffer(ByteBuffer buffer, long presentationTimeUs) throws WriteException { + private boolean writeBuffer(ByteBuffer buffer, long presentationTimeUs) throws WriteException { boolean isNewSourceBuffer = currentSourceBuffer == null; Assertions.checkState(isNewSourceBuffer || currentSourceBuffer == buffer); currentSourceBuffer = buffer; @@ -637,7 +631,7 @@ public final class AudioTrack { // An AC-3 audio track continues to play data written while it is paused. Stop writing so its // buffer empties. See [Internal: b/18899620]. if (audioTrack.getPlayState() == PLAYSTATE_PAUSED) { - return 0; + return false; } // A new AC-3 audio track's playback position continues to increase from the old track's @@ -645,7 +639,7 @@ public final class AudioTrack { // head position actually returns to zero. if (audioTrack.getPlayState() == PLAYSTATE_STOPPED && audioTrackUtil.getPlaybackHeadPosition() != 0) { - return 0; + return false; } } @@ -656,7 +650,7 @@ public final class AudioTrack { if (!currentSourceBuffer.hasRemaining()) { // The buffer is empty. currentSourceBuffer = null; - return RESULT_BUFFER_CONSUMED; + return true; } useResampledBuffer = targetEncoding != sourceEncoding; @@ -689,7 +683,7 @@ public final class AudioTrack { // number of bytes submitted. startMediaTimeUs += (presentationTimeUs - expectedPresentationTimeUs); startMediaTimeState = START_IN_SYNC; - result |= RESULT_POSITION_DISCONTINUITY; + listener.onPositionDiscontinuity(); } } if (Util.SDK_INT < 21) { @@ -739,9 +733,9 @@ public final class AudioTrack { submittedEncodedFrames += framesPerEncodedSample; } currentSourceBuffer = null; - result |= RESULT_BUFFER_CONSUMED; + return true; } - return result; + return false; } /** diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java index 33de2e5085..308ee7773b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java @@ -228,29 +228,26 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } /** - * Called when the audio session id becomes known. Once the id is known it will not change (and - * hence this method will not be called again) unless the renderer is disabled and then - * subsequently re-enabled. - *

- * The default implementation is a no-op. One reason for overriding this method would be to - * instantiate and enable a {@link Virtualizer} in order to spatialize the audio channels. For - * this use case, any {@link Virtualizer} instances should be released in {@link #onDisabled()} - * (if not before). + * Called when the audio session id becomes known. The default implementation is a no-op. One + * reason for overriding this method would be to instantiate and enable a {@link Virtualizer} in + * order to spatialize the audio channels. For this use case, any {@link Virtualizer} instances + * should be released in {@link #onDisabled()} (if not before). * - * @param audioSessionId The audio session id. + * @see AudioTrack.Listener#onAudioSessionId(int) */ protected void onAudioSessionId(int audioSessionId) { // Do nothing. } /** - * Called when an {@link AudioTrack} underrun occurs. - * - * @param bufferSize The size of the {@link AudioTrack}'s buffer, in bytes. - * @param bufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds, if it is - * configured for PCM output. {@link C#TIME_UNSET} if it is configured for passthrough output, - * as the buffered media can have a variable bitrate so the duration may be unknown. - * @param elapsedSinceLastFeedMs The time since the {@link AudioTrack} was last fed data. + * @see AudioTrack.Listener#onPositionDiscontinuity() + */ + protected void onAudioTrackPositionDiscontinuity() { + // Do nothing. + } + + /** + * @see AudioTrack.Listener#onUnderrun(int, long, long) */ protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { @@ -335,26 +332,15 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media return true; } - int handleBufferResult; try { - handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs); + if (audioTrack.handleBuffer(buffer, bufferPresentationTimeUs)) { + codec.releaseOutputBuffer(bufferIndex, false); + decoderCounters.renderedOutputBufferCount++; + return true; + } } catch (AudioTrack.InitializationException | AudioTrack.WriteException e) { throw ExoPlaybackException.createForRenderer(e, getIndex()); } - - // If we are out of sync, allow currentPositionUs to jump backwards. - if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) { - handleAudioTrackDiscontinuity(); - allowPositionDiscontinuity = true; - } - - // Release the buffer if it was consumed. - if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) { - codec.releaseOutputBuffer(bufferIndex, false); - decoderCounters.renderedOutputBufferCount++; - return true; - } - return false; } @@ -363,10 +349,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media audioTrack.handleEndOfStream(); } - protected void handleAudioTrackDiscontinuity() { - // Do nothing - } - @Override public void handleMessage(int messageType, Object message) throws ExoPlaybackException { switch (messageType) { @@ -388,19 +370,25 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media private final class AudioTrackListener implements AudioTrack.Listener { - @Override - public void onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { - eventDispatcher.audioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); - MediaCodecAudioRenderer.this.onAudioTrackUnderrun(bufferSize, bufferSizeMs, - elapsedSinceLastFeedMs); - } - @Override public void onAudioSessionId(int audioSessionId) { eventDispatcher.audioSessionId(audioSessionId); MediaCodecAudioRenderer.this.onAudioSessionId(audioSessionId); } + @Override + public void onPositionDiscontinuity() { + onAudioTrackPositionDiscontinuity(); + // We are out of sync so allow currentPositionUs to jump backwards. + MediaCodecAudioRenderer.this.allowPositionDiscontinuity = true; + } + + @Override + public void onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { + eventDispatcher.audioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); + onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); + } + } } diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java b/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java index 1ba289201a..6cecb83c13 100644 --- a/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java @@ -183,29 +183,26 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements } /** - * Called when the audio session id becomes known. Once the id is known it will not change (and - * hence this method will not be called again) unless the renderer is disabled and then - * subsequently re-enabled. - *

- * The default implementation is a no-op. One reason for overriding this method would be to - * instantiate and enable a {@link Virtualizer} in order to spatialize the audio channels. For - * this use case, any {@link Virtualizer} instances should be released in {@link #onDisabled()} - * (if not before). + * Called when the audio session id becomes known. The default implementation is a no-op. One + * reason for overriding this method would be to instantiate and enable a {@link Virtualizer} in + * order to spatialize the audio channels. For this use case, any {@link Virtualizer} instances + * should be released in {@link #onDisabled()} (if not before). * - * @param audioSessionId The audio session id. + * @see AudioTrack.Listener#onAudioSessionId(int) */ protected void onAudioSessionId(int audioSessionId) { // Do nothing. } /** - * Called when an {@link AudioTrack} underrun occurs. - * - * @param bufferSize The size of the {@link AudioTrack}'s buffer, in bytes. - * @param bufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds, if it is - * configured for PCM output. {@link C#TIME_UNSET} if it is configured for passthrough output, - * as the buffered media can have a variable bitrate so the duration may be unknown. - * @param elapsedSinceLastFeedMs The time since the {@link AudioTrack} was last fed data. + * @see AudioTrack.Listener#onPositionDiscontinuity() + */ + protected void onAudioTrackPositionDiscontinuity() { + // Do nothing. + } + + /** + * @see AudioTrack.Listener#onUnderrun(int, long, long) */ protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { @@ -271,15 +268,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements audioTrackNeedsConfigure = false; } - int handleBufferResult = audioTrack.handleBuffer(outputBuffer.data, outputBuffer.timeUs); - - // If we are out of sync, allow currentPositionUs to jump backwards. - if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) { - allowPositionDiscontinuity = true; - } - - // Release the buffer if it was consumed. - if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) { + if (audioTrack.handleBuffer(outputBuffer.data, outputBuffer.timeUs)) { decoderCounters.renderedOutputBufferCount++; outputBuffer.release(); outputBuffer = null; @@ -563,19 +552,25 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements private final class AudioTrackListener implements AudioTrack.Listener { - @Override - public void onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { - eventDispatcher.audioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); - SimpleDecoderAudioRenderer.this.onAudioTrackUnderrun(bufferSize, bufferSizeMs, - elapsedSinceLastFeedMs); - } - @Override public void onAudioSessionId(int audioSessionId) { eventDispatcher.audioSessionId(audioSessionId); SimpleDecoderAudioRenderer.this.onAudioSessionId(audioSessionId); } + @Override + public void onPositionDiscontinuity() { + onAudioTrackPositionDiscontinuity(); + // We are out of sync so allow currentPositionUs to jump backwards. + SimpleDecoderAudioRenderer.this.allowPositionDiscontinuity = true; + } + + @Override + public void onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { + eventDispatcher.audioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); + onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); + } + } }