AudioChannelService.h (8521B)
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/. */ 6 7 #ifndef mozilla_dom_audiochannelservice_h__ 8 #define mozilla_dom_audiochannelservice_h__ 9 10 #include <functional> 11 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" 18 #include "nsTArray.h" 19 #include "nsTObserverArray.h" 20 21 class nsPIDOMWindowOuter; 22 struct PRLogModuleInfo; 23 24 namespace mozilla::dom { 25 26 class AudioPlaybackConfig { 27 public: 28 AudioPlaybackConfig() 29 : mVolume(1.0), 30 mMuted(false), 31 mSuspend(nsISuspendedTypes::NONE_SUSPENDED), 32 mNumberOfAgents(0) {} 33 34 AudioPlaybackConfig(float aVolume, bool aMuted, uint32_t aSuspended) 35 : mVolume(aVolume), 36 mMuted(aMuted), 37 mSuspend(aSuspended), 38 mNumberOfAgents(0) {} 39 40 float mVolume; 41 bool mMuted; 42 uint32_t mSuspend; 43 bool mCapturedAudio = false; 44 uint32_t mNumberOfAgents; 45 }; 46 47 class AudioChannelService final : public nsIObserver { 48 public: 49 NS_DECL_ISUPPORTS 50 NS_DECL_NSIOBSERVER 51 52 /** 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. 57 * 58 * - Sound indicator 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. 62 * 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. 68 * 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. 73 * 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. 80 * 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. 85 * 86 * --------------------------------------------------------------------------- 87 * 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 91 */ 92 MOZ_DEFINE_ENUM_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE( 93 AudibleState, uint8_t, (eNotAudible, eMaybeAudible, eAudible)); 94 95 enum AudioCaptureState : bool { eCapturing = true, eNotCapturing = false }; 96 97 MOZ_DEFINE_ENUM_WITH_BASE_AND_TOSTRING_AT_CLASS_SCOPE( 98 AudibleChangedReasons, uint32_t, 99 (eVolumeChanged, eDataAudibleChanged, ePauseStateChanged)); 100 101 /** 102 * Returns the AudioChannelServce singleton. 103 * If AudioChannelService doesn't exist, create and return new one. 104 * Only to be called from main thread. 105 */ 106 static already_AddRefed<AudioChannelService> GetOrCreate(); 107 108 /** 109 * Returns the AudioChannelService singleton if one exists. 110 * If AudioChannelService doesn't exist, returns null. 111 */ 112 static already_AddRefed<AudioChannelService> Get(); 113 114 static LogModule* GetAudioChannelLog(); 115 116 static bool IsEnableAudioCompeting(); 117 118 /** 119 * Any audio channel agent that starts playing should register itself to 120 * this service, sharing the AudioChannel. 121 */ 122 void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, 123 AudibleState aAudible); 124 125 /** 126 * Any audio channel agent that stops playing should unregister itself to 127 * this service. 128 */ 129 void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent); 130 131 /** 132 * Return the state to indicate this audioChannel for his window should keep 133 * playing/muted/suspended. 134 */ 135 AudioPlaybackConfig GetMediaConfig(nsPIDOMWindowOuter* aWindow) const; 136 137 /** 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. 141 */ 142 void AudioAudibleChanged(AudioChannelAgent* aAgent, AudibleState aAudible, 143 AudibleChangedReasons aReason); 144 145 bool IsWindowActive(nsPIDOMWindowOuter* aWindow); 146 147 void RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow, float aVolume, 148 bool aMuted); 149 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); 156 157 void NotifyResumingDelayedMedia(nsPIDOMWindowOuter* aWindow); 158 159 private: 160 AudioChannelService(); 161 ~AudioChannelService(); 162 163 void RefreshAgents(nsPIDOMWindowOuter* aWindow, 164 const std::function<void(AudioChannelAgent*)>& aFunc); 165 166 void RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow, 167 nsSuspendedTypes aSuspend); 168 169 static void CreateServiceIfNeeded(); 170 171 /** 172 * Shutdown the singleton. 173 */ 174 static void Shutdown(); 175 176 void RefreshAgentsAudioFocusChanged(AudioChannelAgent* aAgent); 177 178 class AudioChannelWindow final { 179 public: 180 explicit AudioChannelWindow(uint64_t aWindowID) 181 : mWindowID(aWindowID), 182 mIsAudioCaptured(false), 183 mShouldSendActiveMediaBlockStopEvent(false) {} 184 185 void AudioAudibleChanged(AudioChannelAgent* aAgent, AudibleState aAudible, 186 AudibleChangedReasons aReason); 187 188 void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible); 189 void RemoveAgent(AudioChannelAgent* aAgent); 190 191 void NotifyMediaBlockStop(nsPIDOMWindowOuter* aWindow); 192 193 uint64_t mWindowID; 194 bool mIsAudioCaptured; 195 AudioPlaybackConfig mConfig; 196 197 // Raw pointer because the AudioChannelAgent must unregister itself. 198 nsTObserverArray<AudioChannelAgent*> mAgents; 199 nsTObserverArray<AudioChannelAgent*> mAudibleAgents; 200 201 // If we've dispatched "activeMediaBlockStart" event, we must dispatch 202 // another event "activeMediablockStop" when the window is resumed from 203 // suspend-block. 204 bool mShouldSendActiveMediaBlockStopEvent; 205 206 private: 207 void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent, 208 AudibleChangedReasons aReason); 209 void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent, 210 AudibleChangedReasons aReason); 211 212 void AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent); 213 void RemoveAgentAndReduceAgentsNum(AudioChannelAgent* aAgent); 214 215 bool IsFirstAudibleAgent() const; 216 bool IsLastAudibleAgent() const; 217 218 void NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow, 219 AudibleState aAudible, 220 AudibleChangedReasons aReason); 221 222 void MaybeNotifyMediaBlockStart(AudioChannelAgent* aAgent); 223 }; 224 225 AudioChannelWindow* GetOrCreateWindowData(nsPIDOMWindowOuter* aWindow); 226 227 AudioChannelWindow* GetWindowData(uint64_t aWindowID) const; 228 229 nsTObserverArray<UniquePtr<AudioChannelWindow>> mWindows; 230 }; 231 232 const char* SuspendTypeToStr(const nsSuspendedTypes& aSuspend); 233 234 } // namespace mozilla::dom 235 236 #endif