public interface

ExoPlayer

implements Player

 androidx.media3.exoplayer.ExoPlayer

Subclasses:

SimpleExoPlayer, StubExoPlayer

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-exoplayer:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

An extensible media player that plays MediaSources. Instances can be obtained from ExoPlayer.Builder.

Player components

ExoPlayer is designed to make few assumptions about (and hence impose few restrictions on) the type of the media being played, how and where it is stored, and how it is rendered. Rather than implementing the loading and rendering of media directly, ExoPlayer implementations delegate this work to components that are injected when a player is created or when it's prepared for playback. Components common to all ExoPlayer implementations are:

An ExoPlayer can be built using the default components provided by the library, but may also be built using custom implementations if non-standard behaviors are required. For example a custom LoadControl could be injected to change the player's buffering strategy, or a custom Renderer could be injected to add support for a video codec not supported natively by Android.

The concept of injecting components that implement pieces of player functionality is present throughout the library. The default component implementations listed above delegate work to further injected components. This allows many sub-components to be individually replaced with custom implementations. For example the default MediaSource implementations require one or more DataSource factories to be injected via their constructors. By providing a custom factory it's possible to load data from a non-standard source, or through a different network stack.

Threading model

The figure below shows ExoPlayer's threading model.

ExoPlayer's threading model

  • ExoPlayer instances must be accessed from a single application thread unless indicated otherwise. For the vast majority of cases this should be the application's main thread. Using the application's main thread is also a requirement when using ExoPlayer's UI components or the IMA extension. The thread on which an ExoPlayer instance must be accessed can be explicitly specified by passing a when creating the player. If no Looper is specified, then the Looper of the thread that the player is created on is used, or if that thread does not have a Looper, the Looper of the application's main thread is used. In all cases the Looper of the thread from which the player must be accessed can be queried using Player.getApplicationLooper().
  • Registered listeners are called on the thread associated with Player.getApplicationLooper(). Note that this means registered listeners are called on the same thread which must be used to access the player.
  • An internal playback thread is responsible for playback. Injected player components such as Renderers, MediaSources, TrackSelectors and LoadControls are called by the player on this thread.
  • When the application performs an operation on the player, for example a seek, a message is delivered to the internal playback thread via a message queue. The internal playback thread consumes messages from the queue and performs the corresponding operations. Similarly, when a playback event occurs on the internal playback thread, a message is delivered to the application thread via a second message queue. The application thread consumes messages from the queue, updating the application visible state and calling corresponding listener methods.
  • Injected player components may use additional background threads. For example a MediaSource may use background threads to load data. These are implementation specific.

Summary

Fields
public static final longDEFAULT_DETACH_SURFACE_TIMEOUT_MS

The default timeout for detaching a surface from the player, in milliseconds.

public static final longDEFAULT_RELEASE_TIMEOUT_MS

The default timeout for calls to ExoPlayer.release() and ExoPlayer.setForegroundMode(boolean), in milliseconds.

Methods
public voidaddAnalyticsListener(AnalyticsListener listener)

Adds an AnalyticsListener to receive analytics events.

public voidaddAudioOffloadListener(ExoPlayer.AudioOffloadListener listener)

Adds a listener to receive audio offload events.

public voidaddMediaSource(int index, MediaSource mediaSource)

Adds a media source at the given index of the playlist.

public voidaddMediaSource(MediaSource mediaSource)

Adds a media source to the end of the playlist.

public voidaddMediaSources(int index, java.util.List<MediaSource> mediaSources)

Adds a list of media sources at the given index of the playlist.

public voidaddMediaSources(java.util.List<MediaSource> mediaSources)

Adds a list of media sources to the end of the playlist.

public voidclearAuxEffectInfo()

Detaches any previously attached auxiliary audio effect from the underlying audio track.

public voidclearCameraMotionListener(CameraMotionListener listener)

Clears the listener which receives camera motion events if it matches the one passed.

public voidclearVideoFrameMetadataListener(VideoFrameMetadataListener listener)

Clears the listener which receives video frame metadata events if it matches the one passed.

public PlayerMessagecreateMessage(PlayerMessage.Target target)

Creates a message that can be sent to a PlayerMessage.Target.

public AnalyticsCollectorgetAnalyticsCollector()

Returns the AnalyticsCollector used for collecting analytics events.

public ExoPlayer.AudioComponentgetAudioComponent()

public DecoderCountersgetAudioDecoderCounters()

Returns DecoderCounters for audio, or null if no audio is being played.

public FormatgetAudioFormat()

Returns the audio format currently being played, or null if no audio is being played.

public intgetAudioSessionId()

Returns the audio session identifier, or C.AUDIO_SESSION_ID_UNSET if not set.

public ClockgetClock()

Returns the Clock used for playback.

public TrackGroupArraygetCurrentTrackGroups()

Returns the available track groups.

public TrackSelectionArraygetCurrentTrackSelections()

Returns the current track selections for each renderer, which may include null elements if some renderers do not have any selected tracks.

public ExoPlayer.DeviceComponentgetDeviceComponent()

public booleangetPauseAtEndOfMediaItems()

Returns whether the player pauses playback at the end of each media item.

public LoopergetPlaybackLooper()

Returns the associated with the playback thread.

public ExoPlaybackExceptiongetPlayerError()

Equivalent to Player.getPlayerError(), except the exception is guaranteed to be an ExoPlaybackException.

public ExoPlayer.PreloadConfigurationgetPreloadConfiguration()

Returns the preload configuration.

public RenderergetRenderer(int index)

Returns the renderer at the given index.

public intgetRendererCount()

Returns the number of renderers.

public intgetRendererType(int index)

Returns the track type that the renderer at a given index handles.

public SeekParametersgetSeekParameters()

Returns the currently active SeekParameters of the player.

public booleangetSkipSilenceEnabled()

Returns whether skipping silences in the audio stream is enabled.

public ExoPlayer.TextComponentgetTextComponent()

public TrackSelectorgetTrackSelector()

Returns the track selector that this player uses, or null if track selection is not supported.

public intgetVideoChangeFrameRateStrategy()

Returns the .

public ExoPlayer.VideoComponentgetVideoComponent()

public DecoderCountersgetVideoDecoderCounters()

Returns DecoderCounters for video, or null if no video is being played.

public FormatgetVideoFormat()

Returns the video format currently being played, or null if no video is being played.

public intgetVideoScalingMode()

Returns the .

public booleanisReleased()

Returns whether ExoPlayer.release() has been called on the player.

public booleanisSleepingForOffload()

Returns whether the player has paused its main loop to save power in offload scheduling mode.

public booleanisTunnelingEnabled()

Returns whether tunneling is enabled for the currently selected tracks.

public voidprepare(MediaSource mediaSource)

public voidprepare(MediaSource mediaSource, boolean resetPosition, boolean resetState)

public voidrelease()

public voidremoveAnalyticsListener(AnalyticsListener listener)

Removes an AnalyticsListener.

public voidremoveAudioOffloadListener(ExoPlayer.AudioOffloadListener listener)

Removes a listener of audio offload events.

public voidreplaceMediaItem(int index, MediaItem mediaItem)

public voidreplaceMediaItems(int fromIndex, int toIndex, java.util.List<MediaItem> mediaItems)

public voidsetAudioSessionId(int audioSessionId)

Sets the ID of the audio session to attach to the underlying .

public voidsetAuxEffectInfo(AuxEffectInfo auxEffectInfo)

Sets information on an auxiliary audio effect to attach to the underlying audio track.

public voidsetCameraMotionListener(CameraMotionListener listener)

Sets a listener of camera motion events.

public voidsetForegroundMode(boolean foregroundMode)

Sets whether the player is allowed to keep holding limited resources such as video decoders, even when in the idle state.

public voidsetHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy)

Sets whether the player should pause automatically when audio is rerouted from a headset to device speakers.

public voidsetImageOutput(ImageOutput imageOutput)

Sets the ImageOutput where rendered images will be forwarded.

public voidsetMediaSource(MediaSource mediaSource)

Clears the playlist, adds the specified MediaSource and resets the position to the default position.

public voidsetMediaSource(MediaSource mediaSource, boolean resetPosition)

Clears the playlist and adds the specified MediaSource.

public voidsetMediaSource(MediaSource mediaSource, long startPositionMs)

Clears the playlist and adds the specified MediaSource.

public voidsetMediaSources(java.util.List<MediaSource> mediaSources)

Clears the playlist, adds the specified MediaSources and resets the position to the default position.

public voidsetMediaSources(java.util.List<MediaSource> mediaSources, boolean resetPosition)

Clears the playlist and adds the specified MediaSources.

public voidsetMediaSources(java.util.List<MediaSource> mediaSources, int startMediaItemIndex, long startPositionMs)

Clears the playlist and adds the specified MediaSources.

public voidsetPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems)

Sets whether to pause playback at the end of each media item.

public voidsetPreferredAudioDevice(AudioDeviceInfo audioDeviceInfo)

Sets the preferred audio device.

public voidsetPreloadConfiguration(ExoPlayer.PreloadConfiguration preloadConfiguration)

Sets the preload configuration to configure playlist preloading.

public voidsetPriority(int priority)

Sets the for this player.

public voidsetPriorityTaskManager(PriorityTaskManager priorityTaskManager)

Sets a PriorityTaskManager, or null to clear a previously set priority task manager.

public voidsetSeekParameters(SeekParameters seekParameters)

Sets the parameters that control how seek operations are performed.

public voidsetShuffleOrder(ShuffleOrder shuffleOrder)

Sets the shuffle order.

public voidsetSkipSilenceEnabled(boolean skipSilenceEnabled)

Sets whether skipping silences in the audio stream is enabled.

public voidsetVideoChangeFrameRateStrategy(int videoChangeFrameRateStrategy)

Sets a that will be used by the player when provided with a video output .

public voidsetVideoEffects(java.util.List<Effect> videoEffects)

Sets a java.util.List of video effects that will be applied to each video frame.

public voidsetVideoFrameMetadataListener(VideoFrameMetadataListener listener)

Sets a listener to receive video frame metadata events.

public voidsetVideoScalingMode(int videoScalingMode)

Sets the .

public voidsetWakeMode(int wakeMode)

Sets how the player should keep the device awake for playback when the screen is off.

Fields

public static final long DEFAULT_RELEASE_TIMEOUT_MS

The default timeout for calls to ExoPlayer.release() and ExoPlayer.setForegroundMode(boolean), in milliseconds.

public static final long DEFAULT_DETACH_SURFACE_TIMEOUT_MS

The default timeout for detaching a surface from the player, in milliseconds.

Methods

public ExoPlaybackException getPlayerError()

Equivalent to Player.getPlayerError(), except the exception is guaranteed to be an ExoPlaybackException.

public ExoPlayer.AudioComponent getAudioComponent()

Deprecated: Use ExoPlayer, as the ExoPlayer.AudioComponent methods are defined by that interface.

public ExoPlayer.VideoComponent getVideoComponent()

Deprecated: Use ExoPlayer, as the ExoPlayer.VideoComponent methods are defined by that interface.

public ExoPlayer.TextComponent getTextComponent()

Deprecated: Use Player, as the ExoPlayer.TextComponent methods are defined by that interface.

public ExoPlayer.DeviceComponent getDeviceComponent()

Deprecated: Use Player, as the ExoPlayer.DeviceComponent methods are defined by that interface.

public void addAudioOffloadListener(ExoPlayer.AudioOffloadListener listener)

Adds a listener to receive audio offload events.

This method can be called from any thread.

Parameters:

listener: The listener to register.

public void removeAudioOffloadListener(ExoPlayer.AudioOffloadListener listener)

Removes a listener of audio offload events.

Parameters:

listener: The listener to unregister.

public AnalyticsCollector getAnalyticsCollector()

Returns the AnalyticsCollector used for collecting analytics events.

