Bug 1274221 - Ensure MediaStreamListeners are always notified of created and ended tracks. r?ctai, r?jesup draft
authorAndreas Pehrson <pehrsons@gmail.com>
Tue, 24 May 2016 13:23:50 +0200
changeset 370267 e84b908b5f388a840e7e7e0258a2341c9a91790d
parent 370266 78e184e8cbc312b14dad6d5e4a0b993df7bcc67d
child 521716 923d87b1332606760bf177aae8bba34f0632a1af
push id19025
push userpehrsons@gmail.com
push dateTue, 24 May 2016 12:18:19 +0000
reviewersctai, jesup
bugs1274221
milestone49.0a1
Bug 1274221 - Ensure MediaStreamListeners are always notified of created and ended tracks. r?ctai, r?jesup This means that when a MediaStreamListener is added to a stream, we'll call NotifyQueuedTrackChanges with TRACK_EVENT_CREATE for all tracks that already exist. Likewise, we'll call NotifyQueuedTrackChanges with TRACK_EVENT_ENDED for all tracks that exist and have ended. This fixes potential race conditions where a track was created and/or ended before the listener was asynchronously added. MozReview-Commit-ID: G3juhfiZMtg
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraph.h
dom/media/TrackUnionStream.cpp
dom/media/TrackUnionStream.h
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -2308,16 +2308,36 @@ MediaStream::Resume()
 }
 
 void
 MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
 {
   MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
   listener->NotifyBlockingChanged(GraphImpl(),
     mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
+
+  for (StreamTracks::TrackIter it(mTracks); !it.IsEnded(); it.Next()) {
+    MediaStream* inputStream = nullptr;
+    TrackID inputTrackID = TRACK_INVALID;
+    if (ProcessedMediaStream* ps = AsProcessedStream()) {
+      inputStream = ps->GetInputStreamFor(it->GetID());
+      MOZ_ASSERT(inputStream);
+      inputTrackID = ps->GetInputTrackIDFor(it->GetID());
+      MOZ_ASSERT(IsTrackIDExplicit(inputTrackID));
+    }
+
+    uint32_t flags = MediaStreamListener::TRACK_EVENT_CREATED;
+    if (it->IsEnded()) {
+      flags |= MediaStreamListener::TRACK_EVENT_ENDED;
+    }
+    nsAutoPtr<MediaSegment> segment(it->GetSegment()->CreateEmptyClone());
+    listener->NotifyQueuedTrackChanges(Graph(), it->GetID(), it->GetEnd(),
+                                       flags, *segment,
+                                       inputStream, inputTrackID);
+  }
   if (mNotifiedFinished) {
     listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
   }
   if (mNotifiedHasCurrentData) {
     listener->NotifyHasCurrentData(GraphImpl());
   }
 }
 
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -1372,16 +1372,18 @@ public:
   bool HasInputPort(MediaInputPort* aPort)
   {
     return mInputs.Contains(aPort);
   }
   uint32_t InputPortCount()
   {
     return mInputs.Length();
   }
+  virtual MediaStream* GetInputStreamFor(TrackID aTrackID) { return nullptr; }
+  virtual TrackID GetInputTrackIDFor(TrackID aTrackID) { return TRACK_NONE; }
   void DestroyImpl() override;
   /**
    * This gets called after we've computed the blocking states for all
    * streams (mBlocked is up to date up to mStateComputedTime).
    * Also, we've produced output for all streams up to this one. If this stream
    * is not in a cycle, then all its source streams have produced data.
    * Generate output from aFrom to aTo.
    * This will be called on streams that have finished. Most stream types should
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -359,16 +359,40 @@ TrackUnionStream::SetTrackEnabledImpl(Tr
           listener->IncreaseDisabled();
         }
       }
     }
   }
   MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled);
 }
 
+MediaStream*
+TrackUnionStream::GetInputStreamFor(TrackID aTrackID)
+{
+  for (TrackMapEntry& entry : mTrackMap) {
+    if (entry.mOutputTrackID == aTrackID && entry.mInputPort) {
+      return entry.mInputPort->GetSource();
+    }
+  }
+
+  return nullptr;
+}
+
+TrackID
+TrackUnionStream::GetInputTrackIDFor(TrackID aTrackID)
+{
+  for (TrackMapEntry& entry : mTrackMap) {
+    if (entry.mOutputTrackID == aTrackID) {
+      return entry.mInputTrackID;
+    }
+  }
+
+  return TRACK_NONE;
+}
+
 void
 TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
                                              TrackID aTrackID)
 {
   RefPtr<MediaStreamTrackDirectListener> listener = aListener;
 
   for (TrackMapEntry& entry : mTrackMap) {
     if (entry.mOutputTrackID == aTrackID) {
--- a/dom/media/TrackUnionStream.h
+++ b/dom/media/TrackUnionStream.h
@@ -18,16 +18,19 @@ class TrackUnionStream : public Processe
 public:
   explicit TrackUnionStream(DOMMediaStream* aWrapper);
 
   void RemoveInput(MediaInputPort* aPort) override;
   void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
 
   void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) override;
 
+  MediaStream* GetInputStreamFor(TrackID aTrackID) override;
+  TrackID GetInputTrackIDFor(TrackID aTrackID) override;
+
 protected:
   // Only non-ended tracks are allowed to persist in this map.
   struct TrackMapEntry {
     // mEndOfConsumedInputTicks is the end of the input ticks that we've consumed.
     // 0 if we haven't consumed any yet.
     StreamTime mEndOfConsumedInputTicks;
     // mEndOfLastInputIntervalInInputStream is the timestamp for the end of the
     // previous interval which was unblocked for both the input and output