From eef83fde319c53b0a3e04eaf9c626427e23b9058 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 3 Oct 2013 18:42:04 -0400 Subject: [PATCH] Bug 874508 - Web Audio is connected to AudioChannelService, r=ehsan --- content/media/AudioNodeStream.cpp | 17 ++- .../media/webaudio/AudioDestinationNode.cpp | 117 +++++++++++++++++- content/media/webaudio/AudioDestinationNode.h | 12 ++ 3 files changed, 136 insertions(+), 10 deletions(-) diff --git a/content/media/AudioNodeStream.cpp b/content/media/AudioNodeStream.cpp index aaf383521831..cde9beba5592 100644 --- a/content/media/AudioNodeStream.cpp +++ b/content/media/AudioNodeStream.cpp @@ -17,13 +17,12 @@ namespace mozilla { /** * An AudioNodeStream produces a single audio track with ID - * AUDIO_NODE_STREAM_TRACK_ID. This track has rate AudioContext::sIdealAudioRate + * AUDIO_TRACK. This track has rate AudioContext::sIdealAudioRate * for regular audio contexts, and the rate requested by the web content * for offline audio contexts. * Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples. * Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID */ -static const int AUDIO_NODE_STREAM_TRACK_ID = 1; AudioNodeStream::~AudioNodeStream() { @@ -409,7 +408,7 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo) FinishOutput(); } - EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate); + EnsureTrack(AUDIO_TRACK, mSampleRate); uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount()); mLastChunks.SetLength(outputCount); @@ -441,7 +440,7 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo) } } - if (mDisabledTrackIDs.Contains(AUDIO_NODE_STREAM_TRACK_ID)) { + if (mDisabledTrackIDs.Contains(AUDIO_TRACK)) { for (uint32_t i = 0; i < mLastChunks.Length(); ++i) { mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE); } @@ -453,7 +452,7 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo) void AudioNodeStream::AdvanceOutputSegment() { - StreamBuffer::Track* track = EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate); + StreamBuffer::Track* track = EnsureTrack(AUDIO_TRACK, mSampleRate); AudioSegment* segment = track->Get(); if (mKind == MediaStreamGraph::EXTERNAL_STREAM) { @@ -467,7 +466,7 @@ AudioNodeStream::AdvanceOutputSegment() AudioChunk copyChunk = mLastChunks[0]; AudioSegment tmpSegment; tmpSegment.AppendAndConsumeChunk(©Chunk); - l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID, + l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK, mSampleRate, segment->GetDuration(), 0, tmpSegment); } @@ -476,7 +475,7 @@ AudioNodeStream::AdvanceOutputSegment() TrackTicks AudioNodeStream::GetCurrentPosition() { - return EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate)->Get()->GetDuration(); + return EnsureTrack(AUDIO_TRACK, mSampleRate)->Get()->GetDuration(); } void @@ -486,14 +485,14 @@ AudioNodeStream::FinishOutput() return; } - StreamBuffer::Track* track = EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate); + StreamBuffer::Track* track = EnsureTrack(AUDIO_TRACK, mSampleRate); track->SetEnded(); FinishOnGraphThread(); for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; AudioSegment emptySegment; - l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID, + l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK, mSampleRate, track->GetSegment()->GetDuration(), MediaStreamListener::TRACK_EVENT_ENDED, emptySegment); diff --git a/content/media/webaudio/AudioDestinationNode.cpp b/content/media/webaudio/AudioDestinationNode.cpp index d2d6706ee893..b322083357ad 100644 --- a/content/media/webaudio/AudioDestinationNode.cpp +++ b/content/media/webaudio/AudioDestinationNode.cpp @@ -6,10 +6,15 @@ #include "AudioDestinationNode.h" #include "mozilla/dom/AudioDestinationNodeBinding.h" +#include "mozilla/Preferences.h" +#include "AudioChannelAgent.h" #include "AudioNodeEngine.h" #include "AudioNodeStream.h" #include "MediaStreamGraph.h" #include "OfflineAudioCompletionEvent.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIDocShell.h" +#include "nsIDocument.h" namespace mozilla { namespace dom { @@ -213,7 +218,55 @@ private: float mVolume; }; -NS_IMPL_ISUPPORTS_INHERITED0(AudioDestinationNode, AudioNode) +class AudioChannelAgentCallback MOZ_FINAL : public nsIAudioChannelAgentCallback +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgentCallback) + + explicit AudioChannelAgentCallback(AudioDestinationNode* aNode) + : mNode(aNode) + { + } + + ~AudioChannelAgentCallback() + { + } + + NS_IMETHODIMP + CanPlayChanged(int32_t aCanPlay) + { + mNode->SetCanPlay(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL); + return NS_OK; + } + +private: + nsRefPtr mNode; +}; + +NS_IMPL_CYCLE_COLLECTION_1(AudioChannelAgentCallback, mNode) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgentCallback) + NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgentCallback) +NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgentCallback) + +static bool UseAudioChannelService() +{ + return Preferences::GetBool("media.useAudioChannelService"); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED_1(AudioDestinationNode, AudioNode, + mAudioChannelAgent) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) +NS_INTERFACE_MAP_END_INHERITING(AudioNode) + +NS_IMPL_ADDREF_INHERITED(AudioDestinationNode, AudioNode) +NS_IMPL_RELEASE_INHERITED(AudioDestinationNode, AudioNode) AudioDestinationNode::AudioDestinationNode(AudioContext* aContext, bool aIsOffline, @@ -235,6 +288,30 @@ AudioDestinationNode::AudioDestinationNode(AudioContext* aContext, static_cast(new DestinationNodeEngine(this)); mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM); + + if (!aIsOffline && UseAudioChannelService()) { + mAudioChannelAgent = new AudioChannelAgent(); + mAudioChannelAgent->Init(nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_NORMAL, + new AudioChannelAgentCallback(this)); + + nsCOMPtr target = do_QueryInterface(GetOwner()); + if (target) { + target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this, + /* useCapture = */ true, + /* wantsUntrusted = */ false); + } + + nsCOMPtr docshell = do_GetInterface(GetOwner()); + if (docshell) { + bool isActive = false; + docshell->GetIsActive(&isActive); + mAudioChannelAgent->SetVisibilityState(isActive); + } + + int32_t state = 0; + mAudioChannelAgent->StartPlaying(&state); + SetCanPlay(state == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL); + } } void @@ -248,6 +325,18 @@ AudioDestinationNode::DestroyMediaStream() MediaStreamGraph::DestroyNonRealtimeInstance(graph); } AudioNode::DestroyMediaStream(); + + if (mAudioChannelAgent && !Context()->IsOffline()) { + mAudioChannelAgent->StopPlaying(); + + nsCOMPtr target = do_QueryInterface(GetOwner()); + NS_ENSURE_TRUE_VOID(target); + + target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this, + /* useCapture = */ true); + + mAudioChannelAgent = nullptr; + } } uint32_t @@ -304,5 +393,31 @@ AudioDestinationNode::StartRendering() mStream->Graph()->StartNonRealtimeProcessing(mFramesToProduce); } +void +AudioDestinationNode::SetCanPlay(bool aCanPlay) +{ + mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, aCanPlay); +} + +NS_IMETHODIMP +AudioDestinationNode::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString type; + aEvent->GetType(type); + + if (!type.EqualsLiteral("visibilitychange")) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr docshell = do_GetInterface(GetOwner()); + NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE); + + bool isActive = false; + docshell->GetIsActive(&isActive); + + mAudioChannelAgent->SetVisibilityState(isActive); + return NS_OK; +} + } } diff --git a/content/media/webaudio/AudioDestinationNode.h b/content/media/webaudio/AudioDestinationNode.h index 4e83daee99c8..81f09dc2b267 100644 --- a/content/media/webaudio/AudioDestinationNode.h +++ b/content/media/webaudio/AudioDestinationNode.h @@ -8,13 +8,16 @@ #define AudioDestinationNode_h_ #include "AudioNode.h" +#include "nsIDOMEventListener.h" namespace mozilla { namespace dom { class AudioContext; +class AudioChannelAgent; class AudioDestinationNode : public AudioNode + , public nsIDOMEventListener { public: // This node type knows what MediaStreamGraph to use based on @@ -28,6 +31,7 @@ public: virtual void DestroyMediaStream() MOZ_OVERRIDE; NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioDestinationNode, AudioNode) virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; @@ -48,9 +52,17 @@ public: void OfflineShutdown(); + // nsIDOMEventListener + NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent); + + // Used by AudioChannelAgentCallback + void SetCanPlay(bool aCanPlay); + private: SelfReference mOfflineRenderingRef; uint32_t mFramesToProduce; + + nsRefPtr mAudioChannelAgent; }; }