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