Bug 1470568: Remove the rid= and pt= stuff from our simulcast parsing. Also, handle paused rids correctly. r=mjf
authorByron Campen [:bwc] <docfaraday@gmail.com>
Mon, 04 Nov 2019 15:04:14 +0000
changeset 500389 8c4bc326f3c78c9620b4c9114c0c744c1cab802a
parent 500388 cbdf479b1cdc01ded34cb088c4976eb8b8bd94d5
child 500390 f54c4d6a55df2d3fdbdb0dbe39ff5165497fed37
push id36763
push userrmaries@mozilla.com
push dateMon, 04 Nov 2019 21:44:06 +0000
treeherdermozilla-central@75a7a3400888 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmjf
bugs1470568
milestone72.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 1470568: Remove the rid= and pt= stuff from our simulcast parsing. Also, handle paused rids correctly. r=mjf Differential Revision: https://phabricator.services.mozilla.com/D47658
dom/media/tests/mochitest/sdpUtils.js
media/webrtc/signaling/gtest/jsep_track_unittest.cpp
media/webrtc/signaling/gtest/sdp_unittests.cpp
media/webrtc/signaling/src/jsep/JsepTrack.cpp
media/webrtc/signaling/src/jsep/JsepTrack.h
media/webrtc/signaling/src/jsep/JsepTrackEncoding.h
media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
media/webrtc/signaling/src/sdp/SdpAttribute.cpp
media/webrtc/signaling/src/sdp/SdpAttribute.h
media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp
--- a/dom/media/tests/mochitest/sdpUtils.js
+++ b/dom/media/tests/mochitest/sdpUtils.js
@@ -241,22 +241,21 @@ var sdputils = {
       );
   },
 
   transferSimulcastProperties(offer_sdp, answer_sdp) {
     if (!offer_sdp.includes("a=simulcast:")) {
       return answer_sdp;
     }
     ok(
-      offer_sdp.includes("a=simulcast: send rid"),
+      offer_sdp.includes("a=simulcast:send "),
       "Offer contains simulcast attribute"
     );
-    var o_simul = offer_sdp.match(/simulcast: send rid=(.*)([\n$])*/i);
-    var new_answer_sdp =
-      answer_sdp + "a=simulcast: recv rid=" + o_simul[1] + "\r\n";
+    var o_simul = offer_sdp.match(/simulcast:send (.*)([\n$])*/i);
+    var new_answer_sdp = answer_sdp + "a=simulcast:recv " + o_simul[1] + "\r\n";
     ok(offer_sdp.includes("a=rid:"), "Offer contains RID attribute");
     var o_rids = offer_sdp.match(/a=rid:(.*)/gi);
     o_rids.forEach(o_rid => {
       new_answer_sdp = new_answer_sdp + o_rid.replace(/send/, "recv") + "\r\n";
     });
     var extmap_id = offer_sdp.match(
       "a=extmap:([0-9+])/sendonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"
     );
--- a/media/webrtc/signaling/gtest/jsep_track_unittest.cpp
+++ b/media/webrtc/signaling/gtest/jsep_track_unittest.cpp
@@ -1195,19 +1195,19 @@ TEST_F(JsepTrackTest, SimulcastOfferer) 
   ASSERT_EQ(2U, mSendOff.GetNegotiatedDetails()->GetEncodingCount());
   ASSERT_EQ("foo", mSendOff.GetNegotiatedDetails()->GetEncoding(0).mRid);
   ASSERT_EQ(40000U,
             mSendOff.GetNegotiatedDetails()->GetEncoding(0).mConstraints.maxBr);
   ASSERT_EQ("bar", mSendOff.GetNegotiatedDetails()->GetEncoding(1).mRid);
   ASSERT_EQ(10000U,
             mSendOff.GetNegotiatedDetails()->GetEncoding(1).mConstraints.maxBr);
   ASSERT_NE(std::string::npos,
-            mOffer->ToString().find("a=simulcast: send rid=foo;bar"));
+            mOffer->ToString().find("a=simulcast:send foo;bar"));
   ASSERT_NE(std::string::npos,
-            mAnswer->ToString().find("a=simulcast: recv rid=foo;bar"));
+            mAnswer->ToString().find("a=simulcast:recv foo;bar"));
   ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:foo send"));
   ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:bar send"));
   ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:foo recv"));
   ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:bar recv"));
 }
 
 TEST_F(JsepTrackTest, SimulcastAnswerer) {
   Init(SdpMediaSection::kVideo);
@@ -1224,19 +1224,19 @@ TEST_F(JsepTrackTest, SimulcastAnswerer)
   ASSERT_EQ(2U, mSendAns.GetNegotiatedDetails()->GetEncodingCount());
   ASSERT_EQ("foo", mSendAns.GetNegotiatedDetails()->GetEncoding(0).mRid);
   ASSERT_EQ(40000U,
             mSendAns.GetNegotiatedDetails()->GetEncoding(0).mConstraints.maxBr);
   ASSERT_EQ("bar", mSendAns.GetNegotiatedDetails()->GetEncoding(1).mRid);
   ASSERT_EQ(10000U,
             mSendAns.GetNegotiatedDetails()->GetEncoding(1).mConstraints.maxBr);
   ASSERT_NE(std::string::npos,
-            mOffer->ToString().find("a=simulcast: recv rid=foo;bar"));
+            mOffer->ToString().find("a=simulcast:recv foo;bar"));
   ASSERT_NE(std::string::npos,
-            mAnswer->ToString().find("a=simulcast: send rid=foo;bar"));
+            mAnswer->ToString().find("a=simulcast:send foo;bar"));
   ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:foo recv"));
   ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:bar recv"));
   ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:foo send"));
   ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:bar send"));
 }
 
 #define VERIFY_OPUS_MAX_PLAYBACK_RATE(track, expectedRate)          \
   {                                                                 \
--- a/media/webrtc/signaling/gtest/sdp_unittests.cpp
+++ b/media/webrtc/signaling/gtest/sdp_unittests.cpp
@@ -3287,20 +3287,19 @@ TEST_P(NewSdpTest, CheckSimulcast) {
       SdpAttribute::kSimulcastAttribute));
 
   const SdpSimulcastAttribute& simulcast =
       mSdp->GetMediaSection(1).GetAttributeList().GetSimulcast();
 
   ASSERT_EQ(2U, simulcast.recvVersions.size());
   ASSERT_EQ(0U, simulcast.sendVersions.size());
   ASSERT_EQ(1U, simulcast.recvVersions[0].choices.size());
