--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -218,21 +218,26 @@ NS_IMETHODIMP AudioChannelAgent::NotifyS
if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
service == nullptr || mIsRegToService) {
return NS_ERROR_FAILURE;
}
service->RegisterAudioChannelAgent(this,
static_cast<AudioChannel>(mAudioChannelType));
- service->GetState(mWindow, mAudioChannelType, aVolume, aMuted);
+ AudioPlaybackConfig config = service->GetMediaConfig(mWindow,
+ mAudioChannelType);
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
- ("AudioChannelAgent, NotifyStartedPlaying, this = %p, mute = %d, "
- "volume = %f\n", this, *aMuted, *aVolume));
+ ("AudioChannelAgent, NotifyStartedPlaying, this = %p, "
+ "mute = %d, volume = %f, suspend = %d\n", this,
+ config.mMuted, config.mVolume, config.mSuspend));
+
+ *aVolume = config.mVolume;
+ *aMuted = config.mMuted;
mIsRegToService = true;
return NS_OK;
}
NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying()
{
if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
@@ -265,29 +270,59 @@ AudioChannelAgent::GetCallback()
void
AudioChannelAgent::WindowVolumeChanged()
{
nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
if (!callback) {
return;
}
- float volume = 1.0;
- bool muted = false;
+ AudioPlaybackConfig config = GetMediaConfig();
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, "
+ "volume = %f\n", this, config.mMuted, config.mVolume));
+
+ callback->WindowVolumeChanged(config.mVolume, config.mMuted);
+}
- RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
- if (service) {
- service->GetState(mWindow, mAudioChannelType, &volume, &muted);
+void
+AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend)
+{
+ nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
+ if (!callback) {
+ return;
+ }
+
+ if (!IsDisposableSuspend(aSuspend)) {
+ aSuspend = GetMediaConfig().mSuspend;
}
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
- ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, "
- "volume = %f\n", this, muted, volume));
+ ("AudioChannelAgent, WindowSuspendChanged, this = %p, "
+ "suspended = %d\n", this, aSuspend));
+
+ callback->WindowSuspendChanged(aSuspend);
+}
- callback->WindowVolumeChanged(volume, muted);
+AudioPlaybackConfig
+AudioChannelAgent::GetMediaConfig()
+{
+ RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+ AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED);
+ if (service) {
+ config = service->GetMediaConfig(mWindow, mAudioChannelType);
+ }
+ return config;
+}
+
+bool
+AudioChannelAgent::IsDisposableSuspend(nsSuspendedTypes aSuspend) const
+{
+ return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
+ aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
}
uint64_t
AudioChannelAgent::WindowID() const
{
return mWindow ? mWindow->WindowID() : 0;
}
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -18,41 +18,47 @@
{0x90, 0x4e, 0x10, 0xbf, 0x48, 0xd6, 0x4b, 0xd4}}
class nsPIDOMWindowInner;
class nsPIDOMWindowOuter;
namespace mozilla {
namespace dom {
+class AudioPlaybackConfig;
+
/* Header file */
class AudioChannelAgent : public nsIAudioChannelAgent
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIAUDIOCHANNELAGENT
NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
AudioChannelAgent();
void WindowVolumeChanged();
+ void WindowSuspendChanged(nsSuspendedTypes aSuspend);
void WindowAudioCaptureChanged(uint64_t aInnerWindowID, bool aCapture);
nsPIDOMWindowOuter* Window() const
{
return mWindow;
}
uint64_t WindowID() const;
uint64_t InnerWindowID() const;
private:
virtual ~AudioChannelAgent();
+ AudioPlaybackConfig GetMediaConfig();
+ bool IsDisposableSuspend(nsSuspendedTypes aSuspend) const;
+
// Returns mCallback if that's non-null, or otherwise tries to get an
// nsIAudioChannelAgentCallback out of mWeakCallback.
already_AddRefed<nsIAudioChannelAgentCallback> GetCallback();
nsresult InitInternal(nsPIDOMWindowInner* aWindow, int32_t aAudioAgentType,
nsIAudioChannelAgentCallback* aCallback,
bool aUseWeakRef);
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -348,59 +348,62 @@ AudioChannelService::RegisterTabParent(T
void
AudioChannelService::UnregisterTabParent(TabParent* aTabParent)
{
MOZ_ASSERT(aTabParent);
mTabParents.RemoveElement(aTabParent);
}
-void
-AudioChannelService::GetState(nsPIDOMWindowOuter* aWindow, uint32_t aAudioChannel,
- float* aVolume, bool* aMuted)
+AudioPlaybackConfig
+AudioChannelService::GetMediaConfig(nsPIDOMWindowOuter* aWindow,
+ uint32_t aAudioChannel) const
{
MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow());
- MOZ_ASSERT(aVolume && aMuted);
MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS);
+ AudioPlaybackConfig config(1.0, false,
+ nsISuspendedTypes::NONE_SUSPENDED);
if (!aWindow || !aWindow->IsOuterWindow()) {
- *aVolume = 0.0;
- *aMuted = true;
- return;
+ config.SetConfig(0.0, true,
+ nsISuspendedTypes::SUSPENDED_BLOCK);
+ return config;
}
- *aVolume = 1.0;
- *aMuted = false;
-
AudioChannelWindow* winData = nullptr;
nsCOMPtr<nsPIDOMWindowOuter> window = aWindow;
// The volume must be calculated based on the window hierarchy. Here we go up
// to the top window and we calculate the volume and the muted flag.
do {
winData = GetWindowData(window->WindowID());
if (winData) {
- *aVolume *= winData->mChannels[aAudioChannel].mVolume;
- *aMuted = *aMuted || winData->mChannels[aAudioChannel].mMuted;
+ config.mVolume *= winData->mChannels[aAudioChannel].mVolume;
+ config.mMuted = config.mMuted || winData->mChannels[aAudioChannel].mMuted;
}
- *aVolume *= window->GetAudioVolume();
- // TODO : distiguish between suspend and mute, it would be done in bug1242874.
- *aMuted = *aMuted || window->GetMediaSuspended() || window->GetAudioMuted();
+ 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;
nsCOMPtr<nsPIDOMWindowOuter> win = window->GetScriptableParentOrNull();
if (!win) {
break;
}
window = do_QueryInterface(win);
// If there is no parent, or we are the toplevel we don't continue.
} while (window && window != aWindow);
+
+ return config;
}
bool
AudioChannelService::TelephonyChannelIsActive()
{
nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator windowsIter(mWindows);
while (windowsIter.HasMore()) {
AudioChannelWindow* next = windowsIter.GetNext();
@@ -595,17 +598,18 @@ AudioChannelService::RefreshAgentsVolume
winData->mChannels[(uint32_t)aAudioChannel].mVolume,
winData->mChannels[(uint32_t)aAudioChannel].mMuted);
}
RefreshAgentsVolume(aWindow);
}
void
-AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow)
+AudioChannelService::RefreshAgents(nsPIDOMWindowOuter* aWindow,
+ mozilla::function<void(AudioChannelAgent*)> aFunc)
{
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
if (!topWindow) {
return;
}
@@ -613,21 +617,38 @@ AudioChannelService::RefreshAgentsVolume
AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
if (!winData) {
return;
}
nsTObserverArray<AudioChannelAgent*>::ForwardIterator
iter(winData->mAgents);
while (iter.HasMore()) {
- iter.GetNext()->WindowVolumeChanged();
+ aFunc(iter.GetNext());
}
}
void
+AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow)
+{
+ RefreshAgents(aWindow, [] (AudioChannelAgent* agent) {
+ agent->WindowVolumeChanged();
+ });
+}
+
+void
+AudioChannelService::RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow,
+ nsSuspendedTypes aSuspend)
+{
+ RefreshAgents(aWindow, [aSuspend] (AudioChannelAgent* agent) {
+ agent->WindowSuspendChanged(aSuspend);
+ });
+}
+
+void
AudioChannelService::SetWindowAudioCaptured(nsPIDOMWindowOuter* aWindow,
uint64_t aInnerWindowID,
bool aCapture)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -11,32 +11,60 @@
#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
@@ -67,20 +95,20 @@ public:
/**
* 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.
+ * playing/muted/suspended.
*/
- void GetState(nsPIDOMWindowOuter* aWindow, uint32_t aChannel,
- float* aVolume, bool* aMuted);
+ AudioPlaybackConfig GetMediaConfig(nsPIDOMWindowOuter* aWindow,
+ uint32_t aAudioChannel) const;
/* Methods for the BrowserElementAudioChannel */
float GetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
void SetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel,
float aVolume);
bool GetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
@@ -109,16 +137,18 @@ public:
* 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.
@@ -150,42 +180,43 @@ public:
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);
- struct AudioChannelConfig final
+ class AudioChannelConfig final : public AudioPlaybackConfig
{
+ public:
AudioChannelConfig()
- : mVolume(1.0)
- , mMuted(IsAudioChannelMutedByDefault())
+ : AudioPlaybackConfig(1.0, IsAudioChannelMutedByDefault(),
+ nsISuspendedTypes::NONE_SUSPENDED)
, mNumberOfAgents(0)
{}
- float mVolume;
- bool mMuted;
-
uint32_t mNumberOfAgents;
};
struct AudioChannelWindow final
{
explicit AudioChannelWindow(uint64_t aWindowID)
: mWindowID(aWindowID),
mIsAudioCaptured(false)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3794,17 +3794,17 @@ nsPIDOMWindowOuter::RefreshMediaElements
}
}
void
nsPIDOMWindowOuter::RefreshMediaElementsSuspend(SuspendTypes aSuspend)
{
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
if (service) {
- // TODO : Impelement in next patch.
+ service->RefreshAgentsSuspend(GetOuterWindow(), aSuspend);
}
}
bool
nsPIDOMWindowOuter::IsDisposableSuspend(SuspendTypes aSuspend) const
{
return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -458,25 +458,26 @@ FMRadio::EnableAudioChannelAgent()
WindowVolumeChanged(volume, muted);
mAudioChannelAgentEnabled = true;
}
NS_IMETHODIMP
FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
{
+ // TODO : Not support to change volume now, so we just close it.
IFMRadioService::Singleton()->EnableAudio(!aMuted);
- // TODO: what about the volume?
return NS_OK;
}
NS_IMETHODIMP
FMRadio::WindowSuspendChanged(nsSuspendedTypes aSuspend)
{
- // TODO : implementation.
+ bool enable = (aSuspend == nsISuspendedTypes::NONE_SUSPENDED);
+ IFMRadioService::Singleton()->EnableAudio(enable);
return NS_OK;
}
NS_IMETHODIMP
FMRadio::WindowAudioCaptureChanged(bool aCapture)
{
return NS_OK;
}
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1157,21 +1157,16 @@ void HTMLMediaElement::ResumeLoad(Preloa
}
}
static bool IsAutoplayEnabled()
{
return Preferences::GetBool("media.autoplay.enabled");
}
-static bool UseAudioChannelAPI()
-{
- return Preferences::GetBool("media.useAudioChannelAPI");
-}
-
void HTMLMediaElement::UpdatePreloadAction()
{
PreloadAction nextAction = PRELOAD_UNDEFINED;
// If autoplay is set, or we're playing, we should always preload data,
// as we'll need it to play.
if ((IsAutoplayEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
!mPaused)
{
@@ -2239,16 +2234,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
mPlayed(new TimeRanges(ToSupports(OwnerDoc()))),
mCurrentPlayRangeStart(-1.0),
mBegun(false),
mLoadedDataFired(false),
mAutoplaying(true),
mAutoplayEnabled(true),
mPaused(true),
mMuted(0),
+ mAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED),
mStatsShowing(false),
mAllowCasting(false),
mIsCasting(false),
mAudioCaptured(false),
mAudioCapturedByWindow(false),
mPlayingBeforeSeek(false),
mPlayingThroughTheAudioChannelBeforeSeek(false),
mPausedForInactiveDocumentOrChannel(false),
@@ -2390,30 +2386,17 @@ HTMLMediaElement::Play(ErrorResult& aRv)
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
nsresult
HTMLMediaElement::PlayInternal(bool aCallerIsChrome)
{
- // Prevent media element from being auto-started by a script when
- // media.autoplay.enabled=false
- if (!mHasUserInteraction
- && !IsAutoplayEnabled()
- && !EventStateManager::IsHandlingUserInput()
- && !aCallerIsChrome) {
- LOG(LogLevel::Debug, ("%p Blocked attempt to autoplay media.", this));
-#if defined(MOZ_WIDGET_ANDROID)
- nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
- static_cast<nsIContent*>(this),
- NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
- false,
- false);
-#endif
+ if (!IsAllowedToPlay()) {
return NS_OK;
}
// Play was not blocked so assume user interacted with the element.
mHasUserInteraction = true;
StopSuspendingAfterFirstFrame();
SetPlayedOrSeeked(true);
@@ -2468,16 +2451,18 @@ HTMLMediaElement::PlayInternal(bool aCal
case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
break;
}
}
mPaused = false;
mAutoplaying = false;
+ SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+
// We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
// and our preload status.
AddRemoveSelfReference();
UpdatePreloadAction();
UpdateSrcMediaStreamPlaying();
UpdateAudioChannelPlayingState();
return NS_OK;
@@ -4533,22 +4518,17 @@ bool HTMLMediaElement::IsBeingDestroyed(
docShell->IsBeingDestroyed(&isBeingDestroyed);
}
return isBeingDestroyed;
}
void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
{
bool pauseElement = NotifyOwnerDocumentActivityChangedInternal();
- if (pauseElement && mAudioChannelAgent &&
- // On B2G, NotifyOwnerDocumentActivityChangedInternal may return true for
- // two reasons: the document no longer being active, or the element being
- // paused by the audio channel. However we are only interested in the
- // first case here, so we need to filter out the second case.
- (!UseAudioChannelAPI() || !ComputedMuted())) {
+ if (pauseElement && mAudioChannelAgent) {
// If the element is being paused since we are navigating away from the
// document, notify the audio channel agent.
// Be careful to ignore this event during a docshell frame swap.
auto docShell = static_cast<nsDocShell*>(OwnerDoc()->GetDocShell());
if (!docShell) {
return;
}
if (!docShell->InFrameSwap()) {
@@ -4562,24 +4542,16 @@ HTMLMediaElement::NotifyOwnerDocumentAct
{
nsIDocument* ownerDoc = OwnerDoc();
if (mDecoder && !IsBeingDestroyed()) {
mDecoder->SetElementVisibility(!ownerDoc->Hidden());
mDecoder->NotifyOwnerActivityChanged();
}
bool pauseElement = !IsActive();
- // Only pause the element when we start playing. If we pause without playing
- // audio, the resource loading would be affected unexpectedly. For example,
- // the media element is muted by default, but we don't want this behavior
- // interrupting the loading process.
- if (UseAudioChannelAPI() && mAudioChannelAgent) {
- pauseElement |= ComputedMuted();
- }
-
SuspendOrResumeElement(pauseElement, !IsActive());
if (!mPausedForInactiveDocumentOrChannel &&
mPlayBlockedBecauseHidden &&
!OwnerDoc()->Hidden()) {
LOG(LogLevel::Debug, ("%p Resuming playback now that owner doc is visble.", this));
mPlayBlockedBecauseHidden = false;
Play();
@@ -4983,43 +4955,16 @@ NS_IMETHODIMP HTMLMediaElement::SetMozPr
}
ImageContainer* HTMLMediaElement::GetImageContainer()
{
VideoFrameContainer* container = GetVideoFrameContainer();
return container ? container->GetImageContainer() : nullptr;
}
-nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted)
-{
- if (mAudioChannelVolume != aVolume) {
- mAudioChannelVolume = aVolume;
- SetVolumeInternal();
- }
-
- // We have to mute this channel.
- if (aMuted && !ComputedMuted()) {
- SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
- if (UseAudioChannelAPI()) {
- DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
- }
- } else if (!aMuted && ComputedMuted()) {
- SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
- if (UseAudioChannelAPI()) {
- DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
- }
- }
-
- if (UseAudioChannelAPI()) {
- SuspendOrResumeElement(ComputedMuted(), false);
- }
-
- return NS_OK;
-}
-
bool
HTMLMediaElement::MaybeCreateAudioChannelAgent()
{
if (!mAudioChannelAgent) {
nsresult rv;
mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
@@ -5030,16 +4975,21 @@ HTMLMediaElement::MaybeCreateAudioChanne
this);
}
return true;
}
bool
HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
{
+ // It might be resumed from remote, we should keep the audio channel agent.
+ if (IsSuspendedByAudioChannel()) {
+ return true;
+ }
+
// Are we paused or muted
if (mPaused || Muted()) {
return false;
}
// If we have an error, we are not playing.
if (mError) {
return false;
@@ -5116,36 +5066,186 @@ HTMLMediaElement::NotifyAudioChannelAgen
mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
WindowVolumeChanged(volume, muted);
} else {
mAudioChannelAgent->NotifyStoppedPlaying();
mAudioChannelAgent = nullptr;
}
}
-NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
-{
- MOZ_ASSERT(NS_IsMainThread());
-
- UpdateChannelMuteState(aVolume, aMuted);
-
- if (UseAudioChannelAPI()) {
- mPaused.SetCanPlay(!aMuted);
+NS_IMETHODIMP
+HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
+{
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("HTMLMediaElement, WindowVolumeChanged, this = %p, "
+ "aVolume = %f, aMuted = %d\n", this, aVolume, aMuted));
+
+ if (mAudioChannelVolume != aVolume) {
+ mAudioChannelVolume = aVolume;
+ SetVolumeInternal();
+ }
+
+ if (aMuted && !ComputedMuted()) {
+ SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
+ } else if (!aMuted && ComputedMuted()) {
+ SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
}
return NS_OK;
}
NS_IMETHODIMP
-HTMLMediaElement::WindowSuspendChanged(nsSuspendedTypes aSuspend)
-{
- // TODO : implementation.
+HTMLMediaElement::WindowSuspendChanged(SuspendTypes aSuspend)
+{
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("HTMLMediaElement, WindowSuspendChanged, this = %p, "
+ "aSuspend = %d\n", this, aSuspend));
+
+ switch (aSuspend) {
+ case nsISuspendedTypes::NONE_SUSPENDED:
+ ResumeFromAudioChannel();
+ break;
+ case nsISuspendedTypes::SUSPENDED_PAUSE:
+ case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
+ PauseByAudioChannel(aSuspend);
+ break;
+ case nsISuspendedTypes::SUSPENDED_BLOCK:
+ BlockByAudioChannel();
+ break;
+ case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE:
+ Pause();
+ break;
+ default:
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("HTMLMediaElement, WindowSuspendChanged, this = %p, "
+ "Error : unknown suspended type!\n", this));
+ }
+
return NS_OK;
}
+void
+HTMLMediaElement::ResumeFromAudioChannel()
+{
+ if (!IsSuspendedByAudioChannel()) {
+ return;
+ }
+
+ switch (mAudioChannelSuspended) {
+ case nsISuspendedTypes::SUSPENDED_PAUSE:
+ case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
+ ResumeFromAudioChannelPaused(mAudioChannelSuspended);
+ break;
+ case nsISuspendedTypes::SUSPENDED_BLOCK:
+ ResumeFromAudioChannelBlocked();
+ break;
+ default:
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("HTMLMediaElement, ResumeFromAudioChannel, this = %p, "
+ "Error : resume without suspended!\n", this));
+ }
+}
+
+void
+HTMLMediaElement::ResumeFromAudioChannelPaused(SuspendTypes aSuspend)
+{
+ MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+ mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE);
+
+ SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+ nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
+}
+
+void
+HTMLMediaElement::ResumeFromAudioChannelBlocked()
+{
+ MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
+
+ SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+ mPaused.SetCanPlay(true);
+ SuspendOrResumeElement(false /* resume */, false);
+}
+
+void
+HTMLMediaElement::PauseByAudioChannel(SuspendTypes aSuspend)
+{
+ if (IsSuspendedByAudioChannel()) {
+ return;
+ }
+
+ SetAudioChannelSuspended(aSuspend);
+ Pause();
+ DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
+}
+
+void
+HTMLMediaElement::BlockByAudioChannel()
+{
+ if (IsSuspendedByAudioChannel()) {
+ return;
+ }
+
+ SetAudioChannelSuspended(nsISuspendedTypes::SUSPENDED_BLOCK);
+ SuspendOrResumeElement(true /* suspend */, false);
+ mPaused.SetCanPlay(false);
+}
+
+void
+HTMLMediaElement::SetAudioChannelSuspended(SuspendTypes aSuspend)
+{
+ if (mAudioChannelSuspended == aSuspend) {
+ return;
+ }
+
+ mAudioChannelSuspended = aSuspend;
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("HTMLMediaElement, SetAudioChannelSuspended, this = %p, "
+ "aSuspend = %d\n", this, aSuspend));
+}
+
+bool
+HTMLMediaElement::IsSuspendedByAudioChannel() const
+{
+ return (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+ mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
+ mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
+}
+
+bool
+HTMLMediaElement::IsAllowedToPlay()
+{
+ // Prevent media element from being auto-started by a script when
+ // media.autoplay.enabled=false
+ if (!mHasUserInteraction &&
+ !IsAutoplayEnabled() &&
+ !EventStateManager::IsHandlingUserInput() &&
+ !nsContentUtils::IsCallerChrome()) {
+#if defined(MOZ_WIDGET_ANDROID)
+ nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
+ static_cast<nsIContent*>(this),
+ NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
+ false,
+ false);
+#endif
+ return false;
+ }
+
+ // The MediaElement can't start playback until it's resumed by audio channel.
+ if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+ mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) {
+ return false;
+ }
+
+ return true;
+}
+
#ifdef MOZ_EME
MediaKeys*
HTMLMediaElement::GetMediaKeys() const
{
return mMediaKeys;
}
bool
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -32,16 +32,17 @@
#include "mozilla/dom/HTMLMediaElementBinding.h"
// Define to output information on decoding and painting framerate
/* #define DEBUG_FRAME_RATE 1 */
typedef uint16_t nsMediaNetworkState;
typedef uint16_t nsMediaReadyState;
+typedef uint32_t SuspendTypes;
namespace mozilla {
class DecoderDoctorDiagnostics;
class DOMMediaStream;
class ErrorResult;
class MediaResource;
class MediaDecoder;
class VideoFrameContainer;
@@ -1109,19 +1110,16 @@ protected:
#ifdef MOZ_EME
void ReportEMETelemetry();
#endif
void ReportMSETelemetry();
// Check the permissions for audiochannel.
bool CheckAudioChannelPermissions(const nsAString& aType);
- // This method does the check for muting/nmuting the audio channel.
- nsresult UpdateChannelMuteState(float aVolume, bool aMuted);
-
// Seeks to aTime seconds. aSeekType can be Exact to seek to exactly the
// seek target, or PrevSyncPoint if a quicker but less precise seek is
// desired, and we'll seek to the sync point (keyframe and/or start of the
// next block of audio samples) preceeding seek target.
void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
// A method to check if we are playing through the AudioChannel.
bool IsPlayingThroughTheAudioChannel() const;
@@ -1146,16 +1144,48 @@ protected:
// Notifies the audio channel agent when the element starts or stops playing.
void NotifyAudioChannelAgent(bool aPlaying);
// Creates the audio channel agent if needed. Returns true if the audio
// channel agent is ready to be used.
bool MaybeCreateAudioChannelAgent();
+ /**
+ * We have different kinds of suspended cases,
+ * - SUSPENDED_PAUSE
+ * It's used when we temporary lost platform audio focus. MediaElement can
+ * only be resumed when we gain the audio focus again.
+ *
+ * - SUSPENDED_PAUSE_DISPOSABLE
+ * It's used when user press the pause botton on the remote media-control.
+ * MediaElement can be resumed by reomte media-control or via play().
+ *
+ * - SUSPENDED_BLOCK
+ * It's used to reduce the power comsuption, we won't play the auto-play
+ * audio/video in the page we have never visited before. MediaElement would
+ * be resumed when the page is active. See bug647429 for more details.
+ *
+ * - SUSPENDED_STOP_DISPOSABLE
+ * When we permanently lost platform audio focus, we shuold stop playing
+ * and stop the audio channel agent. MediaElement can only be restarted by
+ * play().
+ */
+ void PauseByAudioChannel(SuspendTypes aSuspend);
+ void BlockByAudioChannel();
+
+ void ResumeFromAudioChannel();
+ void ResumeFromAudioChannelPaused(SuspendTypes aSuspend);
+ void ResumeFromAudioChannelBlocked();
+
+ bool IsSuspendedByAudioChannel() const;
+ void SetAudioChannelSuspended(SuspendTypes aSuspend);
+
+ bool IsAllowedToPlay();
+
class nsAsyncEventRunner;
using nsGenericHTMLElement::DispatchEvent;
// For nsAsyncEventRunner.
nsresult DispatchEvent(const nsAString& aName);
// The current decoder. Load() has been called on this decoder.
// At most one of mDecoder and mSrcStream can be non-null.
RefPtr<MediaDecoder> mDecoder;
@@ -1365,16 +1395,17 @@ protected:
enum MutedReasons {
MUTED_BY_CONTENT = 0x01,
MUTED_BY_INVALID_PLAYBACK_RATE = 0x02,
MUTED_BY_AUDIO_CHANNEL = 0x04,
MUTED_BY_AUDIO_TRACK = 0x08
};
uint32_t mMuted;
+ SuspendTypes mAudioChannelSuspended;
// True if the media statistics are currently being shown by the builtin
// video controls
bool mStatsShowing;
// The following two fields are here for the private storage of the builtin
// video controls, and control 'casting' of the video to external devices
// (TVs, projectors etc.)
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -300,21 +300,16 @@ public:
}
private:
float mVolume;
bool mLastInputMuted;
bool mSuspended;
};
-static bool UseAudioChannelAPI()
-{
- return Preferences::GetBool("media.useAudioChannelAPI");
-}
-
NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
mAudioChannelAgent,
mOfflineRenderingPromise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
@@ -326,17 +321,17 @@ AudioDestinationNode::AudioDestinationNo
AudioChannel aChannel,
uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate)
: AudioNode(aContext, aIsOffline ? aNumberOfChannels : 2,
ChannelCountMode::Explicit, ChannelInterpretation::Speakers)
, mFramesToProduce(aLength)
, mAudioChannel(AudioChannel::Normal)
, mIsOffline(aIsOffline)
- , mAudioChannelAgentPlaying(false)
+ , mAudioChannelSuspended(false)
, mCaptured(false)
{
MediaStreamGraph* graph = aIsOffline ?
MediaStreamGraph::CreateNonRealtimeInstance(aSampleRate) :
MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, aChannel);
AudioNodeEngine* engine = aIsOffline ?
new OfflineDestinationNodeEngine(this, aNumberOfChannels,
aLength, aSampleRate) :
@@ -496,48 +491,46 @@ AudioDestinationNode::WrapObject(JSConte
void
AudioDestinationNode::StartRendering(Promise* aPromise)
{
mOfflineRenderingPromise = aPromise;
mOfflineRenderingRef.Take(this);
mStream->Graph()->StartNonRealtimeProcessing(mFramesToProduce);
}
-void
-AudioDestinationNode::SetCanPlay(float aVolume, bool aMuted)
-{
- if (!mStream) {
- return;
- }
-
- mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !aMuted);
- mStream->SetAudioOutputVolume(&gWebAudioOutputKey, aVolume);
-}
-
NS_IMETHODIMP
AudioDestinationNode::WindowVolumeChanged(float aVolume, bool aMuted)
{
- if (aMuted != mAudioChannelAgentPlaying) {
- mAudioChannelAgentPlaying = aMuted;
-
- if (UseAudioChannelAPI()) {
- Context()->DispatchTrustedEvent(
- !aMuted ? NS_LITERAL_STRING("mozinterruptend")
- : NS_LITERAL_STRING("mozinterruptbegin"));
- }
+ if (!mStream) {
+ return NS_OK;
}
- SetCanPlay(aVolume, aMuted);
+ float volume = aMuted ? 0.0 : aVolume;
+ mStream->SetAudioOutputVolume(&gWebAudioOutputKey, volume);
return NS_OK;
}
NS_IMETHODIMP
AudioDestinationNode::WindowSuspendChanged(nsSuspendedTypes aSuspend)
{
- // TODO : implementation.
+ if (!mStream) {
+ return NS_OK;
+ }
+
+ bool suspended = (aSuspend != nsISuspendedTypes::NONE_SUSPENDED);
+ if (mAudioChannelSuspended == suspended) {
+ return NS_OK;
+ }
+
+ mAudioChannelSuspended = suspended;
+ Context()->DispatchTrustedEvent(!suspended ?
+ NS_LITERAL_STRING("mozinterruptend") :
+ NS_LITERAL_STRING("mozinterruptbegin"));
+
+ mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !suspended);
return NS_OK;
}
NS_IMETHODIMP
AudioDestinationNode::WindowAudioCaptureChanged(bool aCapture)
{
MOZ_ASSERT(mAudioChannelAgent);
--- a/dom/media/webaudio/AudioDestinationNode.h
+++ b/dom/media/webaudio/AudioDestinationNode.h
@@ -87,30 +87,28 @@ public:
protected:
virtual ~AudioDestinationNode();
private:
void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
bool CheckAudioChannelPermissions(AudioChannel aValue);
- void SetCanPlay(float aVolume, bool aMuted);
-
SelfReference<AudioDestinationNode> mOfflineRenderingRef;
uint32_t mFramesToProduce;
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
RefPtr<MediaInputPort> mCaptureStreamPort;
RefPtr<Promise> mOfflineRenderingPromise;
// Audio Channel Type.
AudioChannel mAudioChannel;
bool mIsOffline;
- bool mAudioChannelAgentPlaying;
+ bool mAudioChannelSuspended;
bool mCaptured;
};
} // namespace dom
} // namespace mozilla
#endif
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -731,17 +731,21 @@ nsSpeechTask::WindowVolumeChanged(float
{
SetAudioOutputVolume(aMuted ? 0.0 : mVolume * aVolume);
return NS_OK;
}
NS_IMETHODIMP
nsSpeechTask::WindowSuspendChanged(nsSuspendedTypes aSuspend)
{
- // TODO : implementation.
+ if (aSuspend == nsISuspendedTypes::NONE_SUSPENDED) {
+ Resume();
+ } else {
+ Pause();
+ }
return NS_OK;
}
NS_IMETHODIMP
nsSpeechTask::WindowAudioCaptureChanged(bool aCapture)
{
// This is not supported yet.
return NS_OK;
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -658,22 +658,21 @@ nsSynthVoiceRegistry::SpeakUtterance(Spe
aUtterance.mVoice->GetVoiceURI(uri);
}
// Get current audio volume to apply speech call
float volume = aUtterance.Volume();
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
if (service) {
if (nsCOMPtr<nsPIDOMWindowInner> topWindow = aUtterance.GetOwner()) {
- float audioVolume = 1.0f;
- bool muted = false;
- service->GetState(topWindow->GetOuterWindow(),
- static_cast<uint32_t>(AudioChannelService::GetDefaultAudioChannel()),
- &audioVolume, &muted);
- volume = muted ? 0.0f : audioVolume * volume;
+ // TODO : use audio channel agent, open new bug to fix it.
+ uint32_t channel = static_cast<uint32_t>(AudioChannelService::GetDefaultAudioChannel());
+ AudioPlaybackConfig config = service->GetMediaConfig(topWindow->GetOuterWindow(),
+ channel);
+ volume = config.mMuted ? 0.0f : config.mVolume * volume;
}
}
RefPtr<nsSpeechTask> task;
if (XRE_IsContentProcess()) {
task = new SpeechTaskChild(&aUtterance);
SpeechSynthesisRequestChild* actor =
new SpeechSynthesisRequestChild(static_cast<SpeechTaskChild*>(task.get()));
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1846,17 +1846,19 @@ nsNPAPIPluginInstance::WindowVolumeChang
nsresult rv = SetMuted(aMuted);
NS_WARN_IF(NS_FAILED(rv));
return rv;
}
NS_IMETHODIMP
nsNPAPIPluginInstance::WindowSuspendChanged(nsSuspendedTypes aSuspend)
{
- // TODO : implementation.
+ // It doesn't support suspended, so we just do something like mute/unmute.
+ WindowVolumeChanged(1.0, /* useless */
+ aSuspend != nsISuspendedTypes::NONE_SUSPENDED);
return NS_OK;
}
NS_IMETHODIMP
nsNPAPIPluginInstance::WindowAudioCaptureChanged(bool aCapture)
{
return NS_OK;
}