Skip to content

Commit

Permalink
Fix audio focus handling in ExoPlayerImpl
Browse files Browse the repository at this point in the history
Some cases are not handled correctly at the moment:
 - Pausing during suppressed playback should not clear the
   suppression state.
 - Transient focus loss while paused should be reported as
   a playback suppression.

Issue: #1436
#cherrypick
PiperOrigin-RevId: 644971218
(cherry picked from commit e84bb0d)
  • Loading branch information
tonihei authored and tianyif committed Jul 2, 2024
1 parent 05571b3 commit c14110c
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 14 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

* Common Library:
* ExoPlayer:
* Fix some audio focus inconsistencies, e.g. not reporting full or
transient focus loss while the player is paused
([#1436](https://github.com/androidx/media/issues/1436)).
* Transformer:
* Track Selection:
* Extractors:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,8 +550,7 @@ public void prepare() {
boolean playWhenReady = getPlayWhenReady();
@AudioFocusManager.PlayerCommand
int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, Player.STATE_BUFFERING);
updatePlayWhenReady(
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
updatePlayWhenReady(playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playerCommand));
if (playbackInfo.playbackState != Player.STATE_IDLE) {
return;
}
Expand Down Expand Up @@ -831,8 +830,7 @@ public void setPlayWhenReady(boolean playWhenReady) {
verifyApplicationThread();
@AudioFocusManager.PlayerCommand
int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState());
updatePlayWhenReady(
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
updatePlayWhenReady(playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playerCommand));
}

@Override
Expand Down Expand Up @@ -1500,8 +1498,7 @@ public void setAudioAttributes(AudioAttributes newAudioAttributes, boolean handl
boolean playWhenReady = getPlayWhenReady();
@AudioFocusManager.PlayerCommand
int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState());
updatePlayWhenReady(
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
updatePlayWhenReady(playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playerCommand));
listeners.flushEvents();
}

Expand Down Expand Up @@ -2836,7 +2833,7 @@ private void updatePlaybackInfoForPlayWhenReadyAndSuppressionReasonStates(
@PlaybackSuppressionReason
private int computePlaybackSuppressionReason(
boolean playWhenReady, @AudioFocusManager.PlayerCommand int playerCommand) {
if (playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY) {
if (playerCommand == AudioFocusManager.PLAYER_COMMAND_WAIT_FOR_CALLBACK) {
return Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS;
}
if (suppressPlaybackOnUnsuitableOutput) {
Expand Down Expand Up @@ -3005,8 +3002,8 @@ private static DeviceInfo createDeviceInfo(@Nullable StreamVolumeManager streamV
.build();
}

private static int getPlayWhenReadyChangeReason(boolean playWhenReady, int playerCommand) {
return playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY
private static int getPlayWhenReadyChangeReason(int playerCommand) {
return playerCommand == AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY
? PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS
: PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST;
}
Expand Down Expand Up @@ -3291,7 +3288,7 @@ public void setVolumeMultiplier(float volumeMultiplier) {
public void executePlayerCommand(@AudioFocusManager.PlayerCommand int playerCommand) {
boolean playWhenReady = getPlayWhenReady();
updatePlayWhenReady(
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playerCommand));
}

// AudioBecomingNoisyManager.EventListener implementation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4504,10 +4504,12 @@ public void audioFocus_pauseDuringTransientLossWhilePlaying_keepsPlaybackPausedA
run(player).untilPendingCommandsAreFullyHandled();
player.pause();
boolean playWhenReady = player.getPlayWhenReady();
@Player.PlaybackSuppressionReason int suppressionReason = player.getPlaybackSuppressionReason();
player.release();

assertThat(playWhenReady).isFalse();
// TODO: Fix behavior and assert that suppression reason if transient audio focus loss.
assertThat(suppressionReason)
.isEqualTo(Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS);
InOrder inOrder = inOrder(listener);
inOrder
.verify(listener)
Expand Down Expand Up @@ -4607,7 +4609,10 @@ public void audioFocus_lossWhilePaused_rereportsPausedWithFocusLoss() throws Exc
.verify(listener)
.onPlayWhenReadyChanged(
/* playWhenReady= */ false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
// TODO: Fix behavior and assert that audio focus loss is reported via onPlayWhenReadyChanged.
inOrder
.verify(listener)
.onPlayWhenReadyChanged(
/* playWhenReady= */ false, Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS);
}

@Test
Expand All @@ -4629,17 +4634,22 @@ public void audioFocus_transientLossAndGainWhilePaused_suppressesPlayback() thro
.onAudioFocusChange(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
run(player).untilPendingCommandsAreFullyHandled();
boolean playWhenReady = player.getPlayWhenReady();
@Player.PlaybackSuppressionReason int suppressionReason = player.getPlaybackSuppressionReason();
shadowOf(audioManager)
.getLastAudioFocusRequest()
.listener
.onAudioFocusChange(AudioManager.AUDIOFOCUS_GAIN);
run(player).untilPendingCommandsAreFullyHandled();
boolean playWhenReadyAfterGain = player.getPlayWhenReady();
@Player.PlaybackSuppressionReason
int suppressionReasonAfterGain = player.getPlaybackSuppressionReason();
player.release();

assertThat(playWhenReady).isFalse();
assertThat(playWhenReadyAfterGain).isFalse();
// TODO: Fix behavior and assert that suppression reason is transient audio focus loss.
assertThat(suppressionReason)
.isEqualTo(Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS);
assertThat(suppressionReasonAfterGain).isEqualTo(Player.PLAYBACK_SUPPRESSION_REASON_NONE);
InOrder inOrder = inOrder(listener);
inOrder
.verify(listener)
Expand All @@ -4649,6 +4659,13 @@ public void audioFocus_transientLossAndGainWhilePaused_suppressesPlayback() thro
.verify(listener)
.onPlayWhenReadyChanged(
/* playWhenReady= */ false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
inOrder
.verify(listener)
.onPlaybackSuppressionReasonChanged(
Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS);
inOrder
.verify(listener)
.onPlaybackSuppressionReasonChanged(Player.PLAYBACK_SUPPRESSION_REASON_NONE);
verify(listener, never())
.onPlayWhenReadyChanged(
/* playWhenReady= */ false, Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS);
Expand Down Expand Up @@ -4679,7 +4696,6 @@ public void audioFocus_playDuringTransientLossWhilePaused_continuesPlayback() th

assertThat(playWhenReady).isTrue();
assertThat(suppressionReason).isEqualTo(Player.PLAYBACK_SUPPRESSION_REASON_NONE);
// TODO: Fix behavior and assert that suppression reason is transient audio focus loss.
InOrder inOrder = inOrder(listener);
inOrder
.verify(listener)
Expand All @@ -4689,10 +4705,17 @@ public void audioFocus_playDuringTransientLossWhilePaused_continuesPlayback() th
.verify(listener)
.onPlayWhenReadyChanged(
/* playWhenReady= */ false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
inOrder
.verify(listener)
.onPlaybackSuppressionReasonChanged(
Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS);
inOrder
.verify(listener)
.onPlayWhenReadyChanged(
/* playWhenReady= */ true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
inOrder
.verify(listener)
.onPlaybackSuppressionReasonChanged(Player.PLAYBACK_SUPPRESSION_REASON_NONE);
verify(listener, never())
.onPlayWhenReadyChanged(
/* playWhenReady= */ false, Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS);
Expand Down

0 comments on commit c14110c

Please sign in to comment.