summaryrefslogtreecommitdiffstats
path: root/dom/media/mediacontrol/MediaPlaybackStatus.h
blob: f9ac25f73d21a113c140368303cc0aca0ad148e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* 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_MEDIAPLAYBACKSTATUS_H_
#define DOM_MEDIA_MEDIACONTROL_MEDIAPLAYBACKSTATUS_H_

#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/MediaSession.h"
#include "nsISupportsImpl.h"
#include "nsTArray.h"
#include "nsTHashMap.h"
#include "nsID.h"

namespace mozilla::dom {

/**
 * This enum is used to update controlled media state to the media controller in
 * the chrome process.
 * `eStarted`: media has successfully registered to the content media controller
 * `ePlayed` : media has started playing
 * `ePaused` : media has paused playing, but still can be resumed by content
 *             media controller
 * `eStopped`: media has unregistered from the content media controller, we can
 *             not control it anymore
 */
enum class MediaPlaybackState : uint32_t {
  eStarted,
  ePlayed,
  ePaused,
  eStopped,
};

/**
 * This enum is used to update controlled media audible audible state to the
 * media controller in the chrome process.
 */
enum class MediaAudibleState : bool {
  eInaudible = false,
  eAudible = true,
};

/**
 * MediaPlaybackStatus is an internal module for the media controller, it
 * represents a tab's media related status, such like "does the tab contain any
 * controlled media? is the tab playing? is the tab audible?".
 *
 * The reason we need this class is that we would like to encapsulate the
 * details of determining the tab's media status. A tab can contains multiple
 * browsing contexts, and each browsing context can have different media status.
 * The final media status would be decided by checking all those context status.
 *
 * Use `UpdateMediaXXXState()` to update controlled media status, and use
 * `IsXXX()` methods to acquire the playback status of the tab.
 *
 * As we know each context's audible state, we can decide which context should
 * owns the audio focus when multiple contexts are all playing audible media at
 * the same time. In that cases, the latest context that plays media would own
 * the audio focus. When the context owning the audio focus is destroyed, we
 * would see if there is another other context still playing audible media, and
 * switch the audio focus to another context.
 */
class MediaPlaybackStatus final {
 public:
  void UpdateMediaPlaybackState(uint64_t aContextId, MediaPlaybackState aState);
  void UpdateMediaAudibleState(uint64_t aContextId, MediaAudibleState aState);
  void UpdateGuessedPositionState(uint64_t aContextId, const nsID& aElementId,
                                  const Maybe<PositionState>& aState);

  bool IsPlaying() const;
  bool IsAudible() const;
  bool IsAnyMediaBeingControlled() const;
  Maybe<PositionState> GuessedMediaPositionState(
      Maybe<uint64_t> aPreferredContextId) const;

  Maybe<uint64_t> GetAudioFocusOwnerContextId() const;

 private:
  /**
   * This internal class stores detailed media status of controlled media for
   * a browsing context.
   */
  class ContextMediaInfo final {
   public:
    explicit ContextMediaInfo(uint64_t aContextId) : mContextId(aContextId) {}
    ~ContextMediaInfo() = default;

    void IncreaseControlledMediaNum() {
#ifndef FUZZING_SNAPSHOT
      MOZ_DIAGNOSTIC_ASSERT(mControlledMediaNum < UINT_MAX);
#endif
      mControlledMediaNum++;
    }
    void DecreaseControlledMediaNum() {
#ifndef FUZZING_SNAPSHOT
      MOZ_DIAGNOSTIC_ASSERT(mControlledMediaNum > 0);
#endif
      mControlledMediaNum--;
    }
    void IncreasePlayingMediaNum() {
#ifndef FUZZING_SNAPSHOT
      MOZ_DIAGNOSTIC_ASSERT(mPlayingMediaNum < mControlledMediaNum);
#endif
      mPlayingMediaNum++;
    }
    void DecreasePlayingMediaNum() {
#ifndef FUZZING_SNAPSHOT
      MOZ_DIAGNOSTIC_ASSERT(mPlayingMediaNum > 0);
#endif
      mPlayingMediaNum--;
    }
    void IncreaseAudibleMediaNum() {
#ifndef FUZZING_SNAPSHOT
      MOZ_DIAGNOSTIC_ASSERT(mAudibleMediaNum < mPlayingMediaNum);
#endif
      mAudibleMediaNum++;
    }
    void DecreaseAudibleMediaNum() {
#ifndef FUZZING_SNAPSHOT
      MOZ_DIAGNOSTIC_ASSERT(mAudibleMediaNum > 0);
#endif
      mAudibleMediaNum--;
    }
    bool IsPlaying() const { return mPlayingMediaNum > 0; }
    bool IsAudible() const { return mAudibleMediaNum > 0; }
    bool IsAnyMediaBeingControlled() const { return mControlledMediaNum > 0; }
    uint64_t Id() const { return mContextId; }

    Maybe<PositionState> GuessedPositionState() const;
    void UpdateGuessedPositionState(const nsID& aElementId,
                                    const Maybe<PositionState>& aState);

   private:
    /**
     * The possible value for those three numbers should follow this rule,
     * mControlledMediaNum >= mPlayingMediaNum >= mAudibleMediaNum
     */
    uint32_t mControlledMediaNum = 0;
    uint32_t mAudibleMediaNum = 0;
    uint32_t mPlayingMediaNum = 0;
    uint64_t mContextId = 0;

    /**
     * Contains the guessed position state of all media elements in this
     * browsing context identified by their ID.
     */
    nsTHashMap<nsID, PositionState> mGuessedPositionStateMap;
  };

  ContextMediaInfo& GetNotNullContextInfo(uint64_t aContextId);
  void DestroyContextInfo(uint64_t aContextId);

  void ChooseNewContextToOwnAudioFocus();
  void SetOwningAudioFocusContextId(Maybe<uint64_t>&& aContextId);
  bool IsContextOwningAudioFocus(uint64_t aContextId) const;
  bool ShouldRequestAudioFocusForInfo(const ContextMediaInfo& aInfo) const;
  bool ShouldAbandonAudioFocusForInfo(const ContextMediaInfo& aInfo) const;

  // This contains all the media status of browsing contexts within a tab.
  nsTHashMap<uint64_t, UniquePtr<ContextMediaInfo>> mContextInfoMap;
  Maybe<uint64_t> mOwningAudioFocusContextId;
};

}  // namespace mozilla::dom

#endif  //  DOM_MEDIA_MEDIACONTROL_MEDIAPLAYBACKSTATUS_H_