Commit ce03b8c2 by andrewlewis Committed by Oliver Woodman

Move AudioTrack initialization into handleBuffer.

Move initialization code from the audio renderers into handleBuffer. The audio
session id and tunneling enabled state can be altered using new setters.

This change is not intended to change the behavior of the audio renderers.
Before this change, the AudioTrack would always be reinitialized by the
renderers before calling handleBuffer.

Add a protected onAudioTrackUnderrun method for custom renderers that want to
implement custom handling of underruns.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=144067149
parent 5c89bbed
...@@ -41,8 +41,7 @@ import java.nio.ByteBuffer; ...@@ -41,8 +41,7 @@ import java.nio.ByteBuffer;
* Decodes and renders audio using {@link MediaCodec} and {@link AudioTrack}. * Decodes and renders audio using {@link MediaCodec} and {@link AudioTrack}.
*/ */
@TargetApi(16) @TargetApi(16)
public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock, public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock {
AudioTrack.Listener {
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final AudioTrack audioTrack; private final AudioTrack audioTrack;
...@@ -50,7 +49,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -50,7 +49,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private boolean passthroughEnabled; private boolean passthroughEnabled;
private android.media.MediaFormat passthroughMediaFormat; private android.media.MediaFormat passthroughMediaFormat;
private int pcmEncoding; private int pcmEncoding;
private int audioSessionId;
private long currentPositionUs; private long currentPositionUs;
private boolean allowPositionDiscontinuity; private boolean allowPositionDiscontinuity;
...@@ -129,8 +127,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -129,8 +127,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
boolean playClearSamplesWithoutKeys, Handler eventHandler, boolean playClearSamplesWithoutKeys, Handler eventHandler,
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities) { AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities) {
super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys); super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
audioSessionId = C.AUDIO_SESSION_ID_UNSET; audioTrack = new AudioTrack(audioCapabilities, new AudioTrackListener());
audioTrack = new AudioTrack(audioCapabilities, this);
eventDispatcher = new EventDispatcher(eventHandler, eventListener); eventDispatcher = new EventDispatcher(eventHandler, eventListener);
} }
...@@ -246,6 +243,20 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -246,6 +243,20 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
// Do nothing. // 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.
*/
protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs,
long elapsedSinceLastFeedMs) {
// Do nothing.
}
@Override @Override
protected void onEnabled(boolean joining) throws ExoPlaybackException { protected void onEnabled(boolean joining) throws ExoPlaybackException {
super.onEnabled(joining); super.onEnabled(joining);
...@@ -274,7 +285,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -274,7 +285,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected void onDisabled() { protected void onDisabled() {
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
try { try {
audioTrack.release(); audioTrack.release();
} finally { } finally {
...@@ -325,28 +335,10 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -325,28 +335,10 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
return true; return true;
} }
if (!audioTrack.isInitialized()) {
// Initialize the AudioTrack now.
try {
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
audioSessionId = audioTrack.initialize(C.AUDIO_SESSION_ID_UNSET);
eventDispatcher.audioSessionId(audioSessionId);
onAudioSessionId(audioSessionId);
} else {
audioTrack.initialize(audioSessionId);
}
} catch (AudioTrack.InitializationException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex());
}
if (getState() == STATE_STARTED) {
audioTrack.play();
}
}
int handleBufferResult; int handleBufferResult;
try { try {
handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs); handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs);
} catch (AudioTrack.WriteException e) { } catch (AudioTrack.InitializationException | AudioTrack.WriteException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
...@@ -386,9 +378,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -386,9 +378,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
break; break;
case C.MSG_SET_STREAM_TYPE: case C.MSG_SET_STREAM_TYPE:
@C.StreamType int streamType = (Integer) message; @C.StreamType int streamType = (Integer) message;
if (audioTrack.setStreamType(streamType)) { audioTrack.setStreamType(streamType);
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
}
break; break;
default: default:
super.handleMessage(messageType, message); super.handleMessage(messageType, message);
...@@ -396,11 +386,21 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -396,11 +386,21 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
} }
// AudioTrack.Listener implementation. 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 onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
eventDispatcher.audioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs);
} }
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.audio; package com.google.android.exoplayer2.audio;
import android.media.PlaybackParams; import android.media.PlaybackParams;
import android.media.audiofx.Virtualizer;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
...@@ -43,8 +44,7 @@ import java.lang.annotation.RetentionPolicy; ...@@ -43,8 +44,7 @@ import java.lang.annotation.RetentionPolicy;
/** /**
* Decodes and renders audio using a {@link SimpleDecoder}. * Decodes and renders audio using a {@link SimpleDecoder}.
*/ */
public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements MediaClock, public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements MediaClock {
AudioTrack.Listener {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({REINITIALIZATION_STATE_NONE, REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM, @IntDef({REINITIALIZATION_STATE_NONE, REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM,
...@@ -94,8 +94,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -94,8 +94,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
private boolean outputStreamEnded; private boolean outputStreamEnded;
private boolean waitingForKeys; private boolean waitingForKeys;
private int audioSessionId;
public SimpleDecoderAudioRenderer() { public SimpleDecoderAudioRenderer() {
this(null, null); this(null, null);
} }
...@@ -141,11 +139,10 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -141,11 +139,10 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys) { DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys) {
super(C.TRACK_TYPE_AUDIO); super(C.TRACK_TYPE_AUDIO);
eventDispatcher = new EventDispatcher(eventHandler, eventListener); eventDispatcher = new EventDispatcher(eventHandler, eventListener);
audioTrack = new AudioTrack(audioCapabilities, this); audioTrack = new AudioTrack(audioCapabilities, new AudioTrackListener());
this.drmSessionManager = drmSessionManager; this.drmSessionManager = drmSessionManager;
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
decoderReinitializationState = REINITIALIZATION_STATE_NONE; decoderReinitializationState = REINITIALIZATION_STATE_NONE;
audioTrackNeedsConfigure = true; audioTrackNeedsConfigure = true;
} }
...@@ -186,6 +183,36 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -186,6 +183,36 @@ 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).
*
* @param audioSessionId The audio session id.
*/
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.
*/
protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs,
long elapsedSinceLastFeedMs) {
// Do nothing.
}
/**
* Creates a decoder for the given format. * Creates a decoder for the given format.
* *
* @param format The format for which a decoder is required. * @param format The format for which a decoder is required.
...@@ -244,19 +271,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -244,19 +271,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
audioTrackNeedsConfigure = false; audioTrackNeedsConfigure = false;
} }
if (!audioTrack.isInitialized()) {
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
audioSessionId = audioTrack.initialize(C.AUDIO_SESSION_ID_UNSET);
eventDispatcher.audioSessionId(audioSessionId);
onAudioSessionId(audioSessionId);
} else {
audioTrack.initialize(audioSessionId);
}
if (getState() == STATE_STARTED) {
audioTrack.play();
}
}
int handleBufferResult = audioTrack.handleBuffer(outputBuffer.data, outputBuffer.timeUs); int handleBufferResult = audioTrack.handleBuffer(outputBuffer.data, outputBuffer.timeUs);
// If we are out of sync, allow currentPositionUs to jump backwards. // If we are out of sync, allow currentPositionUs to jump backwards.
...@@ -381,19 +395,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -381,19 +395,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
return currentPositionUs; return currentPositionUs;
} }
/**
* 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.
*
* @param audioSessionId The audio session id.
*/
protected void onAudioSessionId(int audioSessionId) {
// Do nothing.
}
@Override @Override
protected void onEnabled(boolean joining) throws ExoPlaybackException { protected void onEnabled(boolean joining) throws ExoPlaybackException {
decoderCounters = new DecoderCounters(); decoderCounters = new DecoderCounters();
...@@ -425,7 +426,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -425,7 +426,6 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
@Override @Override
protected void onDisabled() { protected void onDisabled() {
inputFormat = null; inputFormat = null;
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
audioTrackNeedsConfigure = true; audioTrackNeedsConfigure = true;
waitingForKeys = false; waitingForKeys = false;
try { try {
...@@ -553,9 +553,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -553,9 +553,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
break; break;
case C.MSG_SET_STREAM_TYPE: case C.MSG_SET_STREAM_TYPE:
@C.StreamType int streamType = (Integer) message; @C.StreamType int streamType = (Integer) message;
if (audioTrack.setStreamType(streamType)) { audioTrack.setStreamType(streamType);
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
}
break; break;
default: default:
super.handleMessage(messageType, message); super.handleMessage(messageType, message);
...@@ -563,11 +561,21 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements ...@@ -563,11 +561,21 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
} }
} }
// AudioTrack.Listener implementation. 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 onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
eventDispatcher.audioTrackUnderrun(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