1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/AudioNode.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,398 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "AudioNode.h" 1.11 +#include "mozilla/ErrorResult.h" 1.12 +#include "AudioNodeStream.h" 1.13 +#include "AudioNodeEngine.h" 1.14 +#include "mozilla/dom/AudioParam.h" 1.15 + 1.16 +namespace mozilla { 1.17 +namespace dom { 1.18 + 1.19 +static const uint32_t INVALID_PORT = 0xffffffff; 1.20 + 1.21 +NS_IMPL_CYCLE_COLLECTION_CLASS(AudioNode) 1.22 + 1.23 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, DOMEventTargetHelper) 1.24 + tmp->DisconnectFromGraph(); 1.25 + if (tmp->mContext) { 1.26 + tmp->mContext->UpdateNodeCount(-1); 1.27 + } 1.28 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) 1.29 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes) 1.30 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputParams) 1.31 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.32 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioNode, 1.33 + DOMEventTargetHelper) 1.34 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) 1.35 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputNodes) 1.36 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputParams) 1.37 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.38 + 1.39 +NS_IMPL_ADDREF_INHERITED(AudioNode, DOMEventTargetHelper) 1.40 + 1.41 +NS_IMETHODIMP_(MozExternalRefCountType) 1.42 +AudioNode::Release() 1.43 +{ 1.44 + if (mRefCnt.get() == 1) { 1.45 + // We are about to be deleted, disconnect the object from the graph before 1.46 + // the derived type is destroyed. 1.47 + DisconnectFromGraph(); 1.48 + } 1.49 + nsrefcnt r = DOMEventTargetHelper::Release(); 1.50 + NS_LOG_RELEASE(this, r, "AudioNode"); 1.51 + return r; 1.52 +} 1.53 + 1.54 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioNode) 1.55 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.56 + 1.57 +AudioNode::AudioNode(AudioContext* aContext, 1.58 + uint32_t aChannelCount, 1.59 + ChannelCountMode aChannelCountMode, 1.60 + ChannelInterpretation aChannelInterpretation) 1.61 + : DOMEventTargetHelper(aContext->GetParentObject()) 1.62 + , mContext(aContext) 1.63 + , mChannelCount(aChannelCount) 1.64 + , mChannelCountMode(aChannelCountMode) 1.65 + , mChannelInterpretation(aChannelInterpretation) 1.66 +{ 1.67 + MOZ_ASSERT(aContext); 1.68 + DOMEventTargetHelper::BindToOwner(aContext->GetParentObject()); 1.69 + SetIsDOMBinding(); 1.70 + aContext->UpdateNodeCount(1); 1.71 +} 1.72 + 1.73 +AudioNode::~AudioNode() 1.74 +{ 1.75 + MOZ_ASSERT(mInputNodes.IsEmpty()); 1.76 + MOZ_ASSERT(mOutputNodes.IsEmpty()); 1.77 + MOZ_ASSERT(mOutputParams.IsEmpty()); 1.78 + if (mContext) { 1.79 + mContext->UpdateNodeCount(-1); 1.80 + } 1.81 +} 1.82 + 1.83 +size_t 1.84 +AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.85 +{ 1.86 + // Not owned: 1.87 + // - mContext 1.88 + // - mStream 1.89 + size_t amount = 0; 1.90 + 1.91 + amount += mInputNodes.SizeOfExcludingThis(aMallocSizeOf); 1.92 + for (size_t i = 0; i < mInputNodes.Length(); i++) { 1.93 + amount += mInputNodes[i].SizeOfExcludingThis(aMallocSizeOf); 1.94 + } 1.95 + 1.96 + // Just measure the array. The entire audio node graph is measured via the 1.97 + // MediaStreamGraph's streams, so we don't want to double-count the elements. 1.98 + amount += mOutputNodes.SizeOfExcludingThis(aMallocSizeOf); 1.99 + 1.100 + amount += mOutputParams.SizeOfExcludingThis(aMallocSizeOf); 1.101 + for (size_t i = 0; i < mOutputParams.Length(); i++) { 1.102 + amount += mOutputParams[i]->SizeOfIncludingThis(aMallocSizeOf); 1.103 + } 1.104 + 1.105 + return amount; 1.106 +} 1.107 + 1.108 +size_t 1.109 +AudioNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.110 +{ 1.111 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.112 +} 1.113 + 1.114 +template <class InputNode> 1.115 +static uint32_t 1.116 +FindIndexOfNode(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode) 1.117 +{ 1.118 + for (uint32_t i = 0; i < aInputNodes.Length(); ++i) { 1.119 + if (aInputNodes[i].mInputNode == aNode) { 1.120 + return i; 1.121 + } 1.122 + } 1.123 + return nsTArray<InputNode>::NoIndex; 1.124 +} 1.125 + 1.126 +template <class InputNode> 1.127 +static uint32_t 1.128 +FindIndexOfNodeWithPorts(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode, 1.129 + uint32_t aInputPort, uint32_t aOutputPort) 1.130 +{ 1.131 + for (uint32_t i = 0; i < aInputNodes.Length(); ++i) { 1.132 + if (aInputNodes[i].mInputNode == aNode && 1.133 + aInputNodes[i].mInputPort == aInputPort && 1.134 + aInputNodes[i].mOutputPort == aOutputPort) { 1.135 + return i; 1.136 + } 1.137 + } 1.138 + return nsTArray<InputNode>::NoIndex; 1.139 +} 1.140 + 1.141 +void 1.142 +AudioNode::DisconnectFromGraph() 1.143 +{ 1.144 + // Addref this temporarily so the refcount bumping below doesn't destroy us 1.145 + // prematurely 1.146 + nsRefPtr<AudioNode> kungFuDeathGrip = this; 1.147 + 1.148 + // The idea here is that we remove connections one by one, and at each step 1.149 + // the graph is in a valid state. 1.150 + 1.151 + // Disconnect inputs. We don't need them anymore. 1.152 + while (!mInputNodes.IsEmpty()) { 1.153 + uint32_t i = mInputNodes.Length() - 1; 1.154 + nsRefPtr<AudioNode> input = mInputNodes[i].mInputNode; 1.155 + mInputNodes.RemoveElementAt(i); 1.156 + input->mOutputNodes.RemoveElement(this); 1.157 + } 1.158 + 1.159 + while (!mOutputNodes.IsEmpty()) { 1.160 + uint32_t i = mOutputNodes.Length() - 1; 1.161 + nsRefPtr<AudioNode> output = mOutputNodes[i].forget(); 1.162 + mOutputNodes.RemoveElementAt(i); 1.163 + uint32_t inputIndex = FindIndexOfNode(output->mInputNodes, this); 1.164 + // It doesn't matter which one we remove, since we're going to remove all 1.165 + // entries for this node anyway. 1.166 + output->mInputNodes.RemoveElementAt(inputIndex); 1.167 + } 1.168 + 1.169 + while (!mOutputParams.IsEmpty()) { 1.170 + uint32_t i = mOutputParams.Length() - 1; 1.171 + nsRefPtr<AudioParam> output = mOutputParams[i].forget(); 1.172 + mOutputParams.RemoveElementAt(i); 1.173 + uint32_t inputIndex = FindIndexOfNode(output->InputNodes(), this); 1.174 + // It doesn't matter which one we remove, since we're going to remove all 1.175 + // entries for this node anyway. 1.176 + output->RemoveInputNode(inputIndex); 1.177 + } 1.178 + 1.179 + DestroyMediaStream(); 1.180 +} 1.181 + 1.182 +void 1.183 +AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput, 1.184 + uint32_t aInput, ErrorResult& aRv) 1.185 +{ 1.186 + if (aOutput >= NumberOfOutputs() || 1.187 + aInput >= aDestination.NumberOfInputs()) { 1.188 + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 1.189 + return; 1.190 + } 1.191 + 1.192 + if (Context() != aDestination.Context()) { 1.193 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.194 + return; 1.195 + } 1.196 + 1.197 + if (FindIndexOfNodeWithPorts(aDestination.mInputNodes, this, aInput, aOutput) != 1.198 + nsTArray<AudioNode::InputNode>::NoIndex) { 1.199 + // connection already exists. 1.200 + return; 1.201 + } 1.202 + 1.203 + // The MediaStreamGraph will handle cycle detection. We don't need to do it 1.204 + // here. 1.205 + 1.206 + mOutputNodes.AppendElement(&aDestination); 1.207 + InputNode* input = aDestination.mInputNodes.AppendElement(); 1.208 + input->mInputNode = this; 1.209 + input->mInputPort = aInput; 1.210 + input->mOutputPort = aOutput; 1.211 + if (aDestination.mStream) { 1.212 + // Connect streams in the MediaStreamGraph 1.213 + MOZ_ASSERT(aDestination.mStream->AsProcessedStream()); 1.214 + ProcessedMediaStream* ps = 1.215 + static_cast<ProcessedMediaStream*>(aDestination.mStream.get()); 1.216 + MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number"); 1.217 + MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number"); 1.218 + input->mStreamPort = 1.219 + ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT, 1.220 + static_cast<uint16_t>(aInput), 1.221 + static_cast<uint16_t>(aOutput)); 1.222 + } 1.223 + 1.224 + // This connection may have connected a panner and a source. 1.225 + Context()->UpdatePannerSource(); 1.226 +} 1.227 + 1.228 +void 1.229 +AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput, 1.230 + ErrorResult& aRv) 1.231 +{ 1.232 + if (aOutput >= NumberOfOutputs()) { 1.233 + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 1.234 + return; 1.235 + } 1.236 + 1.237 + if (Context() != aDestination.GetParentObject()) { 1.238 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.239 + return; 1.240 + } 1.241 + 1.242 + if (FindIndexOfNodeWithPorts(aDestination.InputNodes(), this, INVALID_PORT, aOutput) != 1.243 + nsTArray<AudioNode::InputNode>::NoIndex) { 1.244 + // connection already exists. 1.245 + return; 1.246 + } 1.247 + 1.248 + mOutputParams.AppendElement(&aDestination); 1.249 + InputNode* input = aDestination.AppendInputNode(); 1.250 + input->mInputNode = this; 1.251 + input->mInputPort = INVALID_PORT; 1.252 + input->mOutputPort = aOutput; 1.253 + 1.254 + MediaStream* stream = aDestination.Stream(); 1.255 + MOZ_ASSERT(stream->AsProcessedStream()); 1.256 + ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream); 1.257 + 1.258 + // Setup our stream as an input to the AudioParam's stream 1.259 + MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number"); 1.260 + input->mStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT, 1.261 + 0, static_cast<uint16_t>(aOutput)); 1.262 +} 1.263 + 1.264 +void 1.265 +AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue) 1.266 +{ 1.267 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.268 + MOZ_ASSERT(ns, "How come we don't have a stream here?"); 1.269 + ns->SetDoubleParameter(aIndex, aValue); 1.270 +} 1.271 + 1.272 +void 1.273 +AudioNode::SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue) 1.274 +{ 1.275 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.276 + MOZ_ASSERT(ns, "How come we don't have a stream here?"); 1.277 + ns->SetInt32Parameter(aIndex, aValue); 1.278 +} 1.279 + 1.280 +void 1.281 +AudioNode::SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue) 1.282 +{ 1.283 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.284 + MOZ_ASSERT(ns, "How come we don't have a stream here?"); 1.285 + ns->SetThreeDPointParameter(aIndex, aValue); 1.286 +} 1.287 + 1.288 +void 1.289 +AudioNode::SendChannelMixingParametersToStream() 1.290 +{ 1.291 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.292 + MOZ_ASSERT(ns, "How come we don't have a stream here?"); 1.293 + ns->SetChannelMixingParameters(mChannelCount, mChannelCountMode, 1.294 + mChannelInterpretation); 1.295 +} 1.296 + 1.297 +void 1.298 +AudioNode::SendTimelineParameterToStream(AudioNode* aNode, uint32_t aIndex, 1.299 + const AudioParamTimeline& aValue) 1.300 +{ 1.301 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(aNode->mStream.get()); 1.302 + MOZ_ASSERT(ns, "How come we don't have a stream here?"); 1.303 + ns->SetTimelineParameter(aIndex, aValue); 1.304 +} 1.305 + 1.306 +void 1.307 +AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv) 1.308 +{ 1.309 + if (aOutput >= NumberOfOutputs()) { 1.310 + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 1.311 + return; 1.312 + } 1.313 + 1.314 + // An upstream node may be starting to play on the graph thread, and the 1.315 + // engine for a downstream node may be sending a PlayingRefChangeHandler 1.316 + // ADDREF message to this (main) thread. Wait for a round trip before 1.317 + // releasing nodes, to give engines receiving sound now time to keep their 1.318 + // nodes alive. 1.319 + class RunnableRelease : public nsRunnable { 1.320 + public: 1.321 + explicit RunnableRelease(already_AddRefed<AudioNode> aNode) 1.322 + : mNode(aNode) {} 1.323 + 1.324 + NS_IMETHODIMP Run() MOZ_OVERRIDE 1.325 + { 1.326 + mNode = nullptr; 1.327 + return NS_OK; 1.328 + } 1.329 + private: 1.330 + nsRefPtr<AudioNode> mNode; 1.331 + }; 1.332 + 1.333 + for (int32_t i = mOutputNodes.Length() - 1; i >= 0; --i) { 1.334 + AudioNode* dest = mOutputNodes[i]; 1.335 + for (int32_t j = dest->mInputNodes.Length() - 1; j >= 0; --j) { 1.336 + InputNode& input = dest->mInputNodes[j]; 1.337 + if (input.mInputNode == this && input.mOutputPort == aOutput) { 1.338 + // Destroying the InputNode here sends a message to the graph thread 1.339 + // to disconnect the streams, which should be sent before the 1.340 + // RunAfterPendingUpdates() call below. 1.341 + dest->mInputNodes.RemoveElementAt(j); 1.342 + // Remove one instance of 'dest' from mOutputNodes. There could be 1.343 + // others, and it's not correct to remove them all since some of them 1.344 + // could be for different output ports. 1.345 + nsRefPtr<nsIRunnable> runnable = 1.346 + new RunnableRelease(mOutputNodes[i].forget()); 1.347 + mOutputNodes.RemoveElementAt(i); 1.348 + mStream->RunAfterPendingUpdates(runnable.forget()); 1.349 + break; 1.350 + } 1.351 + } 1.352 + } 1.353 + 1.354 + for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) { 1.355 + AudioParam* dest = mOutputParams[i]; 1.356 + for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) { 1.357 + const InputNode& input = dest->InputNodes()[j]; 1.358 + if (input.mInputNode == this && input.mOutputPort == aOutput) { 1.359 + dest->RemoveInputNode(j); 1.360 + // Remove one instance of 'dest' from mOutputParams. There could be 1.361 + // others, and it's not correct to remove them all since some of them 1.362 + // could be for different output ports. 1.363 + mOutputParams.RemoveElementAt(i); 1.364 + break; 1.365 + } 1.366 + } 1.367 + } 1.368 + 1.369 + // This disconnection may have disconnected a panner and a source. 1.370 + Context()->UpdatePannerSource(); 1.371 +} 1.372 + 1.373 +void 1.374 +AudioNode::DestroyMediaStream() 1.375 +{ 1.376 + if (mStream) { 1.377 + { 1.378 + // Remove the node reference on the engine, and take care to not 1.379 + // hold the lock when the stream gets destroyed, because that will 1.380 + // cause the engine to be destroyed as well, and we don't want to 1.381 + // be holding the lock as we're trying to destroy it! 1.382 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.383 + MutexAutoLock lock(ns->Engine()->NodeMutex()); 1.384 + MOZ_ASSERT(ns, "How come we don't have a stream here?"); 1.385 + MOZ_ASSERT(ns->Engine()->Node() == this, "Invalid node reference"); 1.386 + ns->Engine()->ClearNode(); 1.387 + } 1.388 + 1.389 + mStream->Destroy(); 1.390 + mStream = nullptr; 1.391 + } 1.392 +} 1.393 + 1.394 +void 1.395 +AudioNode::RemoveOutputParam(AudioParam* aParam) 1.396 +{ 1.397 + mOutputParams.RemoveElement(aParam); 1.398 +} 1.399 + 1.400 +} 1.401 +}