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 file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifndef DOM_MEDIA_MEDIACONTROL_MEDIAPLAYBACKSTATUS_H_ 6 #define DOM_MEDIA_MEDIACONTROL_MEDIAPLAYBACKSTATUS_H_ 7 8 #include "mozilla/Maybe.h" 9 #include "mozilla/RefPtr.h" 10 #include "nsISupportsImpl.h" 11 #include "nsTArray.h" 12 #include "nsTHashMap.h" 13 14 namespace mozilla { 15 namespace dom { 16 17 /** 18 * This enum is used to update controlled media state to the media controller in 19 * the chrome process. 20 * `eStarted`: media has successfully registered to the content media controller 21 * `ePlayed` : media has started playing 22 * `ePaused` : media has paused playing, but still can be resumed by content 23 * media controller 24 * `eStopped`: media has unregistered from the content media controller, we can 25 * not control it anymore 26 */ 27 enum class MediaPlaybackState : uint32_t { 28 eStarted, 29 ePlayed, 30 ePaused, 31 eStopped, 32 }; 33 34 /** 35 * This enum is used to update controlled media audible audible state to the 36 * media controller in the chrome process. 37 */ 38 enum class MediaAudibleState : bool { 39 eInaudible = false, 40 eAudible = true, 41 }; 42 43 /** 44 * MediaPlaybackStatus is an internal module for the media controller, it 45 * represents a tab's media related status, such like "does the tab contain any 46 * controlled media? is the tab playing? is the tab audible?". 47 * 48 * The reason we need this class is that we would like to encapsulate the 49 * details of determining the tab's media status. A tab can contains multiple 50 * browsing contexts, and each browsing context can have different media status. 51 * The final media status would be decided by checking all those context status. 52 * 53 * Use `UpdateMediaXXXState()` to update controlled media status, and use 54 * `IsXXX()` methods to acquire the playback status of the tab. 55 * 56 * As we know each context's audible state, we can decide which context should 57 * owns the audio focus when multiple contexts are all playing audible media at 58 * the same time. In that cases, the latest context that plays media would own 59 * the audio focus. When the context owning the audio focus is destroyed, we 60 * would see if there is another other context still playing audible media, and 61 * switch the audio focus to another context. 62 */ 63 class MediaPlaybackStatus final { 64 public: 65 void UpdateMediaPlaybackState(uint64_t aContextId, MediaPlaybackState aState); 66 void UpdateMediaAudibleState(uint64_t aContextId, MediaAudibleState aState); 67 68 bool IsPlaying() const; 69 bool IsAudible() const; 70 bool IsAnyMediaBeingControlled() const; 71 72 Maybe<uint64_t> GetAudioFocusOwnerContextId() const; 73 74 private: 75 /** 76 * This internal class stores detailed media status of controlled media for 77 * a browsing context. 78 */ 79 class ContextMediaInfo final { 80 public: ContextMediaInfo(uint64_t aContextId)81 explicit ContextMediaInfo(uint64_t aContextId) : mContextId(aContextId) {} 82 ~ContextMediaInfo() = default; 83 IncreaseControlledMediaNum()84 void IncreaseControlledMediaNum() { 85 MOZ_DIAGNOSTIC_ASSERT(mControlledMediaNum < UINT_MAX); 86 mControlledMediaNum++; 87 } DecreaseControlledMediaNum()88 void DecreaseControlledMediaNum() { 89 MOZ_DIAGNOSTIC_ASSERT(mControlledMediaNum > 0); 90 mControlledMediaNum--; 91 } IncreasePlayingMediaNum()92 void IncreasePlayingMediaNum() { 93 MOZ_DIAGNOSTIC_ASSERT(mPlayingMediaNum < mControlledMediaNum); 94 mPlayingMediaNum++; 95 } DecreasePlayingMediaNum()96 void DecreasePlayingMediaNum() { 97 MOZ_DIAGNOSTIC_ASSERT(mPlayingMediaNum > 0); 98 mPlayingMediaNum--; 99 } IncreaseAudibleMediaNum()100 void IncreaseAudibleMediaNum() { 101 MOZ_DIAGNOSTIC_ASSERT(mAudibleMediaNum < mPlayingMediaNum); 102 mAudibleMediaNum++; 103 } DecreaseAudibleMediaNum()104 void DecreaseAudibleMediaNum() { 105 MOZ_DIAGNOSTIC_ASSERT(mAudibleMediaNum > 0); 106 mAudibleMediaNum--; 107 } IsPlaying()108 bool IsPlaying() const { return mPlayingMediaNum > 0; } IsAudible()109 bool IsAudible() const { return mAudibleMediaNum > 0; } IsAnyMediaBeingControlled()110 bool IsAnyMediaBeingControlled() const { return mControlledMediaNum > 0; } Id()111 uint64_t Id() const { return mContextId; } 112 113 private: 114 /** 115 * The possible value for those three numbers should follow this rule, 116 * mControlledMediaNum >= mPlayingMediaNum >= mAudibleMediaNum 117 */ 118 uint32_t mControlledMediaNum = 0; 119 uint32_t mAudibleMediaNum = 0; 120 uint32_t mPlayingMediaNum = 0; 121 uint64_t mContextId = 0; 122 }; 123 124 ContextMediaInfo& GetNotNullContextInfo(uint64_t aContextId); 125 void DestroyContextInfo(uint64_t aContextId); 126 127 void ChooseNewContextToOwnAudioFocus(); 128 void SetOwningAudioFocusContextId(Maybe<uint64_t>&& aContextId); 129 bool IsContextOwningAudioFocus(uint64_t aContextId) const; 130 bool ShouldRequestAudioFocusForInfo(const ContextMediaInfo& aInfo) const; 131 bool ShouldAbandonAudioFocusForInfo(const ContextMediaInfo& aInfo) const; 132 133 // This contains all the media status of browsing contexts within a tab. 134 nsTHashMap<uint64_t, UniquePtr<ContextMediaInfo>> mContextInfoMap; 135 Maybe<uint64_t> mOwningAudioFocusContextId; 136 }; 137 138 } // namespace dom 139 } // namespace mozilla 140 141 #endif // DOM_MEDIA_MEDIACONTROL_MEDIAPLAYBACKSTATUS_H_ 142