Bug 1346872 - notify audible state change for webaudio and NPAPI. r=ehsan, a=lizzard
☠☠ backed out by 39c86569861c ☠ ☠
authorAlastor Wu <alwu@mozilla.com>
Fri, 31 Mar 2017 14:48:25 +0800
changeset 379394 29e4712df62fe6eefa1958cce98819424d611a79
parent 379393 39bd60385e7475126c9655dd2ecd5648e5161ce5
child 379395 39c86569861c15fa391561284f8f31a3d87cff3d
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, lizzard
bugs1346872
milestone53.0
Bug 1346872 - notify audible state change for webaudio and NPAPI. r=ehsan, a=lizzard
dom/media/webaudio/AudioDestinationNode.cpp
dom/media/webaudio/AudioDestinationNode.h
dom/plugins/base/nsNPAPIPlugin.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -328,16 +328,17 @@ AudioDestinationNode::AudioDestinationNo
                                            uint32_t aLength, float aSampleRate)
   : AudioNode(aContext, aIsOffline ? aNumberOfChannels : 2,
               ChannelCountMode::Explicit, ChannelInterpretation::Speakers)
   , mFramesToProduce(aLength)
   , mAudioChannel(AudioChannel::Normal)
   , mIsOffline(aIsOffline)
   , mAudioChannelSuspended(false)
   , mCaptured(false)
+  , mAudible(AudioChannelService::AudibleState::eAudible)
 {
   MediaStreamGraph* graph = aIsOffline ?
                             MediaStreamGraph::CreateNonRealtimeInstance(aSampleRate) :
                             MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, aChannel);
   AudioNodeEngine* engine = aIsOffline ?
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
                             static_cast<AudioNodeEngine*>(new DestinationNodeEngine(this));
