diff --git a/content/media/AudioNodeEngine.h b/content/media/AudioNodeEngine.h index 5a2368cc8fc..9f3c4b78181 100644 --- a/content/media/AudioNodeEngine.h +++ b/content/media/AudioNodeEngine.h @@ -154,7 +154,6 @@ public: : mNode(aNode) , mNodeMutex("AudioNodeEngine::mNodeMutex") { - MOZ_ASSERT(mNode, "The engine is constructed with a null node"); MOZ_COUNT_CTOR(AudioNodeEngine); } virtual ~AudioNodeEngine() @@ -210,6 +209,11 @@ public: Mutex& NodeMutex() { return mNodeMutex;} + bool HasNode() const + { + return !!mNode; + } + dom::AudioNode* Node() const { mNodeMutex.AssertCurrentThreadOwns(); diff --git a/content/media/AudioNodeStream.cpp b/content/media/AudioNodeStream.cpp index 4f52746a0f9..456641fd046 100644 --- a/content/media/AudioNodeStream.cpp +++ b/content/media/AudioNodeStream.cpp @@ -102,19 +102,19 @@ AudioNodeStream::SetInt32Parameter(uint32_t aIndex, int32_t aValue) void AudioNodeStream::SetTimelineParameter(uint32_t aIndex, - const AudioEventTimeline& aValue) + const AudioParamTimeline& aValue) { class Message : public ControlMessage { public: Message(AudioNodeStream* aStream, uint32_t aIndex, - const AudioEventTimeline& aValue) + const AudioParamTimeline& aValue) : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {} virtual void Run() { static_cast(mStream)->Engine()-> SetTimelineParameter(mIndex, mValue); } - AudioEventTimeline mValue; + AudioParamTimeline mValue; uint32_t mIndex; }; GraphImpl()->AppendMessage(new Message(this, aIndex, aValue)); @@ -247,7 +247,8 @@ AudioNodeStream::ObtainInputBlock(AudioChunk* aTmpChunk) MediaStream* s = mInputs[i]->GetSource(); AudioNodeStream* a = static_cast(s); MOZ_ASSERT(a == s->AsAudioNodeStream()); - if (a->IsFinishedOnGraphThread()) { + if (a->IsFinishedOnGraphThread() || + a->IsAudioParamStream()) { continue; } AudioChunk* chunk = &a->mLastChunk; diff --git a/content/media/AudioNodeStream.h b/content/media/AudioNodeStream.h index 0dffc99b5c3..0cdb15388a0 100644 --- a/content/media/AudioNodeStream.h +++ b/content/media/AudioNodeStream.h @@ -22,6 +22,7 @@ namespace mozilla { namespace dom { struct ThreeDPoint; +class AudioParamTimeline; } class ThreadSharedFloatArrayBufferList; @@ -49,7 +50,8 @@ public: mEngine(aEngine), mKind(aKind), mNumberOfInputChannels(2), - mMarkAsFinishedAfterThisBlock(false) + mMarkAsFinishedAfterThisBlock(false), + mAudioParamStream(false) { mMixingMode.mChannelCountMode = dom::ChannelCountMode::Max; mMixingMode.mChannelInterpretation = dom::ChannelInterpretation::Speakers; @@ -74,6 +76,11 @@ public: void SetChannelMixingParameters(uint32_t aNumberOfChannels, dom::ChannelCountMode aChannelCountMoe, dom::ChannelInterpretation aChannelInterpretation); + void SetAudioParamHelperStream() + { + MOZ_ASSERT(!mAudioParamStream, "Can only do this once"); + mAudioParamStream = true; + } virtual AudioNodeStream* AsAudioNodeStream() { return this; } @@ -86,6 +93,10 @@ public: virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo); TrackTicks GetCurrentPosition(); bool AllInputsFinished() const; + bool IsAudioParamStream() const + { + return mAudioParamStream; + } // Any thread AudioNodeEngine* Engine() { return mEngine; } @@ -112,6 +123,8 @@ protected: // Whether the stream should be marked as finished as soon // as the current time range has been computed block by block. bool mMarkAsFinishedAfterThisBlock; + // Whether the stream is an AudioParamHelper stream. + bool mAudioParamStream; }; } diff --git a/content/media/MediaStreamGraph.cpp b/content/media/MediaStreamGraph.cpp index 609b526853f..42907234286 100644 --- a/content/media/MediaStreamGraph.cpp +++ b/content/media/MediaStreamGraph.cpp @@ -2017,9 +2017,11 @@ MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine, NS_ADDREF(stream); MediaStreamGraphImpl* graph = static_cast(this); stream->SetGraphImpl(graph); - stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(), - aEngine->NodeMainThread()->ChannelCountModeValue(), - aEngine->NodeMainThread()->ChannelInterpretationValue()); + if (aEngine->HasNode()) { + stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(), + aEngine->NodeMainThread()->ChannelCountModeValue(), + aEngine->NodeMainThread()->ChannelInterpretationValue()); + } graph->AppendMessage(new CreateMessage(stream)); return stream; } diff --git a/content/media/webaudio/AudioNode.cpp b/content/media/webaudio/AudioNode.cpp index 0bb543159da..37ef699001a 100644 --- a/content/media/webaudio/AudioNode.cpp +++ b/content/media/webaudio/AudioNode.cpp @@ -66,19 +66,21 @@ AudioNode::~AudioNode() MOZ_ASSERT(mOutputParams.IsEmpty()); } +template static uint32_t -FindIndexOfNode(const nsTArray& aInputNodes, const AudioNode* aNode) +FindIndexOfNode(const nsTArray& aInputNodes, const AudioNode* aNode) { for (uint32_t i = 0; i < aInputNodes.Length(); ++i) { if (aInputNodes[i].mInputNode == aNode) { return i; } } - return nsTArray::NoIndex; + return nsTArray::NoIndex; } +template static uint32_t -FindIndexOfNodeWithPorts(const nsTArray& aInputNodes, const AudioNode* aNode, +FindIndexOfNodeWithPorts(const nsTArray& aInputNodes, const AudioNode* aNode, uint32_t aInputPort, uint32_t aOutputPort) { for (uint32_t i = 0; i < aInputNodes.Length(); ++i) { @@ -88,7 +90,7 @@ FindIndexOfNodeWithPorts(const nsTArray& aInputNodes, cons return i; } } - return nsTArray::NoIndex; + return nsTArray::NoIndex; } void @@ -199,6 +201,13 @@ AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput, input->mInputNode = this; input->mInputPort = INVALID_PORT; input->mOutputPort = aOutput; + + MediaStream* stream = aDestination.Stream(); + MOZ_ASSERT(stream->AsProcessedStream()); + ProcessedMediaStream* ps = static_cast(stream); + + // Setup our stream as an input to the AudioParam's stream + input->mStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT); } void diff --git a/content/media/webaudio/AudioParam.cpp b/content/media/webaudio/AudioParam.cpp index 582b8318243..d2134f7ee67 100644 --- a/content/media/webaudio/AudioParam.cpp +++ b/content/media/webaudio/AudioParam.cpp @@ -9,12 +9,14 @@ #include "nsIDOMWindow.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/AudioParamBinding.h" +#include "AudioNodeEngine.h" +#include "AudioNodeStream.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioParam) - tmp->DisconnectFromGraph(); + tmp->DisconnectFromGraphAndDestroyStream(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mNode) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -33,7 +35,7 @@ AudioParam::Release() if (mRefCnt.get() == 1) { // We are about to be deleted, disconnect the object from the graph before // the derived type is destroyed. - DisconnectFromGraph(); + DisconnectFromGraphAndDestroyStream(); } NS_IMPL_CC_NATIVE_RELEASE_BODY(AudioParam) } @@ -64,7 +66,7 @@ AudioParam::WrapObject(JSContext* aCx, JS::Handle aScope) } void -AudioParam::DisconnectFromGraph() +AudioParam::DisconnectFromGraphAndDestroyStream() { // Addref this temporarily so the refcount bumping below doesn't destroy us // prematurely @@ -76,6 +78,46 @@ AudioParam::DisconnectFromGraph() mInputNodes.RemoveElementAt(i); input->RemoveOutputParam(this); } + + if (mNodeStreamPort) { + mNodeStreamPort->Destroy(); + mNodeStreamPort = nullptr; + } + + if (mStream) { + mStream->Destroy(); + mStream = nullptr; + } +} + +MediaStream* +AudioParam::Stream() +{ + if (mStream) { + return mStream; + } + + AudioNodeEngine* engine = new AudioNodeEngine(nullptr); + nsRefPtr stream = mNode->Context()->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); + + // Force the input to have only one channel, and make it down-mix using + // the speaker rules if needed. + stream->SetChannelMixingParametersImpl(1, ChannelCountMode::Explicit, ChannelInterpretation::Speakers); + // Mark as an AudioParam helper stream + stream->SetAudioParamHelperStream(); + + mStream = stream.forget(); + + // Setup the AudioParam's stream as an input to the owner AudioNode's stream + MediaStream* nodeStream = mNode->Stream(); + MOZ_ASSERT(nodeStream->AsProcessedStream()); + ProcessedMediaStream* ps = static_cast(nodeStream); + mNodeStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT); + + // Let the MSG's copy of AudioParamTimeline know about the change in the stream + mCallback(mNode); + + return mStream; } } diff --git a/content/media/webaudio/AudioParam.h b/content/media/webaudio/AudioParam.h index a09db0850e3..703db4c4aa4 100644 --- a/content/media/webaudio/AudioParam.h +++ b/content/media/webaudio/AudioParam.h @@ -105,6 +105,11 @@ public: return mDefaultValue; } + AudioNode* Node() const + { + return mNode; + } + const nsTArray& InputNodes() const { return mInputNodes; @@ -120,7 +125,10 @@ public: return mInputNodes.AppendElement(); } - void DisconnectFromGraph(); + void DisconnectFromGraphAndDestroyStream(); + + // May create the stream if it doesn't exist + MediaStream* Stream(); protected: nsCycleCollectingAutoRefCnt mRefCnt; @@ -133,6 +141,8 @@ private: nsTArray mInputNodes; CallbackType mCallback; const float mDefaultValue; + // The input port used to connect the AudioParam's stream to its node's stream + nsRefPtr mNodeStreamPort; }; } diff --git a/content/media/webaudio/AudioParamTimeline.h b/content/media/webaudio/AudioParamTimeline.h index 140df506a4e..0902397c193 100644 --- a/content/media/webaudio/AudioParamTimeline.h +++ b/content/media/webaudio/AudioParamTimeline.h @@ -7,17 +7,39 @@ #ifndef AudioParamTimeline_h_ #define AudioParamTimeline_h_ -// This header is intended to make it possible to use AudioParamTimeline -// from multiple places without dealing with #include hell! - #include "AudioEventTimeline.h" #include "mozilla/ErrorResult.h" +#include "nsAutoPtr.h" +#include "MediaStreamGraph.h" namespace mozilla { namespace dom { -typedef AudioEventTimeline AudioParamTimeline; +// This helper class is used to represent the part of the AudioParam +// class that gets sent to AudioNodeEngine instances. In addition to +// AudioEventTimeline methods, it holds a pointer to an optional +// MediaStream which represents the AudioNode inputs to the AudioParam. +// This MediaStream is managed by the AudioParam subclass on the main +// thread, and can only be obtained from the AudioNodeEngine instances +// consuming this class. +class AudioParamTimeline : public AudioEventTimeline +{ +public: + explicit AudioParamTimeline(float aDefaultValue) + : AudioEventTimeline(aDefaultValue) + { + } + + MediaStream* Stream() const + { + return mStream; + } + +protected: + // This is created lazily when needed. + nsRefPtr mStream; +}; } }