gecko-dev/dom/audiochannel/AudioChannelService.h

376 строки
12 KiB
C++

/* -*- 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/Logging.h"
#include <functional>
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 AudioChannelService doesn't exist, create and return new one.
* Only to be called from main thread.
*/
static already_AddRefed<AudioChannelService> GetOrCreate();
/**
* Returns the AudioChannelService singleton if one exists.
* If AudioChannelService doesn't exist, returns null.
*/
static already_AddRefed<AudioChannelService> Get();
static bool IsAudioChannelMutedByDefault();
static LogModule* 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);
bool IsWindowActive(nsPIDOMWindowOuter* aWindow);
/**
* 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);
void NotifyCreatedNewAgent(AudioChannelAgent* aAgent);
void NotifyMediaResumedFromBlock(nsPIDOMWindowOuter* aWindow);
private:
AudioChannelService();
~AudioChannelService();
void RefreshAgents(nsPIDOMWindowOuter* aWindow,
std::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())
, mShouldSendBlockStopEvent(false)
{
// 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);
void NotifyMediaBlockStop(nsPIDOMWindowOuter* aWindow);
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;
// If we've dispatched "blockStart" event, we must dispatch another event
// "blockStop" when the window is resumed from suspend-block.
bool mShouldSendBlockStopEvent;
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 MaybeNotifyMediaBlockStart(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;
// 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