mirror of
https://github.com/samsonjs/media.git
synced 2026-03-30 10:15:48 +00:00
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
This commit is contained in:
parent
85641a38b1
commit
a95b2ebb1d
16 changed files with 157 additions and 249 deletions
|
|
@ -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,
|
||||
|
|
@ -2303,15 +2349,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) {
|
||||
|
|
|
|||
|
|
@ -44,13 +44,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.
|
||||
*
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
|
@ -62,13 +62,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 < 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();
|
||||
}
|
||||
|
|
@ -714,11 +713,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();
|
||||
|
|
|
|||
|
|
@ -261,15 +261,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} < 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}.
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -813,11 +812,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();
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
|||
*
|
||||
* Tunneled playback can be enabled in cases where the combination of renderers and selected tracks
|
||||
* support it. Tunneled playback is enabled by passing an audio session ID to {@link
|
||||
* ParametersBuilder#setTunnelingAudioSessionId(int)}.
|
||||
* ParametersBuilder#setTunnelingEnabled(boolean)}.
|
||||
*/
|
||||
public class DefaultTrackSelector extends MappingTrackSelector {
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
private boolean forceLowestBitrate;
|
||||
private boolean forceHighestSupportedBitrate;
|
||||
private boolean exceedRendererCapabilitiesIfNecessary;
|
||||
private int tunnelingAudioSessionId;
|
||||
private boolean tunnelingEnabled;
|
||||
private boolean allowMultipleAdaptiveSelections;
|
||||
|
||||
private final SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
|
||||
|
|
@ -266,7 +266,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
forceLowestBitrate = initialValues.forceLowestBitrate;
|
||||
forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate;
|
||||
exceedRendererCapabilitiesIfNecessary = initialValues.exceedRendererCapabilitiesIfNecessary;
|
||||
tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId;
|
||||
tunnelingEnabled = initialValues.tunnelingEnabled;
|
||||
allowMultipleAdaptiveSelections = initialValues.allowMultipleAdaptiveSelections;
|
||||
// Overrides
|
||||
selectionOverrides = cloneSelectionOverrides(initialValues.selectionOverrides);
|
||||
|
|
@ -681,20 +681,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the audio session id to use when tunneling.
|
||||
*
|
||||
* <p>Enables or disables tunneling. To enable tunneling, pass an audio session id to use when
|
||||
* in tunneling mode. Session ids can be generated using {@link
|
||||
* C#generateAudioSessionIdV21(Context)}. To disable tunneling pass {@link
|
||||
* C#AUDIO_SESSION_ID_UNSET}. Tunneling will only be activated if it's both enabled and
|
||||
* Sets whether to enable tunneling if possible. Tunneling will only be enabled if it's
|
||||
* supported by the audio and video renderers for the selected tracks.
|
||||
*
|
||||
* @param tunnelingAudioSessionId The audio session id to use when tunneling, or {@link
|
||||
* C#AUDIO_SESSION_ID_UNSET} to disable tunneling.
|
||||
* @param tunnelingEnabled Whether to enable tunneling if possible.
|
||||
* @return This builder.
|
||||
*/
|
||||
public ParametersBuilder setTunnelingAudioSessionId(int tunnelingAudioSessionId) {
|
||||
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
|
||||
public ParametersBuilder setTunnelingEnabled(boolean tunnelingEnabled) {
|
||||
this.tunnelingEnabled = tunnelingEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -865,7 +859,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
forceLowestBitrate,
|
||||
forceHighestSupportedBitrate,
|
||||
exceedRendererCapabilitiesIfNecessary,
|
||||
tunnelingAudioSessionId,
|
||||
tunnelingEnabled,
|
||||
allowMultipleAdaptiveSelections,
|
||||
selectionOverrides,
|
||||
rendererDisabledFlags);
|
||||
|
|
@ -897,7 +891,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
forceLowestBitrate = false;
|
||||
forceHighestSupportedBitrate = false;
|
||||
exceedRendererCapabilitiesIfNecessary = true;
|
||||
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||
tunnelingEnabled = false;
|
||||
allowMultipleAdaptiveSelections = true;
|
||||
}
|
||||
|
||||
|
|
@ -1082,12 +1076,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
* {@code true}.
|
||||
*/
|
||||
public final boolean exceedRendererCapabilitiesIfNecessary;
|
||||
/**
|
||||
* The audio session id to use when tunneling, or {@link C#AUDIO_SESSION_ID_UNSET} if tunneling
|
||||
* is disabled. The default value is {@link C#AUDIO_SESSION_ID_UNSET} (i.e. tunneling is
|
||||
* disabled).
|
||||
*/
|
||||
public final int tunnelingAudioSessionId;
|
||||
/** Whether to enable tunneling if possible. */
|
||||
public final boolean tunnelingEnabled;
|
||||
/**
|
||||
* Whether multiple adaptive selections with more than one track are allowed. The default value
|
||||
* is {@code true}.
|
||||
|
|
@ -1138,7 +1128,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
boolean forceLowestBitrate,
|
||||
boolean forceHighestSupportedBitrate,
|
||||
boolean exceedRendererCapabilitiesIfNecessary,
|
||||
int tunnelingAudioSessionId,
|
||||
boolean tunnelingEnabled,
|
||||
boolean allowMultipleAdaptiveSelections,
|
||||
// Overrides
|
||||
SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides,
|
||||
|
|
@ -1177,7 +1167,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
this.forceLowestBitrate = forceLowestBitrate;
|
||||
this.forceHighestSupportedBitrate = forceHighestSupportedBitrate;
|
||||
this.exceedRendererCapabilitiesIfNecessary = exceedRendererCapabilitiesIfNecessary;
|
||||
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
|
||||
this.tunnelingEnabled = tunnelingEnabled;
|
||||
this.allowMultipleAdaptiveSelections = allowMultipleAdaptiveSelections;
|
||||
// Overrides
|
||||
this.selectionOverrides = selectionOverrides;
|
||||
|
|
@ -1218,7 +1208,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
this.forceLowestBitrate = Util.readBoolean(in);
|
||||
this.forceHighestSupportedBitrate = Util.readBoolean(in);
|
||||
this.exceedRendererCapabilitiesIfNecessary = Util.readBoolean(in);
|
||||
this.tunnelingAudioSessionId = in.readInt();
|
||||
this.tunnelingEnabled = Util.readBoolean(in);
|
||||
this.allowMultipleAdaptiveSelections = Util.readBoolean(in);
|
||||
// Overrides
|
||||
this.selectionOverrides = readSelectionOverrides(in);
|
||||
|
|
@ -1307,7 +1297,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
&& forceLowestBitrate == other.forceLowestBitrate
|
||||
&& forceHighestSupportedBitrate == other.forceHighestSupportedBitrate
|
||||
&& exceedRendererCapabilitiesIfNecessary == other.exceedRendererCapabilitiesIfNecessary
|
||||
&& tunnelingAudioSessionId == other.tunnelingAudioSessionId
|
||||
&& tunnelingEnabled == other.tunnelingEnabled
|
||||
&& allowMultipleAdaptiveSelections == other.allowMultipleAdaptiveSelections
|
||||
// Overrides
|
||||
&& areRendererDisabledFlagsEqual(rendererDisabledFlags, other.rendererDisabledFlags)
|
||||
|
|
@ -1345,7 +1335,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
result = 31 * result + (forceLowestBitrate ? 1 : 0);
|
||||
result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0);
|
||||
result = 31 * result + (exceedRendererCapabilitiesIfNecessary ? 1 : 0);
|
||||
result = 31 * result + tunnelingAudioSessionId;
|
||||
result = 31 * result + (tunnelingEnabled ? 1 : 0);
|
||||
result = 31 * result + (allowMultipleAdaptiveSelections ? 1 : 0);
|
||||
// Overrides (omitted from hashCode).
|
||||
return result;
|
||||
|
|
@ -1389,7 +1379,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
Util.writeBoolean(dest, forceLowestBitrate);
|
||||
Util.writeBoolean(dest, forceHighestSupportedBitrate);
|
||||
Util.writeBoolean(dest, exceedRendererCapabilitiesIfNecessary);
|
||||
dest.writeInt(tunnelingAudioSessionId);
|
||||
Util.writeBoolean(dest, tunnelingEnabled);
|
||||
Util.writeBoolean(dest, allowMultipleAdaptiveSelections);
|
||||
// Overrides
|
||||
writeSelectionOverridesToParcel(dest, selectionOverrides);
|
||||
|
|
@ -1757,12 +1747,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
}
|
||||
|
||||
// Configure audio and video renderers to use tunneling if appropriate.
|
||||
maybeConfigureRenderersForTunneling(
|
||||
mappedTrackInfo,
|
||||
rendererFormatSupports,
|
||||
rendererConfigurations,
|
||||
rendererTrackSelections,
|
||||
params.tunnelingAudioSessionId);
|
||||
if (params.tunnelingEnabled) {
|
||||
maybeConfigureRenderersForTunneling(
|
||||
mappedTrackInfo, rendererFormatSupports, rendererConfigurations, rendererTrackSelections);
|
||||
}
|
||||
|
||||
return Pair.create(rendererConfigurations, rendererTrackSelections);
|
||||
}
|
||||
|
|
@ -2418,9 +2406,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
// Utility methods.
|
||||
|
||||
/**
|
||||
* Determines whether tunneling should be enabled, replacing {@link RendererConfiguration}s in
|
||||
* {@code rendererConfigurations} with configurations that enable tunneling on the appropriate
|
||||
* renderers if so.
|
||||
* Determines whether tunneling can be enabled, replacing {@link RendererConfiguration}s in {@code
|
||||
* rendererConfigurations} with configurations that enable tunneling on the appropriate renderers
|
||||
* if so.
|
||||
*
|
||||
* @param mappedTrackInfo Mapped track information.
|
||||
* @param renderererFormatSupports The {@link Capabilities} for each mapped track, indexed by
|
||||
|
|
@ -2428,18 +2416,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
* @param rendererConfigurations The renderer configurations. Configurations may be replaced with
|
||||
* ones that enable tunneling as a result of this call.
|
||||
* @param trackSelections The renderer track selections.
|
||||
* @param tunnelingAudioSessionId The audio session id to use when tunneling, or {@link
|
||||
* C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled.
|
||||
*/
|
||||
private static void maybeConfigureRenderersForTunneling(
|
||||
MappedTrackInfo mappedTrackInfo,
|
||||
@Capabilities int[][][] renderererFormatSupports,
|
||||
@NullableType RendererConfiguration[] rendererConfigurations,
|
||||
@NullableType TrackSelection[] trackSelections,
|
||||
int tunnelingAudioSessionId) {
|
||||
if (tunnelingAudioSessionId == C.AUDIO_SESSION_ID_UNSET) {
|
||||
return;
|
||||
}
|
||||
@NullableType TrackSelection[] trackSelections) {
|
||||
// Check whether we can enable tunneling. To enable tunneling we require exactly one audio and
|
||||
// one video renderer to support tunneling and have a selection.
|
||||
int tunnelingAudioRendererIndex = -1;
|
||||
|
|
@ -2473,7 +2455,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
|||
enableTunneling &= tunnelingAudioRendererIndex != -1 && tunnelingVideoRendererIndex != -1;
|
||||
if (enableTunneling) {
|
||||
RendererConfiguration tunnelingRendererConfiguration =
|
||||
new RendererConfiguration(tunnelingAudioSessionId);
|
||||
new RendererConfiguration(/* tunneling= */ true);
|
||||
rendererConfigurations[tunnelingAudioRendererIndex] = tunnelingRendererConfiguration;
|
||||
rendererConfigurations[tunnelingVideoRendererIndex] = tunnelingRendererConfiguration;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -294,21 +294,6 @@ public class MediaCodecAudioRendererTest {
|
|||
exceptionThrowingRenderer.render(/* positionUs= */ 750, SystemClock.elapsedRealtime() * 1000);
|
||||
}
|
||||
|
||||
@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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue