Bug 881959 - Mute WebAudio nodes that are part of a cycle that contains no DelayNode, and make cycles work. r=ehsan

--HG--
extra : rebase_source : d4bc378128cf15f8d8395b45b4443ca6b06c5bd2
This commit is contained in:
Paul Adenot 2013-08-26 19:19:36 +02:00
parent bfa902f4ae
commit 7c95321d67
7 changed files with 69 additions and 3 deletions

View File

@ -15,6 +15,7 @@ namespace mozilla {
namespace dom {
struct ThreeDPoint;
class AudioParamTimeline;
class DelayNodeEngine;
}
class AudioNodeStream;
@ -206,6 +207,8 @@ public:
MOZ_COUNT_DTOR(AudioNodeEngine);
}
virtual dom::DelayNodeEngine* AsDelayNodeEngine() { return nullptr; }
virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
{
NS_ERROR("Invalid SetStreamTimeParameter index");

View File

@ -286,6 +286,20 @@ AudioNodeStream::ObtainInputBlock(AudioChunk& aTmpChunk, uint32_t aPortIndex)
a->IsAudioParamStream()) {
continue;
}
// It is possible for mLastChunks to be empty here, because `a` might be a
// AudioNodeStream that has not been scheduled yet, because it is further
// down the graph _but_ as a connection to this node. Because we enforce the
// presence of at least one DelayNode, with at least one block of delay, and
// because the output of a DelayNode when it has been fed less that
// `delayTime` amount of audio is silence, we can simply continue here,
// because this input would not influence the output of this node. Next
// iteration, a->mLastChunks.IsEmpty() will be false, and everthing will
// work as usual.
if (a->mLastChunks.IsEmpty()) {
continue;
}
AudioChunk* chunk = &a->mLastChunks[mInputs[i]->OutputNumber()];
MOZ_ASSERT(chunk);
if (chunk->IsNull() || chunk->mChannelData.IsEmpty()) {
@ -412,8 +426,7 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount());
mLastChunks.SetLength(outputCount);
if (mInCycle) {
// XXX DelayNode not supported yet so just produce silence
if (mMuted) {
for (uint16_t i = 0; i < outputCount; ++i) {
mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
}

View File

@ -21,6 +21,7 @@ namespace mozilla {
namespace dom {
struct ThreeDPoint;
class AudioParamTimeline;
class DelayNodeEngine;
}
class ThreadSharedFloatArrayBufferList;
@ -54,7 +55,8 @@ public:
mKind(aKind),
mNumberOfInputChannels(2),
mMarkAsFinishedAfterThisBlock(false),
mAudioParamStream(false)
mAudioParamStream(false),
mMuted(false)
{
MOZ_ASSERT(NS_IsMainThread());
mChannelCountMode = dom::ChannelCountMode::Max;
@ -103,6 +105,14 @@ public:
{
return mAudioParamStream;
}
void Mute() {
mMuted = true;
}
void Unmute() {
mMuted = false;
}
const OutputChunks& LastChunks() const
{
return mLastChunks;
@ -153,6 +163,8 @@ protected:
bool mMarkAsFinishedAfterThisBlock;
// Whether the stream is an AudioParamHelper stream.
bool mAudioParamStream;
// Whether the stream is muted. Access only on the MediaStreamGraph thread.
bool mMuted;
};
}

View File

@ -472,12 +472,32 @@ MediaStreamGraphImpl::UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream
NS_ASSERTION(!stream->mHasBeenOrdered, "stream should not have already been ordered");
if (stream->mIsOnOrderingStack) {
MediaStream* iter = aStack->getLast();
AudioNodeStream* ns = stream->AsAudioNodeStream();
bool delayNodePresent = ns ? ns->Engine()->AsDelayNodeEngine() != nullptr : false;
bool cycleFound = false;
if (iter) {
do {
cycleFound = true;
iter->AsProcessedStream()->mInCycle = true;
AudioNodeStream* ns = iter->AsAudioNodeStream();
if (ns && ns->Engine()->AsDelayNodeEngine()) {
delayNodePresent = true;
}
iter = iter->getPrevious();
} while (iter && iter != stream);
}
if (cycleFound && !delayNodePresent) {
// If we have detected a cycle, the previous loop should exit with stream
// == iter. Go back in the cycle and mute all nodes we find.
MOZ_ASSERT(iter);
do {
// There can't be non-AudioNodeStream here, MediaStreamAudio{Source,
// Destination}Node are connected to regular MediaStreams, but they can't be
// in a cycle (there is no content API to do so).
MOZ_ASSERT(iter->AsAudioNodeStream());
iter->AsAudioNodeStream()->Mute();
} while((iter = iter->getNext()));
}
return;
}
ProcessedMediaStream* ps = stream->AsProcessedStream();
@ -513,6 +533,10 @@ MediaStreamGraphImpl::UpdateStreamOrder()
ProcessedMediaStream* ps = stream->AsProcessedStream();
if (ps) {
ps->mInCycle = false;
AudioNodeStream* ns = ps->AsAudioNodeStream();
if (ns) {
ns->Unmute();
}
}
}

View File

@ -130,6 +130,10 @@ public:
return nullptr;
}
virtual const DelayNode* AsDelayNode() const {
return nullptr;
}
AudioContext* GetParentObject() const
{
return mContext;

View File

@ -44,6 +44,11 @@ public:
{
}
virtual DelayNodeEngine* AsDelayNodeEngine()
{
return this;
}
void SetSourceStream(AudioNodeStream* aSource)
{
mSource = aSource;

View File

@ -32,6 +32,11 @@ public:
return mDelay;
}
virtual const DelayNode* AsDelayNode() const MOZ_OVERRIDE
{
return this;
}
virtual void NotifyInputConnected() MOZ_OVERRIDE
{
mMediaStreamGraphUpdateIndexAtLastInputConnection =