summaryrefslogtreecommitdiffstats
path: root/dom/media/mediacontrol/MediaStatusManager.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/mediacontrol/MediaStatusManager.h276
1 files changed, 276 insertions, 0 deletions
diff --git a/dom/media/mediacontrol/MediaStatusManager.h b/dom/media/mediacontrol/MediaStatusManager.h
new file mode 100644
index 0000000000..24247d119d
--- /dev/null
+++ b/dom/media/mediacontrol/MediaStatusManager.h
@@ -0,0 +1,276 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DOM_MEDIA_MEDIACONTROL_MEDIASTATUSMANAGER_H_
+#define DOM_MEDIA_MEDIACONTROL_MEDIASTATUSMANAGER_H_
+
+#include "MediaControlKeySource.h"
+#include "MediaEventSource.h"
+#include "MediaPlaybackStatus.h"
+#include "mozilla/dom/MediaMetadata.h"
+#include "mozilla/dom/MediaSessionBinding.h"
+#include "mozilla/Maybe.h"
+#include "nsTHashMap.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla::dom {
+
+class MediaSessionInfo {
+ public:
+ MediaSessionInfo() = default;
+
+ explicit MediaSessionInfo(MediaMetadataBase& aMetadata) {
+ mMetadata.emplace(aMetadata);
+ }
+
+ MediaSessionInfo(MediaMetadataBase& aMetadata,
+ MediaSessionPlaybackState& aState) {
+ mMetadata.emplace(aMetadata);
+ mDeclaredPlaybackState = aState;
+ }
+
+ static MediaSessionInfo EmptyInfo() { return MediaSessionInfo(); }
+
+ static uint32_t GetActionBitMask(MediaSessionAction aAction) {
+ return 1 << static_cast<uint8_t>(aAction);
+ }
+
+ void EnableAction(MediaSessionAction aAction) {
+ mSupportedActions |= GetActionBitMask(aAction);
+ }
+
+ void DisableAction(MediaSessionAction aAction) {
+ mSupportedActions &= ~GetActionBitMask(aAction);
+ }
+
+ bool IsActionSupported(MediaSessionAction aAction) const {
+ return mSupportedActions & GetActionBitMask(aAction);
+ }
+
+ // These attributes are all propagated from the media session in the content
+ // process.
+ Maybe<MediaMetadataBase> mMetadata;
+ MediaSessionPlaybackState mDeclaredPlaybackState =
+ MediaSessionPlaybackState::None;
+ // Use bitwise to store the supported actions.
+ uint32_t mSupportedActions = 0;
+};
+
+/**
+ * IMediaInfoUpdater is an interface which provides methods to update the media
+ * related information that happens in the content process.
+ */
+class IMediaInfoUpdater {
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ // Use this method to update controlled media's playback state and the
+ // browsing context where controlled media exists. When notifying the state
+ // change, we MUST follow the following rules.
+ // (1) `eStart` MUST be the first state and `eStop` MUST be the last state
+ // (2) Do not notify same state again
+ // (3) `ePaused` can only be notified after notifying `ePlayed`.
+ virtual void NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId,
+ MediaPlaybackState aState) = 0;
+
+ // Use this method to update the audible state of controlled media, and MUST
+ // follow the following rules in which `audible` and `inaudible` should be a
+ // pair. `inaudible` should always be notified after `audible`. When audible
+ // media paused, `inaudible` should be notified
+ // Eg. (O) `audible` -> `inaudible` -> `audible` -> `inaudible`
+ // (X) `inaudible` -> `audible` [notify `inaudible` before `audible`]
+ // (X) `audible` -> `audible` [notify `audible` twice]
+ // (X) `audible` -> (media pauses) [forgot to notify `inaudible`]
+ virtual void NotifyMediaAudibleChanged(uint64_t aBrowsingContextId,
+ MediaAudibleState aState) = 0;
+
+ // Use this method to update media session's declared playback state for the
+ // specific media session.
+ virtual void SetDeclaredPlaybackState(uint64_t aBrowsingContextId,
+ MediaSessionPlaybackState aState) = 0;
+
+ // Use these methods to update controller's media session list. We'd use it
+ // when media session is created/destroyed in the content process.
+ virtual void NotifySessionCreated(uint64_t aBrowsingContextId) = 0;
+ virtual void NotifySessionDestroyed(uint64_t aBrowsingContextId) = 0;
+
+ // Use this method to update the metadata for the specific media session.
+ virtual void UpdateMetadata(uint64_t aBrowsingContextId,
+ const Maybe<MediaMetadataBase>& aMetadata) = 0;
+
+ // Use this method to update the picture in picture mode state of controlled
+ // media, and it's safe to notify same state again.
+ virtual void SetIsInPictureInPictureMode(uint64_t aBrowsingContextId,
+ bool aIsInPictureInPictureMode) = 0;
+
+ // Use these methods to update the supported media session action for the
+ // specific media session. For a media session from a given browsing context,
+ // do not re-enable the same action, or disable the action without enabling it
+ // before.
+ virtual void EnableAction(uint64_t aBrowsingContextId,
+ MediaSessionAction aAction) = 0;
+ virtual void DisableAction(uint64_t aBrowsingContextId,
+ MediaSessionAction aAction) = 0;
+
+ // Use this method when media enters or leaves the fullscreen.
+ virtual void NotifyMediaFullScreenState(uint64_t aBrowsingContextId,
+ bool aIsInFullScreen) = 0;
+
+ // Use this method when media session update its position state.
+ virtual void UpdatePositionState(uint64_t aBrowsingContextId,
+ const PositionState& aState) = 0;
+};
+
+/**
+ * MediaStatusManager would decide the media related status which can represents
+ * the whole tab. The status includes the playback status, tab's metadata and
+ * the active media session ID if it exists.
+ *
+ * We would use `IMediaInfoUpdater` methods to update the media playback related
+ * information and then use `MediaPlaybackStatus` to determine the final
+ * playback state.
+ *
+ * The metadata would be the one from the active media session, or the default
+ * one. This class would determine which media session is an active media
+ * session [1] whithin a tab. It tracks all alive media sessions within a tab
+ * and store their metadata which could be used to show on the virtual media
+ * control interface. In addition, we can use it to get the current media
+ * metadata even if there is no media session existing. However, the meaning of
+ * active media session here is not equal to the definition from the spec [1].
+ * We just choose the session which is the active one inside the tab, the global
+ * active media session among different tabs would be the one inside the main
+ * controller which is determined by MediaControlService.
+ *
+ * [1] https://w3c.github.io/mediasession/#active-media-session
+ */
+class MediaStatusManager : public IMediaInfoUpdater {
+ public:
+ explicit MediaStatusManager(uint64_t aBrowsingContextId);
+
+ // IMediaInfoUpdater's methods
+ void NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId,
+ MediaPlaybackState aState) override;
+ void NotifyMediaAudibleChanged(uint64_t aBrowsingContextId,
+ MediaAudibleState aState) override;
+ void SetDeclaredPlaybackState(uint64_t aSessionContextId,
+ MediaSessionPlaybackState aState) override;
+ void NotifySessionCreated(uint64_t aSessionContextId) override;
+ void NotifySessionDestroyed(uint64_t aSessionContextId) override;
+ void UpdateMetadata(uint64_t aSessionContextId,
+ const Maybe<MediaMetadataBase>& aMetadata) override;
+ void EnableAction(uint64_t aBrowsingContextId,
+ MediaSessionAction aAction) override;
+ void DisableAction(uint64_t aBrowsingContextId,
+ MediaSessionAction aAction) override;
+ void UpdatePositionState(uint64_t aBrowsingContextId,
+ const PositionState& aState) override;
+
+ // Return active media session's metadata if active media session exists and
+ // it has already set its metadata. Otherwise, return default media metadata
+ // which is based on website's title and favicon.
+ MediaMetadataBase GetCurrentMediaMetadata() const;
+
+ bool IsMediaAudible() const;
+ bool IsMediaPlaying() const;
+ bool IsAnyMediaBeingControlled() const;
+
+ // These events would be notified when the active media session's certain
+ // property changes.
+ MediaEventSource<MediaMetadataBase>& MetadataChangedEvent() {
+ return mMetadataChangedEvent;
+ }
+
+ MediaEventSource<PositionState>& PositionChangedEvent() {
+ return mPositionStateChangedEvent;
+ }
+
+ MediaEventSource<MediaSessionPlaybackState>& PlaybackChangedEvent() {
+ return mPlaybackStateChangedEvent;
+ }
+
+ // Return the actual playback state.
+ MediaSessionPlaybackState PlaybackState() const;
+
+ // When page title changes, we might need to update it on the default
+ // metadata as well.
+ void NotifyPageTitleChanged();
+
+ protected:
+ ~MediaStatusManager() = default;
+
+ // This event would be notified when the active media session changes its
+ // supported actions.
+ MediaEventSource<nsTArray<MediaSessionAction>>&
+ SupportedActionsChangedEvent() {
+ return mSupportedActionsChangedEvent;
+ }
+
+ uint64_t mTopLevelBrowsingContextId;
+
+ // Within a tab, the Id of the browsing context which has already created a
+ // media session and owns the audio focus within a tab.
+ Maybe<uint64_t> mActiveMediaSessionContextId;
+
+ void ClearActiveMediaSessionContextIdIfNeeded();
+
+ private:
+ nsString GetDefaultFaviconURL() const;
+ nsString GetDefaultTitle() const;
+ MediaMetadataBase CreateDefaultMetadata() const;
+ bool IsInPrivateBrowsing() const;
+ void FillMissingTitleAndArtworkIfNeeded(MediaMetadataBase& aMetadata) const;
+
+ bool IsSessionOwningAudioFocus(uint64_t aBrowsingContextId) const;
+ void SetActiveMediaSessionContextId(uint64_t aBrowsingContextId);
+ void HandleAudioFocusOwnerChanged(Maybe<uint64_t>& aBrowsingContextId);
+
+ void NotifySupportedKeysChangedIfNeeded(uint64_t aBrowsingContextId);
+
+ // Return a copyable array filled with the supported media session actions.
+ // Use copyable array so that we can use the result as a parameter for the
+ // media event.
+ CopyableTArray<MediaSessionAction> GetSupportedActions() const;
+
+ void StoreMediaSessionContextIdOnWindowContext();
+
+ // When the amount of playing media changes, we would use this function to
+ // update the guessed playback state.
+ void SetGuessedPlayState(MediaSessionPlaybackState aState);
+
+ // Whenever the declared playback state or the guessed playback state changes,
+ // we should recompute actual playback state to know if we need to update the
+ // virtual control interface.
+ void UpdateActualPlaybackState();
+
+ // Return the active media session's declared playback state. If the active
+ // media session doesn't exist, return 'None' instead.
+ MediaSessionPlaybackState GetCurrentDeclaredPlaybackState() const;
+
+ // This state can match to the `guessed playback state` in the spec [1], it
+ // indicates if we have any media element playing within the tab which this
+ // controller belongs to. But currently we only take media elements into
+ // account, which is different from the way the spec recommends. In addition,
+ // We don't support web audio and plugin and not consider audible state of
+ // media.
+ // [1] https://w3c.github.io/mediasession/#guessed-playback-state
+ MediaSessionPlaybackState mGuessedPlaybackState =
+ MediaSessionPlaybackState::None;
+
+ // This playback state would be the final playback which can be used to know
+ // if the controller is playing or not.
+ // https://w3c.github.io/mediasession/#actual-playback-state
+ MediaSessionPlaybackState mActualPlaybackState =
+ MediaSessionPlaybackState::None;
+
+ nsTHashMap<nsUint64HashKey, MediaSessionInfo> mMediaSessionInfoMap;
+ MediaEventProducer<MediaMetadataBase> mMetadataChangedEvent;
+ MediaEventProducer<nsTArray<MediaSessionAction>>
+ mSupportedActionsChangedEvent;
+ MediaEventProducer<PositionState> mPositionStateChangedEvent;
+ MediaEventProducer<MediaSessionPlaybackState> mPlaybackStateChangedEvent;
+ MediaPlaybackStatus mPlaybackStatusDelegate;
+};
+
+} // namespace mozilla::dom
+
+#endif // DOM_MEDIA_MEDIACONTROL_MEDIASTATUSMANAGER_H_