Bug 1208371 - Lock MediaStreamAudioSourceNode onto the first AudioStreamTrack in mInputStream at time of construction. r?mt,padenot draft
authorAndreas Pehrson <pehrsons@gmail.com>
Fri, 22 Jan 2016 11:29:40 +0800
changeset 342132 475f9404726f7dbee9f0e199b4cd7db2d4715294
parent 342131 613bf001396b3115eaf8db10b1fc682383b73831
child 342133 6092f83a0f42b67d75fb998a975b990d92eab9aa
push id13352
push userpehrsons@gmail.com
push dateFri, 18 Mar 2016 13:49:47 +0000
reviewersmt, padenot
bugs1208371
milestone47.0a1
Bug 1208371 - Lock MediaStreamAudioSourceNode onto the first AudioStreamTrack in mInputStream at time of construction. r?mt,padenot MozReview-Commit-ID: GLE5dVLEdEL
dom/media/webaudio/MediaStreamAudioSourceNode.cpp
dom/media/webaudio/MediaStreamAudioSourceNode.h
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.cpp
@@ -13,21 +13,27 @@
 #include "mozilla/CORSMode.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamAudioSourceNode)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamAudioSourceNode)
+  if (tmp->mInputStream) {
+    tmp->mInputStream->UnregisterTrackListener(tmp);
+  }
+  tmp->DetachFromTrack();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStream)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputTrack)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamAudioSourceNode, AudioNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStream)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputTrack)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamAudioSourceNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 
@@ -63,62 +69,120 @@ MediaStreamAudioSourceNode::Init(DOMMedi
   if (NS_WARN_IF(graph != inputStream->Graph())) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
   mInputStream = aMediaStream;
   AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this);
   mStream = AudioNodeExternalInputStream::Create(graph, engine);
-  ProcessedMediaStream* outputStream = static_cast<ProcessedMediaStream*>(mStream.get());
-  mInputPort = outputStream->AllocateInputPort(inputStream);
   mInputStream->AddConsumerToKeepAlive(static_cast<nsIDOMEventTarget*>(this));
 
-  PrincipalChanged(mInputStream); // trigger enabling/disabling of the connector
-  mInputStream->AddPrincipalChangeObserver(this);
+  mInputStream->RegisterTrackListener(this);
+  AttachToFirstTrack(mInputStream);
 }
 
-MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
+MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode() {}
+
+void
+MediaStreamAudioSourceNode::AttachToTrack(const RefPtr<MediaStreamTrack>& aTrack)
 {
-  if (mInputStream) {
-    mInputStream->RemovePrincipalChangeObserver(this);
+  MOZ_ASSERT(!mInputTrack);
+  if (!mStream) {
+    return;
+  }
+
+  mInputTrack = aTrack;
+  ProcessedMediaStream* outputStream =
+    static_cast<ProcessedMediaStream*>(mStream.get());
+  mInputPort = mInputTrack->ForwardTrackContentsTo(outputStream);
+  PrincipalChanged(mInputTrack); // trigger enabling/disabling of the connector
+  mInputTrack->AddPrincipalChangeObserver(this);
+}
+
+void
+MediaStreamAudioSourceNode::DetachFromTrack()
+{
+  if (mInputTrack) {
+    mInputTrack->RemovePrincipalChangeObserver(this);
+    mInputTrack = nullptr;
+  }
+  if (mInputPort) {
+    mInputPort->Destroy();
+    mInputPort = nullptr;
   }
 }
 
+void
+MediaStreamAudioSourceNode::AttachToFirstTrack(const RefPtr<DOMMediaStream>& aMediaStream)
+{
+  nsTArray<RefPtr<AudioStreamTrack>> tracks;
+  aMediaStream->GetAudioTracks(tracks);
+
+  if (tracks.IsEmpty()) {
+    return;
+  }
+
+  AttachToTrack(tracks[0]);
+}
+
+void
+MediaStreamAudioSourceNode::NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack)
+{
+  if (mInputTrack) {
+    return;
+  }
+
+  AttachToTrack(aTrack);
+}
+
+void
+MediaStreamAudioSourceNode::NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
+{
+  if (aTrack != mInputTrack) {
+    return;
+  }
+
+  DetachFromTrack();
+  AttachToFirstTrack(mInputStream);
+}
+
 /**
- * Changes the principal.  Note that this will be called on the main thread, but
- * changes will be enacted on the MediaStreamGraph thread.  If the principal
+ * Changes the principal. Note that this will be called on the main thread, but
+ * changes will be enacted on the MediaStreamGraph thread. If the principal
  * change results in the document principal losing access to the stream, then
  * there needs to be other measures in place to ensure that any media that is
- * governed by the new stream principal is not available to the Media Stream
- * Graph before this change completes.  Otherwise, a site could get access to
+ * governed by the new stream principal is not available to the MediaStreamGraph
+ * before this change completes. Otherwise, a site could get access to
  * media that they are not authorized to receive.
  *
  * One solution is to block the altered content, call this method, then dispatch
  * another change request to the MediaStreamGraph thread that allows the content
- * under the new principal to flow.  This might be unnecessary if the principal
+ * under the new principal to flow. This might be unnecessary if the principal
  * change is changing to be the document principal.
  */
 void