@@ -380,16 +381,18 @@ AudioDestinationNode::SizeOfIncludingThi
 }
 
 void
 AudioDestinationNode::DestroyAudioChannelAgent()
 {
   if (mAudioChannelAgent && !Context()->IsOffline()) {
     mAudioChannelAgent->NotifyStoppedPlaying();
     mAudioChannelAgent = nullptr;
+    // Reset the state, and it would always be regard as audible.
+    mAudible = AudioChannelService::AudibleState::eAudible;
   }
 }
 
 void
 AudioDestinationNode::DestroyMediaStream()
 {
   DestroyAudioChannelAgent();
 
@@ -504,16 +507,25 @@ NS_IMETHODIMP
 AudioDestinationNode::WindowVolumeChanged(float aVolume, bool aMuted)
 {
   if (!mStream) {
     return NS_OK;
   }
 
   float volume = aMuted ? 0.0 : aVolume;
   mStream->SetAudioOutputVolume(&gWebAudioOutputKey, volume);
+
+  AudioChannelService::AudibleState audible = volume > 0.0 ?
+    AudioChannelService::AudibleState::eAudible :
+    AudioChannelService::AudibleState::eNotAudible;
+  if (mAudible != audible) {
+    mAudible = audible;
+    mAudioChannelAgent->NotifyStartedAudible(mAudible,
+                                             AudioChannelService::AudibleChangedReasons::eVolumeChanged);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioDestinationNode::WindowSuspendChanged(nsSuspendedTypes aSuspend)
 {
   if (!mStream) {
     return NS_OK;
@@ -527,16 +539,26 @@ AudioDestinationNode::WindowSuspendChang
   mAudioChannelSuspended = suspended;
   Context()->DispatchTrustedEvent(!suspended ?
     NS_LITERAL_STRING("mozinterruptend") :
     NS_LITERAL_STRING("mozinterruptbegin"));
 
   DisabledTrackMode disabledMode = suspended ? DisabledTrackMode::SILENCE_BLACK
                                              : DisabledTrackMode::ENABLED;
   mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, disabledMode);
+
+  AudioChannelService::AudibleState audible =
+    aSuspend == nsISuspendedTypes::NONE_SUSPENDED ?
+      AudioChannelService::AudibleState::eAudible :
+      AudioChannelService::AudibleState::eNotAudible;
+  if (mAudible != audible) {
+    mAudible = audible;
+    mAudioChannelAgent->NotifyStartedAudible(audible,
+                                             AudioChannelService::AudibleChangedReasons::ePauseStateChanged);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioDestinationNode::WindowAudioCaptureChanged(bool aCapture)
 {
   MOZ_ASSERT(mAudioChannelAgent);
 
@@ -662,22 +684,24 @@ AudioDestinationNode::InputMuted(bool aM
     if (aMuted) {
       return;
     }
     CreateAudioChannelAgent();
   }
 
   if (aMuted) {
     mAudioChannelAgent->NotifyStoppedPlaying();
+    // Reset the state, and it would always be regard as audible.
+    mAudible = AudioChannelService::AudibleState::eAudible;
     return;
   }
 
   AudioPlaybackConfig config;
   nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config,
-                                                         AudioChannelService::AudibleState::eAudible);
+                                                         mAudible);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   WindowVolumeChanged(config.mVolume, config.mMuted);
   WindowSuspendChanged(config.mSuspend);
 }
 
--- a/dom/media/webaudio/AudioDestinationNode.h
+++ b/dom/media/webaudio/AudioDestinationNode.h
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 AudioDestinationNode_h_
 #define AudioDestinationNode_h_
 
-#include "mozilla/dom/AudioChannelBinding.h"
+#include "AudioChannelService.h"
 #include "AudioNode.h"
 #include "nsIAudioChannelAgent.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
@@ -101,15 +101,16 @@ private:
   RefPtr<Promise> mOfflineRenderingPromise;
 
   // Audio Channel Type.
   AudioChannel mAudioChannel;
   bool mIsOffline;
   bool mAudioChannelSuspended;
 
   bool mCaptured;
+  AudioChannelService::AudibleState mAudible;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
 
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2225,63 +2225,29 @@ NPError
     }
 
     case NPPVpluginUsesDOMForCursorBool: {
       bool useDOMForCursor = (result != nullptr);
       return inst->SetUsesDOMForCursor(useDOMForCursor);
     }
 
     case NPPVpluginIsPlayingAudio: {
-      bool isMuted = !result;
+      const bool isPlaying = result;
 
       nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*) npp->ndata;
       MOZ_ASSERT(inst);
 
-      if (isMuted && !inst->HasAudioChannelAgent()) {
-        return NPERR_NO_ERROR;
-      }
-
-      nsCOMPtr<nsIAudioChannelAgent> agent;
-      nsresult rv = inst->GetOrCreateAudioChannelAgent(getter_AddRefs(agent));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
+      if (!isPlaying && !inst->HasAudioChannelAgent()) {
         return NPERR_NO_ERROR;
       }
 
-      MOZ_ASSERT(agent);
-
-      if (isMuted) {
-        rv = agent->NotifyStoppedPlaying();
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return NPERR_NO_ERROR;
-        }
+      if (isPlaying) {
+        inst->NotifyStartedPlaying();
       } else {
-
-        dom::AudioPlaybackConfig config;
-        rv = agent->NotifyStartedPlaying(&config,
-                                         dom::AudioChannelService::AudibleState::eAudible);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return NPERR_NO_ERROR;
-        }
-
-        rv = inst->WindowVolumeChanged(config.mVolume, config.mMuted);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return NPERR_NO_ERROR;
-        }
-
-        // Since we only support for muting now, the implementation of suspend
-        // is equal to muting. Therefore, if we have already muted the plugin,
-        // then we don't need to call WindowSuspendChanged() again.
-        if (config.mMuted) {
-          return NPERR_NO_ERROR;
-        }
-
-        rv = inst->WindowSuspendChanged(config.mSuspend);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return NPERR_NO_ERROR;
-        }
+        inst->NotifyStoppedPlaying();
       }
 
       return NPERR_NO_ERROR;
     }
 
 #ifndef MOZ_WIDGET_ANDROID
     // On android, their 'drawing model' uses the same constant!
     case NPPVpluginDrawingModel: {
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1757,41 +1757,87 @@ nsNPAPIPluginInstance::GetRunID(uint32_t
   if (!library) {
     return NS_ERROR_FAILURE;
   }
 
   return library->GetRunID(aRunID);
 }
 
 nsresult
-nsNPAPIPluginInstance::GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent)
+nsNPAPIPluginInstance::CreateAudioChannelAgentIfNeeded()
 {
-  if (!mAudioChannelAgent) {
-    nsresult rv;
-    mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
-    if (NS_WARN_IF(!mAudioChannelAgent)) {
-      return NS_ERROR_FAILURE;
-    }
+  if (mAudioChannelAgent) {
+    return NS_OK;
+  }
+
+  nsresult rv;
+  mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
+  if (NS_WARN_IF(!mAudioChannelAgent)) {
+    return NS_ERROR_FAILURE;
+  }
 
-    nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow();
-    if (NS_WARN_IF(!window)) {
-      return NS_ERROR_FAILURE;
-    }
+  nsCOMPtr<nsPIDOMWindowOuter> window = GetDOMWindow();
+  if (NS_WARN_IF(!window)) {
+    return NS_ERROR_FAILURE;
+  }
 
-    rv = mAudioChannelAgent->Init(window->GetCurrentInnerWindow(),
-                                 (int32_t)AudioChannelService::GetDefaultAudioChannel(),
-                                 this);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+  rv = mAudioChannelAgent->Init(window->GetCurrentInnerWindow(),
+                               (int32_t)AudioChannelService::GetDefaultAudioChannel(),
+                               this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+void
+nsNPAPIPluginInstance::NotifyStartedPlaying()
+{
+  nsresult rv = CreateAudioChannelAgentIfNeeded();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
   }
 
-  nsCOMPtr<nsIAudioChannelAgent> agent = mAudioChannelAgent;
-  agent.forget(aAgent);
-  return NS_OK;
+  MOZ_ASSERT(mAudioChannelAgent);
+  dom::AudioPlaybackConfig config;
+  rv = mAudioChannelAgent->NotifyStartedPlaying(&config,
+                                                dom::AudioChannelService::AudibleState::eAudible);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = WindowVolumeChanged(config.mVolume, config.mMuted);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  // Since we only support muting for now, the implementation of suspend
+  // is equal to muting. Therefore, if we have already muted the plugin,
+  // then we don't need to call WindowSuspendChanged() again.
+  if (config.mMuted) {
+    return;
+  }
+
+  rv = WindowSuspendChanged(config.mSuspend);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+}
+
+void
+nsNPAPIPluginInstance::NotifyStoppedPlaying()
+{
+  MOZ_ASSERT(mAudioChannelAgent);
+
+  // Reset the attribute.
+  mMuted = false;
+  nsresult rv = mAudioChannelAgent->NotifyStoppedPlaying();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
 }
 
 NS_IMETHODIMP
 nsNPAPIPluginInstance::WindowVolumeChanged(float aVolume, bool aMuted)
 {
   // We just support mute/unmute
   nsresult rv = SetMuted(aMuted);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetMuted failed");
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -131,17 +131,18 @@ public:
   void SetOwner(nsPluginInstanceOwner *aOwner);
   void DidComposite();
 
   bool HasAudioChannelAgent() const
   {
     return !!mAudioChannelAgent;
   }
 
-  nsresult GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent);
+  void NotifyStartedPlaying();
+  void NotifyStoppedPlaying();
 
   nsresult SetMuted(bool aIsMuted);
 
   nsNPAPIPlugin* GetPlugin();
 
   nsresult GetNPP(NPP * aNPP);
 
   NPError SetWindowless(bool aWindowless);
@@ -336,16 +337,18 @@ protected:
   nsresult GetTagType(nsPluginTagType *result);
   nsresult GetMode(int32_t *result);
 
   // check if this is a Java applet and affected by bug 750480
   void CheckJavaC2PJSObjectQuirk(uint16_t paramCount,
                                  const char* const* names,
                                  const char* const* values);
 
+  nsresult CreateAudioChannelAgentIfNeeded();
+
   // The structure used to communicate between the plugin instance and
   // the browser.
   NPP_t mNPP;
 
   NPDrawingModel mDrawingModel;
 
 #ifdef MOZ_WIDGET_ANDROID
   uint32_t mANPDrawingModel;