tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

jsep_track_unittest.cpp (84934B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nss.h"
      8 #include "ssl.h"
      9 
     10 #define GTEST_HAS_RTTI 0
     11 #include "gmock/gmock.h"
     12 #include "gtest/gtest.h"
     13 
     14 #include "MockJsepCodecPreferences.h"
     15 #include "jsapi/DefaultCodecPreferences.h"
     16 #include "jsep/JsepTrack.h"
     17 #include "sdp/SipccSdp.h"
     18 #include "sdp/SipccSdpParser.h"
     19 #include "sdp/SdpHelper.h"
     20 
     21 using testing::UnorderedElementsAre;
     22 
     23 namespace mozilla {
     24 
     25 class JsepTrackTestBase : public ::testing::Test {
     26 public:
     27  static void SetUpTestCase() {
     28    NSS_NoDB_Init(nullptr);
     29    NSS_SetDomesticPolicy();
     30  }
     31 };
     32 
     33 struct CodecOverrides {
     34  bool addFecCodecs = false;
     35  bool preferRed = false;
     36  bool addDtmfCodec = false;
     37  bool enableRemb = true;
     38  bool enableTransportCC = true;
     39  void ApplyToPrefs(MockJsepCodecPreferences& aPrefs) const {
     40    aPrefs.mUseRemb = enableRemb;
     41    aPrefs.mUseTransportCC = enableTransportCC;
     42  }
     43 };
     44 
     45 class JsepTrackTest : public JsepTrackTestBase {
     46 public:
     47  JsepTrackTest()
     48      : mSendOff(SdpMediaSection::kAudio, sdp::kSend),
     49        mRecvOff(SdpMediaSection::kAudio, sdp::kRecv),
     50        mSendAns(SdpMediaSection::kAudio, sdp::kSend),
     51        mRecvAns(SdpMediaSection::kAudio, sdp::kRecv) {}
     52 
     53  void TearDown() override {
     54    if (::testing::UnitTest::GetInstance()
     55            ->current_test_info()
     56            ->result()
     57            ->Failed()) {
     58      if (mOffer) {
     59        std::cerr << "Offer SDP: \n";
     60        mOffer->Serialize(std::cerr);
     61      }
     62 
     63      if (mAnswer) {
     64        std::cerr << "Answer SDP: \n";
     65        mAnswer->Serialize(std::cerr);
     66      }
     67    }
     68  }
     69 
     70  std::vector<UniquePtr<JsepCodecDescription>> MakeCodecs(
     71      const CodecOverrides overrides) const {
     72    MockJsepCodecPreferences prefs;
     73    overrides.ApplyToPrefs(prefs);
     74 
     75    prefs.mUseRemb = overrides.enableRemb;
     76    prefs.mUseTransportCC = overrides.enableTransportCC;
     77    JsepCodecPreferences& prefsRef = prefs;
     78    std::cout << "CodecPrefrences: " << prefsRef << "\n";
     79    std::vector<UniquePtr<JsepCodecDescription>> results;
     80    results.emplace_back(JsepAudioCodecDescription::CreateDefaultOpus(prefs));
     81    results.emplace_back(JsepAudioCodecDescription::CreateDefaultG722());
     82    if (overrides.addDtmfCodec) {
     83      results.emplace_back(
     84          JsepAudioCodecDescription::CreateDefaultTelephoneEvent());
     85    }
     86 
     87    if (overrides.addFecCodecs && overrides.preferRed) {
     88      results.emplace_back(JsepVideoCodecDescription::CreateDefaultRed(prefs));
     89    }
     90    results.emplace_back(JsepVideoCodecDescription::CreateDefaultVP8(prefs));
     91    results.emplace_back(JsepVideoCodecDescription::CreateDefaultH264_1(prefs));
     92    results.emplace_back(JsepVideoCodecDescription::CreateDefaultAV1(prefs));
     93 
     94    if (overrides.addFecCodecs) {
     95      if (!overrides.preferRed) {
     96        results.emplace_back(
     97            JsepVideoCodecDescription::CreateDefaultRed(prefs));
     98      }
     99      results.emplace_back(
    100          JsepVideoCodecDescription::CreateDefaultUlpFec(prefs));
    101    }
    102 
    103    results.emplace_back(new JsepApplicationCodecDescription(
    104        "webrtc-datachannel", 256, 5999, 499));
    105 
    106    return results;
    107  }
    108 
    109  void Init(SdpMediaSection::MediaType type) {
    110    InitCodecs(CodecOverrides{});
    111    InitTracks(type);
    112    InitSdp(type);
    113  }
    114 
    115  struct SplitOverrides {
    116    CodecOverrides offer = {};
    117    CodecOverrides answer = {};
    118  };
    119 
    120  void InitCodecs(const CodecOverrides& overrides) {
    121    mOffCodecs = MakeCodecs(overrides);
    122    mAnsCodecs = MakeCodecs(overrides);
    123  }
    124  void InitCodecs(const SplitOverrides& overrides) {
    125    mOffCodecs = MakeCodecs(overrides.offer);
    126    mAnsCodecs = MakeCodecs(overrides.answer);
    127  }
    128 
    129  void InitTracks(SdpMediaSection::MediaType type) {
    130    mSendOff = JsepTrack(type, sdp::kSend);
    131    if (type != SdpMediaSection::MediaType::kApplication) {
    132      mSendOff.UpdateStreamIds(std::vector<std::string>(1, "stream_id"));
    133    }
    134    mRecvOff = JsepTrack(type, sdp::kRecv);
    135    mSendOff.PopulateCodecs(mOffCodecs);
    136    mRecvOff.PopulateCodecs(mOffCodecs);
    137 
    138    mSendAns = JsepTrack(type, sdp::kSend);
    139    if (type != SdpMediaSection::MediaType::kApplication) {
    140      mSendAns.UpdateStreamIds(std::vector<std::string>(1, "stream_id"));
    141    }
    142    mRecvAns = JsepTrack(type, sdp::kRecv);
    143    mSendAns.PopulateCodecs(mAnsCodecs);
    144    mRecvAns.PopulateCodecs(mAnsCodecs);
    145  }
    146 
    147  void InitSdp(SdpMediaSection::MediaType type) {
    148    std::vector<std::string> msids(1, "*");
    149    std::string error;
    150    SdpHelper helper(&error);
    151 
    152    mOffer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
    153    mOffer->AddMediaSection(type, SdpDirectionAttribute::kSendrecv, 0,
    154                            SdpHelper::GetProtocolForMediaType(type),
    155                            sdp::kIPv4, "0.0.0.0");
    156    // JsepTrack doesn't set msid-semantic
    157    helper.SetupMsidSemantic(msids, mOffer.get());
    158 
    159    mAnswer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
    160    mAnswer->AddMediaSection(type, SdpDirectionAttribute::kSendrecv, 0,
    161                             SdpHelper::GetProtocolForMediaType(type),
    162                             sdp::kIPv4, "0.0.0.0");
    163    // JsepTrack doesn't set msid-semantic
    164    helper.SetupMsidSemantic(msids, mAnswer.get());
    165  }
    166 
    167  SdpMediaSection& GetOffer() { return mOffer->GetMediaSection(0); }
    168 
    169  SdpMediaSection& GetAnswer() { return mAnswer->GetMediaSection(0); }
    170 
    171  void CreateOffer() {
    172    mSendOff.AddToOffer(mSsrcGenerator, &GetOffer());
    173    mRecvOff.AddToOffer(mSsrcGenerator, &GetOffer());
    174  }
    175 
    176  void CreateAnswer() {
    177    if (mRecvAns.GetMediaType() != SdpMediaSection::MediaType::kApplication) {
    178      mRecvAns.RecvTrackSetRemote(*mOffer, GetOffer());
    179      mSendAns.SendTrackSetRemote(mSsrcGenerator, GetOffer());
    180    }
    181 
    182    mSendAns.AddToAnswer(GetOffer(), mSsrcGenerator, &GetAnswer());
    183    mRecvAns.AddToAnswer(GetOffer(), mSsrcGenerator, &GetAnswer());
    184  }
    185 
    186  void Negotiate() {
    187    if (mRecvOff.GetMediaType() != SdpMediaSection::MediaType::kApplication) {
    188      mRecvOff.RecvTrackSetRemote(*mAnswer, GetAnswer());
    189      mSendOff.SendTrackSetRemote(mSsrcGenerator, GetAnswer());
    190    }
    191 
    192    if (GetAnswer().IsSending()) {
    193      mSendAns.Negotiate(GetAnswer(), GetOffer(), GetAnswer());
    194      mRecvOff.Negotiate(GetAnswer(), GetAnswer(), GetOffer());
    195    }
    196 
    197    if (GetAnswer().IsReceiving()) {
    198      mRecvAns.Negotiate(GetAnswer(), GetOffer(), GetAnswer());
    199      mSendOff.Negotiate(GetAnswer(), GetAnswer(), GetOffer());
    200    }
    201  }
    202 
    203  void OfferAnswer(bool offerCodecsMatchAnswer = true) {
    204    CreateOffer();
    205    CreateAnswer();
    206    Negotiate();
    207    SanityCheck(offerCodecsMatchAnswer);
    208  }
    209 
    210  // TODO: Look into writing a macro that wraps an ASSERT_ and returns false
    211  // if it fails (probably requires writing a bool-returning function that
    212  // takes a void-returning lambda with a bool outparam, which will in turn
    213  // invokes the ASSERT_)
    214  static void CheckEncodingCount(size_t expected, const JsepTrack& send,
    215                                 const JsepTrack& recv) {
    216    if (expected) {
    217      ASSERT_TRUE(send.GetNegotiatedDetails());
    218      ASSERT_TRUE(recv.GetNegotiatedDetails());
    219    }
    220 
    221    if (!send.GetStreamIds().empty() && send.GetNegotiatedDetails()) {
    222      ASSERT_EQ(expected, send.GetNegotiatedDetails()->GetEncodingCount());
    223    }
    224 
    225    if (!recv.GetStreamIds().empty() && recv.GetNegotiatedDetails()) {
    226      ASSERT_EQ(expected, recv.GetNegotiatedDetails()->GetEncodingCount());
    227    }
    228  }
    229 
    230  void CheckOffEncodingCount(size_t expected) const {
    231    CheckEncodingCount(expected, mSendOff, mRecvAns);
    232  }
    233 
    234  void CheckAnsEncodingCount(size_t expected) const {
    235    CheckEncodingCount(expected, mSendAns, mRecvOff);
    236  }
    237 
    238  UniquePtr<JsepCodecDescription> GetCodec(const JsepTrack& track,
    239                                           SdpMediaSection::MediaType type,
    240                                           size_t expectedSize,
    241                                           size_t codecIndex) const {
    242    if (!track.GetNegotiatedDetails() ||
    243        track.GetNegotiatedDetails()->GetEncodingCount() != 1U ||
    244        track.GetMediaType() != type) {
    245      return nullptr;
    246    }
    247    const auto& codecs =
    248        track.GetNegotiatedDetails()->GetEncoding(0).GetCodecs();
    249    // it should not be possible for codecs to have a different type
    250    // than the track, but we'll check the codec here just in case.
    251    if (codecs.size() != expectedSize || codecIndex >= expectedSize ||
    252        codecs[codecIndex]->Type() != type) {
    253      return nullptr;
    254    }
    255    return UniquePtr<JsepCodecDescription>(codecs[codecIndex]->Clone());
    256  }
    257 
    258  UniquePtr<JsepVideoCodecDescription> GetVideoCodec(
    259      const JsepTrack& track, size_t expectedSize = 1,
    260      size_t codecIndex = 0) const {
    261    auto codec =
    262        GetCodec(track, SdpMediaSection::kVideo, expectedSize, codecIndex);
    263    return UniquePtr<JsepVideoCodecDescription>(
    264        static_cast<JsepVideoCodecDescription*>(codec.release()));
    265  }
    266 
    267  UniquePtr<JsepAudioCodecDescription> GetAudioCodec(
    268      const JsepTrack& track, size_t expectedSize = 1,
    269      size_t codecIndex = 0) const {
    270    auto codec =
    271        GetCodec(track, SdpMediaSection::kAudio, expectedSize, codecIndex);
    272    return UniquePtr<JsepAudioCodecDescription>(
    273        static_cast<JsepAudioCodecDescription*>(codec.release()));
    274  }
    275 
    276  void CheckOtherFbExists(const JsepVideoCodecDescription& videoCodec,
    277                          SdpRtcpFbAttributeList::Type type) const {
    278    for (const auto& fb : videoCodec.mOtherFbTypes) {
    279      if (fb.type == type) {
    280        return;  // found the RtcpFb type, so stop looking
    281      }
    282    }
    283    FAIL();  // RtcpFb type not found
    284  }
    285 
    286  void SanityCheckRtcpFbs(const JsepVideoCodecDescription& a,
    287                          const JsepVideoCodecDescription& b) const {
    288    ASSERT_EQ(a.mNackFbTypes.size(), b.mNackFbTypes.size());
    289    ASSERT_EQ(a.mAckFbTypes.size(), b.mAckFbTypes.size());
    290    ASSERT_EQ(a.mCcmFbTypes.size(), b.mCcmFbTypes.size());
    291    ASSERT_EQ(a.mOtherFbTypes.size(), b.mOtherFbTypes.size());
    292  }
    293 
    294  void SanityCheckCodecs(const JsepCodecDescription& a,
    295                         const JsepCodecDescription& b) const {
    296 #define MSG                                                               \
    297  "For codecs " << a.mName << " (" << a.mDirection << ") and " << b.mName \
    298                << " (" << b.mDirection << ")"
    299    ASSERT_EQ(a.Type(), b.Type()) << MSG;
    300    if (a.Type() != SdpMediaSection::kApplication) {
    301      ASSERT_EQ(a.mDefaultPt, b.mDefaultPt) << MSG;
    302    }
    303    ASSERT_EQ(a.mName, b.mName);
    304    if (!mExpectDifferingFmtp) {
    305      ASSERT_EQ(a.mSdpFmtpLine, b.mSdpFmtpLine) << MSG;
    306    }
    307    ASSERT_EQ(a.mClock, b.mClock) << MSG;
    308    ASSERT_EQ(a.mChannels, b.mChannels) << MSG;
    309    ASSERT_NE(a.mDirection, b.mDirection) << MSG;
    310    // These constraints are for fmtp and rid, which _are_ signaled
    311    ASSERT_EQ(a.mConstraints, b.mConstraints) << MSG;
    312 #undef MSG
    313 
    314    if (a.Type() == SdpMediaSection::kVideo) {
    315      SanityCheckRtcpFbs(static_cast<const JsepVideoCodecDescription&>(a),
    316                         static_cast<const JsepVideoCodecDescription&>(b));
    317    }
    318  }
    319 
    320  void SanityCheckEncodings(const JsepTrackEncoding& a,
    321                            const JsepTrackEncoding& b) const {
    322    ASSERT_EQ(a.GetCodecs().size(), b.GetCodecs().size());
    323    for (size_t i = 0; i < a.GetCodecs().size(); ++i) {
    324      SanityCheckCodecs(*a.GetCodecs()[i], *b.GetCodecs()[i]);
    325    }
    326 
    327    ASSERT_EQ(a.mRid, b.mRid);
    328    // mConstraints will probably differ, since they are not signaled to the
    329    // other side.
    330  }
    331 
    332  void SanityCheckNegotiatedDetails(const JsepTrack& aTrack,
    333                                    const JsepTrack& bTrack,
    334                                    bool codecsMustMatch) const {
    335    const auto aDetails = *aTrack.GetNegotiatedDetails();
    336    const auto bDetails = *bTrack.GetNegotiatedDetails();
    337    ASSERT_EQ(aDetails.GetEncodingCount(), bDetails.GetEncodingCount());
    338    if (codecsMustMatch) {
    339      for (size_t i = 0; i < aDetails.GetEncodingCount(); ++i) {
    340        SanityCheckEncodings(aDetails.GetEncoding(i), bDetails.GetEncoding(i));
    341      }
    342    }
    343 
    344    ASSERT_EQ(aTrack.GetUniqueReceivePayloadTypes().size(),
    345              bTrack.GetUniqueReceivePayloadTypes().size());
    346    for (size_t i = 0; i < aTrack.GetUniqueReceivePayloadTypes().size(); ++i) {
    347      ASSERT_EQ(aTrack.GetUniqueReceivePayloadTypes()[i],
    348                bTrack.GetUniqueReceivePayloadTypes()[i]);
    349    }
    350  }
    351 
    352  void SanityCheckTracks(const JsepTrack& a, const JsepTrack& b,
    353                         bool codecsMustMatch) const {
    354    if (!a.GetNegotiatedDetails()) {
    355      ASSERT_FALSE(!!b.GetNegotiatedDetails());
    356      return;
    357    }
    358 
    359    ASSERT_TRUE(!!a.GetNegotiatedDetails());
    360    ASSERT_TRUE(!!b.GetNegotiatedDetails());
    361    ASSERT_EQ(a.GetMediaType(), b.GetMediaType());
    362    ASSERT_EQ(a.GetStreamIds(), b.GetStreamIds());
    363    ASSERT_EQ(a.GetCNAME(), b.GetCNAME());
    364    ASSERT_NE(a.GetDirection(), b.GetDirection());
    365    ASSERT_EQ(a.GetSsrcs().size(), b.GetSsrcs().size());
    366    for (size_t i = 0; i < a.GetSsrcs().size(); ++i) {
    367      ASSERT_EQ(a.GetSsrcs()[i], b.GetSsrcs()[i]);
    368    }
    369 
    370    SanityCheckNegotiatedDetails(a, b, codecsMustMatch);
    371  }
    372 
    373  void SanityCheck(bool offerCodecsMatchAnswer = true) const {
    374    SanityCheckTracks(mSendOff, mRecvAns, true);
    375    SanityCheckTracks(mRecvOff, mSendAns, offerCodecsMatchAnswer);
    376  }
    377 
    378 protected:
    379  JsepTrack mSendOff;
    380  JsepTrack mRecvOff;
    381  JsepTrack mSendAns;
    382  JsepTrack mRecvAns;
    383  std::vector<UniquePtr<JsepCodecDescription>> mOffCodecs;
    384  std::vector<UniquePtr<JsepCodecDescription>> mAnsCodecs;
    385  UniquePtr<Sdp> mOffer;
    386  UniquePtr<Sdp> mAnswer;
    387  SsrcGenerator mSsrcGenerator;
    388  bool mExpectDifferingFmtp = false;
    389 };
    390 
    391 TEST_F(JsepTrackTestBase, CreateDestroy) {}
    392 
    393 TEST_F(JsepTrackTest, CreateDestroy) { Init(SdpMediaSection::kAudio); }
    394 
    395 TEST_F(JsepTrackTest, AudioNegotiation) {
    396  Init(SdpMediaSection::kAudio);
    397  OfferAnswer();
    398  CheckOffEncodingCount(1);
    399  CheckAnsEncodingCount(1);
    400 }
    401 
    402 TEST_F(JsepTrackTest, VideoNegotiation) {
    403  Init(SdpMediaSection::kVideo);
    404  OfferAnswer();
    405  CheckOffEncodingCount(1);
    406  CheckAnsEncodingCount(1);
    407 }
    408 
    409 class CheckForCodecType {
    410 public:
    411  explicit CheckForCodecType(SdpMediaSection::MediaType type, bool* result)
    412      : mResult(result), mType(type) {}
    413 
    414  void operator()(const UniquePtr<JsepCodecDescription>& codec) {
    415    if (codec->Type() == mType) {
    416      *mResult = true;
    417    }
    418  }
    419 
    420 private:
    421  bool* mResult;
    422  SdpMediaSection::MediaType mType;
    423 };
    424 
    425 TEST_F(JsepTrackTest, CheckForAnsweringWithExtmapAllowMixedWhenNotOffered) {
    426  Init(SdpMediaSection::kAudio);
    427 
    428  CreateOffer();
    429  mOffer->GetMediaSection(0).GetAttributeList().RemoveAttribute(
    430      SdpAttribute::kExtmapAllowMixedAttribute);
    431  CreateAnswer();
    432 
    433  ASSERT_FALSE(mAnswer->GetMediaSection(0).GetAttributeList().HasAttribute(
    434      SdpAttribute::kExtmapAllowMixedAttribute));
    435  Negotiate();
    436  SanityCheck();
    437 }
    438 
    439 TEST_F(JsepTrackTest, CheckForMismatchedAudioCodecAndVideoTrack) {
    440  std::vector<UniquePtr<JsepCodecDescription>> offerCodecs;
    441 
    442  // make codecs including telephone-event (an audio codec)
    443  offerCodecs = MakeCodecs({.addDtmfCodec = true});
    444  JsepTrack videoTrack(SdpMediaSection::kVideo, sdp::kSend);
    445  videoTrack.UpdateStreamIds(std::vector<std::string>(1, "stream_id"));
    446  // populate codecs and then make sure we don't have any audio codecs
    447  // in the video track
    448  videoTrack.PopulateCodecs(offerCodecs);
    449 
    450  bool found = false;
    451  videoTrack.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
    452  ASSERT_FALSE(found);
    453 
    454  found = false;
    455  videoTrack.ForEachCodec(CheckForCodecType(SdpMediaSection::kVideo, &found));
    456  ASSERT_TRUE(found);  // for sanity, make sure we did find video codecs
    457 }
    458 
    459 TEST_F(JsepTrackTest, CheckVideoTrackWithHackedDtmfSdp) {
    460  Init(SdpMediaSection::kVideo);
    461  CreateOffer();
    462  // make sure we don't find sdp containing telephone-event in video track
    463  ASSERT_EQ(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
    464            std::string::npos);
    465  // force audio codec telephone-event into video m= section of offer
    466  GetOffer().AddCodec("101", "telephone-event", 8000, 1);
    467  // make sure we _do_ find sdp containing telephone-event in video track
    468  ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
    469            std::string::npos);
    470 
    471  CreateAnswer();
    472  // make sure we don't find sdp containing telephone-event in video track
    473  ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
    474            std::string::npos);
    475  // force audio codec telephone-event into video m= section of answer
    476  GetAnswer().AddCodec("101", "telephone-event", 8000, 1);
    477  // make sure we _do_ find sdp containing telephone-event in video track
    478  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
    479            std::string::npos);
    480 
    481  Negotiate();
    482  SanityCheck();
    483 
    484  CheckOffEncodingCount(1);
    485  CheckAnsEncodingCount(1);
    486 
    487  // make sure we still don't find any audio codecs in the video track after
    488  // hacking the sdp
    489  bool found = false;
    490  mSendOff.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
    491  ASSERT_FALSE(found);
    492  mRecvOff.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
    493  ASSERT_FALSE(found);
    494  mSendAns.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
    495  ASSERT_FALSE(found);
    496  mRecvAns.ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
    497  ASSERT_FALSE(found);
    498 }
    499 
    500 TEST_F(JsepTrackTest, AudioNegotiationOffererDtmf) {
    501  InitCodecs(
    502      {.offer = {.addDtmfCodec = true}, .answer = {.addDtmfCodec = false}});
    503 
    504  InitTracks(SdpMediaSection::kAudio);
    505  InitSdp(SdpMediaSection::kAudio);
    506  OfferAnswer(false);
    507 
    508  CheckOffEncodingCount(1);
    509  CheckAnsEncodingCount(1);
    510 
    511  ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
    512            std::string::npos);
    513  ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
    514            std::string::npos);
    515 
    516  ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
    517  ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
    518 
    519  UniquePtr<JsepAudioCodecDescription> track;
    520  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 2, 0)));
    521  ASSERT_EQ("109", track->mDefaultPt);
    522  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
    523  ASSERT_EQ("109", track->mDefaultPt);
    524  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 2, 0)));
    525  ASSERT_EQ("109", track->mDefaultPt);
    526  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 2, 0)));
    527  ASSERT_EQ("109", track->mDefaultPt);
    528  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 2, 1)));
    529  ASSERT_EQ("9", track->mDefaultPt);
    530  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
    531  ASSERT_EQ("9", track->mDefaultPt);
    532  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 2, 1)));
    533  ASSERT_EQ("9", track->mDefaultPt);
    534  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 2, 1)));
    535  ASSERT_EQ("9", track->mDefaultPt);
    536 }
    537 
    538 TEST_F(JsepTrackTest, AudioNegotiationAnswererDtmf) {
    539  InitCodecs(
    540      {.offer = {.addDtmfCodec = false}, .answer = {.addDtmfCodec = true}});
    541 
    542  InitTracks(SdpMediaSection::kAudio);
    543  InitSdp(SdpMediaSection::kAudio);
    544  OfferAnswer();
    545 
    546  CheckOffEncodingCount(1);
    547  CheckAnsEncodingCount(1);
    548 
    549  ASSERT_EQ(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
    550            std::string::npos);
    551  ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
    552            std::string::npos);
    553 
    554  ASSERT_EQ(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
    555  ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
    556 
    557  UniquePtr<JsepAudioCodecDescription> track;
    558  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 2, 0)));
    559  ASSERT_EQ("109", track->mDefaultPt);
    560  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 2, 0)));
    561  ASSERT_EQ("109", track->mDefaultPt);
    562  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 2, 0)));
    563  ASSERT_EQ("109", track->mDefaultPt);
    564  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 2, 0)));
    565  ASSERT_EQ("109", track->mDefaultPt);
    566  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 2, 1)));
    567  ASSERT_EQ("9", track->mDefaultPt);
    568  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 2, 1)));
    569  ASSERT_EQ("9", track->mDefaultPt);
    570  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 2, 1)));
    571  ASSERT_EQ("9", track->mDefaultPt);
    572  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 2, 1)));
    573  ASSERT_EQ("9", track->mDefaultPt);
    574 }
    575 
    576 TEST_F(JsepTrackTest, AudioNegotiationOffererAnswererDtmf) {
    577  InitCodecs(
    578      {.offer = {.addDtmfCodec = true}, .answer = {.addDtmfCodec = true}});
    579 
    580  InitTracks(SdpMediaSection::kAudio);
    581  InitSdp(SdpMediaSection::kAudio);
    582  OfferAnswer();
    583 
    584  CheckOffEncodingCount(1);
    585  CheckAnsEncodingCount(1);
    586 
    587  ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
    588            std::string::npos);
    589  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
    590            std::string::npos);
    591 
    592  ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
    593  ASSERT_NE(mAnswer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
    594 
    595  UniquePtr<JsepAudioCodecDescription> track;
    596  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 0)));
    597  ASSERT_EQ("109", track->mDefaultPt);
    598  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
    599  ASSERT_EQ("109", track->mDefaultPt);
    600  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 0)));
    601  ASSERT_EQ("109", track->mDefaultPt);
    602  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 0)));
    603  ASSERT_EQ("109", track->mDefaultPt);
    604  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 1)));
    605  ASSERT_EQ("9", track->mDefaultPt);
    606  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
    607  ASSERT_EQ("9", track->mDefaultPt);
    608  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 1)));
    609  ASSERT_EQ("9", track->mDefaultPt);
    610  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 1)));
    611  ASSERT_EQ("9", track->mDefaultPt);
    612  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 2)));
    613  ASSERT_EQ("101", track->mDefaultPt);
    614  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 2)));
    615  ASSERT_EQ("101", track->mDefaultPt);
    616  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 2)));
    617  ASSERT_EQ("101", track->mDefaultPt);
    618  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 2)));
    619  ASSERT_EQ("101", track->mDefaultPt);
    620 }
    621 
    622 TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererNoFmtpAnswererFmtp) {
    623  InitCodecs(
    624      {.offer = {.addDtmfCodec = true}, .answer = {.addDtmfCodec = true}});
    625 
    626  mExpectDifferingFmtp = true;
    627 
    628  InitTracks(SdpMediaSection::kAudio);
    629  InitSdp(SdpMediaSection::kAudio);
    630 
    631  CreateOffer();
    632  GetOffer().RemoveFmtp("101");
    633 
    634  CreateAnswer();
    635 
    636  Negotiate();
    637  SanityCheck();
    638 
    639  CheckOffEncodingCount(1);
    640  CheckAnsEncodingCount(1);
    641 
    642  ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
    643            std::string::npos);
    644  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
    645            std::string::npos);
    646 
    647  ASSERT_EQ(mOffer->ToString().find("a=fmtp:101"), std::string::npos);
    648  ASSERT_NE(mAnswer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
    649 
    650  UniquePtr<JsepAudioCodecDescription> track;
    651  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 0)));
    652  ASSERT_EQ("109", track->mDefaultPt);
    653  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
    654  ASSERT_EQ("109", track->mDefaultPt);
    655  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 0)));
    656  ASSERT_EQ("109", track->mDefaultPt);
    657  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 0)));
    658  ASSERT_EQ("109", track->mDefaultPt);
    659  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 1)));
    660  ASSERT_EQ("9", track->mDefaultPt);
    661  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
    662  ASSERT_EQ("9", track->mDefaultPt);
    663  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 1)));
    664  ASSERT_EQ("9", track->mDefaultPt);
    665  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 1)));
    666  ASSERT_EQ("9", track->mDefaultPt);
    667  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 2)));
    668  ASSERT_EQ("101", track->mDefaultPt);
    669  ASSERT_EQ("0-15", track->mSdpFmtpLine.valueOr("nothing"));
    670  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 2)));
    671  ASSERT_EQ("101", track->mDefaultPt);
    672  ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
    673  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 2)));
    674  ASSERT_EQ("101", track->mDefaultPt);
    675  ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
    676  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 2)));
    677  ASSERT_EQ("101", track->mDefaultPt);
    678  ASSERT_EQ("0-15", track->mSdpFmtpLine.valueOr("nothing"));
    679 }
    680 
    681 TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererFmtpAnswererNoFmtp) {
    682  InitCodecs(
    683      {.offer = {.addDtmfCodec = true}, .answer = {.addDtmfCodec = true}});
    684 
    685  mExpectDifferingFmtp = true;
    686 
    687  InitTracks(SdpMediaSection::kAudio);
    688  InitSdp(SdpMediaSection::kAudio);
    689 
    690  CreateOffer();
    691 
    692  CreateAnswer();
    693  GetAnswer().RemoveFmtp("101");
    694 
    695  Negotiate();
    696  SanityCheck();
    697 
    698  CheckOffEncodingCount(1);
    699  CheckAnsEncodingCount(1);
    700 
    701  ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
    702            std::string::npos);
    703  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
    704            std::string::npos);
    705 
    706  ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
    707  ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
    708 
    709  UniquePtr<JsepAudioCodecDescription> track;
    710  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 0)));
    711  ASSERT_EQ("109", track->mDefaultPt);
    712  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
    713  ASSERT_EQ("109", track->mDefaultPt);
    714  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 0)));
    715  ASSERT_EQ("109", track->mDefaultPt);
    716  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 0)));
    717  ASSERT_EQ("109", track->mDefaultPt);
    718  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 1)));
    719  ASSERT_EQ("9", track->mDefaultPt);
    720  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
    721  ASSERT_EQ("9", track->mDefaultPt);
    722  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 1)));
    723  ASSERT_EQ("9", track->mDefaultPt);
    724  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 1)));
    725  ASSERT_EQ("9", track->mDefaultPt);
    726  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 2)));
    727  ASSERT_EQ("101", track->mDefaultPt);
    728  ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
    729  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 2)));
    730  ASSERT_EQ("101", track->mDefaultPt);
    731  ASSERT_EQ("0-15", track->mSdpFmtpLine.valueOr("nothing"));
    732  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 2)));
    733  ASSERT_EQ("101", track->mDefaultPt);
    734  ASSERT_EQ("0-15", track->mSdpFmtpLine.valueOr("nothing"));
    735  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 2)));
    736  ASSERT_EQ("101", track->mDefaultPt);
    737  ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
    738 }
    739 
    740 TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererNoFmtpAnswererNoFmtp) {
    741  InitCodecs(
    742      {.offer = {.addDtmfCodec = true}, .answer = {.addDtmfCodec = true}});
    743 
    744  mExpectDifferingFmtp = true;
    745 
    746  InitTracks(SdpMediaSection::kAudio);
    747  InitSdp(SdpMediaSection::kAudio);
    748 
    749  CreateOffer();
    750  GetOffer().RemoveFmtp("101");
    751 
    752  CreateAnswer();
    753  GetAnswer().RemoveFmtp("101");
    754 
    755  Negotiate();
    756  SanityCheck();
    757 
    758  CheckOffEncodingCount(1);
    759  CheckAnsEncodingCount(1);
    760 
    761  ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
    762            std::string::npos);
    763  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
    764            std::string::npos);
    765 
    766  ASSERT_EQ(mOffer->ToString().find("a=fmtp:101"), std::string::npos);
    767  ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
    768 
    769  UniquePtr<JsepAudioCodecDescription> track;
    770  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 0)));
    771  ASSERT_EQ("109", track->mDefaultPt);
    772  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 0)));
    773  ASSERT_EQ("109", track->mDefaultPt);
    774  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 0)));
    775  ASSERT_EQ("109", track->mDefaultPt);
    776  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 0)));
    777  ASSERT_EQ("109", track->mDefaultPt);
    778  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 1)));
    779  ASSERT_EQ("9", track->mDefaultPt);
    780  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 1)));
    781  ASSERT_EQ("9", track->mDefaultPt);
    782  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 1)));
    783  ASSERT_EQ("9", track->mDefaultPt);
    784  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 1)));
    785  ASSERT_EQ("9", track->mDefaultPt);
    786  ASSERT_TRUE((track = GetAudioCodec(mSendOff, 3, 2)));
    787  ASSERT_EQ("101", track->mDefaultPt);
    788  ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
    789  ASSERT_TRUE((track = GetAudioCodec(mRecvOff, 3, 2)));
    790  ASSERT_EQ("101", track->mDefaultPt);
    791  ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
    792  ASSERT_TRUE((track = GetAudioCodec(mSendAns, 3, 2)));
    793  ASSERT_EQ("101", track->mDefaultPt);
    794  ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
    795  ASSERT_TRUE((track = GetAudioCodec(mRecvAns, 3, 2)));
    796  ASSERT_EQ("101", track->mDefaultPt);
    797  ASSERT_EQ("nothing", track->mSdpFmtpLine.valueOr("nothing"));
    798 }
    799 
    800 TEST_F(JsepTrackTest, VideoNegotationOffererFEC) {
    801  InitCodecs(
    802      {.offer = {.addFecCodecs = true}, .answer = {.addFecCodecs = false}});
    803 
    804  InitTracks(SdpMediaSection::kVideo);
    805  InitSdp(SdpMediaSection::kVideo);
    806  OfferAnswer(false);
    807 
    808  CheckOffEncodingCount(1);
    809  CheckAnsEncodingCount(1);
    810 
    811  ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    812  ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    813  ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    814  ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    815 
    816  UniquePtr<JsepVideoCodecDescription> track;
    817  ASSERT_TRUE((track = GetVideoCodec(mSendOff, 3, 0)));
    818  ASSERT_EQ("120", track->mDefaultPt);
    819  ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 5, 0)));
    820  ASSERT_EQ("120", track->mDefaultPt);
    821  ASSERT_TRUE((track = GetVideoCodec(mSendAns, 3, 0)));
    822  ASSERT_EQ("120", track->mDefaultPt);
    823  ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 3, 0)));
    824  ASSERT_EQ("120", track->mDefaultPt);
    825  ASSERT_TRUE((track = GetVideoCodec(mSendOff, 3, 1)));
    826  ASSERT_EQ("126", track->mDefaultPt);
    827  ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 5, 1)));
    828  ASSERT_EQ("126", track->mDefaultPt);
    829  ASSERT_TRUE((track = GetVideoCodec(mSendAns, 3, 1)));
    830  ASSERT_EQ("126", track->mDefaultPt);
    831  ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 3, 1)));
    832  ASSERT_EQ("126", track->mDefaultPt);
    833 }
    834 
    835 TEST_F(JsepTrackTest, VideoNegotationAnswererFEC) {
    836  InitCodecs(
    837      {.offer = {.addFecCodecs = false}, .answer = {.addFecCodecs = true}});
    838 
    839  InitTracks(SdpMediaSection::kVideo);
    840  InitSdp(SdpMediaSection::kVideo);
    841  OfferAnswer();
    842 
    843  CheckOffEncodingCount(1);
    844  CheckAnsEncodingCount(1);
    845 
    846  ASSERT_EQ(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    847  ASSERT_EQ(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    848  ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    849  ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    850 
    851  UniquePtr<JsepVideoCodecDescription> track;
    852  ASSERT_TRUE((track = GetVideoCodec(mSendOff, 3, 0)));
    853  ASSERT_EQ("120", track->mDefaultPt);
    854  ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 3, 0)));
    855  ASSERT_EQ("120", track->mDefaultPt);
    856  ASSERT_TRUE((track = GetVideoCodec(mSendAns, 3, 0)));
    857  ASSERT_EQ("120", track->mDefaultPt);
    858  ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 3, 0)));
    859  ASSERT_EQ("120", track->mDefaultPt);
    860  ASSERT_TRUE((track = GetVideoCodec(mSendOff, 3, 1)));
    861  ASSERT_EQ("126", track->mDefaultPt);
    862  ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 3, 1)));
    863  ASSERT_EQ("126", track->mDefaultPt);
    864  ASSERT_TRUE((track = GetVideoCodec(mSendAns, 3, 1)));
    865  ASSERT_EQ("126", track->mDefaultPt);
    866  ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 3, 1)));
    867  ASSERT_EQ("126", track->mDefaultPt);
    868 }
    869 
    870 TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFEC) {
    871  InitCodecs(
    872      {.offer = {.addFecCodecs = true}, .answer = {.addFecCodecs = true}});
    873 
    874  InitTracks(SdpMediaSection::kVideo);
    875  InitSdp(SdpMediaSection::kVideo);
    876  OfferAnswer();
    877 
    878  CheckOffEncodingCount(1);
    879  CheckAnsEncodingCount(1);
    880 
    881  ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    882  ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    883  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    884  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    885 
    886  UniquePtr<JsepVideoCodecDescription> track;
    887  ASSERT_TRUE((track = GetVideoCodec(mSendOff, 5)));
    888  ASSERT_EQ("120", track->mDefaultPt);
    889  ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 5)));
    890  ASSERT_EQ("120", track->mDefaultPt);
    891  ASSERT_TRUE((track = GetVideoCodec(mSendAns, 5)));
    892  ASSERT_EQ("120", track->mDefaultPt);
    893  ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 5)));
    894  ASSERT_EQ("120", track->mDefaultPt);
    895 }
    896 
    897 TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECPreferred) {
    898  InitCodecs({.offer = {.addFecCodecs = true, .preferRed = true},
    899              .answer = {.addFecCodecs = true}});
    900 
    901  InitTracks(SdpMediaSection::kVideo);
    902  InitSdp(SdpMediaSection::kVideo);
    903  OfferAnswer();
    904 
    905  CheckOffEncodingCount(1);
    906  CheckAnsEncodingCount(1);
    907 
    908  ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    909  ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    910  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    911  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    912 
    913  UniquePtr<JsepVideoCodecDescription> track;
    914  // We should have 4 codecs, the first of which is VP8, because having a
    915  // pseudo codec come first is silly.
    916  ASSERT_TRUE((track = GetVideoCodec(mSendOff, 5)));
    917  ASSERT_EQ("120", track->mDefaultPt);
    918  ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 5)));
    919  ASSERT_EQ("120", track->mDefaultPt);
    920  ASSERT_TRUE((track = GetVideoCodec(mSendAns, 5)));
    921  ASSERT_EQ("120", track->mDefaultPt);
    922  ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 5)));
    923  ASSERT_EQ("120", track->mDefaultPt);
    924 }
    925 
    926 // Make sure we only put the right things in the fmtp:122 120/.... line
    927 TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECMismatch) {
    928  InitCodecs({.offer = {.addFecCodecs = true, .preferRed = true},
    929              .answer = {.addFecCodecs = true}});
    930  // remove h264 & AV1 from answer codecs
    931  ASSERT_EQ("H264", mAnsCodecs[3]->mName);
    932  ASSERT_EQ("AV1", mAnsCodecs[4]->mName);
    933  mAnsCodecs.erase(mAnsCodecs.begin() + 4);
    934  mAnsCodecs.erase(mAnsCodecs.begin() + 3);
    935 
    936  InitTracks(SdpMediaSection::kVideo);
    937  InitSdp(SdpMediaSection::kVideo);
    938  OfferAnswer(false);
    939 
    940  CheckOffEncodingCount(1);
    941  CheckAnsEncodingCount(1);
    942 
    943  ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    944  ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    945  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    946  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    947 
    948  // We should have 3 codecs, the first of which is VP8, because having a
    949  // pseudo codec come first is silly.
    950  UniquePtr<JsepVideoCodecDescription> track;
    951  ASSERT_TRUE((track = GetVideoCodec(mSendOff, 3)));
    952  ASSERT_EQ("120", track->mDefaultPt);
    953  ASSERT_TRUE((track = GetVideoCodec(mRecvOff, 5)));
    954  ASSERT_EQ("120", track->mDefaultPt);
    955  ASSERT_TRUE((track = GetVideoCodec(mSendAns, 3)));
    956  ASSERT_EQ("120", track->mDefaultPt);
    957  ASSERT_TRUE((track = GetVideoCodec(mRecvAns, 3)));
    958  ASSERT_EQ("120", track->mDefaultPt);
    959 }
    960 
    961 TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECZeroVP9Codec) {
    962  MockJsepCodecPreferences prefs;
    963  mOffCodecs = MakeCodecs({.addFecCodecs = true});
    964  auto vp9 = JsepVideoCodecDescription::CreateDefaultVP9(prefs);
    965  vp9->mDefaultPt = "0";
    966  mOffCodecs.push_back(std::move(vp9));
    967 
    968  ASSERT_EQ(9U, mOffCodecs.size());
    969  JsepVideoCodecDescription& red =
    970      static_cast<JsepVideoCodecDescription&>(*mOffCodecs[5]);
    971  ASSERT_EQ("red", red.mName);
    972 
    973  mAnsCodecs = MakeCodecs({.addFecCodecs = true});
    974 
    975  InitTracks(SdpMediaSection::kVideo);
    976  InitSdp(SdpMediaSection::kVideo);
    977  OfferAnswer();
    978 
    979  CheckOffEncodingCount(1);
    980  CheckAnsEncodingCount(1);
    981 
    982  ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    983  ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    984  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
    985  ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
    986 }
    987 
    988 TEST_F(JsepTrackTest, VideoNegotiationOfferRemb) {
    989  // enable remb on the offer codecs
    990  InitCodecs({.offer = {.enableRemb = true, .enableTransportCC = false},
    991              .answer = {.enableRemb = false, .enableTransportCC = false}});
    992  InitTracks(SdpMediaSection::kVideo);
    993  InitSdp(SdpMediaSection::kVideo);
    994  OfferAnswer();
    995 
    996  // make sure REMB is on offer and not on answer
    997  ASSERT_NE(mOffer->ToString().find("a=rtcp-fb:120 goog-remb"),
    998            std::string::npos);
    999  ASSERT_EQ(mAnswer->ToString().find("a=rtcp-fb:120 goog-remb"),
   1000            std::string::npos);
   1001  CheckOffEncodingCount(1);
   1002  CheckAnsEncodingCount(1);
   1003 
   1004  UniquePtr<JsepVideoCodecDescription> codec;
   1005  ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 3, 0)));
   1006  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1007 
   1008  ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 3, 0)));
   1009  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1010  ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 3, 0)));
   1011  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1012  ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 3, 0)));
   1013  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1014 }
   1015 
   1016 TEST_F(JsepTrackTest, VideoNegotiationAnswerRemb) {
   1017  InitCodecs({.offer = {.enableRemb = false, .enableTransportCC = false},
   1018              .answer = {.enableRemb = true, .enableTransportCC = false}});
   1019  // enable remb on the answer codecs
   1020  ((JsepVideoCodecDescription&)*mAnsCodecs[2]).EnableRemb();
   1021  InitTracks(SdpMediaSection::kVideo);
   1022  InitSdp(SdpMediaSection::kVideo);
   1023  OfferAnswer();
   1024 
   1025  // make sure REMB is not on offer and not on answer
   1026  ASSERT_EQ(mOffer->ToString().find("a=rtcp-fb:120 goog-remb"),
   1027            std::string::npos);
   1028  ASSERT_EQ(mAnswer->ToString().find("a=rtcp-fb:120 goog-remb"),
   1029            std::string::npos);
   1030  CheckOffEncodingCount(1);
   1031  CheckAnsEncodingCount(1);
   1032 
   1033  UniquePtr<JsepVideoCodecDescription> codec;
   1034  ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 3, 0)));
   1035  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1036  ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 3, 0)));
   1037  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1038  ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 3, 0)));
   1039  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1040  ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 3, 0)));
   1041  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1042 }
   1043 
   1044 TEST_F(JsepTrackTest, VideoNegotiationOfferAnswerRemb) {
   1045  InitCodecs({.offer = {.enableRemb = true, .enableTransportCC = false},
   1046              .answer = {.enableRemb = true, .enableTransportCC = false}});
   1047  // enable remb on the offer and answer codecs
   1048  ((JsepVideoCodecDescription&)*mOffCodecs[2]).EnableRemb();
   1049  ((JsepVideoCodecDescription&)*mAnsCodecs[2]).EnableRemb();
   1050  InitTracks(SdpMediaSection::kVideo);
   1051  InitSdp(SdpMediaSection::kVideo);
   1052  OfferAnswer();
   1053 
   1054  // make sure REMB is on offer and on answer
   1055  ASSERT_NE(mOffer->ToString().find("a=rtcp-fb:120 goog-remb"),
   1056            std::string::npos);
   1057  ASSERT_NE(mAnswer->ToString().find("a=rtcp-fb:120 goog-remb"),
   1058            std::string::npos);
   1059  CheckOffEncodingCount(1);
   1060  CheckAnsEncodingCount(1);
   1061 
   1062  UniquePtr<JsepVideoCodecDescription> codec;
   1063  ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 3, 0)));
   1064  ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
   1065  CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kRemb);
   1066  ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 3, 0)));
   1067  ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
   1068  CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kRemb);
   1069  ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 3, 0)));
   1070  ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
   1071  CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kRemb);
   1072  ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 3, 0)));
   1073  ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
   1074  CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kRemb);
   1075 }
   1076 
   1077 TEST_F(JsepTrackTest, VideoNegotiationOfferTransportCC) {
   1078  InitCodecs({.offer = {.enableRemb = false, .enableTransportCC = true},
   1079              .answer = {.enableRemb = false, .enableTransportCC = false}});
   1080  // enable TransportCC on the offer codecs
   1081  ((JsepVideoCodecDescription&)*mOffCodecs[2]).EnableTransportCC();
   1082  InitTracks(SdpMediaSection::kVideo);
   1083  InitSdp(SdpMediaSection::kVideo);
   1084  OfferAnswer();
   1085 
   1086  // make sure TransportCC is on offer and not on answer
   1087  ASSERT_NE(mOffer->ToString().find("a=rtcp-fb:120 transport-cc"),
   1088            std::string::npos);
   1089  ASSERT_EQ(mAnswer->ToString().find("a=rtcp-fb:120 transport-cc"),
   1090            std::string::npos);
   1091  CheckOffEncodingCount(1);
   1092  CheckAnsEncodingCount(1);
   1093 
   1094  UniquePtr<JsepVideoCodecDescription> codec;
   1095  ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 3, 0)));
   1096  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1097  ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 3, 0)));
   1098  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1099  ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 3, 0)));
   1100  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1101  ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 3, 0)));
   1102  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1103 }
   1104 
   1105 TEST_F(JsepTrackTest, VideoNegotiationAnswerTransportCC) {
   1106  InitCodecs({.offer = {.enableRemb = false, .enableTransportCC = false},
   1107              .answer = {.enableRemb = false, .enableTransportCC = true}});
   1108  // enable TransportCC on the answer codecs
   1109  ((JsepVideoCodecDescription&)*mAnsCodecs[2]).EnableTransportCC();
   1110  InitTracks(SdpMediaSection::kVideo);
   1111  InitSdp(SdpMediaSection::kVideo);
   1112  OfferAnswer();
   1113 
   1114  // make sure TransportCC is not on offer and not on answer
   1115  ASSERT_EQ(mOffer->ToString().find("a=rtcp-fb:120 transport-cc"),
   1116            std::string::npos);
   1117  ASSERT_EQ(mAnswer->ToString().find("a=rtcp-fb:120 transport-cc"),
   1118            std::string::npos);
   1119  CheckOffEncodingCount(1);
   1120  CheckAnsEncodingCount(1);
   1121 
   1122  UniquePtr<JsepVideoCodecDescription> codec;
   1123  ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 3, 0)));
   1124  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1125  ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 3, 0)));
   1126  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1127  ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 3, 0)));
   1128  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1129  ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 3, 0)));
   1130  ASSERT_EQ(codec->mOtherFbTypes.size(), 0U);
   1131 }
   1132 
   1133 TEST_F(JsepTrackTest, VideoNegotiationOfferAnswerTransportCC) {
   1134  InitCodecs({.enableRemb = false, .enableTransportCC = true});
   1135  // enable TransportCC on the offer and answer codecs
   1136  ((JsepVideoCodecDescription&)*mOffCodecs[2]).EnableTransportCC();
   1137  ((JsepVideoCodecDescription&)*mAnsCodecs[2]).EnableTransportCC();
   1138  InitTracks(SdpMediaSection::kVideo);
   1139  InitSdp(SdpMediaSection::kVideo);
   1140  OfferAnswer();
   1141 
   1142  // make sure TransportCC is on offer and on answer
   1143  ASSERT_NE(mOffer->ToString().find("a=rtcp-fb:120 transport-cc"),
   1144            std::string::npos);
   1145  ASSERT_NE(mAnswer->ToString().find("a=rtcp-fb:120 transport-cc"),
   1146            std::string::npos);
   1147  CheckOffEncodingCount(1);
   1148  CheckAnsEncodingCount(1);
   1149 
   1150  UniquePtr<JsepVideoCodecDescription> codec;
   1151  ASSERT_TRUE((codec = GetVideoCodec(mSendOff, 3, 0)));
   1152  ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
   1153  CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kTransportCC);
   1154  ASSERT_TRUE((codec = GetVideoCodec(mRecvAns, 3, 0)));
   1155  ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
   1156  CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kTransportCC);
   1157  ASSERT_TRUE((codec = GetVideoCodec(mSendAns, 3, 0)));
   1158  ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
   1159  CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kTransportCC);
   1160  ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 3, 0)));
   1161  ASSERT_EQ(codec->mOtherFbTypes.size(), 1U);
   1162  CheckOtherFbExists(*codec, SdpRtcpFbAttributeList::kTransportCC);
   1163 }
   1164 
   1165 TEST_F(JsepTrackTest, AudioOffSendonlyAnsRecvonly) {
   1166  Init(SdpMediaSection::kAudio);
   1167  GetOffer().SetDirection(SdpDirectionAttribute::kSendonly);
   1168  GetAnswer().SetDirection(SdpDirectionAttribute::kRecvonly);
   1169  OfferAnswer();
   1170  CheckOffEncodingCount(1);
   1171  CheckAnsEncodingCount(0);
   1172 }
   1173 
   1174 TEST_F(JsepTrackTest, VideoOffSendonlyAnsRecvonly) {
   1175  Init(SdpMediaSection::kVideo);
   1176  GetOffer().SetDirection(SdpDirectionAttribute::kSendonly);
   1177  GetAnswer().SetDirection(SdpDirectionAttribute::kRecvonly);
   1178  OfferAnswer();
   1179  CheckOffEncodingCount(1);
   1180  CheckAnsEncodingCount(0);
   1181 }
   1182 
   1183 TEST_F(JsepTrackTest, AudioOffSendrecvAnsRecvonly) {
   1184  Init(SdpMediaSection::kAudio);
   1185  GetAnswer().SetDirection(SdpDirectionAttribute::kRecvonly);
   1186  OfferAnswer();
   1187  CheckOffEncodingCount(1);
   1188  CheckAnsEncodingCount(0);
   1189 }
   1190 
   1191 TEST_F(JsepTrackTest, VideoOffSendrecvAnsRecvonly) {
   1192  Init(SdpMediaSection::kVideo);
   1193  GetAnswer().SetDirection(SdpDirectionAttribute::kRecvonly);
   1194  OfferAnswer();
   1195  CheckOffEncodingCount(1);
   1196  CheckAnsEncodingCount(0);
   1197 }
   1198 
   1199 TEST_F(JsepTrackTest, AudioOffRecvonlyAnsSendonly) {
   1200  Init(SdpMediaSection::kAudio);
   1201  GetOffer().SetDirection(SdpDirectionAttribute::kRecvonly);
   1202  GetAnswer().SetDirection(SdpDirectionAttribute::kSendonly);
   1203  OfferAnswer();
   1204  CheckOffEncodingCount(0);
   1205  CheckAnsEncodingCount(1);
   1206 }
   1207 
   1208 TEST_F(JsepTrackTest, VideoOffRecvonlyAnsSendonly) {
   1209  Init(SdpMediaSection::kVideo);
   1210  GetOffer().SetDirection(SdpDirectionAttribute::kRecvonly);
   1211  GetAnswer().SetDirection(SdpDirectionAttribute::kSendonly);
   1212  OfferAnswer();
   1213  CheckOffEncodingCount(0);
   1214  CheckAnsEncodingCount(1);
   1215 }
   1216 
   1217 TEST_F(JsepTrackTest, AudioOffSendrecvAnsSendonly) {
   1218  Init(SdpMediaSection::kAudio);
   1219  GetAnswer().SetDirection(SdpDirectionAttribute::kSendonly);
   1220  OfferAnswer();
   1221  CheckOffEncodingCount(0);
   1222  CheckAnsEncodingCount(1);
   1223 }
   1224 
   1225 TEST_F(JsepTrackTest, VideoOffSendrecvAnsSendonly) {
   1226  Init(SdpMediaSection::kVideo);
   1227  GetAnswer().SetDirection(SdpDirectionAttribute::kSendonly);
   1228  OfferAnswer();
   1229  CheckOffEncodingCount(0);
   1230  CheckAnsEncodingCount(1);
   1231 }
   1232 
   1233 TEST_F(JsepTrackTest, DataChannelDraft05) {
   1234  InitCodecs(CodecOverrides{});
   1235  InitTracks(SdpMediaSection::kApplication);
   1236 
   1237  mOffer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
   1238  mOffer->AddMediaSection(SdpMediaSection::kApplication,
   1239                          SdpDirectionAttribute::kSendrecv, 0,
   1240                          SdpMediaSection::kDtlsSctp, sdp::kIPv4, "0.0.0.0");
   1241  mAnswer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
   1242  mAnswer->AddMediaSection(SdpMediaSection::kApplication,
   1243                           SdpDirectionAttribute::kSendrecv, 0,
   1244                           SdpMediaSection::kDtlsSctp, sdp::kIPv4, "0.0.0.0");
   1245 
   1246  OfferAnswer();
   1247  CheckOffEncodingCount(1);
   1248  CheckAnsEncodingCount(1);
   1249 
   1250  ASSERT_NE(std::string::npos,
   1251            mOffer->ToString().find("a=sctpmap:5999 webrtc-datachannel 256"));
   1252  ASSERT_NE(std::string::npos,
   1253            mAnswer->ToString().find("a=sctpmap:5999 webrtc-datachannel 256"));
   1254  // Note: this is testing for a workaround, see bug 1335262 for details
   1255  ASSERT_NE(std::string::npos,
   1256            mOffer->ToString().find("a=max-message-size:499"));
   1257  ASSERT_NE(std::string::npos,
   1258            mAnswer->ToString().find("a=max-message-size:499"));
   1259  ASSERT_EQ(std::string::npos, mOffer->ToString().find("a=sctp-port"));
   1260  ASSERT_EQ(std::string::npos, mAnswer->ToString().find("a=sctp-port"));
   1261 }
   1262 
   1263 TEST_F(JsepTrackTest, DataChannelDraft21) {
   1264  Init(SdpMediaSection::kApplication);
   1265  OfferAnswer();
   1266  CheckOffEncodingCount(1);
   1267  CheckAnsEncodingCount(1);
   1268 
   1269  ASSERT_NE(std::string::npos, mOffer->ToString().find("a=sctp-port:5999"));
   1270  ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=sctp-port:5999"));
   1271  ASSERT_NE(std::string::npos,
   1272            mOffer->ToString().find("a=max-message-size:499"));
   1273  ASSERT_NE(std::string::npos,
   1274            mAnswer->ToString().find("a=max-message-size:499"));
   1275  ASSERT_EQ(std::string::npos, mOffer->ToString().find("a=sctpmap"));
   1276  ASSERT_EQ(std::string::npos, mAnswer->ToString().find("a=sctpmap"));
   1277 }
   1278 
   1279 TEST_F(JsepTrackTest, DataChannelDraft21AnswerWithDifferentPort) {
   1280  InitCodecs(CodecOverrides{});
   1281 
   1282  mOffCodecs.pop_back();
   1283  mOffCodecs.emplace_back(new JsepApplicationCodecDescription(
   1284      "webrtc-datachannel", 256, 4555, 10544));
   1285 
   1286  InitTracks(SdpMediaSection::kApplication);
   1287  InitSdp(SdpMediaSection::kApplication);
   1288 
   1289  OfferAnswer();
   1290  CheckOffEncodingCount(1);
   1291  CheckAnsEncodingCount(1);
   1292 
   1293  ASSERT_NE(std::string::npos, mOffer->ToString().find("a=sctp-port:4555"));
   1294  ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=sctp-port:5999"));
   1295  ASSERT_NE(std::string::npos,
   1296            mOffer->ToString().find("a=max-message-size:10544"));
   1297  ASSERT_NE(std::string::npos,
   1298            mAnswer->ToString().find("a=max-message-size:499"));
   1299  ASSERT_EQ(std::string::npos, mOffer->ToString().find("a=sctpmap"));
   1300  ASSERT_EQ(std::string::npos, mAnswer->ToString().find("a=sctpmap"));
   1301 }
   1302 
   1303 TEST_F(JsepTrackTest, SimulcastRejected) {
   1304  Init(SdpMediaSection::kVideo);
   1305  std::vector<std::string> rids;
   1306  rids.push_back("foo");
   1307  rids.push_back("bar");
   1308  mSendOff.SetRids(rids);
   1309  OfferAnswer();
   1310  CheckOffEncodingCount(1);
   1311  CheckAnsEncodingCount(1);
   1312 }
   1313 
   1314 TEST_F(JsepTrackTest, SimulcastPrevented) {
   1315  Init(SdpMediaSection::kVideo);
   1316  std::vector<std::string> rids;
   1317  rids.push_back("foo");
   1318  rids.push_back("bar");
   1319  mSendAns.SetRids(rids);
   1320  OfferAnswer();
   1321  CheckOffEncodingCount(1);
   1322  CheckAnsEncodingCount(1);
   1323 }
   1324 
   1325 TEST_F(JsepTrackTest, SimulcastOfferer) {
   1326  Init(SdpMediaSection::kVideo);
   1327  std::vector<std::string> rids;
   1328  rids.push_back("foo");
   1329  rids.push_back("bar");
   1330  mSendOff.SetRids(rids);
   1331  CreateOffer();
   1332  CreateAnswer();
   1333  // Add simulcast/rid to answer
   1334  mRecvAns.AddToMsection(rids, sdp::kRecv, mSsrcGenerator, false, &GetAnswer());
   1335  Negotiate();
   1336  ASSERT_TRUE(mSendOff.GetNegotiatedDetails());
   1337  ASSERT_EQ(2U, mSendOff.GetNegotiatedDetails()->GetEncodingCount());
   1338  ASSERT_EQ("foo", mSendOff.GetNegotiatedDetails()->GetEncoding(0).mRid);
   1339  ASSERT_EQ("bar", mSendOff.GetNegotiatedDetails()->GetEncoding(1).mRid);
   1340  ASSERT_NE(std::string::npos,
   1341            mOffer->ToString().find("a=simulcast:send foo;bar"));
   1342  ASSERT_NE(std::string::npos,
   1343            mAnswer->ToString().find("a=simulcast:recv foo;bar"));
   1344  ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:foo send"));
   1345  ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:bar send"));
   1346  ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:foo recv"));
   1347  ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:bar recv"));
   1348 }
   1349 
   1350 TEST_F(JsepTrackTest, SimulcastOffererWithRtx) {
   1351  Init(SdpMediaSection::kVideo);
   1352  std::vector<std::string> rids;
   1353  rids.push_back("foo");
   1354  rids.push_back("bar");
   1355  rids.push_back("pop");
   1356  mSendOff.SetRids(rids);
   1357  mSendOff.AddToMsection(rids, sdp::kSend, mSsrcGenerator, true, &GetOffer());
   1358  mRecvOff.AddToMsection(rids, sdp::kSend, mSsrcGenerator, true, &GetOffer());
   1359  CreateAnswer();
   1360  // Add simulcast/rid to answer
   1361  mRecvAns.AddToMsection(rids, sdp::kRecv, mSsrcGenerator, false, &GetAnswer());
   1362  Negotiate();
   1363 
   1364  ASSERT_EQ(3U, mSendOff.GetSsrcs().size());
   1365  const auto posSsrc0 =
   1366      mOffer->ToString().find(std::to_string(mSendOff.GetSsrcs()[0]));
   1367  const auto posSsrc1 =
   1368      mOffer->ToString().find(std::to_string(mSendOff.GetSsrcs()[1]));
   1369  const auto posSsrc2 =
   1370      mOffer->ToString().find(std::to_string(mSendOff.GetSsrcs()[2]));
   1371  ASSERT_NE(std::string::npos, posSsrc0);
   1372  ASSERT_NE(std::string::npos, posSsrc1);
   1373  ASSERT_NE(std::string::npos, posSsrc2);
   1374  ASSERT_GT(posSsrc1, posSsrc0);
   1375  ASSERT_GT(posSsrc2, posSsrc0);
   1376  ASSERT_GT(posSsrc2, posSsrc1);
   1377 
   1378  ASSERT_EQ(3U, mSendOff.GetRtxSsrcs().size());
   1379  const auto posRtxSsrc0 =
   1380      mOffer->ToString().find(std::to_string(mSendOff.GetRtxSsrcs()[0]));
   1381  const auto posRtxSsrc1 =
   1382      mOffer->ToString().find(std::to_string(mSendOff.GetRtxSsrcs()[1]));
   1383  const auto posRtxSsrc2 =
   1384      mOffer->ToString().find(std::to_string(mSendOff.GetRtxSsrcs()[2]));
   1385  ASSERT_NE(std::string::npos, posRtxSsrc0);
   1386  ASSERT_NE(std::string::npos, posRtxSsrc1);
   1387  ASSERT_NE(std::string::npos, posRtxSsrc2);
   1388  ASSERT_GT(posRtxSsrc1, posRtxSsrc0);
   1389  ASSERT_GT(posRtxSsrc2, posRtxSsrc0);
   1390  ASSERT_GT(posRtxSsrc2, posRtxSsrc1);
   1391 }
   1392 
   1393 TEST_F(JsepTrackTest, SimulcastAnswerer) {
   1394  Init(SdpMediaSection::kVideo);
   1395  std::vector<std::string> rids;
   1396  rids.push_back("foo");
   1397  rids.push_back("bar");
   1398  mSendAns.SetRids(rids);
   1399  CreateOffer();
   1400  // Add simulcast/rid to offer
   1401  mRecvOff.AddToMsection(rids, sdp::kRecv, mSsrcGenerator, false, &GetOffer());
   1402  CreateAnswer();
   1403  Negotiate();
   1404  ASSERT_TRUE(mSendAns.GetNegotiatedDetails());
   1405  ASSERT_EQ(2U, mSendAns.GetNegotiatedDetails()->GetEncodingCount());
   1406  ASSERT_EQ("foo", mSendAns.GetNegotiatedDetails()->GetEncoding(0).mRid);
   1407  ASSERT_EQ("bar", mSendAns.GetNegotiatedDetails()->GetEncoding(1).mRid);
   1408  ASSERT_NE(std::string::npos,
   1409            mOffer->ToString().find("a=simulcast:recv foo;bar"));
   1410  ASSERT_NE(std::string::npos,
   1411            mAnswer->ToString().find("a=simulcast:send foo;bar"));
   1412  ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:foo recv"));
   1413  ASSERT_NE(std::string::npos, mOffer->ToString().find("a=rid:bar recv"));
   1414  ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:foo send"));
   1415  ASSERT_NE(std::string::npos, mAnswer->ToString().find("a=rid:bar send"));
   1416 }
   1417 
   1418 #define VERIFY_OPUS_MAX_PLAYBACK_RATE(track, expectedRate)          \
   1419  {                                                                 \
   1420    JsepTrack& copy(track);                                         \
   1421    ASSERT_TRUE(copy.GetNegotiatedDetails());                       \
   1422    ASSERT_TRUE(copy.GetNegotiatedDetails()->GetEncodingCount());   \
   1423    for (const auto& codec :                                        \
   1424         copy.GetNegotiatedDetails()->GetEncoding(0).GetCodecs()) { \
   1425      if (codec->mName == "opus") {                                 \
   1426        JsepAudioCodecDescription& audioCodec =                     \
   1427            static_cast<JsepAudioCodecDescription&>(*codec);        \
   1428        ASSERT_EQ((expectedRate), audioCodec.mMaxPlaybackRate);     \
   1429      }                                                             \
   1430    };                                                              \
   1431  }
   1432 
   1433 #define VERIFY_OPUS_FORCE_MONO(track, expected)                       \
   1434  {                                                                   \
   1435    JsepTrack& copy(track);                                           \
   1436    ASSERT_TRUE(copy.GetNegotiatedDetails());                         \
   1437    ASSERT_TRUE(copy.GetNegotiatedDetails()->GetEncodingCount());     \
   1438    for (const auto& codec :                                          \
   1439         copy.GetNegotiatedDetails()->GetEncoding(0).GetCodecs()) {   \
   1440      if (codec->mName == "opus") {                                   \
   1441        JsepAudioCodecDescription& audioCodec =                       \
   1442            static_cast<JsepAudioCodecDescription&>(*codec);          \
   1443        /* gtest has some compiler warnings when using ASSERT_EQ with \
   1444         * booleans. */                                               \
   1445        ASSERT_EQ((int)(expected), (int)audioCodec.mForceMono);       \
   1446      }                                                               \
   1447    };                                                                \
   1448  }
   1449 
   1450 TEST_F(JsepTrackTest, DefaultOpusParameters) {
   1451  Init(SdpMediaSection::kAudio);
   1452  OfferAnswer();
   1453 
   1454  VERIFY_OPUS_MAX_PLAYBACK_RATE(
   1455      mSendOff, SdpFmtpAttributeList::OpusParameters::kDefaultMaxPlaybackRate);
   1456  VERIFY_OPUS_MAX_PLAYBACK_RATE(
   1457      mSendAns, SdpFmtpAttributeList::OpusParameters::kDefaultMaxPlaybackRate);
   1458  VERIFY_OPUS_MAX_PLAYBACK_RATE(mRecvOff, 0U);
   1459  VERIFY_OPUS_FORCE_MONO(mRecvOff, false);
   1460  VERIFY_OPUS_MAX_PLAYBACK_RATE(mRecvAns, 0U);
   1461  VERIFY_OPUS_FORCE_MONO(mRecvAns, false);
   1462 }
   1463 
   1464 TEST_F(JsepTrackTest, NonDefaultOpusParameters) {
   1465  InitCodecs(CodecOverrides{});
   1466  for (auto& codec : mAnsCodecs) {
   1467    if (codec->mName == "opus") {
   1468      JsepAudioCodecDescription* audioCodec =
   1469          static_cast<JsepAudioCodecDescription*>(codec.get());
   1470      audioCodec->mMaxPlaybackRate = 16000;
   1471      audioCodec->mForceMono = true;
   1472    }
   1473  }
   1474  InitTracks(SdpMediaSection::kAudio);
   1475  InitSdp(SdpMediaSection::kAudio);
   1476  OfferAnswer();
   1477 
   1478  VERIFY_OPUS_MAX_PLAYBACK_RATE(mSendOff, 16000U);
   1479  VERIFY_OPUS_FORCE_MONO(mSendOff, true);
   1480  VERIFY_OPUS_MAX_PLAYBACK_RATE(
   1481      mSendAns, SdpFmtpAttributeList::OpusParameters::kDefaultMaxPlaybackRate);
   1482  VERIFY_OPUS_FORCE_MONO(mSendAns, false);
   1483  VERIFY_OPUS_MAX_PLAYBACK_RATE(mRecvOff, 0U);
   1484  VERIFY_OPUS_FORCE_MONO(mRecvOff, false);
   1485  VERIFY_OPUS_MAX_PLAYBACK_RATE(mRecvAns, 16000U);
   1486  VERIFY_OPUS_FORCE_MONO(mRecvAns, true);
   1487 }
   1488 
   1489 TEST_F(JsepTrackTest, RtcpFbWithPayloadTypeAsymmetry) {
   1490  std::vector<std::string> expectedAckFbTypes;
   1491  std::vector<std::string> expectedNackFbTypes{"", "pli"};
   1492  std::vector<std::string> expectedCcmFbTypes{"fir"};
   1493  std::vector<SdpRtcpFbAttributeList::Feedback> expectedOtherFbTypes{
   1494      {"", SdpRtcpFbAttributeList::kRemb, "", ""},
   1495      {"", SdpRtcpFbAttributeList::kTransportCC, "", ""}};
   1496 
   1497  InitCodecs(CodecOverrides{});
   1498 
   1499  // On offerer, configure to support remb and transport-cc on video codecs
   1500  for (auto& codec : mOffCodecs) {
   1501    if (codec->Type() == SdpMediaSection::kVideo) {
   1502      auto& videoCodec = static_cast<JsepVideoCodecDescription&>(*codec);
   1503      videoCodec.EnableRemb();
   1504      videoCodec.EnableTransportCC();
   1505    }
   1506  }
   1507 
   1508  InitTracks(SdpMediaSection::kVideo);
   1509  InitSdp(SdpMediaSection::kVideo);
   1510 
   1511  CreateOffer();
   1512  // We do not bother trying to bamboozle the answerer into doing asymmetric
   1513  // payload types, we just use a raw SDP.
   1514  const std::string answer =
   1515      "v=0\r\n"
   1516      "o=- 0 0 IN IP4 127.0.0.1\r\n"
   1517      "s=-\r\n"
   1518      "t=0 0\r\n"
   1519      "a=msid-semantic:WMS *\r\n"
   1520      "m=video 0 UDP/TLS/RTP/SAVPF 136\r\n"
   1521      "c=IN IP4 0.0.0.0\r\n"
   1522      "a=sendrecv\r\n"
   1523      "a=fmtp:136 "
   1524      "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode="
   1525      "1\r\n"
   1526      "a=msid:stream_id\r\n"
   1527      "a=rtcp-fb:136 nack\r\n"
   1528      "a=rtcp-fb:136 nack pli\r\n"
   1529      "a=rtcp-fb:136 ccm fir\r\n"
   1530      "a=rtcp-fb:136 goog-remb\r\n"
   1531      "a=rtcp-fb:136 transport-cc\r\n"
   1532      "a=rtpmap:136 H264/90000\r\n"
   1533      "a=ssrc:2025549043 cname:\r\n";
   1534 
   1535  UniquePtr<SdpParser> parser(new SipccSdpParser);
   1536  mAnswer = std::move(parser->Parse(answer)->Sdp());
   1537  ASSERT_TRUE(mAnswer);
   1538 
   1539  mRecvOff.RecvTrackSetRemote(*mAnswer, GetAnswer());
   1540  mRecvOff.Negotiate(GetAnswer(), GetAnswer(), GetOffer());
   1541  mSendOff.Negotiate(GetAnswer(), GetAnswer(), GetOffer());
   1542 
   1543  ASSERT_TRUE(mSendOff.GetNegotiatedDetails());
   1544  ASSERT_TRUE(mRecvOff.GetNegotiatedDetails());
   1545 
   1546  UniquePtr<JsepVideoCodecDescription> codec;
   1547  ASSERT_TRUE((codec = GetVideoCodec(mSendOff)));
   1548  ASSERT_EQ("136", codec->mDefaultPt)
   1549      << "Offerer should have seen answer asymmetry!";
   1550  ASSERT_TRUE((codec = GetVideoCodec(mRecvOff, 3, 0)));
   1551  ASSERT_EQ("126", codec->mDefaultPt);
   1552  ASSERT_EQ(expectedAckFbTypes, codec->mAckFbTypes);
   1553  ASSERT_EQ(expectedNackFbTypes, codec->mNackFbTypes);
   1554  ASSERT_EQ(expectedCcmFbTypes, codec->mCcmFbTypes);
   1555  ASSERT_EQ(expectedOtherFbTypes, codec->mOtherFbTypes);
   1556 }
   1557 
   1558 TEST_F(JsepTrackTest, AudioSdpFmtpLine) {
   1559  mOffCodecs = MakeCodecs(
   1560      {.addFecCodecs = true, .preferRed = true, .addDtmfCodec = true});
   1561  mAnsCodecs = MakeCodecs(
   1562      {.addFecCodecs = true, .preferRed = true, .addDtmfCodec = true});
   1563  InitTracks(SdpMediaSection::kAudio);
   1564  InitSdp(SdpMediaSection::kAudio);
   1565  OfferAnswer();
   1566 
   1567  // SanityCheck checks that the sdpFmtpLine for a local codec matches that of
   1568  // the corresponding remote codec.
   1569  UniquePtr<JsepAudioCodecDescription> codec;
   1570  EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 0)));
   1571  EXPECT_EQ("opus", codec->mName);
   1572  EXPECT_EQ("maxplaybackrate=48000;stereo=1;useinbandfec=1",
   1573            codec->mSdpFmtpLine.valueOr("nothing"));
   1574  EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 0)));
   1575  EXPECT_EQ("opus", codec->mName);
   1576  EXPECT_EQ("maxplaybackrate=48000;stereo=1;useinbandfec=1",
   1577            codec->mSdpFmtpLine.valueOr("nothing"));
   1578 
   1579  EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 1)));
   1580  EXPECT_EQ("G722", codec->mName);
   1581  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1582  EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 1)));
   1583  EXPECT_EQ("G722", codec->mName);
   1584  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1585 
   1586  EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 2)));
   1587  EXPECT_EQ("telephone-event", codec->mName);
   1588  EXPECT_EQ("0-15", codec->mSdpFmtpLine.valueOr("nothing"));
   1589  EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 2)));
   1590  EXPECT_EQ("telephone-event", codec->mName);
   1591  EXPECT_EQ("0-15", codec->mSdpFmtpLine.valueOr("nothing"));
   1592 }
   1593 
   1594 TEST_F(JsepTrackTest, NonDefaultAudioSdpFmtpLine) {
   1595  mOffCodecs = MakeCodecs(
   1596      {.addFecCodecs = true, .preferRed = true, .addDtmfCodec = true});
   1597  mAnsCodecs = MakeCodecs(
   1598      {.addFecCodecs = true, .preferRed = true, .addDtmfCodec = true});
   1599 
   1600  for (auto& codec : mOffCodecs) {
   1601    if (codec->mName == "opus") {
   1602      auto* audio = static_cast<JsepAudioCodecDescription*>(codec.get());
   1603      audio->mForceMono = true;
   1604      audio->mMaxPlaybackRate = 32000;
   1605    }
   1606  }
   1607 
   1608  for (auto& codec : mAnsCodecs) {
   1609    if (codec->mName == "opus") {
   1610      auto* audio = static_cast<JsepAudioCodecDescription*>(codec.get());
   1611      audio->mFECEnabled = true;
   1612      audio->mCbrEnabled = true;
   1613      audio->mDTXEnabled = true;
   1614      audio->mFrameSizeMs = 10;
   1615      audio->mMinFrameSizeMs = 5;
   1616      audio->mMaxFrameSizeMs = 20;
   1617    }
   1618  }
   1619 
   1620  InitTracks(SdpMediaSection::kAudio);
   1621  InitSdp(SdpMediaSection::kAudio);
   1622 
   1623  {
   1624    // telephone-event doesn't store any params in JsepAudioCodecDescription.
   1625    // Set them directly in the offer sdp instead.
   1626    auto params = MakeUnique<SdpFmtpAttributeList::TelephoneEventParameters>();
   1627    params->dtmfTones = "2-9";
   1628    GetOffer().SetFmtp({"101", *params});
   1629  }
   1630 
   1631  {
   1632    // telephone-event doesn't store any params in JsepAudioCodecDescription.
   1633    // Set them directly in the answer sdp instead.
   1634    auto params = MakeUnique<SdpFmtpAttributeList::TelephoneEventParameters>();
   1635    params->dtmfTones = "0-3,10";
   1636    GetAnswer().SetFmtp({"101", *params});
   1637  }
   1638 
   1639  OfferAnswer();
   1640 
   1641  // SanityCheck checks that the sdpFmtpLine for a local codec matches that of
   1642  // the corresponding remote codec.
   1643  UniquePtr<JsepAudioCodecDescription> codec;
   1644  EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 0)));
   1645  EXPECT_EQ("opus", codec->mName);
   1646  EXPECT_EQ(
   1647      "maxplaybackrate=48000;stereo=1;useinbandfec=1;usedtx=1;ptime=10;"
   1648      "minptime=5;maxptime=20;cbr=1",
   1649      codec->mSdpFmtpLine.valueOr("nothing"));
   1650  EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 0)));
   1651  EXPECT_EQ("opus", codec->mName);
   1652  EXPECT_EQ("maxplaybackrate=32000;stereo=0;useinbandfec=1",
   1653            codec->mSdpFmtpLine.valueOr("nothing"));
   1654 
   1655  EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 1)));
   1656  EXPECT_EQ("G722", codec->mName);
   1657  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1658  EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 1)));
   1659  EXPECT_EQ("G722", codec->mName);
   1660  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1661 
   1662  EXPECT_TRUE((codec = GetAudioCodec(mSendOff, 3, 2)));
   1663  EXPECT_EQ("telephone-event", codec->mName);
   1664  EXPECT_EQ("0-3,10", codec->mSdpFmtpLine.valueOr("nothing"));
   1665  EXPECT_TRUE((codec = GetAudioCodec(mSendAns, 3, 2)));
   1666  EXPECT_EQ("telephone-event", codec->mName);
   1667  EXPECT_EQ("2-9", codec->mSdpFmtpLine.valueOr("nothing"));
   1668 }
   1669 
   1670 TEST_F(JsepTrackTest, VideoSdpFmtpLine) {
   1671  mOffCodecs = MakeCodecs(
   1672      {.addFecCodecs = true, .preferRed = true, .addDtmfCodec = true});
   1673  mAnsCodecs = MakeCodecs(
   1674      {.addFecCodecs = true, .preferRed = true, .addDtmfCodec = true});
   1675  InitTracks(SdpMediaSection::kVideo);
   1676  InitSdp(SdpMediaSection::kVideo);
   1677  OfferAnswer();
   1678 
   1679  // SanityCheck checks that the sdpFmtpLine for a local codec matches that of
   1680  // the corresponding remote codec.
   1681  UniquePtr<JsepVideoCodecDescription> codec;
   1682  EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 5, 0)));
   1683  EXPECT_EQ("VP8", codec->mName);
   1684  EXPECT_EQ("max-fs=12288;max-fr=60", codec->mSdpFmtpLine.valueOr("nothing"));
   1685  EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 5, 0)));
   1686  EXPECT_EQ("VP8", codec->mName);
   1687  EXPECT_EQ("max-fs=12288;max-fr=60", codec->mSdpFmtpLine.valueOr("nothing"));
   1688 
   1689  EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 5, 1)));
   1690  EXPECT_EQ("H264", codec->mName);
   1691  EXPECT_EQ(
   1692      "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1",
   1693      codec->mSdpFmtpLine.valueOr("nothing"));
   1694  EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 5, 1)));
   1695  EXPECT_EQ("H264", codec->mName);
   1696  EXPECT_EQ(
   1697      "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1",
   1698      codec->mSdpFmtpLine.valueOr("nothing"));
   1699 
   1700  EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 5, 3)));
   1701  EXPECT_EQ("red", codec->mName);
   1702  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1703  EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 5, 3)));
   1704  EXPECT_EQ("red", codec->mName);
   1705  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1706 
   1707  EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 5, 4)));
   1708  EXPECT_EQ("ulpfec", codec->mName);
   1709  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1710  EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 5, 4)));
   1711  EXPECT_EQ("ulpfec", codec->mName);
   1712  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1713 }
   1714 
   1715 TEST_F(JsepTrackTest, NonDefaultVideoSdpFmtpLine) {
   1716  mOffCodecs = MakeCodecs(
   1717      {.addFecCodecs = true, .preferRed = true, .addDtmfCodec = true});
   1718  mAnsCodecs = MakeCodecs(
   1719      {.addFecCodecs = true, .preferRed = true, .addDtmfCodec = true});
   1720 
   1721  for (auto& codec : mOffCodecs) {
   1722    if (codec->mName == "VP8" || codec->mName == "H264") {
   1723      auto* video = static_cast<JsepVideoCodecDescription*>(codec.get());
   1724      video->mConstraints.maxFs = 1200;
   1725      if (codec->mName == "VP8") {
   1726        video->mConstraints.maxFps = Some(15);
   1727      } else {
   1728        video->mConstraints.maxDpb = 6400;
   1729        video->mConstraints.maxBr = 1000;
   1730        JsepVideoCodecDescription::SetSaneH264Level(0x1F0,
   1731                                                    &video->mProfileLevelId);
   1732      }
   1733    }
   1734  }
   1735 
   1736  for (auto& codec : mAnsCodecs) {
   1737    if (codec->mName == "VP8" || codec->mName == "H264") {
   1738      auto* video = static_cast<JsepVideoCodecDescription*>(codec.get());
   1739      video->mConstraints.maxFs = 32400;
   1740      if (codec->mName == "VP8") {
   1741        video->mConstraints.maxFps = Some(60);
   1742      } else {
   1743        video->mConstraints.maxMbps = 1944000;
   1744        video->mConstraints.maxCpb = 800000;
   1745        video->mConstraints.maxDpb = 128000;
   1746        JsepVideoCodecDescription::SetSaneH264Level(0xAB,
   1747                                                    &video->mProfileLevelId);
   1748        video->mPacketizationMode = 1;
   1749      }
   1750    }
   1751  }
   1752 
   1753  InitTracks(SdpMediaSection::kVideo);
   1754  InitSdp(SdpMediaSection::kVideo);
   1755  OfferAnswer();
   1756 
   1757  // SanityCheck checks that the sdpFmtpLine for a local codec matches that of
   1758  // the corresponding remote codec.
   1759  UniquePtr<JsepVideoCodecDescription> codec;
   1760  EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 5, 0)));
   1761  EXPECT_EQ("VP8", codec->mName);
   1762  EXPECT_EQ("max-fs=32400;max-fr=60", codec->mSdpFmtpLine.valueOr("nothing"));
   1763  EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 5, 0)));
   1764  EXPECT_EQ("VP8", codec->mName);
   1765  EXPECT_EQ("max-fs=1200;max-fr=15", codec->mSdpFmtpLine.valueOr("nothing"));
   1766 
   1767  EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 5, 1)));
   1768  EXPECT_EQ("H264", codec->mName);
   1769  EXPECT_EQ(
   1770      "profile-level-id=42f00b;level-asymmetry-allowed=1;packetization-mode=1;"
   1771      "max-mbps=1944000;max-fs=32400;max-cpb=800000;max-dpb=128000",
   1772      codec->mSdpFmtpLine.valueOr("nothing"));
   1773  EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 5, 1)));
   1774  EXPECT_EQ("H264", codec->mName);
   1775  EXPECT_EQ(
   1776      "profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1;"
   1777      "max-fs=1200;max-dpb=6400;max-br=1000",
   1778      codec->mSdpFmtpLine.valueOr("nothing"));
   1779 
   1780  EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 5, 3)));
   1781  EXPECT_EQ("red", codec->mName);
   1782  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1783  EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 5, 3)));
   1784  EXPECT_EQ("red", codec->mName);
   1785  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1786 
   1787  EXPECT_TRUE((codec = GetVideoCodec(mSendOff, 5, 4)));
   1788  EXPECT_EQ("ulpfec", codec->mName);
   1789  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1790  EXPECT_TRUE((codec = GetVideoCodec(mSendAns, 5, 4)));
   1791  EXPECT_EQ("ulpfec", codec->mName);
   1792  EXPECT_EQ("nothing", codec->mSdpFmtpLine.valueOr("nothing"));
   1793 }
   1794 
   1795 TEST(JsepTrackRecvPayloadTypesTest, SingleTrackPTsAreUnique)
   1796 {
   1797  constexpr auto audio = SdpMediaSection::MediaType::kAudio;
   1798 
   1799  std::vector<UniquePtr<JsepCodecDescription>> codecs;
   1800  codecs.emplace_back(
   1801      MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1));
   1802 
   1803  SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   1804  SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
   1805      audio, SdpDirectionAttribute::kRecvonly, 0,
   1806      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1807 
   1808  SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   1809  SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
   1810      audio, SdpDirectionAttribute::kSendonly, 0,
   1811      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1812 
   1813  for (const auto& codec : codecs) {
   1814    codec->mDirection = sdp::kSend;
   1815    offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   1816                             codec->mChannels);
   1817    auto clone = WrapUnique(codec->Clone());
   1818    clone->mDirection = sdp::kRecv;
   1819    clone->AddToMediaSection(answer1Msection1);
   1820  }
   1821 
   1822  JsepTrack t1{audio, sdp::Direction::kRecv};
   1823  t1.PopulateCodecs(codecs, false);
   1824  t1.RecvTrackSetLocal(offer1Msection1);
   1825  t1.RecvTrackSetRemote(answer1, answer1Msection1);
   1826  ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
   1827            NS_OK);
   1828 
   1829  std::vector tracks{&t1};
   1830  JsepTrack::SetReceivePayloadTypes(tracks);
   1831  EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(1));
   1832  EXPECT_THAT(t1.GetOtherReceivePayloadTypes(), UnorderedElementsAre());
   1833 }
   1834 
   1835 TEST(JsepTrackRecvPayloadTypesTest, DoubleTrackPTsAreUnique)
   1836 {
   1837  constexpr auto audio = SdpMediaSection::MediaType::kAudio;
   1838 
   1839  std::vector<UniquePtr<JsepCodecDescription>> codecs1;
   1840  codecs1.emplace_back(
   1841      MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1));
   1842 
   1843  std::vector<UniquePtr<JsepCodecDescription>> codecs2;
   1844  codecs2.emplace_back(
   1845      MakeUnique<JsepAudioCodecDescription>("2", "codec1", 48000, 1));
   1846 
   1847  SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   1848  SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
   1849      audio, SdpDirectionAttribute::kRecvonly, 0,
   1850      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1851  SdpMediaSection& offer1Msection2 = offer1.AddMediaSection(
   1852      audio, SdpDirectionAttribute::kRecvonly, 0,
   1853      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1854 
   1855  SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   1856  SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
   1857      audio, SdpDirectionAttribute::kSendonly, 0,
   1858      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1859  SdpMediaSection& answer1Msection2 = answer1.AddMediaSection(
   1860      audio, SdpDirectionAttribute::kSendonly, 0,
   1861      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1862 
   1863  for (const auto& codec : codecs1) {
   1864    codec->mDirection = sdp::kSend;
   1865    offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   1866                             codec->mChannels);
   1867    auto clone = WrapUnique(codec->Clone());
   1868    clone->mDirection = sdp::kRecv;
   1869    clone->AddToMediaSection(answer1Msection1);
   1870  }
   1871 
   1872  for (const auto& codec : codecs2) {
   1873    codec->mDirection = sdp::kSend;
   1874    offer1Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   1875                             codec->mChannels);
   1876    auto clone = WrapUnique(codec->Clone());
   1877    clone->mDirection = sdp::kRecv;
   1878    clone->AddToMediaSection(answer1Msection2);
   1879  }
   1880 
   1881  JsepTrack t1{audio, sdp::Direction::kRecv};
   1882  t1.PopulateCodecs(codecs1, false);
   1883  t1.RecvTrackSetLocal(offer1Msection1);
   1884  t1.RecvTrackSetRemote(answer1, answer1Msection1);
   1885  ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
   1886            NS_OK);
   1887 
   1888  JsepTrack t2{audio, sdp::Direction::kRecv};
   1889  t2.PopulateCodecs(codecs2, false);
   1890  t2.RecvTrackSetLocal(offer1Msection2);
   1891  t2.RecvTrackSetRemote(answer1, answer1Msection2);
   1892  ASSERT_EQ(t2.Negotiate(answer1Msection2, answer1Msection2, offer1Msection2),
   1893            NS_OK);
   1894 
   1895  std::vector tracks{&t1, &t2};
   1896  JsepTrack::SetReceivePayloadTypes(tracks);
   1897  EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(1));
   1898  EXPECT_THAT(t1.GetOtherReceivePayloadTypes(), UnorderedElementsAre(2));
   1899  EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(2));
   1900  EXPECT_THAT(t2.GetOtherReceivePayloadTypes(), UnorderedElementsAre(1));
   1901 }
   1902 
   1903 TEST(JsepTrackRecvPayloadTypesTest, DoubleTrackPTsAreDuplicates)
   1904 {
   1905  constexpr auto audio = SdpMediaSection::MediaType::kAudio;
   1906 
   1907  std::vector<UniquePtr<JsepCodecDescription>> codecs1;
   1908  codecs1.emplace_back(
   1909      MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1));
   1910 
   1911  std::vector<UniquePtr<JsepCodecDescription>> codecs2;
   1912  codecs2.emplace_back(
   1913      MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1));
   1914 
   1915  SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   1916  SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
   1917      audio, SdpDirectionAttribute::kRecvonly, 0,
   1918      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1919  SdpMediaSection& offer1Msection2 = offer1.AddMediaSection(
   1920      audio, SdpDirectionAttribute::kRecvonly, 0,
   1921      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1922 
   1923  SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   1924  SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
   1925      audio, SdpDirectionAttribute::kSendonly, 0,
   1926      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1927  SdpMediaSection& answer1Msection2 = answer1.AddMediaSection(
   1928      audio, SdpDirectionAttribute::kSendonly, 0,
   1929      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1930 
   1931  for (const auto& codec : codecs1) {
   1932    codec->mDirection = sdp::kSend;
   1933    offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   1934                             codec->mChannels);
   1935    auto clone = WrapUnique(codec->Clone());
   1936    clone->mDirection = sdp::kRecv;
   1937    clone->AddToMediaSection(answer1Msection1);
   1938  }
   1939  for (const auto& codec : codecs2) {
   1940    codec->mDirection = sdp::kSend;
   1941    offer1Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   1942                             codec->mChannels);
   1943    auto clone = WrapUnique(codec->Clone());
   1944    clone->mDirection = sdp::kRecv;
   1945    clone->AddToMediaSection(answer1Msection2);
   1946  }
   1947 
   1948  JsepTrack t1{audio, sdp::Direction::kRecv};
   1949  t1.PopulateCodecs(codecs1, false);
   1950  t1.RecvTrackSetLocal(offer1Msection1);
   1951  t1.RecvTrackSetRemote(answer1, answer1Msection1);
   1952  ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
   1953            NS_OK);
   1954 
   1955  JsepTrack t2{audio, sdp::Direction::kRecv};
   1956  t2.PopulateCodecs(codecs2, false);
   1957  t2.RecvTrackSetLocal(offer1Msection2);
   1958  t2.RecvTrackSetRemote(answer1, answer1Msection2);
   1959  ASSERT_EQ(t2.Negotiate(answer1Msection2, answer1Msection2, offer1Msection2),
   1960            NS_OK);
   1961 
   1962  std::vector tracks{&t1, &t2};
   1963  JsepTrack::SetReceivePayloadTypes(tracks);
   1964  EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre());
   1965  EXPECT_THAT(t1.GetOtherReceivePayloadTypes(), UnorderedElementsAre(1));
   1966  EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre());
   1967  EXPECT_THAT(t2.GetOtherReceivePayloadTypes(), UnorderedElementsAre(1));
   1968 }
   1969 
   1970 TEST(JsepTrackRecvPayloadTypesTest, DoubleTrackPTsOverlap)
   1971 {
   1972  constexpr auto audio = SdpMediaSection::MediaType::kAudio;
   1973 
   1974  std::vector<UniquePtr<JsepCodecDescription>> codecs1;
   1975  codecs1.emplace_back(
   1976      MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1));
   1977  codecs1.emplace_back(
   1978      MakeUnique<JsepAudioCodecDescription>("2", "codec2", 48000, 1));
   1979 
   1980  std::vector<UniquePtr<JsepCodecDescription>> codecs2;
   1981  codecs2.emplace_back(
   1982      MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1));
   1983  codecs2.emplace_back(
   1984      MakeUnique<JsepAudioCodecDescription>("3", "codec2", 48000, 1));
   1985 
   1986  SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   1987  SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
   1988      audio, SdpDirectionAttribute::kRecvonly, 0,
   1989      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1990  SdpMediaSection& offer1Msection2 = offer1.AddMediaSection(
   1991      audio, SdpDirectionAttribute::kRecvonly, 0,
   1992      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1993 
   1994  SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   1995  SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
   1996      audio, SdpDirectionAttribute::kSendonly, 0,
   1997      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   1998  SdpMediaSection& answer1Msection2 = answer1.AddMediaSection(
   1999      audio, SdpDirectionAttribute::kSendonly, 0,
   2000      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2001 
   2002  for (const auto& codec : codecs1) {
   2003    codec->mDirection = sdp::kSend;
   2004    offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   2005                             codec->mChannels);
   2006    auto clone = WrapUnique(codec->Clone());
   2007    clone->mDirection = sdp::kRecv;
   2008    clone->AddToMediaSection(answer1Msection1);
   2009  }
   2010 
   2011  for (const auto& codec : codecs2) {
   2012    codec->mDirection = sdp::kSend;
   2013    offer1Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   2014                             codec->mChannels);
   2015    auto clone = WrapUnique(codec->Clone());
   2016    clone->mDirection = sdp::kRecv;
   2017    clone->AddToMediaSection(answer1Msection2);
   2018  }
   2019 
   2020  JsepTrack t1{audio, sdp::Direction::kRecv};
   2021  t1.PopulateCodecs(codecs1, false);
   2022  t1.RecvTrackSetLocal(offer1Msection1);
   2023  t1.RecvTrackSetRemote(answer1, answer1Msection1);
   2024  ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
   2025            NS_OK);
   2026 
   2027  JsepTrack t2{audio, sdp::Direction::kRecv};
   2028  t2.PopulateCodecs(codecs2, false);
   2029  t2.RecvTrackSetLocal(offer1Msection2);
   2030  t2.RecvTrackSetRemote(answer1, answer1Msection2);
   2031  ASSERT_EQ(t2.Negotiate(answer1Msection2, answer1Msection2, offer1Msection2),
   2032            NS_OK);
   2033 
   2034  std::vector tracks{&t1, &t2};
   2035  JsepTrack::SetReceivePayloadTypes(tracks);
   2036  EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(2));
   2037  EXPECT_THAT(t1.GetOtherReceivePayloadTypes(), UnorderedElementsAre(1, 3));
   2038  EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(3));
   2039  EXPECT_THAT(t2.GetOtherReceivePayloadTypes(), UnorderedElementsAre(1, 2));
   2040 }
   2041 
   2042 TEST(JsepTrackRecvPayloadTypesTest, DoubleTrackPTsDuplicateAfterRenegotiation)
   2043 {
   2044  constexpr auto audio = SdpMediaSection::MediaType::kAudio;
   2045 
   2046  std::vector<UniquePtr<JsepCodecDescription>> codecs1;
   2047  codecs1.emplace_back(
   2048      MakeUnique<JsepAudioCodecDescription>("1", "codec1", 48000, 1));
   2049  codecs1.emplace_back(
   2050      MakeUnique<JsepAudioCodecDescription>("2", "codec2", 48000, 1));
   2051 
   2052  std::vector<UniquePtr<JsepCodecDescription>> codecs2;
   2053  codecs2.emplace_back(
   2054      MakeUnique<JsepAudioCodecDescription>("3", "codec1", 48000, 1));
   2055  codecs2.emplace_back(
   2056      MakeUnique<JsepAudioCodecDescription>("4", "codec2", 48000, 1));
   2057 
   2058  // First negotiation.
   2059  SipccSdp offer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   2060  SdpMediaSection& offer1Msection1 = offer1.AddMediaSection(
   2061      audio, SdpDirectionAttribute::kRecvonly, 0,
   2062      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2063  SdpMediaSection& offer1Msection2 = offer1.AddMediaSection(
   2064      audio, SdpDirectionAttribute::kRecvonly, 0,
   2065      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2066 
   2067  SipccSdp answer1(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   2068  SdpMediaSection& answer1Msection1 = answer1.AddMediaSection(
   2069      audio, SdpDirectionAttribute::kSendonly, 0,
   2070      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2071  SdpMediaSection& answer1Msection2 = answer1.AddMediaSection(
   2072      audio, SdpDirectionAttribute::kSendonly, 0,
   2073      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2074 
   2075  for (const auto& codec : codecs1) {
   2076    codec->mDirection = sdp::kSend;
   2077    offer1Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   2078                             codec->mChannels);
   2079    auto clone = WrapUnique(codec->Clone());
   2080    clone->mDirection = sdp::kRecv;
   2081    clone->AddToMediaSection(answer1Msection1);
   2082  }
   2083 
   2084  for (const auto& codec : codecs2) {
   2085    codec->mDirection = sdp::kSend;
   2086    offer1Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   2087                             codec->mChannels);
   2088    auto clone = WrapUnique(codec->Clone());
   2089    clone->mDirection = sdp::kRecv;
   2090    clone->AddToMediaSection(answer1Msection2);
   2091  }
   2092 
   2093  // t1 and t2 use distinct payload types in the first negotiation.
   2094  JsepTrack t1{audio, sdp::Direction::kRecv};
   2095  t1.PopulateCodecs(codecs1, false);
   2096  t1.RecvTrackSetLocal(offer1Msection1);
   2097  t1.RecvTrackSetRemote(answer1, answer1Msection1);
   2098  ASSERT_EQ(t1.Negotiate(answer1Msection1, answer1Msection1, offer1Msection1),
   2099            NS_OK);
   2100 
   2101  JsepTrack t2{audio, sdp::Direction::kRecv};
   2102  t2.PopulateCodecs(codecs2, false);
   2103  t2.RecvTrackSetLocal(offer1Msection2);
   2104  t2.RecvTrackSetRemote(answer1, answer1Msection2);
   2105  ASSERT_EQ(t2.Negotiate(answer1Msection2, answer1Msection2, offer1Msection2),
   2106            NS_OK);
   2107 
   2108  std::vector tracks{&t1, &t2};
   2109  JsepTrack::SetReceivePayloadTypes(tracks);
   2110  EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(1, 2));
   2111  EXPECT_THAT(t1.GetOtherReceivePayloadTypes(), UnorderedElementsAre(3, 4));
   2112  EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre(3, 4));
   2113  EXPECT_THAT(t2.GetOtherReceivePayloadTypes(), UnorderedElementsAre(1, 2));
   2114 
   2115  // Second negotiation.
   2116  SipccSdp offer2(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   2117  SdpMediaSection& offer2Msection1 = offer2.AddMediaSection(
   2118      audio, SdpDirectionAttribute::kRecvonly, 0,
   2119      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2120  SdpMediaSection& offer2Msection2 = offer2.AddMediaSection(
   2121      audio, SdpDirectionAttribute::kRecvonly, 0,
   2122      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2123 
   2124  SipccSdp answer2(SdpOrigin("", 0, 0, sdp::kIPv4, ""));
   2125  SdpMediaSection& answer2Msection1 = answer2.AddMediaSection(
   2126      audio, SdpDirectionAttribute::kSendonly, 0,
   2127      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2128  SdpMediaSection& answer2Msection2 = answer2.AddMediaSection(
   2129      audio, SdpDirectionAttribute::kSendonly, 0,
   2130      SdpHelper::GetProtocolForMediaType(audio), sdp::kIPv4, "0.0.0.0");
   2131 
   2132  for (const auto& codec : codecs1) {
   2133    codec->mDirection = sdp::kSend;
   2134    offer2Msection1.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   2135                             codec->mChannels);
   2136    auto clone = WrapUnique(codec->Clone());
   2137    clone->mDirection = sdp::kRecv;
   2138    clone->AddToMediaSection(answer2Msection1);
   2139  }
   2140 
   2141  for (const auto& codec : codecs2) {
   2142    codec->mDirection = sdp::kSend;
   2143    offer2Msection2.AddCodec(codec->mDefaultPt, codec->mName, codec->mClock,
   2144                             codec->mChannels);
   2145    auto clone = WrapUnique(codec->Clone());
   2146    clone->mDirection = sdp::kRecv;
   2147    clone->AddToMediaSection(answer2Msection2);
   2148  }
   2149 
   2150  t1.PopulateCodecs(codecs1, false);
   2151  t1.RecvTrackSetLocal(offer2Msection1);
   2152  t1.RecvTrackSetRemote(answer2, answer2Msection1);
   2153  ASSERT_EQ(t1.Negotiate(answer2Msection1, answer2Msection1, offer2Msection1),
   2154            NS_OK);
   2155 
   2156  // Change t2 to use the same payload types as t1. Both tracks should now mark
   2157  // all their payload types as duplicates.
   2158  t2.PopulateCodecs(codecs1, false);
   2159  t2.RecvTrackSetLocal(offer2Msection2);
   2160  t2.RecvTrackSetRemote(answer2, answer2Msection2);
   2161  ASSERT_EQ(t2.Negotiate(answer2Msection2, answer2Msection2, offer2Msection2),
   2162            NS_OK);
   2163 
   2164  std::vector newTracks{&t1, &t2};
   2165  JsepTrack::SetReceivePayloadTypes(newTracks);
   2166  EXPECT_THAT(t1.GetUniqueReceivePayloadTypes(), UnorderedElementsAre());
   2167  EXPECT_THAT(t1.GetOtherReceivePayloadTypes(), UnorderedElementsAre(1, 2));
   2168  EXPECT_THAT(t2.GetUniqueReceivePayloadTypes(), UnorderedElementsAre());
   2169  EXPECT_THAT(t2.GetOtherReceivePayloadTypes(), UnorderedElementsAre(1, 2));
   2170 }
   2171 }  // namespace mozilla