content/media/webaudio/DelayNode.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "DelayNode.h"
     8 #include "mozilla/dom/DelayNodeBinding.h"
     9 #include "AudioNodeEngine.h"
    10 #include "AudioNodeStream.h"
    11 #include "AudioDestinationNode.h"
    12 #include "WebAudioUtils.h"
    13 #include "DelayBuffer.h"
    14 #include "PlayingRefChangeHandler.h"
    16 namespace mozilla {
    17 namespace dom {
    19 NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode,
    20                                    mDelay)
    22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode)
    23 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
    25 NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
    26 NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
    28 class DelayNodeEngine : public AudioNodeEngine
    29 {
    30   typedef PlayingRefChangeHandler PlayingRefChanged;
    31 public:
    32   DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
    33                   double aMaxDelayTicks)
    34     : AudioNodeEngine(aNode)
    35     , mSource(nullptr)
    36     , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
    37     // Keep the default value in sync with the default value in DelayNode::DelayNode.
    38     , mDelay(0.f)
    39     // Use a smoothing range of 20ms
    40     , mBuffer(std::max(aMaxDelayTicks,
    41                        static_cast<double>(WEBAUDIO_BLOCK_SIZE)),
    42               WebAudioUtils::ComputeSmoothingRate(0.02,
    43                                                   mDestination->SampleRate()))
    44     , mMaxDelay(aMaxDelayTicks)
    45     , mLastOutputPosition(-1)
    46     , mLeftOverData(INT32_MIN)
    47   {
    48   }
    50   virtual DelayNodeEngine* AsDelayNodeEngine()
    51   {
    52     return this;
    53   }
    55   void SetSourceStream(AudioNodeStream* aSource)
    56   {
    57     mSource = aSource;
    58   }
    60   enum Parameters {
    61     DELAY,
    62   };
    63   void SetTimelineParameter(uint32_t aIndex,
    64                             const AudioParamTimeline& aValue,
    65                             TrackRate aSampleRate) MOZ_OVERRIDE
    66   {
    67     switch (aIndex) {
    68     case DELAY:
    69       MOZ_ASSERT(mSource && mDestination);
    70       mDelay = aValue;
    71       WebAudioUtils::ConvertAudioParamToTicks(mDelay, mSource, mDestination);
    72       break;
    73     default:
    74       NS_ERROR("Bad DelayNodeEngine TimelineParameter");
    75     }
    76   }
    78   virtual void ProcessBlock(AudioNodeStream* aStream,
    79                             const AudioChunk& aInput,
    80                             AudioChunk* aOutput,
    81                             bool* aFinished) MOZ_OVERRIDE
    82   {
    83     MOZ_ASSERT(mSource == aStream, "Invalid source stream");
    84     MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate());
    86     if (!aInput.IsNull()) {
    87       if (mLeftOverData <= 0) {
    88         nsRefPtr<PlayingRefChanged> refchanged =
    89           new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
    90         aStream->Graph()->
    91           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
    92       }
    93       mLeftOverData = mBuffer.MaxDelayTicks();
    94     } else if (mLeftOverData > 0) {
    95       mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
    96     } else {
    97       if (mLeftOverData != INT32_MIN) {
    98         mLeftOverData = INT32_MIN;
    99         // Delete our buffered data now we no longer need it
   100         mBuffer.Reset();
   102         nsRefPtr<PlayingRefChanged> refchanged =
   103           new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
   104         aStream->Graph()->
   105           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
   106       }
   107       *aOutput = aInput;
   108       return;
   109     }
   111     mBuffer.Write(aInput);
   113     UpdateOutputBlock(aOutput);
   114     mBuffer.NextBlock();
   115   }
   117   void UpdateOutputBlock(AudioChunk* aOutput)
   118   {
   119     TrackTicks tick = mSource->GetCurrentPosition();
   120     if (tick == mLastOutputPosition) {
   121       return; // mLastChunks is already set on the stream
   122     }
   124     mLastOutputPosition = tick;
   125     bool inCycle = mSource->AsProcessedStream()->InCycle();
   126     double minDelay = inCycle ? static_cast<double>(WEBAUDIO_BLOCK_SIZE) : 0.0;
   127     double maxDelay = mMaxDelay;
   128     double sampleRate = mSource->SampleRate();
   129     ChannelInterpretation channelInterpretation =
   130       mSource->GetChannelInterpretation();
   131     if (mDelay.HasSimpleValue()) {
   132       // If this DelayNode is in a cycle, make sure the delay value is at least
   133       // one block, even if that is greater than maxDelay.
   134       double delayFrames = mDelay.GetValue() * sampleRate;
   135       double delayFramesClamped =
   136         std::max(minDelay, std::min(delayFrames, maxDelay));
   137       mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation);
   138     } else {
   139       // Compute the delay values for the duration of the input AudioChunk
   140       // If this DelayNode is in a cycle, make sure the delay value is at least
   141       // one block.
   142       double computedDelay[WEBAUDIO_BLOCK_SIZE];
   143       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
   144         double delayAtTick = mDelay.GetValueAtTime(tick, counter) * sampleRate;
   145         double delayAtTickClamped =
   146           std::max(minDelay, std::min(delayAtTick, maxDelay));
   147         computedDelay[counter] = delayAtTickClamped;
   148       }
   149       mBuffer.Read(computedDelay, aOutput, channelInterpretation);
   150     }
   151   }
   153   virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) MOZ_OVERRIDE
   154   {
   155     if (mLeftOverData <= 0) {
   156       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
   157     } else {
   158       UpdateOutputBlock(aOutput);
   159     }
   160   }
   162   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   163   {
   164     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
   165     // Not owned:
   166     // - mSource - probably not owned
   167     // - mDestination - probably not owned
   168     // - mDelay - shares ref with AudioNode, don't count
   169     amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
   170     return amount;
   171   }
   173   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   174   {
   175     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   176   }
   178   AudioNodeStream* mSource;
   179   AudioNodeStream* mDestination;
   180   AudioParamTimeline mDelay;
   181   DelayBuffer mBuffer;
   182   double mMaxDelay;
   183   TrackTicks mLastOutputPosition;
   184   // How much data we have in our buffer which needs to be flushed out when our inputs
   185   // finish.
   186   int32_t mLeftOverData;
   187 };
   189 DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
   190   : AudioNode(aContext,
   191               2,
   192               ChannelCountMode::Max,
   193               ChannelInterpretation::Speakers)
   194   , mDelay(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
   195                           SendDelayToStream, 0.0f))
   196 {
   197   DelayNodeEngine* engine =
   198     new DelayNodeEngine(this, aContext->Destination(),
   199                         aContext->SampleRate() * aMaxDelay);
   200   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   201   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
   202 }
   204 size_t
   205 DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   206 {
   207   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   208   amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
   209   return amount;
   210 }
   212 size_t
   213 DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   214 {
   215   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   216 }
   218 JSObject*
   219 DelayNode::WrapObject(JSContext* aCx)
   220 {
   221   return DelayNodeBinding::Wrap(aCx, this);
   222 }
   224 void
   225 DelayNode::SendDelayToStream(AudioNode* aNode)
   226 {
   227   DelayNode* This = static_cast<DelayNode*>(aNode);
   228   SendTimelineParameterToStream(This, DelayNodeEngine::DELAY, *This->mDelay);
   229 }
   231 }
   232 }

mercurial