/* -*- 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 { 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 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); 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 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 mAgents; nsTObserverArray 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> mWindows; nsTObserverArray> mPlayingChildren; // Raw pointers because TabParents must unregister themselves. nsTArray mTabParents; nsCOMPtr 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