Commit a95b2ebb by olly Committed by Oliver Woodman

Fix audio session ID generation

- SimpleExoPlayer now always generates a session ID at
  construction time. This ID is used indefinitely, including
  for tunneling, unless a call to setAudioSessionId is made
  to change it.
- DefaultTrackSelector support for enabling tunneling has
  been changed to a boolean, since tunneling now uses the
  same session ID as non-tunneled mode.
- Since the session ID is now always set at the top level,
  internal propagation of generated session IDs is no longer
  necessary, and so is removed.

PiperOrigin-RevId: 351349687
parent 85641a38
......@@ -55,6 +55,10 @@
* Track selection:
* Allow parallel adaptation for video and audio
([#5111](https://github.com/google/ExoPlayer/issues/5111)).
* Simplified enabling tunneling with `DefaultTrackSelector`.
`ParametersBuilder.setTunnelingAudioSessionId` has been replaced with
`ParametersBuilder.setTunnelingEnabled`. The player's audio session ID
will be used, and so a tunneling specified ID is no longer needed.
* Add option to specify multiple preferred audio or text languages.
* Add option to specify preferred MIME type(s) for video and audio
([#8320](https://github.com/google/ExoPlayer/issues/8320)).
......@@ -87,6 +91,18 @@
`DecoderReuseEvaluation` indicates whether it was possible to re-use an
existing decoder instance for the new format, and if not then the
reasons why.
* Audio:
* Fix handling of audio session IDs
([#8190](https://github.com/google/ExoPlayer/issues/8190)).
`SimpleExoPlayer` now generates an audio session ID on construction,
which can be immediately queried by calling
`SimpleExoPlayer.getAudioSessionId`. The audio session ID will only
change if application code calls `SimpleExoPlayer.setAudioSessionId`.
* Text:
* Gracefully handle null-terminated subtitle content in Matroska
containers.
* Fix CEA-708 anchor positioning
([#1807](https://github.com/google/ExoPlayer/issues/1807)).
* Metadata retriever:
* Parse Google Photos HEIC motion photos metadata.
* Data sources:
......
......@@ -89,7 +89,7 @@ public interface Player {
* default audio attributes will be used. They are suitable for general media playback.
*
* <p>Setting the audio attributes during playback may introduce a short gap in audio output as
* the audio track is recreated. A new audio session id will also be generated.
* the audio track is recreated.
*
* <p>If tunneling is enabled by the track selector, the specified audio attributes will be
* ignored, but they will take effect if audio is later played without tunneling.
......
......@@ -22,24 +22,16 @@ import androidx.annotation.Nullable;
*/
public final class RendererConfiguration {
/**
* The default configuration.
*/
/** The default configuration. */
public static final RendererConfiguration DEFAULT =
new RendererConfiguration(C.AUDIO_SESSION_ID_UNSET);
new RendererConfiguration(/* tunneling= */ false);
/**
* The audio session id to use for tunneling, or {@link C#AUDIO_SESSION_ID_UNSET} if tunneling
* should not be enabled.
*/
public final int tunnelingAudioSessionId;
/** Whether to enable tunneling. */
public final boolean tunneling;
/**
* @param tunnelingAudioSessionId The audio session id to use for tunneling, or
* {@link C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled.
*/
public RendererConfiguration(int tunnelingAudioSessionId) {
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
/** @param tunneling Whether to enable tunneling. */
public RendererConfiguration(boolean tunneling) {
this.tunneling = tunneling;
}
@Override
......@@ -51,12 +43,11 @@ public final class RendererConfiguration {
return false;
}
RendererConfiguration other = (RendererConfiguration) obj;
return tunnelingAudioSessionId == other.tunnelingAudioSessionId;
return tunneling == other.tunneling;
}
@Override
public int hashCode() {
return tunnelingAudioSessionId;
return tunneling ? 0 : 1;
}
}
......@@ -18,6 +18,8 @@ package com.google.android.exoplayer2;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.PlaybackParams;
import android.os.Handler;
......@@ -568,6 +570,7 @@ public class SimpleExoPlayer extends BasePlayer
protected final Renderer[] renderers;
private final Context applicationContext;
private final ExoPlayerImpl player;
private final ComponentListener componentListener;
private final CopyOnWriteArraySet<com.google.android.exoplayer2.video.VideoListener>
......@@ -588,7 +591,7 @@ public class SimpleExoPlayer extends BasePlayer
@Nullable private Format videoFormat;
@Nullable private Format audioFormat;
@Nullable private AudioTrack keepSessionIdAudioTrack;
@Nullable private VideoDecoderOutputBufferRenderer videoDecoderOutputBufferRenderer;
@Nullable private Surface surface;
private boolean ownsSurface;
......@@ -640,6 +643,7 @@ public class SimpleExoPlayer extends BasePlayer
/** @param builder The {@link Builder} to obtain all construction parameters. */
protected SimpleExoPlayer(Builder builder) {
applicationContext = builder.context.getApplicationContext();
analyticsCollector = builder.analyticsCollector;
priorityTaskManager = builder.priorityTaskManager;
audioAttributes = builder.audioAttributes;
......@@ -665,7 +669,11 @@ public class SimpleExoPlayer extends BasePlayer
// Set initial values.
audioVolume = 1;
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
if (Util.SDK_INT < 21) {
audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET);
} else {
audioSessionId = C.generateAudioSessionIdV21(applicationContext);
}
currentCues = Collections.emptyList();
throwsWhenUsingWrongThread = true;
......@@ -706,6 +714,8 @@ public class SimpleExoPlayer extends BasePlayer
wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK);
deviceInfo = createDeviceInfo(streamVolumeManager);
sendRendererMessage(C.TRACK_TYPE_AUDIO, Renderer.MSG_SET_AUDIO_SESSION_ID, audioSessionId);
sendRendererMessage(C.TRACK_TYPE_VIDEO, Renderer.MSG_SET_AUDIO_SESSION_ID, audioSessionId);
sendRendererMessage(C.TRACK_TYPE_AUDIO, Renderer.MSG_SET_AUDIO_ATTRIBUTES, audioAttributes);
sendRendererMessage(C.TRACK_TYPE_VIDEO, Renderer.MSG_SET_SCALING_MODE, videoScalingMode);
sendRendererMessage(
......@@ -960,11 +970,22 @@ public class SimpleExoPlayer extends BasePlayer
if (this.audioSessionId == audioSessionId) {
return;
}
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
if (Util.SDK_INT < 21) {
audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET);
} else {
audioSessionId = C.generateAudioSessionIdV21(applicationContext);
}
} else if (Util.SDK_INT < 21) {
// We need to re-initialize keepSessionIdAudioTrack to make sure the session is kept alive for
// as long as the player is using it.
initializeKeepSessionIdAudioTrack(audioSessionId);
}
this.audioSessionId = audioSessionId;
sendRendererMessage(C.TRACK_TYPE_AUDIO, Renderer.MSG_SET_AUDIO_SESSION_ID, audioSessionId);
sendRendererMessage(C.TRACK_TYPE_VIDEO, Renderer.MSG_SET_AUDIO_SESSION_ID, audioSessionId);
if (audioSessionId != C.AUDIO_SESSION_ID_UNSET) {
notifyAudioSessionIdSet();
for (AudioListener audioListener : audioListeners) {
audioListener.onAudioSessionId(audioSessionId);
}
}
......@@ -1024,7 +1045,7 @@ public class SimpleExoPlayer extends BasePlayer
* Sets the stream type for audio playback, used by the underlying audio track.
*
* <p>Setting the stream type during playback may introduce a short gap in audio output as the
* audio track is recreated. A new audio session id will also be generated.
* audio track is recreated.
*
* <p>Calling this method overwrites any attributes set previously by calling {@link
* #setAudioAttributes(AudioAttributes)}.
......@@ -1760,6 +1781,10 @@ public class SimpleExoPlayer extends BasePlayer
@Override
public void release() {
verifyApplicationThread();
if (Util.SDK_INT < 21 && keepSessionIdAudioTrack != null) {
keepSessionIdAudioTrack.release();
keepSessionIdAudioTrack = null;
}
audioBecomingNoisyManager.setEnabled(false);
streamVolumeManager.release();
wakeLockManager.setStayAwake(false);
......@@ -2100,19 +2125,6 @@ public class SimpleExoPlayer extends BasePlayer
sendRendererMessage(C.TRACK_TYPE_AUDIO, Renderer.MSG_SET_VOLUME, scaledVolume);
}
private void notifyAudioSessionIdSet() {
for (AudioListener audioListener : audioListeners) {
// Prevent duplicate notification if a listener is both a AudioRendererEventListener and
// a AudioListener, as they have the same method signature.
if (!audioDebugListeners.contains(audioListener)) {
audioListener.onAudioSessionId(audioSessionId);
}
}
for (AudioRendererEventListener audioDebugListener : audioDebugListeners) {
audioDebugListener.onAudioSessionId(audioSessionId);
}
}
@SuppressWarnings("SuspiciousMethodCalls")
private void notifySkipSilenceEnabledChanged() {
for (AudioListener listener : audioListeners) {
......@@ -2181,6 +2193,40 @@ public class SimpleExoPlayer extends BasePlayer
}
}
/**
* Initializes {@link #keepSessionIdAudioTrack} to keep an audio session ID alive. If the audio
* session ID is {@link C#AUDIO_SESSION_ID_UNSET} then a new audio session ID is generated.
*
* <p>Use of this method is only required on API level 21 and earlier.
*
* @param audioSessionId The audio session ID, or {@link C#AUDIO_SESSION_ID_UNSET} to generate a
* new one.
* @return The audio session ID.
*/
private int initializeKeepSessionIdAudioTrack(int audioSessionId) {
if (keepSessionIdAudioTrack != null
&& keepSessionIdAudioTrack.getAudioSessionId() != audioSessionId) {
keepSessionIdAudioTrack.release();
keepSessionIdAudioTrack = null;
}
if (keepSessionIdAudioTrack == null) {
int sampleRate = 4000; // Minimum sample rate supported by the platform.
int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
@C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT;
int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback.
keepSessionIdAudioTrack =
new AudioTrack(
C.STREAM_TYPE_DEFAULT,
sampleRate,
channelConfig,
encoding,
bufferSize,
AudioTrack.MODE_STATIC,
audioSessionId);
}
return keepSessionIdAudioTrack.getAudioSessionId();
}
private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) {
return new DeviceInfo(
DeviceInfo.PLAYBACK_TYPE_LOCAL,
......@@ -2304,15 +2350,6 @@ public class SimpleExoPlayer extends BasePlayer
}
@Override
public void onAudioSessionId(int sessionId) {
if (audioSessionId == sessionId) {
return;
}
audioSessionId = sessionId;
notifyAudioSessionIdSet();
}
@Override
public void onAudioDecoderInitialized(
String decoderName, long initializedTimestampMs, long initializationDurationMs) {
for (AudioRendererEventListener audioDebugListener : audioDebugListeners) {
......
......@@ -45,13 +45,6 @@ public interface AudioRendererEventListener {
default void onAudioEnabled(DecoderCounters counters) {}
/**
* Called when the audio session is set.
*
* @param audioSessionId The audio session id.
*/
default void onAudioSessionId(int audioSessionId) {}
/**
* Called when a decoder is created.
*
* @param decoderName The decoder that was created.
......@@ -224,13 +217,6 @@ public interface AudioRendererEventListener {
}
}
/** Invokes {@link AudioRendererEventListener#onAudioSessionId(int)}. */
public void audioSessionId(int audioSessionId) {
if (handler != null) {
handler.post(() -> castNonNull(listener).onAudioSessionId(audioSessionId));
}
}
/** Invokes {@link AudioRendererEventListener#onSkipSilenceEnabledChanged(boolean)}. */
public void skipSilenceEnabledChanged(boolean skipSilenceEnabled) {
if (handler != null) {
......
......@@ -49,9 +49,9 @@ import java.nio.ByteBuffer;
*
* <p>The implementation may be backed by a platform {@link AudioTrack}. In this case, {@link
* #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link
* #enableTunnelingV21(int)} and/or {@link #disableTunneling()} may be called before writing data to
* the sink. These methods may also be called after writing data to the sink, in which case it will
* be reinitialized as required. For implementations that are not based on platform {@link
* #enableTunnelingV21()} and {@link #disableTunneling()} may be called before writing data to the
* sink. These methods may also be called after writing data to the sink, in which case it will be
* reinitialized as required. For implementations that are not based on platform {@link
* AudioTrack}s, calling methods relating to audio sessions, audio attributes, and tunneling may
* have no effect.
*/
......@@ -63,13 +63,6 @@ public interface AudioSink {
interface Listener {
/**
* Called if the audio sink has started rendering audio to a new platform audio session.
*
* @param audioSessionId The newly generated audio session's identifier.
*/
void onAudioSessionId(int audioSessionId);
/**
* Called when the audio sink handles a buffer whose timestamp is discontinuous with the last
* buffer handled since it was reset.
*/
......@@ -392,14 +385,13 @@ public interface AudioSink {
void setAuxEffectInfo(AuxEffectInfo auxEffectInfo);
/**
* Enables tunneling, if possible. The sink is reset if tunneling was previously disabled or if
* the audio session id has changed. Enabling tunneling is only possible if the sink is based on a
* platform {@link AudioTrack}, and requires platform API version 21 onwards.
* Enables tunneling, if possible. The sink is reset if tunneling was previously disabled.
* Enabling tunneling is only possible if the sink is based on a platform {@link AudioTrack}, and
* requires platform API version 21 onwards.
*
* @param tunnelingAudioSessionId The audio session id to use.
* @throws IllegalStateException Thrown if enabling tunneling on platform API version &lt; 21.
*/
void enableTunnelingV21(int tunnelingAudioSessionId);
void enableTunnelingV21();
/**
* Disables tunneling. If tunneling was previously enabled then the sink is reset and any audio
......
......@@ -512,9 +512,8 @@ public abstract class DecoderAudioRenderer<
throws ExoPlaybackException {
decoderCounters = new DecoderCounters();
eventDispatcher.enabled(decoderCounters);
int tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId;
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
audioSink.enableTunnelingV21(tunnelingAudioSessionId);
if (getConfiguration().tunneling) {
audioSink.enableTunnelingV21();
} else {
audioSink.disableTunneling();
}
......@@ -715,11 +714,6 @@ public abstract class DecoderAudioRenderer<
private final class AudioSinkListener implements AudioSink.Listener {
@Override
public void onAudioSessionId(int audioSessionId) {
eventDispatcher.audioSessionId(audioSessionId);
}
@Override
public void onPositionDiscontinuity() {
DecoderAudioRenderer.this.onPositionDiscontinuity();
}
......
......@@ -262,15 +262,6 @@ public final class DefaultAudioSink implements AudioSink {
private static final String TAG = "DefaultAudioSink";
/**
* Whether to enable a workaround for an issue where an audio effect does not keep its session
* active across releasing/initializing a new audio track, on platform builds where
* {@link Util#SDK_INT} &lt; 21.
* <p>
* The flag must be set before creating a player.
*/
public static boolean enablePreV21AudioSessionWorkaround = false;
/**
* Whether to throw an {@link InvalidAudioTrackTimestampException} when a spurious timestamp is
* reported from {@link AudioTrack#getTimestamp}.
* <p>
......@@ -297,11 +288,6 @@ public final class DefaultAudioSink implements AudioSink {
private final PendingExceptionHolder<WriteException> writeExceptionPendingExceptionHolder;
@Nullable private Listener listener;
/**
* Used to keep the audio session active on pre-V21 builds (see {@link #initializeAudioTrack()}).
*/
@Nullable private AudioTrack keepSessionIdAudioTrack;
@Nullable private Configuration pendingConfiguration;
@MonotonicNonNull private Configuration configuration;
@Nullable private AudioTrack audioTrack;
......@@ -336,6 +322,7 @@ public final class DefaultAudioSink implements AudioSink {
private boolean stoppedAudioTrack;
private boolean playing;
private boolean externalAudioSessionIdProvided;
private int audioSessionId;
private AuxEffectInfo auxEffectInfo;
private boolean tunneling;
......@@ -646,27 +633,7 @@ public final class DefaultAudioSink implements AudioSink {
audioTrack.setOffloadDelayPadding(
configuration.inputFormat.encoderDelay, configuration.inputFormat.encoderPadding);
}
int audioSessionId = audioTrack.getAudioSessionId();
if (enablePreV21AudioSessionWorkaround) {
if (Util.SDK_INT < 21) {
// The workaround creates an audio track with a two byte buffer on the same session, and
// does not release it until this object is released, which keeps the session active.
if (keepSessionIdAudioTrack != null
&& audioSessionId != keepSessionIdAudioTrack.getAudioSessionId()) {
releaseKeepSessionIdAudioTrack();
}
if (keepSessionIdAudioTrack == null) {
keepSessionIdAudioTrack = initializeKeepSessionIdAudioTrack(audioSessionId);
}
}
}
if (this.audioSessionId != audioSessionId) {
this.audioSessionId = audioSessionId;
if (listener != null) {
listener.onAudioSessionId(audioSessionId);
}
}
audioSessionId = audioTrack.getAudioSessionId();
audioTrackPositionTracker.setAudioTrack(
audioTrack,
/* isPassthrough= */ configuration.outputMode == OUTPUT_MODE_PASSTHROUGH,
......@@ -1115,13 +1082,13 @@ public final class DefaultAudioSink implements AudioSink {
return;
}
flush();
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
}
@Override
public void setAudioSessionId(int audioSessionId) {
if (this.audioSessionId != audioSessionId) {
this.audioSessionId = audioSessionId;
externalAudioSessionIdProvided = audioSessionId != C.AUDIO_SESSION_ID_UNSET;
flush();
}
}
......@@ -1145,11 +1112,11 @@ public final class DefaultAudioSink implements AudioSink {
}
@Override
public void enableTunnelingV21(int tunnelingAudioSessionId) {
public void enableTunnelingV21() {
Assertions.checkState(Util.SDK_INT >= 21);
if (!tunneling || audioSessionId != tunnelingAudioSessionId) {
Assertions.checkState(externalAudioSessionIdProvided);
if (!tunneling) {
tunneling = true;
audioSessionId = tunnelingAudioSessionId;
flush();
}
}
......@@ -1158,7 +1125,6 @@ public final class DefaultAudioSink implements AudioSink {
public void disableTunneling() {
if (tunneling) {
tunneling = false;
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
flush();
}
}
......@@ -1203,6 +1169,14 @@ public final class DefaultAudioSink implements AudioSink {
// AudioTrack.release can take some time, so we call it on a background thread.
final AudioTrack toRelease = audioTrack;
audioTrack = null;
if (Util.SDK_INT < 21 && !externalAudioSessionIdProvided) {
// Prior to API level 21, audio sessions are not kept alive once there are no components
// associated with them. If we generated the session ID internally, the only component
// associated with the session is the audio track that's being released, and therefore
// the session will not be kept alive. As a result, we need to generate a new session when
// we next create an audio track.
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
}
if (pendingConfiguration != null) {
configuration = pendingConfiguration;
pendingConfiguration = null;
......@@ -1261,14 +1235,12 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public void reset() {
flush();
releaseKeepSessionIdAudioTrack();
for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
audioProcessor.reset();
}
for (AudioProcessor audioProcessor : toFloatPcmAvailableAudioProcessors) {
audioProcessor.reset();
}
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
playing = false;
offloadDisabledUntilNextConfiguration = false;
}
......@@ -1303,23 +1275,6 @@ public final class DefaultAudioSink implements AudioSink {
flushAudioProcessors();
}
/** Releases {@link #keepSessionIdAudioTrack} asynchronously, if it is non-{@code null}. */
private void releaseKeepSessionIdAudioTrack() {
if (keepSessionIdAudioTrack == null) {
return;
}
// AudioTrack.release can take some time, so we call it on a background thread.
final AudioTrack toRelease = keepSessionIdAudioTrack;
keepSessionIdAudioTrack = null;
new Thread() {
@Override
public void run() {
toRelease.release();
}
}.start();
}
@RequiresApi(23)
private void setAudioTrackPlaybackParametersV23(PlaybackParameters audioTrackPlaybackParameters) {
if (isAudioTrackInitialized()) {
......@@ -1587,21 +1542,6 @@ public final class DefaultAudioSink implements AudioSink {
return Util.SDK_INT >= 30 && Util.MODEL.startsWith("Pixel");
}
private static AudioTrack initializeKeepSessionIdAudioTrack(int audioSessionId) {
int sampleRate = 4000; // Equal to private AudioTrack.MIN_SAMPLE_RATE.
int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
@C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT;
int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback.
return new AudioTrack(
C.STREAM_TYPE_DEFAULT,
sampleRate,
channelConfig,
encoding,
bufferSize,
AudioTrack.MODE_STATIC,
audioSessionId);
}
private static int getMaximumEncodedRateBytesPerSecond(@C.Encoding int encoding) {
switch (encoding) {
case C.ENCODING_MP3:
......
......@@ -124,8 +124,8 @@ public class ForwardingAudioSink implements AudioSink {
}
@Override
public void enableTunnelingV21(int tunnelingAudioSessionId) {
sink.enableTunnelingV21(tunnelingAudioSessionId);
public void enableTunnelingV21() {
sink.enableTunnelingV21();
}
@Override
......
......@@ -486,9 +486,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
throws ExoPlaybackException {
super.onEnabled(joining, mayRenderStartOfStream);
eventDispatcher.enabled(decoderCounters);
int tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId;
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
audioSink.enableTunnelingV21(tunnelingAudioSessionId);
if (getConfiguration().tunneling) {
audioSink.enableTunnelingV21();
} else {
audioSink.disableTunneling();
}
......@@ -814,11 +813,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private final class AudioSinkListener implements AudioSink.Listener {
@Override
public void onAudioSessionId(int audioSessionId) {
eventDispatcher.audioSessionId(audioSessionId);
}
@Override
public void onPositionDiscontinuity() {
MediaCodecAudioRenderer.this.onPositionDiscontinuity();
}
......
......@@ -285,6 +285,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
currentHeight = Format.NO_VALUE;
currentPixelWidthHeightRatio = Format.NO_VALUE;
scalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
clearReportedVideoSize();
}
......@@ -400,10 +401,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
throws ExoPlaybackException {
super.onEnabled(joining, mayRenderStartOfStream);
int oldTunnelingAudioSessionId = tunnelingAudioSessionId;
tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId;
tunneling = tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET;
if (tunnelingAudioSessionId != oldTunnelingAudioSessionId) {
boolean tunneling = getConfiguration().tunneling;
Assertions.checkState(!tunneling || tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET);
if (this.tunneling != tunneling) {
this.tunneling = tunneling;
releaseCodec();
}
eventDispatcher.enabled(decoderCounters);
......@@ -516,7 +517,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
frameMetadataListener = (VideoFrameMetadataListener) message;
break;
case MSG_SET_AUDIO_SESSION_ID:
// TODO: Set tunnelingAudioSessionId.
int tunnelingAudioSessionId = (int) message;
if (this.tunnelingAudioSessionId != tunnelingAudioSessionId) {
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
if (tunneling) {
releaseCodec();
}
}
break;
default:
super.handleMessage(messageType, message);
......@@ -600,7 +607,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
codecMaxValues,
codecOperatingRate,
deviceNeedsNoPostProcessWorkaround,
tunnelingAudioSessionId);
tunneling ? tunnelingAudioSessionId : C.AUDIO_SESSION_ID_UNSET);
if (surface == null) {
if (!shouldUseDummySurface(codecInfo)) {
throw new IllegalStateException();
......
......@@ -20,7 +20,6 @@ import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_AU
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_AUDIO_ENABLED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_AUDIO_INPUT_FORMAT_CHANGED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_AUDIO_POSITION_ADVANCING;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_AUDIO_SESSION_ID;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DOWNSTREAM_FORMAT_CHANGED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DRM_KEYS_LOADED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DRM_SESSION_ACQUIRED;
......@@ -243,7 +242,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_ENABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_DECODER_INITIALIZED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)).containsExactly(period0);
......@@ -323,7 +321,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED))
.containsExactly(period0, period1)
.inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED))
......@@ -399,7 +396,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_ENABLED)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_AUDIO_DECODER_INITIALIZED)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)).containsExactly(period0);
......@@ -492,9 +488,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED))
.containsExactly(period0, period1)
.inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID))
.containsExactly(period0, period1)
.inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING))
.containsExactly(period0, period1)
.inOrder();
......@@ -595,9 +588,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED))
.containsExactly(period1Seq1, period1Seq2)
.inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID))
.containsExactly(period1Seq1, period1Seq2)
.inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING))
.containsExactly(period1Seq1, period1Seq2)
.inOrder();
......
......@@ -296,21 +296,6 @@ public class MediaCodecAudioRendererTest {
@Test
public void
render_callsAudioRendererEventListener_whenAudioSinkListenerOnAudioSessionIdIsCalled() {
final ArgumentCaptor<AudioSink.Listener> listenerCaptor =
ArgumentCaptor.forClass(AudioSink.Listener.class);
verify(audioSink, atLeastOnce()).setListener(listenerCaptor.capture());
AudioSink.Listener audioSinkListener = listenerCaptor.getValue();
int audioSessionId = 2;
audioSinkListener.onAudioSessionId(audioSessionId);
shadowOf(Looper.getMainLooper()).idle();
verify(audioRendererEventListener).onAudioSessionId(audioSessionId);
}
@Test
public void
render_callsAudioRendererEventListener_whenAudioSinkListenerOnAudioSinkErrorIsCalled() {
final ArgumentCaptor<AudioSink.Listener> listenerCaptor =
ArgumentCaptor.forClass(AudioSink.Listener.class);
......
......@@ -1683,7 +1683,7 @@ public final class DefaultTrackSelectorTest {
/* forceLowestBitrate= */ false,
/* forceHighestSupportedBitrate= */ true,
/* exceedRendererCapabilitiesIfNecessary= */ false,
/* tunnelingAudioSessionId= */ 13,
/* tunnelingEnabled= */ true,
/* allowMultipleAdaptiveSelections= */ true,
// Overrides
selectionOverrides,
......
......@@ -29,7 +29,6 @@ public class FakeAudioRenderer extends FakeRenderer {
private final AudioRendererEventListener.EventDispatcher eventDispatcher;
private final DecoderCounters decoderCounters;
private boolean notifiedAudioSessionId;
private boolean notifiedPositionAdvancing;
public FakeAudioRenderer(Handler handler, AudioRendererEventListener eventListener) {
......@@ -43,7 +42,6 @@ public class FakeAudioRenderer extends FakeRenderer {
throws ExoPlaybackException {
super.onEnabled(joining, mayRenderStartOfStream);
eventDispatcher.enabled(decoderCounters);
notifiedAudioSessionId = false;
notifiedPositionAdvancing = false;
}
......@@ -65,10 +63,6 @@ public class FakeAudioRenderer extends FakeRenderer {
@Override
protected boolean shouldProcessBuffer(long bufferTimeUs, long playbackPositionUs) {
boolean shouldProcess = super.shouldProcessBuffer(bufferTimeUs, playbackPositionUs);
if (shouldProcess && !notifiedAudioSessionId) {
eventDispatcher.audioSessionId(/* audioSessionId= */ 1);
notifiedAudioSessionId = true;
}
if (shouldProcess && !notifiedPositionAdvancing) {
eventDispatcher.positionAdvancing(System.currentTimeMillis());
notifiedPositionAdvancing = true;
......
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