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