Fix audio focus handling in ExoPlayerImpl

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: androidx/media#1436
#cherrypick
PiperOrigin-RevId: 644971218
This commit is contained in:
tonihei 2024-06-20 03:59:03 -07:00 committed by Copybara-Service
parent 1d26d1891e
commit e84bb0d21c
3 changed files with 37 additions and 14 deletions

View File

@ -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:

View File

@ -550,8 +550,7 @@ import java.util.concurrent.TimeoutException;
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;
}
@ -831,8 +830,7 @@ import java.util.concurrent.TimeoutException;
verifyApplicationThread();
@AudioFocusManager.PlayerCommand
int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState());
updatePlayWhenReady(
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
updatePlayWhenReady(playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playerCommand));
}
@Override
@ -1500,8 +1498,7 @@ import java.util.concurrent.TimeoutException;
boolean playWhenReady = getPlayWhenReady();
@AudioFocusManager.PlayerCommand
int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState());
updatePlayWhenReady(
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
updatePlayWhenReady(playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playerCommand));
listeners.flushEvents();
}
@ -2836,7 +2833,7 @@ import java.util.concurrent.TimeoutException;
@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) {
@ -3005,8 +3002,8 @@ import java.util.concurrent.TimeoutException;
.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;
}
@ -3291,7 +3288,7 @@ import java.util.concurrent.TimeoutException;
public void executePlayerCommand(@AudioFocusManager.PlayerCommand int playerCommand) {
boolean playWhenReady = getPlayWhenReady();
updatePlayWhenReady(
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand));
playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playerCommand));
}
// AudioBecomingNoisyManager.EventListener implementation.

View File

@ -4504,10 +4504,12 @@ public class ExoPlayerTest {
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)
@ -4607,7 +4609,10 @@ public class ExoPlayerTest {
.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
@ -4629,17 +4634,22 @@ public class ExoPlayerTest {
.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)
@ -4649,6 +4659,13 @@ public class ExoPlayerTest {
.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);
@ -4679,7 +4696,6 @@ public class ExoPlayerTest {
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)
@ -4689,10 +4705,17 @@ public class ExoPlayerTest {
.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);