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
This commit is contained in:
andrewlewis 2017-01-11 07:02:27 -08:00 committed by Oliver Woodman
parent 444811c010
commit 2906a2ea09
3 changed files with 87 additions and 110 deletions

View file

@ -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.
* <p>
* 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.
* <p>
* 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;
}
/**

View file

@ -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.
* <p>
* 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);
}
}
}

View file

@ -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.
* <p>
* 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);
}
}
}