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 @@ ...@@ -55,6 +55,10 @@
* Track selection: * Track selection:
* Allow parallel adaptation for video and audio * Allow parallel adaptation for video and audio
([#5111](https://github.com/google/ExoPlayer/issues/5111)). ([#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 multiple preferred audio or text languages.
* Add option to specify preferred MIME type(s) for video and audio * Add option to specify preferred MIME type(s) for video and audio
([#8320](https://github.com/google/ExoPlayer/issues/8320)). ([#8320](https://github.com/google/ExoPlayer/issues/8320)).
...@@ -87,6 +91,18 @@ ...@@ -87,6 +91,18 @@
`DecoderReuseEvaluation` indicates whether it was possible to re-use an `DecoderReuseEvaluation` indicates whether it was possible to re-use an
existing decoder instance for the new format, and if not then the existing decoder instance for the new format, and if not then the
reasons why. 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: * Metadata retriever:
* Parse Google Photos HEIC motion photos metadata. * Parse Google Photos HEIC motion photos metadata.
* Data sources: * Data sources:
......
...@@ -89,7 +89,7 @@ public interface Player { ...@@ -89,7 +89,7 @@ public interface Player {
* default audio attributes will be used. They are suitable for general media playback. * 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 * <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 * <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. * ignored, but they will take effect if audio is later played without tunneling.
......
...@@ -22,24 +22,16 @@ import androidx.annotation.Nullable; ...@@ -22,24 +22,16 @@ import androidx.annotation.Nullable;
*/ */
public final class RendererConfiguration { public final class RendererConfiguration {
/** /** The default configuration. */
* The default configuration.
*/
public static final RendererConfiguration DEFAULT = public static final RendererConfiguration DEFAULT =
new RendererConfiguration(C.AUDIO_SESSION_ID_UNSET); new RendererConfiguration(/* tunneling= */ false);
/** /** Whether to enable tunneling. */
* The audio session id to use for tunneling, or {@link C#AUDIO_SESSION_ID_UNSET} if tunneling public final boolean tunneling;
* should not be enabled.
*/
public final int tunnelingAudioSessionId;
/** /** @param tunneling Whether to enable tunneling. */
* @param tunnelingAudioSessionId The audio session id to use for tunneling, or public RendererConfiguration(boolean tunneling) {
* {@link C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled. this.tunneling = tunneling;
*/
public RendererConfiguration(int tunnelingAudioSessionId) {
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
} }
@Override @Override
...@@ -51,12 +43,11 @@ public final class RendererConfiguration { ...@@ -51,12 +43,11 @@ public final class RendererConfiguration {
return false; return false;
} }
RendererConfiguration other = (RendererConfiguration) obj; RendererConfiguration other = (RendererConfiguration) obj;
return tunnelingAudioSessionId == other.tunnelingAudioSessionId; return tunneling == other.tunneling;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return tunnelingAudioSessionId; return tunneling ? 0 : 1;
} }
} }
...@@ -18,6 +18,8 @@ package com.google.android.exoplayer2; ...@@ -18,6 +18,8 @@ package com.google.android.exoplayer2;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.PlaybackParams; import android.media.PlaybackParams;
import android.os.Handler; import android.os.Handler;
...@@ -568,6 +570,7 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -568,6 +570,7 @@ public class SimpleExoPlayer extends BasePlayer
protected final Renderer[] renderers; protected final Renderer[] renderers;
private final Context applicationContext;
private final ExoPlayerImpl player; private final ExoPlayerImpl player;
private final ComponentListener componentListener; private final ComponentListener componentListener;
private final CopyOnWriteArraySet<com.google.android.exoplayer2.video.VideoListener> private final CopyOnWriteArraySet<com.google.android.exoplayer2.video.VideoListener>
...@@ -588,7 +591,7 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -588,7 +591,7 @@ public class SimpleExoPlayer extends BasePlayer
@Nullable private Format videoFormat; @Nullable private Format videoFormat;
@Nullable private Format audioFormat; @Nullable private Format audioFormat;
@Nullable private AudioTrack keepSessionIdAudioTrack;
@Nullable private VideoDecoderOutputBufferRenderer videoDecoderOutputBufferRenderer; @Nullable private VideoDecoderOutputBufferRenderer videoDecoderOutputBufferRenderer;
@Nullable private Surface surface; @Nullable private Surface surface;
private boolean ownsSurface; private boolean ownsSurface;
...@@ -640,6 +643,7 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -640,6 +643,7 @@ public class SimpleExoPlayer extends BasePlayer
/** @param builder The {@link Builder} to obtain all construction parameters. */ /** @param builder The {@link Builder} to obtain all construction parameters. */
protected SimpleExoPlayer(Builder builder) { protected SimpleExoPlayer(Builder builder) {
applicationContext = builder.context.getApplicationContext();
analyticsCollector = builder.analyticsCollector; analyticsCollector = builder.analyticsCollector;
priorityTaskManager = builder.priorityTaskManager; priorityTaskManager = builder.priorityTaskManager;
audioAttributes = builder.audioAttributes; audioAttributes = builder.audioAttributes;
...@@ -665,7 +669,11 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -665,7 +669,11 @@ public class SimpleExoPlayer extends BasePlayer
// Set initial values. // Set initial values.
audioVolume = 1; 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(); currentCues = Collections.emptyList();
throwsWhenUsingWrongThread = true; throwsWhenUsingWrongThread = true;
...@@ -706,6 +714,8 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -706,6 +714,8 @@ public class SimpleExoPlayer extends BasePlayer
wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK); wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK);
deviceInfo = createDeviceInfo(streamVolumeManager); 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_AUDIO, Renderer.MSG_SET_AUDIO_ATTRIBUTES, audioAttributes);
sendRendererMessage(C.TRACK_TYPE_VIDEO, Renderer.MSG_SET_SCALING_MODE, videoScalingMode); sendRendererMessage(C.TRACK_TYPE_VIDEO, Renderer.MSG_SET_SCALING_MODE, videoScalingMode);
sendRendererMessage( sendRendererMessage(
...@@ -960,11 +970,22 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -960,11 +970,22 @@ public class SimpleExoPlayer extends BasePlayer
if (this.audioSessionId == audioSessionId) { if (this.audioSessionId == audioSessionId) {
return; 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; this.audioSessionId = audioSessionId;
sendRendererMessage(C.TRACK_TYPE_AUDIO, Renderer.MSG_SET_AUDIO_SESSION_ID, 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); sendRendererMessage(C.TRACK_TYPE_VIDEO, Renderer.MSG_SET_AUDIO_SESSION_ID, audioSessionId);
if (audioSessionId != C.AUDIO_SESSION_ID_UNSET) { for (AudioListener audioListener : audioListeners) {
notifyAudioSessionIdSet(); audioListener.onAudioSessionId(audioSessionId);
} }
} }
...@@ -1024,7 +1045,7 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -1024,7 +1045,7 @@ public class SimpleExoPlayer extends BasePlayer
* Sets the stream type for audio playback, used by the underlying audio track. * 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 * <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 * <p>Calling this method overwrites any attributes set previously by calling {@link
* #setAudioAttributes(AudioAttributes)}. * #setAudioAttributes(AudioAttributes)}.
...@@ -1760,6 +1781,10 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -1760,6 +1781,10 @@ public class SimpleExoPlayer extends BasePlayer
@Override @Override
public void release() { public void release() {
verifyApplicationThread(); verifyApplicationThread();
if (Util.SDK_INT < 21 && keepSessionIdAudioTrack != null) {
keepSessionIdAudioTrack.release();
keepSessionIdAudioTrack = null;
}
audioBecomingNoisyManager.setEnabled(false); audioBecomingNoisyManager.setEnabled(false);
streamVolumeManager.release(); streamVolumeManager.release();
wakeLockManager.setStayAwake(false); wakeLockManager.setStayAwake(false);
...@@ -2100,19 +2125,6 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -2100,19 +2125,6 @@ public class SimpleExoPlayer extends BasePlayer
sendRendererMessage(C.TRACK_TYPE_AUDIO, Renderer.MSG_SET_VOLUME, scaledVolume); 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") @SuppressWarnings("SuspiciousMethodCalls")
private void notifySkipSilenceEnabledChanged() { private void notifySkipSilenceEnabledChanged() {
for (AudioListener listener : audioListeners) { for (AudioListener listener : audioListeners) {
...@@ -2181,6 +2193,40 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -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) { private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) {
return new DeviceInfo( return new DeviceInfo(
DeviceInfo.PLAYBACK_TYPE_LOCAL, DeviceInfo.PLAYBACK_TYPE_LOCAL,
...@@ -2304,15 +2350,6 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -2304,15 +2350,6 @@ public class SimpleExoPlayer extends BasePlayer
} }
@Override @Override
public void onAudioSessionId(int sessionId) {
if (audioSessionId == sessionId) {
return;
}
audioSessionId = sessionId;
notifyAudioSessionIdSet();
}
@Override
public void onAudioDecoderInitialized( public void onAudioDecoderInitialized(
String decoderName, long initializedTimestampMs, long initializationDurationMs) { String decoderName, long initializedTimestampMs, long initializationDurationMs) {
for (AudioRendererEventListener audioDebugListener : audioDebugListeners) { for (AudioRendererEventListener audioDebugListener : audioDebugListeners) {
......
...@@ -45,13 +45,6 @@ public interface AudioRendererEventListener { ...@@ -45,13 +45,6 @@ public interface AudioRendererEventListener {
default void onAudioEnabled(DecoderCounters counters) {} 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. * Called when a decoder is created.
* *
* @param decoderName The decoder that was created. * @param decoderName The decoder that was created.
...@@ -224,13 +217,6 @@ public interface AudioRendererEventListener { ...@@ -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)}. */ /** Invokes {@link AudioRendererEventListener#onSkipSilenceEnabledChanged(boolean)}. */
public void skipSilenceEnabledChanged(boolean skipSilenceEnabled) { public void skipSilenceEnabledChanged(boolean skipSilenceEnabled) {
if (handler != null) { if (handler != null) {
......
...@@ -49,9 +49,9 @@ import java.nio.ByteBuffer; ...@@ -49,9 +49,9 @@ import java.nio.ByteBuffer;
* *
* <p>The implementation may be backed by a platform {@link AudioTrack}. In this case, {@link * <p>The implementation may be backed by a platform {@link AudioTrack}. In this case, {@link
* #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link * #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link
* #enableTunnelingV21(int)} and/or {@link #disableTunneling()} may be called before writing data to * #enableTunnelingV21()} and {@link #disableTunneling()} may be called before writing data to the
* the sink. These methods may also be called after writing data to the sink, in which case it will * sink. These methods may also be called after writing data to the sink, in which case it will be
* be reinitialized as required. For implementations that are not based on platform {@link * 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 * AudioTrack}s, calling methods relating to audio sessions, audio attributes, and tunneling may
* have no effect. * have no effect.
*/ */
...@@ -63,13 +63,6 @@ public interface AudioSink { ...@@ -63,13 +63,6 @@ public interface AudioSink {
interface Listener { 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 * Called when the audio sink handles a buffer whose timestamp is discontinuous with the last
* buffer handled since it was reset. * buffer handled since it was reset.
*/ */
...@@ -392,14 +385,13 @@ public interface AudioSink { ...@@ -392,14 +385,13 @@ public interface AudioSink {
void setAuxEffectInfo(AuxEffectInfo auxEffectInfo); void setAuxEffectInfo(AuxEffectInfo auxEffectInfo);
/** /**
* Enables tunneling, if possible. The sink is reset if tunneling was previously disabled or if * Enables tunneling, if possible. The sink is reset if tunneling was previously disabled.
* the audio session id has changed. Enabling tunneling is only possible if the sink is based on a * Enabling tunneling is only possible if the sink is based on a platform {@link AudioTrack}, and
* platform {@link AudioTrack}, and requires platform API version 21 onwards. * 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. * @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 * Disables tunneling. If tunneling was previously enabled then the sink is reset and any audio
......
...@@ -512,9 +512,8 @@ public abstract class DecoderAudioRenderer< ...@@ -512,9 +512,8 @@ public abstract class DecoderAudioRenderer<
throws ExoPlaybackException { throws ExoPlaybackException {
decoderCounters = new DecoderCounters(); decoderCounters = new DecoderCounters();
eventDispatcher.enabled(decoderCounters); eventDispatcher.enabled(decoderCounters);
int tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId; if (getConfiguration().tunneling) {
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) { audioSink.enableTunnelingV21();
audioSink.enableTunnelingV21(tunnelingAudioSessionId);
} else { } else {
audioSink.disableTunneling(); audioSink.disableTunneling();
} }
...@@ -715,11 +714,6 @@ public abstract class DecoderAudioRenderer< ...@@ -715,11 +714,6 @@ public abstract class DecoderAudioRenderer<
private final class AudioSinkListener implements AudioSink.Listener { private final class AudioSinkListener implements AudioSink.Listener {
@Override @Override
public void onAudioSessionId(int audioSessionId) {
eventDispatcher.audioSessionId(audioSessionId);
}
@Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
DecoderAudioRenderer.this.onPositionDiscontinuity(); DecoderAudioRenderer.this.onPositionDiscontinuity();
} }
......
...@@ -262,15 +262,6 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -262,15 +262,6 @@ public final class DefaultAudioSink implements AudioSink {
private static final String TAG = "DefaultAudioSink"; 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 * Whether to throw an {@link InvalidAudioTrackTimestampException} when a spurious timestamp is
* reported from {@link AudioTrack#getTimestamp}. * reported from {@link AudioTrack#getTimestamp}.
* <p> * <p>
...@@ -297,11 +288,6 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -297,11 +288,6 @@ public final class DefaultAudioSink implements AudioSink {
private final PendingExceptionHolder<WriteException> writeExceptionPendingExceptionHolder; private final PendingExceptionHolder<WriteException> writeExceptionPendingExceptionHolder;
@Nullable private Listener listener; @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; @Nullable private Configuration pendingConfiguration;
@MonotonicNonNull private Configuration configuration; @MonotonicNonNull private Configuration configuration;
@Nullable private AudioTrack audioTrack; @Nullable private AudioTrack audioTrack;
...@@ -336,6 +322,7 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -336,6 +322,7 @@ public final class DefaultAudioSink implements AudioSink {
private boolean stoppedAudioTrack; private boolean stoppedAudioTrack;
private boolean playing; private boolean playing;
private boolean externalAudioSessionIdProvided;
private int audioSessionId; private int audioSessionId;
private AuxEffectInfo auxEffectInfo; private AuxEffectInfo auxEffectInfo;
private boolean tunneling; private boolean tunneling;
...@@ -646,27 +633,7 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -646,27 +633,7 @@ public final class DefaultAudioSink implements AudioSink {
audioTrack.setOffloadDelayPadding( audioTrack.setOffloadDelayPadding(
configuration.inputFormat.encoderDelay, configuration.inputFormat.encoderPadding); configuration.inputFormat.encoderDelay, configuration.inputFormat.encoderPadding);
} }
int audioSessionId = audioTrack.getAudioSessionId(); 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);
}
}
audioTrackPositionTracker.setAudioTrack( audioTrackPositionTracker.setAudioTrack(
audioTrack, audioTrack,
/* isPassthrough= */ configuration.outputMode == OUTPUT_MODE_PASSTHROUGH, /* isPassthrough= */ configuration.outputMode == OUTPUT_MODE_PASSTHROUGH,
...@@ -1115,13 +1082,13 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1115,13 +1082,13 @@ public final class DefaultAudioSink implements AudioSink {
return; return;
} }
flush(); flush();
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
} }
@Override @Override
public void setAudioSessionId(int audioSessionId) { public void setAudioSessionId(int audioSessionId) {
if (this.audioSessionId != audioSessionId) { if (this.audioSessionId != audioSessionId) {
this.audioSessionId = audioSessionId; this.audioSessionId = audioSessionId;
externalAudioSessionIdProvided = audioSessionId != C.AUDIO_SESSION_ID_UNSET;
flush(); flush();
} }
} }
...@@ -1145,11 +1112,11 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1145,11 +1112,11 @@ public final class DefaultAudioSink implements AudioSink {
} }
@Override @Override
public void enableTunnelingV21(int tunnelingAudioSessionId) { public void enableTunnelingV21() {
Assertions.checkState(Util.SDK_INT >= 21); Assertions.checkState(Util.SDK_INT >= 21);
if (!tunneling || audioSessionId != tunnelingAudioSessionId) { Assertions.checkState(externalAudioSessionIdProvided);
if (!tunneling) {
tunneling = true; tunneling = true;
audioSessionId = tunnelingAudioSessionId;
flush(); flush();
} }
} }
...@@ -1158,7 +1125,6 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1158,7 +1125,6 @@ public final class DefaultAudioSink implements AudioSink {
public void disableTunneling() { public void disableTunneling() {
if (tunneling) { if (tunneling) {
tunneling = false; tunneling = false;
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
flush(); flush();
} }
} }
...@@ -1203,6 +1169,14 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1203,6 +1169,14 @@ public final class DefaultAudioSink implements AudioSink {
// AudioTrack.release can take some time, so we call it on a background thread. // AudioTrack.release can take some time, so we call it on a background thread.
final AudioTrack toRelease = audioTrack; final AudioTrack toRelease = audioTrack;
audioTrack = null; 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) { if (pendingConfiguration != null) {
configuration = pendingConfiguration; configuration = pendingConfiguration;
pendingConfiguration = null; pendingConfiguration = null;
...@@ -1261,14 +1235,12 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1261,14 +1235,12 @@ public final class DefaultAudioSink implements AudioSink {
@Override @Override
public void reset() { public void reset() {
flush(); flush();
releaseKeepSessionIdAudioTrack();
for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) { for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
audioProcessor.reset(); audioProcessor.reset();
} }
for (AudioProcessor audioProcessor : toFloatPcmAvailableAudioProcessors) { for (AudioProcessor audioProcessor : toFloatPcmAvailableAudioProcessors) {
audioProcessor.reset(); audioProcessor.reset();
} }
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
playing = false; playing = false;
offloadDisabledUntilNextConfiguration = false; offloadDisabledUntilNextConfiguration = false;
} }
...@@ -1303,23 +1275,6 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1303,23 +1275,6 @@ public final class DefaultAudioSink implements AudioSink {
flushAudioProcessors(); 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) @RequiresApi(23)
private void setAudioTrackPlaybackParametersV23(PlaybackParameters audioTrackPlaybackParameters) { private void setAudioTrackPlaybackParametersV23(PlaybackParameters audioTrackPlaybackParameters) {
if (isAudioTrackInitialized()) { if (isAudioTrackInitialized()) {
...@@ -1587,21 +1542,6 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1587,21 +1542,6 @@ public final class DefaultAudioSink implements AudioSink {
return Util.SDK_INT >= 30 && Util.MODEL.startsWith("Pixel"); 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) { private static int getMaximumEncodedRateBytesPerSecond(@C.Encoding int encoding) {
switch (encoding) { switch (encoding) {
case C.ENCODING_MP3: case C.ENCODING_MP3:
......
...@@ -124,8 +124,8 @@ public class ForwardingAudioSink implements AudioSink { ...@@ -124,8 +124,8 @@ public class ForwardingAudioSink implements AudioSink {
} }
@Override @Override
public void enableTunnelingV21(int tunnelingAudioSessionId) { public void enableTunnelingV21() {
sink.enableTunnelingV21(tunnelingAudioSessionId); sink.enableTunnelingV21();
} }
@Override @Override
......
...@@ -486,9 +486,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -486,9 +486,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
throws ExoPlaybackException { throws ExoPlaybackException {
super.onEnabled(joining, mayRenderStartOfStream); super.onEnabled(joining, mayRenderStartOfStream);
eventDispatcher.enabled(decoderCounters); eventDispatcher.enabled(decoderCounters);
int tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId; if (getConfiguration().tunneling) {
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) { audioSink.enableTunnelingV21();
audioSink.enableTunnelingV21(tunnelingAudioSessionId);
} else { } else {
audioSink.disableTunneling(); audioSink.disableTunneling();
} }
...@@ -814,11 +813,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -814,11 +813,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private final class AudioSinkListener implements AudioSink.Listener { private final class AudioSinkListener implements AudioSink.Listener {
@Override @Override
public void onAudioSessionId(int audioSessionId) {
eventDispatcher.audioSessionId(audioSessionId);
}
@Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
MediaCodecAudioRenderer.this.onPositionDiscontinuity(); MediaCodecAudioRenderer.this.onPositionDiscontinuity();
} }
......
...@@ -285,6 +285,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -285,6 +285,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
currentHeight = Format.NO_VALUE; currentHeight = Format.NO_VALUE;
currentPixelWidthHeightRatio = Format.NO_VALUE; currentPixelWidthHeightRatio = Format.NO_VALUE;
scalingMode = C.VIDEO_SCALING_MODE_DEFAULT; scalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
clearReportedVideoSize(); clearReportedVideoSize();
} }
...@@ -400,10 +401,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -400,10 +401,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
protected void onEnabled(boolean joining, boolean mayRenderStartOfStream) protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
throws ExoPlaybackException { throws ExoPlaybackException {
super.onEnabled(joining, mayRenderStartOfStream); super.onEnabled(joining, mayRenderStartOfStream);
int oldTunnelingAudioSessionId = tunnelingAudioSessionId; boolean tunneling = getConfiguration().tunneling;
tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId; Assertions.checkState(!tunneling || tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET);
tunneling = tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET; if (this.tunneling != tunneling) {
if (tunnelingAudioSessionId != oldTunnelingAudioSessionId) { this.tunneling = tunneling;
releaseCodec(); releaseCodec();
} }
eventDispatcher.enabled(decoderCounters); eventDispatcher.enabled(decoderCounters);
...@@ -516,7 +517,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -516,7 +517,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
frameMetadataListener = (VideoFrameMetadataListener) message; frameMetadataListener = (VideoFrameMetadataListener) message;
break; break;
case MSG_SET_AUDIO_SESSION_ID: case MSG_SET_AUDIO_SESSION_ID:
// TODO: Set tunnelingAudioSessionId. int tunnelingAudioSessionId = (int) message;
if (this.tunnelingAudioSessionId != tunnelingAudioSessionId) {
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
if (tunneling) {
releaseCodec();
}
}
break; break;
default: default:
super.handleMessage(messageType, message); super.handleMessage(messageType, message);
...@@ -600,7 +607,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -600,7 +607,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
codecMaxValues, codecMaxValues,
codecOperatingRate, codecOperatingRate,
deviceNeedsNoPostProcessWorkaround, deviceNeedsNoPostProcessWorkaround,
tunnelingAudioSessionId); tunneling ? tunnelingAudioSessionId : C.AUDIO_SESSION_ID_UNSET);
if (surface == null) { if (surface == null) {
if (!shouldUseDummySurface(codecInfo)) { if (!shouldUseDummySurface(codecInfo)) {
throw new IllegalStateException(); throw new IllegalStateException();
......
...@@ -20,7 +20,6 @@ import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_AU ...@@ -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_ENABLED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_AUDIO_INPUT_FORMAT_CHANGED; 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_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_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_KEYS_LOADED;
import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DRM_SESSION_ACQUIRED; import static com.google.android.exoplayer2.analytics.AnalyticsListener.EVENT_DRM_SESSION_ACQUIRED;
...@@ -243,7 +242,6 @@ public final class AnalyticsCollectorTest { ...@@ -243,7 +242,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_ENABLED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_AUDIO_ENABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_DECODER_INITIALIZED)).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_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_AUDIO_POSITION_ADVANCING)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)).containsExactly(period0);
...@@ -323,7 +321,6 @@ public final class AnalyticsCollectorTest { ...@@ -323,7 +321,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED))
.containsExactly(period0, period1) .containsExactly(period0, period1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING)).containsExactly(period0); assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)) assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED))
...@@ -399,7 +396,6 @@ public final class AnalyticsCollectorTest { ...@@ -399,7 +396,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_ENABLED)).containsExactly(period1); assertThat(listener.getEvents(EVENT_AUDIO_ENABLED)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_AUDIO_DECODER_INITIALIZED)).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_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_AUDIO_POSITION_ADVANCING)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_ENABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)).containsExactly(period0);
...@@ -492,9 +488,6 @@ public final class AnalyticsCollectorTest { ...@@ -492,9 +488,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED))
.containsExactly(period0, period1) .containsExactly(period0, period1)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID))
.containsExactly(period0, period1)
.inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING)) assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING))
.containsExactly(period0, period1) .containsExactly(period0, period1)
.inOrder(); .inOrder();
...@@ -595,9 +588,6 @@ public final class AnalyticsCollectorTest { ...@@ -595,9 +588,6 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_AUDIO_INPUT_FORMAT_CHANGED))
.containsExactly(period1Seq1, period1Seq2) .containsExactly(period1Seq1, period1Seq2)
.inOrder(); .inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID))
.containsExactly(period1Seq1, period1Seq2)
.inOrder();
assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING)) assertThat(listener.getEvents(EVENT_AUDIO_POSITION_ADVANCING))
.containsExactly(period1Seq1, period1Seq2) .containsExactly(period1Seq1, period1Seq2)
.inOrder(); .inOrder();
......
...@@ -296,21 +296,6 @@ public class MediaCodecAudioRendererTest { ...@@ -296,21 +296,6 @@ public class MediaCodecAudioRendererTest {
@Test @Test
public void 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() { render_callsAudioRendererEventListener_whenAudioSinkListenerOnAudioSinkErrorIsCalled() {
final ArgumentCaptor<AudioSink.Listener> listenerCaptor = final ArgumentCaptor<AudioSink.Listener> listenerCaptor =
ArgumentCaptor.forClass(AudioSink.Listener.class); ArgumentCaptor.forClass(AudioSink.Listener.class);
......
...@@ -1683,7 +1683,7 @@ public final class DefaultTrackSelectorTest { ...@@ -1683,7 +1683,7 @@ public final class DefaultTrackSelectorTest {
/* forceLowestBitrate= */ false, /* forceLowestBitrate= */ false,
/* forceHighestSupportedBitrate= */ true, /* forceHighestSupportedBitrate= */ true,
/* exceedRendererCapabilitiesIfNecessary= */ false, /* exceedRendererCapabilitiesIfNecessary= */ false,
/* tunnelingAudioSessionId= */ 13, /* tunnelingEnabled= */ true,
/* allowMultipleAdaptiveSelections= */ true, /* allowMultipleAdaptiveSelections= */ true,
// Overrides // Overrides
selectionOverrides, selectionOverrides,
......
...@@ -29,7 +29,6 @@ public class FakeAudioRenderer extends FakeRenderer { ...@@ -29,7 +29,6 @@ public class FakeAudioRenderer extends FakeRenderer {
private final AudioRendererEventListener.EventDispatcher eventDispatcher; private final AudioRendererEventListener.EventDispatcher eventDispatcher;
private final DecoderCounters decoderCounters; private final DecoderCounters decoderCounters;
private boolean notifiedAudioSessionId;
private boolean notifiedPositionAdvancing; private boolean notifiedPositionAdvancing;
public FakeAudioRenderer(Handler handler, AudioRendererEventListener eventListener) { public FakeAudioRenderer(Handler handler, AudioRendererEventListener eventListener) {
...@@ -43,7 +42,6 @@ public class FakeAudioRenderer extends FakeRenderer { ...@@ -43,7 +42,6 @@ public class FakeAudioRenderer extends FakeRenderer {
throws ExoPlaybackException { throws ExoPlaybackException {
super.onEnabled(joining, mayRenderStartOfStream); super.onEnabled(joining, mayRenderStartOfStream);
eventDispatcher.enabled(decoderCounters); eventDispatcher.enabled(decoderCounters);
notifiedAudioSessionId = false;
notifiedPositionAdvancing = false; notifiedPositionAdvancing = false;
} }
...@@ -65,10 +63,6 @@ public class FakeAudioRenderer extends FakeRenderer { ...@@ -65,10 +63,6 @@ public class FakeAudioRenderer extends FakeRenderer {
@Override @Override
protected boolean shouldProcessBuffer(long bufferTimeUs, long playbackPositionUs) { protected boolean shouldProcessBuffer(long bufferTimeUs, long playbackPositionUs) {
boolean shouldProcess = super.shouldProcessBuffer(bufferTimeUs, playbackPositionUs); boolean shouldProcess = super.shouldProcessBuffer(bufferTimeUs, playbackPositionUs);
if (shouldProcess && !notifiedAudioSessionId) {
eventDispatcher.audioSessionId(/* audioSessionId= */ 1);
notifiedAudioSessionId = true;
}
if (shouldProcess && !notifiedPositionAdvancing) { if (shouldProcess && !notifiedPositionAdvancing) {
eventDispatcher.positionAdvancing(System.currentTimeMillis()); eventDispatcher.positionAdvancing(System.currentTimeMillis());
notifiedPositionAdvancing = true; 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