1 // Copyright 2015 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_IMPL_H_ 6 #define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_IMPL_H_ 7 8 #include <stddef.h> 9 10 #include <map> 11 #include <set> 12 #include <unordered_map> 13 #include <unordered_set> 14 #include <vector> 15 16 #include "base/containers/flat_map.h" 17 #include "base/containers/id_map.h" 18 #include "base/macros.h" 19 #include "base/optional.h" 20 #include "base/timer/timer.h" 21 #include "content/browser/media/session/audio_focus_delegate.h" 22 #include "content/browser/media/session/media_session_uma_helper.h" 23 #include "content/common/content_export.h" 24 #include "content/public/browser/media_session.h" 25 #include "content/public/browser/web_contents_observer.h" 26 #include "content/public/browser/web_contents_user_data.h" 27 #include "mojo/public/cpp/bindings/interface_ptr_set.h" 28 #include "mojo/public/cpp/bindings/receiver_set.h" 29 #include "mojo/public/cpp/bindings/remote_set.h" 30 #include "services/media_session/public/mojom/audio_focus.mojom.h" 31 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h" 32 #include "third_party/blink/public/mojom/mediasession/media_session.mojom.h" 33 34 #if defined(OS_ANDROID) 35 #include "base/android/scoped_java_ref.h" 36 #endif // defined(OS_ANDROID) 37 38 namespace media { 39 enum class MediaContentType; 40 } // namespace media 41 42 namespace media_session { 43 struct MediaMetadata; 44 } // namespace media_session 45 46 namespace content { 47 48 class AudioFocusManagerTest; 49 class MediaSessionImplServiceRoutingTest; 50 class MediaSessionImplStateObserver; 51 class MediaSessionImplVisibilityBrowserTest; 52 class MediaSessionPlayerObserver; 53 class MediaSessionServiceImpl; 54 class MediaSessionServiceImplBrowserTest; 55 56 #if defined(OS_ANDROID) 57 class MediaSessionAndroid; 58 #endif // defined(OS_ANDROID) 59 60 // MediaSessionImpl is the implementation of MediaSession. It manages the media 61 // session and audio focus for a given WebContents. It is requesting the audio 62 // focus, pausing when requested by the system and dropping it on demand. The 63 // audio focus can be of two types: Transient or Content. A Transient audio 64 // focus will allow other players to duck instead of pausing and will be 65 // declared as temporary to the system. A Content audio focus will not be 66 // declared as temporary and will not allow other players to duck. If a given 67 // WebContents can only have one audio focus at a time, it will be Content in 68 // case of Transient and Content audio focus are both requested. 69 // TODO(thakis,mlamouri): MediaSessionImpl isn't CONTENT_EXPORT'd because it 70 // creates complicated build issues with WebContentsUserData being a 71 // non-exported template, see https://crbug.com/589840. As a result, the class 72 // uses CONTENT_EXPORT for methods that are being used from tests. 73 // CONTENT_EXPORT should be moved back to the class when the Windows build will 74 // work with it. 75 class MediaSessionImpl : public MediaSession, 76 public WebContentsObserver, 77 public WebContentsUserData<MediaSessionImpl> { 78 public: 79 enum class State { ACTIVE, SUSPENDED, INACTIVE }; 80 81 // Returns the MediaSessionImpl associated to this WebContents. Creates one if 82 // none is currently available. 83 CONTENT_EXPORT static MediaSessionImpl* Get(WebContents* web_contents); 84 85 ~MediaSessionImpl() override; 86 87 #if defined(OS_ANDROID) 88 static MediaSession* FromJavaMediaSession( 89 const base::android::JavaRef<jobject>& j_media_session); session_android()90 MediaSessionAndroid* session_android() const { 91 return session_android_.get(); 92 } 93 #endif // defined(OS_ANDROID) 94 95 void NotifyMediaSessionMetadataChange(); 96 97 // Adds the given player to the current media session. Returns whether the 98 // player was successfully added. If it returns false, AddPlayer() should be 99 // called again later. 100 CONTENT_EXPORT bool AddPlayer(MediaSessionPlayerObserver* observer, 101 int player_id, 102 media::MediaContentType media_content_type); 103 104 // Removes the given player from the current media session. Abandons audio 105 // focus if that was the last player in the session. 106 CONTENT_EXPORT void RemovePlayer(MediaSessionPlayerObserver* observer, 107 int player_id); 108 109 // Removes all the players associated with |observer|. Abandons audio focus if 110 // these were the last players in the session. 111 CONTENT_EXPORT void RemovePlayers(MediaSessionPlayerObserver* observer); 112 113 // Record that the session was ducked. 114 void RecordSessionDuck(); 115 116 // Called when a player is paused in the content. 117 // If the paused player is the last player, we suspend the MediaSession. 118 // Otherwise, the paused player will be removed from the MediaSession. 119 CONTENT_EXPORT void OnPlayerPaused(MediaSessionPlayerObserver* observer, 120 int player_id); 121 122 // Called when the position state of the session might have changed. 123 CONTENT_EXPORT void RebuildAndNotifyMediaPositionChanged(); 124 125 // Returns if the session is currently active. 126 CONTENT_EXPORT bool IsActive() const; 127 128 // Returns if the session is currently suspended. 129 CONTENT_EXPORT bool IsSuspended() const; 130 131 // Returns whether the session has Pepper instances. 132 CONTENT_EXPORT bool HasPepper() const; 133 134 // WebContentsObserver implementation 135 void WebContentsDestroyed() override; 136 void RenderFrameDeleted(RenderFrameHost* rfh) override; 137 void DidFinishNavigation(NavigationHandle* navigation_handle) override; 138 void OnWebContentsFocused(RenderWidgetHost*) override; 139 void OnWebContentsLostFocus(RenderWidgetHost*) override; 140 void TitleWasSet(NavigationEntry* entry) override; 141 void DidUpdateFaviconURL( 142 const std::vector<blink::mojom::FaviconURLPtr>& candidates) override; 143 void MediaPictureInPictureChanged(bool is_picture_in_picture) override; 144 145 // MediaSessionService-related methods 146 147 // Called when a MediaSessionService is created, which registers itself to 148 // this session. 149 void OnServiceCreated(MediaSessionServiceImpl* service); 150 // Called when a MediaSessionService is destroyed, which unregisters itself 151 // from this session. 152 void OnServiceDestroyed(MediaSessionServiceImpl* service); 153 154 // Called when the playback state of a MediaSessionService has 155 // changed. Will notify observers of media session state change. 156 void OnMediaSessionPlaybackStateChanged(MediaSessionServiceImpl* service); 157 158 // Called when the metadata of a MediaSessionService has changed. Will notify 159 // observers if the service is currently routed. 160 void OnMediaSessionMetadataChanged(MediaSessionServiceImpl* service); 161 // Called when the actions of a MediaSessionService has changed. Will notify 162 // observers if the service is currently routed. 163 void OnMediaSessionActionsChanged(MediaSessionServiceImpl* service); 164 165 // Requests audio focus to the AudioFocusDelegate. 166 // Returns whether the request was granted. 167 CONTENT_EXPORT AudioFocusDelegate::AudioFocusResult RequestSystemAudioFocus( 168 media_session::mojom::AudioFocusType audio_focus_type); 169 170 // Creates a binding between |this| and |request|. 171 mojo::PendingRemote<media_session::mojom::MediaSession> AddRemote(); 172 173 // Returns information about the MediaSession. 174 CONTENT_EXPORT media_session::mojom::MediaSessionInfoPtr 175 GetMediaSessionInfoSync(); 176 177 // Returns if the session can be controlled by the user. 178 CONTENT_EXPORT bool IsControllable() const; 179 180 // MediaSession overrides --------------------------------------------------- 181 182 // Resume the media session. 183 // |type| represents the origin of the request. 184 CONTENT_EXPORT void Resume(MediaSession::SuspendType suspend_type) override; 185 186 // Stop the media session. 187 // |type| represents the origin of the request. 188 CONTENT_EXPORT void Stop(MediaSession::SuspendType suspend_type) override; 189 190 // Seek the media session. 191 CONTENT_EXPORT void Seek(base::TimeDelta seek_time) override; 192 193 // Called when a MediaSessionAction is received. The action will be forwarded 194 // to blink::MediaSession corresponding to the current routed service. 195 void DidReceiveAction( 196 media_session::mojom::MediaSessionAction action) override; 197 198 // Set the volume multiplier applied during ducking. 199 CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override; 200 201 // Set the audio focus group id for this media session. Sessions in the same 202 // group can share audio focus. Setting this to null will use the browser 203 // default value. 204 CONTENT_EXPORT void SetAudioFocusGroupId( 205 const base::UnguessableToken& group_id) override; 206 207 // Suspend the media session. 208 // |type| represents the origin of the request. 209 CONTENT_EXPORT void Suspend(MediaSession::SuspendType suspend_type) override; 210 211 // Let the media session start ducking such that the volume multiplier is 212 // reduced. 213 CONTENT_EXPORT void StartDucking() override; 214 215 // Let the media session stop ducking such that the volume multiplier is 216 // recovered. 217 CONTENT_EXPORT void StopDucking() override; 218 219 // Returns information about the MediaSession. The sync method is not actually 220 // slower and should be used over the async one which is available over mojo. 221 void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override; 222 223 // Returns debugging information to be displayed on chrome://media-internals. 224 void GetDebugInfo(GetDebugInfoCallback) override; 225 226 // Adds a mojo based observer to listen to events related to this session. 227 void AddObserver( 228 mojo::PendingRemote<media_session::mojom::MediaSessionObserver> observer) 229 override; 230 231 // Called by |AudioFocusDelegate| when an async audio focus request is 232 // completed. 233 CONTENT_EXPORT void FinishSystemAudioFocusRequest( 234 media_session::mojom::AudioFocusType type, 235 bool result); 236 237 // Skip to the previous track. 238 CONTENT_EXPORT void PreviousTrack() override; 239 240 // Skip to the next track. 241 CONTENT_EXPORT void NextTrack() override; 242 243 // Skip ad. 244 CONTENT_EXPORT void SkipAd() override; 245 246 // Seek the media session to a specific time. 247 void SeekTo(base::TimeDelta seek_time) override; 248 249 // Scrub ("fast seek") the media session to a specific time. 250 void ScrubTo(base::TimeDelta seek_time) override; 251 252 // Enter picture-in-picture. 253 void EnterPictureInPicture() override; 254 255 // Exit picture-in-picture. 256 void ExitPictureInPicture() override; 257 258 // Downloads the bitmap version of a MediaImage at least |minimum_size_px| 259 // and closest to |desired_size_px|. If the download failed, was too small or 260 // the image did not come from the media session then returns a null image. 261 CONTENT_EXPORT void GetMediaImageBitmap( 262 const media_session::MediaImage& image, 263 int minimum_size_px, 264 int desired_size_px, 265 GetMediaImageBitmapCallback callback) override; 266 audio_focus_group_id()267 const base::UnguessableToken& audio_focus_group_id() const { 268 return audio_focus_group_id_; 269 } 270 271 void OnPictureInPictureAvailabilityChanged(); 272 273 // Returns whether the action should be routed to |routed_service_|. 274 bool ShouldRouteAction(media_session::mojom::MediaSessionAction action) const; 275 276 // Returns the source ID which links media sessions on the same browser 277 // context together. 278 CONTENT_EXPORT const base::UnguessableToken& GetSourceId() const; 279 280 // Returns the Audio Focus request ID associated with this media session. 281 const base::UnguessableToken& GetRequestId() const; 282 283 private: 284 friend class content::WebContentsUserData<MediaSessionImpl>; 285 friend class MediaSessionImplBrowserTest; 286 friend class content::MediaSessionImplVisibilityBrowserTest; 287 friend class content::AudioFocusManagerTest; 288 friend class content::MediaSessionImplServiceRoutingTest; 289 friend class content::MediaSessionImplStateObserver; 290 friend class content::MediaSessionServiceImplBrowserTest; 291 friend class MediaSessionImplTest; 292 friend class MediaInternalsAudioFocusTest; 293 294 CONTENT_EXPORT void SetDelegateForTests( 295 std::unique_ptr<AudioFocusDelegate> delegate); 296 CONTENT_EXPORT void RemoveAllPlayersForTest(); 297 CONTENT_EXPORT MediaSessionUmaHelper* uma_helper_for_test(); 298 299 // Representation of a player for the MediaSessionImpl. 300 struct PlayerIdentifier { 301 PlayerIdentifier(MediaSessionPlayerObserver* observer, int player_id); 302 PlayerIdentifier(const PlayerIdentifier&) = default; 303 304 void operator=(const PlayerIdentifier&) = delete; 305 bool operator==(const PlayerIdentifier& player_identifier) const; 306 bool operator<(const PlayerIdentifier&) const; 307 308 // Hash operator for std::unordered_map<>. 309 struct Hash { 310 size_t operator()(const PlayerIdentifier& player_identifier) const; 311 }; 312 313 MediaSessionPlayerObserver* observer; 314 int player_id; 315 }; 316 using PlayersMap = 317 std::unordered_set<PlayerIdentifier, PlayerIdentifier::Hash>; 318 319 CONTENT_EXPORT explicit MediaSessionImpl(WebContents* web_contents); 320 321 void Initialize(); 322 323 // Called when we have finished downloading an image. 324 void OnImageDownloadComplete(GetMediaImageBitmapCallback callback, 325 int minimum_size_px, 326 int desired_size_px, 327 int id, 328 int http_status_code, 329 const GURL& image_url, 330 const std::vector<SkBitmap>& bitmaps, 331 const std::vector<gfx::Size>& sizes); 332 333 // Called when system audio focus has been requested and whether the request 334 // was granted. 335 void OnSystemAudioFocusRequested(bool result); 336 337 CONTENT_EXPORT void OnSuspendInternal(MediaSession::SuspendType suspend_type, 338 State new_state); 339 CONTENT_EXPORT void OnResumeInternal(MediaSession::SuspendType suspend_type); 340 341 // To be called after a call to AbandonAudioFocus() in order request the 342 // delegate to abandon the audio focus. 343 CONTENT_EXPORT void AbandonSystemAudioFocusIfNeeded(); 344 345 // Internal method that should be used instead of setting audio_focus_state_. 346 // It sets audio_focus_state_ and notifies observers about the state change. 347 void SetAudioFocusState(State audio_focus_state); 348 349 // Flushes any mojo bindings for testing. 350 CONTENT_EXPORT void FlushForTesting(); 351 352 // Notifies |observers_| and |delegate_| that |MediaSessionInfo| has changed. 353 void RebuildAndNotifyMediaSessionInfoChanged(); 354 355 // Update the volume multiplier when ducking state changes. 356 void UpdateVolumeMultiplier(); 357 358 // Get the volume multiplier, which depends on whether the media session is 359 // ducking. 360 double GetVolumeMultiplier() const; 361 362 CONTENT_EXPORT bool AddPepperPlayer(MediaSessionPlayerObserver* observer, 363 int player_id); 364 365 CONTENT_EXPORT bool AddOneShotPlayer(MediaSessionPlayerObserver* observer, 366 int player_id); 367 368 // MediaSessionService-related methods 369 370 // Called when the routed service may have changed. 371 void UpdateRoutedService(); 372 373 // Returns whether the frame |rfh| uses MediaSession API. 374 bool IsServiceActiveForRenderFrameHost(RenderFrameHost* rfh); 375 376 // Compute the MediaSessionService that should be routed, which will be used 377 // to update |routed_service_|. 378 CONTENT_EXPORT MediaSessionServiceImpl* ComputeServiceForRouting(); 379 380 // Rebuilds |actions_| and notifies observers if they have changed. 381 void RebuildAndNotifyActionsChanged(); 382 383 // Rebuilds |metadata_| and |images_| and notifies observers if they have 384 // changed. 385 void RebuildAndNotifyMetadataChanged(); 386 387 bool IsPictureInPictureAvailable() const; 388 389 // Called when a MediaSessionAction is received. The action will be forwarded 390 // to blink::MediaSession corresponding to the current routed service. 391 void DidReceiveAction(media_session::mojom::MediaSessionAction action, 392 blink::mojom::MediaSessionActionDetailsPtr details); 393 394 // Returns the media audio video state. This is whether the players associated 395 // with the media session are audio-only or have audio and video. If we have 396 // a |routed_service_| then we limit to players on that frame because this 397 // should align with the metadata. 398 media_session::mojom::MediaAudioVideoState GetMediaAudioVideoState(); 399 400 // Calls the callback with each |PlayerIdentifier| for every player associated 401 // with this media session. 402 void ForAllPlayers(base::RepeatingCallback<void(const PlayerIdentifier&)>); 403 404 // A set of actions supported by |routed_service_| and the current media 405 // session. 406 std::set<media_session::mojom::MediaSessionAction> actions_; 407 408 std::unique_ptr<AudioFocusDelegate> delegate_; 409 std::map<PlayerIdentifier, media_session::mojom::AudioFocusType> 410 normal_players_; 411 PlayersMap pepper_players_; 412 413 // Players that are playing in the web contents but we cannot control (e.g. 414 // WebAudio or MediaStream). 415 PlayersMap one_shot_players_; 416 417 State audio_focus_state_ = State::INACTIVE; 418 MediaSession::SuspendType suspend_type_; 419 420 // The |desired_audio_focus_type_| is the AudioFocusType we will request when 421 // we request system audio focus. 422 media_session::mojom::AudioFocusType desired_audio_focus_type_; 423 424 // The last updated |MediaSessionInfo| that was sent to |observers_|. 425 media_session::mojom::MediaSessionInfoPtr session_info_; 426 427 // The last updated |MediaPosition| that was sent to |observers_|. 428 base::Optional<media_session::MediaPosition> position_; 429 430 MediaSessionUmaHelper uma_helper_; 431 432 // The ducking state of this media session. The initial value is |false|, and 433 // is set to |true| after StartDucking(), and will be set to |false| after 434 // StopDucking(). 435 bool is_ducking_; 436 437 base::UnguessableToken audio_focus_group_id_ = base::UnguessableToken::Null(); 438 439 double ducking_volume_multiplier_; 440 441 // True if the WebContents associated with this MediaSessionImpl is focused. 442 bool focused_ = false; 443 444 #if defined(OS_ANDROID) 445 std::unique_ptr<MediaSessionAndroid> session_android_; 446 #endif // defined(OS_ANDROID) 447 448 // MediaSessionService-related fields 449 using ServicesMap = std::map<RenderFrameHost*, MediaSessionServiceImpl*>; 450 451 // The current metadata and images associated with the current media session. 452 media_session::MediaMetadata metadata_; 453 base::flat_map<media_session::mojom::MediaSessionImageType, 454 std::vector<media_session::MediaImage>> 455 images_; 456 457 // The collection of all managed services (non-owned pointers). The services 458 // are owned by RenderFrameHost and should be registered on creation and 459 // unregistered on destroy. 460 ServicesMap services_; 461 // The currently routed service (non-owned pointer). 462 MediaSessionServiceImpl* routed_service_; 463 464 // Bindings for Mojo pointers to |this| held by media route providers. 465 mojo::ReceiverSet<media_session::mojom::MediaSession> receivers_; 466 467 mojo::RemoteSet<media_session::mojom::MediaSessionObserver> observers_; 468 469 WEB_CONTENTS_USER_DATA_KEY_DECL(); 470 471 DISALLOW_COPY_AND_ASSIGN(MediaSessionImpl); 472 }; 473 474 } // namespace content 475 476 #endif // CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_IMPL_H_ 477