Bug 1273136: Start remote streams on SRD, and end them even if offer/answer never completed. r?jesup, r=pehrsons draft
authorByron Campen [:bwc] <docfaraday@gmail.com>
Mon, 23 May 2016 10:22:01 -0500
changeset 373457 0fb3eeaed0f70a87432257ebfa0d4af6f65cb003
parent 373456 b6ecadf710aba28eceeba2220ccdbd2a6ddc83a6
child 522393 8754af6d73a34e58538056573f1d8ae26739a5b7
push id19750
push userbcampen@mozilla.com
push dateTue, 31 May 2016 17:34:51 +0000
reviewersjesup, pehrsons
bugs1273136
milestone49.0a1
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,62 +1971,58 @@ 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_);
 
-  listener_->EndTrack();
   if (stream_) {
     stream_->RemoveListener(listener_);
     stream_ = nullptr;
   }
 }
 
 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")
   {
@@ -2119,25 +2030,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_),
@@ -2290,30 +2196,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;
@@ -2324,17 +2228,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;
   }