Bug 1493779 - Gracefuly handle OOM when setting a buffer on ConvolverNode. r=karlt
☠☠ backed out by d9f492389275 ☠ ☠
authorPaul Adenot <paul@paul.cx>
Thu, 04 Oct 2018 15:31:42 +0000
changeset 498076 e78d9996b1a44045dcf8a85722189a3db77033d3
parent 498075 53950ea4b1055b291c417948cc685f044fef97aa
child 498077 3f4e7b1382fd2749c9fa7ae950f12e513c356c5b
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)
reviewerskarlt
bugs1493779
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 1493779 - Gracefuly handle OOM when setting a buffer on ConvolverNode. r=karlt When OOMing when allocating the temporary buffer, we return an error from the ctor via an output parameter, and make the ConvolverNode output silence. Additionaly, a warning is issued each time we fail to set a buffer to the buffer property of a ConvolverNode. Differential Revision: https://phabricator.services.mozilla.com/D6936
dom/locales/en-US/chrome/dom/dom.properties
dom/media/webaudio/ConvolverNode.cpp
dom/media/webaudio/blink/Reverb.cpp
dom/media/webaudio/blink/Reverb.h
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -102,16 +102,18 @@ MediaDecodeAudioDataNoAudio=The buffer p
 MediaElementAudioSourceNodeCrossOrigin=The HTMLMediaElement passed to createMediaElementSource has a cross-origin resource, the node will output silence.
 # LOCALIZATION NOTE: Do not translate MediaStream and createMediaStreamSource.
 MediaStreamAudioSourceNodeCrossOrigin=The MediaStream passed to createMediaStreamSource has a cross-origin resource, the node will output silence.
 # LOCALIZATION NOTE: Do not translate HTMLMediaElement and MediaStream.
 MediaElementAudioCaptureOfMediaStreamError=The captured HTMLMediaElement is playing a MediaStream. Applying volume or mute status is not currently supported.
 MediaLoadExhaustedCandidates=All candidate resources failed to load. Media load paused.
 MediaLoadSourceMissingSrc=<source> element has no “src” attribute. Media resource load failed.
 MediaStreamAudioSourceNodeDifferentRate=Connecting AudioNodes from AudioContexts with different sample-rate is currently not supported.
