Exclude tracks if COMMAND_GET_TRACKS is not available

Issue: androidx/media#102
#minor-release
PiperOrigin-RevId: 461891031
This commit is contained in:
bachinger 2022-07-19 15:39:27 +00:00 committed by Rohit Singh
parent 5adf708b43
commit 5bf9e2fb31
9 changed files with 160 additions and 14 deletions

View File

@ -121,7 +121,9 @@ import java.lang.annotation.Target;
|| !playerCommandsFromSession.contains(Player.COMMAND_GET_MEDIA_ITEMS_METADATA),
/* excludeCues= */ !playerCommandsFromPlayer.contains(Player.COMMAND_GET_TEXT)
|| !playerCommandsFromSession.contains(Player.COMMAND_GET_TEXT),
/* excludeTimeline= */ false));
/* excludeTimeline= */ false,
/* excludeTracks= */ !playerCommandsFromPlayer.contains(Player.COMMAND_GET_TRACKS)
|| !playerCommandsFromSession.contains(Player.COMMAND_GET_TRACKS)));
return bundle;
}

View File

@ -2395,9 +2395,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
listener.onMediaItemTransition(
currentMediaItem, playerInfo.mediaItemTransitionReason));
}
if (!Util.areEqual(oldPlayerInfo.currentTracks, newPlayerInfo.currentTracks)) {
if (!Util.areEqual(oldPlayerInfo.currentTracks, playerInfo.currentTracks)) {
listeners.queueEvent(
EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(newPlayerInfo.currentTracks));
EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(playerInfo.currentTracks));
}
if (!Util.areEqual(oldPlayerInfo.playbackParameters, playerInfo.playbackParameters)) {
listeners.queueEvent(

View File

@ -1070,7 +1070,8 @@ public class MediaSession {
boolean excludeMediaItems,
boolean excludeMediaItemsMetadata,
boolean excludeCues,
boolean excludeTimeline)
boolean excludeTimeline,
boolean excludeTracks)
throws RemoteException {}
default void onPeriodicSessionPositionInfoChanged(

View File

@ -411,7 +411,10 @@ import org.checkerframework.checker.initialization.qual.Initialized;
/* excludeCues= */ !sessionStub
.getConnectedControllersManager()
.isPlayerCommandAvailable(controller, Player.COMMAND_GET_TEXT),
excludeTimeline);
excludeTimeline,
/* excludeTracks= */ !sessionStub
.getConnectedControllersManager()
.isPlayerCommandAvailable(controller, Player.COMMAND_GET_TRACKS));
} catch (DeadObjectException e) {
onDeadObjectException(controller);
} catch (RemoteException e) {

View File

@ -1710,12 +1710,17 @@ import java.util.concurrent.ExecutionException;
boolean excludeMediaItems,
boolean excludeMediaItemsMetadata,
boolean excludeCues,
boolean excludeTimeline)
boolean excludeTimeline,
boolean excludeTracks)
throws RemoteException {
iController.onPlayerInfoChanged(
seq,
playerInfo.toBundle(
excludeMediaItems, excludeMediaItemsMetadata, excludeCues, excludeTimeline),
excludeMediaItems,
excludeMediaItemsMetadata,
excludeCues,
excludeTimeline,
excludeTracks),
/* isTimelineExcluded= */ excludeTimeline);
}

View File

@ -754,7 +754,8 @@ import java.lang.annotation.Target;
boolean excludeMediaItems,
boolean excludeMediaItemsMetadata,
boolean excludeCues,
boolean excludeTimeline) {
boolean excludeTimeline,
boolean excludeTracks) {
Bundle bundle = new Bundle();
if (playerError != null) {
bundle.putBundle(keyForField(FIELD_PLAYBACK_ERROR), playerError.toBundle());
@ -794,7 +795,9 @@ import java.lang.annotation.Target;
bundle.putLong(keyForField(FIELD_SEEK_FORWARD_INCREMENT_MS), seekForwardIncrementMs);
bundle.putLong(
keyForField(FIELD_MAX_SEEK_TO_PREVIOUS_POSITION_MS), maxSeekToPreviousPositionMs);
if (!excludeTracks) {
bundle.putBundle(keyForField(FIELD_CURRENT_TRACKS), currentTracks.toBundle());
}
bundle.putBundle(
keyForField(FIELD_TRACK_SELECTION_PARAMETERS), trackSelectionParameters.toBundle());
@ -807,7 +810,8 @@ import java.lang.annotation.Target;
/* excludeMediaItems= */ false,
/* excludeMediaItemsMetadata= */ false,
/* excludeCues= */ false,
/* excludeTimeline= */ false);
/* excludeTimeline= */ false,
/* excludeTracks= */ false);
}
/** Object that can restore {@link PlayerInfo} from a {@link Bundle}. */

