Bug 1496496 - part4 : avoid to notify audible state changed frequently. r=padenot
authoralwu <alwu@mozilla.com>
Mon, 08 Oct 2018 18:52:01 +0000
changeset 499245 967412e016eced5e128f398db7725796ed67fcb3
parent 499244 ae372bd85df496b40a2be6950b5879f80fbb11ad
child 499246 502e176fd34a4b81683e20a13c9905a8d2d56095
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1496496
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1496496 - part4 : avoid to notify audible state changed frequently. r=padenot We don't want to notify state changed frequently if the input stream is consist of interleaving audible and inaudible blocks. This situation is really common, especially when user is using OscillatorNode to produce sound. Sending unnessary runnable frequently would cause performance debasing. If the stream contains 10 interleaving samples and 5 of them are audible, others are inaudible, user would tend to feel the stream is audible. Therefore, we have the loose checking when stream is changing from inaudible to audible, but have strict checking when streaming is changing from audible to inaudible. If the inaudible blocks continue over a speicific time thersold, then we will think the steam as inaudible. Differential Revision: https://phabricator.services.mozilla.com/D7823
dom/media/webaudio/AudioDestinationNode.cpp
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -3,16 +3,17 @@
 /* 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/. */
 
 #include "AudioDestinationNode.h"
 #include "AudioContext.h"
 #include "AlignmentUtils.h"
 #include "AudioContext.h"
+#include "CubebUtils.h"
 #include "mozilla/dom/AudioDestinationNodeBinding.h"
 #include "mozilla/dom/OfflineAudioCompletionEvent.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/BaseAudioContextBinding.h"
 #include "mozilla/Services.h"
 #include "AudioChannelAgent.h"
 #include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
@@ -211,16 +212,17 @@ private:
 class DestinationNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit DestinationNodeEngine(AudioDestinationNode* aNode)
     : AudioNodeEngine(aNode)
     , mVolume(1.0f)
     , mLastInputAudible(false)
     , mSuspended(false)
+    , mSampleRate(CubebUtils::PreferredSampleRate())
   {
     MOZ_ASSERT(aNode);
   }
 
   void ProcessBlock(AudioNodeStream* aStream,
                     GraphTime aFrom,
                     const AudioBlock& aInput,
                     AudioBlock* aOutput,
@@ -231,33 +233,58 @@ public:
 
     if (mSuspended) {
       return;
     }
 
     bool isInputAudible = !aInput.IsNull() &&
                           !aInput.IsMuted() &&
                           aInput.IsAudible();
-    if (isInputAudible != mLastInputAudible) {
+
+    auto shouldNotifyChanged = [&] () {
+      // We don't want to notify state changed frequently if the input stream is
+      // consist of interleaving audible and inaudible blocks. This situation is
+      // really common, especially when user is using OscillatorNode to produce
+      // sound. Sending unnessary runnable frequently would cause performance
+      // debasing. If the stream contains 10 interleaving samples and 5 of them
+      // are audible, others are inaudible, user would tend to feel the stream is
+      // audible. Therefore, we have the loose checking when stream is changing
+      // from inaudible to audible, but have strict checking when streaming is
+      // changing from audible to inaudible. If the inaudible blocks continue
+      // over a speicific time threshold, then we will treat the stream as inaudible.
+      if (isInputAudible && !mLastInputAudible) {
+        return true;
+      }
+      // Use more strict condition, choosing 1 seconds as a threshold.
+      if (!isInputAudible && mLastInputAudible &&
+          aFrom - mLastInputAudibleTime >= mSampleRate) {
+        return true;
+      }
+      return false;
+    };
+    if (shouldNotifyChanged()) {
       mLastInputAudible = isInputAudible;
-
       RefPtr<AudioNodeStream> stream = aStream;
       auto r = [stream, isInputAudible] () -> void {
         MOZ_ASSERT(NS_IsMainThread());
         RefPtr<AudioNode> node = stream->Engine()->NodeMainThread();
         if (node) {
           RefPtr<AudioDestinationNode> destinationNode =
             static_cast<AudioDestinationNode*>(node.get());
           destinationNode->NotifyAudibleStateChanged(isInputAudible);
         }
       };
 
       aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
         NS_NewRunnableFunction("dom::WebAudioAudibleStateChangedRunnable", r));
     }
+
+    if (isInputAudible) {
+      mLastInputAudibleTime = aFrom;
+    }
   }
 
   bool IsActive() const override
   {
     // Keep processing to track stream time, which is used for all timelines
     // associated with the same AudioContext.  If there are no other engines
     // for the AudioContext, then this could return false to suspend the
     // stream, but the stream is blocked anyway through
@@ -290,17 +317,19 @@ public:
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   float mVolume;
   bool mLastInputAudible;
+  GraphTime mLastInputAudibleTime = 0;
   bool mSuspended;
+  int mSampleRate;
 };
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
                                    mAudioChannelAgent,
                                    mOfflineRenderingPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioDestinationNode)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)