Bug 1208371 - Allow MediaInputPorts mapped to a destination TrackID. r=roc
authorAndreas Pehrson <pehrsons@gmail.com>
Tue, 05 Jan 2016 10:16:22 +0800
changeset 292072 4ad566b38ebac36a905795473ed2b2706b8b5d58
parent 292071 49c2839c3cf99e50de1e124178e643dc467f00b2
child 292073 ce9ca4390483f48ca5c561efa6f7335234d613d8
push id30152
push userkwierso@gmail.com
push dateThu, 07 Apr 2016 20:42:20 +0000
treeherdermozilla-central@06678484909c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1208371
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
@@ -830,18 +830,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
@@ -2783,16 +2783,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()),
@@ -2809,18 +2810,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?");