1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_audiochannelservice_h__
8 #define mozilla_dom_audiochannelservice_h__
12 #include "AudioChannelAgent.h"
13 #include "mozilla/DefineEnum.h"
14 #include "mozilla/Logging.h"
15 #include "mozilla/UniquePtr.h"
16 #include "nsAttrValue.h"
17 #include "nsIObserver.h"
19 #include "nsTObserverArray.h"
21 class nsPIDOMWindowOuter
;
22 struct PRLogModuleInfo
;
24 namespace mozilla::dom
{
26 class AudioPlaybackConfig
{
31 mSuspend(nsISuspendedTypes::NONE_SUSPENDED
),
34 AudioPlaybackConfig(float aVolume
, bool aMuted
, uint32_t aSuspended
)
43 bool mCapturedAudio
= false;
44 uint32_t mNumberOfAgents
;
47 class AudioChannelService final
: public nsIObserver
{
53 * We use `AudibleState` to represent the audible state of an owner of audio
54 * channel agent. Those information in AudioChannelWindow could help us to
55 * determine if a tab is being audible or not, in order to tell Chrome JS to
56 * show the sound indicator or delayed autoplay icon on the tab bar.
59 * When a tab is playing sound, we would show the sound indicator on tab bar
60 * to tell users that this tab is producing sound now. In addition, the sound
61 * indicator also give users an ablility to mute or unmute tab.
63 * When an AudioChannelWindow first contains an agent with state `eAudible`,
64 * or an AudioChannelWindow losts its last agent with state `eAudible`, we
65 * would notify Chrome JS about those changes, to tell them that a tab has
66 * been being audible or not, in order to display or remove the indicator for
67 * a corresponding tab.
69 * - Delayed autoplay icon (Play Tab icon)
70 * When we enable delaying autoplay, which is to postpone the autoplay media
71 * for unvisited tab until it first goes to foreground, or user click the
72 * play tab icon to resume the delayed media.
74 * When an AudioChannelWindow first contains an agent with state `eAudible` or
75 * `eMaybeAudible`, we would notify Chrome JS about this change, in order to
76 * show the delayed autoplay tab icon to user, which is used to notice user
77 * there is a media being delayed starting, and then user can click the play
78 * tab icon to resume the start of media, or visit that tab to resume delayed
79 * media automatically.
81 * According to our UX design, we don't show this icon for inaudible media.
82 * The reason of showing the icon for a tab, where the agent starts with state
83 * `eMaybeAudible`, is because some video might be silent in the beginning
84 * but would soon become audible later.
86 * ---------------------------------------------------------------------------
88 * eNotAudible : agent is not audible
89 * eMaybeAudible : agent is not audible now, but it might be audible later
90 * eAudible : agent is audible now
92 MOZ_DEFINE_ENUM_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE(
93 AudibleState
, uint8_t, (eNotAudible
, eMaybeAudible
, eAudible
));
95 enum AudioCaptureState
: bool { eCapturing
= true, eNotCapturing
= false };
97 MOZ_DEFINE_ENUM_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE(
98 AudibleChangedReasons
, uint32_t,
99 (eVolumeChanged
, eDataAudibleChanged
, ePauseStateChanged
));
102 * Returns the AudioChannelServce singleton.
103 * If AudioChannelService doesn't exist, create and return new one.
104 * Only to be called from main thread.
106 static already_AddRefed
<AudioChannelService
> GetOrCreate();
109 * Returns the AudioChannelService singleton if one exists.
110 * If AudioChannelService doesn't exist, returns null.
112 static already_AddRefed
<AudioChannelService
> Get();
114 static LogModule
* GetAudioChannelLog();
116 static bool IsEnableAudioCompeting();
119 * Any audio channel agent that starts playing should register itself to
120 * this service, sharing the AudioChannel.
122 void RegisterAudioChannelAgent(AudioChannelAgent
* aAgent
,
123 AudibleState aAudible
);
126 * Any audio channel agent that stops playing should unregister itself to
129 void UnregisterAudioChannelAgent(AudioChannelAgent
* aAgent
);
132 * Return the state to indicate this audioChannel for his window should keep
133 * playing/muted/suspended.
135 AudioPlaybackConfig
GetMediaConfig(nsPIDOMWindowOuter
* aWindow
) const;
138 * Called this method when the audible state of the audio playback changed,
139 * it would dispatch the playback event to observers which want to know the
140 * actual audible state of the window.
142 void AudioAudibleChanged(AudioChannelAgent
* aAgent
, AudibleState aAudible
,
143 AudibleChangedReasons aReason
);
145 bool IsWindowActive(nsPIDOMWindowOuter
* aWindow
);
147 void RefreshAgentsVolume(nsPIDOMWindowOuter
* aWindow
, float aVolume
,
150 // This method needs to know the inner window that wants to capture audio. We
151 // group agents per top outer window, but we can have multiple innerWindow per
152 // top outerWindow (subiframes, etc.) and we have to identify all the agents
153 // just for a particular innerWindow.
154 void SetWindowAudioCaptured(nsPIDOMWindowOuter
* aWindow
,
155 uint64_t aInnerWindowID
, bool aCapture
);
157 void NotifyResumingDelayedMedia(nsPIDOMWindowOuter
* aWindow
);
160 AudioChannelService();
161 ~AudioChannelService();
163 void RefreshAgents(nsPIDOMWindowOuter
* aWindow
,
164 const std::function
<void(AudioChannelAgent
*)>& aFunc
);
166 void RefreshAgentsSuspend(nsPIDOMWindowOuter
* aWindow
,
167 nsSuspendedTypes aSuspend
);
169 static void CreateServiceIfNeeded();
172 * Shutdown the singleton.
174 static void Shutdown();
176 void RefreshAgentsAudioFocusChanged(AudioChannelAgent
* aAgent
);
178 class AudioChannelWindow final
{
180 explicit AudioChannelWindow(uint64_t aWindowID
)
181 : mWindowID(aWindowID
),
182 mIsAudioCaptured(false),
183 mShouldSendActiveMediaBlockStopEvent(false) {}
185 void AudioAudibleChanged(AudioChannelAgent
* aAgent
, AudibleState aAudible
,
186 AudibleChangedReasons aReason
);
188 void AppendAgent(AudioChannelAgent
* aAgent
, AudibleState aAudible
);
189 void RemoveAgent(AudioChannelAgent
* aAgent
);
191 void NotifyMediaBlockStop(nsPIDOMWindowOuter
* aWindow
);
194 bool mIsAudioCaptured
;
195 AudioPlaybackConfig mConfig
;
197 // Raw pointer because the AudioChannelAgent must unregister itself.
198 nsTObserverArray
<AudioChannelAgent
*> mAgents
;
199 nsTObserverArray
<AudioChannelAgent
*> mAudibleAgents
;
201 // If we've dispatched "activeMediaBlockStart" event, we must dispatch
202 // another event "activeMediablockStop" when the window is resumed from
204 bool mShouldSendActiveMediaBlockStopEvent
;
207 void AppendAudibleAgentIfNotContained(AudioChannelAgent
* aAgent
,
208 AudibleChangedReasons aReason
);
209 void RemoveAudibleAgentIfContained(AudioChannelAgent
* aAgent
,
210 AudibleChangedReasons aReason
);
212 void AppendAgentAndIncreaseAgentsNum(AudioChannelAgent
* aAgent
);
213 void RemoveAgentAndReduceAgentsNum(AudioChannelAgent
* aAgent
);
215 bool IsFirstAudibleAgent() const;
216 bool IsLastAudibleAgent() const;
218 void NotifyAudioAudibleChanged(nsPIDOMWindowOuter
* aWindow
,
219 AudibleState aAudible
,
220 AudibleChangedReasons aReason
);
222 void MaybeNotifyMediaBlockStart(AudioChannelAgent
* aAgent
);
225 AudioChannelWindow
* GetOrCreateWindowData(nsPIDOMWindowOuter
* aWindow
);
227 AudioChannelWindow
* GetWindowData(uint64_t aWindowID
) const;
229 nsTObserverArray
<UniquePtr
<AudioChannelWindow
>> mWindows
;
232 const char* SuspendTypeToStr(const nsSuspendedTypes
& aSuspend
);
234 } // namespace mozilla::dom