diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java index 52278506c5..8410eb21b7 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java @@ -30,6 +30,7 @@ import com.google.android.exoplayer2.MediaMetadata; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; @@ -251,14 +252,13 @@ public final class CastPlayer extends BasePlayer { public MediaQueueItem getItem(int periodId) { MediaStatus mediaStatus = getMediaStatus(); return mediaStatus != null && currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET - ? mediaStatus.getItemById(periodId) : null; + ? mediaStatus.getItemById(periodId) + : null; } // CastSession methods. - /** - * Returns whether a cast session is available. - */ + /** Returns whether a cast session is available. */ public boolean isCastSessionAvailable() { return remoteMediaClient != null; } @@ -274,12 +274,6 @@ public final class CastPlayer extends BasePlayer { // Player implementation. - @Override - @Nullable - public AudioComponent getAudioComponent() { - return null; - } - @Override @Nullable public VideoComponent getVideoComponent() { @@ -461,8 +455,10 @@ public final class CastPlayer extends BasePlayer { positionMs = positionMs != C.TIME_UNSET ? positionMs : 0; if (mediaStatus != null) { if (getCurrentWindowIndex() != windowIndex) { - remoteMediaClient.queueJumpToItem((int) currentTimeline.getPeriod(windowIndex, period).uid, - positionMs, null).setResultCallback(seekResultCallback); + remoteMediaClient + .queueJumpToItem( + (int) currentTimeline.getPeriod(windowIndex, period).uid, positionMs, null) + .setResultCallback(seekResultCallback); } else { remoteMediaClient.seek(positionMs).setResultCallback(seekResultCallback); } @@ -545,7 +541,8 @@ public final class CastPlayer extends BasePlayer { } @Override - @RepeatMode public int getRepeatMode() { + @RepeatMode + public int getRepeatMode() { return repeatMode.value; } @@ -657,6 +654,22 @@ public final class CastPlayer extends BasePlayer { return getBufferedPosition(); } + /** This method is not supported and returns {@link AudioAttributes#DEFAULT}. */ + @Override + public AudioAttributes getAudioAttributes() { + return AudioAttributes.DEFAULT; + } + + /** This method is not supported and does nothing. */ + @Override + public void setVolume(float audioVolume) {} + + /** This method is not supported and returns 1. */ + @Override + public float getVolume() { + return 1; + } + // Internal methods. // Call deprecated callbacks. @@ -897,7 +910,8 @@ public final class CastPlayer extends BasePlayer { long id = mediaTrack.getId(); int trackType = MimeTypes.getTrackType(mediaTrack.getContentType()); int rendererIndex = getRendererIndexForTrackType(trackType); - if (isTrackActive(id, activeTrackIds) && rendererIndex != C.INDEX_UNSET + if (isTrackActive(id, activeTrackIds) + && rendererIndex != C.INDEX_UNSET && trackSelections[rendererIndex] == null) { trackSelections[rendererIndex] = new CastTrackSelection(trackGroups[i]); } @@ -1097,8 +1111,8 @@ public final class CastPlayer extends BasePlayer { } /** - * Retrieves the repeat mode from {@code remoteMediaClient} and maps it into a - * {@link Player.RepeatMode}. + * Retrieves the repeat mode from {@code remoteMediaClient} and maps it into a {@link + * Player.RepeatMode}. */ @RepeatMode private static int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { @@ -1238,8 +1252,12 @@ public final class CastPlayer extends BasePlayer { @Override public void onSessionResumeFailed(CastSession castSession, int statusCode) { - Log.e(TAG, "Session resume failed. Error code " + statusCode + ": " - + CastUtils.getLogString(statusCode)); + Log.e( + TAG, + "Session resume failed. Error code " + + statusCode + + ": " + + CastUtils.getLogString(statusCode)); } @Override @@ -1249,8 +1267,12 @@ public final class CastPlayer extends BasePlayer { @Override public void onSessionStartFailed(CastSession castSession, int statusCode) { - Log.e(TAG, "Session start failed. Error code " + statusCode + ": " - + CastUtils.getLogString(statusCode)); + Log.e( + TAG, + "Session start failed. Error code " + + statusCode + + ": " + + CastUtils.getLogString(statusCode)); } @Override @@ -1262,7 +1284,6 @@ public final class CastPlayer extends BasePlayer { public void onSessionResuming(CastSession castSession, String s) { // Do nothing. } - } private final class SeekResultCallback implements ResultCallback { @@ -1274,8 +1295,9 @@ public final class CastPlayer extends BasePlayer { public void onResult(MediaChannelResult result) { int statusCode = result.getStatus().getStatusCode(); if (statusCode != CastStatusCodes.SUCCESS && statusCode != CastStatusCodes.REPLACED) { - Log.e(TAG, "Seek failed. Error code " + statusCode + ": " - + CastUtils.getLogString(statusCode)); + Log.e( + TAG, + "Seek failed. Error code " + statusCode + ": " + CastUtils.getLogString(statusCode)); } if (--pendingSeekCount == 0) { currentWindowIndex = pendingSeekWindowIndex; diff --git a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastPlayerTest.java b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastPlayerTest.java index cb17f0da85..ab49831c60 100644 --- a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastPlayerTest.java +++ b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastPlayerTest.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.cast; import static com.google.android.exoplayer2.Player.COMMAND_ADJUST_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_CHANGE_MEDIA_ITEMS; +import static com.google.android.exoplayer2.Player.COMMAND_GET_AUDIO_ATTRIBUTES; import static com.google.android.exoplayer2.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.COMMAND_GET_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS; @@ -102,6 +103,7 @@ public class CastPlayerTest { @Captor private ArgumentCaptor> setResultCallbackArgumentCaptor; + @Captor private ArgumentCaptor callbackArgumentCaptor; @Captor private ArgumentCaptor queueItemsArgumentCaptor; @Captor private ArgumentCaptor mediaItemCaptor; @@ -1119,6 +1121,7 @@ public class CastPlayerTest { assertThat(castPlayer.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS)).isTrue(); assertThat(castPlayer.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS_METADATA)).isTrue(); assertThat(castPlayer.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)).isTrue(); + assertThat(castPlayer.isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES)).isFalse(); assertThat(castPlayer.isCommandAvailable(COMMAND_GET_VOLUME)).isFalse(); assertThat(castPlayer.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isFalse(); assertThat(castPlayer.isCommandAvailable(COMMAND_SET_VOLUME)).isFalse(); diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java index 048321de3d..9b79dbade0 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.ext.ima; +import static com.google.android.exoplayer2.Player.COMMAND_GET_VOLUME; import static com.google.android.exoplayer2.ext.ima.ImaUtil.BITRATE_UNSET; import static com.google.android.exoplayer2.ext.ima.ImaUtil.TIMEOUT_UNSET; import static com.google.android.exoplayer2.ext.ima.ImaUtil.getAdGroupTimesUsForCuePoints; @@ -700,9 +701,8 @@ import java.util.Map; return lastVolumePercent; } - @Nullable Player.AudioComponent audioComponent = player.getAudioComponent(); - if (audioComponent != null) { - return (int) (audioComponent.getVolume() * 100); + if (player.isCommandAvailable(COMMAND_GET_VOLUME)) { + return (int) (player.getVolume() * 100); } // Check for a selected track using an audio renderer. diff --git a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java index 8b1b908170..9cd3a228c2 100644 --- a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java +++ b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java @@ -194,6 +194,17 @@ import com.google.android.exoplayer2.util.ListenerSet; listeners.remove(listener); } + @Override + public boolean isCommandAvailable(@Command int command) { + // Only support querrying the minimal set of command for testing. + switch (command) { + case COMMAND_GET_VOLUME: + return false; + default: + throw new UnsupportedOperationException(); + } + } + @Override @Player.State public int getPlaybackState() { diff --git a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java index 40225eec9f..f4d934bcf9 100644 --- a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java +++ b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java @@ -33,7 +33,6 @@ import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.audio.AudioAttributes; -import com.google.android.exoplayer2.audio.AudioListener; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; @@ -138,10 +137,6 @@ import java.util.List; controlDispatcher = new DefaultControlDispatcher(); componentListener = new ComponentListener(); player.addListener(componentListener); - @Nullable Player.AudioComponent audioComponent = player.getAudioComponent(); - if (audioComponent != null) { - audioComponent.addAudioListener(componentListener); - } handler = new Handler(player.getApplicationLooper()); pollBufferRunnable = new PollBufferRunnable(); @@ -455,15 +450,15 @@ import java.util.List; } public void setAudioAttributes(AudioAttributesCompat audioAttributes) { - Player.AudioComponent audioComponent = Assertions.checkStateNotNull(player.getAudioComponent()); - audioComponent.setAudioAttributes( - Utils.getAudioAttributes(audioAttributes), /* handleAudioFocus= */ true); + // Player interface doesn't support setting audio attributes. } public AudioAttributesCompat getAudioAttributes() { - @Nullable Player.AudioComponent audioComponent = player.getAudioComponent(); - return Utils.getAudioAttributesCompat( - audioComponent != null ? audioComponent.getAudioAttributes() : AudioAttributes.DEFAULT); + AudioAttributes audioAttributes = AudioAttributes.DEFAULT; + if (player.isCommandAvailable(Player.COMMAND_GET_AUDIO_ATTRIBUTES)) { + audioAttributes = player.getAudioAttributes(); + } + return Utils.getAudioAttributesCompat(audioAttributes); } public void setPlaybackSpeed(float playbackSpeed) { @@ -483,11 +478,6 @@ import java.util.List; public void close() { handler.removeCallbacks(pollBufferRunnable); player.removeListener(componentListener); - - @Nullable Player.AudioComponent audioComponent = player.getAudioComponent(); - if (audioComponent != null) { - audioComponent.removeAudioListener(componentListener); - } } public boolean isCurrentMediaItemSeekable() { @@ -584,7 +574,7 @@ import java.util.List; } } - private final class ComponentListener implements Player.EventListener, AudioListener { + private final class ComponentListener implements Player.Listener { // Player.EventListener implementation. diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Player.java b/library/common/src/main/java/com/google/android/exoplayer2/Player.java index 5cf9f44f1f..5b8d522ed9 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Player.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer2; -import android.content.Context; import android.os.Looper; import android.view.Surface; import android.view.SurfaceHolder; @@ -25,7 +24,6 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioListener; -import com.google.android.exoplayer2.audio.AuxEffectInfo; import com.google.android.exoplayer2.device.DeviceInfo; import com.google.android.exoplayer2.device.DeviceListener; import com.google.android.exoplayer2.metadata.Metadata; @@ -68,95 +66,6 @@ import java.util.List; */ public interface Player { - /** The audio component of a {@link Player}. */ - interface AudioComponent { - - /** - * Adds a listener to receive audio events. - * - * @param listener The listener to register. - */ - void addAudioListener(AudioListener listener); - - /** - * Removes a listener of audio events. - * - * @param listener The listener to unregister. - */ - void removeAudioListener(AudioListener listener); - - /** - * 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. - * - *

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. - * - *

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. - * - *

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)}. - * - *

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); - - /** Returns the attributes for audio playback. */ - AudioAttributes getAudioAttributes(); - - /** - * Sets the ID of the audio session to attach to the underlying {@link - * android.media.AudioTrack}. - * - *

The audio session ID can be generated using {@link C#generateAudioSessionIdV21(Context)} - * for API 21+. - * - * @param audioSessionId The audio session ID, or {@link C#AUDIO_SESSION_ID_UNSET} if it should - * be generated by the framework. - */ - void setAudioSessionId(int audioSessionId); - - /** Returns the audio session identifier, or {@link C#AUDIO_SESSION_ID_UNSET} if not set. */ - int getAudioSessionId(); - - /** Sets information on an auxiliary audio effect to attach to the underlying audio track. */ - void setAuxEffectInfo(AuxEffectInfo auxEffectInfo); - - /** Detaches any previously attached auxiliary audio effect from the underlying audio track. */ - void clearAuxEffectInfo(); - - /** - * Sets the audio volume, with 0 being silence and 1 being unity gain (signal unchanged). - * - * @param audioVolume Linear output gain to apply to all audio channels. - */ - void setVolume(float audioVolume); - - /** - * Returns the audio volume, with 0 being silence and 1 being unity gain (signal unchanged). - * - * @return The linear gain applied to all audio channels. - */ - float getVolume(); - - /** - * Sets whether skipping silences in the audio stream is enabled. - * - * @param skipSilenceEnabled Whether skipping silences in the audio stream is enabled. - */ - void setSkipSilenceEnabled(boolean skipSilenceEnabled); - - /** Returns whether skipping silences in the audio stream is enabled. */ - boolean getSkipSilenceEnabled(); - } - /** The video component of a {@link Player}. */ interface VideoComponent { @@ -354,8 +263,7 @@ public interface Player { *

For devices with {@link DeviceInfo#PLAYBACK_TYPE_LOCAL local playback}, the volume * returned by this method varies according to the current {@link C.StreamType stream type}. The * stream type is determined by {@link AudioAttributes#usage} which can be converted to stream - * type with {@link Util#getStreamTypeForAudioUsage(int)}. The audio attributes can be set to - * the player by calling {@link AudioComponent#setAudioAttributes}. + * type with {@link Util#getStreamTypeForAudioUsage(int)}. * *

For devices with {@link DeviceInfo#PLAYBACK_TYPE_REMOTE remote playback}, the volume of * the remote device is returned. @@ -930,11 +838,12 @@ public interface Player { DeviceListener, EventListener { - @Override - default void onMetadata(Metadata metadata) {} - + // For backward compatibility TextOutput and MetadataOutput must stay functional interfaces. @Override default void onCues(List cues) {} + + @Override + default void onMetadata(Metadata metadata) {} } /** @@ -1179,10 +1088,10 @@ public interface Player { * #COMMAND_SET_SPEED_AND_PITCH}, {@link #COMMAND_SET_SHUFFLE_MODE}, {@link * #COMMAND_SET_REPEAT_MODE}, {@link #COMMAND_GET_CURRENT_MEDIA_ITEM}, {@link * #COMMAND_GET_MEDIA_ITEMS}, {@link #COMMAND_GET_MEDIA_ITEMS_METADATA}, {@link - * #COMMAND_CHANGE_MEDIA_ITEMS}, {@link #COMMAND_GET_VOLUME}, {@link #COMMAND_GET_DEVICE_VOLUME}, - * {@link #COMMAND_SET_VOLUME}, {@link #COMMAND_SET_DEVICE_VOLUME}, {@link - * #COMMAND_ADJUST_DEVICE_VOLUME}, {@link #COMMAND_SET_VIDEO_SURFACE} or {@link - * #COMMAND_GET_TEXT}. + * #COMMAND_CHANGE_MEDIA_ITEMS}, {@link #COMMAND_GET_AUDIO_ATTRIBUTES}, {@link + * #COMMAND_GET_VOLUME}, {@link #COMMAND_GET_DEVICE_VOLUME}, {@link #COMMAND_SET_VOLUME}, {@link + * #COMMAND_SET_DEVICE_VOLUME}, {@link #COMMAND_ADJUST_DEVICE_VOLUME}, {@link + * #COMMAND_SET_VIDEO_SURFACE} or {@link #COMMAND_GET_TEXT}. */ @Documented @Retention(RetentionPolicy.SOURCE) @@ -1201,6 +1110,7 @@ public interface Player { COMMAND_GET_MEDIA_ITEMS, COMMAND_GET_MEDIA_ITEMS_METADATA, COMMAND_CHANGE_MEDIA_ITEMS, + COMMAND_GET_AUDIO_ATTRIBUTES, COMMAND_GET_VOLUME, COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_VOLUME, @@ -1238,24 +1148,22 @@ public interface Player { int COMMAND_GET_MEDIA_ITEMS_METADATA = 13; /** Command to change the {@link MediaItem MediaItems} in the playlist. */ int COMMAND_CHANGE_MEDIA_ITEMS = 14; + /** Command to get the player current {@link AudioAttributes}. */ + int COMMAND_GET_AUDIO_ATTRIBUTES = 15; /** Command to get the player volume. */ - int COMMAND_GET_VOLUME = 15; + int COMMAND_GET_VOLUME = 16; /** Command to get the device volume and whether it is muted. */ - int COMMAND_GET_DEVICE_VOLUME = 16; + int COMMAND_GET_DEVICE_VOLUME = 17; /** Command to set the player volume. */ - int COMMAND_SET_VOLUME = 17; + int COMMAND_SET_VOLUME = 18; /** Command to set the device volume and mute it. */ - int COMMAND_SET_DEVICE_VOLUME = 18; + int COMMAND_SET_DEVICE_VOLUME = 19; /** Command to increase and decrease the device volume and mute it. */ - int COMMAND_ADJUST_DEVICE_VOLUME = 19; + int COMMAND_ADJUST_DEVICE_VOLUME = 20; /** Command to set and clear the surface on which to render the video. */ - int COMMAND_SET_VIDEO_SURFACE = 20; + int COMMAND_SET_VIDEO_SURFACE = 21; /** Command to get the text that should currently be displayed by the player. */ - int COMMAND_GET_TEXT = 21; - - /** Returns the component of this player for audio output, or null if audio is not supported. */ - @Nullable - AudioComponent getAudioComponent(); + int COMMAND_GET_TEXT = 22; /** Returns the component of this player for video output, or null if video is not supported. */ @Nullable @@ -1293,11 +1201,6 @@ public interface Player { /** * Registers a listener to receive all events from the player. * - *

Do not register the listener additionally in individual `Player` components (such as {@link - * Player.AudioComponent#addAudioListener(AudioListener)}, {@link - * Player.VideoComponent#addVideoListener(VideoListener)}) as it will already receive all their - * events. - * * @param listener The listener to register. */ void addListener(Listener listener); @@ -1936,4 +1839,21 @@ public interface Player { * playing, the returned position is the same as that returned by {@link #getBufferedPosition()}. */ long getContentBufferedPosition(); + + /** Returns the attributes for audio playback. */ + AudioAttributes getAudioAttributes(); + + /** + * Sets the audio volume, with 0 being silence and 1 being unity gain (signal unchanged). + * + * @param audioVolume Linear output gain to apply to all audio channels. + */ + void setVolume(float audioVolume); + + /** + * Returns the audio volume, with 0 being silence and 1 being unity gain (signal unchanged). + * + * @return The linear gain applied to all audio channels. + */ + float getVolume(); } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/metadata/MetadataOutput.java b/library/common/src/main/java/com/google/android/exoplayer2/metadata/MetadataOutput.java index b635cbc4b2..d203ffd801 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/metadata/MetadataOutput.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/metadata/MetadataOutput.java @@ -15,9 +15,7 @@ */ package com.google.android.exoplayer2.metadata; -/** - * Receives metadata output. - */ +/** Receives metadata output. */ public interface MetadataOutput { /** @@ -26,5 +24,4 @@ public interface MetadataOutput { * @param metadata The metadata. */ void onMetadata(Metadata metadata); - } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 968d4a728f..a5e6a1efca 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -21,8 +21,11 @@ import android.os.Looper; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.analytics.AnalyticsCollector; +import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioCapabilities; +import com.google.android.exoplayer2.audio.AudioListener; import com.google.android.exoplayer2.audio.AudioSink; +import com.google.android.exoplayer2.audio.AuxEffectInfo; import com.google.android.exoplayer2.audio.DefaultAudioSink; import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer; import com.google.android.exoplayer2.metadata.MetadataRenderer; @@ -126,6 +129,95 @@ import java.util.List; */ public interface ExoPlayer extends Player { + /** The audio component of an {@link ExoPlayer}. */ + interface AudioComponent { + + /** + * Adds a listener to receive audio events. + * + * @param listener The listener to register. + */ + void addAudioListener(AudioListener listener); + + /** + * Removes a listener of audio events. + * + * @param listener The listener to unregister. + */ + void removeAudioListener(AudioListener listener); + + /** + * 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. + * + *

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. + * + *

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. + * + *

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)}. + * + *

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); + + /** Returns the attributes for audio playback. */ + AudioAttributes getAudioAttributes(); + + /** + * Sets the ID of the audio session to attach to the underlying {@link + * android.media.AudioTrack}. + * + *

The audio session ID can be generated using {@link C#generateAudioSessionIdV21(Context)} + * for API 21+. + * + * @param audioSessionId The audio session ID, or {@link C#AUDIO_SESSION_ID_UNSET} if it should + * be generated by the framework. + */ + void setAudioSessionId(int audioSessionId); + + /** Returns the audio session identifier, or {@link C#AUDIO_SESSION_ID_UNSET} if not set. */ + int getAudioSessionId(); + + /** Sets information on an auxiliary audio effect to attach to the underlying audio track. */ + void setAuxEffectInfo(AuxEffectInfo auxEffectInfo); + + /** Detaches any previously attached auxiliary audio effect from the underlying audio track. */ + void clearAuxEffectInfo(); + + /** + * Sets the audio volume, with 0 being silence and 1 being unity gain (signal unchanged). + * + * @param audioVolume Linear output gain to apply to all audio channels. + */ + void setVolume(float audioVolume); + + /** + * Returns the audio volume, with 0 being silence and 1 being unity gain (signal unchanged). + * + * @return The linear gain applied to all audio channels. + */ + float getVolume(); + + /** + * Sets whether skipping silences in the audio stream is enabled. + * + * @param skipSilenceEnabled Whether skipping silences in the audio stream is enabled. + */ + void setSkipSilenceEnabled(boolean skipSilenceEnabled); + + /** Returns whether skipping silences in the audio stream is enabled. */ + boolean getSkipSilenceEnabled(); + } + /** * The default timeout for calls to {@link #release} and {@link #setForegroundMode}, in * milliseconds. @@ -465,6 +557,10 @@ public interface ExoPlayer extends Player { } } + /** Returns the component of this player for audio output, or null if audio is not supported. */ + @Nullable + AudioComponent getAudioComponent(); + /** * Adds a listener to receive audio offload events. * diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 1e648e03a6..f41f8dfbf3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -28,6 +28,7 @@ import android.util.Pair; import androidx.annotation.Nullable; import com.google.android.exoplayer2.PlayerMessage.Target; import com.google.android.exoplayer2.analytics.AnalyticsCollector; +import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; @@ -985,6 +986,22 @@ import java.util.concurrent.CopyOnWriteArraySet; return playbackInfo.timeline; } + /** This method is not supported and returns {@link AudioAttributes#DEFAULT}. */ + @Override + public AudioAttributes getAudioAttributes() { + return AudioAttributes.DEFAULT; + } + + /** This method is not supported and does nothing. */ + @Override + public void setVolume(float audioVolume) {} + + /** This method is not supported and returns 1. */ + @Override + public float getVolume() { + return 1; + } + private int getCurrentWindowIndexInternal() { if (playbackInfo.timeline.isEmpty()) { return maskingWindowIndex; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index f7e53ba2e7..20e0ab0c1a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -78,7 +78,7 @@ import java.util.concurrent.TimeoutException; */ public class SimpleExoPlayer extends BasePlayer implements ExoPlayer, - Player.AudioComponent, + ExoPlayer.AudioComponent, Player.VideoComponent, Player.TextComponent, Player.MetadataComponent, @@ -676,6 +676,7 @@ public class SimpleExoPlayer extends BasePlayer Commands additionalPermanentAvailableCommands = new Commands.Builder() .addAll( + COMMAND_GET_AUDIO_ATTRIBUTES, COMMAND_GET_VOLUME, COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_VOLUME, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 9011d0a102..666d0d4e65 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2; import static com.google.android.exoplayer2.Player.COMMAND_ADJUST_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_CHANGE_MEDIA_ITEMS; +import static com.google.android.exoplayer2.Player.COMMAND_GET_AUDIO_ATTRIBUTES; import static com.google.android.exoplayer2.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.COMMAND_GET_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS; @@ -8086,6 +8087,7 @@ public final class ExoPlayerTest { assertThat(player.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS_METADATA)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_VOLUME)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_VOLUME)).isTrue(); @@ -10275,6 +10277,7 @@ public final class ExoPlayerTest { COMMAND_GET_MEDIA_ITEMS, COMMAND_GET_MEDIA_ITEMS_METADATA, COMMAND_CHANGE_MEDIA_ITEMS, + COMMAND_GET_AUDIO_ATTRIBUTES, COMMAND_GET_VOLUME, COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_VOLUME, diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index 79fefcaccc..ccf6a1a250 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -27,6 +27,7 @@ import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.PlayerMessage; import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.ShuffleOrder; @@ -422,6 +423,21 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } + @Override + public AudioAttributes getAudioAttributes() { + throw new UnsupportedOperationException(); + } + + @Override + public void setVolume(float audioVolume) { + throw new UnsupportedOperationException(); + } + + @Override + public float getVolume() { + throw new UnsupportedOperationException(); + } + @Override public void setForegroundMode(boolean foregroundMode) { throw new UnsupportedOperationException();