View File

@ -26,10 +26,12 @@ public class MediaSessionConstants {
public static final String TEST_WITH_CUSTOM_COMMANDS = "testWithCustomCommands";
public static final String TEST_CONTROLLER_LISTENER_SESSION_REJECTS = "connection_sessionRejects";
public static final String TEST_IS_SESSION_COMMAND_AVAILABLE = "testIsSessionCommandAvailable";
public static final String TEST_COMMAND_GET_TRACKS = "testCommandGetTracksUnavailable";
// Bundle keys
public static final String KEY_AVAILABLE_SESSION_COMMANDS = "availableSessionCommands";
public static final String KEY_CONTROLLER = "controllerKey";
public static final String KEY_COMMAND_GET_TASKS_UNAVAILABLE = "commandGetTasksUnavailable";
private MediaSessionConstants() {}
}

View File

@ -29,7 +29,9 @@ import static androidx.media3.session.SessionResult.RESULT_SUCCESS;
import static androidx.media3.test.session.common.CommonConstants.DEFAULT_TEST_NAME;
import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA3_LIBRARY_SERVICE;
import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA3_SESSION_SERVICE;
import static androidx.media3.test.session.common.MediaSessionConstants.KEY_COMMAND_GET_TASKS_UNAVAILABLE;
import static androidx.media3.test.session.common.MediaSessionConstants.KEY_CONTROLLER;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_COMMAND_GET_TRACKS;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_CONTROLLER_LISTENER_SESSION_REJECTS;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_WITH_CUSTOM_COMMANDS;
import static androidx.media3.test.session.common.TestUtils.LONG_TIMEOUT_MS;
@ -878,6 +880,101 @@ public class MediaControllerListenerTest {
assertThat(changedCurrentTracksFromGetterRef.get()).isEqualTo(currentTracks);
}
@Test
public void getCurrentTracks_commandGetTracksUnavailable_currentTracksEmpty() throws Exception {
RemoteMediaSession remoteSession = createRemoteMediaSession(TEST_COMMAND_GET_TRACKS);
RemoteMediaSession.RemoteMockPlayer player = remoteSession.getMockPlayer();
CountDownLatch latch = new CountDownLatch(2);
// A controller with the COMMAND_GET_TRACKS unavailable.
Bundle connectionHints = new Bundle();
connectionHints.putBoolean(KEY_COMMAND_GET_TASKS_UNAVAILABLE, true);
MediaController controller =
controllerTestRule.createController(
remoteSession.getToken(), connectionHints, /* listener= */ null);
List<Tracks> capturedCurrentTracks = new ArrayList<>();
Player.Listener listener =
new Player.Listener() {
@Override
public void onEvents(Player player, Player.Events events) {
capturedCurrentTracks.add(controller.getCurrentTracks());
latch.countDown();
}
};
// A controller with the COMMAND_GET_TRACKS available.
MediaController controllerWithCommandAvailable =
controllerTestRule.createController(remoteSession.getToken());
AtomicReference<Tracks> capturedCurrentTracksWithCommandAvailable = new AtomicReference<>();
Player.Listener listenerWithCommandAvailable =
new Player.Listener() {
@Override
public void onEvents(Player player, Player.Events events) {
capturedCurrentTracksWithCommandAvailable.set(player.getCurrentTracks());
latch.countDown();
}
};
AtomicReference<Tracks> initialCurrentTracks = new AtomicReference<>();
AtomicReference<Tracks> initialCurrentTracksWithCommandAvailable = new AtomicReference<>();
threadTestRule
.getHandler()
.postAndSync(
() -> {
initialCurrentTracks.set(controller.getCurrentTracks());
initialCurrentTracksWithCommandAvailable.set(
controllerWithCommandAvailable.getCurrentTracks());
controller.addListener(listener);
controllerWithCommandAvailable.addListener(listenerWithCommandAvailable);
});
player.notifyIsLoadingChanged(true);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(initialCurrentTracks.get()).isEqualTo(Tracks.EMPTY);
assertThat(capturedCurrentTracks).containsExactly(Tracks.EMPTY);
assertThat(initialCurrentTracksWithCommandAvailable.get().getGroups()).hasSize(1);
assertThat(capturedCurrentTracksWithCommandAvailable.get().getGroups()).hasSize(1);
}
@Test
public void getCurrentTracks_commandGetTracksBecomesUnavailable_tracksResetToEmpty()
throws Exception {
RemoteMediaSession remoteSession = createRemoteMediaSession(TEST_COMMAND_GET_TRACKS);
RemoteMediaSession.RemoteMockPlayer player = remoteSession.getMockPlayer();
CountDownLatch latch = new CountDownLatch(2);
// A controller with the COMMAND_GET_TRACKS available.
MediaController controller = controllerTestRule.createController(remoteSession.getToken());
List<Tracks> capturedCurrentTracks = new ArrayList<>();
Player.Listener listener =
new Player.Listener() {
@Override
public void onAvailableCommandsChanged(Commands availableCommands) {
capturedCurrentTracks.add(controller.getCurrentTracks());
latch.countDown();
}
@Override
public void onTracksChanged(Tracks tracks) {
// The track change as a result of the available command change is notified second.
capturedCurrentTracks.add(controller.getCurrentTracks());
latch.countDown();
}
};
AtomicReference<Commands> availableCommands = new AtomicReference<>();
threadTestRule
.getHandler()
.postAndSync(
() -> {
availableCommands.set(controller.getAvailableCommands());
controller.addListener(listener);
});
player.notifyAvailableCommandsChanged(
availableCommands.get().buildUpon().remove(Player.COMMAND_GET_TRACKS).build());
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(capturedCurrentTracks.get(0).getGroups()).hasSize(1);
assertThat(capturedCurrentTracks.get(1)).isEqualTo(Tracks.EMPTY);
}
/** This also tests {@link MediaController#getShuffleModeEnabled()}. */
@Test
public void onShuffleModeEnabledChanged() throws Exception {

View File

@ -15,6 +15,7 @@
*/
package androidx.media3.session;
import static androidx.media3.common.Player.COMMAND_GET_TRACKS;
import static androidx.media3.test.session.common.CommonConstants.ACTION_MEDIA3_SESSION;
import static androidx.media3.test.session.common.CommonConstants.KEY_AUDIO_ATTRIBUTES;
import static androidx.media3.test.session.common.CommonConstants.KEY_BUFFERED_PERCENTAGE;
@ -55,7 +56,9 @@ import static androidx.media3.test.session.common.CommonConstants.KEY_TRACK_SELE
import static androidx.media3.test.session.common.CommonConstants.KEY_VIDEO_SIZE;
import static androidx.media3.test.session.common.CommonConstants.KEY_VOLUME;
import static androidx.media3.test.session.common.MediaSessionConstants.KEY_AVAILABLE_SESSION_COMMANDS;
import static androidx.media3.test.session.common.MediaSessionConstants.KEY_COMMAND_GET_TASKS_UNAVAILABLE;
import static androidx.media3.test.session.common.MediaSessionConstants.KEY_CONTROLLER;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_COMMAND_GET_TRACKS;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_CONTROLLER_LISTENER_SESSION_REJECTS;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_SESSION_ACTIVITY;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_IS_SESSION_COMMAND_AVAILABLE;
@ -71,6 +74,7 @@ import androidx.annotation.Nullable;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.DeviceInfo;
import androidx.media3.common.Format;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.PlaybackException;
@ -79,6 +83,7 @@ import androidx.media3.common.Player;
import androidx.media3.common.Player.DiscontinuityReason;
import androidx.media3.common.Player.PositionInfo;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.Tracks;
import androidx.media3.common.VideoSize;
@ -159,11 +164,10 @@ public class MediaSessionProviderService extends Service {
@Override
public void create(String sessionId, Bundle tokenExtras) throws RemoteException {
MockPlayer mockPlayer =
new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
MediaSession.Builder builder =
new MediaSession.Builder(
MediaSessionProviderService.this,
new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build())
.setId(sessionId);
new MediaSession.Builder(MediaSessionProviderService.this, mockPlayer).setId(sessionId);
if (tokenExtras != null) {
builder.setExtras(tokenExtras);
@ -229,6 +233,34 @@ public class MediaSessionProviderService extends Service {
});
break;
}
case TEST_COMMAND_GET_TRACKS:
{
ImmutableList<Tracks.Group> trackGroups =
ImmutableList.of(
new Tracks.Group(
new TrackGroup(new Format.Builder().setChannelCount(2).build()),
/* adaptiveSupported= */ false,
/* trackSupport= */ new int[1],
/* trackSelected= */ new boolean[1]));
mockPlayer.currentTracks = new Tracks(trackGroups);
builder.setCallback(
new MediaSession.Callback() {
@Override
public MediaSession.ConnectionResult onConnect(
MediaSession session, ControllerInfo controller) {
Player.Commands.Builder commandBuilder =
new Player.Commands.Builder().addAllCommands();
if (controller
.getConnectionHints()
.getBoolean(KEY_COMMAND_GET_TASKS_UNAVAILABLE, /* defaultValue= */ false)) {
commandBuilder.remove(COMMAND_GET_TRACKS);
}
return MediaSession.ConnectionResult.accept(
SessionCommands.EMPTY, commandBuilder.build());
}
});
break;
}
default: // fall out
}