Bug 1273136: Start remote streams on SRD, and end them even if offer/answer never completed. r=jesup, r=pehrsons
authorByron Campen [:bwc] <docfaraday@gmail.com>
Mon, 23 May 2016 10:22:01 -0500
changeset 341431 1b3eddbebb7ab27b07d3ff9d90eca37ccb6863a0
parent 341430 2842eb7d2fb0de5d3edc4b18d857faf40df7db98
child 341432 0cb38314f603d6a3f01fd90ee296b11df262b0cf
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, pehrsons
bugs1273136
milestone49.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 1273136: Start remote streams on SRD, and end them even if offer/answer never completed. r=jesup, r=pehrsons MozReview-Commit-ID: ulrDM0Gzj6
dom/media/MediaStreamTrack.h
media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
media/webrtc/signaling/test/mediapipeline_unittest.cpp
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -25,16 +25,17 @@ class MediaStreamGraph;
 class MediaStreamGraphImpl;
 class MediaStreamTrackListener;
 class MediaStreamTrackDirectListener;
 class PeerConnectionImpl;
 class PeerConnectionMedia;
 class PeerIdentity;
 class ProcessedMediaStream;
 class RemoteSourceStreamInfo;
+class SourceStreamInfo;
 
 namespace dom {
 
 class AudioStreamTrack;
 class VideoStreamTrack;
 
 /**
  * Common interface through which a MediaStreamTrack can communicate with its
@@ -223,16 +224,17 @@ class MediaStreamTrack : public DOMEvent
 {
   // DOMMediaStream owns MediaStreamTrack instances, and requires access to
   // some internal state, e.g., GetInputStream(), GetOwnedStream().
   friend class mozilla::DOMMediaStream;
 
   // PeerConnection and friends need to know our owning DOMStream and track id.
   friend class mozilla::PeerConnectionImpl;
   friend class mozilla::PeerConnectionMedia;
+  friend class mozilla::SourceStreamInfo;
   friend class mozilla::RemoteSourceStreamInfo;
 
   class PrincipalHandleListener;
 
 public:
   /**
    * aTrackID is the MediaStreamGraph track ID for the track in the
    * MediaStream owned by aStream.
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -53,18 +53,21 @@
 
 #include "webrtc/common_types.h"
 #include "webrtc/common_video/interface/native_handle.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 #include "webrtc/video_engine/include/vie_errors.h"
 
 #include "logging.h"
 
-// Should come from MediaEngineWebRTC.h, but that's a pain to include here
-#define DEFAULT_SAMPLE_RATE 32000
+// Max size given stereo is 480*2*2 = 1920 (48KHz)
+#define AUDIO_SAMPLE_BUFFER_MAX 480*2*2
+static_assert((WEBRTC_DEFAULT_SAMPLE_RATE/100)*sizeof(uint16_t) * 2
+               <= AUDIO_SAMPLE_BUFFER_MAX,
+               "AUDIO_SAMPLE_BUFFER_MAX is not large enough");
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 // Logging context
 MOZ_MTLOG_MODULE("mediapipeline")
@@ -1708,19 +1711,16 @@ void MediaPipelineTransmit::PipelineList
   packetizer_->Input(samples, chunk.mDuration);
 
   while (packetizer_->PacketsAvailable()) {
     uint32_t samplesPerPacket = packetizer_->PacketSize() *
                                 packetizer_->Channels();
 
     // We know that webrtc.org's code going to copy the samples down the line,
     // so we can just use a stack buffer here instead of malloc-ing.
-    // Max size given stereo is 480*2*2 = 1920 (10ms of 16-bits stereo audio at
-    // 48KHz)
-    const size_t AUDIO_SAMPLE_BUFFER_MAX = 1920;
     int16_t packet[AUDIO_SAMPLE_BUFFER_MAX];
 
     packetizer_->Output(packet);
     conduit->SendAudioFrame(packet,
                             samplesPerPacket,
                             rate, 0);
   }
 }
@@ -1744,127 +1744,54 @@ class GenericReceiveCallback : public Tr
     : listener_(listener) {}
 
   void TrackAdded(TrackTicks time);
 
  private:
   RefPtr<GenericReceiveListener> listener_;
 };
 
-// Add a track and listener on the MSG thread using the MSG command queue
-static void AddTrackAndListener(MediaStream* source,
-                                TrackID track_id, TrackRate track_rate,
-                                MediaStreamListener* listener, MediaSegment* segment,
-                                const RefPtr<TrackAddedCallback>& completed,
-                                bool queue_track) {
-  // This both adds the listener and the track
+// Add a listener on the MSG thread using the MSG command queue
+static void AddListener(MediaStream* source, MediaStreamListener* listener) {
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   class Message : public ControlMessage {
    public:
-    Message(MediaStream* stream, TrackID track, TrackRate rate,
-            MediaSegment* segment, MediaStreamListener* listener,
-            const RefPtr<TrackAddedCallback>& completed)
+    Message(MediaStream* stream, MediaStreamListener* listener)
       : ControlMessage(stream),
-        track_id_(track),
-        track_rate_(rate),
-        segment_(segment),
-        listener_(listener),
-        completed_(completed) {}
+        listener_(listener) {}
 
     virtual void Run() override {
-      StreamTime current_end = mStream->GetTracksEnd();
-      TrackTicks current_ticks =
-        mStream->TimeToTicksRoundUp(track_rate_, current_end);
-
       mStream->AddListenerImpl(listener_.forget());
-
-      // Add a track 'now' to avoid possible underrun, especially if we add
-      // a track "later".
-
-      if (current_end != 0L) {
-        MOZ_MTLOG(ML_DEBUG, "added track @ " << current_end <<
-                  " -> " << mStream->StreamTimeToSeconds(current_end));
-      }
-
-      // To avoid assertions, we need to insert a dummy segment that covers up
-      // to the "start" time for the track
-      segment_->AppendNullData(current_ticks);
-      if (segment_->GetType() == MediaSegment::AUDIO) {
-        mStream->AsSourceStream()->AddAudioTrack(track_id_, track_rate_, 0,
-                                                 static_cast<AudioSegment*>(segment_.forget()));
-      } else {
-        NS_ASSERTION(mStream->GraphRate() == track_rate_, "Rate mismatch");
-        mStream->AsSourceStream()->AddTrack(track_id_, 0, segment_.forget());
-      }
-
-      // We need to know how much has been "inserted" because we're given absolute
-      // times in NotifyPull.
-      completed_->TrackAdded(current_ticks);
     }
    private:
-    TrackID track_id_;
-    TrackRate track_rate_;
-    nsAutoPtr<MediaSegment> segment_;
     RefPtr<MediaStreamListener> listener_;
-    const RefPtr<TrackAddedCallback> completed_;
   };
 
   MOZ_ASSERT(listener);
 
-  if (!queue_track) {
-    // We're only queueing the initial set of tracks since they are added
-    // atomically and have start time 0. When not queueing we have to add
-    // the track on the MediaStreamGraph thread so it can be added with the
-    // appropriate start time.
-    source->GraphImpl()->AppendMessage(MakeUnique<Message>(source, track_id, track_rate, segment, listener, completed));
-    MOZ_MTLOG(ML_INFO, "Dispatched track-add for track id " << track_id <<
-                       " on stream " << source);
-    return;
-  }
+  source->GraphImpl()->AppendMessage(MakeUnique<Message>(source, listener));
+#else
+  source->AddListener(listener);
 #endif
-  source->AddListener(listener);
-  if (segment->GetType() == MediaSegment::AUDIO) {
-    source->AsSourceStream()->AddAudioTrack(track_id, track_rate, 0,
-                                            static_cast<AudioSegment*>(segment),
-                                            SourceMediaStream::ADDTRACK_QUEUED);
-  } else {
-    source->AsSourceStream()->AddTrack(track_id, 0, segment,
-                                       SourceMediaStream::ADDTRACK_QUEUED);
-  }
-  MOZ_MTLOG(ML_INFO, "Queued track-add for track id " << track_id <<
-                     " on MediaStream " << source);
 }
 
 class GenericReceiveListener : public MediaStreamListener
 {
  public:
-  GenericReceiveListener(SourceMediaStream *source, TrackID track_id,
-                         TrackRate track_rate, bool queue_track)
+  GenericReceiveListener(SourceMediaStream *source, TrackID track_id)
     : source_(source),
       track_id_(track_id),
-      track_rate_(track_rate),
       played_ticks_(0),
-      queue_track_(queue_track),
       principal_handle_(PRINCIPAL_HANDLE_NONE) {}
 
   virtual ~GenericReceiveListener() {}
 
-  void AddSelf(MediaSegment* segment)
+  void AddSelf()
   {
-    RefPtr<TrackAddedCallback> callback = new GenericReceiveCallback(this);
-    AddTrackAndListener(source_, track_id_, track_rate_, this, segment, callback,
-                        queue_track_);
-  }
-
-  void SetPlayedTicks(TrackTicks time) {
-    played_ticks_ = time;
-  }
-
-  void EndTrack() {
-    source_->EndTrack(track_id_);
+    AddListener(source_, this);
   }
 
 #ifndef USE_FAKE_MEDIA_STREAMS
   // Must be called on the main thread
   void SetPrincipalHandle_m(const PrincipalHandle& principal_handle)
   {
     class Message : public ControlMessage
     {
@@ -1893,27 +1820,20 @@ class GenericReceiveListener : public Me
   {
     principal_handle_ = principal_handle;
   }
 #endif // USE_FAKE_MEDIA_STREAMS
 
  protected:
   SourceMediaStream *source_;
   TrackID track_id_;
-  TrackRate track_rate_;
   TrackTicks played_ticks_;
-  bool queue_track_;
   PrincipalHandle principal_handle_;
 };
 
-void GenericReceiveCallback::TrackAdded(TrackTicks time)
-{
-  listener_->SetPlayedTicks(time);
-}
-
 MediaPipelineReceive::MediaPipelineReceive(
     const std::string& pc,
     nsCOMPtr<nsIEventTarget> main_thread,
     nsCOMPtr<nsIEventTarget> sts_thread,
     SourceMediaStream *stream,
     const std::string& track_id,
     int level,
     RefPtr<MediaSessionConduit> conduit,
@@ -1934,22 +1854,20 @@ MediaPipelineReceive::~MediaPipelineRece
   MOZ_ASSERT(!stream_);  // Check that we have shut down already.
 }
 
 class MediaPipelineReceiveAudio::PipelineListener
   : public GenericReceiveListener
 {
 public:
   PipelineListener(SourceMediaStream * source, TrackID track_id,
-                   const RefPtr<MediaSessionConduit>& conduit,
-                   bool queue_track)
-    : GenericReceiveListener(source, track_id, DEFAULT_SAMPLE_RATE, queue_track), // XXX rate assumption
+                   const RefPtr<MediaSessionConduit>& conduit)
+    : GenericReceiveListener(source, track_id),
       conduit_(conduit)
   {
-    MOZ_ASSERT(track_rate_%100 == 0);
   }
 
   ~PipelineListener()
   {
     if (!NS_IsMainThread()) {
       // release conduit on mainthread.  Must use forget()!
       nsresult rv = NS_DispatchToMainThread(new
                                             ConduitDeleteEvent(conduit_.forget()));
@@ -1967,56 +1885,53 @@ public:
   {
     MOZ_ASSERT(source_);
     if (!source_) {
       MOZ_MTLOG(ML_ERROR, "NotifyPull() called from a non-SourceMediaStream");
       return;
     }
 
     // This comparison is done in total time to avoid accumulated roundoff errors.
-    while (source_->TicksToTimeRoundDown(track_rate_, played_ticks_) <
-           desired_time) {
-      // Max size given stereo is 480*2*2 = 1920 (48KHz)
-      const size_t AUDIO_SAMPLE_BUFFER_MAX = 1920;
-      MOZ_ASSERT((track_rate_/100)*sizeof(uint16_t) * 2 <= AUDIO_SAMPLE_BUFFER_MAX);
-
+    while (source_->TicksToTimeRoundDown(WEBRTC_DEFAULT_SAMPLE_RATE,
+                                         played_ticks_) < desired_time) {
       int16_t scratch_buffer[AUDIO_SAMPLE_BUFFER_MAX];
 
       int samples_length;
 
       // This fetches 10ms of data, either mono or stereo
       MediaConduitErrorCode err =
           static_cast<AudioSessionConduit*>(conduit_.get())->GetAudioFrame(
               scratch_buffer,
-              track_rate_,
+              WEBRTC_DEFAULT_SAMPLE_RATE,
               0,  // TODO(ekr@rtfm.com): better estimate of "capture" (really playout) delay
               samples_length);
 
       if (err != kMediaConduitNoError) {
         // Insert silence on conduit/GIPS failure (extremely unlikely)
         MOZ_MTLOG(ML_ERROR, "Audio conduit failed (" << err
                   << ") to return data @ " << played_ticks_
                   << " (desired " << desired_time << " -> "
                   << source_->StreamTimeToSeconds(desired_time) << ")");
-        samples_length = track_rate_/100; // if this is not enough we'll loop and provide more
+        // if this is not enough we'll loop and provide more
+        samples_length = WEBRTC_DEFAULT_SAMPLE_RATE/100;
         PodArrayZero(scratch_buffer);
       }
 
       MOZ_ASSERT(samples_length * sizeof(uint16_t) < AUDIO_SAMPLE_BUFFER_MAX);
 
       MOZ_MTLOG(ML_DEBUG, "Audio conduit returned buffer of length "
                 << samples_length);
 
       RefPtr<SharedBuffer> samples = SharedBuffer::Create(samples_length * sizeof(uint16_t));
       int16_t *samples_data = static_cast<int16_t *>(samples->Data());
       AudioSegment segment;
       // We derive the number of channels of the stream from the number of samples
       // the AudioConduit gives us, considering it gives us packets of 10ms and we
       // know the rate.
-      uint32_t channelCount = samples_length / (track_rate_ / 100);
+      uint32_t channelCount = samples_length / (WEBRTC_DEFAULT_SAMPLE_RATE / 100);
       AutoTArray<int16_t*,2> channels;
       AutoTArray<const int16_t*,2> outputChannels;
       size_t frames = samples_length / channelCount;
 
       channels.SetLength(channelCount);
 
       size_t offset = 0;
       for (size_t i = 0; i < channelCount; i++) {
@@ -2056,23 +1971,21 @@ MediaPipelineReceiveAudio::MediaPipeline
     nsCOMPtr<nsIEventTarget> sts_thread,
     SourceMediaStream* stream,
     const std::string& media_stream_track_id,
     TrackID numeric_track_id,
     int level,
     RefPtr<AudioSessionConduit> conduit,
     RefPtr<TransportFlow> rtp_transport,
     RefPtr<TransportFlow> rtcp_transport,
-    nsAutoPtr<MediaPipelineFilter> filter,
-    bool queue_track) :
+    nsAutoPtr<MediaPipelineFilter> filter) :
   MediaPipelineReceive(pc, main_thread, sts_thread,
                        stream, media_stream_track_id, level, conduit,
                        rtp_transport, rtcp_transport, filter),
-  listener_(new PipelineListener(stream, numeric_track_id, conduit,
-                                 queue_track))
+  listener_(new PipelineListener(stream, numeric_track_id, conduit))
 {}
 
 void MediaPipelineReceiveAudio::DetachMedia()
 {
   ASSERT_ON_THREAD(main_thread_);
   if (stream_) {
     stream_->RemoveListener(listener_);
     stream_ = nullptr;
@@ -2082,34 +1995,33 @@ void MediaPipelineReceiveAudio::DetachMe
 nsresult MediaPipelineReceiveAudio::Init() {
   ASSERT_ON_THREAD(main_thread_);
   MOZ_MTLOG(ML_DEBUG, __FUNCTION__);
 
   description_ = pc_ + "| Receive audio[";
   description_ += track_id_;
   description_ += "]";
 
-  listener_->AddSelf(new AudioSegment());
+  listener_->AddSelf();
 
   return MediaPipelineReceive::Init();
 }
 
 #ifndef USE_FAKE_MEDIA_STREAMS
 void MediaPipelineReceiveAudio::SetPrincipalHandle_m(const PrincipalHandle& principal_handle)
 {
   listener_->SetPrincipalHandle_m(principal_handle);
 }
 #endif // USE_FAKE_MEDIA_STREAMS
 
 class MediaPipelineReceiveVideo::PipelineListener
   : public GenericReceiveListener {
 public:
-  PipelineListener(SourceMediaStream * source, TrackID track_id,
-                   bool queue_track)
-    : GenericReceiveListener(source, track_id, source->GraphRate(), queue_track),
+  PipelineListener(SourceMediaStream * source, TrackID track_id)
+    : GenericReceiveListener(source, track_id),
       width_(0),
       height_(0),
 #if defined(MOZILLA_INTERNAL_API)
       image_container_(),
       image_(),
 #endif
       monitor_("Video PipelineListener")
   {
@@ -2117,25 +2029,20 @@ public:
     image_container_ =
       LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
 #endif
   }
 
   // Implement MediaStreamListener
   void NotifyPull(MediaStreamGraph* graph, StreamTime desired_time) override
   {
+  #if defined(MOZILLA_INTERNAL_API)
     ReentrantMonitorAutoEnter enter(monitor_);
 
-  #if defined(MOZILLA_INTERNAL_API)
     RefPtr<Image> image = image_;
-    // our constructor sets track_rate_ to the graph rate
-    MOZ_ASSERT(track_rate_ == source_->GraphRate());
-  #endif
-
-  #if defined(MOZILLA_INTERNAL_API)
     StreamTime delta = desired_time - played_ticks_;
 
     // Don't append if we've already provided a frame that supposedly
     // goes past the current aDesiredTime Doing so means a negative
     // delta and thus messes up handling of the graph
     if (delta > 0) {
       VideoSegment segment;
       segment.AppendFrame(image.forget(), delta, IntSize(width_, height_),
@@ -2288,30 +2195,28 @@ MediaPipelineReceiveVideo::MediaPipeline
     nsCOMPtr<nsIEventTarget> sts_thread,
     SourceMediaStream *stream,
     const std::string& media_stream_track_id,
     TrackID numeric_track_id,
     int level,
     RefPtr<VideoSessionConduit> conduit,
     RefPtr<TransportFlow> rtp_transport,
     RefPtr<TransportFlow> rtcp_transport,
-    nsAutoPtr<MediaPipelineFilter> filter,
-    bool queue_track) :
+    nsAutoPtr<MediaPipelineFilter> filter) :
   MediaPipelineReceive(pc, main_thread, sts_thread,
                        stream, media_stream_track_id, level, conduit,
                        rtp_transport, rtcp_transport, filter),
   renderer_(new PipelineRenderer(this)),
-  listener_(new PipelineListener(stream, numeric_track_id, queue_track))
+  listener_(new PipelineListener(stream, numeric_track_id))
 {}
 
 void MediaPipelineReceiveVideo::DetachMedia()
 {
   ASSERT_ON_THREAD(main_thread_);
 
-  listener_->EndTrack();
   // stop generating video and thus stop invoking the PipelineRenderer
   // and PipelineListener - the renderer has a raw ptr to the Pipeline to
   // avoid cycles, and the render callbacks are invoked from a different
   // thread so simple null-checks would cause TSAN bugs without locks.
   static_cast<VideoSessionConduit*>(conduit_.get())->DetachRenderer();
   if (stream_) {
     stream_->RemoveListener(listener_);
     stream_ = nullptr;
@@ -2322,17 +2227,17 @@ nsresult MediaPipelineReceiveVideo::Init
   ASSERT_ON_THREAD(main_thread_);
   MOZ_MTLOG(ML_DEBUG, __FUNCTION__);
 
   description_ = pc_ + "| Receive video[";
   description_ += track_id_;
   description_ += "]";
 
 #if defined(MOZILLA_INTERNAL_API)
-  listener_->AddSelf(new VideoSegment());
+  listener_->AddSelf();
 #endif
 
   // Always happens before we can DetachMedia()
   static_cast<VideoSessionConduit *>(conduit_.get())->
       AttachRenderer(renderer_);
 
   return MediaPipelineReceive::Init();
 }
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -20,16 +20,20 @@
 #include "databuffer.h"
 #include "runnable_utils.h"
 #include "transportflow.h"
 #include "AudioPacketizer.h"
 #include "StreamTracks.h"
 
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 
+// Should come from MediaEngine.h, but that's a pain to include here
+// because of the MOZILLA_EXTERNAL_LINKAGE stuff.
+#define WEBRTC_DEFAULT_SAMPLE_RATE 32000
+
 class nsIPrincipal;
 
 namespace mozilla {
 class MediaPipelineFilter;
 class PeerIdentity;
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
 class VideoFrameConverter;
 #endif
@@ -399,18 +403,17 @@ class MediaPipelineReceiveAudio : public
                             // This is an integer identifier that is only
                             // unique within a single DOMMediaStream, which is
                             // used by MediaStreamGraph
                             TrackID numeric_track_id,
                             int level,
                             RefPtr<AudioSessionConduit> conduit,
                             RefPtr<TransportFlow> rtp_transport,
                             RefPtr<TransportFlow> rtcp_transport,
-                            nsAutoPtr<MediaPipelineFilter> filter,
-                            bool queue_track);
+                            nsAutoPtr<MediaPipelineFilter> filter);
 
   void DetachMedia() override;
 
   nsresult Init() override;
   bool IsVideo() const override { return false; }
 
 #ifndef USE_FAKE_MEDIA_STREAMS
   void SetPrincipalHandle_m(const PrincipalHandle& principal_handle) override;
@@ -438,18 +441,17 @@ class MediaPipelineReceiveVideo : public
                             // This is an integer identifier that is only
                             // unique within a single DOMMediaStream, which is
                             // used by MediaStreamGraph
                             TrackID numeric_track_id,
                             int level,
                             RefPtr<VideoSessionConduit> conduit,
                             RefPtr<TransportFlow> rtp_transport,
                             RefPtr<TransportFlow> rtcp_transport,
-                            nsAutoPtr<MediaPipelineFilter> filter,
-                            bool queue_track);
+                            nsAutoPtr<MediaPipelineFilter> filter);
 
   // Called on the main thread.
   void DetachMedia() override;
 
   nsresult Init() override;
   bool IsVideo() const override { return true; }
 
 #ifndef USE_FAKE_MEDIA_STREAMS
--- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ -530,49 +530,45 @@ MediaPipelineFactory::CreateMediaPipelin
   RefPtr<RemoteSourceStreamInfo> stream =
       mPCMedia->GetRemoteStreamById(aTrack.GetStreamId());
 
   RefPtr<MediaPipelineReceive> pipeline;
 
   TrackID numericTrackId = stream->GetNumericTrackId(aTrack.GetTrackId());
   MOZ_ASSERT(IsTrackIDExplicit(numericTrackId));
 
-  bool queue_track = stream->ShouldQueueTracks();
-
   MOZ_MTLOG(ML_DEBUG, __FUNCTION__ << ": Creating pipeline for "
             << numericTrackId << " -> " << aTrack.GetTrackId());
 
   if (aTrack.GetMediaType() == SdpMediaSection::kAudio) {
     pipeline = new MediaPipelineReceiveAudio(
         mPC->GetHandle(),
         mPC->GetMainThread().get(),
         mPC->GetSTSThread(),
         stream->GetMediaStream()->GetInputStream()->AsSourceStream(),
         aTrack.GetTrackId(),
         numericTrackId,
         aLevel,
         static_cast<AudioSessionConduit*>(aConduit.get()), // Ugly downcast.
         aRtpFlow,
         aRtcpFlow,
-        aFilter,
-        queue_track);
+        aFilter);
   } else if (aTrack.GetMediaType() == SdpMediaSection::kVideo) {
     pipeline = new MediaPipelineReceiveVideo(
         mPC->GetHandle(),
         mPC->GetMainThread().get(),
         mPC->GetSTSThread(),
         stream->GetMediaStream()->GetInputStream()->AsSourceStream(),
         aTrack.GetTrackId(),
         numericTrackId,
         aLevel,
         static_cast<VideoSessionConduit*>(aConduit.get()), // Ugly downcast.
         aRtpFlow,
         aRtcpFlow,
-        aFilter,
-        queue_track);
+        aFilter);
   } else {
     MOZ_ASSERT(false);
     MOZ_MTLOG(ML_ERROR, "Invalid media type in CreateMediaPipelineReceiving");
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = pipeline->Init();
   if (NS_FAILED(rv)) {
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -92,16 +92,20 @@
 #include "rlogringbuffer.h"
 #include "WebrtcGlobalInformation.h"
 #include "mozilla/dom/Event.h"
 #include "nsIDOMCustomEvent.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/net/DataChannelProtocol.h"
 #endif
 
+#ifndef USE_FAKE_MEDIA_STREAMS
+#include "MediaStreamGraphImpl.h"
+#endif
+
 #ifdef XP_WIN
 // We need to undef the MS macro again in case the windows include file
 // got imported after we included nsIDocument.h
 #ifdef CreateEvent
 #undef CreateEvent
 #endif
 #endif // XP_WIN
 
@@ -1738,16 +1742,71 @@ static void DeferredSetRemote(const std:
     if (!PeerConnectionCtx::GetInstance()->isReady()) {
       MOZ_CRASH("Why is DeferredSetRemote being executed when the "
                 "PeerConnectionCtx isn't ready?");
     }
     wrapper.impl()->SetRemoteDescription(aAction, aSdp.c_str());
   }
 }
 
+static void StartTrack(MediaStream* aSource,
+                       TrackID aTrackId,
+                       nsAutoPtr<MediaSegment>&& aSegment) {
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+  class Message : public ControlMessage {
+   public:
+    Message(MediaStream* aStream,
+            TrackID aTrack,
+            nsAutoPtr<MediaSegment>&& aSegment)
+      : ControlMessage(aStream),
+        track_id_(aTrack),
+        segment_(aSegment) {}
+
+    virtual void Run() override {
+      TrackRate track_rate = segment_->GetType() == MediaSegment::AUDIO ?
+        WEBRTC_DEFAULT_SAMPLE_RATE : mStream->GraphRate();
+      StreamTime current_end = mStream->GetTracksEnd();
+      TrackTicks current_ticks =
+        mStream->TimeToTicksRoundUp(track_rate, current_end);
+
+      // Add a track 'now' to avoid possible underrun, especially if we add
+      // a track "later".
+
+      if (current_end != 0L) {
+        CSFLogDebug(logTag, "added track @ %u -> %f",
+                    static_cast<unsigned>(current_end),
+                    mStream->StreamTimeToSeconds(current_end));
+      }
+
+      // To avoid assertions, we need to insert a dummy segment that covers up
+      // to the "start" time for the track
+      segment_->AppendNullData(current_ticks);
+      if (segment_->GetType() == MediaSegment::AUDIO) {
+        mStream->AsSourceStream()->AddAudioTrack(
+            track_id_,
+            WEBRTC_DEFAULT_SAMPLE_RATE,
+            0,
+            static_cast<AudioSegment*>(segment_.forget()));
+      } else {
+        mStream->AsSourceStream()->AddTrack(track_id_, 0, segment_.forget());
+      }
+    }
+   private:
+    TrackID track_id_;
+    nsAutoPtr<MediaSegment> segment_;
+  };
+
+  aSource->GraphImpl()->AppendMessage(
+      MakeUnique<Message>(aSource, aTrackId, Move(aSegment)));
+  CSFLogInfo(logTag, "Dispatched track-add for track id %u on stream %p",
+             aTrackId, aSource);
+#endif
+}
+
+
 nsresult
 PeerConnectionImpl::CreateNewRemoteTracks(RefPtr<PeerConnectionObserver>& aPco)
 {
   JSErrorResult jrv;
 
   std::vector<RefPtr<JsepTrack>> newTracks =
     mJsepSession->GetRemoteTracksAdded();
 
@@ -1821,39 +1880,56 @@ PeerConnectionImpl::CreateNewRemoteTrack
       principal = doc->NodePrincipal();
     } else {
       // we're either certain that we need isolation for the streams, OR
       // we're not sure and we can fix the stream in SetDtlsConnected
       principal =  nsNullPrincipal::CreateWithInheritedAttributes(doc->NodePrincipal());
     }
 #endif
 
+    // We need to select unique ids, just use max + 1
+    TrackID maxTrackId = 0;
+    {
+      nsTArray<RefPtr<dom::MediaStreamTrack>> domTracks;
+      info->GetMediaStream()->GetTracks(domTracks);
+      for (auto& track : domTracks) {
+        maxTrackId = std::max(maxTrackId, track->mTrackID);
+      }
+    }
+
     for (RefPtr<JsepTrack>& track : tracks) {
       std::string webrtcTrackId(track->GetTrackId());
       if (!info->HasTrack(webrtcTrackId)) {
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
         RefPtr<RemoteTrackSource> source =
           new RemoteTrackSource(principal, nsString());
 #else
         RefPtr<MediaStreamTrackSource> source = new MediaStreamTrackSource();
 #endif
-        TrackID trackID = info->GetNextAvailableNumericTrackId();
+        TrackID trackID = ++maxTrackId;
         RefPtr<MediaStreamTrack> domTrack;
+        nsAutoPtr<MediaSegment> segment;
         if (track->GetMediaType() == SdpMediaSection::kAudio) {
           domTrack =
             info->GetMediaStream()->CreateDOMTrack(trackID,
                                                    MediaSegment::AUDIO,
                                                    source);
+          segment = new AudioSegment;
         } else {
           domTrack =
             info->GetMediaStream()->CreateDOMTrack(trackID,
                                                    MediaSegment::VIDEO,
                                                    source);
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+          segment = new VideoSegment;
+#endif
         }
 
+        StartTrack(info->GetMediaStream()->GetInputStream()->AsSourceStream(),
+                   trackID, Move(segment));
         info->AddTrack(webrtcTrackId, domTrack);
         CSFLogDebug(logTag, "Added remote track %s/%s",
                     info->GetId().c_str(), webrtcTrackId.c_str());
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
         domTrack->AssignId(NS_ConvertUTF8toUTF16(webrtcTrackId.c_str()));
         aPco->OnAddTrack(*domTrack, streams, jrv);
         if (jrv.Failed()) {
@@ -2204,30 +2280,16 @@ PeerConnectionImpl::PrincipalChanged(Med
   if (doc) {
     mMedia->UpdateSinkIdentity_m(aTrack, doc->NodePrincipal(), mPeerIdentity);
   } else {
     CSFLogInfo(logTag, "Can't update sink principal; document gone");
   }
 }
 #endif
 
-#if !defined(MOZILLA_EXTERNAL_LINKAGE)
-nsresult
-PeerConnectionImpl::GetRemoteTrackId(const std::string streamId,
-                                     const MediaStreamTrack& aTrack,
-                                     std::string* trackId) const
-{
-  if (IsClosed()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  return mMedia->GetRemoteTrackId(streamId, aTrack, trackId);
-}
-#endif
-
 std::string
 PeerConnectionImpl::GetTrackId(const MediaStreamTrack& aTrack)
 {
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   nsString wideTrackId;
   aTrack.GetId(wideTrackId);
   return NS_ConvertUTF16toUTF8(wideTrackId).get();
 #else
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -643,19 +643,16 @@ public:
       RTCStatsQuery *query);
 
   static nsresult ExecuteStatsQuery_s(RTCStatsQuery *query);
 
   // for monitoring changes in track ownership
   // PeerConnectionMedia can't do it because it doesn't know about principals
   virtual void PrincipalChanged(dom::MediaStreamTrack* aTrack) override;
 
-  nsresult GetRemoteTrackId(const std::string streamId,
-                            const dom::MediaStreamTrack& track,
-                            std::string* trackId) const;
 #endif
 
   static std::string GetStreamId(const DOMMediaStream& aStream);
   static std::string GetTrackId(const dom::MediaStreamTrack& track);
 
   void OnMediaError(const std::string& aError);
 
 private:
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -28,16 +28,20 @@
 #include "FakeMediaStreams.h"
 #else
 #include "MediaSegment.h"
 #ifdef MOZILLA_INTERNAL_API
 #include "MediaStreamGraph.h"
 #endif
 #endif
 
+#ifndef USE_FAKE_MEDIA_STREAMS
+#include "MediaStreamGraphImpl.h"
+#endif
+
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsICancelable.h"
 #include "nsIDocument.h"
 #include "nsILoadInfo.h"
 #include "nsIContentPolicy.h"
@@ -106,19 +110,47 @@ PipelineDetachTransport_s(RefPtr<MediaPi
   mainThread->Dispatch(
       // Make sure we let go of our reference before dispatching
       // If the dispatch fails, well, we're hosed anyway.
       WrapRunnableNM(PipelineReleaseRef_m, pipeline.forget()),
       NS_DISPATCH_NORMAL);
 }
 
 void
+SourceStreamInfo::EndTrack(MediaStream* stream, dom::MediaStreamTrack* track)
+{
+  if (!stream || !stream->AsSourceStream()) {
+    return;
+  }
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+  class Message : public ControlMessage {
+   public:
+    Message(MediaStream* stream, TrackID track)
+      : ControlMessage(stream),
+        track_id_(track) {}
+
+    virtual void Run() override {
+      mStream->AsSourceStream()->EndTrack(track_id_);
+    }
+   private:
+    TrackID track_id_;
+  };
+
+  stream->GraphImpl()->AppendMessage(
+      MakeUnique<Message>(stream, track->mTrackID));
+#endif
+
+}
+
+void
 SourceStreamInfo::RemoveTrack(const std::string& trackId)
 {
   mTracks.erase(trackId);
+
   RefPtr<MediaPipeline> pipeline = GetPipelineByTrackId_m(trackId);
   if (pipeline) {
     mPipelines.erase(trackId);
     pipeline->ShutdownMedia_m();
     mParent->GetSTSThread()->Dispatch(
         WrapRunnableNM(PipelineDetachTransport_s,
                        pipeline.forget(),
                        mParent->GetMainThread()),
@@ -940,33 +972,16 @@ PeerConnectionMedia::RemoveRemoteTrack(c
 
   remoteSourceStream->RemoveTrack(trackId);
   if (!remoteSourceStream->GetTrackCount()) {
     mRemoteSourceStreams.RemoveElement(remoteSourceStream);
   }
   return NS_OK;
 }
 
-nsresult
-PeerConnectionMedia::GetRemoteTrackId(const std::string streamId,
-                                      const MediaStreamTrack& track,
-                                      std::string* trackId) const
-{
-  auto* ncThis = const_cast<PeerConnectionMedia*>(this);
-  const RemoteSourceStreamInfo* info =
-    ncThis->GetRemoteStreamById(streamId);
-
-  if (!info) {
-    CSFLogError(logTag, "%s: Could not find stream info", __FUNCTION__);
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  return info->GetTrackId(track, trackId);
-}
-
 void
 PeerConnectionMedia::SelfDestruct()
 {
   ASSERT_ON_THREAD(mMainThread);
 
   CSFLogDebug(logTag, "%s: ", __FUNCTION__);
 
   // Shut down the media
@@ -1508,16 +1523,36 @@ SourceStreamInfo::StorePipeline(
     return NS_ERROR_FAILURE;
   }
 
   mPipelines[trackId] = aPipeline;
   return NS_OK;
 }
 
 void
+RemoteSourceStreamInfo::DetachMedia_m()
+{
+  for (auto& webrtcIdAndTrack : mTracks) {
+    EndTrack(mMediaStream->GetInputStream(), webrtcIdAndTrack.second);
+  }
+  SourceStreamInfo::DetachMedia_m();
+}
+
+void
+RemoteSourceStreamInfo::RemoveTrack(const std::string& trackId)
+{
+  auto it = mTracks.find(trackId);
+  if (it != mTracks.end()) {
+    EndTrack(mMediaStream->GetInputStream(), it->second);
+  }
+
+  SourceStreamInfo::RemoveTrack(trackId);
+}
+
+void
 RemoteSourceStreamInfo::SyncPipeline(
   RefPtr<MediaPipelineReceive> aPipeline)
 {
   // See if we have both audio and video here, and if so cross the streams and
   // sync them
   // TODO: Do we need to prevent multiple syncs if there is more than one audio
   // or video track in a single media stream? What are we supposed to do in this
   // case?
@@ -1545,17 +1580,16 @@ RemoteSourceStreamInfo::StartReceiving()
 {
   if (mReceiving || mPipelines.empty()) {
     return;
   }
 
   mReceiving = true;
 
   SourceMediaStream* source = GetMediaStream()->GetInputStream()->AsSourceStream();
-  source->FinishAddTracks();
   source->SetPullEnabled(true);
   // AdvanceKnownTracksTicksTime(HEAT_DEATH_OF_UNIVERSE) means that in
   // theory per the API, we can't add more tracks before that
   // time. However, the impl actually allows it, and it avoids a whole
   // bunch of locking that would be required (and potential blocking)
   // if we used smaller values and updated them on each NotifyPull.
   source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
   CSFLogDebug(logTag, "Finished adding tracks to MediaStream %p", source);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -80,47 +80,48 @@ public:
   nsresult StorePipeline(const std::string& trackId,
                          const RefPtr<MediaPipeline>& aPipeline);
 
   virtual void AddTrack(const std::string& trackId,
                         const RefPtr<dom::MediaStreamTrack>& aTrack)
   {
     mTracks.insert(std::make_pair(trackId, aTrack));
   }
-  void RemoveTrack(const std::string& trackId);
+  virtual void RemoveTrack(const std::string& trackId);
   bool HasTrack(const std::string& trackId) const
   {
     return !!mTracks.count(trackId);
   }
   size_t GetTrackCount() const { return mTracks.size(); }
 
   // This method exists for stats and the unittests.
   // It allows visibility into the pipelines and flows.
   const std::map<std::string, RefPtr<MediaPipeline>>&
   GetPipelines() const { return mPipelines; }
   RefPtr<MediaPipeline> GetPipelineByTrackId_m(const std::string& trackId);
   // This is needed so PeerConnectionImpl can unregister itself as
   // PrincipalChangeObserver from each track.
   const std::map<std::string, RefPtr<dom::MediaStreamTrack>>&
   GetMediaStreamTracks() const { return mTracks; }
-  dom::MediaStreamTrack* GetTrackById(const std::string& trackId)
+  dom::MediaStreamTrack* GetTrackById(const std::string& trackId) const
   {
     auto it = mTracks.find(trackId);
     if (it == mTracks.end()) {
       return nullptr;
     }
 
     return it->second;
   }
   const std::string& GetId() const { return mId; }
 
   void DetachTransport_s();
-  void DetachMedia_m();
+  virtual void DetachMedia_m();
   bool AnyCodecHasPluginID(uint64_t aPluginID);
 protected:
+  void EndTrack(MediaStream* stream, dom::MediaStreamTrack* track);
   RefPtr<DOMMediaStream> mMediaStream;
   PeerConnectionMedia *mParent;
   const std::string mId;
   // These get set up before we generate our local description, the pipelines
   // and conduits are set up once offer/answer completes.
   std::map<std::string, RefPtr<dom::MediaStreamTrack>> mTracks;
   std::map<std::string, RefPtr<MediaPipeline>> mPipelines;
 };
@@ -195,81 +196,44 @@ class RemoteSourceStreamInfo : public So
   RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
                          PeerConnectionMedia *aParent,
                          const std::string& aId)
     : SourceStreamInfo(aMediaStream, aParent, aId),
       mReceiving(false)
   {
   }
 
+  void DetachMedia_m() override;
+  void RemoveTrack(const std::string& trackId) override;
   void SyncPipeline(RefPtr<MediaPipelineReceive> aPipeline);
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   void UpdatePrincipal_m(nsIPrincipal* aPrincipal);
 #endif
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteSourceStreamInfo)
 
   void AddTrack(const std::string& trackId,
                 const RefPtr<dom::MediaStreamTrack>& aTrack) override
   {
-    mTrackIdMap.push_back(trackId);
-    MOZ_RELEASE_ASSERT(GetNumericTrackId(trackId) == aTrack->mTrackID);
     SourceStreamInfo::AddTrack(trackId, aTrack);
   }
 
-  TrackID GetNextAvailableNumericTrackId() const
-  {
-    return mTrackIdMap.size() + 1;
-  }
-
   TrackID GetNumericTrackId(const std::string& trackId) const
   {
-    for (size_t i = 0; i < mTrackIdMap.size(); ++i) {
-      if (mTrackIdMap[i] == trackId) {
-        return static_cast<TrackID>(i + 1);
-      }
+    dom::MediaStreamTrack* track = GetTrackById(trackId);
+    if (!track) {
+      return TRACK_INVALID;
     }
-    return TRACK_INVALID;
-  }
-
-  nsresult GetTrackId(const dom::MediaStreamTrack& track, std::string* trackId) const
-  {
-    TrackID numericTrackId = track.mTrackID;
-
-    if (numericTrackId <= 0 ||
-        static_cast<size_t>(numericTrackId) > mTrackIdMap.size()) {
-      return NS_ERROR_INVALID_ARG;;
-    }
-
-    *trackId = mTrackIdMap[numericTrackId - 1];
-    return NS_OK;
+    return track->mTrackID;
   }
 
   void StartReceiving();
 
-  /**
-   * Returns true if a |MediaPipeline| should be queueing its track instead of
-   * adding it to the |SourceMediaStream| directly.
-   */
-  bool ShouldQueueTracks() const
-  {
-    return !mReceiving;
-  }
-
  private:
-  // For remote streams, the MediaStreamGraph API forces us to select a
-  // numeric track id before creation of the MediaStreamTrack, and does not
-  // allow us to specify a string-based id until later. We cannot simply use
-  // something based on mline index, since renegotiation can move tracks
-  // around. Hopefully someday we'll be able to specify the string id up-front,
-  // and have the numeric track id selected for us, in which case this variable
-  // and its dependencies can go away.
-  std::vector<std::string> mTrackIdMap;
-
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   // MediaStreamTrackSources associated with this remote stream.
   // We use them for updating their principal if that's needed.
   std::vector<RefPtr<RemoteTrackSource>> mTrackSources;
 #endif
 
   // True iff SetPullEnabled(true) has been called on the DOMMediaStream. This
   // happens when offer/answer concludes.
@@ -344,20 +308,16 @@ class PeerConnectionMedia : public sigsl
                     dom::MediaStreamTrack& aTrack,
                     const std::string& trackId);
 
   nsresult RemoveLocalTrack(const std::string& streamId,
                             const std::string& trackId);
   nsresult RemoveRemoteTrack(const std::string& streamId,
                             const std::string& trackId);
 
-  nsresult GetRemoteTrackId(const std::string streamId,
-                            const dom::MediaStreamTrack& track,
-                            std::string* trackId) const;
-
   // Get a specific local stream
   uint32_t LocalStreamsLength()
   {
     return mLocalSourceStreams.Length();
   }
   LocalSourceStreamInfo* GetLocalStreamByIndex(int index);
   LocalSourceStreamInfo* GetLocalStreamById(const std::string& id);
   LocalSourceStreamInfo* GetLocalStreamByTrackId(const std::string& id);
--- a/media/webrtc/signaling/test/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/test/mediapipeline_unittest.cpp
@@ -329,18 +329,17 @@ class TestAgentReceive : public TestAgen
     audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio(
         test_pc,
         nullptr,
         test_utils->sts_target(),
         audio_->GetStream()->AsSourceStream(), "audio_track_fake_uuid", 1, 1,
         static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get()),
         audio_rtp_transport_.flow_,
         audio_rtcp_transport_.flow_,
-        bundle_filter_,
-        false);
+        bundle_filter_);
 
     audio_pipeline_->Init();
   }
 
   void SetBundleFilter(nsAutoPtr<MediaPipelineFilter> filter) {
     bundle_filter_ = filter;
   }