public void addAnalyticsListener(AnalyticsListener listener)

Adds an AnalyticsListener to receive analytics events.

This method can be called from any thread.

Parameters:

listener: The listener to be added.

public void removeAnalyticsListener(AnalyticsListener listener)

Removes an AnalyticsListener.

Parameters:

listener: The listener to be removed.

public int getRendererCount()

Returns the number of renderers.

public int getRendererType(int index)

Returns the track type that the renderer at a given index handles.

For example, a video renderer will return C.TRACK_TYPE_VIDEO, an audio renderer will return C.TRACK_TYPE_AUDIO and a text renderer will return C.TRACK_TYPE_TEXT.

Parameters:

index: The index of the renderer.

Returns:

The that the renderer handles.

public Renderer getRenderer(int index)

Returns the renderer at the given index.

Parameters:

index: The index of the renderer.

Returns:

The renderer at this index.

public TrackSelector getTrackSelector()

Returns the track selector that this player uses, or null if track selection is not supported.

public TrackGroupArray getCurrentTrackGroups()

Deprecated: Use Player.getCurrentTracks().

Returns the available track groups.

See also:

public TrackSelectionArray getCurrentTrackSelections()

Deprecated: Use Player.getCurrentTracks().

Returns the current track selections for each renderer, which may include null elements if some renderers do not have any selected tracks.

See also:

public Looper getPlaybackLooper()

Returns the associated with the playback thread.

This method may be called from any thread.

public Clock getClock()

Returns the Clock used for playback.

This method can be called from any thread.

public void prepare(MediaSource mediaSource)

Deprecated: Use ExoPlayer.setMediaSource(MediaSource) and Player.prepare() instead.

public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState)

Deprecated: Use ExoPlayer.setMediaSource(MediaSource, boolean) and Player.prepare() instead.

public void setMediaSources(java.util.List<MediaSource> mediaSources)

Clears the playlist, adds the specified MediaSources and resets the position to the default position.

Parameters:

mediaSources: The new MediaSources.

public void setMediaSources(java.util.List<MediaSource> mediaSources, boolean resetPosition)

Clears the playlist and adds the specified MediaSources.

Parameters:

mediaSources: The new MediaSources.
resetPosition: Whether the playback position should be reset to the default position in the first . If false, playback will start from the position defined by Player.getCurrentMediaItemIndex() and Player.getCurrentPosition().

public void setMediaSources(java.util.List<MediaSource> mediaSources, int startMediaItemIndex, long startPositionMs)

Clears the playlist and adds the specified MediaSources.

Parameters:

mediaSources: The new MediaSources.
startMediaItemIndex: The media item index to start playback from. If C.INDEX_UNSET is passed, the current position is not reset.
startPositionMs: The position in milliseconds to start playback from. If C.TIME_UNSET is passed, the default position of the given media source is used. In any case, if startMediaItemIndex is set to C.INDEX_UNSET, this parameter is ignored and the position is not reset at all.

public void setMediaSource(MediaSource mediaSource)

Clears the playlist, adds the specified MediaSource and resets the position to the default position.

Parameters:

mediaSource: The new MediaSource.

public void setMediaSource(MediaSource mediaSource, long startPositionMs)

Clears the playlist and adds the specified MediaSource.

Parameters:

mediaSource: The new MediaSource.
startPositionMs: The position in milliseconds to start playback from. If C.TIME_UNSET is passed, the default position of the given media source is used.

public void setMediaSource(MediaSource mediaSource, boolean resetPosition)

Clears the playlist and adds the specified MediaSource.

Parameters:

mediaSource: The new MediaSource.
resetPosition: Whether the playback position should be reset to the default position. If false, playback will start from the position defined by Player.getCurrentMediaItemIndex() and Player.getCurrentPosition().

public void addMediaSource(MediaSource mediaSource)

Adds a media source to the end of the playlist.

Parameters:

mediaSource: The MediaSource to add.

public void addMediaSource(int index, MediaSource mediaSource)

Adds a media source at the given index of the playlist.

Parameters:

index: The index at which to add the source.
mediaSource: The MediaSource to add.

public void addMediaSources(java.util.List<MediaSource> mediaSources)

Adds a list of media sources to the end of the playlist.

Parameters:

mediaSources: The MediaSources to add.

public void addMediaSources(int index, java.util.List<MediaSource> mediaSources)

Adds a list of media sources at the given index of the playlist.

Parameters:

index: The index at which to add the media sources.
mediaSources: The MediaSources to add.

public void setShuffleOrder(ShuffleOrder shuffleOrder)

Sets the shuffle order.

The ShuffleOrder passed must have the same length as the current playlist (Player.getMediaItemCount()).

Parameters:

shuffleOrder: The shuffle order.

public void setPreloadConfiguration(ExoPlayer.PreloadConfiguration preloadConfiguration)

Sets the preload configuration to configure playlist preloading.

Parameters:

preloadConfiguration: The preload configuration.

public ExoPlayer.PreloadConfiguration getPreloadConfiguration()

Returns the preload configuration.

public void replaceMediaItem(int index, MediaItem mediaItem)

ExoPlayer will keep the existing MediaSource for this MediaItem if supported by the MediaSource. If the current item is replaced, this will also not interrupt the ongoing playback.

public void replaceMediaItems(int fromIndex, int toIndex, java.util.List<MediaItem> mediaItems)

ExoPlayer will keep the existing MediaSource instances for the new MediaItems if supported by all of these MediaSource instances. If the current item is replaced, this will also not interrupt the ongoing playback.

public void setAudioSessionId(int audioSessionId)

Sets the ID of the audio session to attach to the underlying .

The audio session ID can be generated using Util.generateAudioSessionIdV21(Context) for API 21+.

Parameters:

audioSessionId: The audio session ID, or C.AUDIO_SESSION_ID_UNSET if it should be generated by the framework.

public int getAudioSessionId()

Returns the audio session identifier, or C.AUDIO_SESSION_ID_UNSET if not set.

See also:

public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo)

Sets information on an auxiliary audio effect to attach to the underlying audio track.

public void clearAuxEffectInfo()

Detaches any previously attached auxiliary audio effect from the underlying audio track.

public void setPreferredAudioDevice(AudioDeviceInfo audioDeviceInfo)

Sets the preferred audio device.

Parameters:

audioDeviceInfo: The preferred , or null to restore the default.

public void setSkipSilenceEnabled(boolean skipSilenceEnabled)

Sets whether skipping silences in the audio stream is enabled.

Parameters:

skipSilenceEnabled: Whether skipping silences in the audio stream is enabled.

public boolean getSkipSilenceEnabled()

Returns whether skipping silences in the audio stream is enabled.

See also:

public void setVideoEffects(java.util.List<Effect> videoEffects)

Sets a java.util.List of video effects that will be applied to each video frame.

If passing a surface to the player directly, the output resolution needs to be signaled by passing a message to the video renderer with type Renderer.MSG_SET_VIDEO_OUTPUT_RESOLUTION after calling this method. For , TextureView and SurfaceHolder output this happens automatically.

The following limitations exist for using video effects:

  • The androidx.media3:media3-effect module must be available on the runtime classpath. androidx.media3:media3-exoplayer does not explicitly depend on the effect module, so apps must make sure it's available themselves. It must be the same version as the rest of the androidx.media3 modules being used by the app.
  • This feature works only with the default MediaCodecVideoRenderer and not custom or extension video renderers.
  • This feature does not work with effects that update the frame timestamps.
  • This feature does not work with DRM-protected content.
  • This method must be called at least once before calling Player.prepare() (in order to set up the effects pipeline). The effects can be changed during playback by subsequent calls to this method after Player.prepare().

Parameters:

videoEffects: The java.util.List of video effects to apply.

public void setVideoScalingMode(int videoScalingMode)

Sets the .

The scaling mode only applies if a MediaCodec-based video Renderer is enabled and if the output surface is owned by a .

Parameters:

videoScalingMode: The .

public int getVideoScalingMode()

Returns the .

public void setVideoChangeFrameRateStrategy(int videoChangeFrameRateStrategy)

Sets a that will be used by the player when provided with a video output .

The strategy only applies if a MediaCodec-based video Renderer is enabled. Applications wishing to use should set the mode to C.VIDEO_CHANGE_FRAME_RATE_STRATEGY_OFF to disable calls to from ExoPlayer, and should then call directly from application code.

Parameters:

videoChangeFrameRateStrategy: A .

public int getVideoChangeFrameRateStrategy()

Returns the .

public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener)

Sets a listener to receive video frame metadata events.

This method is intended to be called by the same component that sets the onto which video will be rendered. If using ExoPlayer's standard UI components, this method should not be called directly from application code.

Parameters:

listener: The listener.

public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener)

Clears the listener which receives video frame metadata events if it matches the one passed. Else does nothing.

Parameters:

listener: The listener to clear.

public void setCameraMotionListener(CameraMotionListener listener)

Sets a listener of camera motion events.

Parameters:

listener: The listener.

public void clearCameraMotionListener(CameraMotionListener listener)

Clears the listener which receives camera motion events if it matches the one passed. Else does nothing.

Parameters:

listener: The listener to clear.

public PlayerMessage createMessage(PlayerMessage.Target target)

Creates a message that can be sent to a PlayerMessage.Target. By default, the message will be delivered immediately without blocking on the playback thread. The default PlayerMessage.getType() is 0 and the default PlayerMessage.getPayload() is null. If a position is specified with PlayerMessage.setPosition(long), the message will be delivered at this position in the current media item defined by Player.getCurrentMediaItemIndex(). Alternatively, the message can be sent at a specific mediaItem using PlayerMessage.setPosition(int, long).

public void setSeekParameters(SeekParameters seekParameters)

Sets the parameters that control how seek operations are performed.

Parameters:

seekParameters: The seek parameters, or null to use the defaults.

public SeekParameters getSeekParameters()

Returns the currently active SeekParameters of the player.

public void setForegroundMode(boolean foregroundMode)

Sets whether the player is allowed to keep holding limited resources such as video decoders, even when in the idle state. By doing so, the player may be able to reduce latency when starting to play another piece of content for which the same resources are required.

This mode should be used with caution, since holding limited resources may prevent other players of media components from acquiring them. It should only be enabled when both of the following conditions are true:

  • The application that owns the player is in the foreground.
  • The player is used in a way that may benefit from foreground mode. For this to be true, the same player instance must be used to play multiple pieces of content, and there must be gaps between the playbacks (i.e. Player.stop() is called to halt one playback, and ExoPlayer.prepare(MediaSource) is called some time later to start a new one).

Note that foreground mode is not useful for switching between content without gaps between the playbacks. For this use case Player.stop() does not need to be called, and simply calling ExoPlayer.prepare(MediaSource) for the new media will cause limited resources to be retained even if foreground mode is not enabled.

If foreground mode is enabled, it's the application's responsibility to disable it when the conditions described above no longer hold.

Parameters:

foregroundMode: Whether the player is allowed to keep limited resources even when in the idle state.

public void setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems)

Sets whether to pause playback at the end of each media item.

This means the player will pause at the end of each window in the current timeline. Listeners will be informed by a call to with the reason Player.PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM when this happens.

Parameters:

pauseAtEndOfMediaItems: Whether to pause playback at the end of each media item.

public boolean getPauseAtEndOfMediaItems()

Returns whether the player pauses playback at the end of each media item.

See also: ExoPlayer.setPauseAtEndOfMediaItems(boolean)

public Format getAudioFormat()

Returns the audio format currently being played, or null if no audio is being played.

public Format getVideoFormat()

Returns the video format currently being played, or null if no video is being played.

public DecoderCounters getAudioDecoderCounters()

Returns DecoderCounters for audio, or null if no audio is being played.

public DecoderCounters getVideoDecoderCounters()

Returns DecoderCounters for video, or null if no video is being played.

