diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index ead783827e..b3f981986e 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -1094,7 +1094,7 @@ public final class CastPlayer extends BasePlayer { private void updateAvailableCommandsAndNotifyIfChanged() { Commands previousAvailableCommands = availableCommands; - availableCommands = getAvailableCommands(PERMANENT_AVAILABLE_COMMANDS); + availableCommands = Util.getAvailableCommands(/* player= */ this, PERMANENT_AVAILABLE_COMMANDS); if (!availableCommands.equals(previousAvailableCommands)) { listeners.queueEvent( Player.EVENT_AVAILABLE_COMMANDS_CHANGED, diff --git a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java index 44926b25fc..f79df0ff90 100644 --- a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java @@ -384,37 +384,6 @@ public abstract class BasePlayer implements Player { : timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs(); } - /** - * Returns the {@link Commands} available in the player. - * - * @param permanentAvailableCommands The commands permanently available in the player. - * @return The available {@link Commands}. - */ - protected Commands getAvailableCommands(Commands permanentAvailableCommands) { - return new Commands.Builder() - .addAll(permanentAvailableCommands) - .addIf(COMMAND_SEEK_TO_DEFAULT_POSITION, !isPlayingAd()) - .addIf(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, isCurrentMediaItemSeekable() && !isPlayingAd()) - .addIf(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, hasPreviousMediaItem() && !isPlayingAd()) - .addIf( - COMMAND_SEEK_TO_PREVIOUS, - !getCurrentTimeline().isEmpty() - && (hasPreviousMediaItem() - || !isCurrentMediaItemLive() - || isCurrentMediaItemSeekable()) - && !isPlayingAd()) - .addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNextMediaItem() && !isPlayingAd()) - .addIf( - COMMAND_SEEK_TO_NEXT, - !getCurrentTimeline().isEmpty() - && (hasNextMediaItem() || (isCurrentMediaItemLive() && isCurrentMediaItemDynamic())) - && !isPlayingAd()) - .addIf(COMMAND_SEEK_TO_MEDIA_ITEM, !isPlayingAd()) - .addIf(COMMAND_SEEK_BACK, isCurrentMediaItemSeekable() && !isPlayingAd()) - .addIf(COMMAND_SEEK_FORWARD, isCurrentMediaItemSeekable() && !isPlayingAd()) - .build(); - } - @RepeatMode private int getRepeatModeForNavigation() { @RepeatMode int repeatMode = getRepeatMode(); diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java index 548e759424..9246d37c6d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java @@ -16,6 +16,15 @@ package androidx.media3.common.util; import static android.content.Context.UI_MODE_SERVICE; +import static androidx.media3.common.Player.COMMAND_SEEK_BACK; +import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD; +import static androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM; +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.util.Assertions.checkNotNull; import static java.lang.Math.abs; import static java.lang.Math.max; @@ -64,6 +73,8 @@ import androidx.media3.common.MediaLibraryInfo; import androidx.media3.common.MimeTypes; import androidx.media3.common.ParserException; import androidx.media3.common.PlaybackException; +import androidx.media3.common.Player; +import androidx.media3.common.Player.Commands; import com.google.common.base.Ascii; import com.google.common.base.Charsets; import java.io.ByteArrayOutputStream; @@ -2479,6 +2490,43 @@ public final class Util { } } + /** + * Returns the {@link Commands} available in the {@link Player}. + * + * @param player The {@link Player}. + * @param permanentAvailableCommands The commands permanently available in the player. + * @return The available {@link Commands}. + */ + public static Commands getAvailableCommands(Player player, Commands permanentAvailableCommands) { + boolean isPlayingAd = player.isPlayingAd(); + boolean isCurrentMediaItemSeekable = player.isCurrentMediaItemSeekable(); + boolean hasPreviousMediaItem = player.hasPreviousMediaItem(); + boolean hasNextMediaItem = player.hasNextMediaItem(); + boolean isCurrentMediaItemLive = player.isCurrentMediaItemLive(); + boolean isCurrentMediaItemDynamic = player.isCurrentMediaItemDynamic(); + boolean isTimelineEmpty = player.getCurrentTimeline().isEmpty(); + return new Commands.Builder() + .addAll(permanentAvailableCommands) + .addIf(COMMAND_SEEK_TO_DEFAULT_POSITION, !isPlayingAd) + .addIf(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, isCurrentMediaItemSeekable && !isPlayingAd) + .addIf(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, hasPreviousMediaItem && !isPlayingAd) + .addIf( + COMMAND_SEEK_TO_PREVIOUS, + !isTimelineEmpty + && (hasPreviousMediaItem || !isCurrentMediaItemLive || isCurrentMediaItemSeekable) + && !isPlayingAd) + .addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNextMediaItem && !isPlayingAd) + .addIf( + COMMAND_SEEK_TO_NEXT, + !isTimelineEmpty + && (hasNextMediaItem || (isCurrentMediaItemLive && isCurrentMediaItemDynamic)) + && !isPlayingAd) + .addIf(COMMAND_SEEK_TO_MEDIA_ITEM, !isPlayingAd) + .addIf(COMMAND_SEEK_BACK, isCurrentMediaItemSeekable && !isPlayingAd) + .addIf(COMMAND_SEEK_FORWARD, isCurrentMediaItemSeekable && !isPlayingAd) + .build(); + } + @Nullable private static String getSystemProperty(String name) { try { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index fd693e8274..c2c61cf953 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -15,6 +15,39 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS; +import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA; +import static androidx.media3.common.Player.COMMAND_GET_TIMELINE; +import static androidx.media3.common.Player.COMMAND_GET_TRACK_INFOS; +import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE; +import static androidx.media3.common.Player.COMMAND_PREPARE; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA; +import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE; +import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; +import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH; +import static androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS; +import static androidx.media3.common.Player.COMMAND_STOP; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_AUTO_TRANSITION; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_INTERNAL; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_REMOVE; +import static androidx.media3.common.Player.DISCONTINUITY_REASON_SEEK; +import static androidx.media3.common.Player.EVENT_MEDIA_METADATA_CHANGED; +import static androidx.media3.common.Player.EVENT_PLAYLIST_METADATA_CHANGED; +import static androidx.media3.common.Player.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED; +import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO; +import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED; +import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT; +import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_SEEK; +import static androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_NONE; +import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; +import static androidx.media3.common.Player.STATE_BUFFERING; +import static androidx.media3.common.Player.STATE_ENDED; +import static androidx.media3.common.Player.STATE_IDLE; +import static androidx.media3.common.Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED; +import static androidx.media3.common.Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Util.castNonNull; @@ -26,17 +59,10 @@ import android.media.metrics.LogSessionId; import android.os.Handler; import android.os.Looper; import android.util.Pair; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.TextureView; import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import androidx.media3.common.AudioAttributes; -import androidx.media3.common.BasePlayer; import androidx.media3.common.C; -import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; import androidx.media3.common.IllegalSeekPositionException; import androidx.media3.common.MediaItem; @@ -46,14 +72,23 @@ import androidx.media3.common.Metadata; import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; +import androidx.media3.common.Player.Commands; +import androidx.media3.common.Player.DiscontinuityReason; +import androidx.media3.common.Player.EventListener; +import androidx.media3.common.Player.Events; +import androidx.media3.common.Player.Listener; +import androidx.media3.common.Player.PlayWhenReadyChangeReason; +import androidx.media3.common.Player.PlaybackSuppressionReason; +import androidx.media3.common.Player.PositionInfo; +import androidx.media3.common.Player.RepeatMode; +import androidx.media3.common.Player.State; +import androidx.media3.common.Player.TimelineChangeReason; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackSelectionArray; import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TracksInfo; -import androidx.media3.common.VideoSize; -import androidx.media3.common.text.Cue; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Clock; import androidx.media3.common.util.HandlerWrapper; @@ -78,8 +113,8 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; -/** An {@link ExoPlayer} implementation. */ -/* package */ final class ExoPlayerImpl extends BasePlayer { +/** A helper class for the {@link SimpleExoPlayer} implementation of {@link ExoPlayer}. */ +/* package */ final class ExoPlayerImpl { static { MediaLibraryInfo.registerModule("media3.exoplayer"); @@ -97,6 +132,7 @@ import java.util.concurrent.CopyOnWriteArraySet; /* package */ final TrackSelectorResult emptyTrackSelectorResult; /* package */ final Commands permanentAvailableCommands; + private final Player wrappingPlayer; private final Renderer[] renderers; private final TrackSelector trackSelector; private final HandlerWrapper playbackInfoUpdateHandler; @@ -105,10 +141,11 @@ import java.util.concurrent.CopyOnWriteArraySet; private final ListenerSet listeners; private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; + private final Timeline.Window window; private final List mediaSourceHolderSnapshots; private final boolean useLazyPreparation; private final MediaSourceFactory mediaSourceFactory; - @Nullable private final AnalyticsCollector analyticsCollector; + private final AnalyticsCollector analyticsCollector; private final Looper applicationLooper; private final BandwidthMeter bandwidthMeter; private final long seekBackIncrementMs; @@ -154,16 +191,15 @@ import java.util.concurrent.CopyOnWriteArraySet; * loads and other initial preparation steps happen immediately. If true, these initial * preparations are triggered only when the player starts buffering the media. * @param seekParameters The {@link SeekParameters}. - * @param seekBackIncrementMs The {@link #seekBack()} increment in milliseconds. - * @param seekForwardIncrementMs The {@link #seekForward()} increment in milliseconds. + * @param seekBackIncrementMs The seek back increment in milliseconds. + * @param seekForwardIncrementMs The seek forward increment in milliseconds. * @param livePlaybackSpeedControl The {@link LivePlaybackSpeedControl}. * @param releaseTimeoutMs The timeout for calls to {@link #release()} in milliseconds. * @param pauseAtEndOfMediaItems Whether to pause playback at the end of each media item. * @param clock The {@link Clock}. * @param applicationLooper The {@link Looper} that must be used for all calls to the player and * which is used to call listeners on. - * @param wrappingPlayer The {@link Player} wrapping this one if applicable. This player instance - * should be used for all externally visible callbacks. + * @param wrappingPlayer The {@link Player} using this class. * @param additionalPermanentAvailableCommands The {@link Commands} that are permanently available * in the wrapping player but that are not in this player. */ @@ -174,7 +210,7 @@ import java.util.concurrent.CopyOnWriteArraySet; MediaSourceFactory mediaSourceFactory, LoadControl loadControl, BandwidthMeter bandwidthMeter, - @Nullable AnalyticsCollector analyticsCollector, + AnalyticsCollector analyticsCollector, boolean useLazyPreparation, SeekParameters seekParameters, long seekBackIncrementMs, @@ -184,7 +220,7 @@ import java.util.concurrent.CopyOnWriteArraySet; boolean pauseAtEndOfMediaItems, Clock clock, Looper applicationLooper, - @Nullable Player wrappingPlayer, + Player wrappingPlayer, Commands additionalPermanentAvailableCommands) { Log.i( TAG, @@ -208,13 +244,13 @@ import java.util.concurrent.CopyOnWriteArraySet; this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems; this.applicationLooper = applicationLooper; this.clock = clock; + this.wrappingPlayer = wrappingPlayer; repeatMode = Player.REPEAT_MODE_OFF; - Player playerForListeners = wrappingPlayer != null ? wrappingPlayer : this; listeners = new ListenerSet<>( applicationLooper, clock, - (listener, flags) -> listener.onEvents(playerForListeners, new Events(flags))); + (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); audioOffloadListeners = new CopyOnWriteArraySet<>(); mediaSourceHolderSnapshots = new ArrayList<>(); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); @@ -225,6 +261,7 @@ import java.util.concurrent.CopyOnWriteArraySet; TracksInfo.EMPTY, /* info= */ null); period = new Timeline.Period(); + window = new Timeline.Window(); permanentAvailableCommands = new Commands.Builder() .addAll( @@ -258,11 +295,9 @@ import java.util.concurrent.CopyOnWriteArraySet; playbackInfoUpdate -> playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); - if (analyticsCollector != null) { - analyticsCollector.setPlayer(playerForListeners, applicationLooper); - addListener(analyticsCollector); - bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); - } + analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); + addListener(analyticsCollector); + bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() : Api31.createPlayerId(); internalPlayer = new ExoPlayerImplInternal( @@ -310,7 +345,6 @@ import java.util.concurrent.CopyOnWriteArraySet; return internalPlayer.getPlaybackLooper(); } - @Override public Looper getApplicationLooper() { return applicationLooper; } @@ -319,16 +353,10 @@ import java.util.concurrent.CopyOnWriteArraySet; return clock; } - @Override public void addListener(Listener listener) { addEventListener(listener); } - @Override - public void removeListener(Listener listener) { - removeEventListener(listener); - } - @SuppressWarnings("deprecation") // Register deprecated EventListener. public void addEventListener(Player.EventListener eventListener) { listeners.add(eventListener); @@ -347,24 +375,20 @@ import java.util.concurrent.CopyOnWriteArraySet; audioOffloadListeners.remove(listener); } - @Override public Commands getAvailableCommands() { return availableCommands; } - @Override @State public int getPlaybackState() { return playbackInfo.playbackState; } - @Override @PlaybackSuppressionReason public int getPlaybackSuppressionReason() { return playbackInfo.playbackSuppressionReason; } - @Override @Nullable public ExoPlaybackException getPlayerError() { return playbackInfo.playbackError; @@ -376,7 +400,6 @@ import java.util.concurrent.CopyOnWriteArraySet; prepare(); } - @Override public void prepare() { if (playbackInfo.playbackState != Player.STATE_IDLE) { return; @@ -384,7 +407,7 @@ import java.util.concurrent.CopyOnWriteArraySet; PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackError(null); playbackInfo = playbackInfo.copyWithPlaybackState( - playbackInfo.timeline.isEmpty() ? Player.STATE_ENDED : Player.STATE_BUFFERING); + playbackInfo.timeline.isEmpty() ? STATE_ENDED : STATE_BUFFERING); // Trigger internal prepare first before updating the playback info and notifying external // listeners to ensure that new operations issued in the listener notifications reach the // player after this prepare. The internal player can't change the playback info immediately @@ -421,12 +444,10 @@ import java.util.concurrent.CopyOnWriteArraySet; prepare(); } - @Override public void setMediaItems(List mediaItems, boolean resetPosition) { setMediaSources(createMediaSources(mediaItems), resetPosition); } - @Override public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { setMediaSources(createMediaSources(mediaItems), startIndex, startPositionMs); } @@ -462,7 +483,6 @@ import java.util.concurrent.CopyOnWriteArraySet; mediaSources, startWindowIndex, startPositionMs, /* resetToDefaultPosition= */ false); } - @Override public void addMediaItems(int index, List mediaItems) { index = min(index, mediaSourceHolderSnapshots.size()); addMediaSources(index, createMediaSources(mediaItems)); @@ -503,7 +523,6 @@ import java.util.concurrent.CopyOnWriteArraySet; /* ignored */ C.INDEX_UNSET); } - @Override public void removeMediaItems(int fromIndex, int toIndex) { toIndex = min(toIndex, mediaSourceHolderSnapshots.size()); PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(fromIndex, toIndex); @@ -515,12 +534,11 @@ import java.util.concurrent.CopyOnWriteArraySet; /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, /* seekProcessed= */ false, positionDiscontinuity, - Player.DISCONTINUITY_REASON_REMOVE, + DISCONTINUITY_REASON_REMOVE, /* discontinuityWindowStartPositionUs= */ getCurrentPositionUsInternal(newPlaybackInfo), /* ignored */ C.INDEX_UNSET); } - @Override public void moveMediaItems(int fromIndex, int toIndex, int newFromIndex) { Assertions.checkArgument( fromIndex >= 0 @@ -571,14 +589,6 @@ import java.util.concurrent.CopyOnWriteArraySet; /* ignored */ C.INDEX_UNSET); } - @Override - public void setPlayWhenReady(boolean playWhenReady) { - setPlayWhenReady( - playWhenReady, - PLAYBACK_SUPPRESSION_REASON_NONE, - PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST); - } - public void setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems) { if (this.pauseAtEndOfMediaItems == pauseAtEndOfMediaItems) { return; @@ -614,12 +624,10 @@ import java.util.concurrent.CopyOnWriteArraySet; /* ignored */ C.INDEX_UNSET); } - @Override public boolean getPlayWhenReady() { return playbackInfo.playWhenReady; } - @Override public void setRepeatMode(@RepeatMode int repeatMode) { if (this.repeatMode != repeatMode) { this.repeatMode = repeatMode; @@ -631,12 +639,11 @@ import java.util.concurrent.CopyOnWriteArraySet; } } - @Override - public @RepeatMode int getRepeatMode() { + @RepeatMode + public int getRepeatMode() { return repeatMode; } - @Override public void setShuffleModeEnabled(boolean shuffleModeEnabled) { if (this.shuffleModeEnabled != shuffleModeEnabled) { this.shuffleModeEnabled = shuffleModeEnabled; @@ -649,17 +656,14 @@ import java.util.concurrent.CopyOnWriteArraySet; } } - @Override public boolean getShuffleModeEnabled() { return shuffleModeEnabled; } - @Override public boolean isLoading() { return playbackInfo.isLoading; } - @Override public void seekTo(int mediaItemIndex, long positionMs) { Timeline timeline = playbackInfo.timeline; if (mediaItemIndex < 0 @@ -680,7 +684,7 @@ import java.util.concurrent.CopyOnWriteArraySet; } @Player.State int newPlaybackState = - getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING; + getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : STATE_BUFFERING; int oldMaskingMediaItemIndex = getCurrentMediaItemIndex(); PlaybackInfo newPlaybackInfo = playbackInfo.copyWithPlaybackState(newPlaybackState); newPlaybackInfo = @@ -700,22 +704,18 @@ import java.util.concurrent.CopyOnWriteArraySet; oldMaskingMediaItemIndex); } - @Override public long getSeekBackIncrement() { return seekBackIncrementMs; } - @Override public long getSeekForwardIncrement() { return seekForwardIncrementMs; } - @Override public long getMaxSeekToPreviousPosition() { return C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS; } - @Override public void setPlaybackParameters(PlaybackParameters playbackParameters) { if (playbackParameters == null) { playbackParameters = PlaybackParameters.DEFAULT; @@ -737,7 +737,6 @@ import java.util.concurrent.CopyOnWriteArraySet; /* ignored */ C.INDEX_UNSET); } - @Override public PlaybackParameters getPlaybackParameters() { return playbackInfo.playbackParameters; } @@ -770,13 +769,6 @@ import java.util.concurrent.CopyOnWriteArraySet; } } - @Override - public void stop() { - stop(/* reset= */ false); - } - - @Deprecated - @Override public void stop(boolean reset) { stop(reset, /* error= */ null); } @@ -819,7 +811,6 @@ import java.util.concurrent.CopyOnWriteArraySet; /* ignored */ C.INDEX_UNSET); } - @Override public void release() { Log.i( TAG, @@ -844,9 +835,7 @@ import java.util.concurrent.CopyOnWriteArraySet; } listeners.release(); playbackInfoUpdateHandler.removeCallbacksAndMessages(null); - if (analyticsCollector != null) { - bandwidthMeter.removeEventListener(analyticsCollector); - } + bandwidthMeter.removeEventListener(analyticsCollector); playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(playbackInfo.periodId); playbackInfo.bufferedPositionUs = playbackInfo.positionUs; @@ -863,7 +852,6 @@ import java.util.concurrent.CopyOnWriteArraySet; internalPlayer.getPlaybackLooper()); } - @Override public int getCurrentPeriodIndex() { if (playbackInfo.timeline.isEmpty()) { return maskingPeriodIndex; @@ -872,13 +860,11 @@ import java.util.concurrent.CopyOnWriteArraySet; } } - @Override public int getCurrentMediaItemIndex() { int currentWindowIndex = getCurrentWindowIndexInternal(); return currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex; } - @Override public long getDuration() { if (isPlayingAd()) { MediaPeriodId periodId = playbackInfo.periodId; @@ -889,12 +875,17 @@ import java.util.concurrent.CopyOnWriteArraySet; return getContentDuration(); } - @Override + private long getContentDuration() { + Timeline timeline = getCurrentTimeline(); + return timeline.isEmpty() + ? C.TIME_UNSET + : timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs(); + } + public long getCurrentPosition() { return Util.usToMs(getCurrentPositionUsInternal(playbackInfo)); } - @Override public long getBufferedPosition() { if (isPlayingAd()) { return playbackInfo.loadingMediaPeriodId.equals(playbackInfo.periodId) @@ -904,27 +895,22 @@ import java.util.concurrent.CopyOnWriteArraySet; return getContentBufferedPosition(); } - @Override public long getTotalBufferedDuration() { return Util.usToMs(playbackInfo.totalBufferedDurationUs); } - @Override public boolean isPlayingAd() { return playbackInfo.periodId.isAd(); } - @Override public int getCurrentAdGroupIndex() { return isPlayingAd() ? playbackInfo.periodId.adGroupIndex : C.INDEX_UNSET; } - @Override public int getCurrentAdIndexInAdGroup() { return isPlayingAd() ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET; } - @Override public long getContentPosition() { if (isPlayingAd()) { playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period); @@ -939,7 +925,6 @@ import java.util.concurrent.CopyOnWriteArraySet; } } - @Override public long getContentBufferedPosition() { if (playbackInfo.timeline.isEmpty()) { return maskingWindowPositionMs; @@ -971,32 +956,26 @@ import java.util.concurrent.CopyOnWriteArraySet; return renderers[index].getTrackType(); } - @Nullable public TrackSelector getTrackSelector() { return trackSelector; } - @Override public TrackGroupArray getCurrentTrackGroups() { return playbackInfo.trackGroups; } - @Override public TrackSelectionArray getCurrentTrackSelections() { return new TrackSelectionArray(playbackInfo.trackSelectorResult.selections); } - @Override public TracksInfo getCurrentTracksInfo() { return playbackInfo.trackSelectorResult.tracksInfo; } - @Override public TrackSelectionParameters getTrackSelectionParameters() { return trackSelector.getParameters(); } - @Override public void setTrackSelectionParameters(TrackSelectionParameters parameters) { if (!trackSelector.isSetParametersSupported() || parameters.equals(trackSelector.getParameters())) { @@ -1008,7 +987,6 @@ import java.util.concurrent.CopyOnWriteArraySet; listener -> listener.onTrackSelectionParametersChanged(parameters)); } - @Override public MediaMetadata getMediaMetadata() { return mediaMetadata; } @@ -1027,12 +1005,10 @@ import java.util.concurrent.CopyOnWriteArraySet; EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } - @Override public MediaMetadata getPlaylistMetadata() { return playlistMetadata; } - @Override public void setPlaylistMetadata(MediaMetadata playlistMetadata) { checkNotNull(playlistMetadata); if (playlistMetadata.equals(this.playlistMetadata)) { @@ -1044,109 +1020,10 @@ import java.util.concurrent.CopyOnWriteArraySet; listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); } - @Override public Timeline getCurrentTimeline() { 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 volume) {} - - /** This method is not supported and returns 1. */ - @Override - public float getVolume() { - return 1; - } - - /** This method is not supported and does nothing. */ - @Override - public void clearVideoSurface() {} - - /** This method is not supported and does nothing. */ - @Override - public void clearVideoSurface(@Nullable Surface surface) {} - - /** This method is not supported and does nothing. */ - @Override - public void setVideoSurface(@Nullable Surface surface) {} - - /** This method is not supported and does nothing. */ - @Override - public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {} - - /** This method is not supported and does nothing. */ - @Override - public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {} - - /** This method is not supported and does nothing. */ - @Override - public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) {} - - /** This method is not supported and does nothing. */ - @Override - public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) {} - - /** This method is not supported and does nothing. */ - @Override - public void setVideoTextureView(@Nullable TextureView textureView) {} - - /** This method is not supported and does nothing. */ - @Override - public void clearVideoTextureView(@Nullable TextureView textureView) {} - - /** This method is not supported and returns {@link VideoSize#UNKNOWN}. */ - @Override - public VideoSize getVideoSize() { - return VideoSize.UNKNOWN; - } - - /** This method is not supported and returns an empty list. */ - @Override - public ImmutableList getCurrentCues() { - return ImmutableList.of(); - } - - /** This method is not supported and always returns {@link DeviceInfo#UNKNOWN}. */ - @Override - public DeviceInfo getDeviceInfo() { - return DeviceInfo.UNKNOWN; - } - - /** This method is not supported and always returns {@code 0}. */ - @Override - public int getDeviceVolume() { - return 0; - } - - /** This method is not supported and always returns {@link false}. */ - @Override - public boolean isDeviceMuted() { - return false; - } - - /** This method is not supported and does nothing. */ - @Override - public void setDeviceVolume(int volume) {} - - /** This method is not supported and does nothing. */ - @Override - public void increaseDeviceVolume() {} - - /** This method is not supported and does nothing. */ - @Override - public void decreaseDeviceVolume() {} - - /** This method is not supported and does nothing. */ - @Override - public void setDeviceMuted(boolean muted) {} - private int getCurrentWindowIndexInternal() { if (playbackInfo.timeline.isEmpty()) { return maskingWindowIndex; @@ -1329,7 +1206,7 @@ import java.util.concurrent.CopyOnWriteArraySet; if (metadataChanged) { final MediaMetadata finalMediaMetadata = mediaMetadata; listeners.queueEvent( - Player.EVENT_MEDIA_METADATA_CHANGED, + EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); } if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { @@ -1537,7 +1414,7 @@ import java.util.concurrent.CopyOnWriteArraySet; private void updateAvailableCommands() { Commands previousAvailableCommands = availableCommands; - availableCommands = getAvailableCommands(permanentAvailableCommands); + availableCommands = Util.getAvailableCommands(wrappingPlayer, permanentAvailableCommands); if (!availableCommands.equals(previousAvailableCommands)) { listeners.queueEvent( Player.EVENT_AVAILABLE_COMMANDS_CHANGED, @@ -1599,7 +1476,7 @@ import java.util.concurrent.CopyOnWriteArraySet; /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, /* seekProcessed= */ false, /* positionDiscontinuity= */ positionDiscontinuity, - Player.DISCONTINUITY_REASON_REMOVE, + DISCONTINUITY_REASON_REMOVE, /* discontinuityWindowStartPositionUs= */ getCurrentPositionUsInternal(newPlaybackInfo), /* ignored */ C.INDEX_UNSET); } @@ -1838,12 +1715,11 @@ import java.util.concurrent.CopyOnWriteArraySet; * #onMetadata(Metadata)}) sources. */ private MediaMetadata buildUpdatedMediaMetadata() { - @Nullable MediaItem mediaItem = getCurrentMediaItem(); - - if (mediaItem == null) { + Timeline timeline = getCurrentTimeline(); + if (timeline.isEmpty()) { return staticAndDynamicMediaMetadata; } - + MediaItem mediaItem = timeline.getWindow(getCurrentMediaItemIndex(), window).mediaItem; // MediaItem metadata is prioritized over metadata within the media. return staticAndDynamicMediaMetadata.buildUpon().populate(mediaItem.mediaMetadata).build(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index ed8ee0d4a1..ff97399d12 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -1362,7 +1362,6 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @Nullable public TrackSelector getTrackSelector() { verifyApplicationThread(); return player.getTrackSelector();