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 "nsIAudioChannelService.h" 11 #include "nsAutoPtr.h" 12 #include "nsIObserver.h" 13 #include "nsTObserverArray.h" 14 #include "nsTArray.h" 15 16 #include "AudioChannelAgent.h" 17 #include "nsAttrValue.h" 18 #include "mozilla/dom/AudioChannelBinding.h" 19 #include "mozilla/Function.h" 20 21 class nsIRunnable; 22 class nsPIDOMWindowOuter; 23 struct PRLogModuleInfo; 24 25 namespace mozilla { 26 namespace dom { 27 28 #ifdef MOZ_WIDGET_GONK 29 class SpeakerManagerService; 30 #endif 31 32 class TabParent; 33 34 #define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_ 35 36 class AudioPlaybackConfig 37 { 38 public: AudioPlaybackConfig()39 AudioPlaybackConfig() 40 : mVolume(1.0) 41 , mMuted(false) 42 , mSuspend(nsISuspendedTypes::NONE_SUSPENDED) 43 {} 44 AudioPlaybackConfig(float aVolume,bool aMuted,uint32_t aSuspended)45 AudioPlaybackConfig(float aVolume, bool aMuted, uint32_t aSuspended) 46 : mVolume(aVolume) 47 , mMuted(aMuted) 48 , mSuspend(aSuspended) 49 {} 50 SetConfig(float aVolume,bool aMuted,uint32_t aSuspended)51 void SetConfig(float aVolume, bool aMuted, uint32_t aSuspended) 52 { 53 mVolume = aVolume; 54 mMuted = aMuted; 55 mSuspend = aSuspended; 56 } 57 58 float mVolume; 59 bool mMuted; 60 uint32_t mSuspend; 61 }; 62 63 class AudioChannelService final : public nsIAudioChannelService 64 , public nsIObserver 65 { 66 public: 67 NS_DECL_ISUPPORTS 68 NS_DECL_NSIOBSERVER 69 NS_DECL_NSIAUDIOCHANNELSERVICE 70 71 /** 72 * eNotAudible : agent is not audible 73 * eMaybeAudible : agent is not audible now, but it might be audible later 74 * eAudible : agent is audible now 75 */ 76 enum AudibleState : uint8_t { 77 eNotAudible = 0, 78 eMaybeAudible = 1, 79 eAudible = 2 80 }; 81 82 enum AudioCaptureState : bool { 83 eCapturing = true, 84 eNotCapturing = false 85 }; 86 87 enum AudibleChangedReasons : uint32_t { 88 eVolumeChanged = 0, 89 eDataAudibleChanged = 1, 90 ePauseStateChanged = 2 91 }; 92 93 /** 94 * Returns the AudioChannelServce singleton. 95 * If AudioChannelServce is not exist, create and return new one. 96 * Only to be called from main thread. 97 */ 98 static already_AddRefed<AudioChannelService> GetOrCreate(); 99 100 static bool IsAudioChannelMutedByDefault(); 101 102 static PRLogModuleInfo* GetAudioChannelLog(); 103 104 static bool IsEnableAudioCompeting(); 105 106 /** 107 * Any audio channel agent that starts playing should register itself to 108 * this service, sharing the AudioChannel. 109 */ 110 void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, 111 AudibleState aAudible); 112 113 /** 114 * Any audio channel agent that stops playing should unregister itself to 115 * this service. 116 */ 117 void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent); 118 119 /** 120 * For nested iframes. 121 */ 122 void RegisterTabParent(TabParent* aTabParent); 123 void UnregisterTabParent(TabParent* aTabParent); 124 125 /** 126 * Return the state to indicate this audioChannel for his window should keep 127 * playing/muted/suspended. 128 */ 129 AudioPlaybackConfig GetMediaConfig(nsPIDOMWindowOuter* aWindow, 130 uint32_t aAudioChannel) const; 131 132 /** 133 * Called this method when the audible state of the audio playback changed, 134 * it would dispatch the playback event to observers which want to know the 135 * actual audible state of the window. 136 */ 137 void AudioAudibleChanged(AudioChannelAgent* aAgent, 138 AudibleState aAudible, 139 AudibleChangedReasons aReason); 140 141 /* Methods for the BrowserElementAudioChannel */ 142 float GetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel); 143 144 void SetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel, 145 float aVolume); 146 147 bool GetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel); 148 149 void SetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel, 150 bool aMuted); 151 152 bool IsAudioChannelActive(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel); 153 154 /** 155 * Return true if there is a telephony channel active in this process 156 * or one of its subprocesses. 157 */ 158 bool TelephonyChannelIsActive(); 159 160 /** 161 * Return true if a normal or content channel is active for the given 162 * process ID. 163 */ 164 bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID); 165 166 /*** 167 * AudioChannelManager calls this function to notify the default channel used 168 * to adjust volume when there is no any active channel. if aChannel is -1, 169 * the default audio channel will be used. Otherwise aChannel is casted to 170 * AudioChannel enum. 171 */ 172 virtual void SetDefaultVolumeControlChannel(int32_t aChannel, 173 bool aVisible); 174 175 bool AnyAudioChannelIsActive(); 176 177 void RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow); 178 void RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow, 179 nsSuspendedTypes aSuspend); 180 181 void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel, 182 nsPIDOMWindowOuter* aWindow); 183 184 // This method needs to know the inner window that wants to capture audio. We 185 // group agents per top outer window, but we can have multiple innerWindow per 186 // top outerWindow (subiframes, etc.) and we have to identify all the agents 187 // just for a particular innerWindow. 188 void SetWindowAudioCaptured(nsPIDOMWindowOuter* aWindow, 189 uint64_t aInnerWindowID, 190 bool aCapture); 191 192 #ifdef MOZ_WIDGET_GONK RegisterSpeakerManager(SpeakerManagerService * aSpeakerManager)193 void RegisterSpeakerManager(SpeakerManagerService* aSpeakerManager) 194 { 195 if (!mSpeakerManager.Contains(aSpeakerManager)) { 196 mSpeakerManager.AppendElement(aSpeakerManager); 197 } 198 } 199 UnregisterSpeakerManager(SpeakerManagerService * aSpeakerManager)200 void UnregisterSpeakerManager(SpeakerManagerService* aSpeakerManager) 201 { 202 mSpeakerManager.RemoveElement(aSpeakerManager); 203 } 204 #endif 205 206 static const nsAttrValue::EnumTable* GetAudioChannelTable(); 207 static AudioChannel GetAudioChannel(const nsAString& aString); 208 static AudioChannel GetDefaultAudioChannel(); 209 static void GetAudioChannelString(AudioChannel aChannel, nsAString& aString); 210 static void GetDefaultAudioChannelString(nsAString& aString); 211 212 void Notify(uint64_t aWindowID); 213 214 void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel, 215 bool aContentOrNormalChannel, bool aAnyChannel); 216 217 private: 218 AudioChannelService(); 219 ~AudioChannelService(); 220 221 void RefreshAgents(nsPIDOMWindowOuter* aWindow, 222 mozilla::function<void(AudioChannelAgent*)> aFunc); 223 224 static void CreateServiceIfNeeded(); 225 226 /** 227 * Shutdown the singleton. 228 */ 229 static void Shutdown(); 230 231 void MaybeSendStatusUpdate(); 232 233 bool ContentOrNormalChannelIsActive(); 234 235 /* Send the default-volume-channel-changed notification */ 236 void SetDefaultVolumeControlChannelInternal(int32_t aChannel, 237 bool aVisible, uint64_t aChildID); 238 239 void RefreshAgentsAudioFocusChanged(AudioChannelAgent* aAgent); 240 241 class AudioChannelConfig final : public AudioPlaybackConfig 242 { 243 public: AudioChannelConfig()244 AudioChannelConfig() 245 : AudioPlaybackConfig(1.0, IsAudioChannelMutedByDefault(), 246 nsISuspendedTypes::NONE_SUSPENDED) 247 , mNumberOfAgents(0) 248 {} 249 250 uint32_t mNumberOfAgents; 251 }; 252 253 class AudioChannelWindow final 254 { 255 public: AudioChannelWindow(uint64_t aWindowID)256 explicit AudioChannelWindow(uint64_t aWindowID) 257 : mWindowID(aWindowID) 258 , mIsAudioCaptured(false) 259 , mOwningAudioFocus(!AudioChannelService::IsEnableAudioCompeting()) 260 { 261 // Workaround for bug1183033, system channel type can always playback. 262 mChannels[(int16_t)AudioChannel::System].mMuted = false; 263 } 264 265 void AudioFocusChanged(AudioChannelAgent* aNewPlayingAgent); 266 void AudioAudibleChanged(AudioChannelAgent* aAgent, 267 AudibleState aAudible, 268 AudibleChangedReasons aReason); 269 270 void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible); 271 void RemoveAgent(AudioChannelAgent* aAgent); 272 273 uint64_t mWindowID; 274 bool mIsAudioCaptured; 275 AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS]; 276 277 // Raw pointer because the AudioChannelAgent must unregister itself. 278 nsTObserverArray<AudioChannelAgent*> mAgents; 279 nsTObserverArray<AudioChannelAgent*> mAudibleAgents; 280 281 // Owning audio focus when the window starts playing audible sound, and 282 // lose audio focus when other windows starts playing. 283 bool mOwningAudioFocus; 284 285 private: 286 void AudioCapturedChanged(AudioChannelAgent* aAgent, 287 AudioCaptureState aCapture); 288 289 void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent, 290 AudibleChangedReasons aReason); 291 void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent, 292 AudibleChangedReasons aReason); 293 294 void AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent); 295 void RemoveAgentAndReduceAgentsNum(AudioChannelAgent* aAgent); 296 297 bool IsFirstAudibleAgent() const; 298 bool IsLastAudibleAgent() const; 299 300 void NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow, 301 AudibleState aAudible, 302 AudibleChangedReasons aReason); 303 304 void NotifyChannelActive(uint64_t aWindowID, AudioChannel aChannel, 305 bool aActive); 306 void MaybeNotifyMediaBlocked(AudioChannelAgent* aAgent); 307 308 void RequestAudioFocus(AudioChannelAgent* aAgent); 309 // We need to do audio competing only when the new incoming agent started. 310 void NotifyAudioCompetingChanged(AudioChannelAgent* aAgent); 311 312 uint32_t GetCompetingBehavior(AudioChannelAgent* aAgent, 313 int32_t aIncomingChannelType) const; 314 bool IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const; 315 bool IsAudioCompetingInSameTab() const; 316 bool IsContainingPlayingAgent(AudioChannelAgent* aAgent) const; 317 318 bool IsInactiveWindow() const; 319 }; 320 321 AudioChannelWindow* 322 GetOrCreateWindowData(nsPIDOMWindowOuter* aWindow); 323 324 AudioChannelWindow* 325 GetWindowData(uint64_t aWindowID) const; 326 327 struct AudioChannelChildStatus final 328 { AudioChannelChildStatusfinal329 explicit AudioChannelChildStatus(uint64_t aChildID) 330 : mChildID(aChildID) 331 , mActiveTelephonyChannel(false) 332 , mActiveContentOrNormalChannel(false) 333 {} 334 335 uint64_t mChildID; 336 bool mActiveTelephonyChannel; 337 bool mActiveContentOrNormalChannel; 338 }; 339 340 AudioChannelChildStatus* 341 GetChildStatus(uint64_t aChildID) const; 342 343 void 344 RemoveChildStatus(uint64_t aChildID); 345 346 nsTObserverArray<nsAutoPtr<AudioChannelWindow>> mWindows; 347 348 nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>> mPlayingChildren; 349 350 #ifdef MOZ_WIDGET_GONK 351 nsTArray<SpeakerManagerService*> mSpeakerManager; 352 #endif 353 354 // Raw pointers because TabParents must unregister themselves. 355 nsTArray<TabParent*> mTabParents; 356 357 nsCOMPtr<nsIRunnable> mRunnable; 358 359 uint64_t mDefChannelChildID; 360 361 // These boolean are used to know if we have to send an status update to the 362 // service running in the main process. 363 bool mTelephonyChannel; 364 bool mContentOrNormalChannel; 365 bool mAnyChannel; 366 367 // This is needed for IPC comunication between 368 // AudioChannelServiceChild and this class. 369 friend class ContentParent; 370 friend class ContentChild; 371 }; 372 373 } // namespace dom 374 } // namespace mozilla 375 376 #endif 377