mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1257738
- part1 : implement the audio competing mechanism.
MozReview-Commit-ID: GZw7P0kbhOa
This commit is contained in:
parent
1e3f941f78
commit
fa47d77e11
@ -1087,6 +1087,8 @@ pref("dom.performance.enable_notify_performance_timing", true);
|
||||
pref("b2g.multiscreen.chrome_remote_url", "chrome://b2g/content/shell_remote.html");
|
||||
pref("b2g.multiscreen.system_remote_url", "index_remote.html");
|
||||
|
||||
// Audio competing between tabs
|
||||
pref("dom.audiochannel.audioCompeting", false);
|
||||
|
||||
// Because we can't have nice things.
|
||||
#ifdef MOZ_GRAPHENE
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozilla/dom/SettingChangeNotificationBinding.h"
|
||||
@ -41,6 +42,7 @@ namespace {
|
||||
|
||||
// If true, any new AudioChannelAgent will be muted when created.
|
||||
bool sAudioChannelMutedByDefault = false;
|
||||
bool sAudioChannelCompeting = false;
|
||||
bool sXPCOMShuttingDown = false;
|
||||
|
||||
class NotifyChannelActiveRunnable final : public Runnable
|
||||
@ -210,6 +212,13 @@ AudioChannelService::Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
AudioChannelService::IsEnableAudioCompeting()
|
||||
{
|
||||
CreateServiceIfNeeded();
|
||||
return sAudioChannelCompeting;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(AudioChannelService)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAudioChannelService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIAudioChannelService)
|
||||
@ -241,6 +250,8 @@ AudioChannelService::AudioChannelService()
|
||||
|
||||
Preferences::AddBoolVarCache(&sAudioChannelMutedByDefault,
|
||||
"dom.audiochannel.mutedByDefault");
|
||||
Preferences::AddBoolVarCache(&sAudioChannelCompeting,
|
||||
"dom.audiochannel.audioCompeting");
|
||||
}
|
||||
|
||||
AudioChannelService::~AudioChannelService()
|
||||
@ -336,14 +347,15 @@ AudioChannelService::GetMediaConfig(nsPIDOMWindowOuter* aWindow,
|
||||
if (winData) {
|
||||
config.mVolume *= winData->mChannels[aAudioChannel].mVolume;
|
||||
config.mMuted = config.mMuted || winData->mChannels[aAudioChannel].mMuted;
|
||||
config.mSuspend = winData->mOwningAudioFocus ?
|
||||
config.mSuspend : nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE;
|
||||
}
|
||||
|
||||
config.mVolume *= window->GetAudioVolume();
|
||||
config.mMuted = config.mMuted || window->GetAudioMuted();
|
||||
|
||||
// If the mSuspend is already suspended, we don't need to set it again.
|
||||
config.mSuspend = (config.mSuspend == nsISuspendedTypes::NONE_SUSPENDED) ?
|
||||
window->GetMediaSuspend() : config.mSuspend;
|
||||
if (window->GetMediaSuspend() != nsISuspendedTypes::NONE_SUSPENDED) {
|
||||
config.mSuspend = window->GetMediaSuspend();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> win = window->GetScriptableParentOrNull();
|
||||
if (!win) {
|
||||
@ -994,6 +1006,176 @@ AudioChannelService::ChildStatusReceived(uint64_t aChildID,
|
||||
data->mActiveContentOrNormalChannel = aContentOrNormalChannel;
|
||||
}
|
||||
|
||||
void
|
||||
AudioChannelService::RefreshAgentsAudioFocusChanged(AudioChannelAgent* aAgent,
|
||||
bool aActive)
|
||||
{
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
|
||||
iter(mWindows);
|
||||
while (iter.HasMore()) {
|
||||
AudioChannelWindow* winData = iter.GetNext();
|
||||
if (winData->mOwningAudioFocus) {
|
||||
winData->AudioFocusChanged(aAgent, aActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioChannelService::AudioChannelWindow::RequestAudioFocus(AudioChannelAgent* aAgent)
|
||||
{
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
// We already have the audio focus. No operation is needed.
|
||||
if (mOwningAudioFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only foreground window can request audio focus, but it would still own the
|
||||
// audio focus even it goes to background. Audio focus would be abandoned
|
||||
// only when other foreground window starts audio competing.
|
||||
mOwningAudioFocus = !(aAgent->Window()->IsBackground());
|
||||
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelWindow, RequestAudioFocus, this = %p, "
|
||||
"agent = %p, owning audio focus = %d\n",
|
||||
this, aAgent, mOwningAudioFocus));
|
||||
}
|
||||
|
||||
void
|
||||
AudioChannelService::AudioChannelWindow::NotifyAudioCompetingChanged(AudioChannelAgent* aAgent,
|
||||
bool aActive)
|
||||
{
|
||||
// This function may be called after RemoveAgentAndReduceAgentsNum(), so the
|
||||
// agent may be not contained in mAgent. In addition, the agent would still
|
||||
// be alive because we have kungFuDeathGrip in UnregisterAudioChannelAgent().
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
|
||||
MOZ_ASSERT(service);
|
||||
|
||||
if (!service->IsEnableAudioCompeting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAgentInvolvingInAudioCompeting(aAgent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelWindow, NotifyAudioCompetingChanged, this = %p, "
|
||||
"agent = %p, active = %d\n",
|
||||
this, aAgent, aActive));
|
||||
|
||||
service->RefreshAgentsAudioFocusChanged(aAgent, aActive);
|
||||
}
|
||||
|
||||
bool
|
||||
AudioChannelService::AudioChannelWindow::IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const
|
||||
{
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
if(!mOwningAudioFocus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsAudioCompetingInSameTab()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO : add MediaSession::ambient kind, because it doens't interact with
|
||||
// other kinds.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AudioChannelService::AudioChannelWindow::IsAudioCompetingInSameTab() const
|
||||
{
|
||||
return (mOwningAudioFocus && mAudibleAgents.Length() > 1);
|
||||
}
|
||||
|
||||
void
|
||||
AudioChannelService::AudioChannelWindow::AudioFocusChanged(AudioChannelAgent* aNewPlayingAgent,
|
||||
bool aActive)
|
||||
{
|
||||
// This agent isn't always known for the current window, because it can comes
|
||||
// from other window.
|
||||
MOZ_ASSERT(aNewPlayingAgent);
|
||||
|
||||
if (mAudibleAgents.IsEmpty()) {
|
||||
// These would happen in two situations,
|
||||
// (1) Audio in page A was ended, and another page B want to play audio.
|
||||
// Page A should abandon its focus.
|
||||
// (2) Audio was paused by remote-control, page should still own the focus.
|
||||
mOwningAudioFocus = IsContainingPlayingAgent(aNewPlayingAgent);
|
||||
} else {
|
||||
nsTObserverArray<AudioChannelAgent*>::ForwardIterator iter(mAudibleAgents);
|
||||
while (iter.HasMore()) {
|
||||
AudioChannelAgent* agent = iter.GetNext();
|
||||
MOZ_ASSERT(agent);
|
||||
|
||||
// Don't need to update the playing state of new playing agent.
|
||||
if (agent == aNewPlayingAgent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t type = GetCompetingBehavior(agent,
|
||||
aNewPlayingAgent->AudioChannelType(),
|
||||
aActive);
|
||||
|
||||
// If window will be suspended, it needs to abandon the audio focus
|
||||
// because only one window can own audio focus at a time. However, we
|
||||
// would support multiple audio focus at the same time in the future.
|
||||
mOwningAudioFocus = (type == nsISuspendedTypes::NONE_SUSPENDED);
|
||||
|
||||
// TODO : support other behaviors which are definded in MediaSession API.
|
||||
switch (type) {
|
||||
case nsISuspendedTypes::NONE_SUSPENDED:
|
||||
case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE:
|
||||
agent->WindowSuspendChanged(type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelWindow, AudioFocusChanged, this = %p, "
|
||||
"OwningAudioFocus = %d\n", this, mOwningAudioFocus));
|
||||
}
|
||||
|
||||
bool
|
||||
AudioChannelService::AudioChannelWindow::IsContainingPlayingAgent(AudioChannelAgent* aAgent) const
|
||||
{
|
||||
return (aAgent->WindowID() == mWindowID);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AudioChannelService::AudioChannelWindow::GetCompetingBehavior(AudioChannelAgent* aAgent,
|
||||
int32_t aIncomingChannelType,
|
||||
bool aIncomingChannelActive) const
|
||||
{
|
||||
MOZ_ASSERT(aAgent);
|
||||
MOZ_ASSERT(mAudibleAgents.Contains(aAgent));
|
||||
|
||||
uint32_t competingBehavior = nsISuspendedTypes::NONE_SUSPENDED;
|
||||
int32_t presentChannelType = aAgent->AudioChannelType();
|
||||
|
||||
// TODO : add other competing cases for MediaSession API
|
||||
if (presentChannelType == int32_t(AudioChannel::Normal) &&
|
||||
aIncomingChannelType == int32_t(AudioChannel::Normal) &&
|
||||
aIncomingChannelActive) {
|
||||
competingBehavior = nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE;
|
||||
}
|
||||
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelWindow, GetCompetingBehavior, this = %p, "
|
||||
"present type = %d, incoming channel = %d, behavior = %d\n",
|
||||
this, presentChannelType, aIncomingChannelType, competingBehavior));
|
||||
|
||||
return competingBehavior;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
AudioChannelService::IsAudioChannelMutedByDefault()
|
||||
{
|
||||
@ -1007,9 +1189,12 @@ AudioChannelService::AudioChannelWindow::AppendAgent(AudioChannelAgent* aAgent,
|
||||
{
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
RequestAudioFocus(aAgent);
|
||||
AppendAgentAndIncreaseAgentsNum(aAgent);
|
||||
AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing);
|
||||
AudioAudibleChanged(aAgent, aAudible);
|
||||
if (aAudible) {
|
||||
AudioAudibleChanged(aAgent, AudibleState::eAudible);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1082,6 +1267,8 @@ AudioChannelService::AudioChannelWindow::AudioAudibleChanged(AudioChannelAgent*
|
||||
} else {
|
||||
RemoveAudibleAgentIfContained(aAgent);
|
||||
}
|
||||
|
||||
NotifyAudioCompetingChanged(aAgent, aAudible);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -89,6 +89,8 @@ public:
|
||||
|
||||
static PRLogModuleInfo* GetAudioChannelLog();
|
||||
|
||||
static bool IsEnableAudioCompeting();
|
||||
|
||||
/**
|
||||
* Any audio channel agent that starts playing should register itself to
|
||||
* this service, sharing the AudioChannel.
|
||||
@ -220,6 +222,9 @@ private:
|
||||
void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
|
||||
bool aVisible, uint64_t aChildID);
|
||||
|
||||
void RefreshAgentsAudioFocusChanged(AudioChannelAgent* aAgent,
|
||||
bool aActive);
|
||||
|
||||
class AudioChannelConfig final : public AudioPlaybackConfig
|
||||
{
|
||||
public:
|
||||
@ -236,13 +241,15 @@ private:
|
||||
{
|
||||
public:
|
||||
explicit AudioChannelWindow(uint64_t aWindowID)
|
||||
: mWindowID(aWindowID),
|
||||
mIsAudioCaptured(false)
|
||||
: 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, bool aActive);
|
||||
void AudioAudibleChanged(AudioChannelAgent* aAgent, AudibleState aAudible);
|
||||
|
||||
void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible);
|
||||
@ -256,6 +263,10 @@ private:
|
||||
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);
|
||||
@ -273,6 +284,16 @@ private:
|
||||
AudibleState aAudible);
|
||||
void NotifyChannelActive(uint64_t aWindowID, AudioChannel aChannel,
|
||||
bool aActive);
|
||||
|
||||
void RequestAudioFocus(AudioChannelAgent* aAgent);
|
||||
void NotifyAudioCompetingChanged(AudioChannelAgent* aAgent, bool aActive);
|
||||
|
||||
uint32_t GetCompetingBehavior(AudioChannelAgent* aAgent,
|
||||
int32_t aIncomingChannelType,
|
||||
bool aIncomingChannelActive) const;
|
||||
bool IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const;
|
||||
bool IsAudioCompetingInSameTab() const;
|
||||
bool IsContainingPlayingAgent(AudioChannelAgent* aAgent) const;
|
||||
};
|
||||
|
||||
AudioChannelWindow*
|
||||
|
@ -21,6 +21,10 @@ UNIFIED_SOURCES += [
|
||||
'AudioChannelService.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base/',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -923,6 +923,7 @@ pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sy
|
||||
pref("dom.presentation.enabled", true);
|
||||
pref("dom.presentation.discovery.enabled", true);
|
||||
|
||||
pref("dom.audiochannel.audioCompeting", true);
|
||||
// TODO : remove it after landing bug1242874 because now it's the only way to
|
||||
// suspend the MediaElement.
|
||||
pref("media.useAudioChannelAPI", true);
|
||||
|
@ -5404,6 +5404,8 @@ pref("dom.mozBrowserFramesEnabled", false);
|
||||
// Is support for 'color-adjust' CSS property enabled?
|
||||
pref("layout.css.color-adjust.enabled", true);
|
||||
|
||||
pref("dom.audiochannel.audioCompeting", false);
|
||||
|
||||
// Disable Node.rootNode in release builds.
|
||||
#ifdef RELEASE_BUILD
|
||||
pref("dom.node.rootNode.enabled", false);
|
||||
|
Loading…
Reference in New Issue
Block a user