Bug 874508 - Web Audio is connected to AudioChannelService, r=ehsan
☠☠ backed out by c7bb40f2578a ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 03 Oct 2013 18:42:04 -0400
changeset 149875 643354059afbc5a07b2e4105d7ad1806bf25a92e
parent 149874 7df45f386400f6b61bbd572daf97510dcce11247
child 149876 c7bb40f2578a52cd3468753370c35172a46d667c
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersehsan
bugs874508
milestone27.0a1
Bug 874508 - Web Audio is connected to AudioChannelService, r=ehsan
content/media/AudioNodeStream.cpp
content/media/webaudio/AudioDestinationNode.cpp
content/media/webaudio/AudioDestinationNode.h
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -12,23 +12,22 @@
 #include "AudioParamTimeline.h"
 
 using namespace mozilla::dom;
 
 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()
 {
   MOZ_COUNT_DTOR(AudioNodeStream);
 }
 
 void
 AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, MediaStream* aRelativeToStream,
@@ -404,17 +403,17 @@ AudioNodeStream::ProduceOutput(GraphTime
 {
   if (mMarkAsFinishedAfterThisBlock) {
     // This stream was finished the last time that we looked at it, and all
     // of the depending streams have finished their output as well, so now
     // it's time to mark this stream as finished.
     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);
 
   if (mMuted) {
     for (uint16_t i = 0; i < outputCount; ++i) {
       mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
     }
@@ -436,68 +435,68 @@ AudioNodeStream::ProduceOutput(GraphTime
     } else {
       mEngine->ProduceAudioBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
     }
     if (finished) {
       mMarkAsFinishedAfterThisBlock = true;
     }
   }
 
-  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);
     }
   }
 
   AdvanceOutputSegment();
 }
 
 void
 AudioNodeStream::AdvanceOutputSegment()
 {
-  StreamBuffer::Track* track = EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate);
+  StreamBuffer::Track* track = EnsureTrack(AUDIO_TRACK, mSampleRate);
   AudioSegment* segment = track->Get<AudioSegment>();
 
   if (mKind == MediaStreamGraph::EXTERNAL_STREAM) {
     segment->AppendAndConsumeChunk(&mLastChunks[0]);
   } else {
     segment->AppendNullData(mLastChunks[0].GetDuration());
   }
 
   for (uint32_t j = 0; j < mListeners.Length(); ++j) {
     MediaStreamListener* l = mListeners[j];
     AudioChunk copyChunk = mLastChunks[0];
     AudioSegment tmpSegment;
     tmpSegment.AppendAndConsumeChunk(&copyChunk);
-    l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID,
+    l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
                                 mSampleRate, segment->GetDuration(), 0,
                                 tmpSegment);
   }
 }
 
 TrackTicks
 AudioNodeStream::GetCurrentPosition()
 {
-  return EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate)->Get<AudioSegment>()->GetDuration();
+  return EnsureTrack(AUDIO_TRACK, mSampleRate)->Get<AudioSegment>()->GetDuration();
 }
 
 void
 AudioNodeStream::FinishOutput()
 {
   if (IsFinishedOnGraphThread()) {
     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);
   }
 }
 
 }
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -1,20 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #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 {
 
 class OfflineDestinationNodeEngine : public AudioNodeEngine
 {
 public:
   typedef AutoFallibleTArray<nsAutoArrayPtr<float>, 2> InputChannels;
@@ -208,17 +213,65 @@ public:
   enum Parameters {
     VOLUME,
   };
 
 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<AudioDestinationNode> 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,
                                            uint32_t aNumberOfChannels,
                                            uint32_t aLength,
                                            float aSampleRate)
   : AudioNode(aContext,
               aIsOffline ? aNumberOfChannels : 2,
@@ -230,29 +283,65 @@ AudioDestinationNode::AudioDestinationNo
                             MediaStreamGraph::CreateNonRealtimeInstance() :
                             MediaStreamGraph::GetInstance();
   AudioNodeEngine* engine = aIsOffline ?
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
                             static_cast<AudioNodeEngine*>(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<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
+    if (target) {
+      target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
+                                     /* useCapture = */ true,
+                                     /* wantsUntrusted = */ false);
+    }
+
+    nsCOMPtr<nsIDocShell> 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
 AudioDestinationNode::DestroyMediaStream()
 {
   if (!mStream)
     return;
 
   MediaStreamGraph* graph = mStream->Graph();
   if (graph->IsNonRealtime()) {
     MediaStreamGraph::DestroyNonRealtimeInstance(graph);
   }
   AudioNode::DestroyMediaStream();
+
+  if (mAudioChannelAgent && !Context()->IsOffline()) {
+    mAudioChannelAgent->StopPlaying();
+
+    nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
+    NS_ENSURE_TRUE_VOID(target);
+
+    target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
+                                      /* useCapture = */ true);
+
+    mAudioChannelAgent = nullptr;
+  }
 }
 
 uint32_t
 AudioDestinationNode::MaxChannelCount() const
 {
   return Context()->MaxChannelCount();
 }
 
@@ -299,10 +388,36 @@ AudioDestinationNode::WrapObject(JSConte
 
 void
 AudioDestinationNode::StartRendering()
 {
   mOfflineRenderingRef.Take(this);
   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<nsIDocShell> docshell = do_GetInterface(GetOwner());
+  NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
+
+  bool isActive = false;
+  docshell->GetIsActive(&isActive);
+
+  mAudioChannelAgent->SetVisibilityState(isActive);
+  return NS_OK;
+}
+
 }
 }
--- a/content/media/webaudio/AudioDestinationNode.h
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -3,36 +3,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef AudioDestinationNode_h_
 #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
   // whether it's in offline mode.
   AudioDestinationNode(AudioContext* aContext,
                        bool aIsOffline,
                        uint32_t aNumberOfChannels = 0,
                        uint32_t aLength = 0,
                        float aSampleRate = 0.0f);
 
   virtual void DestroyMediaStream() MOZ_OVERRIDE;
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioDestinationNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfOutputs() const MOZ_FINAL MOZ_OVERRIDE
   {
     return 0;
   }
@@ -43,18 +47,26 @@ public:
 
   void Mute();
   void Unmute();
 
   void StartRendering();
 
   void OfflineShutdown();
 
+  // nsIDOMEventListener
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
+
+  // Used by AudioChannelAgentCallback
+  void SetCanPlay(bool aCanPlay);
+
 private:
   SelfReference<AudioDestinationNode> mOfflineRenderingRef;
   uint32_t mFramesToProduce;
+
+  nsRefPtr<AudioChannelAgent> mAudioChannelAgent;
 };
 
 }
 }
 
 #endif