Bug 1208371 - Allow MediaInputPorts mapped to a destination TrackID. r?roc draft
authorAndreas Pehrson <pehrsons@gmail.com>
Tue, 05 Jan 2016 10:16:22 +0800
changeset 342096 3c8104515d957e533d74eead9b16a01cc592f65d
parent 342095 84240bb72ad01fa5938f34a8d1065695aeb980c4
child 342097 44d1be17b33f4cdafb535e078c62cb81d1153843
push id13352
push userpehrsons@gmail.com
push dateFri, 18 Mar 2016 13:49:47 +0000
reviewersroc
bugs1208371
milestone47.0a1
Bug 1208371 - Allow MediaInputPorts mapped to a destination TrackID. r?roc This lets us know the track's TrackID in the destination stream before the input port has been processed. For sanity we only allow mapping to a destination TrackID if the destination stream does not have any TRACK_ANY input ports already assigned to it as that can cause intermittent TrackID collisions. MozReview-Commit-ID: ClFyQl0nYFC
dom/media/MediaRecorder.cpp
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraph.h
dom/media/TrackUnionStream.cpp
dom/media/webaudio/AudioNode.cpp
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -797,18 +797,20 @@ MediaRecorder::MediaRecorder(AudioNode& 
     AudioContext* ctx = aSrcAudioNode.Context();
     AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
     AudioNodeStream::Flags flags =
       AudioNodeStream::EXTERNAL_OUTPUT |
       AudioNodeStream::NEED_MAIN_THREAD_FINISHED;
     mPipeStream = AudioNodeStream::Create(ctx, engine, flags);
     AudioNodeStream* ns = aSrcAudioNode.GetStream();
     if (ns) {
-      mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.GetStream(),
-                                                  TRACK_ANY, 0, aSrcOutput);
+      mInputPort =
+        mPipeStream->AllocateInputPort(aSrcAudioNode.GetStream(),
+                                       TRACK_ANY, TRACK_ANY,
+                                       0, aSrcOutput);
     }
   }
   mAudioNode = &aSrcAudioNode;
 
   RegisterActivityObserver();
 }
 
 void
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -2743,16 +2743,17 @@ MediaInputPort::BlockTrackId(TrackID aTr
 
   MOZ_ASSERT(IsTrackIDExplicit(aTrackId),
              "Only explicit TrackID is allowed");
   GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId));
 }
 
 already_AddRefed<MediaInputPort>
 ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, TrackID aTrackID,