-  ASSERT_EQ("bar", simulcast.recvVersions[0].choices[0]);
+  ASSERT_EQ("bar", simulcast.recvVersions[0].choices[0].rid);
   ASSERT_EQ(1U, simulcast.recvVersions[1].choices.size());
-  ASSERT_EQ("bar123", simulcast.recvVersions[1].choices[0]);
-  ASSERT_EQ(SdpSimulcastAttribute::Versions::kRid, simulcast.recvVersions.type);
+  ASSERT_EQ("bar123", simulcast.recvVersions[1].choices[0].rid);
 }
 
 TEST_P(NewSdpTest, CheckSctpmap) {
   ParseSdp(kBasicAudioVideoDataOffer);
   ASSERT_TRUE(!!mSdp)
   << "Parse failed: " << GetParseErrors();
   ASSERT_EQ(3U, mSdp->GetMediaSectionCount())
       << "Wrong number of media sections";
@@ -4826,29 +4825,29 @@ TEST(NewSdpTestNoFixture, CheckImageattr
   os.str("");
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastVersionSerialize)
 {
   std::ostringstream os;
 
   SdpSimulcastAttribute::Version version;
-  version.choices.push_back("8");
+  version.choices.push_back(SdpSimulcastAttribute::Encoding("8", false));
   version.Serialize(os);
   ASSERT_EQ("8", os.str());
   os.str("");
 
-  version.choices.push_back("9");
+  version.choices.push_back(SdpSimulcastAttribute::Encoding("9", true));
   version.Serialize(os);
-  ASSERT_EQ("8,9", os.str());
+  ASSERT_EQ("8,~9", os.str());
   os.str("");
 
-  version.choices.push_back("0");
+  version.choices.push_back(SdpSimulcastAttribute::Encoding("0", false));
   version.Serialize(os);
-  ASSERT_EQ("8,9,0", os.str());
+  ASSERT_EQ("8,~9,0", os.str());
   os.str("");
 }
 
 static SdpSimulcastAttribute::Version ParseSimulcastVersion(
     const std::string& input) {
   std::istringstream is(input + ";");
   std::string error;
   SdpSimulcastAttribute::Version version;
@@ -4858,24 +4857,27 @@ static SdpSimulcastAttribute::Version Pa
   return version;
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastVersionValidParse)
 {
   {
     SdpSimulcastAttribute::Version version(ParseSimulcastVersion("1"));
     ASSERT_EQ(1U, version.choices.size());
-    ASSERT_EQ("1", version.choices[0]);
+    ASSERT_EQ("1", version.choices[0].rid);
+    ASSERT_FALSE(version.choices[0].paused);
   }
 
   {
-    SdpSimulcastAttribute::Version version(ParseSimulcastVersion("1,2"));
+    SdpSimulcastAttribute::Version version(ParseSimulcastVersion("1,~2"));
     ASSERT_EQ(2U, version.choices.size());
-    ASSERT_EQ("1", version.choices[0]);
-    ASSERT_EQ("2", version.choices[1]);
+    ASSERT_EQ("1", version.choices[0].rid);
+    ASSERT_EQ("2", version.choices[1].rid);
+    ASSERT_FALSE(version.choices[0].paused);
+    ASSERT_TRUE(version.choices[1].paused);
   }
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastVersionInvalidParse)
 {
   ParseInvalid<SdpSimulcastAttribute::Version>("", 0);
   ParseInvalid<SdpSimulcastAttribute::Version>(",", 0);
   ParseInvalid<SdpSimulcastAttribute::Version>(";", 0);
@@ -4886,42 +4888,38 @@ TEST(NewSdpTestNoFixture, CheckSimulcast
   ParseInvalid<SdpSimulcastAttribute::Version>("8,;", 2);
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastVersionsSerialize)
 {
   std::ostringstream os;
 
   SdpSimulcastAttribute::Versions versions;
-  versions.type = SdpSimulcastAttribute::Versions::kPt;
   versions.push_back(SdpSimulcastAttribute::Version());
-  versions.back().choices.push_back("8");
+  versions.back().choices.push_back(
+      SdpSimulcastAttribute::Encoding("8", false));
   versions.Serialize(os);
-  ASSERT_EQ("pt=8", os.str());
-  os.str("");
-
-  versions.type = SdpSimulcastAttribute::Versions::kRid;
-  versions.Serialize(os);
-  ASSERT_EQ("rid=8", os.str());
+  ASSERT_EQ("8", os.str());
   os.str("");
 
   versions.push_back(SdpSimulcastAttribute::Version());
   versions.Serialize(os);
-  ASSERT_EQ("rid=8", os.str());
+  ASSERT_EQ("8", os.str());
   os.str("");
 
-  versions.back().choices.push_back("9");
+  versions.back().choices.push_back(SdpSimulcastAttribute::Encoding("9", true));
   versions.Serialize(os);
-  ASSERT_EQ("rid=8;9", os.str());
+  ASSERT_EQ("8;~9", os.str());
   os.str("");
 
   versions.push_back(SdpSimulcastAttribute::Version());
-  versions.back().choices.push_back("0");
+  versions.back().choices.push_back(
+      SdpSimulcastAttribute::Encoding("0", false));
   versions.Serialize(os);
-  ASSERT_EQ("rid=8;9;0", os.str());
+  ASSERT_EQ("8;~9;0", os.str());
   os.str("");
 }
 
 static SdpSimulcastAttribute::Versions ParseSimulcastVersions(
     const std::string& input) {
   std::istringstream is(input + " ");
   std::string error;
   SdpSimulcastAttribute::Versions list;
@@ -4929,220 +4927,146 @@ static SdpSimulcastAttribute::Versions P
   EXPECT_EQ(' ', is.get());
   EXPECT_EQ(EOF, is.get());
   return list;
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastVersionsValidParse)
 {
   {
-    SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("pt=8"));
-    ASSERT_EQ(1U, versions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kPt, versions.type);
-    ASSERT_EQ(1U, versions[0].choices.size());
-    ASSERT_EQ("8", versions[0].choices[0]);
-  }
-
-  {
-    SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("rid=8"));
-    ASSERT_EQ(1U, versions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kRid, versions.type);
-    ASSERT_EQ(1U, versions[0].choices.size());
-    ASSERT_EQ("8", versions[0].choices[0]);
-  }
-
-  {
     SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("8"));
     ASSERT_EQ(1U, versions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kRid, versions.type);
     ASSERT_EQ(1U, versions[0].choices.size());
-    ASSERT_EQ("8", versions[0].choices[0]);
+    ASSERT_EQ("8", versions[0].choices[0].rid);
+    ASSERT_FALSE(versions[0].choices[0].paused);
   }
 
   {
-    SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("pt=8,9"));
+    SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("~8,9"));
     ASSERT_EQ(1U, versions.size());
     ASSERT_EQ(2U, versions[0].choices.size());
