Bug 1425621 - Part 4: Move track event logic to JS. r=jib, r=drno, r=smaug draft
authorByron Campen [:bwc] <docfaraday@gmail.com>
Wed, 20 Dec 2017 17:00:40 -0600
changeset 719751 5c011c91c975ee63aaa92d1d579f2ea405fe83d7
parent 716415 78145b45986022de0c6f3e306bc92a12b6e19282
child 719752 f9480698f89bd8714895cf4c00f665aef4414418
push id95369
push userbcampen@mozilla.com
push dateFri, 12 Jan 2018 19:48:14 +0000
reviewersjib, drno, smaug
bugs1425621
milestone59.0a1
Bug 1425621 - Part 4: Move track event logic to JS. r=jib, r=drno, r=smaug MozReview-Commit-ID: 8kUbYQnD3Oc
dom/media/PeerConnection.js
dom/webidl/MediaStreamTrack.webidl
dom/webidl/PeerConnectionObserver.webidl
dom/webidl/RTCRtpReceiver.webidl
media/webrtc/signaling/gtest/jsep_session_unittest.cpp
media/webrtc/signaling/src/jsep/JsepSession.h
media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
media/webrtc/signaling/src/jsep/JsepSessionImpl.h
media/webrtc/signaling/src/jsep/JsepTrack.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -1318,16 +1318,41 @@ class RTCPeerConnection {
 
     this._queueTaskWithClosedCheck(() => {
       if (this._negotiationNeeded) {
         this.dispatchEvent(new this._win.Event("negotiationneeded"));
       }
     });
   }
 
+  _processTrackAdditionsAndRemovals() {
+    let postProcessing = {
+      updateStreamFunctions: [],
+      muteTracks: [],
+      trackEvents: []
+    };
+
+    for (let transceiver of this._transceivers) {
+      transceiver.receiver.processTrackAdditionsAndRemovals(transceiver,
+                                                            postProcessing);
+    }
+
+    for (let f of postProcessing.updateStreamFunctions) {
+      f();
+    }
+
+    for (let t of postProcessing.muteTracks) {
+      t.mutedChanged(true);
+    }
+
+    for (let ev of postProcessing.trackEvents) {
+      this.dispatchEvent(ev);
+    }
+  }
+
   // TODO(Bug 1241291): Legacy event, remove eventually
   _fireLegacyAddStreamEvents() {
     for (let stream of this._newStreams) {
       let ev = new this._win.MediaStreamEvent("addstream", { stream });
       this.dispatchEvent(ev);
     }
     this._newStreams = [];
   }
@@ -1650,16 +1675,17 @@ class PeerConnectionObserver {
 
   onSetLocalDescriptionSuccess() {
     this._dompc._syncTransceivers();
     this._dompc._onSetLocalDescriptionSuccess();
   }
 
   onSetRemoteDescriptionSuccess() {
     this._dompc._syncTransceivers();
+    this._dompc._processTrackAdditionsAndRemovals();
     this._dompc._fireLegacyAddStreamEvents();
     this._dompc._onSetRemoteDescriptionSuccess();
   }
 
   onSetLocalDescriptionError(code, message) {
     this._localType = null;
     this._dompc._onSetLocalDescriptionFailure(this.newError(message, code));
   }
@@ -1816,49 +1842,16 @@ class PeerConnectionObserver {
                                                              { stream }));
   }
 
   _getTransceiverWithRecvTrack(webrtcTrackId) {
     return this._dompc.getTransceivers().find(
         transceiver => transceiver.remoteTrackIdIs(webrtcTrackId));
   }
 
-  onTrack(webrtcTrackId, streamIds) {
-    let pc = this._dompc;
-    let matchingTransceiver = this._getTransceiverWithRecvTrack(webrtcTrackId);
-
-    // Get or create MediaStreams, and add the new track to them.
-    let streams = streamIds.map(id => this._dompc._getOrCreateStream(id));
-
-    streams.forEach(stream => {
-      stream.addTrack(matchingTransceiver.receiver.track);
-      // Adding tracks from JS does not result in the stream getting
-      // onaddtrack, so we need to do that here. The mediacapture spec says
-      // this needs to be queued, also.
-      pc._queueTaskWithClosedCheck(() => {
-        stream.dispatchEvent(
-            new pc._win.MediaStreamTrackEvent(
-              "addtrack", { track: matchingTransceiver.receiver.track }));
-      });
-    });
-
-
-    let ev = new pc._win.RTCTrackEvent("track", {
-      receiver: matchingTransceiver.receiver,
-      track: matchingTransceiver.receiver.track,
-      streams,
-      transceiver: matchingTransceiver });
-    this.dispatchEvent(ev);
-
-    // Fire legacy event as well for a little bit.
-    ev = new pc._win.MediaStreamTrackEvent("addtrack",
-        { track: matchingTransceiver.receiver.track });
-    this.dispatchEvent(ev);
-  }
-
   onTransceiverNeeded(kind, transceiverImpl) {
     this._dompc._onTransceiverNeeded(kind, transceiverImpl);
   }
 
   notifyDataChannel(channel) {
     this.dispatchEvent(new this._dompc._win.RTCDataChannelEvent("datachannel",
                                                                 { channel }));
   }
