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