MediaStatusManager.h (12647B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifndef DOM_MEDIA_MEDIACONTROL_MEDIASTATUSMANAGER_H_ 6 #define DOM_MEDIA_MEDIACONTROL_MEDIASTATUSMANAGER_H_ 7 8 #include "MediaControlKeySource.h" 9 #include "MediaEventSource.h" 10 #include "MediaPlaybackStatus.h" 11 #include "mozilla/Maybe.h" 12 #include "mozilla/dom/MediaMetadata.h" 13 #include "mozilla/dom/MediaSessionBinding.h" 14 #include "nsISupportsImpl.h" 15 #include "nsTHashMap.h" 16 17 namespace mozilla::dom { 18 19 class MediaSessionInfo { 20 public: 21 MediaSessionInfo() = default; 22 23 explicit MediaSessionInfo(MediaMetadataBase& aMetadata) { 24 mMetadata.emplace(aMetadata); 25 } 26 27 MediaSessionInfo(MediaMetadataBase& aMetadata, 28 MediaSessionPlaybackState& aState) { 29 mMetadata.emplace(aMetadata); 30 mDeclaredPlaybackState = aState; 31 } 32 33 static MediaSessionInfo EmptyInfo() { return MediaSessionInfo(); } 34 35 static uint32_t GetActionBitMask(MediaSessionAction aAction) { 36 return 1 << static_cast<uint8_t>(aAction); 37 } 38 39 void EnableAction(MediaSessionAction aAction) { 40 mSupportedActions |= GetActionBitMask(aAction); 41 } 42 43 void DisableAction(MediaSessionAction aAction) { 44 mSupportedActions &= ~GetActionBitMask(aAction); 45 } 46 47 bool IsActionSupported(MediaSessionAction aAction) const { 48 return mSupportedActions & GetActionBitMask(aAction); 49 } 50 51 // These attributes are all propagated from the media session in the content 52 // process. 53 Maybe<MediaMetadataBase> mMetadata; 54 MediaSessionPlaybackState mDeclaredPlaybackState = 55 MediaSessionPlaybackState::None; 56 Maybe<PositionState> mPositionState; 57 // Use bitwise to store the supported actions. 58 uint32_t mSupportedActions = 0; 59 }; 60 61 /** 62 * IMediaInfoUpdater is an interface which provides methods to update the media 63 * related information that happens in the content process. 64 */ 65 class IMediaInfoUpdater { 66 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING 67 68 // Use this method to update controlled media's playback state and the 69 // browsing context where controlled media exists. When notifying the state 70 // change, we MUST follow the following rules. 71 // (1) `eStart` MUST be the first state and `eStop` MUST be the last state 72 // (2) Do not notify same state again 73 // (3) `ePaused` can only be notified after notifying `ePlayed`. 74 virtual void NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId, 75 MediaPlaybackState aState) = 0; 76 77 // Use this method to update the audible state of controlled media, and MUST 78 // follow the following rules in which `audible` and `inaudible` should be a 79 // pair. `inaudible` should always be notified after `audible`. When audible 80 // media paused, `inaudible` should be notified 81 // Eg. (O) `audible` -> `inaudible` -> `audible` -> `inaudible` 82 // (X) `inaudible` -> `audible` [notify `inaudible` before `audible`] 83 // (X) `audible` -> `audible` [notify `audible` twice] 84 // (X) `audible` -> (media pauses) [forgot to notify `inaudible`] 85 virtual void NotifyMediaAudibleChanged(uint64_t aBrowsingContextId, 86 MediaAudibleState aState) = 0; 87 88 // Use this method to update media session's declared playback state for the 89 // specific media session. 90 virtual void SetDeclaredPlaybackState(uint64_t aBrowsingContextId, 91 MediaSessionPlaybackState aState) = 0; 92 93 // Use these methods to update controller's media session list. We'd use it 94 // when media session is created/destroyed in the content process. 95 virtual void NotifySessionCreated(uint64_t aBrowsingContextId) = 0; 96 virtual void NotifySessionDestroyed(uint64_t aBrowsingContextId) = 0; 97 98 // Use this method to update the metadata for the specific media session. 99 virtual void UpdateMetadata(uint64_t aBrowsingContextId, 100 const Maybe<MediaMetadataBase>& aMetadata) = 0; 101 102 // Use this method to update the picture in picture mode state of controlled 103 // media, and it's safe to notify same state again. 104 virtual void SetIsInPictureInPictureMode(uint64_t aBrowsingContextId, 105 bool aIsInPictureInPictureMode) = 0; 106 107 // Use these methods to update the supported media session action for the 108 // specific media session. For a media session from a given browsing context, 109 // do not re-enable the same action, or disable the action without enabling it 110 // before. 111 virtual void EnableAction(uint64_t aBrowsingContextId, 112 MediaSessionAction aAction) = 0; 113 virtual void DisableAction(uint64_t aBrowsingContextId, 114 MediaSessionAction aAction) = 0; 115 116 // Use this method when media enters or leaves the fullscreen. 117 virtual void NotifyMediaFullScreenState(uint64_t aBrowsingContextId, 118 bool aIsInFullScreen) = 0; 119 120 // Use this method when media session update its position state. 121 virtual void UpdatePositionState(uint64_t aBrowsingContextId, 122 const Maybe<PositionState>& aState) = 0; 123 124 // Use this method to update controlled media's position state and the 125 // browsing context where controlled media exists. 126 virtual void UpdateGuessedPositionState( 127 uint64_t aBrowsingContextId, const nsID& aMediaId, 128 const Maybe<PositionState>& aGuessedState) = 0; 129 }; 130 131 /** 132 * MediaStatusManager would decide the media related status which can represents 133 * the whole tab. The status includes the playback status, tab's metadata and 134 * the active media session ID if it exists. 135 * 136 * We would use `IMediaInfoUpdater` methods to update the media playback related 137 * information and then use `MediaPlaybackStatus` to determine the final 138 * playback state. 139 * 140 * The metadata would be the one from the active media session, or the default 141 * one. This class would determine which media session is an active media 142 * session [1] whithin a tab. It tracks all alive media sessions within a tab 143 * and store their metadata which could be used to show on the virtual media 144 * control interface. In addition, we can use it to get the current media 145 * metadata even if there is no media session existing. However, the meaning of 146 * active media session here is not equal to the definition from the spec [1]. 147 * We just choose the session which is the active one inside the tab, the global 148 * active media session among different tabs would be the one inside the main 149 * controller which is determined by MediaControlService. 150 * 151 * [1] https://w3c.github.io/mediasession/#active-media-session 152 */ 153 class MediaStatusManager : public IMediaInfoUpdater { 154 public: 155 explicit MediaStatusManager(uint64_t aBrowsingContextId); 156 157 // IMediaInfoUpdater's methods 158 void NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId, 159 MediaPlaybackState aState) override; 160 void NotifyMediaAudibleChanged(uint64_t aBrowsingContextId, 161 MediaAudibleState aState) override; 162 void SetDeclaredPlaybackState(uint64_t aSessionContextId, 163 MediaSessionPlaybackState aState) override; 164 void NotifySessionCreated(uint64_t aSessionContextId) override; 165 void NotifySessionDestroyed(uint64_t aSessionContextId) override; 166 void UpdateMetadata(uint64_t aSessionContextId, 167 const Maybe<MediaMetadataBase>& aMetadata) override; 168 void EnableAction(uint64_t aBrowsingContextId, 169 MediaSessionAction aAction) override; 170 void DisableAction(uint64_t aBrowsingContextId, 171 MediaSessionAction aAction) override; 172 void UpdatePositionState(uint64_t aBrowsingContextId, 173 const Maybe<PositionState>& aState) override; 174 void UpdateGuessedPositionState( 175 uint64_t aBrowsingContextId, const nsID& aMediaId, 176 const Maybe<PositionState>& aGuessedState) override; 177 178 // Return active media session's metadata if active media session exists and 179 // it has already set its metadata. Otherwise, return default media metadata 180 // which is based on website's title and favicon. 181 MediaMetadataBase GetCurrentMediaMetadata() const; 182 183 // Return the active media session's position state. If the active media 184 // session doesn't exist or doesn't have any state, Nothing is returned. 185 Maybe<PositionState> GetCurrentPositionState() const; 186 187 bool IsMediaAudible() const; 188 bool IsMediaPlaying() const; 189 bool IsAnyMediaBeingControlled() const; 190 191 // These events would be notified when the active media session's certain 192 // property changes. 193 MediaEventSource<MediaMetadataBase>& MetadataChangedEvent() { 194 return mMetadataChangedEvent; 195 } 196 197 MediaEventSource<Maybe<PositionState>>& PositionChangedEvent() { 198 return mPositionStateChangedEvent; 199 } 200 201 MediaEventSource<MediaSessionPlaybackState>& PlaybackChangedEvent() { 202 return mPlaybackStateChangedEvent; 203 } 204 205 // Return the actual playback state. 206 MediaSessionPlaybackState PlaybackState() const; 207 208 // When page title changes, we might need to update it on the default 209 // metadata as well. 210 void NotifyPageTitleChanged(); 211 212 protected: 213 ~MediaStatusManager() = default; 214 215 // This event would be notified when the active media session changes its 216 // supported actions. 217 MediaEventSource<nsTArray<MediaSessionAction>>& 218 SupportedActionsChangedEvent() { 219 return mSupportedActionsChangedEvent; 220 } 221 222 uint64_t mTopLevelBrowsingContextId; 223 224 // Within a tab, the Id of the browsing context which has already created a 225 // media session and owns the audio focus within a tab. 226 Maybe<uint64_t> mActiveMediaSessionContextId; 227 228 void ClearActiveMediaSessionContextIdIfNeeded(); 229 230 private: 231 nsString GetDefaultFaviconURL() const; 232 nsString GetDefaultTitle() const; 233 nsCString GetUrl() const; 234 MediaMetadataBase CreateDefaultMetadata() const; 235 bool IsInPrivateBrowsing() const; 236 void FillMissingTitleAndArtworkIfNeeded(MediaMetadataBase& aMetadata) const; 237 238 bool IsSessionOwningAudioFocus(uint64_t aBrowsingContextId) const; 239 void SetActiveMediaSessionContextId(uint64_t aBrowsingContextId); 240 void HandleAudioFocusOwnerChanged(Maybe<uint64_t>& aBrowsingContextId); 241 242 void NotifySupportedKeysChangedIfNeeded(uint64_t aBrowsingContextId); 243 244 // Return a copyable array filled with the supported media session actions. 245 // Use copyable array so that we can use the result as a parameter for the 246 // media event. 247 CopyableTArray<MediaSessionAction> GetSupportedActions() const; 248 249 void StoreMediaSessionContextIdOnWindowContext(); 250 251 // When the amount of playing media changes, we would use this function to 252 // update the guessed playback state. 253 void SetGuessedPlayState(MediaSessionPlaybackState aState); 254 255 // Whenever the declared playback state or the guessed playback state changes, 256 // we should recompute actual playback state to know if we need to update the 257 // virtual control interface. 258 void UpdateActualPlaybackState(); 259 260 // Return the active media session's declared playback state. If the active 261 // media session doesn't exist, return 'None' instead. 262 MediaSessionPlaybackState GetCurrentDeclaredPlaybackState() const; 263 264 // This state can match to the `guessed playback state` in the spec [1], it 265 // indicates if we have any media element playing within the tab which this 266 // controller belongs to. But currently we only take media elements into 267 // account, which is different from the way the spec recommends. In addition, 268 // We don't support web audio and plugin and not consider audible state of 269 // media. 270 // [1] https://w3c.github.io/mediasession/#guessed-playback-state 271 MediaSessionPlaybackState mGuessedPlaybackState = 272 MediaSessionPlaybackState::None; 273 274 // This playback state would be the final playback which can be used to know 275 // if the controller is playing or not. 276 // https://w3c.github.io/mediasession/#actual-playback-state 277 MediaSessionPlaybackState mActualPlaybackState = 278 MediaSessionPlaybackState::None; 279 280 nsTHashMap<nsUint64HashKey, MediaSessionInfo> mMediaSessionInfoMap; 281 MediaEventProducer<MediaMetadataBase> mMetadataChangedEvent; 282 MediaEventProducer<nsTArray<MediaSessionAction>> 283 mSupportedActionsChangedEvent; 284 MediaEventProducer<Maybe<PositionState>> mPositionStateChangedEvent; 285 MediaEventProducer<MediaSessionPlaybackState> mPlaybackStateChangedEvent; 286 MediaPlaybackStatus mPlaybackStatusDelegate; 287 }; 288 289 } // namespace mozilla::dom 290 291 #endif // DOM_MEDIA_MEDIACONTROL_MEDIASTATUSMANAGER_H_