diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 96fb3aa349..39184f0d7f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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: diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 0a16ed745f..118824eb65 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -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. diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index e1ec207519..9a3384823c 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -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);