mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Move setAudioAttributes from ExoPlayer to Player
PiperOrigin-RevId: 566607528
This commit is contained in:
parent
f35e7bdc6a
commit
39c1ac7f3a
@ -23,6 +23,7 @@
|
|||||||
([#11226](https://github.com/google/ExoPlayer/issues/11226)).
|
([#11226](https://github.com/google/ExoPlayer/issues/11226)).
|
||||||
* Change `BaseRenderer.onStreamChanged()` to also receive a
|
* Change `BaseRenderer.onStreamChanged()` to also receive a
|
||||||
`MediaPeriodId` argument.
|
`MediaPeriodId` argument.
|
||||||
|
* Move `ExoPlayer.setAudioAttributes` to the `Player` interface.
|
||||||
* Transformer:
|
* Transformer:
|
||||||
* Changed `frameRate` and `durationUs` parameters of
|
* Changed `frameRate` and `durationUs` parameters of
|
||||||
`SampleConsumer.queueInputBitmap` to `TimestampIterator`.
|
`SampleConsumer.queueInputBitmap` to `TimestampIterator`.
|
||||||
|
6
api.txt
6
api.txt
@ -759,6 +759,7 @@ package androidx.media3.common {
|
|||||||
method public void seekToNextMediaItem();
|
method public void seekToNextMediaItem();
|
||||||
method public void seekToPrevious();
|
method public void seekToPrevious();
|
||||||
method public void seekToPreviousMediaItem();
|
method public void seekToPreviousMediaItem();
|
||||||
|
method public void setAudioAttributes(androidx.media3.common.AudioAttributes, boolean);
|
||||||
method @Deprecated public void setDeviceMuted(boolean);
|
method @Deprecated public void setDeviceMuted(boolean);
|
||||||
method public void setDeviceMuted(boolean, @androidx.media3.common.C.VolumeFlags int);
|
method public void setDeviceMuted(boolean, @androidx.media3.common.C.VolumeFlags int);
|
||||||
method @Deprecated public void setDeviceVolume(@IntRange(from=0) int);
|
method @Deprecated public void setDeviceVolume(@IntRange(from=0) int);
|
||||||
@ -807,6 +808,7 @@ package androidx.media3.common {
|
|||||||
field public static final int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 8; // 0x8
|
field public static final int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 8; // 0x8
|
||||||
field public static final int COMMAND_SEEK_TO_PREVIOUS = 7; // 0x7
|
field public static final int COMMAND_SEEK_TO_PREVIOUS = 7; // 0x7
|
||||||
field public static final int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 6; // 0x6
|
field public static final int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 6; // 0x6
|
||||||
|
field public static final int COMMAND_SET_AUDIO_ATTRIBUTES = 35; // 0x23
|
||||||
field @Deprecated public static final int COMMAND_SET_DEVICE_VOLUME = 25; // 0x19
|
field @Deprecated public static final int COMMAND_SET_DEVICE_VOLUME = 25; // 0x19
|
||||||
field public static final int COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS = 33; // 0x21
|
field public static final int COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS = 33; // 0x21
|
||||||
field public static final int COMMAND_SET_MEDIA_ITEM = 31; // 0x1f
|
field public static final int COMMAND_SET_MEDIA_ITEM = 31; // 0x1f
|
||||||
@ -881,7 +883,7 @@ package androidx.media3.common {
|
|||||||
field public static final int TIMELINE_CHANGE_REASON_SOURCE_UPDATE = 1; // 0x1
|
field public static final int TIMELINE_CHANGE_REASON_SOURCE_UPDATE = 1; // 0x1
|
||||||
}
|
}
|
||||||
|
|
||||||
@IntDef({androidx.media3.common.Player.COMMAND_INVALID, androidx.media3.common.Player.COMMAND_PLAY_PAUSE, androidx.media3.common.Player.COMMAND_PREPARE, androidx.media3.common.Player.COMMAND_STOP, androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION, androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS, androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT, androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_BACK, androidx.media3.common.Player.COMMAND_SEEK_FORWARD, androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH, androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE, androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE, androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_GET_TIMELINE, androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA, androidx.media3.common.Player.COMMAND_GET_METADATA, androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA, androidx.media3.common.Player.COMMAND_SET_PLAYLIST_METADATA, androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS, androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES, androidx.media3.common.Player.COMMAND_GET_VOLUME, androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_SET_VOLUME, androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE, androidx.media3.common.Player.COMMAND_GET_TEXT, androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS, androidx.media3.common.Player.COMMAND_GET_TRACKS, androidx.media3.common.Player.COMMAND_RELEASE}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.Command {
|
@IntDef({androidx.media3.common.Player.COMMAND_INVALID, androidx.media3.common.Player.COMMAND_PLAY_PAUSE, androidx.media3.common.Player.COMMAND_PREPARE, androidx.media3.common.Player.COMMAND_STOP, androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION, androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS, androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT, androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_BACK, androidx.media3.common.Player.COMMAND_SEEK_FORWARD, androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH, androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE, androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE, androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_GET_TIMELINE, androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA, androidx.media3.common.Player.COMMAND_GET_METADATA, androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA, androidx.media3.common.Player.COMMAND_SET_PLAYLIST_METADATA, androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS, androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES, androidx.media3.common.Player.COMMAND_GET_VOLUME, androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_SET_VOLUME, androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, androidx.media3.common.Player.COMMAND_SET_AUDIO_ATTRIBUTES, androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE, androidx.media3.common.Player.COMMAND_GET_TEXT, androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS, androidx.media3.common.Player.COMMAND_GET_TRACKS, androidx.media3.common.Player.COMMAND_RELEASE}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Player.Commands {
|
public static final class Player.Commands {
|
||||||
@ -1347,7 +1349,6 @@ package androidx.media3.exoplayer {
|
|||||||
method public void addAnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener);
|
method public void addAnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener);
|
||||||
method @Nullable public androidx.media3.exoplayer.ExoPlaybackException getPlayerError();
|
method @Nullable public androidx.media3.exoplayer.ExoPlaybackException getPlayerError();
|
||||||
method public void removeAnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener);
|
method public void removeAnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener);
|
||||||
method public void setAudioAttributes(androidx.media3.common.AudioAttributes, boolean);
|
|
||||||
method public void setHandleAudioBecomingNoisy(boolean);
|
method public void setHandleAudioBecomingNoisy(boolean);
|
||||||
method public void setWakeMode(@androidx.media3.common.C.WakeMode int);
|
method public void setWakeMode(@androidx.media3.common.C.WakeMode int);
|
||||||
}
|
}
|
||||||
@ -1618,6 +1619,7 @@ package androidx.media3.session {
|
|||||||
method public final void seekToPrevious();
|
method public final void seekToPrevious();
|
||||||
method public final void seekToPreviousMediaItem();
|
method public final void seekToPreviousMediaItem();
|
||||||
method public final com.google.common.util.concurrent.ListenableFuture<androidx.media3.session.SessionResult> sendCustomCommand(androidx.media3.session.SessionCommand, android.os.Bundle);
|
method public final com.google.common.util.concurrent.ListenableFuture<androidx.media3.session.SessionResult> sendCustomCommand(androidx.media3.session.SessionCommand, android.os.Bundle);
|
||||||
|
method public final void setAudioAttributes(androidx.media3.common.AudioAttributes, boolean);
|
||||||
method @Deprecated public final void setDeviceMuted(boolean);
|
method @Deprecated public final void setDeviceMuted(boolean);
|
||||||
method public final void setDeviceMuted(boolean, @androidx.media3.common.C.VolumeFlags int);
|
method public final void setDeviceMuted(boolean, @androidx.media3.common.C.VolumeFlags int);
|
||||||
method @Deprecated public final void setDeviceVolume(@IntRange(from=0) int);
|
method @Deprecated public final void setDeviceVolume(@IntRange(from=0) int);
|
||||||
|
@ -811,6 +811,10 @@ public final class CastPlayer extends BasePlayer {
|
|||||||
@Override
|
@Override
|
||||||
public void setDeviceMuted(boolean muted, @C.VolumeFlags int flags) {}
|
public void setDeviceMuted(boolean muted, @C.VolumeFlags int flags) {}
|
||||||
|
|
||||||
|
/** This method is not supported and does nothing. */
|
||||||
|
@Override
|
||||||
|
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {}
|
||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
// Call deprecated callbacks.
|
// Call deprecated callbacks.
|
||||||
|
@ -917,6 +917,12 @@ public class ForwardingPlayer implements Player {
|
|||||||
player.setDeviceMuted(muted, flags);
|
player.setDeviceMuted(muted, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Calls {@link Player#setAudioAttributes(AudioAttributes, boolean)} on the delegate. */
|
||||||
|
@Override
|
||||||
|
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
player.setAudioAttributes(audioAttributes, handleAudioFocus);
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the {@link Player} to which operations are forwarded. */
|
/** Returns the {@link Player} to which operations are forwarded. */
|
||||||
public Player getWrappedPlayer() {
|
public Player getWrappedPlayer() {
|
||||||
return player;
|
return player;
|
||||||
|
@ -491,6 +491,7 @@ public interface Player {
|
|||||||
COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS,
|
COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS,
|
||||||
COMMAND_ADJUST_DEVICE_VOLUME,
|
COMMAND_ADJUST_DEVICE_VOLUME,
|
||||||
COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS,
|
COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS,
|
||||||
|
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||||
COMMAND_SET_VIDEO_SURFACE,
|
COMMAND_SET_VIDEO_SURFACE,
|
||||||
COMMAND_GET_TEXT,
|
COMMAND_GET_TEXT,
|
||||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
||||||
@ -1607,6 +1608,7 @@ public interface Player {
|
|||||||
* <li>{@link #COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS}
|
* <li>{@link #COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS}
|
||||||
* <li>{@link #COMMAND_ADJUST_DEVICE_VOLUME}
|
* <li>{@link #COMMAND_ADJUST_DEVICE_VOLUME}
|
||||||
* <li>{@link #COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS}
|
* <li>{@link #COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS}
|
||||||
|
* <li>{@link #COMMAND_SET_AUDIO_ATTRIBUTES}
|
||||||
* <li>{@link #COMMAND_SET_VIDEO_SURFACE}
|
* <li>{@link #COMMAND_SET_VIDEO_SURFACE}
|
||||||
* <li>{@link #COMMAND_GET_TEXT}
|
* <li>{@link #COMMAND_GET_TEXT}
|
||||||
* <li>{@link #COMMAND_SET_TRACK_SELECTION_PARAMETERS}
|
* <li>{@link #COMMAND_SET_TRACK_SELECTION_PARAMETERS}
|
||||||
@ -1653,6 +1655,7 @@ public interface Player {
|
|||||||
COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS,
|
COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS,
|
||||||
COMMAND_ADJUST_DEVICE_VOLUME,
|
COMMAND_ADJUST_DEVICE_VOLUME,
|
||||||
COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS,
|
COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS,
|
||||||
|
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||||
COMMAND_SET_VIDEO_SURFACE,
|
COMMAND_SET_VIDEO_SURFACE,
|
||||||
COMMAND_GET_TEXT,
|
COMMAND_GET_TEXT,
|
||||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
||||||
@ -2005,6 +2008,14 @@ public interface Player {
|
|||||||
*/
|
*/
|
||||||
int COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS = 34;
|
int COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS = 34;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to set the player's audio attributes.
|
||||||
|
*
|
||||||
|
* <p>The {@link #setAudioAttributes(AudioAttributes, boolean)} method must only be called if this
|
||||||
|
* command is {@linkplain #isCommandAvailable(int) available}.
|
||||||
|
*/
|
||||||
|
int COMMAND_SET_AUDIO_ATTRIBUTES = 35;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command to set and clear the surface on which to render the video.
|
* Command to set and clear the surface on which to render the video.
|
||||||
*
|
*
|
||||||
@ -3393,4 +3404,30 @@ public interface Player {
|
|||||||
* @param flags Either 0 or a bitwise combination of one or more {@link C.VolumeFlags}.
|
* @param flags Either 0 or a bitwise combination of one or more {@link C.VolumeFlags}.
|
||||||
*/
|
*/
|
||||||
void setDeviceMuted(boolean muted, @C.VolumeFlags int flags);
|
void setDeviceMuted(boolean muted, @C.VolumeFlags int flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the attributes for audio playback, used by the underlying audio track. If not set, the
|
||||||
|
* default audio attributes will be used. They are suitable for general media playback.
|
||||||
|
*
|
||||||
|
* <p>Setting the audio attributes during playback may introduce a short gap in audio output as
|
||||||
|
* the audio track is recreated. A new audio session id will also be generated.
|
||||||
|
*
|
||||||
|
* <p>If tunneling is enabled by the track selector, the specified audio attributes will be
|
||||||
|
* ignored, but they will take effect if audio is later played without tunneling.
|
||||||
|
*
|
||||||
|
* <p>If the device is running a build before platform API version 21, audio attributes cannot be
|
||||||
|
* set directly on the underlying audio track. In this case, the usage will be mapped onto an
|
||||||
|
* equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}.
|
||||||
|
*
|
||||||
|
* <p>If audio focus should be handled, the {@link AudioAttributes#usage} must be {@link
|
||||||
|
* C#USAGE_MEDIA} or {@link C#USAGE_GAME}. Other usages will throw an {@link
|
||||||
|
* IllegalArgumentException}.
|
||||||
|
*
|
||||||
|
* <p>This method must only be called if {@link #COMMAND_SET_AUDIO_ATTRIBUTES} is {@linkplain
|
||||||
|
* #getAvailableCommands() available}.
|
||||||
|
*
|
||||||
|
* @param audioAttributes The attributes to use for audio playback.
|
||||||
|
* @param handleAudioFocus True if the player should handle audio focus, false otherwise.
|
||||||
|
*/
|
||||||
|
void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);
|
||||||
}
|
}
|
||||||
|
@ -2888,6 +2888,20 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||||||
/* placeholderStateSupplier= */ () -> state.buildUpon().setIsDeviceMuted(muted).build());
|
/* placeholderStateSupplier= */ () -> state.buildUpon().setIsDeviceMuted(muted).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
verifyApplicationThreadAndInitState();
|
||||||
|
// Use a local copy to ensure the lambda below uses the current state value.
|
||||||
|
State state = this.state;
|
||||||
|
if (!shouldHandleCommand(Player.COMMAND_SET_AUDIO_ATTRIBUTES)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateStateForPendingOperation(
|
||||||
|
/* pendingOperation= */ handleSetAudioAttributes(audioAttributes, handleAudioFocus),
|
||||||
|
/* placeholderStateSupplier= */ () ->
|
||||||
|
state.buildUpon().setAudioAttributes(audioAttributes).build());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidates the current state.
|
* Invalidates the current state.
|
||||||
*
|
*
|
||||||
@ -3175,6 +3189,23 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||||||
+ " COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS");
|
+ " COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles calls to set the audio attributes.
|
||||||
|
*
|
||||||
|
* <p>Will only be called if {@link Player#COMMAND_SET_AUDIO_ATTRIBUTES} is available.
|
||||||
|
*
|
||||||
|
* @param audioAttributes The attributes to use for audio playback.
|
||||||
|
* @param handleAudioFocus True if the player should handle audio focus, false otherwise.
|
||||||
|
* @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
|
||||||
|
* changes caused by this call.
|
||||||
|
*/
|
||||||
|
@ForOverride
|
||||||
|
protected ListenableFuture<?> handleSetAudioAttributes(
|
||||||
|
AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Missing implementation to handle COMMAND_SET_AUDIO_ATTRIBUTES");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles calls to set the video output.
|
* Handles calls to set the video output.
|
||||||
*
|
*
|
||||||
|
@ -3617,6 +3617,125 @@ public class SimpleBasePlayerTest {
|
|||||||
assertThat(callForwarded.get()).isFalse();
|
assertThat(callForwarded.get()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setAudioAttributes_immediateHandling_updatesStateAndInformsListeners() {
|
||||||
|
State state =
|
||||||
|
new State.Builder()
|
||||||
|
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
|
||||||
|
.build();
|
||||||
|
// Also change the audio attributes to ensure the updated state is used.
|
||||||
|
AudioAttributes newAudioAttributes =
|
||||||
|
new AudioAttributes.Builder()
|
||||||
|
.setContentType(C.AUDIO_CONTENT_TYPE_MOVIE)
|
||||||
|
.setUsage(C.USAGE_MEDIA)
|
||||||
|
.build();
|
||||||
|
State updatedState = state.buildUpon().setAudioAttributes(newAudioAttributes).build();
|
||||||
|
SimpleBasePlayer player =
|
||||||
|
new SimpleBasePlayer(Looper.myLooper()) {
|
||||||
|
private State playerState = state;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected State getState() {
|
||||||
|
return playerState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ListenableFuture<?> handleSetAudioAttributes(
|
||||||
|
AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
playerState = updatedState;
|
||||||
|
return Futures.immediateVoidFuture();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Listener listener = mock(Listener.class);
|
||||||
|
player.addListener(listener);
|
||||||
|
|
||||||
|
player.setAudioAttributes(newAudioAttributes, /* handleAudioFocus= */ true);
|
||||||
|
|
||||||
|
assertThat(player.getAudioAttributes()).isEqualTo(newAudioAttributes);
|
||||||
|
verify(listener).onAudioAttributesChanged(newAudioAttributes);
|
||||||
|
verifyNoMoreInteractions(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setAudioAttributes_asyncHandling_usesPlaceholderStateAndInformsListeners() {
|
||||||
|
State state =
|
||||||
|
new State.Builder()
|
||||||
|
.setAvailableCommands(new Commands.Builder().addAllCommands().build())
|
||||||
|
.build();
|
||||||
|
SettableFuture<?> future = SettableFuture.create();
|
||||||
|
AudioAttributes firstAudioAttributes =
|
||||||
|
new AudioAttributes.Builder()
|
||||||
|
.setContentType(C.AUDIO_CONTENT_TYPE_SONIFICATION)
|
||||||
|
.setUsage(C.USAGE_ALARM)
|
||||||
|
.build();
|
||||||
|
AudioAttributes lastAudioAttributes =
|
||||||
|
new AudioAttributes.Builder()
|
||||||
|
.setContentType(C.AUDIO_CONTENT_TYPE_MOVIE)
|
||||||
|
.setUsage(C.USAGE_MEDIA)
|
||||||
|
.build();
|
||||||
|
State updatedState = state.buildUpon().setAudioAttributes(lastAudioAttributes).build();
|
||||||
|
SimpleBasePlayer player =
|
||||||
|
new SimpleBasePlayer(Looper.myLooper()) {
|
||||||
|
@Override
|
||||||
|
protected State getState() {
|
||||||
|
return future.isDone() ? updatedState : state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ListenableFuture<?> handleSetAudioAttributes(
|
||||||
|
AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Listener listener = mock(Listener.class);
|
||||||
|
player.addListener(listener);
|
||||||
|
|
||||||
|
player.setAudioAttributes(firstAudioAttributes, /* handleAudioFocus= */ true);
|
||||||
|
|
||||||
|
// Verify placeholder state and listener calls.
|
||||||
|
assertThat(player.getAudioAttributes()).isEqualTo(firstAudioAttributes);
|
||||||
|
verify(listener).onAudioAttributesChanged(firstAudioAttributes);
|
||||||
|
verifyNoMoreInteractions(listener);
|
||||||
|
|
||||||
|
future.set(null);
|
||||||
|
|
||||||
|
// Verify actual state update.
|
||||||
|
assertThat(player.getAudioAttributes()).isEqualTo(lastAudioAttributes);
|
||||||
|
verify(listener).onAudioAttributesChanged(lastAudioAttributes);
|
||||||
|
verifyNoMoreInteractions(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setAudioAttributes_withoutAvailableCommand_isNotForwarded() {
|
||||||
|
State state =
|
||||||
|
new State.Builder()
|
||||||
|
.setAvailableCommands(
|
||||||
|
new Commands.Builder()
|
||||||
|
.addAllCommands()
|
||||||
|
.remove(Player.COMMAND_SET_AUDIO_ATTRIBUTES)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
AtomicBoolean callForwarded = new AtomicBoolean();
|
||||||
|
SimpleBasePlayer player =
|
||||||
|
new SimpleBasePlayer(Looper.myLooper()) {
|
||||||
|
@Override
|
||||||
|
protected State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ListenableFuture<?> handleSetAudioAttributes(
|
||||||
|
AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
callForwarded.set(true);
|
||||||
|
return Futures.immediateVoidFuture();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true);
|
||||||
|
|
||||||
|
assertThat(callForwarded.get()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setVideoSurface_immediateHandling_updatesStateAndInformsListeners() {
|
public void setVideoSurface_immediateHandling_updatesStateAndInformsListeners() {
|
||||||
State state =
|
State state =
|
||||||
|
@ -173,7 +173,7 @@ public interface ExoPlayer extends Player {
|
|||||||
interface AudioComponent {
|
interface AudioComponent {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link ExoPlayer#setAudioAttributes(AudioAttributes, boolean)} instead.
|
* @deprecated Use {@link Player#setAudioAttributes(AudioAttributes, boolean)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);
|
void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);
|
||||||
@ -1514,29 +1514,6 @@ public interface ExoPlayer extends Player {
|
|||||||
@Override
|
@Override
|
||||||
void replaceMediaItems(int fromIndex, int toIndex, List<MediaItem> mediaItems);
|
void replaceMediaItems(int fromIndex, int toIndex, List<MediaItem> mediaItems);
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the attributes for audio playback, used by the underlying audio track. If not set, the
|
|
||||||
* default audio attributes will be used. They are suitable for general media playback.
|
|
||||||
*
|
|
||||||
* <p>Setting the audio attributes during playback may introduce a short gap in audio output as
|
|
||||||
* the audio track is recreated. A new audio session id will also be generated.
|
|
||||||
*
|
|
||||||
* <p>If tunneling is enabled by the track selector, the specified audio attributes will be
|
|
||||||
* ignored, but they will take effect if audio is later played without tunneling.
|
|
||||||
*
|
|
||||||
* <p>If the device is running a build before platform API version 21, audio attributes cannot be
|
|
||||||
* set directly on the underlying audio track. In this case, the usage will be mapped onto an
|
|
||||||
* equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}.
|
|
||||||
*
|
|
||||||
* <p>If audio focus should be handled, the {@link AudioAttributes#usage} must be {@link
|
|
||||||
* C#USAGE_MEDIA} or {@link C#USAGE_GAME}. Other usages will throw an {@link
|
|
||||||
* IllegalArgumentException}.
|
|
||||||
*
|
|
||||||
* @param audioAttributes The attributes to use for audio playback.
|
|
||||||
* @param handleAudioFocus True if the player should handle audio focus, false otherwise.
|
|
||||||
*/
|
|
||||||
void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the ID of the audio session to attach to the underlying {@link android.media.AudioTrack}.
|
* Sets the ID of the audio session to attach to the underlying {@link android.media.AudioTrack}.
|
||||||
*
|
*
|
||||||
|
@ -314,6 +314,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||||
COMMAND_GET_TRACKS,
|
COMMAND_GET_TRACKS,
|
||||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||||
|
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||||
COMMAND_GET_VOLUME,
|
COMMAND_GET_VOLUME,
|
||||||
COMMAND_SET_VOLUME,
|
COMMAND_SET_VOLUME,
|
||||||
COMMAND_SET_VIDEO_SURFACE,
|
COMMAND_SET_VIDEO_SURFACE,
|
||||||
|
@ -38,6 +38,7 @@ import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT;
|
|||||||
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
|
||||||
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS;
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS;
|
||||||
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
|
||||||
|
import static androidx.media3.common.Player.COMMAND_SET_AUDIO_ATTRIBUTES;
|
||||||
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME;
|
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME;
|
||||||
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS;
|
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS;
|
||||||
import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM;
|
import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM;
|
||||||
@ -9456,6 +9457,7 @@ public final class ExoPlayerTest {
|
|||||||
assertThat(player.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)).isTrue();
|
assertThat(player.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)).isTrue();
|
||||||
assertThat(player.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)).isTrue();
|
assertThat(player.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)).isTrue();
|
||||||
assertThat(player.isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES)).isTrue();
|
assertThat(player.isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES)).isTrue();
|
||||||
|
assertThat(player.isCommandAvailable(COMMAND_SET_AUDIO_ATTRIBUTES)).isTrue();
|
||||||
assertThat(player.isCommandAvailable(COMMAND_GET_VOLUME)).isTrue();
|
assertThat(player.isCommandAvailable(COMMAND_GET_VOLUME)).isTrue();
|
||||||
assertThat(player.isCommandAvailable(COMMAND_SET_VOLUME)).isTrue();
|
assertThat(player.isCommandAvailable(COMMAND_SET_VOLUME)).isTrue();
|
||||||
assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue();
|
assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue();
|
||||||
@ -14143,6 +14145,7 @@ public final class ExoPlayerTest {
|
|||||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||||
COMMAND_SET_MEDIA_ITEM,
|
COMMAND_SET_MEDIA_ITEM,
|
||||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||||
|
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||||
COMMAND_GET_VOLUME,
|
COMMAND_GET_VOLUME,
|
||||||
COMMAND_SET_VOLUME,
|
COMMAND_SET_VOLUME,
|
||||||
COMMAND_SET_VIDEO_SURFACE,
|
COMMAND_SET_VIDEO_SURFACE,
|
||||||
|
@ -40,6 +40,7 @@ oneway interface IMediaSession {
|
|||||||
void decreaseDeviceVolumeWithFlags(IMediaController caller, int seq, int flags) = 3052;
|
void decreaseDeviceVolumeWithFlags(IMediaController caller, int seq, int flags) = 3052;
|
||||||
void setDeviceMuted(IMediaController caller, int seq, boolean muted) = 3005;
|
void setDeviceMuted(IMediaController caller, int seq, boolean muted) = 3005;
|
||||||
void setDeviceMutedWithFlags(IMediaController caller, int seq, boolean muted, int flags) = 3053;
|
void setDeviceMutedWithFlags(IMediaController caller, int seq, boolean muted, int flags) = 3053;
|
||||||
|
void setAudioAttributes(IMediaController caller, int seq, in Bundle audioAttributes, boolean handleAudioFocus) = 3056;
|
||||||
void setMediaItem(
|
void setMediaItem(
|
||||||
IMediaController caller,
|
IMediaController caller,
|
||||||
int seq,
|
int seq,
|
||||||
@ -120,7 +121,7 @@ oneway interface IMediaSession {
|
|||||||
void setRatingWithMediaId(
|
void setRatingWithMediaId(
|
||||||
IMediaController caller, int seq, String mediaId, in Bundle rating) = 3048;
|
IMediaController caller, int seq, String mediaId, in Bundle rating) = 3048;
|
||||||
void setRating(IMediaController caller, int seq, in Bundle rating) = 3049;
|
void setRating(IMediaController caller, int seq, in Bundle rating) = 3049;
|
||||||
// Next Id for MediaSession: 3056
|
// Next Id for MediaSession: 3057
|
||||||
|
|
||||||
void getLibraryRoot(IMediaController caller, int seq, in Bundle libraryParams) = 4000;
|
void getLibraryRoot(IMediaController caller, int seq, in Bundle libraryParams) = 4000;
|
||||||
void getItem(IMediaController caller, int seq, String mediaId) = 4001;
|
void getItem(IMediaController caller, int seq, String mediaId) = 4001;
|
||||||
|
@ -1793,6 +1793,16 @@ public class MediaController implements Player {
|
|||||||
impl.setDeviceMuted(muted, flags);
|
impl.setDeviceMuted(muted, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
verifyApplicationThread();
|
||||||
|
if (!isConnected()) {
|
||||||
|
Log.w(TAG, "The controller is not connected. Ignoring setAudioAttributes().");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
impl.setAudioAttributes(audioAttributes, handleAudioFocus);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final MediaMetadata getMediaMetadata() {
|
public final MediaMetadata getMediaMetadata() {
|
||||||
verifyApplicationThread();
|
verifyApplicationThread();
|
||||||
@ -2139,6 +2149,8 @@ public class MediaController implements Player {
|
|||||||
|
|
||||||
void setDeviceMuted(boolean muted, @C.VolumeFlags int flags);
|
void setDeviceMuted(boolean muted, @C.VolumeFlags int flags);
|
||||||
|
|
||||||
|
void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);
|
||||||
|
|
||||||
boolean getPlayWhenReady();
|
boolean getPlayWhenReady();
|
||||||
|
|
||||||
@PlaybackSuppressionReason
|
@PlaybackSuppressionReason
|
||||||
|
@ -1672,6 +1672,26 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
if (!isPlayerCommandAvailable(Player.COMMAND_SET_AUDIO_ATTRIBUTES)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchRemoteSessionTaskWithPlayerCommand(
|
||||||
|
(iSession, seq) ->
|
||||||
|
iSession.setAudioAttributes(
|
||||||
|
controllerStub, seq, audioAttributes.toBundle(), handleAudioFocus));
|
||||||
|
|
||||||
|
if (!playerInfo.audioAttributes.equals(audioAttributes)) {
|
||||||
|
playerInfo = playerInfo.copyWithAudioAttributes(audioAttributes);
|
||||||
|
listeners.queueEvent(
|
||||||
|
/* eventFlag= */ Player.EVENT_AUDIO_ATTRIBUTES_CHANGED,
|
||||||
|
listener -> listener.onAudioAttributesChanged(audioAttributes));
|
||||||
|
listeners.flushEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VideoSize getVideoSize() {
|
public VideoSize getVideoSize() {
|
||||||
return playerInfo.videoSize;
|
return playerInfo.videoSize;
|
||||||
|
@ -1206,6 +1206,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
controllerCompat.adjustVolume(direction, flags);
|
controllerCompat.adjustVolume(direction, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
Log.w(TAG, "Legacy session doesn't support setting audio attributes remotely");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPlayWhenReady(boolean playWhenReady) {
|
public void setPlayWhenReady(boolean playWhenReady) {
|
||||||
if (playWhenReady) {
|
if (playWhenReady) {
|
||||||
|
@ -29,6 +29,7 @@ import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT;
|
|||||||
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
|
||||||
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS;
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS;
|
||||||
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
|
||||||
|
import static androidx.media3.common.Player.COMMAND_SET_AUDIO_ATTRIBUTES;
|
||||||
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME;
|
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME;
|
||||||
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS;
|
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS;
|
||||||
import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM;
|
import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM;
|
||||||
@ -65,6 +66,7 @@ import android.view.Surface;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.util.ObjectsCompat;
|
import androidx.core.util.ObjectsCompat;
|
||||||
import androidx.media.MediaSessionManager;
|
import androidx.media.MediaSessionManager;
|
||||||
|
import androidx.media3.common.AudioAttributes;
|
||||||
import androidx.media3.common.BundleListRetriever;
|
import androidx.media3.common.BundleListRetriever;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
@ -1541,6 +1543,25 @@ import java.util.concurrent.ExecutionException;
|
|||||||
sendSessionResultSuccess(player -> player.setDeviceMuted(muted, flags)));
|
sendSessionResultSuccess(player -> player.setDeviceMuted(muted, flags)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAudioAttributes(
|
||||||
|
@Nullable IMediaController caller,
|
||||||
|
int sequenceNumber,
|
||||||
|
Bundle audioAttributes,
|
||||||
|
boolean handleAudioFocus) {
|
||||||
|
if (caller == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
queueSessionTaskWithPlayerCommand(
|
||||||
|
caller,
|
||||||
|
sequenceNumber,
|
||||||
|
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||||
|
sendSessionResultSuccess(
|
||||||
|
player ->
|
||||||
|
player.setAudioAttributes(
|
||||||
|
AudioAttributes.CREATOR.fromBundle(audioAttributes), handleAudioFocus)));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPlayWhenReady(
|
public void setPlayWhenReady(
|
||||||
@Nullable IMediaController caller, int sequenceNumber, boolean playWhenReady) {
|
@Nullable IMediaController caller, int sequenceNumber, boolean playWhenReady) {
|
||||||
|
@ -81,6 +81,7 @@ interface IRemoteMediaController {
|
|||||||
void decreaseDeviceVolumeWithFlags(String controllerId, int flags);
|
void decreaseDeviceVolumeWithFlags(String controllerId, int flags);
|
||||||
void setDeviceMuted(String controllerId, boolean muted);
|
void setDeviceMuted(String controllerId, boolean muted);
|
||||||
void setDeviceMutedWithFlags(String controllerId, boolean muted, int flags);
|
void setDeviceMutedWithFlags(String controllerId, boolean muted, int flags);
|
||||||
|
void setAudioAttributes(String controllerId, in Bundle audioAttributes, boolean handleAudioFocus);
|
||||||
Bundle sendCustomCommand(String controllerId, in Bundle command, in Bundle args);
|
Bundle sendCustomCommand(String controllerId, in Bundle command, in Bundle args);
|
||||||
Bundle setRatingWithMediaId(String controllerId, String mediaId, in Bundle rating);
|
Bundle setRatingWithMediaId(String controllerId, String mediaId, in Bundle rating);
|
||||||
Bundle setRating(String controllerId, in Bundle rating);
|
Bundle setRating(String controllerId, in Bundle rating);
|
||||||
|
@ -28,6 +28,7 @@ import android.content.Context;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.AudioAttributes;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.DeviceInfo;
|
import androidx.media3.common.DeviceInfo;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
@ -711,6 +712,52 @@ public class MediaControllerStateMaskingTest {
|
|||||||
.containsExactly(Player.EVENT_DEVICE_VOLUME_CHANGED);
|
.containsExactly(Player.EVENT_DEVICE_VOLUME_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setAudioAttributes() throws Exception {
|
||||||
|
AudioAttributes originalAttrs =
|
||||||
|
new AudioAttributes.Builder().setContentType(C.AUDIO_CONTENT_TYPE_MUSIC).build();
|
||||||
|
Bundle playerConfig =
|
||||||
|
new RemoteMediaSession.MockPlayerConfigBuilder().setAudioAttributes(originalAttrs).build();
|
||||||
|
remoteSession.setPlayer(playerConfig);
|
||||||
|
|
||||||
|
MediaController controller = controllerTestRule.createController(remoteSession.getToken());
|
||||||
|
CountDownLatch latch = new CountDownLatch(2);
|
||||||
|
AtomicReference<AudioAttributes> audioAttributesFromCallbackRef = new AtomicReference<>();
|
||||||
|
AtomicReference<Player.Events> onEventsRef = new AtomicReference<>();
|
||||||
|
Player.Listener listener =
|
||||||
|
new Player.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onAudioAttributesChanged(AudioAttributes audioAttributes) {
|
||||||
|
audioAttributesFromCallbackRef.set(originalAttrs);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvents(Player player, Player.Events events) {
|
||||||
|
onEventsRef.set(events);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener));
|
||||||
|
|
||||||
|
AtomicReference<AudioAttributes> audioAttributesFromGetterRef = new AtomicReference<>();
|
||||||
|
AudioAttributes newAttributes =
|
||||||
|
new AudioAttributes.Builder().setContentType(C.AUDIO_CONTENT_TYPE_SPEECH).build();
|
||||||
|
threadTestRule
|
||||||
|
.getHandler()
|
||||||
|
.postAndSync(
|
||||||
|
() -> {
|
||||||
|
controller.setAudioAttributes(newAttributes, /* handleAudioFocus= */ false);
|
||||||
|
audioAttributesFromGetterRef.set(controller.getAudioAttributes());
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(audioAttributesFromCallbackRef.get()).isEqualTo(originalAttrs);
|
||||||
|
assertThat(audioAttributesFromGetterRef.get()).isEqualTo(newAttributes);
|
||||||
|
assertThat(getEventsAsList(onEventsRef.get()))
|
||||||
|
.containsExactly(Player.EVENT_AUDIO_ATTRIBUTES_CHANGED);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void prepare() throws Exception {
|
public void prepare() throws Exception {
|
||||||
int testPlaybackState = Player.STATE_ENDED;
|
int testPlaybackState = Player.STATE_ENDED;
|
||||||
|
@ -27,6 +27,8 @@ import android.os.HandlerThread;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.support.v4.media.MediaDescriptionCompat;
|
import android.support.v4.media.MediaDescriptionCompat;
|
||||||
import android.support.v4.media.session.MediaControllerCompat;
|
import android.support.v4.media.session.MediaControllerCompat;
|
||||||
|
import androidx.media3.common.AudioAttributes;
|
||||||
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.DeviceInfo;
|
import androidx.media3.common.DeviceInfo;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MediaMetadata;
|
import androidx.media3.common.MediaMetadata;
|
||||||
@ -268,7 +270,7 @@ public class MediaSessionPlayerTest {
|
|||||||
remoteControllerTestRule.createRemoteController(session.getToken());
|
remoteControllerTestRule.createRemoteController(session.getToken());
|
||||||
|
|
||||||
// The controller should only be able to see the current item without Timeline access.
|
// The controller should only be able to see the current item without Timeline access.
|
||||||
controller.seekTo(/* mediaItemIndex= */ 0, /* seekPositionMs= */ 2000);
|
controller.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 2000);
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX, TIMEOUT_MS);
|
||||||
controller.release();
|
controller.release();
|
||||||
session.release();
|
session.release();
|
||||||
@ -828,6 +830,20 @@ public class MediaSessionPlayerTest {
|
|||||||
assertThat(player.deviceMuted).isTrue();
|
assertThat(player.deviceMuted).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setAudioAttributes() throws Exception {
|
||||||
|
AudioAttributes audioAttributes =
|
||||||
|
new AudioAttributes.Builder()
|
||||||
|
.setContentType(C.AUDIO_CONTENT_TYPE_SONIFICATION)
|
||||||
|
.setUsage(C.USAGE_ALARM)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
controller.setAudioAttributes(audioAttributes, /* handleAudioFocus= */ true);
|
||||||
|
|
||||||
|
player.awaitMethodCalled(MockPlayer.METHOD_SET_AUDIO_ATTRIBUTES, TIMEOUT_MS);
|
||||||
|
assertThat(player.getAudioAttributes()).isEqualTo(audioAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void seekBack() throws Exception {
|
public void seekBack() throws Exception {
|
||||||
controller.seekBack();
|
controller.seekBack();
|
||||||
|
@ -28,6 +28,7 @@ import android.media.AudioManager;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import androidx.media3.common.AudioAttributes;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MediaMetadata;
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.PlaybackParameters;
|
import androidx.media3.common.PlaybackParameters;
|
||||||
@ -697,6 +698,18 @@ public class MediaControllerProviderService extends Service {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAudioAttributes(
|
||||||
|
String controllerId, Bundle audioAttributes, boolean handleAudioFocus)
|
||||||
|
throws RemoteException {
|
||||||
|
runOnHandler(
|
||||||
|
() -> {
|
||||||
|
MediaController controller = mediaControllerMap.get(controllerId);
|
||||||
|
controller.setAudioAttributes(
|
||||||
|
AudioAttributes.CREATOR.fromBundle(audioAttributes), handleAudioFocus);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release(String controllerId) throws RemoteException {
|
public void release(String controllerId) throws RemoteException {
|
||||||
runOnHandler(
|
runOnHandler(
|
||||||
|
@ -257,6 +257,9 @@ public class MockPlayer implements Player {
|
|||||||
/** Maps to {@link Player#replaceMediaItems(int, int, List)} . */
|
/** Maps to {@link Player#replaceMediaItems(int, int, List)} . */
|
||||||
public static final int METHOD_REPLACE_MEDIA_ITEMS = 47;
|
public static final int METHOD_REPLACE_MEDIA_ITEMS = 47;
|
||||||
|
|
||||||
|
/** Maps to {@link Player#setAudioAttributes(AudioAttributes, boolean)}. */
|
||||||
|
public static final int METHOD_SET_AUDIO_ATTRIBUTES = 48;
|
||||||
|
|
||||||
private final boolean changePlayerStateWithTransportControl;
|
private final boolean changePlayerStateWithTransportControl;
|
||||||
private final Looper applicationLooper;
|
private final Looper applicationLooper;
|
||||||
private final ArraySet<Listener> listeners = new ArraySet<>();
|
private final ArraySet<Listener> listeners = new ArraySet<>();
|
||||||
@ -766,6 +769,12 @@ public class MockPlayer implements Player {
|
|||||||
checkNotNull(conditionVariables.get(METHOD_SET_DEVICE_MUTED_WITH_FLAGS)).open();
|
checkNotNull(conditionVariables.get(METHOD_SET_DEVICE_MUTED_WITH_FLAGS)).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
this.audioAttributes = audioAttributes;
|
||||||
|
checkNotNull(conditionVariables.get(METHOD_SET_AUDIO_ATTRIBUTES)).open();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPlayWhenReady(boolean playWhenReady) {
|
public void setPlayWhenReady(boolean playWhenReady) {
|
||||||
this.playWhenReady = playWhenReady;
|
this.playWhenReady = playWhenReady;
|
||||||
@ -1478,6 +1487,7 @@ public class MockPlayer implements Player {
|
|||||||
.put(METHOD_SET_SHUFFLE_MODE, new ConditionVariable())
|
.put(METHOD_SET_SHUFFLE_MODE, new ConditionVariable())
|
||||||
.put(METHOD_SET_TRACK_SELECTION_PARAMETERS, new ConditionVariable())
|
.put(METHOD_SET_TRACK_SELECTION_PARAMETERS, new ConditionVariable())
|
||||||
.put(METHOD_SET_VOLUME, new ConditionVariable())
|
.put(METHOD_SET_VOLUME, new ConditionVariable())
|
||||||
|
.put(METHOD_SET_AUDIO_ATTRIBUTES, new ConditionVariable())
|
||||||
.put(METHOD_STOP, new ConditionVariable())
|
.put(METHOD_STOP, new ConditionVariable())
|
||||||
.put(METHOD_REPLACE_MEDIA_ITEM, new ConditionVariable())
|
.put(METHOD_REPLACE_MEDIA_ITEM, new ConditionVariable())
|
||||||
.put(METHOD_REPLACE_MEDIA_ITEMS, new ConditionVariable())
|
.put(METHOD_REPLACE_MEDIA_ITEMS, new ConditionVariable())
|
||||||
|
@ -30,6 +30,7 @@ import android.os.Bundle;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.AudioAttributes;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MediaMetadata;
|
import androidx.media3.common.MediaMetadata;
|
||||||
@ -314,6 +315,11 @@ public class RemoteMediaController {
|
|||||||
binder.setDeviceMutedWithFlags(controllerId, muted, flags);
|
binder.setDeviceMutedWithFlags(controllerId, muted, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus)
|
||||||
|
throws RemoteException {
|
||||||
|
binder.setAudioAttributes(controllerId, audioAttributes.toBundle(), handleAudioFocus);
|
||||||
|
}
|
||||||
|
|
||||||
public SessionResult sendCustomCommand(SessionCommand command, Bundle args)
|
public SessionResult sendCustomCommand(SessionCommand command, Bundle args)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
Bundle result = binder.sendCustomCommand(controllerId, command.toBundle(), args);
|
Bundle result = binder.sendCustomCommand(controllerId, command.toBundle(), args);
|
||||||
|
@ -431,4 +431,9 @@ public class StubPlayer extends BasePlayer {
|
|||||||
public void setDeviceMuted(boolean muted, @C.VolumeFlags int flags) {
|
public void setDeviceMuted(boolean muted, @C.VolumeFlags int flags) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user