@@ -2070,16 +2063,19 @@ setupPrototype(RTCRtpSender, {
 class RTCRtpReceiver {
   constructor(pc, transceiverImpl) {
     // We do not set the track here; that is done when _transceiverImpl is set
     Object.assign(this,
         {
           _pc: pc,
           _transceiverImpl: transceiverImpl,
           track: transceiverImpl.getReceiveTrack(),
+          _remoteSetSendBit: false,
+          _ontrackFired: false,
+          streamIds: [],
           // Sync and contributing sources must be kept cached so that timestamps
           // remain stable, as the timestamp offset can vary
           // note key = entry.source + entry.sourceType
           _rtpSources: new Map(),
           _rtpSourcesJsTimestamp: null,
         });
   }
 
@@ -2131,18 +2127,16 @@ class RTCRtpReceiver {
         removeKeys.push(entry.source + entry.sourceType);
       }
     }
     for (let delKey of removeKeys) {
       this._rtpSources.delete(delKey);
     }
   }
 
-
-
   _getRtpSourcesByType(type) {
     this._fetchRtpSources();
     // Only return the values from within the last 10 seconds as per the spec
     let cutoffTime = this._rtpSourcesJsTimestamp - 10 * 1000;
     let sources = [...this._rtpSources.values()].filter(
       (entry) => {
         return entry.sourceType == type &&
             (entry.timestamp + entry.sourceClockOffset) >= cutoffTime;
@@ -2157,16 +2151,77 @@ class RTCRtpReceiver {
   getContributingSources() {
     return this._getRtpSourcesByType("contributing");
   }
 
   getSynchronizationSources() {
     return this._getRtpSourcesByType("synchronization");
   }
 
+  setStreamIds(streamIds) {
+    this.streamIds = streamIds;
+  }
+
+  setRemoteSendBit(sendBit) {
+    this._remoteSetSendBit = sendBit;
+  }
+
+  processTrackAdditionsAndRemovals(transceiver,
+                                   {updateStreamFunctions, muteTracks, trackEvents}) {
+    let streamsWithTrack = this.streamIds
+      .map(id => this._pc._getOrCreateStream(id));
+
+    let streamsWithoutTrack = this._pc.getRemoteStreams()
+      .filter(s => !this.streamIds.includes(s.id));
+
+    updateStreamFunctions.push(...streamsWithTrack.map(stream => () => {
+      if (!stream.getTracks().includes(this.track)) {
+        stream.addTrack(this.track);
+        // Adding tracks from JS does not result in the stream getting
+        // onaddtrack, so we need to do that here.
+        stream.dispatchEvent(
+            new this._pc._win.MediaStreamTrackEvent(
+              "addtrack", { track: this.track }));
+      }
+    }));
+
+    updateStreamFunctions.push(...streamsWithoutTrack.map(stream => () => {
+      // Content JS might remove this track from the stream before this function fires (ugh)
+      if (stream.getTracks().includes(this.track)) {
+        stream.removeTrack(this.track);
+        // Removing tracks from JS does not result in the stream getting
+        // onremovetrack, so we need to do that here.
+        stream.dispatchEvent(
+            new this._pc._win.MediaStreamTrackEvent(
+              "removetrack", { track: this.track }));
+      }
+    }));
+
+    if (!this._remoteSetSendBit) {
+      // remote used "recvonly" or "inactive"
+      this._ontrackFired = false;
+      if (!this.track.muted) {
+        muteTracks.push(this.track);
+      }
+    } else if (!this._ontrackFired) {
+      // remote used "sendrecv" or "sendonly", and we haven't fired ontrack
+      let ev = new this._pc._win.RTCTrackEvent("track", {
+        receiver: this.__DOM_IMPL__,
+        track: this.track,
+        streams: streamsWithTrack,
+        transceiver });
+      trackEvents.push(ev);
+      this._ontrackFired = true;
+
+      // Fire legacy event as well for a little bit.
+      ev = new this._pc._win.MediaStreamTrackEvent("addtrack",
+          { track: this.track });
+      trackEvents.push(ev);
+    }
+  }
 }
 setupPrototype(RTCRtpReceiver, {
   classID: PC_RECEIVER_CID,
   contractID: PC_RECEIVER_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
 });
 
 class RTCRtpTransceiver {
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -90,9 +90,12 @@ interface MediaStreamTrack : EventTarget
 //  MediaTrackCapabilities getCapabilities ();
     MediaTrackConstraints  getConstraints ();
     [NeedsCallerType]
     MediaTrackSettings     getSettings ();
 
     [Throws, NeedsCallerType]
     Promise<void>          applyConstraints (optional MediaTrackConstraints constraints);
 //              attribute EventHandler          onoverconstrained;
+
+    [ChromeOnly]
+    void mutedChanged(boolean muted);
 };
--- a/dom/webidl/PeerConnectionObserver.webidl
+++ b/dom/webidl/PeerConnectionObserver.webidl
@@ -31,17 +31,16 @@ interface PeerConnectionObserver
   /* Data channel callbacks */
   void notifyDataChannel(DataChannel channel);
 
   /* Notification of one of several types of state changed */
   void onStateChange(PCObserverStateType state);
 
   /* Changes to MediaStreamTracks */
   void onRemoveStream(MediaStream stream);
-  void onTrack(DOMString webrtcTrackId, sequence<DOMString> streamIds);
 
   /* Transceiver management; called when setRemoteDescription causes a
      transceiver to be created on the C++ side */
   void onTransceiverNeeded(DOMString kind, TransceiverImpl transceiverImpl);
 
   /* DTMF callback */
   void onDTMFToneChange(MediaStreamTrack track, DOMString tone);
 
--- a/dom/webidl/RTCRtpReceiver.webidl
+++ b/dom/webidl/RTCRtpReceiver.webidl
@@ -9,9 +9,18 @@
 
 [Pref="media.peerconnection.enabled",
  JSImplementation="@mozilla.org/dom/rtpreceiver;1"]
 interface RTCRtpReceiver {
   readonly attribute MediaStreamTrack   track;
   Promise<RTCStatsReport>               getStats();
   sequence<RTCRtpContributingSource>    getContributingSources();
   sequence<RTCRtpSynchronizationSource> getSynchronizationSources();
+
+  [ChromeOnly]
+  void setStreamIds(sequence<DOMString> streamIds);
+  [ChromeOnly]
+  void setRemoteSendBit(boolean sendBit);
+  [ChromeOnly]
+  void processTrackAdditionsAndRemovals(
+      RTCRtpTransceiver transceiver,
+      object postProcessing);
 };
--- a/media/webrtc/signaling/gtest/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/gtest/jsep_session_unittest.cpp
@@ -508,23 +508,16 @@ protected:
   JsepTrack GetTrackOff(size_t index, SdpMediaSection::MediaType type) {
     return GetTrack(*mSessionOff, type, index);
   }
 
   JsepTrack GetTrackAns(size_t index, SdpMediaSection::MediaType type) {
     return GetTrack(*mSessionAns, type, index);
   }
 
-  size_t CountRtpTypes() const {
-    return std::count_if(
-        types.begin(), types.end(),
-        [](SdpMediaSection::MediaType type)
-          {return type != SdpMediaSection::MediaType::kApplication;});
-  }
-
   bool Equals(const SdpFingerprintAttributeList::Fingerprint& f1,
               const SdpFingerprintAttributeList::Fingerprint& f2) const {
     if (f1.hashFunc != f2.hashFunc) {
       return false;
     }
 
     if (f1.fingerprint != f2.fingerprint) {
       return false;
@@ -1634,57 +1627,37 @@ TEST_P(JsepSessionTest, GetDescriptions)
 
 TEST_P(JsepSessionTest, RenegotiationNoChange)
 {
   AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(CountRtpTypes(), added.size());
-  ASSERT_EQ(0U, removed.size());
-
   AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(CountRtpTypes(), added.size());
-  ASSERT_EQ(0U, removed.size());
-
   ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
 
   std::vector<RefPtr<JsepTransceiver>> origOffererTransceivers
     = DeepCopy(mSessionOff->GetTransceivers());
   std::vector<RefPtr<JsepTransceiver>> origAnswererTransceivers
     = DeepCopy(mSessionAns->GetTransceivers());
 
   std::string reoffer = CreateOffer();
   SetLocalOffer(reoffer);
   SetRemoteOffer(reoffer);
 
-  added = mSessionAns->GetRemoteTracksAdded();
-  removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
-
   std::string reanswer = CreateAnswer();
   SetLocalAnswer(reanswer);
   SetRemoteAnswer(reanswer);
 
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
-
   ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
 
   auto newOffererTransceivers = mSessionOff->GetTransceivers();
   auto newAnswererTransceivers = mSessionAns->GetTransceivers();
 
   ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers));
@@ -1693,57 +1666,37 @@ TEST_P(JsepSessionTest, RenegotiationNoC
 // Disabled: See Bug 1329028
 TEST_P(JsepSessionTest, DISABLED_RenegotiationSwappedRolesNoChange)
 {
   AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(types.size(), added.size());
-  ASSERT_EQ(0U, removed.size());
-
   AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(types.size(), added.size());
-  ASSERT_EQ(0U, removed.size());
-
   ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
 
   auto offererTransceivers = DeepCopy(mSessionOff->GetTransceivers());
   auto answererTransceivers = DeepCopy(mSessionAns->GetTransceivers());
 
   SwapOfferAnswerRoles();
 
   std::string reoffer = CreateOffer();
   SetLocalOffer(reoffer);
   SetRemoteOffer(reoffer);
 
-  added = mSessionAns->GetRemoteTracksAdded();
-  removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
-
   std::string reanswer = CreateAnswer();
   SetLocalAnswer(reanswer);
   SetRemoteAnswer(reanswer);
 
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
-
   ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kPassive);
 
   auto newOffererTransceivers = mSessionOff->GetTransceivers();
   auto newAnswererTransceivers = mSessionAns->GetTransceivers();
 
   ASSERT_TRUE(Equals(offererTransceivers, newAnswererTransceivers));
   ASSERT_TRUE(Equals(answererTransceivers, newOffererTransceivers));
@@ -1771,28 +1724,16 @@ TEST_P(JsepSessionTest, RenegotiationOff
   AddTracks(*mSessionOff, extraTypes);
   types.insert(types.end(), extraTypes.begin(), extraTypes.end());
 
   OfferAnswer(CHECK_SUCCESS);
 
   ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(2U, added.size());
-  ASSERT_EQ(0U, removed.size());
-  ASSERT_EQ(SdpMediaSection::kAudio, added[0].GetMediaType());
-  ASSERT_EQ(SdpMediaSection::kVideo, added[1].GetMediaType());
-
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
-
   auto newOffererTransceivers = mSessionOff->GetTransceivers();
   auto newAnswererTransceivers = mSessionAns->GetTransceivers();
 
   ASSERT_LE(2U, newOffererTransceivers.size());
   newOffererTransceivers.resize(newOffererTransceivers.size() - 2);
   ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
 
   ASSERT_LE(2U, newAnswererTransceivers.size());
@@ -1833,28 +1774,16 @@ TEST_P(JsepSessionTest, RenegotiationAns
 
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
   ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
-
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(2U, added.size());
-  ASSERT_EQ(0U, removed.size());
-  ASSERT_EQ(SdpMediaSection::kAudio, added[0].GetMediaType());
-  ASSERT_EQ(SdpMediaSection::kVideo, added[1].GetMediaType());
-
   auto newOffererTransceivers = mSessionOff->GetTransceivers();
   auto newAnswererTransceivers = mSessionAns->GetTransceivers();
 
   ASSERT_LE(2U, newOffererTransceivers.size());
   newOffererTransceivers.resize(newOffererTransceivers.size() - 2);
   ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
 
   ASSERT_LE(2U, newAnswererTransceivers.size());
@@ -1884,30 +1813,16 @@ TEST_P(JsepSessionTest, RenegotiationBot
   AddTracks(*mSessionOff, extraTypes);
   types.insert(types.end(), extraTypes.begin(), extraTypes.end());
 
   OfferAnswer(CHECK_SUCCESS);
 
   ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(2U, added.size());
-  ASSERT_EQ(0U, removed.size());
-  ASSERT_EQ(SdpMediaSection::kAudio, added[0].GetMediaType());
-  ASSERT_EQ(SdpMediaSection::kVideo, added[1].GetMediaType());
-
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(2U, added.size());
-  ASSERT_EQ(0U, removed.size());
-  ASSERT_EQ(SdpMediaSection::kAudio, added[0].GetMediaType());
-  ASSERT_EQ(SdpMediaSection::kVideo, added[1].GetMediaType());
-
   auto newOffererTransceivers = mSessionOff->GetTransceivers();
   auto newAnswererTransceivers = mSessionAns->GetTransceivers();
 
   ASSERT_LE(2U, newOffererTransceivers.size());
   newOffererTransceivers.resize(newOffererTransceivers.size() - 2);
   ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
 
   ASSERT_LE(2U, newAnswererTransceivers.size());
@@ -1981,42 +1896,33 @@ TEST_P(JsepSessionTest, RenegotiationOff
     return;
   }
 
   OfferAnswer();
 
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
 
-  JsepTransceiver* transceiver = GetNegotiatedTransceiver(*mSessionOff, 0);
+  RefPtr<JsepTransceiver> transceiver =
+    GetNegotiatedTransceiver(*mSessionOff, 0);
   ASSERT_TRUE(transceiver);
   std::string streamId = transceiver->mSendTrack.GetStreamIds()[0];
   std::string trackId = transceiver->mSendTrack.GetTrackId();
   std::string msidToReplace("a=msid:");
   msidToReplace += streamId;
   msidToReplace += " ";
   msidToReplace += trackId;
   size_t msidOffset = offer.find(msidToReplace);
   ASSERT_NE(std::string::npos, msidOffset);
   offer.replace(msidOffset, msidToReplace.size(), "a=msid:foo bar");
 
   SetRemoteOffer(offer);
-
-  std::vector<JsepTrack> removedTracks = mSessionAns->GetRemoteTracksRemoved();
-  std::vector<JsepTrack> addedTracks = mSessionAns->GetRemoteTracksAdded();
-
-  ASSERT_EQ(1U, removedTracks.size());
-  ASSERT_FALSE(IsNull(removedTracks[0]));
-  ASSERT_EQ(streamId, removedTracks[0].GetStreamIds()[0]);
-  ASSERT_EQ(trackId, removedTracks[0].GetTrackId());
-
-  ASSERT_EQ(1U, addedTracks.size());
-  ASSERT_FALSE(IsNull(addedTracks[0]));
-  ASSERT_EQ("foo", addedTracks[0].GetStreamIds()[0]);
-  ASSERT_EQ("bar", addedTracks[0].GetTrackId());
+  transceiver = GetNegotiatedTransceiver(*mSessionAns, 0);
+  ASSERT_EQ("foo", transceiver->mRecvTrack.GetStreamIds()[0]);
+  ASSERT_EQ("bar", transceiver->mRecvTrack.GetTrackId());
 
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 }
 
 // The JSEP draft explicitly forbids changing the msid on an m-section, but
 // that is a new restriction that older versions of Firefox do not follow.
@@ -2031,42 +1937,34 @@ TEST_P(JsepSessionTest, RenegotiationAns
   OfferAnswer();
 
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 
-  JsepTransceiver* transceiver = GetNegotiatedTransceiver(*mSessionAns, 0);
+  RefPtr<JsepTransceiver> transceiver =
+    GetNegotiatedTransceiver(*mSessionAns, 0);
   ASSERT_TRUE(transceiver);
   std::string streamId = transceiver->mSendTrack.GetStreamIds()[0];
   std::string trackId = transceiver->mSendTrack.GetTrackId();
   std::string msidToReplace("a=msid:");
   msidToReplace += streamId;
   msidToReplace += " ";
   msidToReplace += trackId;
   size_t msidOffset = answer.find(msidToReplace);
   ASSERT_NE(std::string::npos, msidOffset);
   answer.replace(msidOffset, msidToReplace.size(), "a=msid:foo bar");
 
   SetRemoteAnswer(answer);
 
-  std::vector<JsepTrack> removedTracks = mSessionOff->GetRemoteTracksRemoved();
-  std::vector<JsepTrack> addedTracks = mSessionOff->GetRemoteTracksAdded();
-
-  ASSERT_EQ(1U, removedTracks.size());
-  ASSERT_FALSE(IsNull(removedTracks[0]));
-  ASSERT_EQ(streamId, removedTracks[0].GetStreamIds()[0]);
-  ASSERT_EQ(trackId, removedTracks[0].GetTrackId());
-
-  ASSERT_EQ(1U, addedTracks.size());
-  ASSERT_FALSE(IsNull(addedTracks[0]));
-  ASSERT_EQ("foo", addedTracks[0].GetStreamIds()[0]);
-  ASSERT_EQ("bar", addedTracks[0].GetTrackId());
+  transceiver = GetNegotiatedTransceiver(*mSessionOff, 0);
+  ASSERT_EQ("foo", transceiver->mRecvTrack.GetStreamIds()[0]);
+  ASSERT_EQ("bar", transceiver->mRecvTrack.GetTrackId());
 }
 
 TEST_P(JsepSessionTest, RenegotiationOffererStopsTransceiver)
 {
   AddTracks(*mSessionOff);
   AddTracks(*mSessionAns);
   if (types.back() == SdpMediaSection::kApplication) {
     return;
@@ -2080,30 +1978,16 @@ TEST_P(JsepSessionTest, RenegotiationOff
     DeepCopy(mSessionAns->GetTransceivers());
 
   // Avoid bundle transport side effects; don't stop the BUNDLE-tag!
   mSessionOff->GetTransceivers().back()->Stop();
   JsepTrack removedTrack(mSessionOff->GetTransceivers().back()->mSendTrack);
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(1U, removed.size());
-
-  ASSERT_EQ(removedTrack.GetMediaType(), removed[0].GetMediaType());
-  ASSERT_EQ(removedTrack.GetStreamIds(), removed[0].GetStreamIds());
-  ASSERT_EQ(removedTrack.GetTrackId(), removed[0].GetTrackId());
-
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(1U, removed.size());
-
   // Last m-section should be disabled
   auto offer = GetParsedLocalDescription(*mSessionOff);
   const SdpMediaSection* msection =
     &offer->GetMediaSection(offer->GetMediaSectionCount() - 1);
   ASSERT_TRUE(msection);
   ValidateDisabledMSection(msection);
 
   // Last m-section should be disabled
@@ -2149,30 +2033,16 @@ TEST_P(JsepSessionTest, RenegotiationAns
     = DeepCopy(mSessionAns->GetTransceivers());
 
   // Avoid bundle transport side effects; don't stop the BUNDLE-tag!
   mSessionAns->GetTransceivers().back()->Stop();
   JsepTrack removedTrack(mSessionAns->GetTransceivers().back()->mSendTrack);
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
-
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(1U, removed.size());
-
-  ASSERT_EQ(removedTrack.GetMediaType(), removed[0].GetMediaType());
-  ASSERT_EQ(removedTrack.GetStreamIds(), removed[0].GetStreamIds());
-  ASSERT_EQ(removedTrack.GetTrackId(), removed[0].GetTrackId());
-
   // Last m-section should be sendrecv
   auto offer = GetParsedLocalDescription(*mSessionOff);
   const SdpMediaSection* msection =
     &offer->GetMediaSection(offer->GetMediaSectionCount() - 1);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_TRUE(msection->IsSending());
 
@@ -2218,34 +2088,16 @@ TEST_P(JsepSessionTest, RenegotiationBot
   // Avoid bundle transport side effects; don't stop the BUNDLE-tag!
   mSessionOff->GetTransceivers().back()->Stop();
   JsepTrack removedTrackOffer(mSessionOff->GetTransceivers().back()->mSendTrack);
   mSessionAns->GetTransceivers().back()->Stop();
   JsepTrack removedTrackAnswer(mSessionAns->GetTransceivers().back()->mSendTrack);
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(1U, removed.size());
-
-  ASSERT_EQ(removedTrackOffer.GetMediaType(), removed[0].GetMediaType());
-  ASSERT_EQ(removedTrackOffer.GetStreamIds(), removed[0].GetStreamIds());
-  ASSERT_EQ(removedTrackOffer.GetTrackId(), removed[0].GetTrackId());
-
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(1U, removed.size());
-
-  ASSERT_EQ(removedTrackAnswer.GetMediaType(), removed[0].GetMediaType());
-  ASSERT_EQ(removedTrackAnswer.GetStreamIds(), removed[0].GetStreamIds());
-  ASSERT_EQ(removedTrackAnswer.GetTrackId(), removed[0].GetTrackId());
-
   // Last m-section should be disabled
   auto offer = GetParsedLocalDescription(*mSessionOff);
   const SdpMediaSection* msection =
     &offer->GetMediaSection(offer->GetMediaSectionCount() - 1);
   ASSERT_TRUE(msection);
   ValidateDisabledMSection(msection);
 
   // Last m-section should be disabled
@@ -2300,28 +2152,16 @@ TEST_P(JsepSessionTest, RenegotiationBot
   std::vector<SdpMediaSection::MediaType> extraTypes;
   extraTypes.push_back(removedType);
   AddTracks(*mSessionAns, extraTypes);
   AddTracks(*mSessionOff, extraTypes);
   types.insert(types.end(), extraTypes.begin(), extraTypes.end());
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(1U, added.size());
-  ASSERT_EQ(0U, removed.size());
-  ASSERT_EQ(removedType, added[0].GetMediaType());
-
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(1U, added.size());
-  ASSERT_EQ(0U, removed.size());
-  ASSERT_EQ(removedType, added[0].GetMediaType());
-
   auto newOffererTransceivers = mSessionOff->GetTransceivers();
   auto newAnswererTransceivers = mSessionAns->GetTransceivers();
 
   ASSERT_EQ(origOffererTransceivers.size() + 1, newOffererTransceivers.size());
   ASSERT_EQ(origAnswererTransceivers.size() + 1,
             newAnswererTransceivers.size());
 
   // Ensure that the m-section was re-used; no gaps
@@ -2347,26 +2187,18 @@ TEST_P(JsepSessionTest, RenegotiationBot
   }
 
   OfferAnswer();
 
   mSessionOff->GetTransceivers()[0]->Stop();
   mSessionOff->GetTransceivers()[1]->Stop();
 
   OfferAnswer(CHECK_SUCCESS);
-
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(2U, removed.size());
-
-  added = mSessionOff->GetRemoteTracksAdded();
-  removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(2U, removed.size());
+  ASSERT_TRUE(mSessionAns->GetTransceivers()[0]->IsStopped());
+  ASSERT_TRUE(mSessionAns->GetTransceivers()[1]->IsStopped());
 }
 
 TEST_P(JsepSessionTest, RenegotiationOffererReplacesTrack)
 {
   AddTracks(*mSessionOff);
   AddTracks(*mSessionAns);
 
   if (types.front() == SdpMediaSection::kApplication) {
@@ -2377,20 +2209,20 @@ TEST_P(JsepSessionTest, RenegotiationOff
 
   mSessionOff->GetTransceivers()[0]->mSendTrack.UpdateTrackIds(
       std::vector<std::string>(1, "newstream"), "newtrack");
 
   OfferAnswer(CHECK_SUCCESS);
 
   // Latest JSEP spec says the msid never changes, so the other side will not
   // notice track replacement.
-  auto added = mSessionAns->GetRemoteTracksAdded();
-  auto removed = mSessionAns->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
+  ASSERT_NE("newtrack",
+      mSessionAns->GetTransceivers()[0]->mRecvTrack.GetTrackId());
+  ASSERT_NE("newstream",
+      mSessionAns->GetTransceivers()[0]->mRecvTrack.GetStreamIds()[0]);
 }
 
 TEST_P(JsepSessionTest, RenegotiationAnswererReplacesTrack)
 {
   AddTracks(*mSessionOff);
   AddTracks(*mSessionAns);
 
   if (types.front() == SdpMediaSection::kApplication) {
@@ -2401,20 +2233,20 @@ TEST_P(JsepSessionTest, RenegotiationAns
 
   mSessionAns->GetTransceivers()[0]->mSendTrack.UpdateTrackIds(
       std::vector<std::string>(1, "newstream"), "newtrack");
 
   OfferAnswer(CHECK_SUCCESS);
 
   // Latest JSEP spec says the msid never changes, so the other side will not
   // notice track replacement.
-  auto added = mSessionOff->GetRemoteTracksAdded();
-  auto removed = mSessionOff->GetRemoteTracksRemoved();
-  ASSERT_EQ(0U, added.size());
-  ASSERT_EQ(0U, removed.size());
+  ASSERT_NE("newtrack",
+      mSessionOff->GetTransceivers()[0]->mRecvTrack.GetTrackId());
+  ASSERT_NE("newstream",
+      mSessionOff->GetTransceivers()[0]->mRecvTrack.GetStreamIds()[0]);
 }
 
 // Tests whether auto-assigned remote msids (ie; what happens when the other
 // side doesn't use msid attributes) are stable across renegotiation.
 TEST_P(JsepSessionTest, RenegotiationAutoAssignedMsidIsStable)
 {
   AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
@@ -4739,17 +4571,19 @@ TEST_P(JsepSessionTest, TestRejectOfferR
 
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 
   ASSERT_EQ(NS_OK,
             mSessionAns->SetRemoteDescription(kJsepSdpRollback, ""));
   ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
-  ASSERT_EQ(CountRtpTypes(), mSessionAns->GetRemoteTracksRemoved().size());
+  for (const auto& transceiver : mSessionAns->GetTransceivers()) {
+    ASSERT_EQ(0U, transceiver->mRecvTrack.GetStreamIds().size());
+  }
 
   ASSERT_EQ(NS_OK,
             mSessionOff->SetLocalDescription(kJsepSdpRollback, ""));
   ASSERT_EQ(kJsepStateStable, mSessionOff->GetState());
 
   OfferAnswer();
 }
 
--- a/media/webrtc/signaling/src/jsep/JsepSession.h
+++ b/media/webrtc/signaling/src/jsep/JsepSession.h
@@ -127,20 +127,16 @@ public:
   {
     std::stable_sort(Codecs().begin(), Codecs().end(), sorter);
     for (auto& transceiver : GetTransceivers()) {
       transceiver->mSendTrack.SortCodecs(sorter);
       transceiver->mRecvTrack.SortCodecs(sorter);
     }
   }
 
-  // Helpful for firing events.
-  virtual std::vector<JsepTrack> GetRemoteTracksAdded() const = 0;
-  virtual std::vector<JsepTrack> GetRemoteTracksRemoved() const = 0;
-
   virtual const std::vector<RefPtr<JsepTransceiver>>&
     GetTransceivers() const = 0;
   virtual std::vector<RefPtr<JsepTransceiver>>& GetTransceivers() = 0;
   virtual nsresult AddTransceiver(RefPtr<JsepTransceiver> transceiver) = 0;
 
   // Basic JSEP operations.
   virtual nsresult CreateOffer(const JsepOfferOptions& options,
                                std::string* offer) = 0;
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -183,28 +183,16 @@ JsepSessionImpl::AddAudioRtpExtension(co
 
 nsresult
 JsepSessionImpl::AddVideoRtpExtension(const std::string& extensionName,
                                       SdpDirectionAttribute::Direction direction)
 {
   return AddRtpExtension(mVideoRtpExtensions, extensionName, direction);
 }
 
-std::vector<JsepTrack>
-JsepSessionImpl::GetRemoteTracksAdded() const
-{
-  return mRemoteTracksAdded;
-}
-
-std::vector<JsepTrack>
-JsepSessionImpl::GetRemoteTracksRemoved() const
-{
-  return mRemoteTracksRemoved;
-}
-
 nsresult
 JsepSessionImpl::CreateOfferMsection(const JsepOfferOptions& options,
                                      JsepTransceiver& transceiver,
                                      Sdp* local)
 {
   JsepTrack& sendTrack(transceiver.mSendTrack);
   JsepTrack& recvTrack(transceiver.mRecvTrack);
 
@@ -1380,22 +1368,16 @@ JsepSessionImpl::SetRemoteDescriptionAns
   mCurrentLocalDescription = Move(mPendingLocalDescription);
   MOZ_ASSERT(mIsOfferer);
   mWasOffererLastTime = true;
 
   SetState(kJsepStateStable);
   return NS_OK;
 }
 
-static bool
-TrackIdCompare(const JsepTrack& t1, const JsepTrack& t2)
-{
-  return t1.GetTrackId() < t2.GetTrackId();
-}
-
 JsepTransceiver*
 JsepSessionImpl::GetTransceiverForLevel(size_t level)
 {
   for (RefPtr<JsepTransceiver>& transceiver : mTransceivers) {
     if (transceiver->HasLevel() && (transceiver->GetLevel() == level)) {
       return transceiver.get();
     }
   }
@@ -1465,88 +1447,53 @@ JsepSessionImpl::GetTransceiverForRemote
   nsresult rv = AddTransceiver(newTransceiver);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return mTransceivers.back().get();
 }
 
 nsresult
 JsepSessionImpl::UpdateTransceiversFromRemoteDescription(const Sdp& remote)
 {
-  std::vector<JsepTrack> oldRemoteTracks;
-  std::vector<JsepTrack> newRemoteTracks;
-
   // Iterate over the sdp, updating remote tracks as we go
   for (size_t i = 0; i < remote.GetMediaSectionCount(); ++i) {
     const SdpMediaSection& msection = remote.GetMediaSection(i);
 
     JsepTransceiver* transceiver(GetTransceiverForRemote(msection));
     if (!transceiver) {
       return NS_ERROR_FAILURE;
     }
 
-    bool isRtp =
-      msection.GetMediaType() != SdpMediaSection::MediaType::kApplication;
-
-    if (isRtp && transceiver->mRecvTrack.GetActive()) {
-      oldRemoteTracks.push_back(transceiver->mRecvTrack);
-    }
-
     if (!mSdpHelper.MsectionIsDisabled(msection)) {
       transceiver->Associate(msection.GetAttributeList().GetMid());
     } else {
       transceiver->Disassociate();
       // This cannot be rolled back.
       transceiver->Stop();
       continue;
     }
 
-    if (!isRtp) {
+    if (msection.GetMediaType() == SdpMediaSection::MediaType::kApplication) {
       continue;
     }
 
     // Interop workaround for endpoints that don't support msid.
-    // If the receiver has no ids, set some initial values, one way or another.
+    // Ensures that there is a default track id set.
+    // TODO(bug 1426005): Remove this
     if (msection.IsSending() && transceiver->mRecvTrack.GetTrackId().empty()) {
       std::vector<std::string> streamIds;
       std::string trackId;
 
       nsresult rv = GetRemoteIds(remote, msection, &streamIds, &trackId);
       NS_ENSURE_SUCCESS(rv, rv);
       transceiver->mRecvTrack.UpdateTrackIds(streamIds, trackId);
     }
 
     transceiver->mRecvTrack.UpdateRecvTrack(remote, msection);
-
-    if (msection.IsSending()) {
-      newRemoteTracks.push_back(transceiver->mRecvTrack);
-    }
   }
 
-  std::sort(oldRemoteTracks.begin(), oldRemoteTracks.end(), TrackIdCompare);
-  std::sort(newRemoteTracks.begin(), newRemoteTracks.end(), TrackIdCompare);
-
-  mRemoteTracksAdded.clear();
-  mRemoteTracksRemoved.clear();
-
-  std::set_difference(
-      oldRemoteTracks.begin(),
-      oldRemoteTracks.end(),
-      newRemoteTracks.begin(),
-      newRemoteTracks.end(),
-      std::inserter(mRemoteTracksRemoved, mRemoteTracksRemoved.begin()),
-      TrackIdCompare);
-
-  std::set_difference(
-      newRemoteTracks.begin(),
-      newRemoteTracks.end(),
-      oldRemoteTracks.begin(),
-      oldRemoteTracks.end(),
-      std::inserter(mRemoteTracksAdded, mRemoteTracksAdded.begin()),
-      TrackIdCompare);
-
   return NS_OK;
 }
 
 
 bool
 JsepSessionImpl::WasMsectionDisabledLastNegotiation(size_t level) const
 {
   const Sdp* answer(GetAnswer());
@@ -1599,36 +1546,34 @@ JsepSessionImpl::RollbackRemoteOffer()
   for (size_t i = 0; i < mTransceivers.size(); ++i) {
     RefPtr<JsepTransceiver>& transceiver(mTransceivers[i]);
     if (i < mOldTransceivers.size()) {
       transceiver->Rollback(*mOldTransceivers[i]);
       continue;
     }
 
     // New transceiver!
-    if (!transceiver->HasAddTrackMagic() &&
-        transceiver->WasCreatedBySetRemote()) {
+    bool shouldRemove = !transceiver->HasAddTrackMagic() &&
+                        transceiver->WasCreatedBySetRemote();
+
+    // We rollback even for transceivers we will remove, just to ensure we end
+    // up at the starting state.
+    RefPtr<JsepTransceiver> temp(
+        new JsepTransceiver(transceiver->GetMediaType()));
+    transceiver->Rollback(*temp);
+
+    if (shouldRemove) {
       transceiver->Stop();
-      transceiver->Disassociate();
-      transceiver->ClearLevel();
       transceiver->SetRemoved();
       mTransceivers.erase(mTransceivers.begin() + i);
       --i;
-      continue;
     }
-
-    // Transceiver has been "touched" by addTrack; let it live, but unhook it
-    // from everything.
-    RefPtr<JsepTransceiver> temp(
-        new JsepTransceiver(transceiver->GetMediaType()));
-    transceiver->Rollback(*temp);
   }
 
   mOldTransceivers.clear();
-  std::swap(mRemoteTracksAdded, mRemoteTracksRemoved);
 }
 
 nsresult
 JsepSessionImpl::ValidateLocalDescription(const Sdp& description)
 {
   // TODO(bug 1095226): Better checking.
   if (!mGeneratedLocalDescription) {
     JSEP_SET_ERROR("Calling SetLocal without first calling CreateOffer/Answer"
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
@@ -90,20 +90,16 @@ public:
       SdpDirectionAttribute::Direction::kSendrecv) override;
 
   virtual std::vector<JsepCodecDescription*>&
   Codecs() override
   {
     return mSupportedCodecs.values;
   }
 
-  virtual std::vector<JsepTrack> GetRemoteTracksAdded() const override;
-
-  virtual std::vector<JsepTrack> GetRemoteTracksRemoved() const override;
-
   virtual nsresult CreateOffer(const JsepOfferOptions& options,
                                std::string* offer) override;
 
   virtual nsresult CreateAnswer(const JsepAnswerOptions& options,
                                 std::string* answer) override;
 
   virtual std::string GetLocalDescription(JsepDescriptionPendingOrCurrent type)
                                           const override;
@@ -248,19 +244,16 @@ private:
   nsresult EnableOfferMsection(SdpMediaSection* msection);
 
   mozilla::Sdp* GetParsedLocalDescription(JsepDescriptionPendingOrCurrent type)
                                           const;
   mozilla::Sdp* GetParsedRemoteDescription(JsepDescriptionPendingOrCurrent type)
                                            const;
   const Sdp* GetAnswer() const;
 
-  // By the most recent SetRemoteDescription
-  std::vector<JsepTrack> mRemoteTracksAdded;
-  std::vector<JsepTrack> mRemoteTracksRemoved;
   // !!!NOT INDEXED BY LEVEL!!! These are in the order they were created in. The
   // level mapping is done with JsepTransceiver::mLevel.
   std::vector<RefPtr<JsepTransceiver>> mTransceivers;
   // So we can rollback. Not as simple as just going back to the old, though...
   std::vector<RefPtr<JsepTransceiver>> mOldTransceivers;
 
   bool mIsOfferer;
   bool mWasOffererLastTime;
--- a/media/webrtc/signaling/src/jsep/JsepTrack.h
+++ b/media/webrtc/signaling/src/jsep/JsepTrack.h
@@ -93,17 +93,18 @@ private:
 
 class JsepTrack
 {
 public:
   JsepTrack(mozilla::SdpMediaSection::MediaType type,
             sdp::Direction direction)
       : mType(type),
         mDirection(direction),
-        mActive(false)
+        mActive(false),
+        mRemoteSetSendBit(false)
   {
   }
 
   virtual ~JsepTrack() {}
 
   void UpdateTrackIds(const std::vector<std::string>& streamIds,
                       const std::string& trackId)
   {
@@ -120,18 +121,22 @@ public:
   void UpdateRecvTrack(const Sdp& sdp, const SdpMediaSection& msection)
   {
     MOZ_ASSERT(mDirection == sdp::kRecv);
     MOZ_ASSERT(
         msection.GetMediaType() != SdpMediaSection::MediaType::kApplication);
     std::string error;
     SdpHelper helper(&error);
 
+    mRemoteSetSendBit = msection.IsSending();
+
     if (msection.IsSending()) {
       (void)helper.GetIdsFromMsid(sdp, msection, &mStreamIds, &mTrackId);
+    } else {
+      mStreamIds.clear();
     }
 
     // We do this whether or not the track is active
     SetCNAME(helper.GetCNAME(msection));
     mSsrcs.clear();
     if (msection.GetAttributeList().HasAttribute(
           SdpAttribute::kSsrcAttribute)) {
       for (auto& ssrcAttr : msection.GetAttributeList().GetSsrc().mSsrcs) {
@@ -154,16 +159,17 @@ public:
       mType = rhs.mType;
       mStreamIds = rhs.mStreamIds;
       mTrackId = rhs.mTrackId;
       mCNAME = rhs.mCNAME;
       mDirection = rhs.mDirection;
       mJsEncodeConstraints = rhs.mJsEncodeConstraints;
       mSsrcs = rhs.mSsrcs;
       mActive = rhs.mActive;
+      mRemoteSetSendBit = rhs.mRemoteSetSendBit;
 
       for (const JsepCodecDescription* codec : rhs.mPrototypeCodecs.values) {
         mPrototypeCodecs.values.push_back(codec->Clone());
       }
       if (rhs.mNegotiatedDetails) {
         mNegotiatedDetails.reset(
           new JsepTrackNegotiatedDetails(*rhs.mNegotiatedDetails));
       }
@@ -222,16 +228,22 @@ public:
   }
 
   void
   SetActive(bool active)
   {
     mActive = active;
   }
 
+  bool
+  GetRemoteSetSendBit() const
+  {
+    return mRemoteSetSendBit;
+  }
+
   virtual void PopulateCodecs(
       const std::vector<JsepCodecDescription*>& prototype);
 
   template <class UnaryFunction>
   void ForEachCodec(UnaryFunction func)
   {
     std::for_each(mPrototypeCodecs.values.begin(),
                   mPrototypeCodecs.values.end(), func);
@@ -345,13 +357,14 @@ private:
   PtrVector<JsepCodecDescription> mPrototypeCodecs;
   // Holds encoding params/constraints from JS. Simulcast happens when there are
   // multiple of these. If there are none, we assume unconstrained unicast with
   // no rid.
   std::vector<JsConstraints> mJsEncodeConstraints;
   UniquePtr<JsepTrackNegotiatedDetails> mNegotiatedDetails;
   std::vector<uint32_t> mSsrcs;
   bool mActive;
+  bool mRemoteSetSendBit;
 };
 
 } // namespace mozilla
 
 #endif
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1769,46 +1769,16 @@ 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());
   }
 }
 
-void
-PeerConnectionImpl::FireOnTrackEvents(RefPtr<PeerConnectionObserver>& aPco)
-{
-  for (auto& track : mJsepSession->GetRemoteTracksAdded()) {
-    if (track.GetMediaType() == mozilla::SdpMediaSection::kApplication) {
-      // Ignore datachannel
-      continue;
-    }
-
-    MOZ_ASSERT(!track.GetTrackId().empty());
-
-    nsString trackId = NS_ConvertUTF8toUTF16(track.GetTrackId().c_str());
-
-    dom::Sequence<nsString> streamIds;
-    for (const std::string& streamId : track.GetStreamIds()) {
-      // If this fails, oh well.
-      streamIds.AppendElement(
-          NS_ConvertASCIItoUTF16(streamId.c_str()), fallible);
-    }
-
-    JSErrorResult jrv;
-    aPco->OnTrack(trackId, streamIds, jrv);
-    if (jrv.Failed()) {
-      CSFLogError(LOGTAG, ": OnTrack(%s) failed! Error: %u",
-          track.GetTrackId().c_str(),
-          jrv.ErrorCodeAsInt());
-    }
-  }
-}
-
 NS_IMETHODIMP
 PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   if (!aSDP) {
     CSFLogError(LOGTAG, "%s - aSDP is NULL", __FUNCTION__);
     return NS_ERROR_FAILURE;
@@ -1925,21 +1895,16 @@ PeerConnectionImpl::SetRemoteDescription
                     __FUNCTION__, mHandle.c_str(), static_cast<int>(rv));
         MOZ_CRASH();
         return NS_ERROR_FAILURE;
       }
     }
 
     UpdateSignalingState(sdpType == mozilla::kJsepSdpRollback);
 
-    // This needs to be done before we fire ontrack events
-    pco->SyncTransceivers(jrv);
-
-    FireOnTrackEvents(pco);
-
     pco->OnSetRemoteDescriptionSuccess(jrv);
 
     startCallTelem();
   }
 
   return NS_OK;
 }
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -356,18 +356,16 @@ public:
 
   NS_IMETHODIMP SetLocalDescription (int32_t aAction, const char* aSDP);
 
   void SetLocalDescription (int32_t aAction, const nsAString& aSDP, ErrorResult &rv)
   {
     rv = SetLocalDescription(aAction, NS_ConvertUTF16toUTF8(aSDP).get());
   }
 
-  void FireOnTrackEvents(RefPtr<PeerConnectionObserver>& aPco);
-
   NS_IMETHODIMP SetRemoteDescription (int32_t aAction, const char* aSDP);
 
   void SetRemoteDescription (int32_t aAction, const nsAString& aSDP, ErrorResult &rv)
   {
     rv = SetRemoteDescription(aAction, NS_ConvertUTF16toUTF8(aSDP).get());
   }
 
   NS_IMETHODIMP_TO_ERRORRESULT(GetStats, ErrorResult &rv,
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
@@ -517,31 +517,45 @@ TransceiverImpl::SyncWithJS(dom::RTCRtpT
     } else {
       if (mJsepTransceiver->mSendTrack.GetActive()) {
         aJsTransceiver.SetCurrentDirection(
             dom::RTCRtpTransceiverDirection::Sendonly, aRv);
       } else {
         aJsTransceiver.SetCurrentDirection(
             dom::RTCRtpTransceiverDirection::Inactive, aRv);
       }
-
-      // If negotiation stops a track from receiving (ie; m-section is
-      // negotiated "sendonly" or "inactive"), we mark the track muted.  We do
-      // _not_ do the reverse; we need to wait for RTP to unmute according to
-      // the spec. That happens in MediaPipeline.
-      if (!mReceiveTrack->Muted()) {
-        mReceiveTrack->MutedChanged(true);
-      }
     }
 
     if (aRv.Failed()) {
       return;
     }
   }
 
+  RefPtr<dom::RTCRtpReceiver> receiver = aJsTransceiver.GetReceiver(aRv);
+  if (aRv.Failed()) {
+    return;
+  }
+
+  // receive stream ids from JSEP
+  dom::Sequence<nsString> receiveStreamIds;
+  for (const auto& id : mJsepTransceiver->mRecvTrack.GetStreamIds()) {
+    receiveStreamIds.AppendElement(NS_ConvertUTF8toUTF16(id.c_str()),
+                                   fallible);
+  }
+  receiver->SetStreamIds(receiveStreamIds, aRv);
+  if (aRv.Failed()) {
+    return;
+  }
+
+  receiver->SetRemoteSendBit(mJsepTransceiver->mRecvTrack.GetRemoteSetSendBit(),
+                             aRv);
+  if (aRv.Failed()) {
+    return;
+  }
+
   // AddTrack magic from JS
   if (aJsTransceiver.GetAddTrackMagic(aRv)) {
     mJsepTransceiver->SetAddTrackMagic();
   }
 
   if (aRv.Failed()) {
     return;
   }