-    ASSERT_EQ("8", versions[0].choices[0]);
-    ASSERT_EQ("9", versions[0].choices[1]);
-  }
-
-  {
-    SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("8,9"));
-    ASSERT_EQ(1U, versions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kRid, versions.type);
-    ASSERT_EQ(2U, versions[0].choices.size());
-    ASSERT_EQ("8", versions[0].choices[0]);
-    ASSERT_EQ("9", versions[0].choices[1]);
+    ASSERT_EQ("8", versions[0].choices[0].rid);
+    ASSERT_EQ("9", versions[0].choices[1].rid);
+    ASSERT_TRUE(versions[0].choices[0].paused);
+    ASSERT_FALSE(versions[0].choices[1].paused);
   }
 
   {
-    SdpSimulcastAttribute::Versions versions(
-        ParseSimulcastVersions("pt=8,9;10"));
+    SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("8,9;~10"));
     ASSERT_EQ(2U, versions.size());
     ASSERT_EQ(2U, versions[0].choices.size());
-    ASSERT_EQ("8", versions[0].choices[0]);
-    ASSERT_EQ("9", versions[0].choices[1]);
+    ASSERT_EQ("8", versions[0].choices[0].rid);
+    ASSERT_EQ("9", versions[0].choices[1].rid);
+    ASSERT_FALSE(versions[0].choices[0].paused);
+    ASSERT_FALSE(versions[0].choices[1].paused);
     ASSERT_EQ(1U, versions[1].choices.size());