-MediaStreamAudioSourceNode::PrincipalChanged(DOMMediaStream* aDOMMediaStream)
+MediaStreamAudioSourceNode::PrincipalChanged(MediaStreamTrack* aMediaStreamTrack)
 {
+  MOZ_ASSERT(aMediaStreamTrack == mInputTrack);
+
   bool subsumes = false;
   if (nsPIDOMWindowInner* parent = Context()->GetParentObject()) {
     nsIDocument* doc = parent->GetExtantDoc();
     if (doc) {
       nsIPrincipal* docPrincipal = doc->NodePrincipal();
-      nsIPrincipal* streamPrincipal = mInputStream->GetPrincipal();
-      if (!streamPrincipal || NS_FAILED(docPrincipal->Subsumes(streamPrincipal, &subsumes))) {
+      nsIPrincipal* trackPrincipal = aMediaStreamTrack->GetPrincipal();
+      if (!trackPrincipal || NS_FAILED(docPrincipal->Subsumes(trackPrincipal, &subsumes))) {
         subsumes = false;
       }
     }
   }
   auto stream = static_cast<AudioNodeExternalInputStream*>(mStream.get());
   stream->SetInt32Parameter(MediaStreamAudioSourceNodeEngine::ENABLE,
-                            subsumes || aDOMMediaStream->GetCORSMode() != CORS_NONE);
+                            subsumes || aMediaStreamTrack->GetCORSMode() != CORS_NONE);
 }
 
 size_t
 MediaStreamAudioSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   // Future:
   // - mInputStream
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.h
@@ -36,17 +36,18 @@ public:
     }
   }
 
 private:
   bool mEnabled;
 };
 
 class MediaStreamAudioSourceNode : public AudioNode,
-                                   public PrincipalChangeObserver<DOMMediaStream>
+                                   public DOMMediaStream::TrackListener,
+                                   public PrincipalChangeObserver<MediaStreamTrack>
 {
 public:
   static already_AddRefed<MediaStreamAudioSourceNode>
   Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 
@@ -59,24 +60,41 @@ public:
   const char* NodeType() const override
   {
     return "MediaStreamAudioSourceNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-  void PrincipalChanged(DOMMediaStream* aMediaStream) override;
+  // Attaches to aTrack so that its audio content will be used as input.
+  void AttachToTrack(const RefPtr<MediaStreamTrack>& aTrack);
+
+  // Detaches from the currently attached track if there is one.
+  void DetachFromTrack();
+
+  // Attaches to the first available audio track in aMediaStream.
+  void AttachToFirstTrack(const RefPtr<DOMMediaStream>& aMediaStream);
+
+  // From DOMMediaStream::TrackListener.
+  void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override;
+  void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) override;
+
+  // From PrincipalChangeObserver<MediaStreamTrack>.
+  void PrincipalChanged(MediaStreamTrack* aMediaStreamTrack) override;
 
 protected:
   explicit MediaStreamAudioSourceNode(AudioContext* aContext);
   void Init(DOMMediaStream* aMediaStream, ErrorResult& aRv);
   virtual ~MediaStreamAudioSourceNode();
 
 private:
   RefPtr<MediaInputPort> mInputPort;
   RefPtr<DOMMediaStream> mInputStream;
+
+  // On construction we set this to the first audio track of mInputStream.
+  RefPtr<MediaStreamTrack> mInputTrack;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif