| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_IMPL_H_ |
| #define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_IMPL_H_ |
| |
| #include <stddef.h> |
| |
| #include <map> |
| #include <optional> |
| #include <set> |
| #include <vector> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/containers/flat_set.h" |
| #include "base/containers/id_map.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/timer/timer.h" |
| #include "build/build_config.h" |
| #include "content/browser/media/session/audio_focus_delegate.h" |
| #include "content/browser/media/session/media_session_uma_helper.h" |
| #include "content/common/content_export.h" |
| #include "content/public/browser/media_session.h" |
| #include "content/public/browser/page_user_data.h" |
| #include "content/public/browser/presentation_observer.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/browser/web_contents_user_data.h" |
| #include "mojo/public/cpp/bindings/receiver_set.h" |
| #include "mojo/public/cpp/bindings/remote_set.h" |
| #include "services/media_session/public/mojom/audio_focus.mojom.h" |
| #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h" |
| #include "third_party/blink/public/mojom/mediasession/media_session.mojom.h" |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include "base/android/scoped_java_ref.h" |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| namespace media_session { |
| struct MediaMetadata; |
| } // namespace media_session |
| |
| namespace content { |
| |
| class AudioFocusManagerTest; |
| class MediaSessionImplServiceRoutingTest; |
| class MediaSessionImplServiceRoutingThrottleTest; |
| class MediaSessionImplStateObserver; |
| class MediaSessionImplVisibilityBrowserTest; |
| class MediaSessionPlayerObserver; |
| class MediaSessionServiceImpl; |
| class MediaSessionServiceImplBrowserTest; |
| class VideoPictureInPictureWindowControllerImpl; |
| |
| #if BUILDFLAG(IS_ANDROID) |
| class MediaSessionAndroid; |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| // MediaSessionImpl is the implementation of MediaSession. It manages the media |
| // session and audio focus for a given WebContents. It is requesting the audio |
| // focus, pausing when requested by the system and dropping it on demand. The |
| // audio focus can be of two types: Transient or Content. A Transient audio |
| // focus will allow other players to duck instead of pausing and will be |
| // declared as temporary to the system. A Content audio focus will not be |
| // declared as temporary and will not allow other players to duck. If a given |
| // WebContents can only have one audio focus at a time, it will be Content in |
| // case of Transient and Content audio focus are both requested. |
| // TODO(thakis,mlamouri): MediaSessionImpl isn't CONTENT_EXPORT'd because it |
| // creates complicated build issues with WebContentsUserData being a |
| // non-exported template, see https://crbug.com/589840. As a result, the class |
| // uses CONTENT_EXPORT for methods that are being used from tests. |
| // CONTENT_EXPORT should be moved back to the class when the Windows build will |
| // work with it. |
| class MediaSessionImpl : public MediaSession, |
| public WebContentsObserver, |
| public WebContentsUserData<MediaSessionImpl>, |
| public PresentationObserver { |
| public: |
| enum class State { ACTIVE, SUSPENDED, INACTIVE }; |
| |
| // Returns the MediaSessionImpl associated to this WebContents. Creates one if |
| // none is currently available. |
| CONTENT_EXPORT static MediaSessionImpl* Get(WebContents* web_contents); |
| |
| MediaSessionImpl(const MediaSessionImpl&) = delete; |
| MediaSessionImpl& operator=(const MediaSessionImpl&) = delete; |
| |
| ~MediaSessionImpl() override; |
| |
| CONTENT_EXPORT void SetDelegateForTests( |
| std::unique_ptr<AudioFocusDelegate> delegate); |
| |
| #if BUILDFLAG(IS_ANDROID) |
| void ClearMediaSessionAndroid(); |
| MediaSessionAndroid* GetMediaSessionAndroid(); |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| void NotifyMediaSessionMetadataChange(); |
| |
| // Adds the given player to the current media session. Returns whether the |
| // player was successfully added. If it returns false, AddPlayer() should be |
| // called again later. |
| CONTENT_EXPORT bool AddPlayer(MediaSessionPlayerObserver* observer, |
| int player_id); |
| |
| // Removes the given player from the current media session. Abandons audio |
| // focus if that was the last player in the session. |
| CONTENT_EXPORT void RemovePlayer(MediaSessionPlayerObserver* observer, |
| int player_id); |
| |
| // Removes all the players associated with |observer|. Abandons audio focus if |
| // these were the last players in the session. |
| CONTENT_EXPORT void RemovePlayers(MediaSessionPlayerObserver* observer); |
| |
| // Called when a player is paused in the content. |
| // If the paused player is the last player, we suspend the MediaSession. |
| // Otherwise, the paused player will be removed from the MediaSession. |
| CONTENT_EXPORT void OnPlayerPaused(MediaSessionPlayerObserver* observer, |
| int player_id); |
| |
| // Called when the position state of the session might have changed. |
| CONTENT_EXPORT void RebuildAndNotifyMediaPositionChanged(); |
| |
| // Returns if the session is currently active. |
| CONTENT_EXPORT bool IsActive() const; |
| |
| // Returns if the session is currently suspended. |
| CONTENT_EXPORT bool IsSuspended() const; |
| |
| // WebContentsObserver implementation |
| void WebContentsDestroyed() override; |
| void RenderFrameDeleted(RenderFrameHost* rfh) override; |
| void DidFinishNavigation(NavigationHandle* navigation_handle) override; |
| void OnWebContentsFocused(RenderWidgetHost*) override; |
| void OnWebContentsLostFocus(RenderWidgetHost*) override; |
| void TitleWasSet(NavigationEntry* entry) override; |
| void DidUpdateFaviconURL( |
| RenderFrameHost* rfh, |
| const std::vector<blink::mojom::FaviconURLPtr>& candidates) override; |
| void MediaPictureInPictureChanged(bool is_picture_in_picture) override; |
| void RenderFrameHostStateChanged( |
| RenderFrameHost* host, |
| RenderFrameHost::LifecycleState old_state, |
| RenderFrameHost::LifecycleState new_state) override; |
| |
| // MediaSessionService-related methods |
| |
| // Called when a MediaSessionService is created, which registers itself to |
| // this session. |
| void OnServiceCreated(MediaSessionServiceImpl* service); |
| // Called when a MediaSessionService is destroyed, which unregisters itself |
| // from this session. |
| void OnServiceDestroyed(MediaSessionServiceImpl* service); |
| |
| // Called when the playback state of a MediaSessionService has |
| // changed. Will notify observers of media session state change. |
| void OnMediaSessionPlaybackStateChanged(MediaSessionServiceImpl* service); |
| |
| // Called when the metadata of a MediaSessionService has changed. Will notify |
| // observers if the service is currently routed. |
| void OnMediaSessionMetadataChanged(MediaSessionServiceImpl* service); |
| |
| // Called when the actions of a MediaSessionService has changed. Will notify |
| // observers if the service is currently routed. |
| void OnMediaSessionActionsChanged(MediaSessionServiceImpl* service); |
| |
| // Called when the info of a MediaSessionService has changed. Will notify |
| // observers if the service is currently routed. |
| void OnMediaSessionInfoChanged(MediaSessionServiceImpl* service); |
| |
| // Requests audio focus to the AudioFocusDelegate. |
| // Returns whether the request was granted. |
| CONTENT_EXPORT AudioFocusDelegate::AudioFocusResult RequestSystemAudioFocus( |
| media_session::mojom::AudioFocusType audio_focus_type); |
| |
| // Creates a binding between |this| and |request|. |
| mojo::PendingRemote<media_session::mojom::MediaSession> AddRemote(); |
| |
| // Returns if the session can be controlled by the user. |
| CONTENT_EXPORT bool IsControllable() const; |
| |
| // MediaSession overrides --------------------------------------------------- |
| |
| // Resume the media session. |
| // |type| represents the origin of the request. |
| CONTENT_EXPORT void Resume(MediaSession::SuspendType suspend_type) override; |
| |
| // Stop the media session. |
| // |type| represents the origin of the request. |
| CONTENT_EXPORT void Stop(MediaSession::SuspendType suspend_type) override; |
| |
| // Seek the media session. |
| CONTENT_EXPORT void Seek(base::TimeDelta seek_time) override; |
| |
| // Called when a MediaSessionAction is received. The action will be forwarded |
| // to blink::MediaSession corresponding to the current routed service. |
| void DidReceiveAction( |
| media_session::mojom::MediaSessionAction action) override; |
| |
| // Set the volume multiplier applied during ducking. |
| CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override; |
| |
| // Set the audio focus group id for this media session. Sessions in the same |
| // group can share audio focus. Setting this to null will use the browser |
| // default value. |
| CONTENT_EXPORT void SetAudioFocusGroupId( |
| const base::UnguessableToken& group_id) override; |
| |
| // Returns the `RenderFrameHost` for the currently MediaSession routed |
| // service, if the routed service exists, otherwise returns the top most frame |
| // with an active media player. |
| RenderFrameHost* GetRoutedFrame() override; |
| |
| // Returns the current media session info synchronously for a one-off request. |
| CONTENT_EXPORT media_session::mojom::MediaSessionInfoPtr |
| GetMediaSessionInfoSync() override; |
| |
| // Returns the current media session position for a one-off request. |
| CONTENT_EXPORT std::optional<media_session::MediaPosition> |
| GetMediaSessionPosition() override; |
| |
| // Returns the current media session metadata for a one-off request. |
| CONTENT_EXPORT const media_session::MediaMetadata& GetMediaSessionMetadata() |
| override; |
| |
| // Suspend the media session. |
| // |type| represents the origin of the request. |
| CONTENT_EXPORT void Suspend(MediaSession::SuspendType suspend_type) override; |
| |
| // Let the media session start ducking such that the volume multiplier is |
| // reduced. |
| CONTENT_EXPORT void StartDucking() override; |
| |
| // Let the media session stop ducking such that the volume multiplier is |
| // recovered. |
| CONTENT_EXPORT void StopDucking() override; |
| |
| // Returns information about the MediaSession. The sync method is not actually |
| // slower and should be used over the async one which is available over mojo. |
| void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override; |
| |
| // Returns debugging information to be displayed on chrome://media-internals. |
| void GetDebugInfo(GetDebugInfoCallback) override; |
| |
| // Adds a mojo based observer to listen to events related to this session. |
| void AddObserver( |
| mojo::PendingRemote<media_session::mojom::MediaSessionObserver> observer) |
| override; |
| |
| // Called by |AudioFocusDelegate| when an async audio focus request is |
| // completed. |
| CONTENT_EXPORT void FinishSystemAudioFocusRequest( |
| media_session::mojom::AudioFocusType type, |
| bool result); |
| |
| // Skip to the previous track. |
| CONTENT_EXPORT void PreviousTrack() override; |
| |
| // Skip to the next track. |
| CONTENT_EXPORT void NextTrack() override; |
| |
| // Skip ad. |
| CONTENT_EXPORT void SkipAd() override; |
| |
| // Go back to previous slide. |
| CONTENT_EXPORT void PreviousSlide() override; |
| |
| // Go to next slide. |
| CONTENT_EXPORT void NextSlide() override; |
| |
| // Seek the media session to a specific time. |
| void SeekTo(base::TimeDelta seek_time) override; |
| |
| // Scrub ("fast seek") the media session to a specific time. |
| void ScrubTo(base::TimeDelta seek_time) override; |
| |
| // Enter picture-in-picture. |
| void EnterPictureInPicture() override; |
| |
| // Exit picture-in-picture. |
| void ExitPictureInPicture() override; |
| |
| // Automatically enter picture-in-picture from a non-user source (e.g. in |
| // reaction to content being hidden). |
| void EnterAutoPictureInPicture() override; |
| |
| // Routes the audio from this Media Session to the given output device. If |
| // |id| is null, we will route to the default output device. |
| // Players created after this setting has been set will also have their audio |
| // rerouted. This setting persists until cross-origin navigation occurs, the |
| // renderer reports an audio sink change to a device different from |id|, or |
| // this method is called again. |
| void SetAudioSinkId(const std::optional<std::string>& id) override; |
| |
| // Mute/Unmute the microphone for a WebRTC session. |
| void ToggleMicrophone() override; |
| |
| // Turn on or off the camera for a WebRTC session. |
| void ToggleCamera() override; |
| |
| // Hang up a WebRTC session. |
| void HangUp() override; |
| |
| // Brings the associated tab into focus. |
| void Raise() override; |
| |
| // Mute or unmute the media player. |
| void SetMute(bool mute) override; |
| |
| // Request the media player to start Media Remoting once there are available |
| // sinks. |
| void RequestMediaRemoting() override; |
| |
| // PresentationObserver: |
| void OnPresentationsChanged(bool has_presentation) override; |
| |
| // Downloads the bitmap version of a MediaImage at least |minimum_size_px| |
| // and closest to |desired_size_px|. If the download failed, was too small or |
| // the image did not come from the media session then returns a null image. |
| CONTENT_EXPORT void GetMediaImageBitmap( |
| const media_session::MediaImage& image, |
| int minimum_size_px, |
| int desired_size_px, |
| GetMediaImageBitmapCallback callback) override; |
| |
| // Called to report to all players that the auto picture in picture |
| // information changed. |
| void ReportAutoPictureInPictureInfoChanged() override; |
| |
| const base::UnguessableToken& audio_focus_group_id() const { |
| return audio_focus_group_id_; |
| } |
| |
| void OnMediaMutedStatusChanged(bool mute); |
| |
| void OnPictureInPictureAvailabilityChanged(); |
| |
| // Called when any of the normal players have switched to a different audio |
| // output device. |
| void OnAudioOutputSinkIdChanged(); |
| |
| // Called when any of the normal players can no longer support audio output |
| // device switching. |
| void OnAudioOutputSinkChangingDisabled(); |
| |
| // Called when any of the normal players video visibility changes. |
| CONTENT_EXPORT void OnVideoVisibilityChanged(); |
| |
| // Update the value of `remote_playback_metadata_`. |
| CONTENT_EXPORT void SetRemotePlaybackMetadata( |
| media_session::mojom::RemotePlaybackMetadataPtr metadata); |
| |
| // Returns whether the action should be routed to |routed_service_|. |
| CONTENT_EXPORT bool ShouldRouteAction( |
| media_session::mojom::MediaSessionAction action) const; |
| |
| // Returns the source ID which links media sessions on the same browser |
| // context together. |
| CONTENT_EXPORT const base::UnguessableToken& GetSourceId() const; |
| |
| // Returns the Audio Focus request ID associated with this media session. |
| const base::UnguessableToken& GetRequestId() const; |
| |
| // Flushes the VideoPictureInPictureWindowControllerImpl with the latest data. |
| void UpdateVideoPictureInPictureWindowController( |
| VideoPictureInPictureWindowControllerImpl* pip_controller) const; |
| |
| // Returns a WeakPtr to `this`. |
| base::WeakPtr<MediaSessionImpl> GetWeakPtr(); |
| |
| CONTENT_EXPORT bool HasImageCacheForTest(const GURL& image_url) const; |
| |
| // Make sure that all observers have received any pending callbacks from us, |
| // that might otherwise be sitting in a message pipe somewhere. |
| void flush_observers_for_testing() { observers_.FlushForTesting(); } |
| |
| private: |
| friend class content::WebContentsUserData<MediaSessionImpl>; |
| friend class MediaSessionImplBrowserTest; |
| friend class content::MediaSessionImplVisibilityBrowserTest; |
| friend class content::AudioFocusManagerTest; |
| friend class content::MediaSessionImplServiceRoutingTest; |
| friend class content::MediaSessionImplServiceRoutingThrottleTest; |
| friend class content::MediaSessionImplStateObserver; |
| friend class content::MediaSessionServiceImplBrowserTest; |
| friend class MediaSessionImplTest; |
| friend class MediaSessionImplDurationThrottleTest; |
| friend class MediaInternalsAudioFocusTest; |
| friend class WebAppSystemMediaControlsBrowserTest; |
| |
| CONTENT_EXPORT void RemoveAllPlayersForTest(); |
| CONTENT_EXPORT MediaSessionUmaHelper* uma_helper_for_test(); |
| |
| // Representation of a player for the MediaSessionImpl. |
| struct PlayerIdentifier { |
| PlayerIdentifier(MediaSessionPlayerObserver* observer, int player_id); |
| |
| PlayerIdentifier(const PlayerIdentifier&) = default; |
| PlayerIdentifier(PlayerIdentifier&&) = default; |
| |
| PlayerIdentifier& operator=(const PlayerIdentifier&) = default; |
| PlayerIdentifier& operator=(PlayerIdentifier&&) = default; |
| |
| friend bool operator==(const PlayerIdentifier&, |
| const PlayerIdentifier&) = default; |
| friend auto operator<=>(const PlayerIdentifier&, |
| const PlayerIdentifier&) = default; |
| // RAW_PTR_EXCLUSION: #union |
| RAW_PTR_EXCLUSION MediaSessionPlayerObserver* observer; |
| int player_id; |
| }; |
| |
| CONTENT_EXPORT explicit MediaSessionImpl(WebContents* web_contents); |
| |
| void Initialize(); |
| |
| // Called when we have finished downloading an image. |
| void OnImageDownloadComplete(GetMediaImageBitmapCallback callback, |
| int minimum_size_px, |
| int desired_size_px, |
| bool source_icon, |
| int id, |
| int http_status_code, |
| const GURL& image_url, |
| const std::vector<SkBitmap>& bitmaps, |
| const std::vector<gfx::Size>& sizes); |
| |
| // Called when system audio focus has been requested and whether the request |
| // was granted. |
| void OnSystemAudioFocusRequested(bool result); |
| |
| CONTENT_EXPORT void OnSuspendInternal(MediaSession::SuspendType suspend_type, |
| State new_state); |
| CONTENT_EXPORT void OnResumeInternal(MediaSession::SuspendType suspend_type); |
| |
| // To be called after a call to AbandonAudioFocus() in order request the |
| // delegate to abandon the audio focus. |
| CONTENT_EXPORT void AbandonSystemAudioFocusIfNeeded(); |
| |
| // Internal method that should be used instead of setting audio_focus_state_. |
| // It sets audio_focus_state_ and notifies observers about the state change. |
| void SetAudioFocusState(State audio_focus_state); |
| |
| // Flushes any mojo bindings for testing. |
| CONTENT_EXPORT void FlushForTesting(); |
| |
| // Notifies |observers_| and |delegate_| that |MediaSessionInfo| has changed. |
| void RebuildAndNotifyMediaSessionInfoChanged(); |
| |
| // Update the volume multiplier when ducking state changes. |
| void UpdateVolumeMultiplier(); |
| |
| // Get the volume multiplier, which depends on whether the media session is |
| // ducking. |
| double GetVolumeMultiplier() const; |
| |
| CONTENT_EXPORT bool AddOneShotPlayer(MediaSessionPlayerObserver* observer, |
| int player_id); |
| |
| CONTENT_EXPORT bool AddAmbientPlayer(MediaSessionPlayerObserver* observer, |
| int player_id); |
| |
| // Returns true if there is at least one player and all the players are |
| // one-shot. |
| bool HasOnlyOneShotPlayers() const; |
| |
| // MediaSessionService-related methods |
| |
| // Called when the routed service may have changed. |
| void UpdateRoutedService(); |
| |
| // Returns whether the frame |rfh| uses MediaSession API. |
| bool IsServiceActiveForRenderFrameHost(RenderFrameHost* rfh); |
| |
| // Compute the frame that should be routed for media session. If |
| // |ensure_service| is true, the routed frame must have an active |
| // MediaSessionService, otherwise it does not, e.g. when no MediaSession API |
| // has been called but there is an active media player. This method can be |
| // used to compute both the routed frame and routed service. |
| CONTENT_EXPORT RenderFrameHost* ComputeFrameForRouting(bool ensure_service); |
| |
| // Rebuilds |actions_| and notifies observers if they have changed. |
| void RebuildAndNotifyActionsChanged(); |
| |
| // Rebuilds |metadata_| and |images_| and notifies observers if they have |
| // changed. |
| void RebuildAndNotifyMetadataChanged(); |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| void BuildPlaceholderMetadata( |
| media_session::MediaMetadata& metadata, |
| std::vector<media_session::MediaImage>& artwork); |
| #endif |
| |
| void BuildMetadata(media_session::MediaMetadata& metadata, |
| std::vector<media_session::MediaImage>& artwork); |
| |
| bool IsPictureInPictureAvailable() const; |
| |
| // Iterates over all |normal_players_| and returns true if any of the players' |
| // videos is sufficiently visible, false otherwise. |
| CONTENT_EXPORT bool HasSufficientlyVisibleVideo() const; |
| |
| // Iterates over all |normal_players_| and returns true if any of the players' |
| // videos is sufficiently visible, false otherwise. |
| // |
| // This is very similar to `HasSufficientlyVisibleVideo`, however this method |
| // is used to get notifications on demand, while `HasSufficientlyVisibleVideo` |
| // is constantly reporting visibility. |
| void GetVisibility(GetVisibilityCallback get_visibility_callback) override; |
| |
| // Returns the device ID for the audio output device being used by all of the |
| // normal players. If the players are not all using the same audio output |
| // device, the id of the default device will be returned. |
| std::string GetSharedAudioOutputDeviceId() const; |
| |
| bool IsAudioOutputDeviceSwitchingSupported() const; |
| |
| // Called when a MediaSessionAction is received. The action will be forwarded |
| // to blink::MediaSession corresponding to the current routed service. |
| void DidReceiveAction(media_session::mojom::MediaSessionAction action, |
| blink::mojom::MediaSessionActionDetailsPtr details); |
| |
| // Returns the media audio video state for each player. This is whether the |
| // players associated with the media session are audio-only, video-only, or |
| // have both audio and video. If we have a |routed_service_| then we limit to |
| // players on that frame because this should align with the metadata. |
| std::vector<media_session::mojom::MediaAudioVideoState> |
| GetMediaAudioVideoStates(); |
| |
| // Calls the callback with each |PlayerIdentifier| for every player associated |
| // with this media session. |
| void ForAllPlayers(base::RepeatingCallback<void(const PlayerIdentifier&)>); |
| |
| // Restrict duration update under certain frequency. |
| std::optional<media_session::MediaPosition> MaybeGuardDurationUpdate( |
| std::optional<media_session::MediaPosition> position); |
| |
| void IncreaseDurationUpdateAllowance(); |
| |
| void ResetDurationUpdateGuard(); |
| |
| CONTENT_EXPORT void SetShouldThrottleDurationUpdateForTest( |
| bool should_throttle); |
| |
| // True if `routed_service_` exists and either the camera or microphone are |
| // currently actively used, false otherwise. |
| bool IsActivelyUsingCameraOrMicrophone() const; |
| |
| // Returns true if there exists a single normal "playing" player with picture |
| // in picture available, false otherwise. |
| bool CouldEnterBrowserInitiatedAutomaticPictureInPicture() const; |
| |
| // Automatically enter picture-in-picture from a non-user source (e.g. in |
| // reaction to content being hidden), if the EnterAutoPictureInPicture action |
| // is registered by the browser (the user did not provide an |
| // `enterpictureinpicture` action handler). |
| void MaybeEnterBrowserInitiatedAutomaticPictureInPicture() const; |
| |
| // Duration update allowance is inscreasing by 1 every 20 seconds, and |
| // capped at 3. Every duration updates will consume 1 allowance, and |
| // if updates happen when we have 0 allowance, we consider the media as |
| // a livestream and stop instreasing allowance until the time difference |
| // between two updates is greater than 20 seconds. |
| CONTENT_EXPORT static constexpr int kDurationUpdateMaxAllowance = 3; |
| CONTENT_EXPORT static constexpr base::TimeDelta |
| kDurationUpdateAllowanceIncreaseInterval = base::Seconds(20); |
| |
| // A set of actions supported by |routed_service_| and the current media |
| // session. |
| std::set<media_session::mojom::MediaSessionAction> actions_; |
| |
| std::unique_ptr<AudioFocusDelegate> delegate_; |
| |
| // Standard video playback (e.g. WebMediaPlayerImpl players). |
| std::map<PlayerIdentifier, media_session::mojom::AudioFocusType> |
| normal_players_; |
| |
| // Players that are playing in the web contents but we cannot control (e.g. |
| // MediaStream). |
| base::flat_set<PlayerIdentifier> one_shot_players_; |
| |
| // Players that we can neither control nor should affect other players in the |
| // audio focus stack (e.g. WebAudio). |
| base::flat_set<PlayerIdentifier> ambient_players_; |
| |
| // Players that are removed from |normal_players_| temporarily when the page |
| // goes to back-forward cache. When the page is restored from the cache, these |
| // players are also restored to |normal_players_|. |
| base::flat_set<PlayerIdentifier> hidden_players_; |
| |
| State audio_focus_state_ = State::INACTIVE; |
| MediaSession::SuspendType suspend_type_; |
| |
| // The |desired_audio_focus_type_| is the AudioFocusType we will request when |
| // we request system audio focus. |
| media_session::mojom::AudioFocusType desired_audio_focus_type_; |
| |
| // The last updated |MediaSessionInfo| that was sent to |observers_|. |
| media_session::mojom::MediaSessionInfoPtr session_info_; |
| |
| // The last updated |MediaPosition| that was sent to |observers_|. |
| std::optional<media_session::MediaPosition> position_; |
| |
| MediaSessionUmaHelper uma_helper_; |
| |
| // The ducking state of this media session. The initial value is |false|, and |
| // is set to |true| after StartDucking(), and will be set to |false| after |
| // StopDucking(). |
| bool is_ducking_; |
| |
| // True if we should unduck when we gain audio focus. This is set to true each |
| // time we request focus, and set to false if the AudioFocusManager tells us |
| // to duck. If our request is granted and this is still true, we will unduck. |
| // If false (because we were told to duck after our request began) we will |
| // remain ducked, as that is the intended state the AudioFocusManager expects. |
| bool should_unduck_on_focus_gained_ = true; |
| |
| base::UnguessableToken audio_focus_group_id_ = base::UnguessableToken::Null(); |
| |
| double ducking_volume_multiplier_; |
| |
| // True if the WebContents associated with this MediaSessionImpl is focused. |
| bool focused_ = false; |
| |
| bool is_muted_ = false; |
| |
| // Used to persist audio device selection between navigations on the same |
| // origin. |
| url::Origin origin_; |
| std::optional<std::string> audio_device_id_for_origin_; |
| |
| class PageData : public content::PageUserData<PageData> { |
| public: |
| explicit PageData(content::Page& page); |
| |
| PageData(const PageData&) = delete; |
| PageData& operator=(const PageData&) = delete; |
| |
| ~PageData() override; |
| |
| void AddImageCache(const GURL& image_url, const SkBitmap& bitmap) { |
| image_cache_.emplace(image_url, bitmap); |
| } |
| |
| const SkBitmap* GetImageCache(const GURL& image_url) const { |
| auto it = image_cache_.find(image_url); |
| if (it == image_cache_.end()) |
| return nullptr; |
| |
| return &it->second; |
| } |
| |
| PAGE_USER_DATA_KEY_DECL(); |
| |
| private: |
| // Cache of images that have been requested by clients. |
| base::flat_map<GURL, SkBitmap> image_cache_; |
| }; |
| |
| // Returns the PageData for the specified |page|. |
| PageData& GetPageData(content::Page& page) const; |
| |
| #if BUILDFLAG(IS_ANDROID) |
| std::unique_ptr<MediaSessionAndroid> session_android_; |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| // MediaSessionService-related fields |
| using ServicesMap = |
| std::map<GlobalRenderFrameHostId, |
| raw_ptr<MediaSessionServiceImpl, CtnExperimental>>; |
| |
| // The current metadata and images associated with the current media session. |
| media_session::MediaMetadata metadata_; |
| base::flat_map<media_session::mojom::MediaSessionImageType, |
| std::vector<media_session::MediaImage>> |
| images_; |
| |
| // The collection of all managed services (non-owned pointers). The services |
| // are owned by RenderFrameHost and should be registered on creation and |
| // unregistered on destroy. |
| ServicesMap services_; |
| // The currently routed service (non-owned pointer). |
| raw_ptr<MediaSessionServiceImpl> routed_service_; |
| |
| // Bindings for Mojo pointers to |this| held by media route providers. |
| mojo::ReceiverSet<media_session::mojom::MediaSession> receivers_; |
| |
| mojo::RemoteSet<media_session::mojom::MediaSessionObserver> observers_; |
| |
| base::RepeatingTimer duration_update_allowance_timer_; |
| |
| bool is_throttling_ = false; |
| |
| // This is guaranteed to be reset to |kDurationUpdateMaxAllowance| at |
| // first update because |guarding_player_id_| is always a mismatch |
| // at first, and will trigger a reset. |
| int duration_update_allowance_ = 0; |
| |
| bool should_throttle_duration_update_ = false; |
| |
| // Whether the associated WebContents is connected to a presentation. |
| bool has_presentation_ = false; |
| |
| std::optional<PlayerIdentifier> guarding_player_id_; |
| |
| media_session::mojom::RemotePlaybackMetadataPtr remote_playback_metadata_; |
| |
| // Used by tests to force media sessions to be ignored when finding a new |
| // active session. |
| bool always_ignore_for_active_session_for_testing_ = false; |
| |
| // True if the given media has infinite duration OR has a duration that |
| // changes often enough to be considered live. See |
| // `MaybeGuardDurationUpdate()` for details on duration changes. |
| bool is_considered_live_ = false; |
| |
| base::WeakPtrFactory<MediaSessionImpl> weak_factory_{this}; |
| |
| WEB_CONTENTS_USER_DATA_KEY_DECL(); |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_IMPL_H_ |