Commit 2906a2ea by andrewlewis Committed by Oliver Woodman

Simplify AudioTrack.handleBuffer's return value.

Position discontinuities are notified via AudioTrack.Listener.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=144202048
parent 444811c0
...@@ -64,6 +64,19 @@ public final class AudioTrack { ...@@ -64,6 +64,19 @@ public final class AudioTrack {
public interface Listener { 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. * Called when the audio track underruns.
* *
* @param bufferSize The size of the track's buffer, in bytes. * @param bufferSize The size of the track's buffer, in bytes.
...@@ -74,13 +87,6 @@ public final class AudioTrack { ...@@ -74,13 +87,6 @@ public final class AudioTrack {
*/ */
void onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); 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);
} }
/** /**
...@@ -145,15 +151,6 @@ public final class AudioTrack { ...@@ -145,15 +151,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. * Returned by {@link #getCurrentPositionUs} when the position is not set.
*/ */
public static final long CURRENT_POSITION_NOT_SET = Long.MIN_VALUE; public static final long CURRENT_POSITION_NOT_SET = Long.MIN_VALUE;
...@@ -591,24 +588,21 @@ public final class AudioTrack { ...@@ -591,24 +588,21 @@ public final class AudioTrack {
* Attempts to write data from a {@link ByteBuffer} to the audio track, starting from its current * 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 * position and ending at its limit (exclusive). The position of the {@link ByteBuffer} is
* advanced by the number of bytes that were successfully written. * 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.
* <p> * <p>
* Returns a bit field containing {@link #RESULT_BUFFER_CONSUMED} if the data was written in full, * Returns whether the data was written in full. If the data was not written in full then the same
* and {@link #RESULT_POSITION_DISCONTINUITY} if the buffer was discontinuous with previously * {@link ByteBuffer} must be provided to subsequent calls until it has been fully consumed,
* written data. * except in the case of an interleaving call to {@link #reset()} (or an interleaving call to
* <p> * {@link #configure(String, int, int, int, int)} that caused the track to be reset).
* 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}.
* *
* @param buffer The buffer containing audio data to play back. * @param buffer The buffer containing audio data to play back.
* @param presentationTimeUs Presentation timestamp of the next buffer in microseconds. * @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 * @return Whether the buffer was consumed fully.
* {@link #RESULT_POSITION_DISCONTINUITY} if the buffer was not contiguous with previously
* written data.
* @throws InitializationException If an error occurs initializing the track. * @throws InitializationException If an error occurs initializing the track.
* @throws WriteException If an error occurs writing the audio data. * @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 { throws InitializationException, WriteException {
if (!isInitialized()) { if (!isInitialized()) {
initialize(); initialize();
...@@ -623,12 +617,12 @@ public final class AudioTrack { ...@@ -623,12 +617,12 @@ public final class AudioTrack {
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs; long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
listener.onUnderrun(bufferSize, C.usToMs(bufferSizeUs), elapsedSinceLastFeedMs); listener.onUnderrun(bufferSize, C.usToMs(bufferSizeUs), elapsedSinceLastFeedMs);
} }
int result = writeBuffer(buffer, presentationTimeUs); boolean result = writeBuffer(buffer, presentationTimeUs);
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime(); lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
return result; return result;
} }
private int writeBuffer(ByteBuffer buffer, long presentationTimeUs) throws WriteException { private boolean writeBuffer(ByteBuffer buffer, long presentationTimeUs) throws WriteException {
boolean isNewSourceBuffer = currentSourceBuffer == null; boolean isNewSourceBuffer = currentSourceBuffer == null;
Assertions.checkState(isNewSourceBuffer || currentSourceBuffer == buffer); Assertions.checkState(isNewSourceBuffer || currentSourceBuffer == buffer);
currentSourceBuffer = buffer; currentSourceBuffer = buffer;
...@@ -637,7 +631,7 @@ public final class AudioTrack { ...@@ -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 // An AC-3 audio track continues to play data written while it is paused. Stop writing so its
// buffer empties. See [Internal: b/18899620]. // buffer empties. See [Internal: b/18899620].
if (audioTrack.getPlayState() == PLAYSTATE_PAUSED) { 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 // 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 { ...@@ -645,7 +639,7 @@ public final class AudioTrack {
// head position actually returns to zero. // head position actually returns to zero.
if (audioTrack.getPlayState() == PLAYSTATE_STOPPED if (audioTrack.getPlayState() == PLAYSTATE_STOPPED
&& audioTrackUtil.getPlaybackHeadPosition() != 0) { && audioTrackUtil.getPlaybackHeadPosition() != 0) {
return 0; return false;
} }
} }
...@@ -656,7 +650,7 @@ public final class AudioTrack { ...@@ -656,7 +650,7 @@ public final class AudioTrack {
if (!currentSourceBuffer.hasRemaining()) { if (!currentSourceBuffer.hasRemaining()) {
// The buffer is empty. // The buffer is empty.
currentSourceBuffer = null; currentSourceBuffer = null;
return RESULT_BUFFER_CONSUMED; return true;
} }
useResampledBuffer = targetEncoding != sourceEncoding; useResampledBuffer = targetEncoding != sourceEncoding;
...@@ -689,7 +683,7 @@ public final class AudioTrack { ...@@ -689,7 +683,7 @@ public final class AudioTrack {
// number of bytes submitted. // number of bytes submitted.
startMediaTimeUs += (presentationTimeUs - expectedPresentationTimeUs); startMediaTimeUs += (presentationTimeUs - expectedPresentationTimeUs);
startMediaTimeState = START_IN_SYNC; startMediaTimeState = START_IN_SYNC;
result |= RESULT_POSITION_DISCONTINUITY; listener.onPositionDiscontinuity();
} }
} }
if (Util.SDK_INT < 21) { if (Util.SDK_INT < 21) {
...@@ -739,9 +733,9 @@ public final class AudioTrack { ...@@ -739,9 +733,9 @@ public final class AudioTrack {
submittedEncodedFrames += framesPerEncodedSample; submittedEncodedFrames += framesPerEncodedSample;
} }
currentSourceBuffer = null; currentSourceBuffer = null;
result |= RESULT_BUFFER_CONSUMED; return true;
} }
return result; return false;
} }
/** /**
......
...@@ -228,29 +228,26 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -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 * Called when the audio session id becomes known. The default implementation is a no-op. One
* hence this method will not be called again) unless the renderer is disabled and then * reason for overriding this method would be to instantiate and enable a {@link Virtualizer} in
* subsequently re-enabled. * order to spatialize the audio channels. For this use case, any {@link Virtualizer} instances
* <p> * should be released in {@link #onDisabled()} (if not before).
* 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) { protected void onAudioSessionId(int audioSessionId) {
// Do nothing. // Do nothing.
} }
/** /**
* Called when an {@link AudioTrack} underrun occurs. * @see AudioTrack.Listener#onPositionDiscontinuity()
* */
* @param bufferSize The size of the {@link AudioTrack}'s buffer, in bytes. protected void onAudioTrackPositionDiscontinuity() {
* @param bufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds, if it is // Do nothing.
* 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#onUnderrun(int, long, long)
*/ */
protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs,
long elapsedSinceLastFeedMs) { long elapsedSinceLastFeedMs) {
...@@ -335,26 +332,15 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -335,26 +332,15 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
return true; return true;
} }
int handleBufferResult;
try { try {
handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs); if (audioTrack.handleBuffer(buffer, bufferPresentationTimeUs)) {
} 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); codec.releaseOutputBuffer(bufferIndex, false);
decoderCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
return true; return true;
} }
} catch (AudioTrack.InitializationException | AudioTrack.WriteException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex());
}
return false; return false;
} }
...@@ -363,10 +349,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -363,10 +349,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
audioTrack.handleEndOfStream(); audioTrack.handleEndOfStream();
} }
protected void handleAudioTrackDiscontinuity() {
// Do nothing
}
@Override @Override
public void handleMessage(int messageType, Object message) throws ExoPlaybackException { public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
switch (messageType) { switch (messageType) {
...@@ -389,18 +371,24 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -389,18 +371,24 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private final class AudioTrackListener implements AudioTrack.Listener { private final class AudioTrackListener implements AudioTrack.Listener {
@Override @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) { public void onAudioSessionId(int audioSessionId) {
eventDispatcher.audioSessionId(audioSessionId); eventDispatcher.audioSessionId(audioSessionId);
MediaCodecAudioRenderer.this.onAudioSessionId(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);
}
} }
} }
...@@ -183,29 +183,26 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -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 * Called when the audio session id becomes known. The default implementation is a no-op. One
* hence this method will not be called again) unless the renderer is disabled and then * reason for overriding this method would be to instantiate and enable a {@link Virtualizer} in
* subsequently re-enabled. * order to spatialize the audio channels. For this use case, any {@link Virtualizer} instances
* <p> * should be released in {@link #onDisabled()} (if not before).
* 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) { protected void onAudioSessionId(int audioSessionId) {
// Do nothing. // Do nothing.
} }
/** /**
* Called when an {@link AudioTrack} underrun occurs. * @see AudioTrack.Listener#onPositionDiscontinuity()
* */
* @param bufferSize The size of the {@link AudioTrack}'s buffer, in bytes. protected void onAudioTrackPositionDiscontinuity() {
* @param bufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds, if it is // Do nothing.
* 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#onUnderrun(int, long, long)
*/ */
protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs,
long elapsedSinceLastFeedMs) { long elapsedSinceLastFeedMs) {
...@@ -271,15 +268,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -271,15 +268,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
audioTrackNeedsConfigure = false; audioTrackNeedsConfigure = false;
} }
int handleBufferResult = audioTrack.handleBuffer(outputBuffer.data, outputBuffer.timeUs); if (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) {
decoderCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
outputBuffer.release(); outputBuffer.release();
outputBuffer = null; outputBuffer = null;
...@@ -564,18 +553,24 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -564,18 +553,24 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
private final class AudioTrackListener implements AudioTrack.Listener { private final class AudioTrackListener implements AudioTrack.Listener {
@Override @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) { public void onAudioSessionId(int audioSessionId) {
eventDispatcher.audioSessionId(audioSessionId); eventDispatcher.audioSessionId(audioSessionId);
SimpleDecoderAudioRenderer.this.onAudioSessionId(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);
}
} }
} }
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