From 3f4f2f90b5d8dcc7dff20f6422243b4fb072a51f Mon Sep 17 00:00:00 2001 From: krocard Date: Fri, 12 Mar 2021 09:30:47 +0000 Subject: [PATCH] Move offload events to their own listener Move offload events from Player.EventListener to ExoPlayer.AudioOffloadListener. PiperOrigin-RevId: 362472462 --- .../com/google/android/exoplayer2/Player.java | 18 --------- .../google/android/exoplayer2/ExoPlayer.java | 40 ++++++++++++++++++- .../android/exoplayer2/ExoPlayerImpl.java | 38 ++++++++++++------ .../android/exoplayer2/SimpleExoPlayer.java | 18 ++++++++- .../robolectric/TestPlayerRunHelper.java | 22 +++++----- .../exoplayer2/testutil/StubExoPlayer.java | 10 +++++ 6 files changed, 102 insertions(+), 44 deletions(-) diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Player.java b/library/common/src/main/java/com/google/android/exoplayer2/Player.java index fc9a3c800e..4ec99cf010 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Player.java @@ -612,24 +612,6 @@ public interface Player { @Deprecated default void onSeekProcessed() {} - /** - * Called when the player has started or stopped offload scheduling. - * - *

If using ExoPlayer, this is done by calling {@code - * ExoPlayer#experimentalSetOffloadSchedulingEnabled(boolean)}. - * - *

This method is experimental, and will be renamed or removed in a future release. - */ - // TODO(b/172315872) Move this method in a new ExoPlayer.EventListener. - default void onExperimentalOffloadSchedulingEnabledChanged(boolean offloadSchedulingEnabled) {} - - /** - * Called when the player has started or finished sleeping for offload. - * - *

This method is experimental, and will be renamed or removed in a future release. - */ - default void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) {} - /** * Called when one or more player states changed. * diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 7536fd65b9..f02ba4d6ff 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -140,6 +140,28 @@ public interface ExoPlayer extends Player { */ long DEFAULT_RELEASE_TIMEOUT_MS = 500; + /** + * A listener for audio offload events. + * + *

This class is experimental, and might be renamed, moved or removed in a future release. + */ + interface AudioOffloadListener { + /** + * Called when the player has started or stopped offload scheduling using {@link + * ExoPlayer#experimentalSetOffloadSchedulingEnabled(boolean)}. + * + *

This method is experimental, and will be renamed or removed in a future release. + */ + default void onExperimentalOffloadSchedulingEnabledChanged(boolean offloadSchedulingEnabled) {} + + /** + * Called when the player has started or finished sleeping for offload. + * + *

This method is experimental, and will be renamed or removed in a future release. + */ + default void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) {} + } + /** * A builder for {@link ExoPlayer} instances. * @@ -450,6 +472,20 @@ public interface ExoPlayer extends Player { } } + /** + * Adds a listener to receive audio offload events. + * + * @param listener The listener to register. + */ + void addAudioOffloadListener(AudioOffloadListener listener); + + /** + * Removes a listener of audio offload events. + * + * @param listener The listener to unregister. + */ + void removeAudioOffloadListener(AudioOffloadListener listener); + /** Returns the number of renderers. */ int getRendererCount(); @@ -665,7 +701,7 @@ public interface ExoPlayer extends Player { *

While offload scheduling is enabled, player events may be delivered severely delayed and * apps should not interact with the player. When returning to the foreground, disable offload * scheduling and wait for {@link - * Player.EventListener#onExperimentalOffloadSchedulingEnabledChanged(boolean)} to be called with + * AudioOffloadListener#onExperimentalOffloadSchedulingEnabledChanged(boolean)} to be called with * {@code offloadSchedulingEnabled = false} before interacting with the player. * *

This mode should save significant power when the phone is playing offload audio with the @@ -697,7 +733,7 @@ public interface ExoPlayer extends Player { * Returns whether the player has paused its main loop to save power in offload scheduling mode. * * @see #experimentalSetOffloadSchedulingEnabled(boolean) - * @see EventListener#onExperimentalSleepingForOffloadChanged(boolean) + * @see AudioOffloadListener#onExperimentalSleepingForOffloadChanged(boolean) */ boolean experimentalIsSleepingForOffload(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index d823975595..32b341b437 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -49,6 +49,7 @@ import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CopyOnWriteArraySet; /** * An {@link ExoPlayer} implementation. Instances can be obtained from {@link ExoPlayer.Builder}. @@ -72,6 +73,7 @@ import java.util.List; private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener; private final ExoPlayerImplInternal internalPlayer; private final ListenerSet listeners; + private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; private final List mediaSourceHolderSnapshots; private final boolean useLazyPreparation; @@ -166,6 +168,7 @@ import java.util.List; applicationLooper, clock, (listener, flags) -> listener.onEvents(playerForListeners, new Events(flags))); + audioOffloadListeners = new CopyOnWriteArraySet<>(); mediaSourceHolderSnapshots = new ArrayList<>(); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); emptyTrackSelectorResult = @@ -284,6 +287,16 @@ import java.util.List; listeners.remove(listener); } + @Override + public void addAudioOffloadListener(AudioOffloadListener listener) { + audioOffloadListeners.add(listener); + } + + @Override + public void removeAudioOffloadListener(AudioOffloadListener listener) { + audioOffloadListeners.remove(listener); + } + @Override public boolean isCommandAvailable(@Command int command) { return availableCommands.contains(command); @@ -1099,21 +1112,20 @@ import java.util.List; if (seekProcessed) { listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); } - if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) { - listeners.queueEvent( - /* eventFlag= */ C.INDEX_UNSET, - listener -> - listener.onExperimentalOffloadSchedulingEnabledChanged( - newPlaybackInfo.offloadSchedulingEnabled)); - } - if (previousPlaybackInfo.sleepingForOffload != newPlaybackInfo.sleepingForOffload) { - listeners.queueEvent( - /* eventFlag= */ C.INDEX_UNSET, - listener -> - listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload)); - } updateAvailableCommands(); listeners.flushEvents(); + + if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) { + for (AudioOffloadListener listener : audioOffloadListeners) { + listener.onExperimentalOffloadSchedulingEnabledChanged( + newPlaybackInfo.offloadSchedulingEnabled); + } + } + if (previousPlaybackInfo.sleepingForOffload != newPlaybackInfo.sleepingForOffload) { + for (AudioOffloadListener listener : audioOffloadListeners) { + listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload); + } + } } private Pair evaluateMediaItemTransitionReason( diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index ea4d8f05bc..13a284ff7f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -685,6 +685,7 @@ public class SimpleExoPlayer extends BasePlayer builder.looper, /* wrappingPlayer= */ this); player.addListener(componentListener); + player.addAudioOffloadListener(componentListener); audioBecomingNoisyManager = new AudioBecomingNoisyManager(builder.context, eventHandler, componentListener); @@ -894,6 +895,18 @@ public class SimpleExoPlayer extends BasePlayer } } + @Override + public void addAudioOffloadListener(AudioOffloadListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. + player.addAudioOffloadListener(listener); + } + + @Override + public void removeAudioOffloadListener(AudioOffloadListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. + player.removeAudioOffloadListener(listener); + } + @Override public void addAudioListener(AudioListener listener) { // Don't verify application thread. We allow calls to this method from any thread. @@ -1973,7 +1986,8 @@ public class SimpleExoPlayer extends BasePlayer AudioFocusManager.PlayerControl, AudioBecomingNoisyManager.EventListener, StreamVolumeManager.Listener, - Player.EventListener { + Player.EventListener, + AudioOffloadListener { // VideoRendererEventListener implementation @@ -2241,6 +2255,8 @@ public class SimpleExoPlayer extends BasePlayer updateWakeAndWifiLock(); } + // Player.AudioOffloadListener implementation. + @Override public void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) { updateWakeAndWifiLock(); diff --git a/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java b/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java index bd203de5c6..1eaf1ee36a 100644 --- a/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java +++ b/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java @@ -210,46 +210,48 @@ public class TestPlayerRunHelper { /** * Runs tasks of the main {@link Looper} until a {@link - * Player.EventListener#onExperimentalOffloadSchedulingEnabledChanged} callback occurred. + * ExoPlayer.AudioOffloadListener#onExperimentalOffloadSchedulingEnabledChanged} callback + * occurred. * * @param player The {@link Player}. * @return The new offloadSchedulingEnabled state. * @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is * exceeded. */ - public static boolean runUntilReceiveOffloadSchedulingEnabledNewState(Player player) + public static boolean runUntilReceiveOffloadSchedulingEnabledNewState(ExoPlayer player) throws TimeoutException { verifyMainTestThread(player); AtomicReference<@NullableType Boolean> offloadSchedulingEnabledReceiver = new AtomicReference<>(); - Player.EventListener listener = - new Player.EventListener() { + ExoPlayer.AudioOffloadListener listener = + new ExoPlayer.AudioOffloadListener() { @Override public void onExperimentalOffloadSchedulingEnabledChanged( boolean offloadSchedulingEnabled) { offloadSchedulingEnabledReceiver.set(offloadSchedulingEnabled); } }; - player.addListener(listener); + player.addAudioOffloadListener(listener); runMainLooperUntil(() -> offloadSchedulingEnabledReceiver.get() != null); return Assertions.checkNotNull(offloadSchedulingEnabledReceiver.get()); } /** * Runs tasks of the main {@link Looper} until a {@link - * Player.EventListener#onExperimentalSleepingForOffloadChanged(boolean)} callback occurred. + * ExoPlayer.AudioOffloadListener#onExperimentalSleepingForOffloadChanged(boolean)} callback + * occurred. * * @param player The {@link Player}. * @param expectedSleepForOffload The expected sleep of offload state. * @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is * exceeded. */ - public static void runUntilSleepingForOffload(Player player, boolean expectedSleepForOffload) + public static void runUntilSleepingForOffload(ExoPlayer player, boolean expectedSleepForOffload) throws TimeoutException { verifyMainTestThread(player); AtomicBoolean receiverCallback = new AtomicBoolean(false); - Player.EventListener listener = - new Player.EventListener() { + ExoPlayer.AudioOffloadListener listener = + new ExoPlayer.AudioOffloadListener() { @Override public void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) { if (sleepingForOffload == expectedSleepForOffload) { @@ -257,7 +259,7 @@ public class TestPlayerRunHelper { } } }; - player.addListener(listener); + player.addAudioOffloadListener(listener); runMainLooperUntil( () -> { // Make sure progress is being made, see [internal: b/170387438#comment2] assertThat(player.getPlayerError()).isNull(); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index af68d4f8e1..d99a28f325 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -91,6 +91,16 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } + @Override + public void addAudioOffloadListener(AudioOffloadListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeAudioOffloadListener(AudioOffloadListener listener) { + throw new UnsupportedOperationException(); + } + @Override @State public int getPlaybackState() {