mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Move audio session id generation to playback thread
PiperOrigin-RevId: 726556015
This commit is contained in:
parent
22853a5c4c
commit
385498c24e
@ -6,6 +6,10 @@
|
||||
* Upgrade Kotlin from 1.9.20 to 2.0.20 and use Compose Compiler Gradle
|
||||
plugin. Upgrade KotlinX Coroutines library from 1.8.1 to 1.9.0.
|
||||
* ExoPlayer:
|
||||
* Initial audio session id is no longer immediately available after
|
||||
creating the player. You can use
|
||||
`AnalyticsListener.onAudioSessionIdChanged` to listen to the initial
|
||||
update if required.
|
||||
* Transformer:
|
||||
* Add `MediaProjectionAssetLoader`, which provides media from a
|
||||
`MediaProjection` for screen recording, and add support for screen
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package androidx.media3.exoplayer;
|
||||
|
||||
import static androidx.media3.common.C.AUDIO_SESSION_ID_UNSET;
|
||||
import static androidx.media3.common.C.TRACK_TYPE_AUDIO;
|
||||
import static androidx.media3.common.C.TRACK_TYPE_CAMERA_MOTION;
|
||||
import static androidx.media3.common.C.TRACK_TYPE_IMAGE;
|
||||
@ -82,6 +83,7 @@ import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.VideoSize;
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.common.text.CueGroup;
|
||||
import androidx.media3.common.util.BackgroundThreadStateHandler;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.ConditionVariable;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
@ -178,6 +180,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
@Nullable private AudioManager audioManager;
|
||||
private final boolean suppressPlaybackOnUnsuitableOutput;
|
||||
@Nullable private final SuitableOutputChecker suitableOutputChecker;
|
||||
private final BackgroundThreadStateHandler<Integer> audioSessionIdState;
|
||||
|
||||
private @RepeatMode int repeatMode;
|
||||
private boolean shuffleModeEnabled;
|
||||
@ -205,7 +208,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
private Size surfaceSize;
|
||||
@Nullable private DecoderCounters videoDecoderCounters;
|
||||
@Nullable private DecoderCounters audioDecoderCounters;
|
||||
private int audioSessionId;
|
||||
private AudioAttributes audioAttributes;
|
||||
private float volume;
|
||||
private boolean skipSilenceEnabled;
|
||||
@ -390,7 +392,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
playlistMetadata = MediaMetadata.EMPTY;
|
||||
staticAndDynamicMediaMetadata = MediaMetadata.EMPTY;
|
||||
maskingWindowIndex = C.INDEX_UNSET;
|
||||
audioSessionId = Util.generateAudioSessionIdV21(applicationContext);
|
||||
currentCueGroup = CueGroup.EMPTY_TIME_ZERO;
|
||||
throwsWhenUsingWrongThread = true;
|
||||
|
||||
@ -401,6 +402,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
internalPlayer.experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs);
|
||||
}
|
||||
|
||||
audioSessionIdState =
|
||||
new BackgroundThreadStateHandler<>(
|
||||
AUDIO_SESSION_ID_UNSET,
|
||||
playbackLooper,
|
||||
applicationLooper,
|
||||
clock,
|
||||
/* onStateChanged= */ this::onAudioSessionIdChanged);
|
||||
audioSessionIdState.runInBackground(
|
||||
() ->
|
||||
audioSessionIdState.setStateInBackground(
|
||||
Util.generateAudioSessionIdV21(applicationContext)));
|
||||
audioBecomingNoisyManager =
|
||||
new AudioBecomingNoisyManager(
|
||||
builder.context, playbackLooper, builder.looper, componentListener, clock);
|
||||
@ -440,8 +452,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
surfaceSize = Size.UNKNOWN;
|
||||
|
||||
internalPlayer.setAudioAttributes(audioAttributes);
|
||||
sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId);
|
||||
sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId);
|
||||
sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes);
|
||||
sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode);
|
||||
sendRendererMessage(
|
||||
@ -1491,24 +1501,22 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
@Override
|
||||
public void setAudioSessionId(int audioSessionId) {
|
||||
verifyApplicationThread();
|
||||
if (this.audioSessionId == audioSessionId) {
|
||||
if (audioSessionIdState.get() == audioSessionId) {
|
||||
return;
|
||||
}
|
||||
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
|
||||
audioSessionId = Util.generateAudioSessionIdV21(applicationContext);
|
||||
}
|
||||
this.audioSessionId = audioSessionId;
|
||||
sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId);
|
||||
sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId);
|
||||
int finalAudioSessionId = audioSessionId;
|
||||
listeners.sendEvent(
|
||||
EVENT_AUDIO_SESSION_ID, listener -> listener.onAudioSessionIdChanged(finalAudioSessionId));
|
||||
audioSessionIdState.updateStateAsync(
|
||||
/* placeholderState= */ previousId ->
|
||||
audioSessionId != AUDIO_SESSION_ID_UNSET ? audioSessionId : previousId,
|
||||
/* backgroundStateUpdate= */ previousId ->
|
||||
audioSessionId != AUDIO_SESSION_ID_UNSET
|
||||
? audioSessionId
|
||||
: Util.generateAudioSessionIdV21(applicationContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAudioSessionId() {
|
||||
verifyApplicationThread();
|
||||
return audioSessionId;
|
||||
return audioSessionIdState.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2934,6 +2942,14 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
}
|
||||
}
|
||||
|
||||
private void onAudioSessionIdChanged(int oldAudioSessionId, int newAudioSessionId) {
|
||||
verifyApplicationThread();
|
||||
sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, newAudioSessionId);
|
||||
sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, newAudioSessionId);
|
||||
listeners.sendEvent(
|
||||
EVENT_AUDIO_SESSION_ID, listener -> listener.onAudioSessionIdChanged(newAudioSessionId));
|
||||
}
|
||||
|
||||
private static DeviceInfo createDeviceInfo(@Nullable StreamVolumeManager streamVolumeManager) {
|
||||
return new DeviceInfo.Builder(DeviceInfo.PLAYBACK_TYPE_LOCAL)
|
||||
.setMinVolume(streamVolumeManager != null ? streamVolumeManager.getMinVolume() : 0)
|
||||
|
@ -7276,6 +7276,7 @@ public class ExoPlayerTest {
|
||||
.build();
|
||||
player.setRepeatMode(Player.REPEAT_MODE_ONE);
|
||||
player.setMediaSource(mediaSource);
|
||||
advance(player).untilPendingCommandsAreFullyHandled();
|
||||
|
||||
player.prepare();
|
||||
advance(player).untilPendingCommandsAreFullyHandled();
|
||||
@ -12440,6 +12441,7 @@ public class ExoPlayerTest {
|
||||
@Test
|
||||
public void onEvents_correspondToListenerCalls() throws Exception {
|
||||
ExoPlayer player = parameterizeTestExoPlayerBuilder(new TestExoPlayerBuilder(context)).build();
|
||||
advance(player).untilPendingCommandsAreFullyHandled();
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
player.addListener(listener);
|
||||
Format formatWithStaticMetadata =
|
||||
@ -16302,6 +16304,58 @@ public class ExoPlayerTest {
|
||||
assertThat(reportedSpeedChanges).containsExactly(2f, 1.5f, 1f).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builderBuild_createsInitialAudioSessionId() throws Exception {
|
||||
ExoPlayer player = new ExoPlayer.Builder(context).build();
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
player.addListener(listener);
|
||||
|
||||
int audioSessionIdAfterBuild = player.getAudioSessionId();
|
||||
advance(player).untilPendingCommandsAreFullyHandled();
|
||||
int audioSessionIdAfterInit = player.getAudioSessionId();
|
||||
player.release();
|
||||
|
||||
assertThat(audioSessionIdAfterBuild).isEqualTo(C.AUDIO_SESSION_ID_UNSET);
|
||||
assertThat(audioSessionIdAfterInit).isNotEqualTo(C.AUDIO_SESSION_ID_UNSET);
|
||||
verify(listener).onAudioSessionIdChanged(audioSessionIdAfterInit);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAudioSessionId_withDefinedId_updatesGetterAndListener() throws Exception {
|
||||
ExoPlayer player = new ExoPlayer.Builder(context).build();
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
player.addListener(listener);
|
||||
|
||||
player.setAudioSessionId(1234);
|
||||
int audioSessionId = player.getAudioSessionId();
|
||||
// Verify there are no further or duplicated updates.
|
||||
advance(player).untilPendingCommandsAreFullyHandled();
|
||||
int audioSessionIdAfterIdle = player.getAudioSessionId();
|
||||
player.release();
|
||||
|
||||
assertThat(audioSessionId).isEqualTo(1234);
|
||||
assertThat(audioSessionIdAfterIdle).isEqualTo(1234);
|
||||
verify(listener).onAudioSessionIdChanged(anyInt());
|
||||
verify(listener).onAudioSessionIdChanged(1234);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAudioSessionId_withUndefinedId_updatesGetterAndListener() throws Exception {
|
||||
ExoPlayer player = new ExoPlayer.Builder(context).build();
|
||||
advance(player).untilPendingCommandsAreFullyHandled();
|
||||
Player.Listener listener = mock(Player.Listener.class);
|
||||
player.addListener(listener);
|
||||
|
||||
int initialAudioSessionId = player.getAudioSessionId();
|
||||
player.setAudioSessionId(C.AUDIO_SESSION_ID_UNSET);
|
||||
advance(player).untilPendingCommandsAreFullyHandled();
|
||||
int audioSessionId = player.getAudioSessionId();
|
||||
player.release();
|
||||
|
||||
assertThat(audioSessionId).isNotEqualTo(initialAudioSessionId);
|
||||
verify(listener).onAudioSessionIdChanged(audioSessionId);
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void addWatchAsSystemFeature() {
|
||||
|
@ -135,6 +135,7 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.Test;
|
||||
@ -2092,7 +2093,7 @@ public final class DefaultAnalyticsCollectorTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static ExoPlayer setupPlayer() {
|
||||
private static ExoPlayer setupPlayer() throws TimeoutException {
|
||||
Clock clock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
return setupPlayer(
|
||||
/* renderersFactory= */ (eventHandler,
|
||||
@ -2110,11 +2111,12 @@ public final class DefaultAnalyticsCollectorTest {
|
||||
clock);
|
||||
}
|
||||
|
||||
private static ExoPlayer setupPlayer(RenderersFactory renderersFactory) {
|
||||
private static ExoPlayer setupPlayer(RenderersFactory renderersFactory) throws TimeoutException {
|
||||
return setupPlayer(renderersFactory, new FakeClock(/* isAutoAdvancing= */ true));
|
||||
}
|
||||
|
||||
private static ExoPlayer setupPlayer(RenderersFactory renderersFactory, Clock clock) {
|
||||
private static ExoPlayer setupPlayer(RenderersFactory renderersFactory, Clock clock)
|
||||
throws TimeoutException {
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||
ExoPlayer player =
|
||||
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext())
|
||||
@ -2122,6 +2124,7 @@ public final class DefaultAnalyticsCollectorTest {
|
||||
.setRenderersFactory(renderersFactory)
|
||||
.build();
|
||||
player.setVideoSurface(surface);
|
||||
advance(player).untilPendingCommandsAreFullyHandled();
|
||||
return player;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user