#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 "media/base/picture_in_picture_events_info.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"
#include "arkweb/build/features/features.h"
#if BUILDFLAG(ARKWEB_MEDIA_POLICY)
#include "media/base/media_content_type.h"
#endif
#if BUILDFLAG(ARKWEB_MEDIA_MEMORY_PRESSURE)
#include "base/memory/memory_pressure_listener.h"
#endif
#if BUILDFLAG(IS_ANDROID)
#include "base/android/scoped_java_ref.h"
#endif
#if BUILDFLAG(ARKWEB_MEDIA_POLICY)
namespace media {
class OHOSAudioOutputStream;
}
#endif
namespace media_session {
struct MediaMetadata;
}
namespace content {
class AudioFocusManagerTest;
class MediaSessionImplServiceRoutingTest;
class MediaSessionImplServiceRoutingThrottleTest;
class MediaSessionImplStateObserver;
class MediaSessionImplVisibilityBrowserTest;
class MediaSessionPlayerObserver;
class MediaSessionServiceImpl;
class MediaSessionServiceImplBrowserTest;
class MediaSessionImplUtils;
class VideoPictureInPictureWindowControllerImpl;
#if BUILDFLAG(IS_ANDROID)
class MediaSessionAndroid;
#endif
#if BUILDFLAG(ARKWEB_MEDIA_AVSESSION)
class MediaSessionOHOS;
#endif
class CONTENT_EXPORT MediaSessionImpl : public MediaSession,
public WebContentsObserver,
public WebContentsUserData<MediaSessionImpl>,
public PresentationObserver {
public:
enum class State { ACTIVE, SUSPENDED, INACTIVE };
friend class MediaSessionImplUtils;
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
void NotifyMediaSessionMetadataChange();
CONTENT_EXPORT bool AddPlayer(MediaSessionPlayerObserver* observer,
int player_id);
CONTENT_EXPORT void RemovePlayer(MediaSessionPlayerObserver* observer,
int player_id);
CONTENT_EXPORT void RemovePlayers(MediaSessionPlayerObserver* observer);
CONTENT_EXPORT void OnPlayerPaused(MediaSessionPlayerObserver* observer,
int player_id);
CONTENT_EXPORT void RebuildAndNotifyMediaPositionChanged();
CONTENT_EXPORT bool IsActive() const;
CONTENT_EXPORT bool IsSuspended() const;
void WebContentsDestroyed() override;
void RenderFrameDeleted(RenderFrameHost* rfh) override;
void PrimaryPageChanged(content::Page& page) 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;
void OnServiceCreated(MediaSessionServiceImpl* service);
void OnServiceDestroyed(MediaSessionServiceImpl* service);
void OnMediaSessionPlaybackStateChanged(MediaSessionServiceImpl* service);
void OnMediaSessionMetadataChanged(MediaSessionServiceImpl* service);
void OnMediaSessionActionsChanged(MediaSessionServiceImpl* service);
void OnMediaSessionInfoChanged(MediaSessionServiceImpl* service);
CONTENT_EXPORT AudioFocusDelegate::AudioFocusResult RequestSystemAudioFocus(
media_session::mojom::AudioFocusType audio_focus_type);
mojo::PendingRemote<media_session::mojom::MediaSession> AddRemote();
CONTENT_EXPORT bool IsControllable() const;
CONTENT_EXPORT void Resume(MediaSession::SuspendType suspend_type) override;
CONTENT_EXPORT void Stop(MediaSession::SuspendType suspend_type) override;
CONTENT_EXPORT void Seek(base::TimeDelta seek_time) override;
void DidReceiveAction(
media_session::mojom::MediaSessionAction action) override;
CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override;
CONTENT_EXPORT void SetAudioFocusGroupId(
const base::UnguessableToken& group_id) override;
RenderFrameHost* GetRoutedFrame() override;
CONTENT_EXPORT media_session::mojom::MediaSessionInfoPtr
GetMediaSessionInfoSync() override;
CONTENT_EXPORT std::optional<media_session::MediaPosition>
GetMediaSessionPosition() override;
CONTENT_EXPORT const media_session::MediaMetadata& GetMediaSessionMetadata()
override;
CONTENT_EXPORT void Suspend(MediaSession::SuspendType suspend_type) override;
CONTENT_EXPORT void StartDucking() override;
CONTENT_EXPORT void StopDucking() override;
void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override;
void GetDebugInfo(GetDebugInfoCallback) override;
void AddObserver(
mojo::PendingRemote<media_session::mojom::MediaSessionObserver> observer)
override;
CONTENT_EXPORT void FinishSystemAudioFocusRequest(
media_session::mojom::AudioFocusType type,
bool result);
CONTENT_EXPORT void PreviousTrack() override;
CONTENT_EXPORT void NextTrack() override;
CONTENT_EXPORT void SkipAd() override;
CONTENT_EXPORT void PreviousSlide() override;
CONTENT_EXPORT void NextSlide() override;
void SeekTo(base::TimeDelta seek_time) override;
void ScrubTo(base::TimeDelta seek_time) override;
void EnterPictureInPicture() override;
void ExitPictureInPicture() override;
void EnterAutoPictureInPicture() override;
void SetAudioSinkId(const std::optional<std::string>& id) override;
void ToggleMicrophone() override;
void ToggleCamera() override;
void HangUp() override;
void Raise() override;
void SetMute(bool mute) override;
void RequestMediaRemoting() override;
void OnPresentationsChanged(bool has_presentation) override;
CONTENT_EXPORT void GetMediaImageBitmap(
const media_session::MediaImage& image,
int minimum_size_px,
int desired_size_px,
GetMediaImageBitmapCallback callback) override;
void ReportAutoPictureInPictureInfoChanged() override;
const base::UnguessableToken& audio_focus_group_id() const {
return audio_focus_group_id_;
}
void OnMediaMutedStatusChanged(bool mute);
void OnPictureInPictureAvailabilityChanged();
void OnAudioOutputSinkIdChanged();
void OnAudioOutputSinkChangingDisabled();
CONTENT_EXPORT void OnVideoVisibilityChanged();
CONTENT_EXPORT void SetRemotePlaybackMetadata(
media_session::mojom::RemotePlaybackMetadataPtr metadata);
CONTENT_EXPORT bool ShouldRouteAction(
media_session::mojom::MediaSessionAction action) const;
CONTENT_EXPORT const base::UnguessableToken& GetSourceId() const;
const base::UnguessableToken& GetRequestId() const;
void UpdateVideoPictureInPictureWindowController(
VideoPictureInPictureWindowControllerImpl* pip_controller) const;
base::WeakPtr<MediaSessionImpl> GetWeakPtr();
CONTENT_EXPORT bool HasImageCacheForTest(const GURL& image_url) const;
void flush_observers_for_testing() { observers_.FlushForTesting(); }
#if BUILDFLAG(ARKWEB_MEDIA_POLICY)
bool HasOnlyOneShotPlayersPublic() const;
bool HasOneShotPlayersWhenSetMetadataPublic() const;
#endif
#if BUILDFLAG(ARKWEB_MEDIA_AVSESSION)
void PutWebMediaAVSessionEnabled(bool enable);
#endif
#if BUILDFLAG(ARKWEB_MEDIA_POLICY)
public:
enum NWebPlaybackState { NONE, PLAYING, PAUSED, STOP };
enum NWebMediaSessionState { NOINITIAL, NONEED, NEED };
NWebPlaybackState NWebGetState();
bool IsEndOfMedia();
void SetEndOfMedia(bool end_of_media);
bool GetPlayingState();
void SetPlayingState(bool playingState);
bool GetMuteState();
bool IsPlayingAudio();
bool IsPauseByAvsession();
void SetPauseByAvsession(bool is_pause);
void SetWebviewShow(bool show, bool is_special_for_audio);
void EndSessionWhenHide();
void SetMediaContentType(media::MediaContentType media_content_type) { media_content_type_ = media_content_type; }
media::MediaContentType getMediaContentType() { return media_content_type_; }
void SetSessionState(NWebMediaSessionState sessionState);
NWebMediaSessionState GetSessionState();
std::unordered_set<media::OHOSAudioOutputStream*> activeAudioStream_;
int audioResumeInterval_ = 0;
bool audioExclusive_ = true;
int audioSessionType_ = 0;
bool isPlayingState_ = false;
bool fileAccess_ = false;
std::vector<std::string> grantMediaFileAccessDirs_;
NWebMediaSessionState sessionState_ = NWebMediaSessionState::NOINITIAL;
bool has_one_shot_players_ = false;
#endif
#if BUILDFLAG(ARKWEB_PIP)
void OnPictureInPictureStateChanged(
const MediaPlayerId& id, uint32_t state, int32_t width, int32_t height);
#endif
#if BUILDFLAG(ARKWEB_MEDIA_MEMORY_PRESSURE)
void OnNotifyMemoryLevel(int32_t level);
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level_ =
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
#endif
#if BUILDFLAG(ARKWEB_MEDIA_POLICY)
void UpdateMediaPlayersMuteState(int player_id, bool mute);
bool GetMediaPlayerMuteState();
#endif
#if BUILDFLAG(ARKWEB_TEST)
public:
#else
private:
#endif
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;
#if BUILDFLAG(ARKWEB_MEDIA_AVSESSION)
friend class MediaSessionOHOS;
#endif
raw_ptr<MediaSessionImplUtils> implUtils_;
CONTENT_EXPORT void RemoveAllPlayersForTest();
CONTENT_EXPORT MediaSessionUmaHelper* uma_helper_for_test();
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 MediaSessionPlayerObserver* observer;
int player_id;
};
CONTENT_EXPORT explicit MediaSessionImpl(WebContents* web_contents);
void Initialize();
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);
void OnSystemAudioFocusRequested(bool result);
CONTENT_EXPORT void OnSuspendInternal(MediaSession::SuspendType suspend_type,
State new_state);
CONTENT_EXPORT void OnResumeInternal(MediaSession::SuspendType suspend_type);
CONTENT_EXPORT void AbandonSystemAudioFocusIfNeeded();
void SetAudioFocusState(State audio_focus_state);
CONTENT_EXPORT void FlushForTesting();
void RebuildAndNotifyMediaSessionInfoChanged();
void UpdateVolumeMultiplier();
double GetVolumeMultiplier() const;
CONTENT_EXPORT bool AddOneShotPlayer(MediaSessionPlayerObserver* observer,
int player_id);
CONTENT_EXPORT bool AddAmbientPlayer(MediaSessionPlayerObserver* observer,
int player_id);
bool HasOnlyOneShotPlayers() const;
void UpdateRoutedService();
bool IsServiceActiveForRenderFrameHost(RenderFrameHost* rfh);
CONTENT_EXPORT RenderFrameHost* ComputeFrameForRouting(bool ensure_service);
void RebuildAndNotifyActionsChanged();
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;
CONTENT_EXPORT bool HasSufficientlyVisibleVideo() const;
void GetVisibility(GetVisibilityCallback get_visibility_callback) override;
std::string GetSharedAudioOutputDeviceId() const;
bool IsAudioOutputDeviceSwitchingSupported() const;
void DidReceiveAction(media_session::mojom::MediaSessionAction action,
blink::mojom::MediaSessionActionDetailsPtr details);
std::vector<media_session::mojom::MediaAudioVideoState>
GetMediaAudioVideoStates();
void ForAllPlayers(base::RepeatingCallback<void(const PlayerIdentifier&)>);
std::optional<media_session::MediaPosition> MaybeGuardDurationUpdate(
std::optional<media_session::MediaPosition> position);
void IncreaseDurationUpdateAllowance();
void ResetDurationUpdateGuard();
CONTENT_EXPORT void SetShouldThrottleDurationUpdateForTest(
bool should_throttle);
bool IsActivelyUsingCameraOrMicrophone() const;
bool CanEnterBrowserInitiatedAutomaticPictureInPicture() const;
void MaybeEnterBrowserInitiatedAutomaticPictureInPicture();
void NotifyPlayerOfAutoPictureInPictureInfo(
MediaSessionPlayerObserver* observer,
int player_id);
CONTENT_EXPORT static constexpr int kDurationUpdateMaxAllowance = 3;
CONTENT_EXPORT static constexpr base::TimeDelta
kDurationUpdateAllowanceIncreaseInterval = base::Seconds(20);
std::set<media_session::mojom::MediaSessionAction> actions_;
std::unique_ptr<AudioFocusDelegate> delegate_;
std::map<PlayerIdentifier, media_session::mojom::AudioFocusType>
normal_players_;
base::flat_set<PlayerIdentifier> one_shot_players_;
base::flat_set<PlayerIdentifier> ambient_players_;
base::flat_set<PlayerIdentifier> hidden_players_;
State audio_focus_state_ = State::INACTIVE;
MediaSession::SuspendType suspend_type_;
media_session::mojom::AudioFocusType desired_audio_focus_type_;
media_session::mojom::MediaSessionInfoPtr session_info_;
std::optional<media_session::MediaPosition> position_;
MediaSessionUmaHelper uma_helper_;
bool is_ducking_;
bool should_unduck_on_focus_gained_ = true;
base::UnguessableToken audio_focus_group_id_ = base::UnguessableToken::Null();
double ducking_volume_multiplier_;
bool focused_ = false;
bool is_muted_ = false;
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:
base::flat_map<GURL, SkBitmap> image_cache_;
};
#if BUILDFLAG(ARKWEB_MEDIA_POLICY)
media::MediaContentType media_content_type_;
std::unordered_map<int, bool> players_mute_state_;
#endif
PageData& GetPageData(content::Page& page) const;
#if BUILDFLAG(IS_ANDROID)
std::unique_ptr<MediaSessionAndroid> session_android_;
#endif
#if BUILDFLAG(ARKWEB_MEDIA_AVSESSION)
std::unique_ptr<MediaSessionOHOS> session_ohos_;
void CreateSessionOhos();
#endif
using ServicesMap =
std::map<GlobalRenderFrameHostId,
raw_ptr<MediaSessionServiceImpl, CtnExperimental>>;
media_session::MediaMetadata metadata_;
base::flat_map<media_session::mojom::MediaSessionImageType,
std::vector<media_session::MediaImage>>
images_;
ServicesMap services_;
raw_ptr<MediaSessionServiceImpl> routed_service_;
mojo::ReceiverSet<media_session::mojom::MediaSession> receivers_;
mojo::RemoteSet<media_session::mojom::MediaSessionObserver> observers_;
base::RepeatingTimer duration_update_allowance_timer_;
bool is_throttling_ = false;
int duration_update_allowance_ = 0;
bool should_throttle_duration_update_ = false;
bool has_presentation_ = false;
std::optional<PlayerIdentifier> guarding_player_id_;
media_session::mojom::RemotePlaybackMetadataPtr remote_playback_metadata_;
bool always_ignore_for_active_session_for_testing_ = false;
bool is_considered_live_ = false;
#if BUILDFLAG(ARKWEB_MEDIA_POLICY)
public:
base::WeakPtrFactory<content::MediaSessionImpl> weakMediaSessionFactory_;
#endif
#if BUILDFLAG(ARKWEB_MEDIA_CAST)
void OnNotifyMeidaCastUri(const std::string& media_uri);
void CreateAVCastAdapter();
void HandleStopMediaCast();
int32_t GetMediaCastCurrentTime();
void PullUpCastBackGround(const std::string& device_name);
void UpdateUiPlayState(bool is_playing);
void UpdateUiPlayPosition(int64_t position);
void UpdateRemotePlayState(bool is_playing);
void UpdateRemotePlayPosition(int64_t position);
void MediaCastStopped();
bool IsPageBackground();
void SetPauseByAvcast(bool pause_avcast);
void NotifyCastControlShow(bool is_show);
#endif
private:
std::optional<media::PictureInPictureEventsInfo::AutoPipInfo>
last_auto_picture_in_picture_info_;
bool pause_avcast_ = false;
base::WeakPtrFactory<MediaSessionImpl> weak_factory_{this};
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
}
#include "arkweb/chromium_ext/content/browser/media/session/media_session_impl_utils.h"
#endif