public void setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy)

Sets whether the player should pause automatically when audio is rerouted from a headset to device speakers. See the audio becoming noisy documentation for more information.

Parameters:

handleAudioBecomingNoisy: Whether the player should pause automatically when audio is rerouted from a headset to device speakers.

public void setWakeMode(int wakeMode)

Sets how the player should keep the device awake for playback when the screen is off.

Enabling this feature requires the permission. It should be used together with a foreground for use cases where playback occurs and the screen is off (e.g. background audio playback). It is not useful when the screen will be kept on during playback (e.g. foreground video playback).

When enabled, the locks ( / ) will be held whenever the player is in the Player.STATE_READY or Player.STATE_BUFFERING states with playWhenReady = true. The locks held depends on the specified .

Parameters:

wakeMode: The option to keep the device awake during playback.

public void setPriority(int priority)

Sets the for this player.

The priority may influence resource allocation between multiple players or other components running in the same app.

This priority is used for the PriorityTaskManager, if set.

Parameters:

priority: The .

public void setPriorityTaskManager(PriorityTaskManager priorityTaskManager)

Sets a PriorityTaskManager, or null to clear a previously set priority task manager.

The priority set via ExoPlayer.setPriority(int) (or by default) will be set while the player is loading.

Parameters:

priorityTaskManager: The PriorityTaskManager, or null to clear a previously set priority task manager.

public boolean isSleepingForOffload()

Returns whether the player has paused its main loop to save power in offload scheduling mode.

Offload scheduling mode should save significant power when the phone is playing offload audio with the screen off.

Offload scheduling is only enabled when playing an audio track in offload mode, which requires all the following:

  • Audio offload rendering is enabled through .
  • An audio track is playing in a format that the device supports offloading (for example, MP3 or AAC).
  • The AudioSink is playing with an offload AudioTrack.

See also: ExoPlayer.AudioOffloadListener.onSleepingForOffloadChanged(boolean)

public boolean isTunnelingEnabled()

Returns whether tunneling is enabled for the currently selected tracks.

See also:

public void release()

The exception to the above rule is ExoPlayer.isReleased() which can be called on a released player.

public boolean isReleased()

Returns whether ExoPlayer.release() has been called on the player.

This method is allowed to be called after ExoPlayer.release().

public void setImageOutput(ImageOutput imageOutput)

Sets the ImageOutput where rendered images will be forwarded.

Parameters:

imageOutput: The ImageOutput. May be null to clear a previously set image output.

Source

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.exoplayer;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;

import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.AuxEffectInfo;
import androidx.media3.common.C;
import androidx.media3.common.DeviceInfo;
import androidx.media3.common.Effect;
import androidx.media3.common.Format;
import androidx.media3.common.MediaItem;
import androidx.media3.common.Player;
import androidx.media3.common.PriorityTaskManager;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.Tracks;
import androidx.media3.common.VideoSize;
import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSource;
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
import androidx.media3.exoplayer.analytics.AnalyticsListener;
import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.audio.AudioSink;
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
import androidx.media3.exoplayer.image.ImageOutput;
import androidx.media3.exoplayer.metadata.MetadataRenderer;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.ShuffleOrder;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.text.TextRenderer;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
import androidx.media3.exoplayer.trackselection.TrackSelector;
import androidx.media3.exoplayer.upstream.BandwidthMeter;
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
import androidx.media3.exoplayer.video.MediaCodecVideoRenderer;
import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
import androidx.media3.exoplayer.video.spherical.CameraMotionListener;
import androidx.media3.extractor.DefaultExtractorsFactory;
import androidx.media3.extractor.ExtractorsFactory;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.List;

/**
 * An extensible media player that plays {@link MediaSource}s. Instances can be obtained from {@link
 * Builder}.
 *
 * <h2>Player components</h2>
 *
 * <p>ExoPlayer is designed to make few assumptions about (and hence impose few restrictions on) the
 * type of the media being played, how and where it is stored, and how it is rendered. Rather than
 * implementing the loading and rendering of media directly, ExoPlayer implementations delegate this
 * work to components that are injected when a player is created or when it's prepared for playback.
 * Components common to all ExoPlayer implementations are:
 *
 * <ul>
 *   <li><b>{@link MediaSource MediaSources}</b> that define the media to be played, load the media,
 *       and from which the loaded media can be read. MediaSources are created from {@link MediaItem
 *       MediaItems} by the {@link MediaSource.Factory} injected into the player {@link
 *       Builder#setMediaSourceFactory Builder}, or can be added directly by methods like {@link
 *       #setMediaSource(MediaSource)}. The library provides a {@link DefaultMediaSourceFactory} for
 *       progressive media files, DASH, SmoothStreaming and HLS, which also includes functionality
 *       for side-loading subtitle files and clipping media.
 *   <li><b>{@link Renderer}</b>s that render individual components of the media. The library
 *       provides default implementations for common media types ({@link MediaCodecVideoRenderer},
 *       {@link MediaCodecAudioRenderer}, {@link TextRenderer} and {@link MetadataRenderer}). A
 *       Renderer consumes media from the MediaSource being played. Renderers are injected when the
 *       player is created. The number of renderers and their respective track types can be obtained
 *       by calling {@link #getRendererCount()} and {@link #getRendererType(int)}.
 *   <li>A <b>{@link TrackSelector}</b> that selects tracks provided by the MediaSource to be
 *       consumed by each of the available Renderers. The library provides a default implementation
 *       ({@link DefaultTrackSelector}) suitable for most use cases. A TrackSelector is injected
 *       when the player is created.
 *   <li>A <b>{@link LoadControl}</b> that controls when the MediaSource buffers more media, and how
 *       much media is buffered. The library provides a default implementation ({@link
 *       DefaultLoadControl}) suitable for most use cases. A LoadControl is injected when the player
 *       is created.
 * </ul>
 *
 * <p>An ExoPlayer can be built using the default components provided by the library, but may also
 * be built using custom implementations if non-standard behaviors are required. For example a
 * custom LoadControl could be injected to change the player's buffering strategy, or a custom
 * Renderer could be injected to add support for a video codec not supported natively by Android.
 *
 * <p>The concept of injecting components that implement pieces of player functionality is present
 * throughout the library. The default component implementations listed above delegate work to
 * further injected components. This allows many sub-components to be individually replaced with
 * custom implementations. For example the default MediaSource implementations require one or more
 * {@link DataSource} factories to be injected via their constructors. By providing a custom factory
 * it's possible to load data from a non-standard source, or through a different network stack.
 *
 * <h2>Threading model</h2>
 *
 * <p>The figure below shows ExoPlayer's threading model.
 *
 * <p style="align:center"><img
 * src="https://developer.android.com/static/images/reference/androidx/media3/exoplayer/exoplayer-threading-model.svg"
 * alt="ExoPlayer's threading model">
 *
 * <ul>
 *   <li>ExoPlayer instances must be accessed from a single application thread unless indicated
 *       otherwise. For the vast majority of cases this should be the application's main thread.
 *       Using the application's main thread is also a requirement when using ExoPlayer's UI
 *       components or the IMA extension. The thread on which an ExoPlayer instance must be accessed
 *       can be explicitly specified by passing a {@link Looper} when creating the player. If no
 *       {@code Looper} is specified, then the {@code Looper} of the thread that the player is
 *       created on is used, or if that thread does not have a {@code Looper}, the {@code Looper} of
 *       the application's main thread is used. In all cases the {@code Looper} of the thread from
 *       which the player must be accessed can be queried using {@link #getApplicationLooper()}.
 *   <li>Registered listeners are called on the thread associated with {@link
 *       #getApplicationLooper()}. Note that this means registered listeners are called on the same
 *       thread which must be used to access the player.
 *   <li>An internal playback thread is responsible for playback. Injected player components such as
 *       Renderers, MediaSources, TrackSelectors and LoadControls are called by the player on this
 *       thread.
 *   <li>When the application performs an operation on the player, for example a seek, a message is
 *       delivered to the internal playback thread via a message queue. The internal playback thread
 *       consumes messages from the queue and performs the corresponding operations. Similarly, when
 *       a playback event occurs on the internal playback thread, a message is delivered to the
 *       application thread via a second message queue. The application thread consumes messages
 *       from the queue, updating the application visible state and calling corresponding listener
 *       methods.
 *   <li>Injected player components may use additional background threads. For example a MediaSource
 *       may use background threads to load data. These are implementation specific.
 * </ul>
 */
public interface ExoPlayer extends Player {

  /**
   * @deprecated Use {@link ExoPlayer}, as all methods are defined by that interface.
   */
  @UnstableApi
  @Deprecated
  interface AudioComponent {

    /**
     * @deprecated Use {@link Player#setAudioAttributes(AudioAttributes, boolean)} instead.
     */
    @Deprecated
    void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus);

    /**
     * @deprecated Use {@link Player#getAudioAttributes()} instead.
     */
    @Deprecated
    AudioAttributes getAudioAttributes();

    /**
     * @deprecated Use {@link ExoPlayer#setAudioSessionId(int)} instead.
     */
    @Deprecated
    void setAudioSessionId(int audioSessionId);

    /**
     * @deprecated Use {@link ExoPlayer#getAudioSessionId()} instead.
     */
    @Deprecated
    int getAudioSessionId();

    /**
     * @deprecated Use {@link ExoPlayer#setAuxEffectInfo(AuxEffectInfo)} instead.
     */
    @Deprecated
    void setAuxEffectInfo(AuxEffectInfo auxEffectInfo);

    /**
     * @deprecated Use {@link ExoPlayer#clearAuxEffectInfo()} instead.
     */
    @Deprecated
    void clearAuxEffectInfo();

    /**
     * @deprecated Use {@link Player#setVolume(float)} instead.
     */
    @Deprecated
    void setVolume(float audioVolume);

    /**
     * @deprecated Use {@link Player#getVolume()} instead.
     */
    @Deprecated
    float getVolume();

    /**
     * @deprecated Use {@link ExoPlayer#setSkipSilenceEnabled(boolean)} instead.
     */
    @Deprecated
    void setSkipSilenceEnabled(boolean skipSilenceEnabled);