-    ASSERT_EQ("10", versions[1].choices[0]);
-  }
-
-  {
-    SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("8,9;10"));
-    ASSERT_EQ(2U, versions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kRid, versions.type);
-    ASSERT_EQ(2U, versions[0].choices.size());
-    ASSERT_EQ("8", versions[0].choices[0]);
-    ASSERT_EQ("9", versions[0].choices[1]);
-    ASSERT_EQ(1U, versions[1].choices.size());
-    ASSERT_EQ("10", versions[1].choices[0]);
+    ASSERT_EQ("10", versions[1].choices[0].rid);
+    ASSERT_TRUE(versions[1].choices[0].paused);
   }
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastVersionsInvalidParse)
 {
   ParseInvalid<SdpSimulcastAttribute::Versions>("", 0);
   ParseInvalid<SdpSimulcastAttribute::Versions>(";", 0);
-  ParseInvalid<SdpSimulcastAttribute::Versions>("foo=", 4);
-  ParseInvalid<SdpSimulcastAttribute::Versions>("foo=8", 4);
-  ParseInvalid<SdpSimulcastAttribute::Versions>("pt=9999", 7);
-  ParseInvalid<SdpSimulcastAttribute::Versions>("pt=-1", 5);
-  ParseInvalid<SdpSimulcastAttribute::Versions>("pt=x", 4);
-  ParseInvalid<SdpSimulcastAttribute::Versions>("pt=8;", 5);
-  ParseInvalid<SdpSimulcastAttribute::Versions>("pt=8;x", 6);
-  ParseInvalid<SdpSimulcastAttribute::Versions>("pt=8;;", 5);
+  ParseInvalid<SdpSimulcastAttribute::Versions>("8;", 2);
+  ParseInvalid<SdpSimulcastAttribute::Versions>("8;;", 2);
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastSerialize)
 {
   std::ostringstream os;
 
   SdpSimulcastAttribute simulcast;
-  simulcast.recvVersions.type = SdpSimulcastAttribute::Versions::kRid;
   simulcast.recvVersions.push_back(SdpSimulcastAttribute::Version());
-  simulcast.recvVersions.back().choices.push_back("8");
+  simulcast.recvVersions.back().choices.push_back(
+      SdpSimulcastAttribute::Encoding("8", false));
   simulcast.Serialize(os);
-  ASSERT_EQ("a=simulcast: recv rid=8" CRLF, os.str());
+  ASSERT_EQ("a=simulcast:recv 8" CRLF, os.str());
   os.str("");
 
-  simulcast.sendVersions.type = SdpSimulcastAttribute::Versions::kRid;
   simulcast.sendVersions.push_back(SdpSimulcastAttribute::Version());
-  simulcast.sendVersions.back().choices.push_back("9");
+  simulcast.sendVersions.back().choices.push_back(
+      SdpSimulcastAttribute::Encoding("9", true));
   simulcast.Serialize(os);
-  ASSERT_EQ("a=simulcast: send rid=9 recv rid=8" CRLF, os.str());
+  ASSERT_EQ("a=simulcast:send ~9 recv 8" CRLF, os.str());
 }
 
 static SdpSimulcastAttribute ParseSimulcast(const std::string& input) {
   std::istringstream is(input);
   std::string error;
   SdpSimulcastAttribute simulcast;
   EXPECT_TRUE(simulcast.Parse(is, &error)) << error;
   EXPECT_TRUE(is.eof());
   return simulcast;
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastValidParse)
 {
   {
     SdpSimulcastAttribute simulcast(ParseSimulcast("send 8"));
     ASSERT_EQ(1U, simulcast.sendVersions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kRid,
-              simulcast.sendVersions.type);
     ASSERT_EQ(1U, simulcast.sendVersions[0].choices.size());
-    ASSERT_EQ("8", simulcast.sendVersions[0].choices[0]);
+    ASSERT_EQ("8", simulcast.sendVersions[0].choices[0].rid);
+    ASSERT_FALSE(simulcast.sendVersions[0].choices[0].paused);
     ASSERT_EQ(0U, simulcast.recvVersions.size());
   }
 
   {
-    SdpSimulcastAttribute simulcast(ParseSimulcast(" send pt=8"));
+    SdpSimulcastAttribute simulcast(ParseSimulcast(" SEND 8"));
     ASSERT_EQ(1U, simulcast.sendVersions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kPt,
-              simulcast.sendVersions.type);
     ASSERT_EQ(1U, simulcast.sendVersions[0].choices.size());
-    ASSERT_EQ("8", simulcast.sendVersions[0].choices[0]);
+    ASSERT_EQ("8", simulcast.sendVersions[0].choices[0].rid);
+    ASSERT_FALSE(simulcast.sendVersions[0].choices[0].paused);
     ASSERT_EQ(0U, simulcast.recvVersions.size());
   }
 
   {
-    SdpSimulcastAttribute simulcast(ParseSimulcast(" SEND pt=8"));
-    ASSERT_EQ(1U, simulcast.sendVersions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kPt,
-              simulcast.sendVersions.type);
-    ASSERT_EQ(1U, simulcast.sendVersions[0].choices.size());
-    ASSERT_EQ("8", simulcast.sendVersions[0].choices[0]);
-    ASSERT_EQ(0U, simulcast.recvVersions.size());
-  }
-
-  {
-    SdpSimulcastAttribute simulcast(ParseSimulcast(" recv pt=8"));
+    SdpSimulcastAttribute simulcast(ParseSimulcast("recv 8"));
     ASSERT_EQ(1U, simulcast.recvVersions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kPt,
-              simulcast.recvVersions.type);
     ASSERT_EQ(1U, simulcast.recvVersions[0].choices.size());
-    ASSERT_EQ("8", simulcast.recvVersions[0].choices[0]);
+    ASSERT_EQ("8", simulcast.recvVersions[0].choices[0].rid);
+    ASSERT_FALSE(simulcast.recvVersions[0].choices[0].paused);
     ASSERT_EQ(0U, simulcast.sendVersions.size());
   }
 
   {
     SdpSimulcastAttribute simulcast(
-        ParseSimulcast("send 8,9;101;97,98 recv 101,120;97"));
+        ParseSimulcast("send 8,9;~101;97,~98 recv 101,120;97"));
     ASSERT_EQ(3U, simulcast.sendVersions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kRid,
-              simulcast.sendVersions.type);
     ASSERT_EQ(2U, simulcast.sendVersions[0].choices.size());
-    ASSERT_EQ("8", simulcast.sendVersions[0].choices[0]);
-    ASSERT_EQ("9", simulcast.sendVersions[0].choices[1]);
+    ASSERT_EQ("8", simulcast.sendVersions[0].choices[0].rid);
+    ASSERT_EQ("9", simulcast.sendVersions[0].choices[1].rid);
+    ASSERT_FALSE(simulcast.sendVersions[0].choices[0].paused);
+    ASSERT_FALSE(simulcast.sendVersions[0].choices[1].paused);
     ASSERT_EQ(1U, simulcast.sendVersions[1].choices.size());
-    ASSERT_EQ("101", simulcast.sendVersions[1].choices[0]);
+    ASSERT_EQ("101", simulcast.sendVersions[1].choices[0].rid);
+    ASSERT_TRUE(simulcast.sendVersions[1].choices[0].paused);
     ASSERT_EQ(2U, simulcast.sendVersions[2].choices.size());
-    ASSERT_EQ("97", simulcast.sendVersions[2].choices[0]);
-    ASSERT_EQ("98", simulcast.sendVersions[2].choices[1]);
+    ASSERT_EQ("97", simulcast.sendVersions[2].choices[0].rid);
+    ASSERT_EQ("98", simulcast.sendVersions[2].choices[1].rid);
+    ASSERT_FALSE(simulcast.sendVersions[2].choices[0].paused);
+    ASSERT_TRUE(simulcast.sendVersions[2].choices[1].paused);
 
     ASSERT_EQ(2U, simulcast.recvVersions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kRid,
-              simulcast.recvVersions.type);
     ASSERT_EQ(2U, simulcast.recvVersions[0].choices.size());
-    ASSERT_EQ("101", simulcast.recvVersions[0].choices[0]);
-    ASSERT_EQ("120", simulcast.recvVersions[0].choices[1]);
+    ASSERT_EQ("101", simulcast.recvVersions[0].choices[0].rid);
+    ASSERT_EQ("120", simulcast.recvVersions[0].choices[1].rid);
     ASSERT_EQ(1U, simulcast.recvVersions[1].choices.size());
-    ASSERT_EQ("97", simulcast.recvVersions[1].choices[0]);
-  }
-  {
-    SdpSimulcastAttribute simulcast(
-        ParseSimulcast(" send pt=8,9;101;97,98 recv pt=101,120;97"));
-    ASSERT_EQ(3U, simulcast.sendVersions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kPt,
-              simulcast.sendVersions.type);
-    ASSERT_EQ(2U, simulcast.sendVersions[0].choices.size());
-    ASSERT_EQ("8", simulcast.sendVersions[0].choices[0]);
-    ASSERT_EQ("9", simulcast.sendVersions[0].choices[1]);
-    ASSERT_EQ(1U, simulcast.sendVersions[1].choices.size());
-    ASSERT_EQ("101", simulcast.sendVersions[1].choices[0]);
-    ASSERT_EQ(2U, simulcast.sendVersions[2].choices.size());
-    ASSERT_EQ("97", simulcast.sendVersions[2].choices[0]);
-    ASSERT_EQ("98", simulcast.sendVersions[2].choices[1]);
-
-    ASSERT_EQ(2U, simulcast.recvVersions.size());
-    ASSERT_EQ(SdpSimulcastAttribute::Versions::kPt,
-              simulcast.recvVersions.type);
-    ASSERT_EQ(2U, simulcast.recvVersions[0].choices.size());
-    ASSERT_EQ("101", simulcast.recvVersions[0].choices[0]);
-    ASSERT_EQ("120", simulcast.recvVersions[0].choices[1]);
-    ASSERT_EQ(1U, simulcast.recvVersions[1].choices.size());
-    ASSERT_EQ("97", simulcast.recvVersions[1].choices[0]);
+    ASSERT_EQ("97", simulcast.recvVersions[1].choices[0].rid);
   }
 }
 
 TEST(NewSdpTestNoFixture, CheckSimulcastInvalidParse)
 {
   ParseInvalid<SdpSimulcastAttribute>("", 0);
   ParseInvalid<SdpSimulcastAttribute>(" ", 1);
   ParseInvalid<SdpSimulcastAttribute>("vcer ", 4);
-  ParseInvalid<SdpSimulcastAttribute>(" send pt=8 send ", 15);
-  ParseInvalid<SdpSimulcastAttribute>(" recv pt=8 recv ", 15);
+  ParseInvalid<SdpSimulcastAttribute>(" send 8 send ", 12);
+  ParseInvalid<SdpSimulcastAttribute>(" recv 8 recv ", 12);
 }
 
 static SdpRidAttributeList::Rid ParseRid(const std::string& input) {
   std::istringstream is(input);
   std::string error;
   SdpRidAttributeList::Rid rid;
   EXPECT_TRUE(rid.Parse(is, &error)) << error;
   EXPECT_TRUE(is.eof());
--- a/media/webrtc/signaling/src/jsep/JsepTrack.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepTrack.cpp
@@ -131,17 +131,17 @@ void JsepTrack::AddToAnswer(const SdpMed
   }
 
   AddToMsection(codecs, answer);
 
   if (mDirection == sdp::kSend) {
     std::vector<JsConstraints> constraints;
     if (answer->IsSending()) {
       constraints = mJsEncodeConstraints;
-      std::vector<SdpRidAttributeList::Rid> rids;
+      std::vector<std::pair<SdpRidAttributeList::Rid, bool>> rids;
       GetRids(offer, sdp::kRecv, &rids);
       NegotiateRids(rids, &constraints);
     }
     AddToMsection(constraints, sdp::kSend, ssrcGenerator, answer);
   }
 }
 
 bool JsepTrack::SetJsConstraints(
@@ -187,24 +187,25 @@ void JsepTrack::AddToMsection(
       }
     }
   }
 }
 
 // Updates the |id| values in |constraintsList| with the rid values in |rids|,
 // where necessary.
 void JsepTrack::NegotiateRids(
-    const std::vector<SdpRidAttributeList::Rid>& rids,
+    const std::vector<std::pair<SdpRidAttributeList::Rid, bool>>& rids,
     std::vector<JsConstraints>* constraintsList) const {
-  for (const SdpRidAttributeList::Rid& rid : rids) {
-    if (!FindConstraints(rid.id, *constraintsList)) {
+  for (const auto& ridAndPaused : rids) {
+    if (!FindConstraints(ridAndPaused.first.id, *constraintsList)) {
       // Pair up the first JsConstraints with an empty id, if it exists.
       JsConstraints* constraints = FindConstraints("", *constraintsList);
       if (constraints) {
-        constraints->rid = rid.id;
+        constraints->rid = ridAndPaused.first.id;
+        constraints->paused = ridAndPaused.second;
       }
     }
   }
 }
 
 void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
   MOZ_ASSERT(mDirection == sdp::kSend);
   MOZ_ASSERT(mType != SdpMediaSection::kApplication);
@@ -234,17 +235,18 @@ void JsepTrack::AddToMsection(const std:
   for (const JsConstraints& constraints : constraintsList) {
     if (!constraints.rid.empty()) {
       SdpRidAttributeList::Rid rid;
       rid.id = constraints.rid;
       rid.direction = direction;
       rids->mRids.push_back(rid);
 
       SdpSimulcastAttribute::Version version;
-      version.choices.push_back(constraints.rid);
+      version.choices.push_back(
+          SdpSimulcastAttribute::Encoding(constraints.rid, false));
       if (direction == sdp::kSend) {
         simulcast->sendVersions.push_back(version);
       } else {
         simulcast->recvVersions.push_back(version);
       }
     }
   }
 
@@ -254,19 +256,19 @@ void JsepTrack::AddToMsection(const std:
   }
 
   if (mType != SdpMediaSection::kApplication && mDirection == sdp::kSend) {
     UpdateSsrcs(ssrcGenerator, constraintsList.size());
     msection->SetSsrcs(mSsrcs, mCNAME);
   }
 }
 
-void JsepTrack::GetRids(const SdpMediaSection& msection,
-                        sdp::Direction direction,
-                        std::vector<SdpRidAttributeList::Rid>* rids) const {
+void JsepTrack::GetRids(
+    const SdpMediaSection& msection, sdp::Direction direction,
+    std::vector<std::pair<SdpRidAttributeList::Rid, bool>>* rids) const {
   rids->clear();
   if (!msection.GetAttributeList().HasAttribute(
           SdpAttribute::kSimulcastAttribute)) {
     return;
   }
 
   const SdpSimulcastAttribute& simulcast(
       msection.GetAttributeList().GetSimulcast());
@@ -280,25 +282,21 @@ void JsepTrack::GetRids(const SdpMediaSe
       versions = &simulcast.recvVersions;
       break;
   }
 
   if (!versions->IsSet()) {
     return;
   }
 
-  if (versions->type != SdpSimulcastAttribute::Versions::kRid) {
-    // No support for PT-based simulcast, yet.
-    return;
-  }
-
   for (const SdpSimulcastAttribute::Version& version : *versions) {
     if (!version.choices.empty()) {
       // We validate that rids are present (and sane) elsewhere.
-      rids->push_back(*msection.FindRid(version.choices[0]));
+      rids->push_back(std::make_pair(*msection.FindRid(version.choices[0].rid),
+                                     version.choices[0].paused));
     }
   }
 }
 
 JsepTrack::JsConstraints* JsepTrack::FindConstraints(
     const std::string& id, std::vector<JsConstraints>& constraintsList) const {
   for (JsConstraints& constraints : constraintsList) {
     if (constraints.rid == id) {
@@ -310,23 +308,23 @@ JsepTrack::JsConstraints* JsepTrack::Fin
 
 void JsepTrack::CreateEncodings(
     const SdpMediaSection& remote,
     const std::vector<UniquePtr<JsepCodecDescription>>& negotiatedCodecs,
     JsepTrackNegotiatedDetails* negotiatedDetails) {
   negotiatedDetails->mTias = remote.GetBandwidth("TIAS");
   // TODO add support for b=AS if TIAS is not set (bug 976521)
 
-  std::vector<SdpRidAttributeList::Rid> rids;
+  std::vector<std::pair<SdpRidAttributeList::Rid, bool>> rids;
   GetRids(remote, sdp::kRecv, &rids);  // Get rids we will send
   NegotiateRids(rids, &mJsEncodeConstraints);
   if (rids.empty()) {
     // Add dummy value with an empty id to make sure we get a single unicast
     // stream.
-    rids.push_back(SdpRidAttributeList::Rid());
+    rids.push_back(std::make_pair(SdpRidAttributeList::Rid(), false));
   }
 
   size_t max_streams = 1;
 
   if (!mJsEncodeConstraints.empty()) {
     max_streams = std::min(rids.size(), mJsEncodeConstraints.size());
   }
   // Drop SSRCs if less RIDs were offered than we have encoding constraints
@@ -340,27 +338,28 @@ void JsepTrack::CreateEncodings(
   for (size_t i = 0; i < max_streams; ++i) {
     if (i == negotiatedDetails->mEncodings.size()) {
       negotiatedDetails->mEncodings.emplace_back(new JsepTrackEncoding);
     }
 
     auto& encoding = negotiatedDetails->mEncodings[i];
 
     for (const auto& codec : negotiatedCodecs) {
-      if (rids[i].HasFormat(codec->mDefaultPt)) {
+      if (rids[i].first.HasFormat(codec->mDefaultPt)) {
         encoding->AddCodec(*codec);
       }
     }
 
-    encoding->mRid = rids[i].id;
+    encoding->mRid = rids[i].first.id;
+    encoding->mPaused = rids[i].second;
     // If we end up supporting params for rid, we would handle that here.
 
     // Incorporate the corresponding JS encoding constraints, if they exist
     for (const JsConstraints& jsConstraints : mJsEncodeConstraints) {
-      if (jsConstraints.rid == rids[i].id) {
+      if (jsConstraints.rid == rids[i].first.id) {
         encoding->mConstraints = jsConstraints.constraints;
       }
     }
   }
 }
 
 std::vector<UniquePtr<JsepCodecDescription>> JsepTrack::GetCodecClones() const {
   std::vector<UniquePtr<JsepCodecDescription>> clones;
--- a/media/webrtc/signaling/src/jsep/JsepTrack.h
+++ b/media/webrtc/signaling/src/jsep/JsepTrack.h
@@ -211,19 +211,21 @@ class JsepTrack {
     }
     return nullptr;
   }
 
   virtual void ClearNegotiatedDetails() { mNegotiatedDetails.reset(); }
 
   struct JsConstraints {
     std::string rid;
+    bool paused = false;
     EncodingConstraints constraints;
     bool operator==(const JsConstraints& other) const {
-      return rid == other.rid && constraints == other.constraints;
+      return rid == other.rid && paused == other.paused &&
+             constraints == other.constraints;
     }
   };
 
   // Returns true if the constraints changed.
   bool SetJsConstraints(const std::vector<JsConstraints>& constraintsList);
 
   void GetJsConstraints(std::vector<JsConstraints>* outConstraintsList) const {
     MOZ_ASSERT(outConstraintsList);
@@ -238,31 +240,33 @@ class JsepTrack {
   std::vector<UniquePtr<JsepCodecDescription>> GetCodecClones() const;
   static void EnsureNoDuplicatePayloadTypes(
       std::vector<UniquePtr<JsepCodecDescription>>* codecs);
   static void GetPayloadTypes(
       const std::vector<UniquePtr<JsepCodecDescription>>& codecs,
       std::vector<uint16_t>* pts);
   void AddToMsection(const std::vector<UniquePtr<JsepCodecDescription>>& codecs,
                      SdpMediaSection* msection);
-  void GetRids(const SdpMediaSection& msection, sdp::Direction direction,
-               std::vector<SdpRidAttributeList::Rid>* rids) const;
+  void GetRids(
+      const SdpMediaSection& msection, sdp::Direction direction,
+      std::vector<std::pair<SdpRidAttributeList::Rid, bool>>* rids) const;
   void CreateEncodings(
       const SdpMediaSection& remote,
       const std::vector<UniquePtr<JsepCodecDescription>>& negotiatedCodecs,
       JsepTrackNegotiatedDetails* details);
 
   virtual std::vector<UniquePtr<JsepCodecDescription>> NegotiateCodecs(
       const SdpMediaSection& remote, bool isOffer);
 
   JsConstraints* FindConstraints(
       const std::string& rid,
       std::vector<JsConstraints>& constraintsList) const;
-  void NegotiateRids(const std::vector<SdpRidAttributeList::Rid>& rids,
-                     std::vector<JsConstraints>* constraints) const;
+  void NegotiateRids(
+      const std::vector<std::pair<SdpRidAttributeList::Rid, bool>>& rids,
+      std::vector<JsConstraints>* constraints) const;
   void UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings);
 
   mozilla::SdpMediaSection::MediaType mType;
   // These are the ids that everyone outside of JsepSession care about
   std::vector<std::string> mStreamIds;
   std::string mTrackId;
   std::string mCNAME;
   sdp::Direction mDirection;
--- a/media/webrtc/signaling/src/jsep/JsepTrackEncoding.h
+++ b/media/webrtc/signaling/src/jsep/JsepTrackEncoding.h
@@ -41,15 +41,16 @@ class JsepTrackEncoding {
         return true;
       }
     }
     return false;
   }
 
   EncodingConstraints mConstraints;
   std::string mRid;
+  bool mPaused = false;
 
  private:
   std::vector<UniquePtr<JsepCodecDescription>> mCodecs;
 };
 }  // namespace mozilla
 
 #endif  // _JESPTRACKENCODING_H_
--- a/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
@@ -887,34 +887,34 @@ SdpSimulcastAttribute::Versions LoadSimu
     const RustSdpAttributeSimulcastVersionVec* rustVersionList) {
   size_t rustVersionCount = sdp_simulcast_get_version_count(rustVersionList);
   auto rustVersionArray =
       MakeUnique<RustSdpAttributeSimulcastVersion[]>(rustVersionCount);
   sdp_simulcast_get_versions(rustVersionList, rustVersionCount,
                              rustVersionArray.get());
 
   SdpSimulcastAttribute::Versions versions;
-  versions.type = SdpSimulcastAttribute::Versions::kRid;
 
   for (size_t i = 0; i < rustVersionCount; i++) {
     const RustSdpAttributeSimulcastVersion& rustVersion = rustVersionArray[i];
     size_t rustIdCount = sdp_simulcast_get_ids_count(rustVersion.ids);
     if (!rustIdCount) {
       continue;
     }
 
     SdpSimulcastAttribute::Version version;
     auto rustIdArray = MakeUnique<RustSdpAttributeSimulcastId[]>(rustIdCount);
     sdp_simulcast_get_ids(rustVersion.ids, rustIdCount, rustIdArray.get());
 
     for (size_t j = 0; j < rustIdCount; j++) {
       const RustSdpAttributeSimulcastId& rustId = rustIdArray[j];
       std::string id = convertStringView(rustId.id);
       // TODO: Bug 1225877. Added support for 'paused'-state
-      version.choices.push_back(id);
+      version.choices.push_back(
+          SdpSimulcastAttribute::Encoding(id, rustId.paused));
     }
 
     versions.push_back(version);
   }
 
   return versions;
 }
 
--- a/media/webrtc/signaling/src/sdp/SdpAttribute.cpp
+++ b/media/webrtc/signaling/src/sdp/SdpAttribute.cpp
@@ -1071,120 +1071,79 @@ void SdpSctpmapAttributeList::Serialize(
 }
 
 void SdpSetupAttribute::Serialize(std::ostream& os) const {
   os << "a=" << mType << ":" << mRole << CRLF;
 }
 
 void SdpSimulcastAttribute::Version::Serialize(std::ostream& os) const {
   SkipFirstDelimiter comma(",");
-  for (const std::string& choice : choices) {
-    os << comma << choice;
+  for (const Encoding& choice : choices) {
+    os << comma;
+    if (choice.paused) {
+      os << '~';
+    }
+    os << choice.rid;
   }
 }
 
 bool SdpSimulcastAttribute::Version::Parse(std::istream& is,
                                            std::string* error) {
   do {
+    bool paused = SkipChar(is, '~', error);
     std::string value = ParseToken(is, ",; ", error);
     if (value.empty()) {
+      *error = "Missing rid";
       return false;
     }
-    choices.push_back(value);
+    choices.push_back(Encoding(value, paused));
   } while (SkipChar(is, ',', error));
 
   return true;
 }
 
-bool SdpSimulcastAttribute::Version::GetChoicesAsFormats(
-    std::vector<uint16_t>* formats) const {
-  for (const std::string& choice : choices) {
-    uint16_t format;
-    if (!SdpHelper::GetPtAsInt(choice, &format) || (format > 127)) {
-      return false;
-    }
-    formats->push_back(format);
-  }
-
-  return true;
-}
-
 void SdpSimulcastAttribute::Versions::Serialize(std::ostream& os) const {
-  switch (type) {
-    case kRid:
-      os << "rid=";
-      break;
-    case kPt:
-      os << "pt=";
-      break;
-  }
-
   SkipFirstDelimiter semic(";");
   for (const Version& version : *this) {
     if (!version.IsSet()) {
       continue;
     }
     os << semic;
     version.Serialize(os);
   }
 }
 
 bool SdpSimulcastAttribute::Versions::Parse(std::istream& is,
                                             std::string* error) {
-  int startPos = is.tellg();
-  std::string rawType = ParseKey(is, error);
-  if (rawType.empty()) {
-    // New simulcast format does not have pt= or rid=, it is always rid
-    rawType = "rid";
-    is.clear();
-    is.seekg(startPos);
-  }
-
-  if (rawType == "pt") {
-    type = kPt;
-  } else if (rawType == "rid") {
-    type = kRid;
-  } else {
-    *error = "Unknown simulcast identification type ";
-    error->append(rawType);
-    return false;
-  }
-
   do {
     Version version;
     if (!version.Parse(is, error)) {
       return false;
     }
-
-    if (type == kPt) {
-      std::vector<uint16_t> formats;
-      if (!version.GetChoicesAsFormats(&formats)) {
-        *error = "Invalid payload type";
-        return false;
-      }
-    }
-
     push_back(version);
   } while (SkipChar(is, ';', error));
 
   return true;
 }
 
 void SdpSimulcastAttribute::Serialize(std::ostream& os) const {
   MOZ_ASSERT(sendVersions.IsSet() || recvVersions.IsSet());
 
   os << "a=" << mType << ":";
 
   if (sendVersions.IsSet()) {
-    os << " send ";
+    os << "send ";
     sendVersions.Serialize(os);
   }
 
   if (recvVersions.IsSet()) {
-    os << " recv ";
+    if (sendVersions.IsSet()) {
+      os << " ";
+    }
+    os << "recv ";
     recvVersions.Serialize(os);
   }
 
   os << CRLF;
 }
 
 bool SdpSimulcastAttribute::Parse(std::istream& is, std::string* error) {
   bool gotRecv = false;
--- a/media/webrtc/signaling/src/sdp/SdpAttribute.h
+++ b/media/webrtc/signaling/src/sdp/SdpAttribute.h
@@ -1530,48 +1530,51 @@ inline std::ostream& operator<<(std::ost
 
 class SdpSimulcastAttribute : public SdpAttribute {
  public:
   SdpSimulcastAttribute() : SdpAttribute(kSimulcastAttribute) {}
 
   void Serialize(std::ostream& os) const override;
   bool Parse(std::istream& is, std::string* error);
 
+  class Encoding {
+   public:
+    Encoding(const std::string& aRid, bool aPaused)
+        : rid(aRid), paused(aPaused) {}
+    std::string rid;
+    bool paused = false;
+  };
+
   class Version {
    public:
     void Serialize(std::ostream& os) const;
     bool IsSet() const { return !choices.empty(); }
     bool Parse(std::istream& is, std::string* error);
-    bool GetChoicesAsFormats(std::vector<uint16_t>* formats) const;
 
-    std::vector<std::string> choices;
+    std::vector<Encoding> choices;
   };
 
   class Versions : public std::vector<Version> {
    public:
-    enum Type { kPt, kRid };
-
-    Versions() : type(kRid) {}
     void Serialize(std::ostream& os) const;
     bool IsSet() const {
       if (empty()) {
         return false;
       }
 
       for (const Version& version : *this) {
         if (version.IsSet()) {
           return true;
         }
       }
 
       return false;
     }
 
     bool Parse(std::istream& is, std::string* error);
-    Type type;
   };
 
   Versions sendVersions;
   Versions recvVersions;
 };
 
 ///////////////////////////////////////////////////////////////////////////
 // a=ssrc, RFC5576
--- a/media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp
+++ b/media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp
@@ -229,34 +229,24 @@ bool SipccSdpMediaSection::ValidateSimul
     errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
                               "simulcast attribute has a direction that is "
                               "inconsistent with the direction of this media "
                               "section.");
     return false;
   }
 
   for (const SdpSimulcastAttribute::Version& version : versions) {
-    for (const std::string& id : version.choices) {
-      if (versions.type == SdpSimulcastAttribute::Versions::kRid) {
-        const SdpRidAttributeList::Rid* ridAttr = FindRid(id);
-        if (!ridAttr || (ridAttr->direction != direction)) {
-          std::ostringstream os;
-          os << "No rid attribute for \'" << id << "\'";
-          errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
-                                    os.str());
-          return false;
-        }
-      } else if (versions.type == SdpSimulcastAttribute::Versions::kPt) {
-        if (std::find(mFormats.begin(), mFormats.end(), id) == mFormats.end()) {
-          std::ostringstream os;
-          os << "No pt for \'" << id << "\'";
-          errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
-                                    os.str());
-          return false;
-        }
+    for (const SdpSimulcastAttribute::Encoding& encoding : version.choices) {
+      const SdpRidAttributeList::Rid* ridAttr = FindRid(encoding.rid);
+      if (!ridAttr || (ridAttr->direction != direction)) {
+        std::ostringstream os;
+        os << "No rid attribute for \'" << encoding.rid << "\'";
+        errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
+                                  os.str());
+        return false;
       }
     }
   }
   return true;
 }
 
 bool SipccSdpMediaSection::LoadConnection(sdp_t* sdp, uint16_t level,
                                           SdpErrorHolder& errorHolder) {