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)).
|
||||
* Change `BaseRenderer.onStreamChanged()` to also receive a
|
||||
`MediaPeriodId` argument.
|
||||
* Move `ExoPlayer.setAudioAttributes` to the `Player` interface.
|
||||
* Transformer:
|
||||
* Changed `frameRate` and `durationUs` parameters of
|
||||
`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 seekToPrevious();
|
||||
method public void seekToPreviousMediaItem();
|
||||
method public void setAudioAttributes(androidx.media3.common.AudioAttributes, boolean);
|
||||
method @Deprecated public void setDeviceMuted(boolean);
|
||||
method public void setDeviceMuted(boolean, @androidx.media3.common.C.VolumeFlags 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_PREVIOUS = 7; // 0x7
|
||||
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 public static final int COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS = 33; // 0x21
|
||||
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
|
||||
}
|
||||
|
||||
@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 {
|
||||
@ -1347,7 +1349,6 @@ package androidx.media3.exoplayer {
|
||||
method public void addAnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener);
|
||||
method @Nullable public androidx.media3.exoplayer.ExoPlaybackException getPlayerError();
|
||||
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 setWakeMode(@androidx.media3.common.C.WakeMode int);
|
||||
}
|
||||
@ -1618,6 +1619,7 @@ package androidx.media3.session {
|
||||
method public final void seekToPrevious();
|
||||
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 void setAudioAttributes(androidx.media3.common.AudioAttributes, boolean);
|
||||
method @Deprecated public final void setDeviceMuted(boolean);
|
||||
method public final void setDeviceMuted(boolean, @androidx.media3.common.C.VolumeFlags int);
|
||||
method @Deprecated public final void setDeviceVolume(@IntRange(from=0) int);
|
||||
|
@ -811,6 +811,10 @@ public final class CastPlayer extends BasePlayer {
|
||||
@Override
|
||||
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.
|
||||
|
||||
// Call deprecated callbacks.
|
||||
|
@ -917,6 +917,12 @@ public class ForwardingPlayer implements Player {
|
||||
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. */
|
||||
public Player getWrappedPlayer() {
|
||||
return player;
|
||||
|
@ -491,6 +491,7 @@ public interface Player {
|
||||
COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS,
|
||||
COMMAND_ADJUST_DEVICE_VOLUME,
|
||||
COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS,
|
||||
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||
COMMAND_SET_VIDEO_SURFACE,
|
||||
COMMAND_GET_TEXT,
|
||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
||||
@ -1607,6 +1608,7 @@ public interface Player {
|
||||
* <li>{@link #COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS}
|
||||
* <li>{@link #COMMAND_ADJUST_DEVICE_VOLUME}
|
||||
* <li>{@link #COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS}
|
||||
* <li>{@link #COMMAND_SET_AUDIO_ATTRIBUTES}
|
||||
* <li>{@link #COMMAND_SET_VIDEO_SURFACE}
|
||||
* <li>{@link #COMMAND_GET_TEXT}
|
||||
* <li>{@link #COMMAND_SET_TRACK_SELECTION_PARAMETERS}
|
||||
@ -1653,6 +1655,7 @@ public interface Player {
|
||||
COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS,
|
||||
COMMAND_ADJUST_DEVICE_VOLUME,
|
||||
COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS,
|
||||
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||
COMMAND_SET_VIDEO_SURFACE,
|
||||
COMMAND_GET_TEXT,
|
||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
||||
@ -2005,6 +2008,14 @@ public interface Player {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -3393,4 +3404,30 @@ public interface Player {
|
||||
* @param flags Either 0 or a bitwise combination of one or more {@link C.VolumeFlags}.
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
@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.
|
||||
*
|
||||
@ -3175,6 +3189,23 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
||||
+ " 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.
|
||||
*
|
||||
|
@ -3617,6 +3617,125 @@ public class SimpleBasePlayerTest {
|
||||
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
|
||||
public void setVideoSurface_immediateHandling_updatesStateAndInformsListeners() {
|
||||
State state =
|
||||
|
@ -173,7 +173,7 @@ public interface ExoPlayer extends Player {
|
||||
interface AudioComponent {
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link ExoPlayer#setAudioAttributes(AudioAttributes, boolean)} instead.
|
||||
* @deprecated Use {@link Player#setAudioAttributes(AudioAttributes, boolean)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);
|
||||
@ -1514,29 +1514,6 @@ public interface ExoPlayer extends Player {
|
||||
@Override
|
||||
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}.
|
||||
*
|
||||
|
@ -314,6 +314,7 @@ import java.util.concurrent.TimeoutException;
|
||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||
COMMAND_GET_TRACKS,
|
||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||
COMMAND_GET_VOLUME,
|
||||
COMMAND_SET_VOLUME,
|
||||
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_PREVIOUS;
|
||||
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_WITH_FLAGS;
|
||||
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_SET_MEDIA_ITEM)).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_SET_VOLUME)).isTrue();
|
||||
assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue();
|
||||
@ -14143,6 +14145,7 @@ public final class ExoPlayerTest {
|
||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||
COMMAND_SET_MEDIA_ITEM,
|
||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||
COMMAND_SET_AUDIO_ATTRIBUTES,
|
||||
COMMAND_GET_VOLUME,
|
||||
COMMAND_SET_VOLUME,
|
||||
COMMAND_SET_VIDEO_SURFACE,
|
||||
|
@ -40,6 +40,7 @@ oneway interface IMediaSession {
|
||||
void decreaseDeviceVolumeWithFlags(IMediaController caller, int seq, int flags) = 3052;
|
||||
void setDeviceMuted(IMediaController caller, int seq, boolean muted) = 3005;
|
||||
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(
|
||||
IMediaController caller,
|
||||
int seq,
|
||||
@ -120,7 +121,7 @@ oneway interface IMediaSession {
|
||||
void setRatingWithMediaId(
|
||||
IMediaController caller, int seq, String mediaId, in Bundle rating) = 3048;
|
||||
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 getItem(IMediaController caller, int seq, String mediaId) = 4001;
|
||||
|
@ -1793,6 +1793,16 @@ public class MediaController implements Player {
|
||||
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
|
||||
public final MediaMetadata getMediaMetadata() {
|
||||
verifyApplicationThread();
|
||||
@ -2139,6 +2149,8 @@ public class MediaController implements Player {
|
||||
|
||||
void setDeviceMuted(boolean muted, @C.VolumeFlags int flags);
|
||||
|
||||
void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);
|
||||
|
||||
boolean getPlayWhenReady();
|
||||
|
||||
@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
|
||||
public VideoSize getVideoSize() {
|
||||
return playerInfo.videoSize;
|
||||
|
@ -1206,6 +1206,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
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
|
||||
public void setPlayWhenReady(boolean 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_PREVIOUS;
|
||||
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_WITH_FLAGS;
|
||||
import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM;
|
||||
@ -65,6 +66,7 @@ import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.ObjectsCompat;
|
||||
import androidx.media.MediaSessionManager;
|
||||
import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.BundleListRetriever;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
@ -1541,6 +1543,25 @@ import java.util.concurrent.ExecutionException;
|
||||
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
|
||||
public void setPlayWhenReady(
|
||||
@Nullable IMediaController caller, int sequenceNumber, boolean playWhenReady) {
|
||||
|
@ -81,6 +81,7 @@ interface IRemoteMediaController {
|
||||
void decreaseDeviceVolumeWithFlags(String controllerId, int flags);
|
||||
void setDeviceMuted(String controllerId, boolean muted);
|
||||
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 setRatingWithMediaId(String controllerId, String mediaId, 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.RemoteException;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.DeviceInfo;
|
||||
import androidx.media3.common.MediaItem;
|
||||
@ -711,6 +712,52 @@ public class MediaControllerStateMaskingTest {
|
||||
.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
|
||||
public void prepare() throws Exception {
|
||||
int testPlaybackState = Player.STATE_ENDED;
|
||||
|
@ -27,6 +27,8 @@ import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.support.v4.media.MediaDescriptionCompat;
|
||||
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.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
@ -268,7 +270,7 @@ public class MediaSessionPlayerTest {
|
||||
remoteControllerTestRule.createRemoteController(session.getToken());
|
||||
|
||||
// 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);
|
||||
controller.release();
|
||||
session.release();
|
||||
@ -828,6 +830,20 @@ public class MediaSessionPlayerTest {
|
||||
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
|
||||
public void seekBack() throws Exception {
|
||||
controller.seekBack();
|
||||
|
@ -28,6 +28,7 @@ import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
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
|
||||
public void release(String controllerId) throws RemoteException {
|
||||
runOnHandler(
|
||||
|
@ -257,6 +257,9 @@ public class MockPlayer implements Player {
|
||||
/** Maps to {@link Player#replaceMediaItems(int, int, List)} . */
|
||||
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 Looper applicationLooper;
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||
this.audioAttributes = audioAttributes;
|
||||
checkNotNull(conditionVariables.get(METHOD_SET_AUDIO_ATTRIBUTES)).open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayWhenReady(boolean playWhenReady) {
|
||||
this.playWhenReady = playWhenReady;
|
||||
@ -1478,6 +1487,7 @@ public class MockPlayer implements Player {
|
||||
.put(METHOD_SET_SHUFFLE_MODE, new ConditionVariable())
|
||||
.put(METHOD_SET_TRACK_SELECTION_PARAMETERS, new ConditionVariable())
|
||||
.put(METHOD_SET_VOLUME, new ConditionVariable())
|
||||
.put(METHOD_SET_AUDIO_ATTRIBUTES, new ConditionVariable())
|
||||
.put(METHOD_STOP, new ConditionVariable())
|
||||
.put(METHOD_REPLACE_MEDIA_ITEM, new ConditionVariable())
|
||||
.put(METHOD_REPLACE_MEDIA_ITEMS, new ConditionVariable())
|
||||
|
@ -30,6 +30,7 @@ import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.AudioAttributes;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
@ -314,6 +315,11 @@ public class RemoteMediaController {
|
||||
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)
|
||||
throws RemoteException {
|
||||
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) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user