    /**
     * @deprecated Use {@link ExoPlayer#getSkipSilenceEnabled()} instead.
     */
    @Deprecated
    boolean getSkipSilenceEnabled();
  }

  /**
   * @deprecated Use {@link ExoPlayer}, as all methods are defined by that interface.
   */
  @UnstableApi
  @Deprecated
  interface VideoComponent {

    /**
     * @deprecated Use {@link ExoPlayer#setVideoScalingMode(int)} instead.
     */
    @Deprecated
    void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode);

    /**
     * @deprecated Use {@link ExoPlayer#getVideoScalingMode()} instead.
     */
    @Deprecated
    @C.VideoScalingMode
    int getVideoScalingMode();

    /**
     * @deprecated Use {@link ExoPlayer#setVideoChangeFrameRateStrategy(int)} instead.
     */
    @Deprecated
    void setVideoChangeFrameRateStrategy(
        @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy);

    /**
     * @deprecated Use {@link ExoPlayer#getVideoChangeFrameRateStrategy()} instead.
     */
    @Deprecated
    @C.VideoChangeFrameRateStrategy
    int getVideoChangeFrameRateStrategy();

    /**
     * @deprecated Use {@link ExoPlayer#setVideoFrameMetadataListener(VideoFrameMetadataListener)}
     *     instead.
     */
    @Deprecated
    void setVideoFrameMetadataListener(VideoFrameMetadataListener listener);

    /**
     * @deprecated Use {@link ExoPlayer#clearVideoFrameMetadataListener(VideoFrameMetadataListener)}
     *     instead.
     */
    @Deprecated
    void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener);

    /**
     * @deprecated Use {@link ExoPlayer#setCameraMotionListener(CameraMotionListener)} instead.
     */
    @Deprecated
    void setCameraMotionListener(CameraMotionListener listener);

    /**
     * @deprecated Use {@link ExoPlayer#clearCameraMotionListener(CameraMotionListener)} instead.
     */
    @Deprecated
    void clearCameraMotionListener(CameraMotionListener listener);

    /**
     * @deprecated Use {@link Player#clearVideoSurface()} instead.
     */
    @Deprecated
    void clearVideoSurface();

    /**
     * @deprecated Use {@link Player#clearVideoSurface(Surface)} instead.
     */
    @Deprecated
    void clearVideoSurface(@Nullable Surface surface);

    /**
     * @deprecated Use {@link Player#setVideoSurface(Surface)} instead.
     */
    @Deprecated
    void setVideoSurface(@Nullable Surface surface);

    /**
     * @deprecated Use {@link Player#setVideoSurfaceHolder(SurfaceHolder)} instead.
     */
    @Deprecated
    void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder);

    /**
     * @deprecated Use {@link Player#clearVideoSurfaceHolder(SurfaceHolder)} instead.
     */
    @Deprecated
    void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder);

    /**
     * @deprecated Use {@link Player#setVideoSurfaceView(SurfaceView)} instead.
     */
    @Deprecated
    void setVideoSurfaceView(@Nullable SurfaceView surfaceView);

    /**
     * @deprecated Use {@link Player#clearVideoSurfaceView(SurfaceView)} instead.
     */
    @Deprecated
    void clearVideoSurfaceView(@Nullable SurfaceView surfaceView);

    /**
     * @deprecated Use {@link Player#setVideoTextureView(TextureView)} instead.
     */
    @Deprecated
    void setVideoTextureView(@Nullable TextureView textureView);

    /**
     * @deprecated Use {@link Player#clearVideoTextureView(TextureView)} instead.
     */
    @Deprecated
    void clearVideoTextureView(@Nullable TextureView textureView);

    /**
     * @deprecated Use {@link Player#getVideoSize()} instead.
     */
    @Deprecated
    VideoSize getVideoSize();
  }

  /**
   * @deprecated Use {@link Player}, as all methods are defined by that interface.
   */
  @UnstableApi
  @Deprecated
  interface TextComponent {

    /**
     * @deprecated Use {@link Player#getCurrentCues()} instead.
     */
    @Deprecated
    CueGroup getCurrentCues();
  }

  /**
   * @deprecated Use {@link Player}, as all methods are defined by that interface.
   */
  @UnstableApi
  @Deprecated
  interface DeviceComponent {

    /**
     * @deprecated Use {@link Player#getDeviceInfo()} instead.
     */
    @Deprecated
    DeviceInfo getDeviceInfo();

    /**
     * @deprecated Use {@link Player#getDeviceVolume()} instead.
     */
    @Deprecated
    int getDeviceVolume();

    /**
     * @deprecated Use {@link Player#isDeviceMuted()} instead.
     */
    @Deprecated
    boolean isDeviceMuted();

    /**
     * @deprecated Use {@link Player#setDeviceVolume(int)} instead.
     */
    @Deprecated
    void setDeviceVolume(int volume);

    /**
     * @deprecated Use {@link Player#increaseDeviceVolume()} instead.
     */
    @Deprecated
    void increaseDeviceVolume();

    /**
     * @deprecated Use {@link Player#decreaseDeviceVolume()} instead.
     */
    @Deprecated
    void decreaseDeviceVolume();

    /**
     * @deprecated Use {@link Player#setDeviceMuted(boolean)} instead.
     */
    @Deprecated
    void setDeviceMuted(boolean muted);
  }

  /** A listener for audio offload events. */
  @UnstableApi
  interface AudioOffloadListener {
    /**
     * Called when the value of {@link #isSleepingForOffload} changes.
     *
     * <p>When {@code isSleepingForOffload} is {@code true} then, the player has paused its main
     * loop to save power in offload scheduling mode.
     */
    default void onSleepingForOffloadChanged(boolean isSleepingForOffload) {}

    /**
     * Called when the value of {@link AudioTrack#isOffloadedPlayback} changes.
     *
     * <p>This should not be generally required to be acted upon. But when offload is critical for
     * efficiency, or audio features (gapless, playback speed), this will let the app know.
     */
    default void onOffloadedPlayback(boolean isOffloadedPlayback) {}
  }

  /** Configuration options for preloading playlist items. */
  @UnstableApi
  class PreloadConfiguration {

    /** Default preload configuration that disables playlist preloading. */
    public static final PreloadConfiguration DEFAULT =
        new PreloadConfiguration(/* targetPreloadDurationUs= */ C.TIME_UNSET);

    /**
     * The target duration to buffer when preloading, in microseconds or {@link C#TIME_UNSET} to
     * disable preloading.
     */
    public final long targetPreloadDurationUs;

    /**
     * Creates an instance.
     *
     * @param targetPreloadDurationUs The target duration to preload, in microseconds or {@link
     *     C#TIME_UNSET} to disable preloading.
     */
    public PreloadConfiguration(long targetPreloadDurationUs) {
      this.targetPreloadDurationUs = targetPreloadDurationUs;
    }
  }

  /**
   * A builder for {@link ExoPlayer} instances.
   *
   * <p>See {@link #Builder(Context)} for the list of default values.
   */
  @SuppressWarnings("deprecation")
  final class Builder {

    /* package */ final Context context;

    /* package */ Clock clock;
    /* package */ long foregroundModeTimeoutMs;
    /* package */ Supplier<RenderersFactory> renderersFactorySupplier;
    /* package */ Supplier<MediaSource.Factory> mediaSourceFactorySupplier;
    /* package */ Supplier<TrackSelector> trackSelectorSupplier;
    /* package */ Supplier<LoadControl> loadControlSupplier;
    /* package */ Supplier<BandwidthMeter> bandwidthMeterSupplier;
    /* package */ Function<Clock, AnalyticsCollector> analyticsCollectorFunction;
    /* package */ Looper looper;
    /* package */ @C.Priority int priority;
    @Nullable /* package */ PriorityTaskManager priorityTaskManager;
    /* package */ AudioAttributes audioAttributes;
    /* package */ boolean handleAudioFocus;
    @C.WakeMode /* package */ int wakeMode;
    /* package */ boolean handleAudioBecomingNoisy;
    /* package */ boolean skipSilenceEnabled;
    /* package */ boolean deviceVolumeControlEnabled;
    @C.VideoScalingMode /* package */ int videoScalingMode;
    @C.VideoChangeFrameRateStrategy /* package */ int videoChangeFrameRateStrategy;
    /* package */ boolean useLazyPreparation;
    /* package */ SeekParameters seekParameters;
    /* package */ long seekBackIncrementMs;
    /* package */ long seekForwardIncrementMs;
    /* package */ long maxSeekToPreviousPositionMs;
    /* package */ LivePlaybackSpeedControl livePlaybackSpeedControl;
    /* package */ long releaseTimeoutMs;
    /* package */ long detachSurfaceTimeoutMs;
    /* package */ boolean pauseAtEndOfMediaItems;
    /* package */ boolean usePlatformDiagnostics;
    @Nullable /* package */ Looper playbackLooper;
    /* package */ boolean buildCalled;
    /* package */ boolean suppressPlaybackOnUnsuitableOutput;
    /* package */ String playerName;
    /* package */ boolean dynamicSchedulingEnabled;
    @Nullable /* package */ SuitableOutputChecker suitableOutputChecker;

    /**
     * Creates a builder.
     *
     * <p>Use {@link #Builder(Context, RenderersFactory)}, {@link #Builder(Context,
     * MediaSource.Factory)} or {@link #Builder(Context, RenderersFactory, MediaSource.Factory)}
     * instead, if you intend to provide a custom {@link RenderersFactory}, {@link
     * ExtractorsFactory} or {@link DefaultMediaSourceFactory}. This is to ensure that ProGuard or
     * R8 can remove ExoPlayer's {@link DefaultRenderersFactory}, {@link DefaultExtractorsFactory}
     * and {@link DefaultMediaSourceFactory} from the APK.
     *
     * <p>The builder uses the following default values:
     *
     * <ul>
     *   <li>{@link RenderersFactory}: {@link DefaultRenderersFactory}
     *   <li>{@link TrackSelector}: {@link DefaultTrackSelector}
     *   <li>{@link MediaSource.Factory}: {@link DefaultMediaSourceFactory}
     *   <li>{@link LoadControl}: {@link DefaultLoadControl}
     *   <li>{@link BandwidthMeter}: {@link DefaultBandwidthMeter#getSingletonInstance(Context)}
     *   <li>{@link LivePlaybackSpeedControl}: {@link DefaultLivePlaybackSpeedControl}
     *   <li>{@link Looper}: The {@link Looper} associated with the current thread, or the {@link
     *       Looper} of the application's main thread if the current thread doesn't have a {@link
     *       Looper}
     *   <li>{@link AnalyticsCollector}: {@link AnalyticsCollector} with {@link Clock#DEFAULT}
     *   <li>{@link C.Priority}: {@link C#PRIORITY_PLAYBACK}
     *   <li>{@link PriorityTaskManager}: {@code null} (not used)
     *   <li>{@link AudioAttributes}: {@link AudioAttributes#DEFAULT}, not handling audio focus
     *   <li>{@link C.WakeMode}: {@link C#WAKE_MODE_NONE}
     *   <li>{@code handleAudioBecomingNoisy}: {@code false}
     *   <li>{@code skipSilenceEnabled}: {@code false}
     *   <li>{@link C.VideoScalingMode}: {@link C#VIDEO_SCALING_MODE_DEFAULT}
     *   <li>{@link C.VideoChangeFrameRateStrategy}: {@link
     *       C#VIDEO_CHANGE_FRAME_RATE_STRATEGY_ONLY_IF_SEAMLESS}
     *   <li>{@code useLazyPreparation}: {@code true}
     *   <li>{@link SeekParameters}: {@link SeekParameters#DEFAULT}
     *   <li>{@code seekBackIncrementMs}: {@link C#DEFAULT_SEEK_BACK_INCREMENT_MS}
     *   <li>{@code seekForwardIncrementMs}: {@link C#DEFAULT_SEEK_FORWARD_INCREMENT_MS}
     *   <li>{@code maxSeekToPreviousPositionMs}: {@link C#DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS}
     *   <li>{@code releaseTimeoutMs}: {@link #DEFAULT_RELEASE_TIMEOUT_MS}
     *   <li>{@code detachSurfaceTimeoutMs}: {@link #DEFAULT_DETACH_SURFACE_TIMEOUT_MS}
     *   <li>{@code pauseAtEndOfMediaItems}: {@code false}
     *   <li>{@code usePlatformDiagnostics}: {@code true}
     *   <li>{@link Clock}: {@link Clock#DEFAULT}
     *   <li>{@code playbackLooper}: {@code null} (create new thread)
     *   <li>{@code dynamicSchedulingEnabled}: {@code false}
     * </ul>
     *
     * @param context A {@link Context}.
     */
    public Builder(Context context) {
      this(
          context,
          () -> new DefaultRenderersFactory(context),
          () -> new DefaultMediaSourceFactory(context, new DefaultExtractorsFactory()));
    }

    /**
     * Creates a builder with a custom {@link RenderersFactory}.
     *
     * <p>See {@link #Builder(Context)} for a list of default values.
     *
     * <p>Note that this constructor is only useful to try and ensure that ExoPlayer's {@link
     * DefaultRenderersFactory} can be removed by ProGuard or R8.
     *
     * @param context A {@link Context}.
     * @param renderersFactory A factory for creating {@link Renderer Renderers} to be used by the
     *     player.
     */
    @UnstableApi
    public Builder(Context context, RenderersFactory renderersFactory) {
      this(
          context,
          () -> renderersFactory,
          () -> new DefaultMediaSourceFactory(context, new DefaultExtractorsFactory()));
      checkNotNull(renderersFactory);
    }

    /**
     * Creates a builder with a custom {@link MediaSource.Factory}.
     *
     * <p>See {@link #Builder(Context)} for a list of default values.
     *
     * <p>Note that this constructor is only useful to try and ensure that ExoPlayer's {@link
     * DefaultMediaSourceFactory} (and therefore {@link DefaultExtractorsFactory}) can be removed by
     * ProGuard or R8.
     *
     * @param context A {@link Context}.
     * @param mediaSourceFactory A factory for creating a {@link MediaSource} from a {@link
     *     MediaItem}.
     */
    @UnstableApi
    public Builder(Context context, MediaSource.Factory mediaSourceFactory) {
      this(context, () -> new DefaultRenderersFactory(context), () -> mediaSourceFactory);
      checkNotNull(mediaSourceFactory);
    }

    /**
     * Creates a builder with a custom {@link RenderersFactory} and {@link MediaSource.Factory}.
     *
     * <p>See {@link #Builder(Context)} for a list of default values.
     *
     * <p>Note that this constructor is only useful to try and ensure that ExoPlayer's {@link
     * DefaultRenderersFactory}, {@link DefaultMediaSourceFactory} (and therefore {@link
     * DefaultExtractorsFactory}) can be removed by ProGuard or R8.
     *
     * @param context A {@link Context}.
     * @param renderersFactory A factory for creating {@link Renderer Renderers} to be used by the
     *     player.
     * @param mediaSourceFactory A factory for creating a {@link MediaSource} from a {@link
     *     MediaItem}.
     */
    @UnstableApi
    public Builder(
        Context context,
        RenderersFactory renderersFactory,
        MediaSource.Factory mediaSourceFactory) {
      this(context, () -> renderersFactory, () -> mediaSourceFactory);
      checkNotNull(renderersFactory);
      checkNotNull(mediaSourceFactory);
    }

    /**
     * Creates a builder with the specified custom components.
     *
     * <p>Note that this constructor is only useful to try and ensure that ExoPlayer's default
     * components can be removed by ProGuard or R8.
     *
     * @param context A {@link Context}.
     * @param renderersFactory A factory for creating {@link Renderer Renderers} to be used by the
     *     player.
     * @param mediaSourceFactory A {@link MediaSource.Factory}.
     * @param trackSelector A {@link TrackSelector}.
     * @param loadControl A {@link LoadControl}.
     * @param bandwidthMeter A {@link BandwidthMeter}.
     * @param analyticsCollector An {@link AnalyticsCollector}.
     */
    @UnstableApi
    public Builder(
        Context context,
        RenderersFactory renderersFactory,
        MediaSource.Factory mediaSourceFactory,
        TrackSelector trackSelector,
        LoadControl loadControl,
        BandwidthMeter bandwidthMeter,
        AnalyticsCollector analyticsCollector) {
      this(
          context,
          () -> renderersFactory,
          () -> mediaSourceFactory,
          () -> trackSelector,
          () -> loadControl,
          () -> bandwidthMeter,
          (clock) -> analyticsCollector);
      checkNotNull(renderersFactory);
      checkNotNull(mediaSourceFactory);
      checkNotNull(trackSelector);
      checkNotNull(bandwidthMeter);
      checkNotNull(analyticsCollector);
    }

    private Builder(
        Context context,
        Supplier<RenderersFactory> renderersFactorySupplier,
        Supplier<MediaSource.Factory> mediaSourceFactorySupplier) {
      this(
          context,
          renderersFactorySupplier,
          mediaSourceFactorySupplier,
          () -> new DefaultTrackSelector(context),
          DefaultLoadControl::new,
          () -> DefaultBandwidthMeter.getSingletonInstance(context),
          DefaultAnalyticsCollector::new);
    }

    private Builder(
        Context context,
        Supplier<RenderersFactory> renderersFactorySupplier,
        Supplier<MediaSource.Factory> mediaSourceFactorySupplier,
        Supplier<TrackSelector> trackSelectorSupplier,
        Supplier<LoadControl> loadControlSupplier,
        Supplier<BandwidthMeter> bandwidthMeterSupplier,
        Function<Clock, AnalyticsCollector> analyticsCollectorFunction) {
      this.context = checkNotNull(context);
      this.renderersFactorySupplier = renderersFactorySupplier;
      this.mediaSourceFactorySupplier = mediaSourceFactorySupplier;
      this.trackSelectorSupplier = trackSelectorSupplier;
      this.loadControlSupplier = loadControlSupplier;
      this.bandwidthMeterSupplier = bandwidthMeterSupplier;
      this.analyticsCollectorFunction = analyticsCollectorFunction;
      looper = Util.getCurrentOrMainLooper();
      audioAttributes = AudioAttributes.DEFAULT;
      wakeMode = C.WAKE_MODE_NONE;
      videoScalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
      videoChangeFrameRateStrategy = C.VIDEO_CHANGE_FRAME_RATE_STRATEGY_ONLY_IF_SEAMLESS;
      useLazyPreparation = true;
      seekParameters = SeekParameters.DEFAULT;
      seekBackIncrementMs = C.DEFAULT_SEEK_BACK_INCREMENT_MS;
      seekForwardIncrementMs = C.DEFAULT_SEEK_FORWARD_INCREMENT_MS;
      maxSeekToPreviousPositionMs = C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS;
      livePlaybackSpeedControl = new DefaultLivePlaybackSpeedControl.Builder().build();
      clock = Clock.DEFAULT;
      releaseTimeoutMs = DEFAULT_RELEASE_TIMEOUT_MS;
      detachSurfaceTimeoutMs = DEFAULT_DETACH_SURFACE_TIMEOUT_MS;
      usePlatformDiagnostics = true;
      playerName = "";
      priority = C.PRIORITY_PLAYBACK;
    }

    /**
     * Sets a limit on the time a call to {@link #setForegroundMode} can spend. If a call to {@link
     * #setForegroundMode} takes more than {@code timeoutMs} milliseconds to complete, the player
     * will raise an error via {@link Player.Listener#onPlayerError}.
     *
     * <p>This method is experimental, and will be renamed or removed in a future release.
     *
     * @param timeoutMs The time limit in milliseconds.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder experimentalSetForegroundModeTimeoutMs(long timeoutMs) {
      checkState(!buildCalled);
      foregroundModeTimeoutMs = timeoutMs;
      return this;
    }

    /**
     * Sets whether dynamic scheduling is enabled.
     *
     * <p>If enabled, ExoPlayer's playback loop will run as rarely as possible by scheduling work
     * for when {@link Renderer} progress can be made.
     *
     * <p>This method is experimental, and will be renamed or removed in a future release.
     *
     * @param dynamicSchedulingEnabled Whether to enable dynamic scheduling.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder experimentalSetDynamicSchedulingEnabled(boolean dynamicSchedulingEnabled) {
      checkState(!buildCalled);
      this.dynamicSchedulingEnabled = dynamicSchedulingEnabled;
      return this;
    }

    /**
     * Sets whether the player should suppress playback that is attempted on an unsuitable output.
     * An example of an unsuitable audio output is the built-in speaker on a Wear OS device (unless
     * it is explicitly selected by the user).
     *
     * <p>If called with {@code suppressPlaybackOnUnsuitableOutput = true}, then a playback attempt
     * on an unsuitable audio output will result in calls to {@link
     * Player.Listener#onPlaybackSuppressionReasonChanged(int)} with the value {@link
     * Player#PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT}.
     *
     * <p>Callers of this may also want to enable {@link #setHandleAudioBecomingNoisy(boolean)} to
     * prevent playback from continuing on the built-in speaker when a headset is disconnected.
     *
     * @param suppressPlaybackOnUnsuitableOutput Whether the player should suppress the playback
     *     when it is attempted on an unsuitable output.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setSuppressPlaybackOnUnsuitableOutput(
        boolean suppressPlaybackOnUnsuitableOutput) {
      checkState(!buildCalled);
      this.suppressPlaybackOnUnsuitableOutput = suppressPlaybackOnUnsuitableOutput;
      return this;
    }

    /**
     * Sets the {@link RenderersFactory} that will be used by the player.
     *
     * @param renderersFactory A {@link RenderersFactory}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setRenderersFactory(RenderersFactory renderersFactory) {
      checkState(!buildCalled);
      checkNotNull(renderersFactory);
      this.renderersFactorySupplier = () -> renderersFactory;
      return this;
    }

    /**
     * Sets the {@link MediaSource.Factory} that will be used by the player.
     *
     * @param mediaSourceFactory A {@link MediaSource.Factory}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    public Builder setMediaSourceFactory(MediaSource.Factory mediaSourceFactory) {
      checkState(!buildCalled);
      checkNotNull(mediaSourceFactory);
      this.mediaSourceFactorySupplier = () -> mediaSourceFactory;
      return this;
    }

    /**
     * Sets the {@link TrackSelector} that will be used by the player.
     *
     * @param trackSelector A {@link TrackSelector}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setTrackSelector(TrackSelector trackSelector) {
      checkState(!buildCalled);
      checkNotNull(trackSelector);
      this.trackSelectorSupplier = () -> trackSelector;
      return this;
    }

    /**
     * Sets the {@link LoadControl} that will be used by the player.
     *
     * @param loadControl A {@link LoadControl}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setLoadControl(LoadControl loadControl) {
      checkState(!buildCalled);
      checkNotNull(loadControl);
      this.loadControlSupplier = () -> loadControl;
      return this;
    }

    /**
     * Sets the {@link BandwidthMeter} that will be used by the player.
     *
     * @param bandwidthMeter A {@link BandwidthMeter}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setBandwidthMeter(BandwidthMeter bandwidthMeter) {
      checkState(!buildCalled);
      checkNotNull(bandwidthMeter);
      this.bandwidthMeterSupplier = () -> bandwidthMeter;
      return this;
    }

    /**
     * Sets the {@link Looper} that must be used for all calls to the player and that is used to
     * call listeners on.
     *
     * @param looper A {@link Looper}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setLooper(Looper looper) {
      checkState(!buildCalled);
      checkNotNull(looper);
      this.looper = looper;
      return this;
    }

    /**
     * Sets the {@link AnalyticsCollector} that will collect and forward all player events.
     *
     * @param analyticsCollector An {@link AnalyticsCollector}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setAnalyticsCollector(AnalyticsCollector analyticsCollector) {
      checkState(!buildCalled);
      checkNotNull(analyticsCollector);
      this.analyticsCollectorFunction = (clock) -> analyticsCollector;
      return this;
    }

    /**
     * Sets the {@link C.Priority} for this player.
     *
     * <p>The priority may influence resource allocation between multiple players or other
     * components running in the same app.
     *
     * <p>This priority is used for the {@link PriorityTaskManager}, if {@linkplain
     * #setPriorityTaskManager set}.
     *
     * @param priority The {@link C.Priority}.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setPriority(@C.Priority int priority) {
      checkState(!buildCalled);
      this.priority = priority;
      return this;
    }

    /**
     * Sets an {@link PriorityTaskManager} that will be used by the player.
     *
     * <p>The priority set via {@link #setPriority} (or {@link C#PRIORITY_PLAYBACK by default)} will
     * be set while the player is loading.
     *
     * @param priorityTaskManager A {@link PriorityTaskManager}, or null to not use one.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setPriorityTaskManager(@Nullable PriorityTaskManager priorityTaskManager) {
      checkState(!buildCalled);
      this.priorityTaskManager = priorityTaskManager;
      return this;
    }

    /**
     * Sets {@link AudioAttributes} that will be used by the player and whether to handle audio
     * focus.
     *
     * <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 {@link AudioAttributes}.
     * @param handleAudioFocus Whether the player should handle audio focus.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    public Builder setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) {
      checkState(!buildCalled);
      this.audioAttributes = checkNotNull(audioAttributes);
      this.handleAudioFocus = handleAudioFocus;
      return this;
    }

    /**
     * Sets the {@link C.WakeMode} that will be used by the player.
     *
     * <p>Enabling this feature requires the {@link android.Manifest.permission#WAKE_LOCK}
     * permission. It should be used together with a foreground {@link android.app.Service} for use
     * cases where playback occurs and the screen is off (e.g. background audio playback). It is not
     * useful when the screen will be kept on during playback (e.g. foreground video playback).
     *
     * <p>When enabled, the locks ({@link android.os.PowerManager.WakeLock} / {@link
     * android.net.wifi.WifiManager.WifiLock}) will be held whenever the player is in the {@link
     * #STATE_READY} or {@link #STATE_BUFFERING} states with {@code playWhenReady = true}. The locks
     * held depend on the specified {@link C.WakeMode}.
     *
     * @param wakeMode A {@link C.WakeMode}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    public Builder setWakeMode(@C.WakeMode int wakeMode) {
      checkState(!buildCalled);
      this.wakeMode = wakeMode;
      return this;
    }

    /**
     * Sets whether the player should pause automatically when audio is rerouted from a headset to
     * device speakers. See the <a
     * href="https://developer.android.com/media/platform/output#becoming-noisy">audio becoming
     * noisy</a> documentation for more information.
     *
     * @param handleAudioBecomingNoisy Whether the player should pause automatically when audio is
     *     rerouted from a headset to device speakers.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    public Builder setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy) {
      checkState(!buildCalled);
      this.handleAudioBecomingNoisy = handleAudioBecomingNoisy;
      return this;
    }

    /**
     * Sets whether silences silences in the audio stream is enabled.
     *
     * @param skipSilenceEnabled Whether skipping silences is enabled.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setSkipSilenceEnabled(boolean skipSilenceEnabled) {
      checkState(!buildCalled);
      this.skipSilenceEnabled = skipSilenceEnabled;
      return this;
    }

    /**
     * Sets whether the player is allowed to set, increase, decrease or mute device volume.
     *
     * @param deviceVolumeControlEnabled Whether controlling device volume is enabled.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setDeviceVolumeControlEnabled(boolean deviceVolumeControlEnabled) {
      checkState(!buildCalled);
      this.deviceVolumeControlEnabled = deviceVolumeControlEnabled;
      return this;
    }

    /**
     * Sets the {@link C.VideoScalingMode} that will be used by the player.
     *
     * <p>The scaling mode only applies if a {@link MediaCodec}-based video {@link Renderer} is
     * enabled and if the output surface is owned by a {@link SurfaceView}.
     *
     * @param videoScalingMode A {@link C.VideoScalingMode}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) {
      checkState(!buildCalled);
      this.videoScalingMode = videoScalingMode;
      return this;
    }

    /**
     * Sets a {@link C.VideoChangeFrameRateStrategy} that will be used by the player when provided
     * with a video output {@link Surface}.
     *
     * <p>The strategy only applies if a {@link MediaCodec}-based video {@link Renderer} is enabled.
     * Applications wishing to use {@link Surface#CHANGE_FRAME_RATE_ALWAYS} should set the mode to
     * {@link C#VIDEO_CHANGE_FRAME_RATE_STRATEGY_OFF} to disable calls to {@link
     * Surface#setFrameRate} from ExoPlayer, and should then call {@link Surface#setFrameRate}
     * directly from application code.
     *
     * @param videoChangeFrameRateStrategy A {@link C.VideoChangeFrameRateStrategy}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setVideoChangeFrameRateStrategy(
        @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy) {
      checkState(!buildCalled);
      this.videoChangeFrameRateStrategy = videoChangeFrameRateStrategy;
      return this;
    }

    /**
     * Sets whether media sources should be initialized lazily.
     *
     * <p>If false, all initial preparation steps (e.g., manifest loads) happen immediately. If
     * true, these initial preparations are triggered only when the player starts buffering the
     * media.
     *
     * @param useLazyPreparation Whether to use lazy preparation.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setUseLazyPreparation(boolean useLazyPreparation) {
      checkState(!buildCalled);
      this.useLazyPreparation = useLazyPreparation;
      return this;
    }

    /**
     * Sets the parameters that control how seek operations are performed.
     *
     * @param seekParameters The {@link SeekParameters}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setSeekParameters(SeekParameters seekParameters) {
      checkState(!buildCalled);
      this.seekParameters = checkNotNull(seekParameters);
      return this;
    }

    /**
     * Sets the {@link #seekBack()} increment.
     *
     * @param seekBackIncrementMs The seek back increment, in milliseconds.
     * @return This builder.
     * @throws IllegalArgumentException If {@code seekBackIncrementMs} is non-positive.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setSeekBackIncrementMs(@IntRange(from = 1) long seekBackIncrementMs) {
      checkArgument(seekBackIncrementMs > 0);
      checkState(!buildCalled);
      this.seekBackIncrementMs = seekBackIncrementMs;
      return this;
    }

    /**
     * Sets the {@link #seekForward()} increment.
     *
     * @param seekForwardIncrementMs The seek forward increment, in milliseconds.
     * @return This builder.
     * @throws IllegalArgumentException If {@code seekForwardIncrementMs} is non-positive.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setSeekForwardIncrementMs(@IntRange(from = 1) long seekForwardIncrementMs) {
      checkArgument(seekForwardIncrementMs > 0);
      checkState(!buildCalled);
      this.seekForwardIncrementMs = seekForwardIncrementMs;
      return this;
    }

    /**
     * Sets the maximum position for which {@link #seekToPrevious()} seeks to the previous {@link
     * MediaItem}.
     *
     * @param maxSeekToPreviousPositionMs The maximum position, in milliseconds.
     * @return This builder.
     * @throws IllegalArgumentException If {@code maxSeekToPreviousPositionMs} is negative.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setMaxSeekToPreviousPositionMs(
        @IntRange(from = 0) long maxSeekToPreviousPositionMs) {
      checkArgument(maxSeekToPreviousPositionMs >= 0L);
      checkState(!buildCalled);
      this.maxSeekToPreviousPositionMs = maxSeekToPreviousPositionMs;
      return this;
    }

    /**
     * Sets a timeout for calls to {@link #release} and {@link #setForegroundMode}.
     *
     * <p>If a call to {@link #release} or {@link #setForegroundMode} takes more than {@code
     * timeoutMs} to complete, the player will report an error via {@link
     * Player.Listener#onPlayerError}.
     *
     * @param releaseTimeoutMs The release timeout, in milliseconds.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setReleaseTimeoutMs(long releaseTimeoutMs) {
      checkState(!buildCalled);
      this.releaseTimeoutMs = releaseTimeoutMs;
      return this;
    }

    /**
     * Sets a timeout for detaching a surface from the player.
     *
     * <p>If detaching a surface or replacing a surface takes more than {@code
     * detachSurfaceTimeoutMs} to complete, the player will report an error via {@link
     * Player.Listener#onPlayerError}.
     *
     * @param detachSurfaceTimeoutMs The timeout for detaching a surface, in milliseconds.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setDetachSurfaceTimeoutMs(long detachSurfaceTimeoutMs) {
      checkState(!buildCalled);
      this.detachSurfaceTimeoutMs = detachSurfaceTimeoutMs;
      return this;
    }

    /**
     * Sets whether to pause playback at the end of each media item.
     *
     * <p>This means the player will pause at the end of each window in the current {@link
     * #getCurrentTimeline() timeline}. Listeners will be informed by a call to {@link
     * Player.Listener#onPlayWhenReadyChanged(boolean, int)} with the reason {@link
     * Player#PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM} when this happens.
     *
     * @param pauseAtEndOfMediaItems Whether to pause playback at the end of each media item.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems) {
      checkState(!buildCalled);
      this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems;
      return this;
    }

    /**
     * Sets the {@link LivePlaybackSpeedControl} that will control the playback speed when playing
     * live streams, in order to maintain a steady target offset from the live stream edge.
     *
     * @param livePlaybackSpeedControl The {@link LivePlaybackSpeedControl}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setLivePlaybackSpeedControl(LivePlaybackSpeedControl livePlaybackSpeedControl) {
      checkState(!buildCalled);
      this.livePlaybackSpeedControl = checkNotNull(livePlaybackSpeedControl);
      return this;
    }

    /**
     * Sets whether the player reports diagnostics data to the Android platform.
     *
     * <p>If enabled, the player will use the {@link android.media.metrics.MediaMetricsManager} to
     * create a {@link android.media.metrics.PlaybackSession} and forward playback events and
     * performance data to this session. This helps to provide system performance and debugging
     * information for media playback on the device. This data may also be collected by Google <a
     * href="https://support.google.com/accounts/answer/6078260">if sharing usage and diagnostics
     * data is enabled</a> by the user of the device.
     *
     * @param usePlatformDiagnostics Whether the player reports diagnostics data to the Android
     *     platform.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setUsePlatformDiagnostics(boolean usePlatformDiagnostics) {
      checkState(!buildCalled);
      this.usePlatformDiagnostics = usePlatformDiagnostics;
      return this;
    }

    /**
     * Sets the {@link Clock} that will be used by the player. Should only be set for testing
     * purposes.
     *
     * @param clock A {@link Clock}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    @VisibleForTesting
    public Builder setClock(Clock clock) {
      checkState(!buildCalled);
      this.clock = clock;
      return this;
    }

    /**
     * Sets the {@link SuitableOutputChecker} to check the suitability of the selected outputs for
     * playback.
     *
     * <p>If this method is not called, the library uses a default implementation based on framework
     * APIs.
     *
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    @RestrictTo(LIBRARY_GROUP)
    @VisibleForTesting
    @RequiresApi(35)
    public Builder setSuitableOutputChecker(SuitableOutputChecker suitableOutputChecker) {
      checkState(!buildCalled);
      this.suitableOutputChecker = suitableOutputChecker;
      return this;
    }

    /**
     * Sets the {@link Looper} that will be used for playback.
     *
     * <p>The backing thread should run with priority {@link Process#THREAD_PRIORITY_AUDIO} and
     * should handle messages within 10ms.
     *
     * @param playbackLooper A {@link Looper}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setPlaybackLooper(Looper playbackLooper) {
      checkState(!buildCalled);
      this.playbackLooper = playbackLooper;
      return this;
    }

    /**
     * Sets the player name that is included in the {@link PlayerId} for informational purpose to
     * recognize the player by its {@link PlayerId}.
     *
     * <p>The default is an empty string.
     *
     * @param playerName A name for the player in the {@link PlayerId}.
     * @return This builder.
     * @throws IllegalStateException If {@link #build()} has already been called.
     */
    @CanIgnoreReturnValue
    @UnstableApi
    public Builder setName(String playerName) {
      checkState(!buildCalled);
      this.playerName = playerName;
      return this;
    }

    /**
     * Builds an {@link ExoPlayer} instance.
     *
     * @throws IllegalStateException If this method has already been called.
     */
    public ExoPlayer build() {
      checkState(!buildCalled);
      buildCalled = true;
      if (suitableOutputChecker == null
          && Util.SDK_INT >= 35
          && suppressPlaybackOnUnsuitableOutput) {
        suitableOutputChecker = new DefaultSuitableOutputChecker(context, new Handler(looper));
      }
      return new ExoPlayerImpl(/* builder= */ this, /* wrappingPlayer= */ null);
    }

    /* package */ SimpleExoPlayer buildSimpleExoPlayer() {
      checkState(!buildCalled);
      buildCalled = true;
      return new SimpleExoPlayer(/* builder= */ this);
    }
  }

  /**
   * The default timeout for calls to {@link #release} and {@link #setForegroundMode}, in
   * milliseconds.
   */
  @UnstableApi long DEFAULT_RELEASE_TIMEOUT_MS = 500;

  /** The default timeout for detaching a surface from the player, in milliseconds. */
  @UnstableApi long DEFAULT_DETACH_SURFACE_TIMEOUT_MS = 2_000;

  /**
   * Equivalent to {@link Player#getPlayerError()}, except the exception is guaranteed to be an
   * {@link ExoPlaybackException}.
   */
  @Override
  @Nullable
  ExoPlaybackException getPlayerError();

  /**
   * @deprecated Use {@link ExoPlayer}, as the {@link AudioComponent} methods are defined by that
   *     interface.
   */
  @SuppressWarnings("deprecation") // Intentionally returning deprecated type
  @UnstableApi
  @Nullable
  @Deprecated
  AudioComponent getAudioComponent();

  /**
   * @deprecated Use {@link ExoPlayer}, as the {@link VideoComponent} methods are defined by that
   *     interface.
   */
  @SuppressWarnings("deprecation") // Intentionally returning deprecated type
  @UnstableApi
  @Nullable
  @Deprecated
  VideoComponent getVideoComponent();

  /**
   * @deprecated Use {@link Player}, as the {@link TextComponent} methods are defined by that
   *     interface.
   */
  @SuppressWarnings("deprecation") // Intentionally returning deprecated type
  @UnstableApi
  @Nullable
  @Deprecated
  TextComponent getTextComponent();

  /**
   * @deprecated Use {@link Player}, as the {@link DeviceComponent} methods are defined by that
   *     interface.
   */
  @SuppressWarnings("deprecation") // Intentionally returning deprecated type
  @UnstableApi
  @Nullable
  @Deprecated
  DeviceComponent getDeviceComponent();

  /**
   * Adds a listener to receive audio offload events.
   *
   * <p>This method can be called from any thread.
   *
   * @param listener The listener to register.
   */
  @UnstableApi
  void addAudioOffloadListener(AudioOffloadListener listener);

  /**
   * Removes a listener of audio offload events.
   *
   * @param listener The listener to unregister.
   */
  @UnstableApi
  void removeAudioOffloadListener(AudioOffloadListener listener);

  /** Returns the {@link AnalyticsCollector} used for collecting analytics events. */
  @UnstableApi
  AnalyticsCollector getAnalyticsCollector();

  /**
   * Adds an {@link AnalyticsListener} to receive analytics events.
   *
   * <p>This method can be called from any thread.
   *
   * @param listener The listener to be added.
   */
  void addAnalyticsListener(AnalyticsListener listener);

  /**
   * Removes an {@link AnalyticsListener}.
   *
   * @param listener The listener to be removed.
   */
  void removeAnalyticsListener(AnalyticsListener listener);

  /** Returns the number of renderers. */
  @UnstableApi
  int getRendererCount();

  /**
   * Returns the track type that the renderer at a given index handles.
   *
   * <p>For example, a video renderer will return {@link C#TRACK_TYPE_VIDEO}, an audio renderer will
   * return {@link C#TRACK_TYPE_AUDIO} and a text renderer will return {@link C#TRACK_TYPE_TEXT}.
   *
   * @param index The index of the renderer.
   * @return The {@link C.TrackType track type} that the renderer handles.
   */
  @UnstableApi
  @C.TrackType
  int getRendererType(int index);

  /**
   * Returns the renderer at the given index.
   *
   * @param index The index of the renderer.
   * @return The renderer at this index.
   */
  @UnstableApi
  Renderer getRenderer(int index);

  /**
   * Returns the track selector that this player uses, or null if track selection is not supported.
   */
  @UnstableApi
  @Nullable
  TrackSelector getTrackSelector();

  /**
   * Returns the available track groups.
   *
   * @see Listener#onTracksChanged(Tracks)
   * @deprecated Use {@link #getCurrentTracks()}.
   */
  @UnstableApi
  @Deprecated
  TrackGroupArray getCurrentTrackGroups();

  /**
   * Returns the current track selections for each renderer, which may include {@code null} elements
   * if some renderers do not have any selected tracks.
   *
   * @see Listener#onTracksChanged(Tracks)
   * @deprecated Use {@link #getCurrentTracks()}.
   */
  @UnstableApi
  @Deprecated
  TrackSelectionArray getCurrentTrackSelections();

  /**
   * Returns the {@link Looper} associated with the playback thread.
   *
   * <p>This method may be called from any thread.
   */
  @UnstableApi
  Looper getPlaybackLooper();

  /**
   * Returns the {@link Clock} used for playback.
   *
   * <p>This method can be called from any thread.
   */
  @UnstableApi
  Clock getClock();

  /**
   * @deprecated Use {@link #setMediaSource(MediaSource)} and {@link #prepare()} instead.
   */
  @UnstableApi
  @Deprecated
  void prepare(MediaSource mediaSource);

  /**
   * @deprecated Use {@link #setMediaSource(MediaSource, boolean)} and {@link #prepare()} instead.
   */
  @UnstableApi
  @Deprecated
  void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState);

  /**
   * Clears the playlist, adds the specified {@link MediaSource MediaSources} and resets the
   * position to the default position.
   *
   * @param mediaSources The new {@link MediaSource MediaSources}.
   */
  @UnstableApi
  void setMediaSources(List<MediaSource> mediaSources);

  /**
   * Clears the playlist and adds the specified {@link MediaSource MediaSources}.
   *
   * @param mediaSources The new {@link MediaSource MediaSources}.
   * @param resetPosition Whether the playback position should be reset to the default position in
   *     the first {@link Timeline.Window}. If false, playback will start from the position defined
   *     by {@link #getCurrentMediaItemIndex()} and {@link #getCurrentPosition()}.
   */
  @UnstableApi
  void setMediaSources(List<MediaSource> mediaSources, boolean resetPosition);

  /**
   * Clears the playlist and adds the specified {@link MediaSource MediaSources}.
   *
   * @param mediaSources The new {@link MediaSource MediaSources}.
   * @param startMediaItemIndex The media item index to start playback from. If {@link
   *     C#INDEX_UNSET} is passed, the current position is not reset.
   * @param startPositionMs The position in milliseconds to start playback from. If {@link
   *     C#TIME_UNSET} is passed, the default position of the given media source is used. In any
   *     case, if {@code startMediaItemIndex} is set to {@link C#INDEX_UNSET}, this parameter is
   *     ignored and the position is not reset at all.
   */
  @UnstableApi
  void setMediaSources(
      List<MediaSource> mediaSources, int startMediaItemIndex, long startPositionMs);

  /**
   * Clears the playlist, adds the specified {@link MediaSource} and resets the position to the
   * default position.
   *
   * @param mediaSource The new {@link MediaSource}.
   */
  @UnstableApi
  void setMediaSource(MediaSource mediaSource);

  /**
   * Clears the playlist and adds the specified {@link MediaSource}.
   *
   * @param mediaSource The new {@link MediaSource}.
   * @param startPositionMs The position in milliseconds to start playback from. If {@link
   *     C#TIME_UNSET} is passed, the default position of the given media source is used.
   */
  @UnstableApi
  void setMediaSource(MediaSource mediaSource, long startPositionMs);

  /**
   * Clears the playlist and adds the specified {@link MediaSource}.
   *
   * @param mediaSource The new {@link MediaSource}.
   * @param resetPosition Whether the playback position should be reset to the default position. If
   *     false, playback will start from the position defined by {@link #getCurrentMediaItemIndex()}
   *     and {@link #getCurrentPosition()}.
   */
  @UnstableApi
  void setMediaSource(MediaSource mediaSource, boolean resetPosition);

  /**
   * Adds a media source to the end of the playlist.
   *
   * @param mediaSource The {@link MediaSource} to add.
   */
  @UnstableApi
  void addMediaSource(MediaSource mediaSource);

  /**
   * Adds a media source at the given index of the playlist.
   *
   * @param index The index at which to add the source.
   * @param mediaSource The {@link MediaSource} to add.
   */
  @UnstableApi
  void addMediaSource(int index, MediaSource mediaSource);

  /**
   * Adds a list of media sources to the end of the playlist.
   *
   * @param mediaSources The {@link MediaSource MediaSources} to add.
   */
  @UnstableApi
  void addMediaSources(List<MediaSource> mediaSources);

  /**
   * Adds a list of media sources at the given index of the playlist.
   *
   * @param index The index at which to add the media sources.
   * @param mediaSources The {@link MediaSource MediaSources} to add.
   */
  @UnstableApi
  void addMediaSources(int index, List<MediaSource> mediaSources);

  /**
   * Sets the shuffle order.
   *
   * <p>The {@link ShuffleOrder} passed must have the same length as the current playlist ({@link
   * Player#getMediaItemCount()}).
   *
   * @param shuffleOrder The shuffle order.
   */
  @UnstableApi
  void setShuffleOrder(ShuffleOrder shuffleOrder);

  /**
   * Sets the {@linkplain PreloadConfiguration preload configuration} to configure playlist
   * preloading.
   *
   * @param preloadConfiguration The preload configuration.
   */
  @UnstableApi
  void setPreloadConfiguration(PreloadConfiguration preloadConfiguration);

  /** Returns the {@linkplain PreloadConfiguration preload configuration}. */
  @UnstableApi
  PreloadConfiguration getPreloadConfiguration();

  /**
   * {@inheritDoc}
   *
   * <p>ExoPlayer will keep the existing {@link MediaSource} for this {@link MediaItem} if
   * {@linkplain MediaSource#canUpdateMediaItem supported} by the {@link MediaSource}. If the
   * current item is replaced, this will also not interrupt the ongoing playback.
   */
  @Override
  void replaceMediaItem(int index, MediaItem mediaItem);

  /**
   * {@inheritDoc}
   *
   * <p>ExoPlayer will keep the existing {@link MediaSource} instances for the new {@link MediaItem
   * MediaItems} if {@linkplain MediaSource#canUpdateMediaItem supported} by all of these {@link
   * MediaSource} instances. If the current item is replaced, this will also not interrupt the
   * ongoing playback.
   */
  @Override
  void replaceMediaItems(int fromIndex, int toIndex, List<MediaItem> mediaItems);

  /**
   * Sets the ID of the audio session to attach to the underlying {@link android.media.AudioTrack}.
   *
   * <p>The audio session ID can be generated using {@link Util#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.
   */
  @UnstableApi
  void setAudioSessionId(int audioSessionId);

  /**
   * Returns the audio session identifier, or {@link C#AUDIO_SESSION_ID_UNSET} if not set.
   *
   * @see Listener#onAudioSessionIdChanged(int)
   */
  @UnstableApi
  int getAudioSessionId();

  /** Sets information on an auxiliary audio effect to attach to the underlying audio track. */
  @UnstableApi
  void setAuxEffectInfo(AuxEffectInfo auxEffectInfo);

  /** Detaches any previously attached auxiliary audio effect from the underlying audio track. */
  @UnstableApi
  void clearAuxEffectInfo();

  /**
   * Sets the preferred audio device.
   *
   * @param audioDeviceInfo The preferred {@linkplain AudioDeviceInfo audio device}, or null to
   *     restore the default.
   */
  @UnstableApi
  @RequiresApi(23)
  void setPreferredAudioDevice(@Nullable AudioDeviceInfo audioDeviceInfo);

  /**
   * Sets whether skipping silences in the audio stream is enabled.
   *
   * @param skipSilenceEnabled Whether skipping silences in the audio stream is enabled.
   */
  @UnstableApi
  void setSkipSilenceEnabled(boolean skipSilenceEnabled);

  /**
   * Returns whether skipping silences in the audio stream is enabled.
   *
   * @see Listener#onSkipSilenceEnabledChanged(boolean)
   */
  @UnstableApi
  boolean getSkipSilenceEnabled();

  /**
   * Sets a {@link List} of {@linkplain Effect video effects} that will be applied to each video
   * frame.
   *
   * <p>If {@linkplain #setVideoSurface passing a surface to the player directly}, the output
   * resolution needs to be signaled by passing a {@linkplain #createMessage(PlayerMessage.Target)
   * message} to the {@linkplain Renderer video renderer} with type {@link
   * Renderer#MSG_SET_VIDEO_OUTPUT_RESOLUTION} after calling this method. For {@link SurfaceView},
   * {@link TextureView} and {@link SurfaceHolder} output this happens automatically.
   *
   * <p>The following limitations exist for using {@linkplain Effect video effects}:
   *
   * <ul>
   *   <li>The {@code androidx.media3:media3-effect} module must be available on the runtime
   *       classpath. {@code androidx.media3:media3-exoplayer} does not explicitly depend on the
   *       effect module, so apps must make sure it's available themselves. It must be the same
   *       version as the rest of the {@code androidx.media3} modules being used by the app.
   *   <li>This feature works only with the default {@link MediaCodecVideoRenderer} and not custom
   *       or extension {@linkplain Renderer video renderers}.
   *   <li>This feature does not work with {@linkplain Effect effects} that update the frame
   *       timestamps.
   *   <li>This feature does not work with DRM-protected content.
   *   <li>This method must be called at least once before calling {@link #prepare()} (in order to
   *       set up the effects pipeline). The effects can be changed during playback by subsequent
   *       calls to this method after {@link #prepare()}.
   * </ul>
   *
   * @param videoEffects The {@link List} of {@linkplain Effect video effects} to apply.
   */
  @UnstableApi
  void setVideoEffects(List<Effect> videoEffects);

  /**
   * Sets the {@link C.VideoScalingMode}.
   *
   * <p>The scaling mode only applies if a {@link MediaCodec}-based video {@link Renderer} is
   * enabled and if the output surface is owned by a {@link SurfaceView}.
   *
   * @param videoScalingMode The {@link C.VideoScalingMode}.
   */
  @UnstableApi
  void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode);

  /** Returns the {@link C.VideoScalingMode}. */
  @UnstableApi
  @C.VideoScalingMode
  int getVideoScalingMode();

  /**
   * Sets a {@link C.VideoChangeFrameRateStrategy} that will be used by the player when provided
   * with a video output {@link Surface}.
   *
   * <p>The strategy only applies if a {@link MediaCodec}-based video {@link Renderer} is enabled.
   * Applications wishing to use {@link Surface#CHANGE_FRAME_RATE_ALWAYS} should set the mode to
   * {@link C#VIDEO_CHANGE_FRAME_RATE_STRATEGY_OFF} to disable calls to {@link Surface#setFrameRate}
   * from ExoPlayer, and should then call {@link Surface#setFrameRate} directly from application
   * code.
   *
   * @param videoChangeFrameRateStrategy A {@link C.VideoChangeFrameRateStrategy}.
   */
  @UnstableApi
  void setVideoChangeFrameRateStrategy(
      @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy);

  /** Returns the {@link C.VideoChangeFrameRateStrategy}. */
  @UnstableApi
  @C.VideoChangeFrameRateStrategy
  int getVideoChangeFrameRateStrategy();

  /**
   * Sets a listener to receive video frame metadata events.
   *
   * <p>This method is intended to be called by the same component that sets the {@link Surface}
   * onto which video will be rendered. If using ExoPlayer's standard UI components, this method
   * should not be called directly from application code.
   *
   * @param listener The listener.
   */
  @UnstableApi
  void setVideoFrameMetadataListener(VideoFrameMetadataListener listener);

  /**
   * Clears the listener which receives video frame metadata events if it matches the one passed.
   * Else does nothing.
   *
   * @param listener The listener to clear.
   */
  @UnstableApi
  void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener);

  /**
   * Sets a listener of camera motion events.
   *
   * @param listener The listener.
   */
  @UnstableApi
  void setCameraMotionListener(CameraMotionListener listener);

  /**
   * Clears the listener which receives camera motion events if it matches the one passed. Else does
   * nothing.
   *
   * @param listener The listener to clear.
   */
  @UnstableApi
  void clearCameraMotionListener(CameraMotionListener listener);

  /**
   * Creates a message that can be sent to a {@link PlayerMessage.Target}. By default, the message
   * will be delivered immediately without blocking on the playback thread. The default {@link
   * PlayerMessage#getType()} is 0 and the default {@link PlayerMessage#getPayload()} is null. If a
   * position is specified with {@link PlayerMessage#setPosition(long)}, the message will be
   * delivered at this position in the current media item defined by {@link
   * #getCurrentMediaItemIndex()}. Alternatively, the message can be sent at a specific mediaItem
   * using {@link PlayerMessage#setPosition(int, long)}.
   */
  @UnstableApi
  PlayerMessage createMessage(PlayerMessage.Target target);

  /**
   * Sets the parameters that control how seek operations are performed.
   *
   * @param seekParameters The seek parameters, or {@code null} to use the defaults.
   */
  @UnstableApi
  void setSeekParameters(@Nullable SeekParameters seekParameters);

  /** Returns the currently active {@link SeekParameters} of the player. */
  @UnstableApi
  SeekParameters getSeekParameters();

  /**
   * Sets whether the player is allowed to keep holding limited resources such as video decoders,
   * even when in the idle state. By doing so, the player may be able to reduce latency when
   * starting to play another piece of content for which the same resources are required.
   *
   * <p>This mode should be used with caution, since holding limited resources may prevent other
   * players of media components from acquiring them. It should only be enabled when <em>both</em>
   * of the following conditions are true:
   *
   * <ul>
   *   <li>The application that owns the player is in the foreground.
   *   <li>The player is used in a way that may benefit from foreground mode. For this to be true,
   *       the same player instance must be used to play multiple pieces of content, and there must
   *       be gaps between the playbacks (i.e. {@link #stop} is called to halt one playback, and
   *       {@link #prepare} is called some time later to start a new one).
   * </ul>
   *
   * <p>Note that foreground mode is <em>not</em> useful for switching between content without gaps
   * between the playbacks. For this use case {@link #stop} does not need to be called, and simply
   * calling {@link #prepare} for the new media will cause limited resources to be retained even if
   * foreground mode is not enabled.
   *
   * <p>If foreground mode is enabled, it's the application's responsibility to disable it when the
   * conditions described above no longer hold.
   *
   * @param foregroundMode Whether the player is allowed to keep limited resources even when in the
   *     idle state.
   */
  @UnstableApi
  void setForegroundMode(boolean foregroundMode);

  /**
   * Sets whether to pause playback at the end of each media item.
   *
   * <p>This means the player will pause at the end of each window in the current {@link
   * #getCurrentTimeline() timeline}. Listeners will be informed by a call to {@link
   * Player.Listener#onPlayWhenReadyChanged(boolean, int)} with the reason {@link
   * Player#PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM} when this happens.
   *
   * @param pauseAtEndOfMediaItems Whether to pause playback at the end of each media item.
   */
  @UnstableApi
  void setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems);

  /**
   * Returns whether the player pauses playback at the end of each media item.
   *
   * @see #setPauseAtEndOfMediaItems(boolean)
   */
  @UnstableApi
  boolean getPauseAtEndOfMediaItems();

  /** Returns the audio format currently being played, or null if no audio is being played. */
  @UnstableApi
  @Nullable
  Format getAudioFormat();

  /** Returns the video format currently being played, or null if no video is being played. */
  @UnstableApi
  @Nullable
  Format getVideoFormat();

  /** Returns {@link DecoderCounters} for audio, or null if no audio is being played. */
  @UnstableApi
  @Nullable
  DecoderCounters getAudioDecoderCounters();

  /** Returns {@link DecoderCounters} for video, or null if no video is being played. */
  @UnstableApi
  @Nullable
  DecoderCounters getVideoDecoderCounters();

  /**
   * Sets whether the player should pause automatically when audio is rerouted from a headset to
   * device speakers. See the <a
   * href="https://developer.android.com/guide/topics/media-apps/volume-and-earphones#becoming-noisy">audio
   * becoming noisy</a> documentation for more information.
   *
   * @param handleAudioBecomingNoisy Whether the player should pause automatically when audio is
   *     rerouted from a headset to device speakers.
   */
  void setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy);

  /**
   * Sets how the player should keep the device awake for playback when the screen is off.
   *
   * <p>Enabling this feature requires the {@link android.Manifest.permission#WAKE_LOCK} permission.
   * It should be used together with a foreground {@link android.app.Service} for use cases where
   * playback occurs and the screen is off (e.g. background audio playback). It is not useful when
   * the screen will be kept on during playback (e.g. foreground video playback).
   *
   * <p>When enabled, the locks ({@link android.os.PowerManager.WakeLock} / {@link
   * android.net.wifi.WifiManager.WifiLock}) will be held whenever the player is in the {@link
   * #STATE_READY} or {@link #STATE_BUFFERING} states with {@code playWhenReady = true}. The locks
   * held depends on the specified {@link C.WakeMode}.
   *
   * @param wakeMode The {@link C.WakeMode} option to keep the device awake during playback.
   */
  void setWakeMode(@C.WakeMode int wakeMode);

  /**
   * Sets the {@link C.Priority} for this player.
   *
   * <p>The priority may influence resource allocation between multiple players or other components
   * running in the same app.
   *
   * <p>This priority is used for the {@link PriorityTaskManager}, if {@linkplain
   * #setPriorityTaskManager set}.
   *
   * @param priority The {@link C.Priority}.
   */
  @UnstableApi
  void setPriority(@C.Priority int priority);

  /**
   * Sets a {@link PriorityTaskManager}, or null to clear a previously set priority task manager.
   *
   * <p>The priority set via {@link #setPriority} (or {@link C#PRIORITY_PLAYBACK by default)} will
   * be set while the player is loading.
   *
   * @param priorityTaskManager The {@link PriorityTaskManager}, or null to clear a previously set
   *     priority task manager.
   */
  @UnstableApi
  void setPriorityTaskManager(@Nullable PriorityTaskManager priorityTaskManager);

  /**
   * Returns whether the player has paused its main loop to save power in offload scheduling mode.
   *
   * <p>Offload scheduling mode should save significant power when the phone is playing offload
   * audio with the screen off.
   *
   * <p>Offload scheduling is only enabled when playing an audio track in offload mode, which
   * requires all the following:
   *
   * <ul>
   *   <li>Audio offload rendering is enabled through {@link
   *       TrackSelectionParameters.Builder#setAudioOffloadPreferences}.
   *   <li>An audio track is playing in a format that the device supports offloading (for example,
   *       MP3 or AAC).
   *   <li>The {@link AudioSink} is playing with an offload {@link AudioTrack}.
   * </ul>
   *
   * @see AudioOffloadListener#onSleepingForOffloadChanged(boolean)
   */
  @UnstableApi
  boolean isSleepingForOffload();

  /**
   * Returns whether <a
   * href="https://source.android.com/devices/tv/multimedia-tunneling">tunneling</a> is enabled for
   * the currently selected tracks.
   *
   * @see Player.Listener#onTracksChanged(Tracks)
   */
  @UnstableApi
  boolean isTunnelingEnabled();

  /**
   * {@inheritDoc}
   *
   * <p>The exception to the above rule is {@link #isReleased()} which can be called on a released
   * player.
   */
  @Override
  void release();

  /**
   * Returns whether {@link #release()} has been called on the player.
   *
   * <p>This method is allowed to be called after {@link #release()}.
   */
  @UnstableApi
  boolean isReleased();

  /**
   * Sets the {@link ImageOutput} where rendered images will be forwarded.
   *
   * @param imageOutput The {@link ImageOutput}. May be null to clear a previously set image output.
   */
  @UnstableApi
  void setImageOutput(@Nullable ImageOutput imageOutput);
}