diff options
Diffstat (limited to 'dom/audiochannel/AudioChannelService.h')
-rw-r--r-- | dom/audiochannel/AudioChannelService.h | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/dom/audiochannel/AudioChannelService.h b/dom/audiochannel/AudioChannelService.h new file mode 100644 index 000000000..b16832b5f --- /dev/null +++ b/dom/audiochannel/AudioChannelService.h @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 mozilla_dom_audiochannelservice_h__ +#define mozilla_dom_audiochannelservice_h__ + +#include "nsIAudioChannelService.h" +#include "nsAutoPtr.h" +#include "nsIObserver.h" +#include "nsTObserverArray.h" +#include "nsTArray.h" + +#include "AudioChannelAgent.h" +#include "nsAttrValue.h" +#include "mozilla/dom/AudioChannelBinding.h" +#include "mozilla/Function.h" + +class nsIRunnable; +class nsPIDOMWindowOuter; +struct PRLogModuleInfo; + +namespace mozilla { +namespace dom { + +#ifdef MOZ_WIDGET_GONK +class SpeakerManagerService; +#endif + +class TabParent; + +#define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_ + +class AudioPlaybackConfig +{ +public: + AudioPlaybackConfig() + : mVolume(1.0) + , mMuted(false) + , mSuspend(nsISuspendedTypes::NONE_SUSPENDED) + {} + + AudioPlaybackConfig(float aVolume, bool aMuted, uint32_t aSuspended) + : mVolume(aVolume) + , mMuted(aMuted) + , mSuspend(aSuspended) + {} + + void SetConfig(float aVolume, bool aMuted, uint32_t aSuspended) + { + mVolume = aVolume; + mMuted = aMuted; + mSuspend = aSuspended; + } + + float mVolume; + bool mMuted; + uint32_t mSuspend; +}; + +class AudioChannelService final : public nsIAudioChannelService + , public nsIObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + NS_DECL_NSIAUDIOCHANNELSERVICE + + /** + * eNotAudible : agent is not audible + * eMaybeAudible : agent is not audible now, but it might be audible later + * eAudible : agent is audible now + */ + enum AudibleState : uint8_t { + eNotAudible = 0, + eMaybeAudible = 1, + eAudible = 2 + }; + + enum AudioCaptureState : bool { + eCapturing = true, + eNotCapturing = false + }; + + enum AudibleChangedReasons : uint32_t { + eVolumeChanged = 0, + eDataAudibleChanged = 1, + ePauseStateChanged = 2 + }; + + /** + * Returns the AudioChannelServce singleton. + * If AudioChannelServce is not exist, create and return new one. + * Only to be called from main thread. + */ + static already_AddRefed<AudioChannelService> GetOrCreate(); + + static bool IsAudioChannelMutedByDefault(); + + static PRLogModuleInfo* GetAudioChannelLog(); + + static bool IsEnableAudioCompeting(); + + /** + * Any audio channel agent that starts playing should register itself to + * this service, sharing the AudioChannel. + */ + void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, + AudibleState aAudible); + + /** + * Any audio channel agent that stops playing should unregister itself to + * this service. + */ + void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent); + + /** + * For nested iframes. + */ + void RegisterTabParent(TabParent* aTabParent); + void UnregisterTabParent(TabParent* aTabParent); + + /** + * Return the state to indicate this audioChannel for his window should keep + * playing/muted/suspended. + */ + AudioPlaybackConfig GetMediaConfig(nsPIDOMWindowOuter* aWindow, + uint32_t aAudioChannel) const; + + /** + * Called this method when the audible state of the audio playback changed, + * it would dispatch the playback event to observers which want to know the + * actual audible state of the window. + */ + void AudioAudibleChanged(AudioChannelAgent* aAgent, + AudibleState aAudible, + AudibleChangedReasons aReason); + + /* Methods for the BrowserElementAudioChannel */ + float GetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel); + + void SetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel, + float aVolume); + + bool GetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel); + + void SetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel, + bool aMuted); + + bool IsAudioChannelActive(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel); + + /** + * Return true if there is a telephony channel active in this process + * or one of its subprocesses. + */ + bool TelephonyChannelIsActive(); + + /** + * Return true if a normal or content channel is active for the given + * process ID. + */ + bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID); + + /*** + * AudioChannelManager calls this function to notify the default channel used + * to adjust volume when there is no any active channel. if aChannel is -1, + * the default audio channel will be used. Otherwise aChannel is casted to + * AudioChannel enum. + */ + virtual void SetDefaultVolumeControlChannel(int32_t aChannel, + bool aVisible); + + bool AnyAudioChannelIsActive(); + + void RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow); + void RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow, + nsSuspendedTypes aSuspend); + + void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel, + nsPIDOMWindowOuter* aWindow); + + // This method needs to know the inner window that wants to capture audio. We + // group agents per top outer window, but we can have multiple innerWindow per + // top outerWindow (subiframes, etc.) and we have to identify all the agents + // just for a particular innerWindow. + void SetWindowAudioCaptured(nsPIDOMWindowOuter* aWindow, + uint64_t aInnerWindowID, + bool aCapture); + +#ifdef MOZ_WIDGET_GONK + void RegisterSpeakerManager(SpeakerManagerService* aSpeakerManager) + { + if (!mSpeakerManager.Contains(aSpeakerManager)) { + mSpeakerManager.AppendElement(aSpeakerManager); + } + } + + void UnregisterSpeakerManager(SpeakerManagerService* aSpeakerManager) + { + mSpeakerManager.RemoveElement(aSpeakerManager); + } +#endif + + static const nsAttrValue::EnumTable* GetAudioChannelTable(); + static AudioChannel GetAudioChannel(const nsAString& aString); + static AudioChannel GetDefaultAudioChannel(); + static void GetAudioChannelString(AudioChannel aChannel, nsAString& aString); + static void GetDefaultAudioChannelString(nsAString& aString); + + void Notify(uint64_t aWindowID); + + void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel, + bool aContentOrNormalChannel, bool aAnyChannel); + +private: + AudioChannelService(); + ~AudioChannelService(); + + void RefreshAgents(nsPIDOMWindowOuter* aWindow, + mozilla::function<void(AudioChannelAgent*)> aFunc); + + static void CreateServiceIfNeeded(); + + /** + * Shutdown the singleton. + */ + static void Shutdown(); + + void MaybeSendStatusUpdate(); + + bool ContentOrNormalChannelIsActive(); + + /* Send the default-volume-channel-changed notification */ + void SetDefaultVolumeControlChannelInternal(int32_t aChannel, + bool aVisible, uint64_t aChildID); + + void RefreshAgentsAudioFocusChanged(AudioChannelAgent* aAgent); + + class AudioChannelConfig final : public AudioPlaybackConfig + { + public: + AudioChannelConfig() + : AudioPlaybackConfig(1.0, IsAudioChannelMutedByDefault(), + nsISuspendedTypes::NONE_SUSPENDED) + , mNumberOfAgents(0) + {} + + uint32_t mNumberOfAgents; + }; + + class AudioChannelWindow final + { + public: + explicit AudioChannelWindow(uint64_t aWindowID) + : mWindowID(aWindowID) + , mIsAudioCaptured(false) + , mOwningAudioFocus(!AudioChannelService::IsEnableAudioCompeting()) + { + // Workaround for bug1183033, system channel type can always playback. + mChannels[(int16_t)AudioChannel::System].mMuted = false; + } + + void AudioFocusChanged(AudioChannelAgent* aNewPlayingAgent); + void AudioAudibleChanged(AudioChannelAgent* aAgent, + AudibleState aAudible, + AudibleChangedReasons aReason); + + void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible); + void RemoveAgent(AudioChannelAgent* aAgent); + + uint64_t mWindowID; + bool mIsAudioCaptured; + AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS]; + + // Raw pointer because the AudioChannelAgent must unregister itself. + nsTObserverArray<AudioChannelAgent*> mAgents; + nsTObserverArray<AudioChannelAgent*> mAudibleAgents; + + // Owning audio focus when the window starts playing audible sound, and + // lose audio focus when other windows starts playing. + bool mOwningAudioFocus; + + private: + void AudioCapturedChanged(AudioChannelAgent* aAgent, + AudioCaptureState aCapture); + + void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent, + AudibleChangedReasons aReason); + void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent, + AudibleChangedReasons aReason); + + void AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent); + void RemoveAgentAndReduceAgentsNum(AudioChannelAgent* aAgent); + + bool IsFirstAudibleAgent() const; + bool IsLastAudibleAgent() const; + + void NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow, + AudibleState aAudible, + AudibleChangedReasons aReason); + + void NotifyChannelActive(uint64_t aWindowID, AudioChannel aChannel, + bool aActive); + void MaybeNotifyMediaBlocked(AudioChannelAgent* aAgent); + + void RequestAudioFocus(AudioChannelAgent* aAgent); + // We need to do audio competing only when the new incoming agent started. + void NotifyAudioCompetingChanged(AudioChannelAgent* aAgent); + + uint32_t GetCompetingBehavior(AudioChannelAgent* aAgent, + int32_t aIncomingChannelType) const; + bool IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const; + bool IsAudioCompetingInSameTab() const; + bool IsContainingPlayingAgent(AudioChannelAgent* aAgent) const; + + bool IsInactiveWindow() const; + }; + + AudioChannelWindow* + GetOrCreateWindowData(nsPIDOMWindowOuter* aWindow); + + AudioChannelWindow* + GetWindowData(uint64_t aWindowID) const; + + struct AudioChannelChildStatus final + { + explicit AudioChannelChildStatus(uint64_t aChildID) + : mChildID(aChildID) + , mActiveTelephonyChannel(false) + , mActiveContentOrNormalChannel(false) + {} + + uint64_t mChildID; + bool mActiveTelephonyChannel; + bool mActiveContentOrNormalChannel; + }; + + AudioChannelChildStatus* + GetChildStatus(uint64_t aChildID) const; + + void + RemoveChildStatus(uint64_t aChildID); + + nsTObserverArray<nsAutoPtr<AudioChannelWindow>> mWindows; + + nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>> mPlayingChildren; + +#ifdef MOZ_WIDGET_GONK + nsTArray<SpeakerManagerService*> mSpeakerManager; +#endif + + // Raw pointers because TabParents must unregister themselves. + nsTArray<TabParent*> mTabParents; + + nsCOMPtr<nsIRunnable> mRunnable; + + uint64_t mDefChannelChildID; + + // These boolean are used to know if we have to send an status update to the + // service running in the main process. + bool mTelephonyChannel; + bool mContentOrNormalChannel; + bool mAnyChannel; + + // This is needed for IPC comunication between + // AudioChannelServiceChild and this class. + friend class ContentParent; + friend class ContentChild; +}; + +} // namespace dom +} // namespace mozilla + +#endif |