+                                        TrackID aDestTrackID,
                                         uint16_t aInputNumber, uint16_t aOutputNumber)
 {
   // This method creates two references to the MediaInputPort: one for
   // the main thread, and one for the MediaStreamGraph.
   class Message : public ControlMessage {
   public:
     explicit Message(MediaInputPort* aPort)
       : ControlMessage(aPort->GetDestination()),
@@ -2769,18 +2770,23 @@ ProcessedMediaStream::AllocateInputPort(
       Run();
     }
     RefPtr<MediaInputPort> mPort;
   };
 
   MOZ_ASSERT(aStream->GraphImpl() == GraphImpl());
   MOZ_ASSERT(aTrackID == TRACK_ANY || IsTrackIDExplicit(aTrackID),
              "Only TRACK_ANY and explicit ID are allowed for source track");
-  RefPtr<MediaInputPort> port = new MediaInputPort(aStream, aTrackID, this,
-                                                     aInputNumber, aOutputNumber);
+  MOZ_ASSERT(aDestTrackID == TRACK_ANY || IsTrackIDExplicit(aDestTrackID),
+             "Only TRACK_ANY and explicit ID are allowed for destination track");
+  MOZ_ASSERT(aTrackID != TRACK_ANY || aDestTrackID == TRACK_ANY,
+             "Generic MediaInputPort cannot produce a single destination track");
+  RefPtr<MediaInputPort> port =
+    new MediaInputPort(aStream, aTrackID, this, aDestTrackID,
+                       aInputNumber, aOutputNumber);
   port->SetGraphImpl(GraphImpl());
   GraphImpl()->AppendMessage(MakeUnique<Message>(port));
   return port.forget();
 }
 
 void
 ProcessedMediaStream::Finish()
 {
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -966,37 +966,46 @@ protected:
  * input streams.
  * We make these refcounted so that stream-related messages with MediaInputPort*
  * pointers can be sent to the main thread safely.
  *
  * A port can be locked to a specific track in the source stream, in which case
  * only this track will be forwarded to the destination stream. TRACK_ANY
  * can used to signal that all tracks shall be forwarded.
  *
+ * When a port is locked to a specific track in the source stream, it may also
+ * indicate a TrackID to map this source track to in the destination stream
+ * by setting aDestTrack to an explicit ID. When we do this, we must know
+ * that this TrackID in the destination stream is available. We assert during
+ * processing that the ID is available and that there are no generic input
+ * ports already attached to the destination stream.
+ * Note that this is currently only handled by TrackUnionStreams.
+ *
  * When a port's source or destination stream dies, the stream's DestroyImpl
  * calls MediaInputPort::Disconnect to disconnect the port from
  * the source and destination streams.
  *
  * The lifetimes of MediaInputPort are controlled from the main thread.
  * The media graph adds a reference to the port. When a MediaInputPort is no
  * longer needed, main-thread code sends a Destroy message for the port and
  * clears its reference (the last main-thread reference to the object). When
  * the Destroy message is processed on the graph manager thread we disconnect
  * the port and drop the graph's reference, destroying the object.
  */
 class MediaInputPort final
 {
 private:
   // Do not call this constructor directly. Instead call aDest->AllocateInputPort.
   MediaInputPort(MediaStream* aSource, TrackID& aSourceTrack,
-                 ProcessedMediaStream* aDest,
+                 ProcessedMediaStream* aDest, TrackID& aDestTrack,
                  uint16_t aInputNumber, uint16_t aOutputNumber)
     : mSource(aSource)
     , mSourceTrack(aSourceTrack)
     , mDest(aDest)
+    , mDestTrack(aDestTrack)
     , mInputNumber(aInputNumber)
     , mOutputNumber(aOutputNumber)
     , mGraph(nullptr)
   {
     MOZ_COUNT_CTOR(MediaInputPort);
   }
 
   // Private destructor, to discourage deletion outside of Release():
@@ -1020,16 +1029,17 @@ public:
    * object again.
    */
   void Destroy();
 
   // Any thread
   MediaStream* GetSource() { return mSource; }
   TrackID GetSourceTrackId() { return mSourceTrack; }
   ProcessedMediaStream* GetDestination() { return mDest; }
+  TrackID GetDestinationTrackId() { return mDestTrack; }
 
   // Block aTrackId in the port. Consumers will interpret this track as ended.
   void BlockTrackId(TrackID aTrackId);
 private:
   void BlockTrackIdImpl(TrackID aTrackId);
 
 public:
   // Returns true if aTrackId has not been blocked and this port has not
@@ -1081,16 +1091,17 @@ public:
 private:
   friend class MediaStreamGraphImpl;
   friend class MediaStream;
   friend class ProcessedMediaStream;
   // Never modified after Init()
   MediaStream* mSource;
   TrackID mSourceTrack;
   ProcessedMediaStream* mDest;
+  TrackID mDestTrack;
   // The input and output numbers are optional, and are currently only used by
   // Web Audio.
   const uint16_t mInputNumber;
   const uint16_t mOutputNumber;
   nsTArray<TrackID> mBlockedTracks;
 
   // Our media stream graph
   MediaStreamGraphImpl* mGraph;
@@ -1109,23 +1120,31 @@ public:
   {}
 
   // Control API.
   /**
    * Allocates a new input port attached to source aStream.
    * This stream can be removed by calling MediaInputPort::Remove().
    * The input port is tied to aTrackID in the source stream.
    * aTrackID can be set to TRACK_ANY to automatically forward all tracks from
-   * aStream. To end a track in the destination stream forwarded with TRACK_ANY,
+   * aStream.
+   * If aTrackID is an explicit ID, aDestTrackID can also be made explicit
+   * to ensure that the track is assigned this ID in the destination stream.
+   * To avoid intermittent TrackID collisions the destination stream may not
+   * have any existing generic input ports (with TRACK_ANY source track) when
+   * you allocate an input port with a destination TrackID.
+   * To end a track in the destination stream forwarded with TRACK_ANY,
    * it can be blocked in the input port through MediaInputPort::BlockTrackId().
    */
-  already_AddRefed<MediaInputPort> AllocateInputPort(MediaStream* aStream,
-                                                     TrackID aTrackID = TRACK_ANY,
-                                                     uint16_t aInputNumber = 0,
-                                                     uint16_t aOutputNumber = 0);
+  already_AddRefed<MediaInputPort>
+  AllocateInputPort(MediaStream* aStream,
+                    TrackID aTrackID = TRACK_ANY,
+                    TrackID aDestTrackID = TRACK_ANY,
+                    uint16_t aInputNumber = 0,
+                    uint16_t aOutputNumber = 0);
   /**
    * Force this stream into the finished state.
    */
   void Finish();
   /**
    * Set the autofinish flag on this stream (defaults to false). When this flag
    * is set, and all input streams are in the finished state (including if there
    * are no input streams), this stream automatically enters the finished state.
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -144,23 +144,45 @@ TrackUnionStream::TrackUnionStream(DOMMe
       // We can make progress if we're not blocked
       mHasCurrentData = true;
     }
   }
 
   uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack,
                     GraphTime aFrom)
   {
-    TrackID id = aTrack->GetID();
-    if (id > mNextAvailableTrackID &&
-       mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) {
+    STREAM_LOG(LogLevel::Verbose, ("TrackUnionStream %p adding track %d for "
+                                   "input stream %p track %d, desired id %d",
+                                   this, aTrack->GetID(), aPort->GetSource(),
+                                   aTrack->GetID(),
+                                   aPort->GetDestinationTrackId()));
+
+    TrackID id;
+    if (IsTrackIDExplicit(id = aPort->GetDestinationTrackId())) {
+      MOZ_ASSERT(id >= mNextAvailableTrackID &&
+                 mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex,
+                 "Desired destination id taken. Only provide a destination ID "
+                 "if you can assure its availability, or we may not be able "
+                 "to bind to the correct DOM-side track.");
+#ifdef DEBUG
+      for (size_t i = 0; mInputs[i] != aPort; ++i) {
+        MOZ_ASSERT(mInputs[i]->GetSourceTrackId() != TRACK_ANY,
+                   "You are adding a MediaInputPort with a track mapping "
+                   "while there already exist generic MediaInputPorts for this "
+                   "destination stream. This can lead to TrackID collisions!");
+      }
+#endif
+      mUsedTracks.InsertElementSorted(id);
+    } else if ((id = aTrack->GetID()) &&
+               id > mNextAvailableTrackID &&
+               mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) {
       // Input id available. Mark it used in mUsedTracks.
       mUsedTracks.InsertElementSorted(id);
     } else {
-      // Input id taken, allocate a new one.
+      // No desired destination id and Input id taken, allocate a new one.
       id = mNextAvailableTrackID;
 
       // Update mNextAvailableTrackID and prune any mUsedTracks members it now
       // covers.
       while (1) {
         if (!mUsedTracks.RemoveElementSorted(++mNextAvailableTrackID)) {
           // Not in use. We're done.
           break;
@@ -180,19 +202,19 @@ TrackUnionStream::TrackUnionStream(DOMMe
       l->NotifyQueuedTrackChanges(Graph(), id, outputStart,
                                   MediaStreamListener::TRACK_EVENT_CREATED,
                                   *segment,
                                   aPort->GetSource(), aTrack->GetID());
     }
     segment->AppendNullData(outputStart);
     StreamBuffer::Track* track =
       &mBuffer.AddTrack(id, outputStart, segment.forget());
-    STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding track %d for input stream %p track %d, start ticks %lld",
-                              this, id, aPort->GetSource(), aTrack->GetID(),
-                              (long long)outputStart));
+    STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p added track %d for input stream %p track %d, start ticks %lld",
+                                 this, track->GetID(), aPort->GetSource(), aTrack->GetID(),
+                                 (long long)outputStart));
 
     TrackMapEntry* map = mTrackMap.AppendElement();
     map->mEndOfConsumedInputTicks = 0;
     map->mEndOfLastInputIntervalInInputStream = -1;
     map->mEndOfLastInputIntervalInOutputStream = -1;
     map->mInputPort = aPort;
     map->mInputTrackID = aTrack->GetID();
     map->mOutputTrackID = track->GetID();
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -206,17 +206,17 @@ AudioNode::Connect(AudioNode& aDestinati
   input->mInputPort = aInput;
   input->mOutputPort = aOutput;
   AudioNodeStream* destinationStream = aDestination.mStream;
   if (mStream && destinationStream) {
     // Connect streams in the MediaStreamGraph
     MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number");
     MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
     input->mStreamPort = destinationStream->
-      AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK,
+      AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, TRACK_ANY,
                         static_cast<uint16_t>(aInput),
                         static_cast<uint16_t>(aOutput));
   }
   aDestination.NotifyInputsChanged();
 
   // This connection may have connected a panner and a source.
   Context()->UpdatePannerSource();
 
@@ -251,17 +251,17 @@ AudioNode::Connect(AudioParam& aDestinat
 
   MediaStream* stream = aDestination.Stream();
   MOZ_ASSERT(stream->AsProcessedStream());
   ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream);
   if (mStream) {
     // Setup our stream as an input to the AudioParam's stream
     MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
     input->mStreamPort =
-      ps->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK,
+      ps->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, TRACK_ANY,
                             0, static_cast<uint16_t>(aOutput));
   }
 }
 
 void
 AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
 {
   MOZ_ASSERT(mStream, "How come we don't have a stream here?");