+# LOCALIZATION NOTE: Do not translate ConvolverNode
+ConvolverNodeAllocationError=Out-of-memory error when instantiating a ConvolverNode: the node will output silence.
 # LOCALIZATION NOTE: %1$S is the Http error code the server returned (e.g. 404, 500, etc), %2$S is the URL of the media resource which failed to load.
 MediaLoadHttpError=HTTP load failed with status %1$S. Load of media resource %2$S failed.
 # LOCALIZATION NOTE: %S is the URL of the media resource which failed to load.
 MediaLoadInvalidURI=Invalid URI. Load of media resource %S failed.
 # LOCALIZATION NOTE: %1$S is the media resource's format/codec type (basically equivalent to the file type, e.g. MP4,AVI,WMV,MOV etc), %2$S is the URL of the media resource which failed to load.
 MediaLoadUnsupportedTypeAttribute=Specified “type” attribute of “%1$S” is not supported. Load of media resource %2$S failed.
 # LOCALIZATION NOTE: %1$S is the "media" attribute value of the <source> element. It is a media query. %2$S is the URL of the media resource which failed to load.
 MediaLoadSourceMediaNotMatched=Specified “media” attribute of “%1$S” does not match the environment. Load of media resource %2$S failed.
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -23,18 +23,19 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNod
 
 NS_IMPL_ADDREF_INHERITED(ConvolverNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(ConvolverNode, AudioNode)
 
 class ConvolverNodeEngine final : public AudioNodeEngine
 {
   typedef PlayingRefChangeHandler PlayingRefChanged;
 public:
-  ConvolverNodeEngine(AudioNode* aNode, bool aNormalize)
+  ConvolverNodeEngine(AudioNode* aNode, bool aNormalize, uint64_t aWindowID)
     : AudioNodeEngine(aNode)
+    , mWindowID(aWindowID)
     , mUseBackgroundThreads(!aNode->Context()->IsOffline())
     , mNormalize(aNormalize)
   {
   }
 
   // Indicates how the right output channel is generated.
   enum class RightConvolverMode {
     // A right convolver is always used when there is more than one impulse
@@ -135,18 +136,26 @@ public:
     }
 
     // Assume for now that convolution of channel difference is not required.
     // Direct may change to Difference during processing.
     mRightConvolverMode =
       aBuffer.ChannelCount() == 1 ? RightConvolverMode::Direct
       : RightConvolverMode::Always;
 
+    bool allocationFailure = false;
     mReverb = new WebCore::Reverb(aBuffer, MaxFFTSize, mUseBackgroundThreads,
-                                  mNormalize, mSampleRate);
+                                  mNormalize, mSampleRate, &allocationFailure);
+    if (allocationFailure) {
+      // If the allocation failed, this AudioNodeEngine is going to output
+      // silence. This is signaled to developers in the console.
+      mReverb = nullptr;
+      WebAudioUtils::LogToDeveloperConsole(mWindowID,
+                                           "ConvolverNodeAllocationError");
+    }
   }
 
   void AllocateReverbInput(const AudioBlock& aInput,
                            uint32_t aTotalChannelCount)
   {
     uint32_t inputChannelCount = aInput.ChannelCount();
     MOZ_ASSERT(inputChannelCount <= aTotalChannelCount);
     mReverbInput.AllocateChannels(aTotalChannelCount);
@@ -191,16 +200,17 @@ public:
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   // Keeping mReverbInput across process calls avoids unnecessary reallocation.
   AudioBlock mReverbInput;
   nsAutoPtr<WebCore::Reverb> mReverb;
+  uint64_t mWindowID;
   // Tracks samples of the tail remaining to be output.  INT32_MIN is a
   // special value to indicate that the end of any previous tail has been
   // handled.
   int32_t mRemainingLeftOutput = INT32_MIN;
   // mRemainingRightOutput and mRemainingRightHistory are only used when
   // mRightOutputMode != Always.  There is no special handling required at the
   // end of tail times and so INT32_MIN is not used.
   // mRemainingRightOutput tracks how much longer this node needs to continue
@@ -392,17 +402,18 @@ ConvolverNodeEngine::ProcessBlock(AudioN
 
 ConvolverNode::ConvolverNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Clamped_max,
               ChannelInterpretation::Speakers)
   , mNormalize(true)
 {
-  ConvolverNodeEngine* engine = new ConvolverNodeEngine(this, mNormalize);
+  uint64_t windowID = aContext->GetParentObject()->WindowID();
+  ConvolverNodeEngine* engine = new ConvolverNodeEngine(this, mNormalize, windowID);
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
 /* static */ already_AddRefed<ConvolverNode>
 ConvolverNode::Create(JSContext* aCx, AudioContext& aAudioContext,
                       const ConvolverOptions& aOptions,
--- a/dom/media/webaudio/blink/Reverb.cpp
+++ b/dom/media/webaudio/blink/Reverb.cpp
@@ -72,30 +72,36 @@ static float calculateNormalizationScale
 
     // True-stereo compensation
     if (numberOfChannels == 4)
         scale *= 0.5f;
 
     return scale;
 }
 
-Reverb::Reverb(const AudioChunk& impulseResponse, size_t maxFFTSize, bool useBackgroundThreads, bool normalize, float sampleRate)
+Reverb::Reverb(const AudioChunk& impulseResponse, size_t maxFFTSize, bool useBackgroundThreads, bool normalize, float sampleRate, bool* aAllocationFailure)
 {
+    MOZ_ASSERT(aAllocationFailure);
     size_t impulseResponseBufferLength = impulseResponse.mDuration;
     float scale = impulseResponse.mVolume;
 
     AutoTArray<const float*,4> irChannels(impulseResponse.ChannelData<float>());
     AutoTArray<float,1024> tempBuf;
 
     if (normalize) {
         scale = calculateNormalizationScale(irChannels, impulseResponseBufferLength, sampleRate);
     }
 
     if (scale != 1.0f) {
-        tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength);
+        bool rv = tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength, mozilla::fallible);
+        *aAllocationFailure = !rv;
+        if (*aAllocationFailure) {
+          return;
+        }
+
         for (uint32_t i = 0; i < irChannels.Length(); ++i) {
             float* buf = &tempBuf[i*impulseResponseBufferLength];
             AudioBufferCopyWithScale(irChannels[i], scale, buf,
                                      impulseResponseBufferLength);
             irChannels[i] = buf;
         }
     }
 
--- a/dom/media/webaudio/blink/Reverb.h
+++ b/dom/media/webaudio/blink/Reverb.h
@@ -39,18 +39,22 @@ namespace WebCore {
 
 // Multi-channel convolution reverb with channel matrixing - one or more ReverbConvolver objects are used internally.
 
 class Reverb {
 public:
     enum { MaxFrameSize = 256 };
 
     // renderSliceSize is a rendering hint, so the FFTs can be optimized to not all occur at the same time (very bad when rendering on a real-time thread).
+    // aAllocation failure is to be checked by the caller. If false, internal
+    // memory could not be allocated, and this Reverb instance is not to be
+    // used.
     Reverb(const mozilla::AudioChunk& impulseResponseBuffer, size_t maxFFTSize,
-           bool useBackgroundThreads, bool normalize, float sampleRate);
+           bool useBackgroundThreads, bool normalize, float sampleRate,
+           bool* aAllocationFailure);
 
     void process(const mozilla::AudioBlock* sourceBus,
                  mozilla::AudioBlock* destinationBus);
 
     size_t impulseResponseLength() const { return m_impulseResponseLength; }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;