tor-browser

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

jsep_session_unittest.cpp (302286B)


      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 <iostream>
      8 #include <map>
      9 
     10 #include "nss.h"
     11 #include "ssl.h"
     12 
     13 #include "mozilla/Preferences.h"
     14 
     15 #define GTEST_HAS_RTTI 0
     16 #include "gmock/gmock.h"
     17 #include "gtest/gtest.h"
     18 
     19 #include "CodecConfig.h"
     20 #include "PeerConnectionImpl.h"
     21 #include "sdp/SdpMediaSection.h"
     22 #include "sdp/SipccSdpParser.h"
     23 #include "jsep/JsepTrack.h"
     24 #include "jsep/JsepSession.h"
     25 #include "jsep/JsepSessionImpl.h"
     26 
     27 using testing::ElementsAre;
     28 using testing::Pair;
     29 using testing::UnorderedElementsAre;
     30 
     31 namespace mozilla {
     32 MOZ_RUNINIT static std::string kAEqualsCandidate("a=candidate:");
     33 const static size_t kNumCandidatesPerComponent = 3;
     34 
     35 class JsepSessionTestBase : public ::testing::Test {
     36 public:
     37  static void SetUpTestCase() {
     38    NSS_NoDB_Init(nullptr);
     39    NSS_SetDomesticPolicy();
     40  }
     41 };
     42 
     43 class FakeUuidGenerator : public mozilla::JsepUuidGenerator {
     44 public:
     45  bool Generate(std::string* str) {
     46    std::ostringstream os;
     47    os << "FAKE_UUID_" << ++ctr;
     48    *str = os.str();
     49 
     50    return true;
     51  }
     52 
     53  mozilla::JsepUuidGenerator* Clone() const {
     54    return new FakeUuidGenerator(*this);
     55  }
     56 
     57 private:
     58  static uint64_t ctr;
     59 };
     60 
     61 uint64_t FakeUuidGenerator::ctr = 1000;
     62 
     63 class JsepSessionTest : public JsepSessionTestBase,
     64                        public ::testing::WithParamInterface<std::string> {
     65 public:
     66  JsepSessionTest() : mSdpHelper(&mLastError) {
     67    Preferences::SetCString("media.peerconnection.sdp.parser", "legacy");
     68    Preferences::SetCString("media.peerconnection.sdp.alternate_parse_mode",
     69                            "never");
     70    Preferences::SetBool("media.peerconnection.video.use_rtx", true);
     71    Preferences::SetBool("media.navigator.video.use_transport_cc", true);
     72    Preferences::SetBool("media.navigator.video.use_remb", true);
     73    Preferences::SetBool("media.navigator.video.disable_h264_baseline", false);
     74    Preferences::SetBool("media.webrtc.codec.video.av1.enabled", true);
     75    Preferences::SetBool("media.navigator.audio.use_fec", false);
     76 
     77    mSessionOff =
     78        MakeUnique<JsepSessionImpl>("Offerer", MakeUnique<FakeUuidGenerator>());
     79    mSessionAns = MakeUnique<JsepSessionImpl>("Answerer",
     80                                              MakeUnique<FakeUuidGenerator>());
     81 
     82    EXPECT_EQ(NS_OK, mSessionOff->Init());
     83    EXPECT_EQ(NS_OK, mSessionAns->Init());
     84 
     85    std::vector<UniquePtr<JsepCodecDescription>> preferredCodecs;
     86    PeerConnectionImpl::SetupPreferredCodecs(preferredCodecs);
     87    for (auto& codec : preferredCodecs) {
     88      // Make H264 P0 recvonly everywhere for better test coverage.
     89      // TODO: For unit testing JSEP, the preferred codecs list should be
     90      //       defined by the tests, not rely on Gecko runtime behavior.
     91      if (codec->mName == "H264") {
     92        auto* video = static_cast<JsepVideoCodecDescription*>(codec.get());
     93        if (video->mPacketizationMode == 0) {
     94          video->mSupportedDirection = sdp::kRecv;
     95        }
     96      }
     97    }
     98    mSessionOff->SetDefaultCodecs(preferredCodecs);
     99    mSessionAns->SetDefaultCodecs(preferredCodecs);
    100 
    101    std::vector<PeerConnectionImpl::RtpExtensionHeader> preferredHeaders;
    102    PeerConnectionImpl::SetupPreferredRtpExtensions(preferredHeaders);
    103 
    104    for (const auto& header : preferredHeaders) {
    105      mSessionOff->AddRtpExtension(header.mMediaType, header.extensionname,
    106                                   header.direction);
    107      mSessionAns->AddRtpExtension(header.mMediaType, header.extensionname,
    108                                   header.direction);
    109    }
    110 
    111    mOffererTransport = MakeUnique<TransportData>();
    112    mAnswererTransport = MakeUnique<TransportData>();
    113 
    114    AddTransportData(*mSessionOff, *mOffererTransport);
    115    AddTransportData(*mSessionAns, *mAnswererTransport);
    116 
    117    mOffCandidates = MakeUnique<CandidateSet>();
    118    mAnsCandidates = MakeUnique<CandidateSet>();
    119  }
    120 
    121  static std::vector<JsepTransceiver>& GetTransceivers(JsepSession& aSession) {
    122    return aSession.GetTransceivers();
    123  }
    124 
    125  static const std::vector<JsepTransceiver>& GetTransceivers(
    126      const JsepSession& aSession) {
    127    return aSession.GetTransceivers();
    128  }
    129 
    130 protected:
    131  struct TransportData {
    132    std::map<nsCString, std::vector<uint8_t>> mFingerprints;
    133  };
    134 
    135  void AddDtlsFingerprint(const nsCString& alg, JsepSessionImpl& session,
    136                          TransportData& tdata) {
    137    std::vector<uint8_t> fp;
    138    fp.assign((alg == "sha-1") ? 20 : 32,
    139              (session.GetName() == "Offerer") ? 0x4f : 0x41);
    140    session.AddDtlsFingerprint(alg, fp);
    141    tdata.mFingerprints[alg] = fp;
    142  }
    143 
    144  void AddTransportData(JsepSessionImpl& session, TransportData& tdata) {
    145    AddDtlsFingerprint("sha-1"_ns, session, tdata);
    146    AddDtlsFingerprint("sha-256"_ns, session, tdata);
    147  }
    148 
    149  void CheckTransceiverInvariants(
    150      const std::vector<JsepTransceiver>& oldTransceivers,
    151      const std::vector<JsepTransceiver>& newTransceivers) {
    152    ASSERT_LE(oldTransceivers.size(), newTransceivers.size());
    153    std::set<size_t> levels;
    154 
    155    for (const auto& newTransceiver : newTransceivers) {
    156      if (newTransceiver.HasLevel()) {
    157        ASSERT_FALSE(levels.count(newTransceiver.GetLevel()))
    158        << "Two new transceivers are mapped to level "
    159        << newTransceiver.GetLevel();
    160        levels.insert(newTransceiver.GetLevel());
    161      }
    162    }
    163 
    164    auto last = levels.rbegin();
    165    if (last != levels.rend()) {
    166      ASSERT_LE(*last, levels.size())
    167          << "Max level observed in transceivers was " << *last
    168          << ", but there are only " << levels.size()
    169          << " levels in the "
    170             "transceivers.";
    171    }
    172 
    173    for (const auto& oldTransceiver : oldTransceivers) {
    174      if (oldTransceiver.HasLevel()) {
    175        ASSERT_TRUE(levels.count(oldTransceiver.GetLevel()))
    176        << "Level " << oldTransceiver.GetLevel()
    177        << " had a transceiver in the old, but not the new (or, "
    178           "perhaps this level had more than one transceiver in the "
    179           "old)";
    180        levels.erase(oldTransceiver.GetLevel());
    181      }
    182    }
    183  }
    184 
    185  std::string CreateOffer(const Maybe<JsepOfferOptions>& options = Nothing()) {
    186    std::vector<JsepTransceiver> transceiversBefore =
    187        GetTransceivers(*mSessionOff);
    188    JsepOfferOptions defaultOptions;
    189    const JsepOfferOptions& optionsRef = options ? *options : defaultOptions;
    190    std::string offer;
    191    JsepSession::Result result = mSessionOff->CreateOffer(optionsRef, &offer);
    192    EXPECT_FALSE(result.mError.isSome()) << mSessionOff->GetLastError();
    193 
    194    std::cerr << "OFFER: " << offer << std::endl;
    195 
    196    ValidateTransport(*mOffererTransport, offer, sdp::kOffer);
    197 
    198    if (transceiversBefore.size() != GetTransceivers(*mSessionOff).size()) {
    199      EXPECT_TRUE(false) << "CreateOffer changed number of transceivers!";
    200      return offer;
    201    }
    202 
    203    CheckTransceiverInvariants(transceiversBefore,
    204                               GetTransceivers(*mSessionOff));
    205 
    206    for (size_t i = 0; i < transceiversBefore.size(); ++i) {
    207      JsepTransceiver oldTransceiver = transceiversBefore[i];
    208      JsepTransceiver newTransceiver = GetTransceivers(*mSessionOff)[i];
    209      EXPECT_EQ(oldTransceiver.IsStopped(), newTransceiver.IsStopped());
    210 
    211      if (oldTransceiver.IsStopped()) {
    212        if (!newTransceiver.HasLevel()) {
    213          // Tolerate unmapping of stopped transceivers by removing this
    214          // difference.
    215          oldTransceiver.ClearLevel();
    216        }
    217      } else if (!oldTransceiver.HasLevel()) {
    218        EXPECT_TRUE(newTransceiver.HasLevel());
    219        // Tolerate new mappings.
    220        oldTransceiver.SetLevel(newTransceiver.GetLevel());
    221      }
    222 
    223      EXPECT_TRUE(Equals(oldTransceiver, newTransceiver));
    224    }
    225 
    226    return offer;
    227  }
    228 
    229  typedef enum { NO_ADDTRACK_MAGIC, ADDTRACK_MAGIC } AddTrackMagic;
    230 
    231  void AddTracks(JsepSessionImpl& side, AddTrackMagic magic = ADDTRACK_MAGIC) {
    232    // Add tracks.
    233    if (types.empty()) {
    234      types = BuildTypes(GetParam());
    235    }
    236    AddTracks(side, types, magic);
    237 
    238    // Now, we move datachannel to the end
    239    auto it =
    240        std::find(types.begin(), types.end(), SdpMediaSection::kApplication);
    241    if (it != types.end()) {
    242      types.erase(it);
    243      types.push_back(SdpMediaSection::kApplication);
    244    }
    245  }
    246 
    247  void AddTracks(JsepSessionImpl& side, const std::string& mediatypes,
    248                 AddTrackMagic magic = ADDTRACK_MAGIC) {
    249    AddTracks(side, BuildTypes(mediatypes), magic);
    250  }
    251 
    252  JsepTrack RemoveTrack(JsepSession& side, size_t index) {
    253    if (GetTransceivers(side).size() <= index) {
    254      EXPECT_TRUE(false) << "Index " << index << " out of bounds!";
    255      return JsepTrack(SdpMediaSection::kAudio, sdp::kSend);
    256    }
    257 
    258    JsepTransceiver transceiver(GetTransceivers(side)[index]);
    259    JsepTrack& track = transceiver.mSendTrack;
    260    EXPECT_FALSE(track.GetStreamIds().empty()) << "No track at index " << index;
    261 
    262    JsepTrack original(track);
    263    track.ClearStreamIds();
    264    transceiver.mJsDirection &= SdpDirectionAttribute::Direction::kRecvonly;
    265    side.SetTransceiver(transceiver);
    266    return original;
    267  }
    268 
    269  void SetDirection(JsepSession& side, size_t index,
    270                    SdpDirectionAttribute::Direction direction) {
    271    ASSERT_LT(index, GetTransceivers(side).size())
    272        << "Index " << index << " out of bounds!";
    273 
    274    auto transceiver = GetTransceivers(side)[index];
    275    transceiver.mJsDirection = direction;
    276    side.SetTransceiver(transceiver);
    277  }
    278 
    279  std::vector<SdpMediaSection::MediaType> BuildTypes(
    280      const std::string& mediatypes) {
    281    std::vector<SdpMediaSection::MediaType> result;
    282    size_t ptr = 0;
    283 
    284    for (;;) {
    285      size_t comma = mediatypes.find(',', ptr);
    286      std::string chunk = mediatypes.substr(ptr, comma - ptr);
    287 
    288      if (chunk == "audio") {
    289        result.push_back(SdpMediaSection::kAudio);
    290      } else if (chunk == "video") {
    291        result.push_back(SdpMediaSection::kVideo);
    292      } else if (chunk == "datachannel") {
    293        result.push_back(SdpMediaSection::kApplication);
    294      } else {
    295        MOZ_CRASH();
    296      }
    297 
    298      if (comma == std::string::npos) break;
    299      ptr = comma + 1;
    300    }
    301 
    302    return result;
    303  }
    304 
    305  void AddTracks(JsepSessionImpl& side,
    306                 const std::vector<SdpMediaSection::MediaType>& mediatypes,
    307                 AddTrackMagic magic = ADDTRACK_MAGIC) {
    308    std::string stream_id;
    309    std::string track_id;
    310 
    311    ASSERT_TRUE(mUuidGen.Generate(&stream_id));
    312 
    313    AddTracksToStream(side, stream_id, mediatypes, magic);
    314  }
    315 
    316  void AddTracksToStream(JsepSessionImpl& side, const std::string stream_id,
    317                         const std::string& mediatypes,
    318                         AddTrackMagic magic = ADDTRACK_MAGIC) {
    319    AddTracksToStream(side, stream_id, BuildTypes(mediatypes), magic);
    320  }
    321 
    322  // A bit of a hack. JsepSessionImpl populates the track-id automatically, just
    323  // in case, because the w3c spec requires msid to be set even when there's no
    324  // send track.
    325  bool IsNull(const JsepTrack& track) const {
    326    return track.GetStreamIds().empty() &&
    327           (track.GetMediaType() != SdpMediaSection::MediaType::kApplication);
    328  }
    329 
    330  void AddTracksToStream(
    331      JsepSessionImpl& side, const std::string stream_id,
    332      const std::vector<SdpMediaSection::MediaType>& mediatypes,
    333      AddTrackMagic magic = ADDTRACK_MAGIC)
    334 
    335  {
    336    std::string track_id;
    337 
    338    for (auto type : mediatypes) {
    339      ASSERT_TRUE(mUuidGen.Generate(&track_id));
    340 
    341      Maybe<JsepTransceiver> suitableTransceiver;
    342      size_t i;
    343      if (magic == ADDTRACK_MAGIC) {
    344        // We're simulating addTrack.
    345        for (i = 0; i < GetTransceivers(side).size(); ++i) {
    346          auto transceiver = GetTransceivers(side)[i];
    347          if (transceiver.mSendTrack.GetMediaType() != type) {
    348            continue;
    349          }
    350 
    351          if (IsNull(transceiver.mSendTrack) ||
    352              transceiver.GetMediaType() == SdpMediaSection::kApplication) {
    353            suitableTransceiver = Some(transceiver);
    354            break;
    355          }
    356        }
    357      }
    358 
    359      if (!suitableTransceiver) {
    360        i = GetTransceivers(side).size();
    361        side.AddTransceiver(JsepTransceiver(type, mUuidGen));
    362        suitableTransceiver = Some(GetTransceivers(side).back());
    363        if (magic == ADDTRACK_MAGIC) {
    364          suitableTransceiver->SetAddTrackMagic();
    365        }
    366      }
    367 
    368      std::cerr << "Updating send track for transceiver " << i << std::endl;
    369      suitableTransceiver->SetOnlyExistsBecauseOfSetRemote(false);
    370      suitableTransceiver->mJsDirection |=
    371          SdpDirectionAttribute::Direction::kSendonly;
    372      suitableTransceiver->mSendTrack.UpdateStreamIds(
    373          std::vector<std::string>(1, stream_id));
    374      side.SetTransceiver(*suitableTransceiver);
    375    }
    376  }
    377 
    378  bool HasMediaStream(const std::vector<JsepTrack>& tracks) const {
    379    for (const auto& track : tracks) {
    380      if (track.GetMediaType() != SdpMediaSection::kApplication) {
    381        return true;
    382      }
    383    }
    384    return false;
    385  }
    386 
    387  const std::string GetFirstLocalStreamId(JsepSessionImpl& side) const {
    388    auto tracks = GetLocalTracks(side);
    389    return tracks.begin()->GetStreamIds()[0];
    390  }
    391 
    392  std::vector<JsepTrack> GetLocalTracks(const JsepSession& session) const {
    393    std::vector<JsepTrack> result;
    394    for (const auto& transceiver : GetTransceivers(session)) {
    395      if (!IsNull(transceiver.mSendTrack)) {
    396        result.push_back(transceiver.mSendTrack);
    397      }
    398    }
    399    return result;
    400  }
    401 
    402  std::vector<JsepTrack> GetRemoteTracks(const JsepSession& session) const {
    403    std::vector<JsepTrack> result;
    404    for (const auto& transceiver : GetTransceivers(session)) {
    405      if (!IsNull(transceiver.mRecvTrack)) {
    406        result.push_back(transceiver.mRecvTrack);
    407      }
    408    }
    409    return result;
    410  }
    411 
    412  JsepTransceiver* GetDatachannelTransceiver(JsepSession& side) {
    413    for (auto& transceiver : GetTransceivers(side)) {
    414      if (transceiver.mSendTrack.GetMediaType() ==
    415          SdpMediaSection::MediaType::kApplication) {
    416        return &transceiver;
    417      }
    418    }
    419 
    420    return nullptr;
    421  }
    422 
    423  JsepTransceiver* GetNegotiatedTransceiver(JsepSession& side, size_t index) {
    424    for (auto& transceiver : GetTransceivers(side)) {
    425      if (transceiver.mSendTrack.GetNegotiatedDetails() ||
    426          transceiver.mRecvTrack.GetNegotiatedDetails()) {
    427        if (index) {
    428          --index;
    429          continue;
    430        }
    431 
    432        return &transceiver;
    433      }
    434    }
    435 
    436    return nullptr;
    437  }
    438 
    439  Maybe<JsepTransceiver> GetTransceiverByLevel(
    440      const std::vector<JsepTransceiver>& transceivers, size_t level) {
    441    for (auto& transceiver : transceivers) {
    442      if (transceiver.HasLevel() && transceiver.GetLevel() == level) {
    443        return Some(transceiver);
    444      }
    445    }
    446 
    447    return Nothing();
    448  }
    449 
    450  Maybe<JsepTransceiver> GetTransceiverByLevel(JsepSession& side,
    451                                               size_t level) {
    452    return GetTransceiverByLevel(GetTransceivers(side), level);
    453  }
    454 
    455  std::vector<std::string> GetMediaStreamIds(
    456      const std::vector<JsepTrack>& tracks) const {
    457    std::vector<std::string> ids;
    458    for (const auto& track : tracks) {
    459      // data channels don't have msid's
    460      if (track.GetMediaType() == SdpMediaSection::kApplication) {
    461        continue;
    462      }
    463      ids.insert(ids.end(), track.GetStreamIds().begin(),
    464                 track.GetStreamIds().end());
    465    }
    466    return ids;
    467  }
    468 
    469  std::vector<std::string> GetLocalMediaStreamIds(JsepSessionImpl& side) const {
    470    return GetMediaStreamIds(GetLocalTracks(side));
    471  }
    472 
    473  std::vector<std::string> GetRemoteMediaStreamIds(
    474      JsepSessionImpl& side) const {
    475    return GetMediaStreamIds(GetRemoteTracks(side));
    476  }
    477 
    478  std::vector<std::string> sortUniqueStrVector(
    479      std::vector<std::string> in) const {
    480    std::sort(in.begin(), in.end());
    481    auto it = std::unique(in.begin(), in.end());
    482    in.resize(std::distance(in.begin(), it));
    483    return in;
    484  }
    485 
    486  std::vector<std::string> GetLocalUniqueStreamIds(
    487      JsepSessionImpl& side) const {
    488    return sortUniqueStrVector(GetLocalMediaStreamIds(side));
    489  }
    490 
    491  std::vector<std::string> GetRemoteUniqueStreamIds(
    492      JsepSessionImpl& side) const {
    493    return sortUniqueStrVector(GetRemoteMediaStreamIds(side));
    494  }
    495 
    496  JsepTrack GetTrack(JsepSessionImpl& side, SdpMediaSection::MediaType type,
    497                     size_t index) const {
    498    for (const auto& transceiver : GetTransceivers(side)) {
    499      if (IsNull(transceiver.mSendTrack) ||
    500          transceiver.mSendTrack.GetMediaType() != type) {
    501        continue;
    502      }
    503 
    504      if (index != 0) {
    505        --index;
    506        continue;
    507      }
    508 
    509      return transceiver.mSendTrack;
    510    }
    511 
    512    return JsepTrack(type, sdp::kSend);
    513  }
    514 
    515  JsepTrack GetTrackOff(size_t index, SdpMediaSection::MediaType type) {
    516    return GetTrack(*mSessionOff, type, index);
    517  }
    518 
    519  JsepTrack GetTrackAns(size_t index, SdpMediaSection::MediaType type) {
    520    return GetTrack(*mSessionAns, type, index);
    521  }
    522 
    523  bool Equals(const SdpFingerprintAttributeList::Fingerprint& f1,
    524              const SdpFingerprintAttributeList::Fingerprint& f2) const {
    525    if (f1.hashFunc != f2.hashFunc) {
    526      return false;
    527    }
    528 
    529    if (f1.fingerprint != f2.fingerprint) {
    530      return false;
    531    }
    532 
    533    return true;
    534  }
    535 
    536  bool Equals(const SdpFingerprintAttributeList& f1,
    537              const SdpFingerprintAttributeList& f2) const {
    538    if (f1.mFingerprints.size() != f2.mFingerprints.size()) {
    539      return false;
    540    }
    541 
    542    for (size_t i = 0; i < f1.mFingerprints.size(); ++i) {
    543      if (!Equals(f1.mFingerprints[i], f2.mFingerprints[i])) {
    544        return false;
    545      }
    546    }
    547 
    548    return true;
    549  }
    550 
    551  bool Equals(const UniquePtr<JsepDtlsTransport>& t1,
    552              const UniquePtr<JsepDtlsTransport>& t2) const {
    553    if (!t1 && !t2) {
    554      return true;
    555    }
    556 
    557    if (!t1 || !t2) {
    558      return false;
    559    }
    560 
    561    if (!Equals(t1->GetFingerprints(), t2->GetFingerprints())) {
    562      return false;
    563    }
    564 
    565    if (t1->GetRole() != t2->GetRole()) {
    566      return false;
    567    }
    568 
    569    return true;
    570  }
    571 
    572  bool Equals(const UniquePtr<JsepIceTransport>& t1,
    573              const UniquePtr<JsepIceTransport>& t2) const {
    574    if (!t1 && !t2) {
    575      return true;
    576    }
    577 
    578    if (!t1 || !t2) {
    579      return false;
    580    }
    581 
    582    if (t1->GetUfrag() != t2->GetUfrag()) {
    583      return false;
    584    }
    585 
    586    if (t1->GetPassword() != t2->GetPassword()) {
    587      return false;
    588    }
    589 
    590    return true;
    591  }
    592 
    593  bool Equals(const JsepTransport& t1, const JsepTransport& t2) const {
    594    if (t1.mTransportId != t2.mTransportId) {
    595      std::cerr << "Transport id differs: " << t1.mTransportId << " vs "
    596                << t2.mTransportId << std::endl;
    597      return false;
    598    }
    599 
    600    if (t1.mComponents != t2.mComponents) {
    601      std::cerr << "Component count differs" << std::endl;
    602      return false;
    603    }
    604 
    605    if (!Equals(t1.mIce, t2.mIce)) {
    606      std::cerr << "ICE differs" << std::endl;
    607      return false;
    608    }
    609 
    610    return true;
    611  }
    612 
    613  bool Equals(const JsepTrack& t1, const JsepTrack& t2) const {
    614    if (t1.GetMediaType() != t2.GetMediaType()) {
    615      return false;
    616    }
    617 
    618    if (t1.GetDirection() != t2.GetDirection()) {
    619      return false;
    620    }
    621 
    622    if (t1.GetStreamIds() != t2.GetStreamIds()) {
    623      return false;
    624    }
    625 
    626    if (t1.GetActive() != t2.GetActive()) {
    627      return false;
    628    }
    629 
    630    if (t1.GetCNAME() != t2.GetCNAME()) {
    631      return false;
    632    }
    633 
    634    if (t1.GetSsrcs() != t2.GetSsrcs()) {
    635      return false;
    636    }
    637 
    638    return true;
    639  }
    640 
    641  bool Equals(const JsepTransceiver& p1, const JsepTransceiver& p2) const {
    642    if (p1.HasLevel() != p2.HasLevel()) {
    643      std::cerr << "One transceiver has a level, the other doesn't"
    644                << std::endl;
    645      return false;
    646    }
    647 
    648    if (p1.HasLevel() && (p1.GetLevel() != p2.GetLevel())) {
    649      std::cerr << "Level differs: " << p1.GetLevel() << " vs " << p2.GetLevel()
    650                << std::endl;
    651      return false;
    652    }
    653 
    654    // We don't check things like BundleLevel(), since that can change without
    655    // any changes to the transport, which is what we're really interested in.
    656 
    657    if (p1.IsStopped() != p2.IsStopped()) {
    658      std::cerr << "One transceiver is stopped, the other is not" << std::endl;
    659      return false;
    660    }
    661 
    662    if (p1.IsAssociated() != p2.IsAssociated()) {
    663      std::cerr << "One transceiver has a mid, the other doesn't" << std::endl;
    664      return false;
    665    }
    666 
    667    if (p1.IsAssociated() && (p1.GetMid() != p2.GetMid())) {
    668      std::cerr << "mid differs: " << p1.GetMid() << " vs " << p2.GetMid()
    669                << std::endl;
    670      return false;
    671    }
    672 
    673    if (!Equals(p1.mSendTrack, p2.mSendTrack)) {
    674      std::cerr << "Send track differs" << std::endl;
    675      return false;
    676    }
    677 
    678    if (!Equals(p1.mRecvTrack, p2.mRecvTrack)) {
    679      std::cerr << "Receive track differs" << std::endl;
    680      return false;
    681    }
    682 
    683    if (!Equals(p1.mTransport, p2.mTransport)) {
    684      std::cerr << "Transport differs" << std::endl;
    685      return false;
    686    }
    687 
    688    return true;
    689  }
    690 
    691  bool Equals(const std::vector<JsepTransceiver>& t1,
    692              const std::vector<JsepTransceiver>& t2) const {
    693    if (t1.size() != t2.size()) {
    694      std::cerr << "Size differs: t1.size = " << t1.size()
    695                << ", t2.size = " << t2.size() << std::endl;
    696      return false;
    697    }
    698 
    699    for (size_t i = 0; i < t1.size(); ++i) {
    700      if (!Equals(t1[i], t2[i])) {
    701        return false;
    702      }
    703    }
    704 
    705    return true;
    706  }
    707 
    708  size_t GetTrackCount(JsepSessionImpl& side,
    709                       SdpMediaSection::MediaType type) const {
    710    size_t result = 0;
    711    for (const auto& track : GetLocalTracks(side)) {
    712      if (track.GetMediaType() == type) {
    713        ++result;
    714      }
    715    }
    716    return result;
    717  }
    718 
    719  UniquePtr<Sdp> GetParsedLocalDescription(const JsepSessionImpl& side) const {
    720    return Parse(side.GetLocalDescription(kJsepDescriptionCurrent));
    721  }
    722 
    723  SdpMediaSection* GetMsection(Sdp& sdp, SdpMediaSection::MediaType type,
    724                               size_t index) const {
    725    for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
    726      auto& msection = sdp.GetMediaSection(i);
    727      if (msection.GetMediaType() != type) {
    728        continue;
    729      }
    730 
    731      if (index) {
    732        --index;
    733        continue;
    734      }
    735 
    736      return &msection;
    737    }
    738 
    739    return nullptr;
    740  }
    741 
    742  void SetPayloadTypeNumber(JsepSession& session, const std::string& codecName,
    743                            const std::string& payloadType) {
    744    for (auto& codec : session.Codecs()) {
    745      if (codec->mName == codecName) {
    746        codec->mDefaultPt = payloadType;
    747      }
    748    }
    749  }
    750 
    751  void SetCodecEnabled(JsepSession& session, const std::string& codecName,
    752                       bool enabled) {
    753    for (auto& codec : session.Codecs()) {
    754      if (codec->mName == codecName) {
    755        codec->mEnabled = enabled;
    756      }
    757    }
    758  }
    759 
    760  void EnsureNegotiationFailure(SdpMediaSection::MediaType type,
    761                                const std::string& codecName) {
    762    for (auto& codec : mSessionOff->Codecs()) {
    763      if (codec->Type() == type && codec->mName != codecName) {
    764        codec->mEnabled = false;
    765      }
    766    }
    767 
    768    for (auto& codec : mSessionAns->Codecs()) {
    769      if (codec->Type() == type && codec->mName == codecName) {
    770        codec->mEnabled = false;
    771      }
    772    }
    773  }
    774 
    775  std::string CreateAnswer() {
    776    std::vector<JsepTransceiver> transceiversBefore =
    777        GetTransceivers(*mSessionAns);
    778 
    779    JsepAnswerOptions options;
    780    std::string answer;
    781 
    782    JsepSession::Result result = mSessionAns->CreateAnswer(options, &answer);
    783    EXPECT_FALSE(result.mError.isSome());
    784 
    785    std::cerr << "ANSWER: " << answer << std::endl;
    786 
    787    ValidateTransport(*mAnswererTransport, answer, sdp::kAnswer);
    788    CheckTransceiverInvariants(transceiversBefore,
    789                               GetTransceivers(*mSessionAns));
    790 
    791    return answer;
    792  }
    793 
    794  static const uint32_t NO_CHECKS = 0;
    795  static const uint32_t CHECK_SUCCESS = 1;
    796  static const uint32_t CHECK_TRACKS = 1 << 2;
    797  static const uint32_t ALL_CHECKS = CHECK_SUCCESS | CHECK_TRACKS;
    798 
    799  void OfferAnswer(uint32_t checkFlags = ALL_CHECKS,
    800                   const Maybe<JsepOfferOptions>& options = Nothing()) {
    801    std::string offer = CreateOffer(options);
    802    SetLocalOffer(offer, checkFlags);
    803    SetRemoteOffer(offer, checkFlags);
    804 
    805    std::string answer = CreateAnswer();
    806    SetLocalAnswer(answer, checkFlags);
    807    SetRemoteAnswer(answer, checkFlags);
    808  }
    809 
    810  void SetLocalOffer(const std::string& offer,
    811                     uint32_t checkFlags = ALL_CHECKS) {
    812    std::vector<JsepTransceiver> transceiversBefore =
    813        GetTransceivers(*mSessionOff);
    814 
    815    JsepSession::Result result =
    816        mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
    817 
    818    CheckTransceiverInvariants(transceiversBefore,
    819                               GetTransceivers(*mSessionOff));
    820 
    821    if (checkFlags & CHECK_SUCCESS) {
    822      ASSERT_FALSE(result.mError.isSome());
    823    }
    824 
    825    if (checkFlags & CHECK_TRACKS) {
    826      // This assumes no recvonly or inactive transceivers.
    827      ASSERT_EQ(types.size(), GetTransceivers(*mSessionOff).size());
    828      for (const auto& transceiver : GetTransceivers(*mSessionOff)) {
    829        if (!transceiver.HasLevel()) {
    830          continue;
    831        }
    832        const auto& track(transceiver.mSendTrack);
    833        size_t level = transceiver.GetLevel();
    834        ASSERT_FALSE(IsNull(track));
    835        ASSERT_EQ(types[level], track.GetMediaType());
    836        if (track.GetMediaType() != SdpMediaSection::kApplication) {
    837          std::string msidAttr("a=msid:");
    838          msidAttr += track.GetStreamIds()[0];
    839          ASSERT_NE(std::string::npos, offer.find(msidAttr))
    840              << "Did not find " << msidAttr << " in offer";
    841        }
    842      }
    843      if (types.size() == 1 && types[0] == SdpMediaSection::kApplication) {
    844        ASSERT_EQ(std::string::npos, offer.find("a=ssrc"))
    845            << "Data channel should not contain SSRC";
    846      }
    847    }
    848  }
    849 
    850  void SetRemoteOffer(const std::string& offer,
    851                      uint32_t checkFlags = ALL_CHECKS) {
    852    std::vector<JsepTransceiver> transceiversBefore =
    853        GetTransceivers(*mSessionAns);
    854 
    855    JsepSession::Result result =
    856        mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
    857 
    858    CheckTransceiverInvariants(transceiversBefore,
    859                               GetTransceivers(*mSessionAns));
    860 
    861    if (checkFlags & CHECK_SUCCESS) {
    862      ASSERT_FALSE(result.mError.isSome());
    863    }
    864 
    865    if (checkFlags & CHECK_TRACKS) {
    866      // This assumes no recvonly or inactive transceivers.
    867      ASSERT_EQ(types.size(), GetTransceivers(*mSessionAns).size());
    868      for (const auto& transceiver : GetTransceivers(*mSessionAns)) {
    869        if (!transceiver.HasLevel()) {
    870          continue;
    871        }
    872        const auto& track(transceiver.mRecvTrack);
    873        size_t level = transceiver.GetLevel();
    874        ASSERT_FALSE(IsNull(track));
    875        ASSERT_EQ(types[level], track.GetMediaType());
    876        if (track.GetMediaType() != SdpMediaSection::kApplication) {
    877          std::string msidAttr("a=msid:");
    878          msidAttr += track.GetStreamIds()[0];
    879          // Track id will not match, and will eventually not be present at all
    880          ASSERT_NE(std::string::npos, offer.find(msidAttr))
    881              << "Did not find " << msidAttr << " in offer";
    882        }
    883      }
    884    }
    885  }
    886 
    887  void SetLocalAnswer(const std::string& answer,
    888                      uint32_t checkFlags = ALL_CHECKS) {
    889    std::vector<JsepTransceiver> transceiversBefore =
    890        GetTransceivers(*mSessionAns);
    891 
    892    JsepSession::Result result =
    893        mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
    894    if (checkFlags & CHECK_SUCCESS) {
    895      ASSERT_FALSE(result.mError.isSome());
    896    }
    897 
    898    CheckTransceiverInvariants(transceiversBefore,
    899                               GetTransceivers(*mSessionAns));
    900 
    901    if (checkFlags & CHECK_TRACKS) {
    902      // Verify that the right stuff is in the tracks.
    903      ASSERT_EQ(types.size(), GetTransceivers(*mSessionAns).size());
    904      for (const auto& transceiver : GetTransceivers(*mSessionAns)) {
    905        if (!transceiver.HasLevel()) {
    906          continue;
    907        }
    908        const auto& sendTrack(transceiver.mSendTrack);
    909        const auto& recvTrack(transceiver.mRecvTrack);
    910        size_t level = transceiver.GetLevel();
    911        ASSERT_FALSE(IsNull(sendTrack));
    912        ASSERT_EQ(types[level], sendTrack.GetMediaType());
    913        // These might have been in the SDP, or might have been randomly
    914        // chosen by JsepSessionImpl
    915        ASSERT_FALSE(IsNull(recvTrack));
    916        ASSERT_EQ(types[level], recvTrack.GetMediaType());
    917 
    918        if (recvTrack.GetMediaType() != SdpMediaSection::kApplication) {
    919          std::string msidAttr("a=msid:");
    920          msidAttr += sendTrack.GetStreamIds()[0];
    921          ASSERT_NE(std::string::npos, answer.find(msidAttr))
    922              << "Did not find " << msidAttr << " in answer";
    923        }
    924      }
    925      if (types.size() == 1 && types[0] == SdpMediaSection::kApplication) {
    926        ASSERT_EQ(std::string::npos, answer.find("a=ssrc"))
    927            << "Data channel should not contain SSRC";
    928      }
    929    }
    930    std::cerr << "Answerer transceivers:" << std::endl;
    931    DumpTransceivers(*mSessionAns);
    932  }
    933 
    934  void SetRemoteAnswer(const std::string& answer,
    935                       uint32_t checkFlags = ALL_CHECKS) {
    936    std::vector<JsepTransceiver> transceiversBefore =
    937        GetTransceivers(*mSessionOff);
    938 
    939    JsepSession::Result result =
    940        mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
    941    if (checkFlags & CHECK_SUCCESS) {
    942      ASSERT_FALSE(result.mError.isSome());
    943    }
    944 
    945    CheckTransceiverInvariants(transceiversBefore,
    946                               GetTransceivers(*mSessionOff));
    947 
    948    if (checkFlags & CHECK_TRACKS) {
    949      // Verify that the right stuff is in the tracks.
    950      ASSERT_EQ(types.size(), GetTransceivers(*mSessionOff).size());
    951      for (const auto& transceiver : GetTransceivers(*mSessionOff)) {
    952        if (!transceiver.HasLevel()) {
    953          continue;
    954        }
    955        const auto& sendTrack(transceiver.mSendTrack);
    956        const auto& recvTrack(transceiver.mRecvTrack);
    957        size_t level = transceiver.GetLevel();
    958        ASSERT_FALSE(IsNull(sendTrack));
    959        ASSERT_EQ(types[level], sendTrack.GetMediaType());
    960        // These might have been in the SDP, or might have been randomly
    961        // chosen by JsepSessionImpl
    962        ASSERT_FALSE(IsNull(recvTrack));
    963        ASSERT_EQ(types[level], recvTrack.GetMediaType());
    964 
    965        if (recvTrack.GetMediaType() != SdpMediaSection::kApplication) {
    966          std::string msidAttr("a=msid:");
    967          msidAttr += recvTrack.GetStreamIds()[0];
    968          // Track id will not match, and will eventually not be present at all
    969          ASSERT_NE(std::string::npos, answer.find(msidAttr))
    970              << "Did not find " << msidAttr << " in answer";
    971        }
    972      }
    973    }
    974    std::cerr << "Offerer transceivers:" << std::endl;
    975    DumpTransceivers(*mSessionOff);
    976  }
    977 
    978  std::string GetTransportId(const JsepSession& session, size_t level) {
    979    for (const auto& transceiver : GetTransceivers(session)) {
    980      if (transceiver.HasLevel() && transceiver.GetLevel() == level) {
    981        return transceiver.mTransport.mTransportId;
    982      }
    983    }
    984    return std::string();
    985  }
    986 
    987  typedef enum { RTP = 1, RTCP = 2 } ComponentType;
    988 
    989  class CandidateSet {
    990   public:
    991    CandidateSet() {}
    992 
    993    void Gather(JsepSession& session, ComponentType maxComponent = RTCP) {
    994      for (const auto& transceiver : GetTransceivers(session)) {
    995        if (transceiver.HasOwnTransport()) {
    996          Gather(session, transceiver.mTransport.mTransportId, RTP);
    997          if (transceiver.mTransport.mComponents > 1) {
    998            Gather(session, transceiver.mTransport.mTransportId, RTCP);
    999          }
   1000        }
   1001      }
   1002      FinishGathering(session);
   1003    }
   1004 
   1005    void Gather(JsepSession& session, const std::string& transportId,
   1006                ComponentType component) {
   1007      static uint16_t port = 1000;
   1008      std::vector<std::string> candidates;
   1009 
   1010      for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) {
   1011        ++port;
   1012        std::ostringstream candidate;
   1013        candidate << "0 " << static_cast<uint16_t>(component)
   1014                  << " UDP 9999 192.168.0.1 " << port << " typ host";
   1015        std::string mid;
   1016        uint16_t level = 0;
   1017        bool skipped;
   1018        session.AddLocalIceCandidate(kAEqualsCandidate + candidate.str(),
   1019                                     transportId, "", &level, &mid, &skipped);
   1020        if (!skipped) {
   1021          mCandidatesToTrickle.push_back(std::tuple<Level, Mid, Candidate>(
   1022              level, mid, kAEqualsCandidate + candidate.str()));
   1023          candidates.push_back(candidate.str());
   1024        }
   1025      }
   1026 
   1027      // Stomp existing candidates
   1028      mCandidates[transportId][component] = candidates;
   1029 
   1030      // Stomp existing defaults
   1031      mDefaultCandidates[transportId][component] =
   1032          std::make_pair("192.168.0.1", port);
   1033      session.UpdateDefaultCandidate(
   1034          mDefaultCandidates[transportId][RTP].first,
   1035          mDefaultCandidates[transportId][RTP].second,
   1036          // Will be empty string if not present, which is how we indicate
   1037          // that there is no default for RTCP
   1038          mDefaultCandidates[transportId][RTCP].first,
   1039          mDefaultCandidates[transportId][RTCP].second, transportId);
   1040    }
   1041 
   1042    void FinishGathering(JsepSession& session) const {
   1043      // Copy so we can be terse and use []
   1044      for (auto idAndCandidates : mDefaultCandidates) {
   1045        ASSERT_EQ(1U, idAndCandidates.second.count(RTP));
   1046        // do a final UpdateDefaultCandidate here in case candidates were
   1047        // cleared during renegotiation.
   1048        session.UpdateDefaultCandidate(
   1049            idAndCandidates.second[RTP].first,
   1050            idAndCandidates.second[RTP].second,
   1051            // Will be empty string if not present, which is how we indicate
   1052            // that there is no default for RTCP
   1053            idAndCandidates.second[RTCP].first,
   1054            idAndCandidates.second[RTCP].second, idAndCandidates.first);
   1055        std::string mid;
   1056        uint16_t level = 0;
   1057        bool skipped;
   1058        session.AddLocalIceCandidate("", idAndCandidates.first, "", &level,
   1059                                     &mid, &skipped);
   1060      }
   1061    }
   1062 
   1063    void Trickle(JsepSession& session) {
   1064      std::string transportId;
   1065      for (const auto& levelMidAndCandidate : mCandidatesToTrickle) {
   1066        auto [level, mid, candidate] = levelMidAndCandidate;
   1067        std::cerr << "trickling candidate: " << candidate << " level: " << level
   1068                  << " mid: " << mid << std::endl;
   1069        Maybe<unsigned long> lev = Some(level);
   1070        session.AddRemoteIceCandidate(candidate, mid, lev, "", &transportId);
   1071      }
   1072      session.AddRemoteIceCandidate("", "", Maybe<uint16_t>(), "",
   1073                                    &transportId);
   1074      mCandidatesToTrickle.clear();
   1075    }
   1076 
   1077    void CheckRtpCandidates(bool expectRtpCandidates,
   1078                            const SdpMediaSection& msection,
   1079                            const std::string& transportId,
   1080                            const std::string& context) const {
   1081      auto& attrs = msection.GetAttributeList();
   1082 
   1083      ASSERT_EQ(expectRtpCandidates,
   1084                attrs.HasAttribute(SdpAttribute::kCandidateAttribute))
   1085          << context << " (level " << msection.GetLevel() << ")";
   1086 
   1087      if (expectRtpCandidates) {
   1088        // Copy so we can be terse and use []
   1089        auto expectedCandidates = mCandidates;
   1090        ASSERT_LE(kNumCandidatesPerComponent,
   1091                  expectedCandidates[transportId][RTP].size());
   1092 
   1093        auto& candidates = attrs.GetCandidate();
   1094        ASSERT_LE(kNumCandidatesPerComponent, candidates.size())
   1095            << context << " (level " << msection.GetLevel() << ")";
   1096        for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) {
   1097          ASSERT_EQ(expectedCandidates[transportId][RTP][i], candidates[i])
   1098              << context << " (level " << msection.GetLevel() << ")";
   1099        }
   1100      }
   1101    }
   1102 
   1103    void CheckRtcpCandidates(bool expectRtcpCandidates,
   1104                             const SdpMediaSection& msection,
   1105                             const std::string& transportId,
   1106                             const std::string& context) const {
   1107      auto& attrs = msection.GetAttributeList();
   1108 
   1109      if (expectRtcpCandidates) {
   1110        // Copy so we can be terse and use []
   1111        auto expectedCandidates = mCandidates;
   1112        ASSERT_LE(kNumCandidatesPerComponent,
   1113                  expectedCandidates[transportId][RTCP].size());
   1114 
   1115        ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kCandidateAttribute))
   1116        << context << " (level " << msection.GetLevel() << ")";
   1117        auto& candidates = attrs.GetCandidate();
   1118        ASSERT_EQ(kNumCandidatesPerComponent * 2, candidates.size())
   1119            << context << " (level " << msection.GetLevel() << ")";
   1120        for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) {
   1121          ASSERT_EQ(expectedCandidates[transportId][RTCP][i],
   1122                    candidates[i + kNumCandidatesPerComponent])
   1123              << context << " (level " << msection.GetLevel() << ")";
   1124        }
   1125      }
   1126    }
   1127 
   1128    void CheckDefaultRtpCandidate(bool expectDefault,
   1129                                  const SdpMediaSection& msection,
   1130                                  const std::string& transportId,
   1131                                  const std::string& context) const {
   1132      Address expectedAddress = "0.0.0.0";
   1133      Port expectedPort = 9U;
   1134 
   1135      if (expectDefault) {
   1136        // Copy so we can be terse and use []
   1137        auto defaultCandidates = mDefaultCandidates;
   1138        expectedAddress = defaultCandidates[transportId][RTP].first;
   1139        expectedPort = defaultCandidates[transportId][RTP].second;
   1140      }
   1141 
   1142      // if bundle-only attribute is present, expect port 0
   1143      const SdpAttributeList& attrs = msection.GetAttributeList();
   1144      if (attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute)) {
   1145        expectedPort = 0U;
   1146      }
   1147 
   1148      ASSERT_EQ(expectedAddress, msection.GetConnection().GetAddress())
   1149          << context << " (level " << msection.GetLevel() << ")";
   1150      ASSERT_EQ(expectedPort, msection.GetPort())
   1151          << context << " (level " << msection.GetLevel() << ")";
   1152    }
   1153 
   1154    void CheckDefaultRtcpCandidate(bool expectDefault,
   1155                                   const SdpMediaSection& msection,
   1156                                   const std::string& transportId,
   1157                                   const std::string& context) const {
   1158      if (expectDefault) {
   1159        // Copy so we can be terse and use []
   1160        auto defaultCandidates = mDefaultCandidates;
   1161        ASSERT_TRUE(msection.GetAttributeList().HasAttribute(
   1162            SdpAttribute::kRtcpAttribute))
   1163        << context << " (level " << msection.GetLevel() << ")";
   1164        auto& rtcpAttr = msection.GetAttributeList().GetRtcp();
   1165        ASSERT_EQ(defaultCandidates[transportId][RTCP].second, rtcpAttr.mPort)
   1166            << context << " (level " << msection.GetLevel() << ")";
   1167        ASSERT_EQ(sdp::kInternet, rtcpAttr.mNetType)
   1168            << context << " (level " << msection.GetLevel() << ")";
   1169        ASSERT_EQ(sdp::kIPv4, rtcpAttr.mAddrType)
   1170            << context << " (level " << msection.GetLevel() << ")";
   1171        ASSERT_EQ(defaultCandidates[transportId][RTCP].first, rtcpAttr.mAddress)
   1172            << context << " (level " << msection.GetLevel() << ")";
   1173      } else {
   1174        ASSERT_FALSE(msection.GetAttributeList().HasAttribute(
   1175            SdpAttribute::kRtcpAttribute))
   1176        << context << " (level " << msection.GetLevel() << ")";
   1177      }
   1178    }
   1179 
   1180   private:
   1181    using Level = size_t;
   1182    using TransportId = std::string;
   1183    using Mid = std::string;
   1184    using Candidate = std::string;
   1185    using Address = std::string;
   1186    using Port = uint16_t;
   1187    // Default candidates are put into the m-line, c-line, and rtcp
   1188    // attribute for endpoints that don't support ICE.
   1189    std::map<TransportId, std::map<ComponentType, std::pair<Address, Port>>>
   1190        mDefaultCandidates;
   1191    std::map<TransportId, std::map<ComponentType, std::vector<Candidate>>>
   1192        mCandidates;
   1193    // Level/mid/candidate tuples that need to be trickled
   1194    std::vector<std::tuple<Level, Mid, Candidate>> mCandidatesToTrickle;
   1195  };
   1196 
   1197  // For streaming parse errors
   1198  std::string GetParseErrors(
   1199      const UniquePtr<SdpParser::Results>& results) const {
   1200    std::stringstream output;
   1201    auto errors = std::move(results->Errors());
   1202    for (auto error : errors) {
   1203      output << error.first << ": " << error.second << std::endl;
   1204    }
   1205    return output.str();
   1206  }
   1207 
   1208  void CheckEndOfCandidates(bool expectEoc, const SdpMediaSection& msection,
   1209                            const std::string& context) {
   1210    if (expectEoc) {
   1211      ASSERT_TRUE(msection.GetAttributeList().HasAttribute(
   1212          SdpAttribute::kEndOfCandidatesAttribute))
   1213      << context << " (level " << msection.GetLevel() << ")";
   1214    } else {
   1215      ASSERT_FALSE(msection.GetAttributeList().HasAttribute(
   1216          SdpAttribute::kEndOfCandidatesAttribute))
   1217      << context << " (level " << msection.GetLevel() << ")";
   1218    }
   1219  }
   1220 
   1221  void CheckTransceiversAreBundled(const JsepSession& session,
   1222                                   const std::string& context) {
   1223    for (const auto& transceiver : GetTransceivers(session)) {
   1224      ASSERT_TRUE(transceiver.HasBundleLevel())
   1225      << context;
   1226      ASSERT_EQ(0U, transceiver.BundleLevel()) << context;
   1227      ASSERT_NE("", transceiver.mTransport.mTransportId);
   1228    }
   1229  }
   1230 
   1231  void DisableMsid(std::string* sdp) const {
   1232    while (true) {
   1233      size_t pos = sdp->find("a=msid");
   1234      if (pos == std::string::npos) {
   1235        break;
   1236      }
   1237      (*sdp)[pos + 2] = 'X';  // garble, a=Xsid
   1238    }
   1239  }
   1240 
   1241  void DisableBundle(std::string* sdp) const {
   1242    size_t pos = sdp->find("a=group:BUNDLE");
   1243    ASSERT_NE(std::string::npos, pos);
   1244    (*sdp)[pos + 11] = 'G';  // garble, a=group:BUNGLE
   1245  }
   1246 
   1247  void DisableMsection(std::string* sdp, size_t level) const {
   1248    UniquePtr<Sdp> parsed(Parse(*sdp));
   1249    ASSERT_TRUE(parsed.get());
   1250    ASSERT_LT(level, parsed->GetMediaSectionCount());
   1251    SdpHelper::DisableMsection(parsed.get(), &parsed->GetMediaSection(level));
   1252    (*sdp) = parsed->ToString();
   1253  }
   1254 
   1255  void CopyTransportAttributes(std::string* sdp, size_t src_level,
   1256                               size_t dst_level) {
   1257    UniquePtr<Sdp> parsed(Parse(*sdp));
   1258    ASSERT_TRUE(parsed.get());
   1259    ASSERT_LT(src_level, parsed->GetMediaSectionCount());
   1260    ASSERT_LT(dst_level, parsed->GetMediaSectionCount());
   1261    nsresult rv =
   1262        mSdpHelper.CopyTransportParams(2, parsed->GetMediaSection(src_level),
   1263                                       &parsed->GetMediaSection(dst_level));
   1264    ASSERT_EQ(NS_OK, rv);
   1265    (*sdp) = parsed->ToString();
   1266  }
   1267 
   1268  void ReplaceInSdp(std::string* sdp, const char* searchStr,
   1269                    const char* replaceStr) const {
   1270    if (searchStr[0] == '\0') return;
   1271    size_t pos = 0;
   1272    while ((pos = sdp->find(searchStr, pos)) != std::string::npos) {
   1273      sdp->replace(pos, strlen(searchStr), replaceStr);
   1274      pos += strlen(replaceStr);
   1275    }
   1276  }
   1277 
   1278  void ValidateDisabledMSection(const SdpMediaSection* msection) {
   1279    ASSERT_EQ(1U, msection->GetFormats().size());
   1280 
   1281    auto& attrs = msection->GetAttributeList();
   1282    ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kMidAttribute));
   1283    ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kDirectionAttribute));
   1284    ASSERT_FALSE(attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute));
   1285    ASSERT_EQ(SdpDirectionAttribute::kInactive,
   1286              msection->GetDirectionAttribute().mValue);
   1287    ASSERT_EQ(3U, attrs.Count());
   1288    if (msection->GetMediaType() == SdpMediaSection::kAudio) {
   1289      ASSERT_EQ("0", msection->GetFormats()[0]);
   1290      const SdpRtpmapAttributeList::Rtpmap* rtpmap(msection->FindRtpmap("0"));
   1291      ASSERT_TRUE(rtpmap);
   1292      ASSERT_EQ("0", rtpmap->pt);
   1293      ASSERT_EQ("PCMU", rtpmap->name);
   1294    } else if (msection->GetMediaType() == SdpMediaSection::kVideo) {
   1295      ASSERT_EQ("120", msection->GetFormats()[0]);
   1296      const SdpRtpmapAttributeList::Rtpmap* rtpmap(msection->FindRtpmap("120"));
   1297      ASSERT_TRUE(rtpmap);
   1298      ASSERT_EQ("120", rtpmap->pt);
   1299      ASSERT_EQ("VP8", rtpmap->name);
   1300    } else if (msection->GetMediaType() == SdpMediaSection::kApplication) {
   1301      if (msection->GetProtocol() == SdpMediaSection::kUdpDtlsSctp ||
   1302          msection->GetProtocol() == SdpMediaSection::kTcpDtlsSctp) {
   1303        // draft 21 format
   1304        ASSERT_EQ("webrtc-datachannel", msection->GetFormats()[0]);
   1305        ASSERT_FALSE(msection->GetSctpmap());
   1306        ASSERT_EQ(0U, msection->GetSctpPort());
   1307      } else {
   1308        // old draft 05 format
   1309        ASSERT_EQ("0", msection->GetFormats()[0]);
   1310        const SdpSctpmapAttributeList::Sctpmap* sctpmap(msection->GetSctpmap());
   1311        ASSERT_TRUE(sctpmap);
   1312        ASSERT_EQ("0", sctpmap->pt);
   1313        ASSERT_EQ("rejected", sctpmap->name);
   1314        ASSERT_EQ(0U, sctpmap->streams);
   1315      }
   1316    } else {
   1317      // Not that we would have any test which tests this...
   1318      ASSERT_EQ("19", msection->GetFormats()[0]);
   1319      const SdpRtpmapAttributeList::Rtpmap* rtpmap(msection->FindRtpmap("19"));
   1320      ASSERT_TRUE(rtpmap);
   1321      ASSERT_EQ("19", rtpmap->pt);
   1322      ASSERT_EQ("reserved", rtpmap->name);
   1323    }
   1324 
   1325    ASSERT_FALSE(msection->GetAttributeList().HasAttribute(
   1326        SdpAttribute::kMsidAttribute));
   1327  }
   1328 
   1329  void ValidateSetupAttribute(const JsepSessionImpl& side,
   1330                              const SdpSetupAttribute::Role expectedRole) {
   1331    auto sdp = GetParsedLocalDescription(side);
   1332    for (size_t i = 0; sdp && i < sdp->GetMediaSectionCount(); ++i) {
   1333      if (sdp->GetMediaSection(i).GetAttributeList().HasAttribute(
   1334              SdpAttribute::kSetupAttribute)) {
   1335        auto role = sdp->GetMediaSection(i).GetAttributeList().GetSetup().mRole;
   1336        ASSERT_EQ(expectedRole, role);
   1337      }
   1338    }
   1339  }
   1340 
   1341  void DumpTrack(const JsepTrack& track) {
   1342    const JsepTrackNegotiatedDetails* details = track.GetNegotiatedDetails();
   1343    std::cerr << "  type=" << track.GetMediaType() << std::endl;
   1344    if (!details) {
   1345      std::cerr << "  not negotiated" << std::endl;
   1346      return;
   1347    }
   1348    std::cerr << "  encodings=" << std::endl;
   1349    for (size_t i = 0; i < details->GetEncodingCount(); ++i) {
   1350      const JsepTrackEncoding& encoding = details->GetEncoding(i);
   1351      std::cerr << "    id=" << encoding.mRid << std::endl;
   1352      for (const auto& codec : encoding.GetCodecs()) {
   1353        std::cerr << "      " << codec->mName << " enabled("
   1354                  << (codec->mEnabled ? "yes" : "no") << ")";
   1355        if (track.GetMediaType() == SdpMediaSection::kAudio) {
   1356          const JsepAudioCodecDescription* audioCodec =
   1357              static_cast<const JsepAudioCodecDescription*>(codec.get());
   1358          std::cerr << " dtmf(" << (audioCodec->mDtmfEnabled ? "yes" : "no")
   1359                    << ")";
   1360        }
   1361        if (track.GetMediaType() == SdpMediaSection::kVideo) {
   1362          const JsepVideoCodecDescription* videoCodec =
   1363              static_cast<const JsepVideoCodecDescription*>(codec.get());
   1364          std::cerr << " rtx("
   1365                    << (videoCodec->mRtxEnabled ? videoCodec->mRtxPayloadType
   1366                                                : "no")
   1367                    << ")";
   1368        }
   1369        std::cerr << std::endl;
   1370      }
   1371    }
   1372  }
   1373 
   1374  void DumpTransport(const JsepTransport& transport) {
   1375    std::cerr << "  id=" << transport.mTransportId << std::endl;
   1376    std::cerr << "  components=" << transport.mComponents << std::endl;
   1377  }
   1378 
   1379  void DumpTransceivers(const JsepSessionImpl& session) {
   1380    for (const auto& transceiver : GetTransceivers(session)) {
   1381      std::cerr << "Transceiver ";
   1382      if (transceiver.HasLevel()) {
   1383        std::cerr << transceiver.GetLevel() << std::endl;
   1384      } else {
   1385        std::cerr << "<NO LEVEL>" << std::endl;
   1386      }
   1387      if (transceiver.HasBundleLevel()) {
   1388        std::cerr << "(bundle level is " << transceiver.BundleLevel() << ")"
   1389                  << std::endl;
   1390      }
   1391      if (!IsNull(transceiver.mSendTrack)) {
   1392        std::cerr << "Sending-->" << std::endl;
   1393        DumpTrack(transceiver.mSendTrack);
   1394      }
   1395      if (!IsNull(transceiver.mRecvTrack)) {
   1396        std::cerr << "Receiving-->" << std::endl;
   1397        DumpTrack(transceiver.mRecvTrack);
   1398      }
   1399      std::cerr << "Transport-->" << std::endl;
   1400      DumpTransport(transceiver.mTransport);
   1401    }
   1402  }
   1403 
   1404  UniquePtr<Sdp> Parse(const std::string& sdp) const {
   1405    SipccSdpParser parser;
   1406    auto results = parser.Parse(sdp);
   1407    UniquePtr<Sdp> parsed = std::move(results->Sdp());
   1408    EXPECT_TRUE(parsed.get()) << "Should have valid SDP" << std::endl
   1409                              << "Errors were: " << GetParseErrors(results);
   1410    return parsed;
   1411  }
   1412 
   1413  std::string SetExtmap(const std::string& aSdp, const std::string& aUri,
   1414                        uint16_t aId, uint16_t* aOldId = nullptr) {
   1415    UniquePtr<Sdp> munge(Parse(aSdp));
   1416    for (size_t i = 0; i < munge->GetMediaSectionCount(); ++i) {
   1417      auto& attrs = munge->GetMediaSection(i).GetAttributeList();
   1418      if (attrs.HasAttribute(SdpAttribute::kExtmapAttribute)) {
   1419        auto extmap = attrs.GetExtmap();
   1420        for (auto it = extmap.mExtmaps.begin(); it != extmap.mExtmaps.end();
   1421             ++it) {
   1422          if (it->extensionname == aUri) {
   1423            if (aOldId) {
   1424              *aOldId = it->entry;
   1425            }
   1426 
   1427            if (aId) {
   1428              it->entry = aId;
   1429            } else {
   1430              extmap.mExtmaps.erase(it);
   1431            }
   1432            break;
   1433          }
   1434        }
   1435        attrs.SetAttribute(extmap.Clone());
   1436      }
   1437    }
   1438    return munge->ToString();
   1439  }
   1440 
   1441  uint16_t GetExtmap(const std::string& aSdp, const std::string& aUri) {
   1442    UniquePtr<Sdp> parsed(Parse(aSdp));
   1443    for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) {
   1444      auto& attrs = parsed->GetMediaSection(i).GetAttributeList();
   1445      if (attrs.HasAttribute(SdpAttribute::kExtmapAttribute)) {
   1446        auto extmap = attrs.GetExtmap();
   1447        for (auto& ext : extmap.mExtmaps) {
   1448          if (ext.extensionname == aUri) {
   1449            return ext.entry;
   1450          }
   1451        }
   1452      }
   1453    }
   1454    return 0;
   1455  }
   1456 
   1457  void SwapOfferAnswerRoles() {
   1458    mSessionOff.swap(mSessionAns);
   1459    mOffCandidates.swap(mAnsCandidates);
   1460    mOffererTransport.swap(mAnswererTransport);
   1461  }
   1462 
   1463  UniquePtr<JsepSessionImpl> mSessionOff;
   1464  UniquePtr<CandidateSet> mOffCandidates;
   1465  UniquePtr<JsepSessionImpl> mSessionAns;
   1466  UniquePtr<CandidateSet> mAnsCandidates;
   1467 
   1468  std::vector<SdpMediaSection::MediaType> types;
   1469  std::vector<std::pair<std::string, uint16_t>> mGatheredCandidates;
   1470 
   1471  FakeUuidGenerator mUuidGen;
   1472 
   1473 private:
   1474  void ValidateTransport(TransportData& source, const std::string& sdp_str,
   1475                         sdp::SdpType type) {
   1476    UniquePtr<Sdp> sdp(Parse(sdp_str));
   1477    ASSERT_TRUE(!!sdp);
   1478    size_t num_m_sections = sdp->GetMediaSectionCount();
   1479    for (size_t i = 0; i < num_m_sections; ++i) {
   1480      auto& msection = sdp->GetMediaSection(i);
   1481 
   1482      if (msection.GetMediaType() == SdpMediaSection::kApplication) {
   1483        if (!(msection.GetProtocol() == SdpMediaSection::kUdpDtlsSctp ||
   1484              msection.GetProtocol() == SdpMediaSection::kTcpDtlsSctp)) {
   1485          // old draft 05 format
   1486          ASSERT_EQ(SdpMediaSection::kDtlsSctp, msection.GetProtocol());
   1487        }
   1488      } else {
   1489        ASSERT_EQ(SdpMediaSection::kUdpTlsRtpSavpf, msection.GetProtocol());
   1490      }
   1491 
   1492      const SdpAttributeList& attrs = msection.GetAttributeList();
   1493      bool bundle_only = attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute);
   1494 
   1495      // port 0 only means disabled when the bundle-only attribute is missing
   1496      if (!bundle_only && msection.GetPort() == 0) {
   1497        ValidateDisabledMSection(&msection);
   1498        continue;
   1499      }
   1500      if (mSdpHelper.OwnsTransport(*sdp, i, type)) {
   1501        const SdpAttributeList& attrs = msection.GetAttributeList();
   1502 
   1503        ASSERT_FALSE(attrs.GetIceUfrag().empty());
   1504        ASSERT_FALSE(attrs.GetIcePwd().empty());
   1505        const SdpFingerprintAttributeList& fps = attrs.GetFingerprint();
   1506        for (auto fp = fps.mFingerprints.begin(); fp != fps.mFingerprints.end();
   1507             ++fp) {
   1508          nsCString alg_str = "None"_ns;
   1509 
   1510          if (fp->hashFunc == SdpFingerprintAttributeList::kSha1) {
   1511            alg_str = "sha-1"_ns;
   1512          } else if (fp->hashFunc == SdpFingerprintAttributeList::kSha256) {
   1513            alg_str = "sha-256"_ns;
   1514          }
   1515          ASSERT_EQ(source.mFingerprints[alg_str], fp->fingerprint);
   1516        }
   1517 
   1518        ASSERT_EQ(source.mFingerprints.size(), fps.mFingerprints.size());
   1519      }
   1520    }
   1521  }
   1522 
   1523 protected:
   1524  bool ExtmapAllowMixed(const JsepSessionImpl& aSession) {
   1525    if (aSession.mCurrentLocalDescription) {
   1526      return aSession.mCurrentLocalDescription->GetAttributeList().HasAttribute(
   1527          SdpAttribute::kExtmapAllowMixedAttribute);
   1528    }
   1529    if (aSession.mPendingLocalDescription) {
   1530      return aSession.mPendingLocalDescription->GetAttributeList().HasAttribute(
   1531          SdpAttribute::kExtmapAllowMixedAttribute);
   1532    }
   1533    return false;
   1534  }
   1535 
   1536 private:
   1537  std::string mLastError;
   1538  SdpHelper mSdpHelper;
   1539 
   1540  UniquePtr<TransportData> mOffererTransport;
   1541  UniquePtr<TransportData> mAnswererTransport;
   1542 };
   1543 
   1544 TEST_F(JsepSessionTestBase, CreateDestroy) {}
   1545 
   1546 TEST_P(JsepSessionTest, CreateOffer) {
   1547  AddTracks(*mSessionOff);
   1548  CreateOffer();
   1549 }
   1550 
   1551 TEST_P(JsepSessionTest, CreateOfferSetLocal) {
   1552  AddTracks(*mSessionOff);
   1553  std::string offer = CreateOffer();
   1554  SetLocalOffer(offer);
   1555 }
   1556 
   1557 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemote) {
   1558  AddTracks(*mSessionOff);
   1559  std::string offer = CreateOffer();
   1560  SetLocalOffer(offer);
   1561  SetRemoteOffer(offer);
   1562 }
   1563 
   1564 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemoteCreateAnswer) {
   1565  AddTracks(*mSessionOff);
   1566  std::string offer = CreateOffer();
   1567  SetLocalOffer(offer);
   1568  SetRemoteOffer(offer);
   1569  AddTracks(*mSessionAns);
   1570  std::string answer = CreateAnswer();
   1571 }
   1572 
   1573 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemoteCreateAnswerSetLocal) {
   1574  AddTracks(*mSessionOff);
   1575  std::string offer = CreateOffer();
   1576  SetLocalOffer(offer);
   1577  SetRemoteOffer(offer);
   1578  AddTracks(*mSessionAns);
   1579  std::string answer = CreateAnswer();
   1580  SetLocalAnswer(answer);
   1581 }
   1582 
   1583 TEST_P(JsepSessionTest, FullCall) {
   1584  AddTracks(*mSessionOff);
   1585  std::string offer = CreateOffer();
   1586  SetLocalOffer(offer);
   1587  SetRemoteOffer(offer);
   1588  AddTracks(*mSessionAns);
   1589  std::string answer = CreateAnswer();
   1590  SetLocalAnswer(answer);
   1591  SetRemoteAnswer(answer);
   1592 }
   1593 
   1594 TEST_P(JsepSessionTest, GetDescriptions) {
   1595  AddTracks(*mSessionOff);
   1596  std::string offer = CreateOffer();
   1597  SetLocalOffer(offer);
   1598  std::string desc = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent);
   1599  ASSERT_EQ(0U, desc.size());
   1600  desc = mSessionOff->GetLocalDescription(kJsepDescriptionPending);
   1601  ASSERT_NE(0U, desc.size());
   1602  desc = mSessionOff->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   1603  ASSERT_NE(0U, desc.size());
   1604  desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   1605  ASSERT_EQ(0U, desc.size());
   1606  desc = mSessionAns->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   1607  ASSERT_EQ(0U, desc.size());
   1608  desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   1609  ASSERT_EQ(0U, desc.size());
   1610 
   1611  SetRemoteOffer(offer);
   1612  desc = mSessionAns->GetRemoteDescription(kJsepDescriptionCurrent);
   1613  ASSERT_EQ(0U, desc.size());
   1614  desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPending);
   1615  ASSERT_NE(0U, desc.size());
   1616  desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   1617  ASSERT_NE(0U, desc.size());
   1618  desc = mSessionAns->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   1619  ASSERT_EQ(0U, desc.size());
   1620  desc = mSessionOff->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   1621  ASSERT_NE(0U, desc.size());
   1622  desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   1623  ASSERT_EQ(0U, desc.size());
   1624 
   1625  AddTracks(*mSessionAns);
   1626  std::string answer = CreateAnswer();
   1627  SetLocalAnswer(answer);
   1628  desc = mSessionAns->GetLocalDescription(kJsepDescriptionCurrent);
   1629  ASSERT_NE(0U, desc.size());
   1630  desc = mSessionAns->GetLocalDescription(kJsepDescriptionPending);
   1631  ASSERT_EQ(0U, desc.size());
   1632  desc = mSessionAns->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   1633  ASSERT_NE(0U, desc.size());
   1634  desc = mSessionAns->GetRemoteDescription(kJsepDescriptionCurrent);
   1635  ASSERT_NE(0U, desc.size());
   1636  desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPending);
   1637  ASSERT_EQ(0U, desc.size());
   1638  desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   1639  ASSERT_NE(0U, desc.size());
   1640  desc = mSessionOff->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   1641  ASSERT_NE(0U, desc.size());
   1642  desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   1643  ASSERT_EQ(0U, desc.size());
   1644 
   1645  SetRemoteAnswer(answer);
   1646  desc = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent);
   1647  ASSERT_NE(0U, desc.size());
   1648  desc = mSessionOff->GetLocalDescription(kJsepDescriptionPending);
   1649  ASSERT_EQ(0U, desc.size());
   1650  desc = mSessionOff->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   1651  ASSERT_NE(0U, desc.size());
   1652  desc = mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent);
   1653  ASSERT_NE(0U, desc.size());
   1654  desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPending);
   1655  ASSERT_EQ(0U, desc.size());
   1656  desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   1657  ASSERT_NE(0U, desc.size());
   1658  desc = mSessionAns->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   1659  ASSERT_NE(0U, desc.size());
   1660  desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   1661  ASSERT_NE(0U, desc.size());
   1662 }
   1663 
   1664 TEST_P(JsepSessionTest, RenegotiationNoChange) {
   1665  AddTracks(*mSessionOff);
   1666  std::string offer = CreateOffer();
   1667  SetLocalOffer(offer);
   1668  SetRemoteOffer(offer);
   1669 
   1670  AddTracks(*mSessionAns);
   1671  std::string answer = CreateAnswer();
   1672  SetLocalAnswer(answer);
   1673  SetRemoteAnswer(answer);
   1674 
   1675  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1676  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1677 
   1678  std::vector<JsepTransceiver> origOffererTransceivers =
   1679      GetTransceivers(*mSessionOff);
   1680  std::vector<JsepTransceiver> origAnswererTransceivers =
   1681      GetTransceivers(*mSessionAns);
   1682 
   1683  std::string reoffer = CreateOffer();
   1684  SetLocalOffer(reoffer);
   1685  SetRemoteOffer(reoffer);
   1686 
   1687  std::string reanswer = CreateAnswer();
   1688  SetLocalAnswer(reanswer);
   1689  SetRemoteAnswer(reanswer);
   1690 
   1691  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1692  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1693 
   1694  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   1695  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   1696 
   1697  ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   1698  ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers));
   1699 }
   1700 
   1701 // Disabled: See Bug 1329028
   1702 TEST_P(JsepSessionTest, DISABLED_RenegotiationSwappedRolesNoChange) {
   1703  AddTracks(*mSessionOff);
   1704  std::string offer = CreateOffer();
   1705  SetLocalOffer(offer);
   1706  SetRemoteOffer(offer);
   1707 
   1708  AddTracks(*mSessionAns);
   1709  std::string answer = CreateAnswer();
   1710  SetLocalAnswer(answer);
   1711  SetRemoteAnswer(answer);
   1712 
   1713  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1714  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1715 
   1716  auto offererTransceivers = GetTransceivers(*mSessionOff);
   1717  auto answererTransceivers = GetTransceivers(*mSessionAns);
   1718 
   1719  SwapOfferAnswerRoles();
   1720 
   1721  std::string reoffer = CreateOffer();
   1722  SetLocalOffer(reoffer);
   1723  SetRemoteOffer(reoffer);
   1724 
   1725  std::string reanswer = CreateAnswer();
   1726  SetLocalAnswer(reanswer);
   1727  SetRemoteAnswer(reanswer);
   1728 
   1729  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1730  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kPassive);
   1731 
   1732  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   1733  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   1734 
   1735  ASSERT_TRUE(Equals(offererTransceivers, newAnswererTransceivers));
   1736  ASSERT_TRUE(Equals(answererTransceivers, newOffererTransceivers));
   1737 }
   1738 
   1739 static void RemoveLastN(std::vector<JsepTransceiver>& aTransceivers,
   1740                        size_t aNum) {
   1741  while (aNum--) {
   1742    // erase doesn't take reverse_iterator :(
   1743    aTransceivers.erase(--aTransceivers.end());
   1744  }
   1745 }
   1746 
   1747 TEST_P(JsepSessionTest, RenegotiationOffererAddsTrack) {
   1748  AddTracks(*mSessionOff);
   1749  AddTracks(*mSessionAns);
   1750 
   1751  OfferAnswer();
   1752 
   1753  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1754  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1755 
   1756  std::vector<JsepTransceiver> origOffererTransceivers =
   1757      GetTransceivers(*mSessionOff);
   1758  std::vector<JsepTransceiver> origAnswererTransceivers =
   1759      GetTransceivers(*mSessionAns);
   1760 
   1761  std::vector<SdpMediaSection::MediaType> extraTypes;
   1762  extraTypes.push_back(SdpMediaSection::kAudio);
   1763  extraTypes.push_back(SdpMediaSection::kVideo);
   1764  AddTracks(*mSessionOff, extraTypes);
   1765  types.insert(types.end(), extraTypes.begin(), extraTypes.end());
   1766 
   1767  OfferAnswer(CHECK_SUCCESS);
   1768 
   1769  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1770  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1771 
   1772  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   1773  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   1774 
   1775  ASSERT_LE(2U, newOffererTransceivers.size());
   1776  RemoveLastN(newOffererTransceivers, 2);
   1777  ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   1778 
   1779  ASSERT_LE(2U, newAnswererTransceivers.size());
   1780  RemoveLastN(newAnswererTransceivers, 2);
   1781  ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers));
   1782 }
   1783 
   1784 TEST_P(JsepSessionTest, RenegotiationAnswererAddsTrack) {
   1785  AddTracks(*mSessionOff);
   1786  AddTracks(*mSessionAns);
   1787 
   1788  OfferAnswer();
   1789 
   1790  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1791  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1792 
   1793  std::vector<JsepTransceiver> origOffererTransceivers =
   1794      GetTransceivers(*mSessionOff);
   1795  std::vector<JsepTransceiver> origAnswererTransceivers =
   1796      GetTransceivers(*mSessionAns);
   1797 
   1798  std::vector<SdpMediaSection::MediaType> extraTypes;
   1799  extraTypes.push_back(SdpMediaSection::kAudio);
   1800  extraTypes.push_back(SdpMediaSection::kVideo);
   1801  AddTracks(*mSessionAns, extraTypes);
   1802  types.insert(types.end(), extraTypes.begin(), extraTypes.end());
   1803 
   1804  // We need to add a recvonly m-section to the offer for this to work
   1805  mSessionOff->AddTransceiver(
   1806      JsepTransceiver(SdpMediaSection::kAudio, mUuidGen,
   1807                      SdpDirectionAttribute::Direction::kRecvonly));
   1808  mSessionOff->AddTransceiver(
   1809      JsepTransceiver(SdpMediaSection::kVideo, mUuidGen,
   1810                      SdpDirectionAttribute::Direction::kRecvonly));
   1811 
   1812  std::string offer = CreateOffer();
   1813  SetLocalOffer(offer, CHECK_SUCCESS);
   1814  SetRemoteOffer(offer, CHECK_SUCCESS);
   1815 
   1816  std::string answer = CreateAnswer();
   1817  SetLocalAnswer(answer, CHECK_SUCCESS);
   1818  SetRemoteAnswer(answer, CHECK_SUCCESS);
   1819 
   1820  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1821  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1822 
   1823  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   1824  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   1825 
   1826  ASSERT_LE(2U, newOffererTransceivers.size());
   1827  RemoveLastN(newOffererTransceivers, 2);
   1828  ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   1829 
   1830  ASSERT_LE(2U, newAnswererTransceivers.size());
   1831  RemoveLastN(newAnswererTransceivers, 2);
   1832  ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers));
   1833 }
   1834 
   1835 TEST_P(JsepSessionTest, RenegotiationBothAddTrack) {
   1836  AddTracks(*mSessionOff);
   1837  AddTracks(*mSessionAns);
   1838 
   1839  OfferAnswer();
   1840 
   1841  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1842  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1843 
   1844  std::vector<JsepTransceiver> origOffererTransceivers =
   1845      GetTransceivers(*mSessionOff);
   1846  std::vector<JsepTransceiver> origAnswererTransceivers =
   1847      GetTransceivers(*mSessionAns);
   1848 
   1849  std::vector<SdpMediaSection::MediaType> extraTypes;
   1850  extraTypes.push_back(SdpMediaSection::kAudio);
   1851  extraTypes.push_back(SdpMediaSection::kVideo);
   1852  AddTracks(*mSessionAns, extraTypes);
   1853  AddTracks(*mSessionOff, extraTypes);
   1854  types.insert(types.end(), extraTypes.begin(), extraTypes.end());
   1855 
   1856  OfferAnswer(CHECK_SUCCESS);
   1857 
   1858  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   1859  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   1860 
   1861  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   1862  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   1863 
   1864  ASSERT_LE(2U, newOffererTransceivers.size());
   1865  RemoveLastN(newOffererTransceivers, 2);
   1866  ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   1867 
   1868  ASSERT_LE(2U, newAnswererTransceivers.size());
   1869  RemoveLastN(newAnswererTransceivers, 2);
   1870  ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers));
   1871 }
   1872 
   1873 TEST_P(JsepSessionTest, RenegotiationBothAddTracksToExistingStream) {
   1874  AddTracks(*mSessionOff);
   1875  AddTracks(*mSessionAns);
   1876  if (GetParam() == "datachannel") {
   1877    return;
   1878  }
   1879 
   1880  OfferAnswer();
   1881 
   1882  auto oHasStream = HasMediaStream(GetLocalTracks(*mSessionOff));
   1883  auto aHasStream = HasMediaStream(GetLocalTracks(*mSessionAns));
   1884  ASSERT_EQ(oHasStream, !GetLocalUniqueStreamIds(*mSessionOff).empty());
   1885  ASSERT_EQ(aHasStream, !GetLocalUniqueStreamIds(*mSessionAns).empty());
   1886  ASSERT_EQ(aHasStream, !GetRemoteUniqueStreamIds(*mSessionOff).empty());
   1887  ASSERT_EQ(oHasStream, !GetRemoteUniqueStreamIds(*mSessionAns).empty());
   1888 
   1889  auto firstOffId = GetFirstLocalStreamId(*mSessionOff);
   1890  auto firstAnsId = GetFirstLocalStreamId(*mSessionAns);
   1891 
   1892  auto offererTransceivers = GetTransceivers(*mSessionOff);
   1893  auto answererTransceivers = GetTransceivers(*mSessionAns);
   1894 
   1895  std::vector<SdpMediaSection::MediaType> extraTypes;
   1896  extraTypes.push_back(SdpMediaSection::kAudio);
   1897  extraTypes.push_back(SdpMediaSection::kVideo);
   1898  AddTracksToStream(*mSessionOff, firstOffId, extraTypes);
   1899  AddTracksToStream(*mSessionAns, firstAnsId, extraTypes);
   1900  types.insert(types.end(), extraTypes.begin(), extraTypes.end());
   1901 
   1902  OfferAnswer(CHECK_SUCCESS);
   1903 
   1904  oHasStream = HasMediaStream(GetLocalTracks(*mSessionOff));
   1905  aHasStream = HasMediaStream(GetLocalTracks(*mSessionAns));
   1906 
   1907  ASSERT_EQ(oHasStream, !GetLocalUniqueStreamIds(*mSessionOff).empty());
   1908  ASSERT_EQ(aHasStream, !GetLocalUniqueStreamIds(*mSessionAns).empty());
   1909  ASSERT_EQ(aHasStream, !GetRemoteUniqueStreamIds(*mSessionOff).empty());
   1910  ASSERT_EQ(oHasStream, !GetRemoteUniqueStreamIds(*mSessionAns).empty());
   1911  if (oHasStream) {
   1912    ASSERT_STREQ(firstOffId.c_str(),
   1913                 GetFirstLocalStreamId(*mSessionOff).c_str());
   1914  }
   1915  if (aHasStream) {
   1916    ASSERT_STREQ(firstAnsId.c_str(),
   1917                 GetFirstLocalStreamId(*mSessionAns).c_str());
   1918 
   1919    auto oHasStream = HasMediaStream(GetLocalTracks(*mSessionOff));
   1920    auto aHasStream = HasMediaStream(GetLocalTracks(*mSessionAns));
   1921    ASSERT_EQ(oHasStream, !GetLocalUniqueStreamIds(*mSessionOff).empty());
   1922    ASSERT_EQ(aHasStream, !GetLocalUniqueStreamIds(*mSessionAns).empty());
   1923  }
   1924 }
   1925 
   1926 // The JSEP draft explicitly forbids changing the msid on an m-section, but
   1927 // that is a bug.
   1928 TEST_P(JsepSessionTest, RenegotiationOffererChangesMsid) {
   1929  AddTracks(*mSessionOff);
   1930  AddTracks(*mSessionAns);
   1931 
   1932  OfferAnswer();
   1933 
   1934  std::string offer = CreateOffer();
   1935  SetLocalOffer(offer);
   1936 
   1937  JsepTransceiver* transceiver = GetNegotiatedTransceiver(*mSessionOff, 0);
   1938  ASSERT_TRUE(transceiver);
   1939  if (transceiver->GetMediaType() == SdpMediaSection::kApplication) {
   1940    return;
   1941  }
   1942  std::string streamId = transceiver->mSendTrack.GetStreamIds()[0];
   1943  std::string msidToReplace("a=msid:");
   1944  msidToReplace += streamId;
   1945  size_t msidOffset = offer.find(msidToReplace);
   1946  ASSERT_NE(std::string::npos, msidOffset);
   1947  offer.replace(msidOffset, msidToReplace.size(), "a=msid:foo");
   1948 
   1949  SetRemoteOffer(offer);
   1950  transceiver = GetNegotiatedTransceiver(*mSessionAns, 0);
   1951  ASSERT_EQ("foo", transceiver->mRecvTrack.GetStreamIds()[0]);
   1952 
   1953  std::string answer = CreateAnswer();
   1954  SetLocalAnswer(answer);
   1955  SetRemoteAnswer(answer);
   1956 }
   1957 
   1958 // The JSEP draft explicitly forbids changing the msid on an m-section, but
   1959 // that is a bug.
   1960 TEST_P(JsepSessionTest, RenegotiationAnswererChangesMsid) {
   1961  AddTracks(*mSessionOff);
   1962  AddTracks(*mSessionAns);
   1963 
   1964  OfferAnswer();
   1965 
   1966  JsepTransceiver* transceiver = GetNegotiatedTransceiver(*mSessionOff, 0);
   1967  ASSERT_TRUE(transceiver);
   1968  if (transceiver->GetMediaType() == SdpMediaSection::kApplication) {
   1969    return;
   1970  }
   1971 
   1972  std::string offer = CreateOffer();
   1973  SetLocalOffer(offer);
   1974  SetRemoteOffer(offer);
   1975  std::string answer = CreateAnswer();
   1976  SetLocalAnswer(answer);
   1977 
   1978  transceiver = GetNegotiatedTransceiver(*mSessionAns, 0);
   1979  ASSERT_TRUE(transceiver);
   1980  if (transceiver->GetMediaType() == SdpMediaSection::kApplication) {
   1981    return;
   1982  }
   1983  std::string streamId = transceiver->mSendTrack.GetStreamIds()[0];
   1984  std::string msidToReplace("a=msid:");
   1985  msidToReplace += streamId;
   1986  size_t msidOffset = answer.find(msidToReplace);
   1987  ASSERT_NE(std::string::npos, msidOffset);
   1988  answer.replace(msidOffset, msidToReplace.size(), "a=msid:foo");
   1989 
   1990  SetRemoteAnswer(answer);
   1991 
   1992  transceiver = GetNegotiatedTransceiver(*mSessionOff, 0);
   1993  ASSERT_EQ("foo", transceiver->mRecvTrack.GetStreamIds()[0]);
   1994 }
   1995 
   1996 TEST_P(JsepSessionTest, RenegotiationOffererStopsTransceiver) {
   1997  AddTracks(*mSessionOff);
   1998  AddTracks(*mSessionAns);
   1999  if (types.back() == SdpMediaSection::kApplication) {
   2000    return;
   2001  }
   2002 
   2003  OfferAnswer();
   2004 
   2005  std::vector<JsepTransceiver> origOffererTransceivers =
   2006      GetTransceivers(*mSessionOff);
   2007  std::vector<JsepTransceiver> origAnswererTransceivers =
   2008      GetTransceivers(*mSessionAns);
   2009 
   2010  JsepTransceiver lastTransceiver = GetTransceivers(*mSessionOff).back();
   2011  // Avoid bundle transport side effects; don't stop the BUNDLE-tag!
   2012  lastTransceiver.Stop();
   2013  mSessionOff->SetTransceiver(lastTransceiver);
   2014  JsepTrack removedTrack(lastTransceiver.mSendTrack);
   2015 
   2016  OfferAnswer(CHECK_SUCCESS);
   2017 
   2018  // Last m-section should be disabled
   2019  auto offer = GetParsedLocalDescription(*mSessionOff);
   2020  const SdpMediaSection* msection =
   2021      &offer->GetMediaSection(offer->GetMediaSectionCount() - 1);
   2022  ASSERT_TRUE(msection);
   2023  ValidateDisabledMSection(msection);
   2024 
   2025  // Last m-section should be disabled
   2026  auto answer = GetParsedLocalDescription(*mSessionAns);
   2027  msection = &answer->GetMediaSection(answer->GetMediaSectionCount() - 1);
   2028  ASSERT_TRUE(msection);
   2029  ValidateDisabledMSection(msection);
   2030 
   2031  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2032  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   2033 
   2034  ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size());
   2035 
   2036  ASSERT_FALSE(origOffererTransceivers.back().IsStopped());
   2037  ASSERT_TRUE(newOffererTransceivers.back().IsStopped());
   2038 
   2039  ASSERT_FALSE(origAnswererTransceivers.back().IsStopped());
   2040  ASSERT_TRUE(newAnswererTransceivers.back().IsStopped());
   2041  RemoveLastN(origOffererTransceivers, 1);   // Ignore this one
   2042  RemoveLastN(newOffererTransceivers, 1);    // Ignore this one
   2043  RemoveLastN(origAnswererTransceivers, 1);  // Ignore this one
   2044  RemoveLastN(newAnswererTransceivers, 1);   // Ignore this one
   2045 
   2046  ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   2047  ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers));
   2048 }
   2049 
   2050 TEST_P(JsepSessionTest, RenegotiationAnswererStopsTransceiver) {
   2051  AddTracks(*mSessionOff);
   2052  AddTracks(*mSessionAns);
   2053  if (types.back() == SdpMediaSection::kApplication) {
   2054    return;
   2055  }
   2056 
   2057  OfferAnswer();
   2058 
   2059  std::vector<JsepTransceiver> origOffererTransceivers =
   2060      GetTransceivers(*mSessionOff);
   2061  std::vector<JsepTransceiver> origAnswererTransceivers =
   2062      GetTransceivers(*mSessionAns);
   2063 
   2064  GetTransceivers(*mSessionAns).back().Stop();
   2065 
   2066  OfferAnswer(CHECK_SUCCESS);
   2067  ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded());
   2068 
   2069  // Last m-section should be sendrecv
   2070  auto offer = GetParsedLocalDescription(*mSessionOff);
   2071  const SdpMediaSection* msection =
   2072      &offer->GetMediaSection(offer->GetMediaSectionCount() - 1);
   2073  ASSERT_TRUE(msection);
   2074  ASSERT_TRUE(msection->IsReceiving());
   2075  ASSERT_TRUE(msection->IsSending());
   2076 
   2077  // Last m-section should be sendrecv; answerer does not reject!
   2078  auto answer = GetParsedLocalDescription(*mSessionAns);
   2079  msection = &answer->GetMediaSection(answer->GetMediaSectionCount() - 1);
   2080  ASSERT_TRUE(msection);
   2081  ASSERT_TRUE(msection->IsReceiving());
   2082  ASSERT_TRUE(msection->IsSending());
   2083 
   2084  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2085  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   2086 
   2087  ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size());
   2088 
   2089  ASSERT_FALSE(origOffererTransceivers.back().IsStopped());
   2090  ASSERT_FALSE(newOffererTransceivers.back().IsStopped());
   2091  ASSERT_FALSE(origAnswererTransceivers.back().IsStopped());
   2092  ASSERT_TRUE(newAnswererTransceivers.back().IsStopping());
   2093  ASSERT_FALSE(newAnswererTransceivers.back().IsStopped());
   2094  RemoveLastN(origAnswererTransceivers, 1);  // Ignore this one
   2095  RemoveLastN(newAnswererTransceivers, 1);   // Ignore this one
   2096 
   2097  ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   2098  ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers));
   2099 }
   2100 
   2101 TEST_P(JsepSessionTest, RenegotiationBothStopSameTransceiver) {
   2102  AddTracks(*mSessionOff);
   2103  AddTracks(*mSessionAns);
   2104  if (types.back() == SdpMediaSection::kApplication) {
   2105    return;
   2106  }
   2107 
   2108  OfferAnswer();
   2109 
   2110  std::vector<JsepTransceiver> origOffererTransceivers =
   2111      GetTransceivers(*mSessionOff);
   2112  std::vector<JsepTransceiver> origAnswererTransceivers =
   2113      GetTransceivers(*mSessionAns);
   2114 
   2115  // Avoid bundle transport side effects; don't stop the BUNDLE-tag!
   2116  GetTransceivers(*mSessionOff).back().Stop();
   2117  JsepTrack removedTrackOffer(GetTransceivers(*mSessionOff).back().mSendTrack);
   2118  GetTransceivers(*mSessionAns).back().Stop();
   2119  JsepTrack removedTrackAnswer(GetTransceivers(*mSessionAns).back().mSendTrack);
   2120  ASSERT_TRUE(mSessionOff->CheckNegotiationNeeded());
   2121  ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded());
   2122 
   2123  OfferAnswer(CHECK_SUCCESS);
   2124  ASSERT_FALSE(mSessionOff->CheckNegotiationNeeded());
   2125  ASSERT_FALSE(mSessionAns->CheckNegotiationNeeded());
   2126 
   2127  // Last m-section should be disabled
   2128  auto offer = GetParsedLocalDescription(*mSessionOff);
   2129  const SdpMediaSection* msection =
   2130      &offer->GetMediaSection(offer->GetMediaSectionCount() - 1);
   2131  ASSERT_TRUE(msection);
   2132  ValidateDisabledMSection(msection);
   2133 
   2134  // Last m-section should be disabled
   2135  auto answer = GetParsedLocalDescription(*mSessionAns);
   2136  msection = &answer->GetMediaSection(answer->GetMediaSectionCount() - 1);
   2137  ASSERT_TRUE(msection);
   2138  ValidateDisabledMSection(msection);
   2139 
   2140  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2141  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   2142 
   2143  ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size());
   2144 
   2145  ASSERT_FALSE(origOffererTransceivers.back().IsStopped());
   2146  ASSERT_TRUE(newOffererTransceivers.back().IsStopped());
   2147  ASSERT_FALSE(origAnswererTransceivers.back().IsStopped());
   2148  ASSERT_TRUE(newAnswererTransceivers.back().IsStopped());
   2149  RemoveLastN(origOffererTransceivers, 1);   // Ignore this one
   2150  RemoveLastN(newOffererTransceivers, 1);    // Ignore this one
   2151  RemoveLastN(origAnswererTransceivers, 1);  // Ignore this one
   2152  RemoveLastN(newAnswererTransceivers, 1);   // Ignore this one
   2153 
   2154  ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   2155  ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers));
   2156 }
   2157 
   2158 TEST_P(JsepSessionTest, RenegotiationBothStopTransceiverThenAddTrack) {
   2159  AddTracks(*mSessionOff);
   2160  AddTracks(*mSessionAns);
   2161  if (types.back() == SdpMediaSection::kApplication) {
   2162    return;
   2163  }
   2164 
   2165  SdpMediaSection::MediaType removedType = types.back();
   2166 
   2167  OfferAnswer();
   2168 
   2169  // Avoid bundle transport side effects; don't stop the BUNDLE-tag!
   2170  GetTransceivers(*mSessionOff).back().Stop();
   2171  JsepTrack removedTrackOffer(GetTransceivers(*mSessionOff).back().mSendTrack);
   2172  GetTransceivers(*mSessionOff).back().Stop();
   2173  JsepTrack removedTrackAnswer(GetTransceivers(*mSessionOff).back().mSendTrack);
   2174 
   2175  OfferAnswer(CHECK_SUCCESS);
   2176 
   2177  std::vector<JsepTransceiver> origOffererTransceivers =
   2178      GetTransceivers(*mSessionOff);
   2179  std::vector<JsepTransceiver> origAnswererTransceivers =
   2180      GetTransceivers(*mSessionAns);
   2181 
   2182  std::vector<SdpMediaSection::MediaType> extraTypes;
   2183  extraTypes.push_back(removedType);
   2184  AddTracks(*mSessionAns, extraTypes);
   2185  AddTracks(*mSessionOff, extraTypes);
   2186  types.insert(types.end(), extraTypes.begin(), extraTypes.end());
   2187 
   2188  OfferAnswer(CHECK_SUCCESS);
   2189 
   2190  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2191  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   2192 
   2193  ASSERT_EQ(origOffererTransceivers.size() + 1, newOffererTransceivers.size());
   2194  ASSERT_EQ(origAnswererTransceivers.size() + 1,
   2195            newAnswererTransceivers.size());
   2196 
   2197  // Ensure that the m-section was re-used; no gaps
   2198  ASSERT_EQ(origOffererTransceivers.back().GetLevel(),
   2199            newOffererTransceivers.back().GetLevel());
   2200 
   2201  ASSERT_EQ(origAnswererTransceivers.back().GetLevel(),
   2202            newAnswererTransceivers.back().GetLevel());
   2203 }
   2204 
   2205 TEST_P(JsepSessionTest, RenegotiationBothStopTransceiverDifferentMsection) {
   2206  AddTracks(*mSessionOff);
   2207  AddTracks(*mSessionAns);
   2208 
   2209  if (types.size() < 2) {
   2210    return;
   2211  }
   2212 
   2213  if (GetTransceivers(*mSessionOff)[0].GetMediaType() ==
   2214          SdpMediaSection::kApplication ||
   2215      GetTransceivers(*mSessionOff)[1].GetMediaType() ==
   2216          SdpMediaSection::kApplication) {
   2217    return;
   2218  }
   2219 
   2220  OfferAnswer();
   2221 
   2222  GetTransceivers(*mSessionOff)[0].Stop();
   2223  GetTransceivers(*mSessionOff)[1].Stop();
   2224 
   2225  OfferAnswer(CHECK_SUCCESS);
   2226  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped());
   2227  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsStopped());
   2228 }
   2229 
   2230 TEST_P(JsepSessionTest, RenegotiationOffererChangesStreamId) {
   2231  AddTracks(*mSessionOff);
   2232  AddTracks(*mSessionAns);
   2233 
   2234  if (GetTransceivers(*mSessionOff)[0].GetMediaType() ==
   2235      SdpMediaSection::kApplication) {
   2236    return;
   2237  }
   2238 
   2239  OfferAnswer();
   2240 
   2241  GetTransceivers(*mSessionOff)[0].mSendTrack.UpdateStreamIds(
   2242      std::vector<std::string>(1, "newstream"));
   2243 
   2244  OfferAnswer(CHECK_SUCCESS);
   2245 
   2246  ASSERT_EQ("newstream",
   2247            GetTransceivers(*mSessionAns)[0].mRecvTrack.GetStreamIds()[0]);
   2248 }
   2249 
   2250 TEST_P(JsepSessionTest, RenegotiationAnswererChangesStreamId) {
   2251  AddTracks(*mSessionOff);
   2252  AddTracks(*mSessionAns);
   2253 
   2254  if (GetTransceivers(*mSessionOff)[0].GetMediaType() ==
   2255      SdpMediaSection::kApplication) {
   2256    return;
   2257  }
   2258 
   2259  OfferAnswer();
   2260 
   2261  GetTransceivers(*mSessionAns)[0].mSendTrack.UpdateStreamIds(
   2262      std::vector<std::string>(1, "newstream"));
   2263 
   2264  OfferAnswer(CHECK_SUCCESS);
   2265 
   2266  ASSERT_EQ("newstream",
   2267            GetTransceivers(*mSessionOff)[0].mRecvTrack.GetStreamIds()[0]);
   2268 }
   2269 
   2270 // Tests whether auto-assigned remote msids (ie; what happens when the other
   2271 // side doesn't use msid attributes) are stable across renegotiation.
   2272 TEST_P(JsepSessionTest, RenegotiationAutoAssignedMsidIsStable) {
   2273  AddTracks(*mSessionOff);
   2274  std::string offer = CreateOffer();
   2275  SetLocalOffer(offer);
   2276  SetRemoteOffer(offer);
   2277  AddTracks(*mSessionAns);
   2278  std::string answer = CreateAnswer();
   2279  SetLocalAnswer(answer);
   2280 
   2281  DisableMsid(&answer);
   2282 
   2283  SetRemoteAnswer(answer, CHECK_SUCCESS);
   2284 
   2285  std::vector<JsepTransceiver> origOffererTransceivers =
   2286      GetTransceivers(*mSessionOff);
   2287  std::vector<JsepTransceiver> origAnswererTransceivers =
   2288      GetTransceivers(*mSessionAns);
   2289 
   2290  ASSERT_EQ(origOffererTransceivers.size(), origAnswererTransceivers.size());
   2291  for (size_t i = 0; i < origOffererTransceivers.size(); ++i) {
   2292    ASSERT_FALSE(IsNull(origOffererTransceivers[i].mRecvTrack));
   2293    ASSERT_FALSE(IsNull(origAnswererTransceivers[i].mSendTrack));
   2294    // These should not match since we've monkeyed with the msid
   2295    ASSERT_NE(origOffererTransceivers[i].mRecvTrack.GetStreamIds(),
   2296              origAnswererTransceivers[i].mSendTrack.GetStreamIds());
   2297  }
   2298 
   2299  offer = CreateOffer();
   2300  SetLocalOffer(offer);
   2301  SetRemoteOffer(offer);
   2302  answer = CreateAnswer();
   2303  SetLocalAnswer(answer);
   2304 
   2305  DisableMsid(&answer);
   2306 
   2307  SetRemoteAnswer(answer, CHECK_SUCCESS);
   2308 
   2309  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2310 
   2311  ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers));
   2312 }
   2313 
   2314 TEST_P(JsepSessionTest, RenegotiationOffererDisablesTelephoneEvent) {
   2315  AddTracks(*mSessionOff);
   2316  AddTracks(*mSessionAns);
   2317  OfferAnswer();
   2318 
   2319  // check all the audio tracks to make sure they have 2 codecs (109 and 101),
   2320  // and dtmf is enabled on all audio tracks
   2321  std::vector<JsepTrack> tracks;
   2322  for (const auto& transceiver : GetTransceivers(*mSessionOff)) {
   2323    tracks.push_back(transceiver.mSendTrack);
   2324    tracks.push_back(transceiver.mRecvTrack);
   2325  }
   2326 
   2327  for (const JsepTrack& track : tracks) {
   2328    if (track.GetMediaType() != SdpMediaSection::kAudio) {
   2329      continue;
   2330    }
   2331    const JsepTrackNegotiatedDetails* details = track.GetNegotiatedDetails();
   2332    ASSERT_EQ(1U, details->GetEncodingCount());
   2333    const JsepTrackEncoding& encoding = details->GetEncoding(0);
   2334    ASSERT_EQ(5U, encoding.GetCodecs().size());
   2335    ASSERT_TRUE(encoding.HasFormat("109"));
   2336    ASSERT_TRUE(encoding.HasFormat("101"));
   2337    for (const auto& codec : encoding.GetCodecs()) {
   2338      ASSERT_TRUE(codec);
   2339      // we can cast here because we've already checked for audio track
   2340      const JsepAudioCodecDescription* audioCodec =
   2341          static_cast<const JsepAudioCodecDescription*>(codec.get());
   2342      ASSERT_TRUE(audioCodec->mDtmfEnabled);
   2343    }
   2344  }
   2345 
   2346  std::string offer = CreateOffer();
   2347  ReplaceInSdp(&offer, "8 101", "8");
   2348  ReplaceInSdp(&offer, "a=fmtp:101 0-15\r\n", "");
   2349  ReplaceInSdp(&offer, "a=rtpmap:101 telephone-event/8000/1\r\n", "");
   2350  std::cerr << "modified OFFER: " << offer << std::endl;
   2351 
   2352  SetLocalOffer(offer);
   2353  SetRemoteOffer(offer);
   2354  std::string answer = CreateAnswer();
   2355  SetLocalAnswer(answer);
   2356  SetRemoteAnswer(answer);
   2357 
   2358  // check all the audio tracks to make sure they have 1 codec (109),
   2359  // and dtmf is disabled on all audio tracks
   2360  tracks.clear();
   2361  for (const auto& transceiver : GetTransceivers(*mSessionOff)) {
   2362    tracks.push_back(transceiver.mSendTrack);
   2363    tracks.push_back(transceiver.mRecvTrack);
   2364  }
   2365 
   2366  for (const JsepTrack& track : tracks) {
   2367    if (track.GetMediaType() != SdpMediaSection::kAudio) {
   2368      continue;
   2369    }
   2370    const JsepTrackNegotiatedDetails* details = track.GetNegotiatedDetails();
   2371    ASSERT_EQ(1U, details->GetEncodingCount());
   2372    const JsepTrackEncoding& encoding = details->GetEncoding(0);
   2373    auto expectedSize = (track.GetDirection() != sdp::kSend) ? 5U : 4U;
   2374    ASSERT_EQ(expectedSize, encoding.GetCodecs().size());
   2375    ASSERT_TRUE(encoding.HasFormat("109"));
   2376    // we can cast here because we've already checked for audio track
   2377    const JsepAudioCodecDescription* audioCodec =
   2378        static_cast<const JsepAudioCodecDescription*>(
   2379            encoding.GetCodecs()[0].get());
   2380    ASSERT_TRUE(audioCodec);
   2381    ASSERT_EQ(track.GetDirection() != sdp::kSend, audioCodec->mDtmfEnabled);
   2382  }
   2383 }
   2384 
   2385 // Tests behavior when the answerer does not use msid in the initial exchange,
   2386 // but does on renegotiation.
   2387 TEST_P(JsepSessionTest, RenegotiationAnswererEnablesMsid) {
   2388  AddTracks(*mSessionOff);
   2389  std::string offer = CreateOffer();
   2390  SetLocalOffer(offer);
   2391  SetRemoteOffer(offer);
   2392  AddTracks(*mSessionAns);
   2393  std::string answer = CreateAnswer();
   2394  SetLocalAnswer(answer);
   2395 
   2396  DisableMsid(&answer);
   2397 
   2398  SetRemoteAnswer(answer, CHECK_SUCCESS);
   2399 
   2400  std::vector<JsepTransceiver> origOffererTransceivers =
   2401      GetTransceivers(*mSessionOff);
   2402  std::vector<JsepTransceiver> origAnswererTransceivers =
   2403      GetTransceivers(*mSessionAns);
   2404 
   2405  offer = CreateOffer();
   2406  SetLocalOffer(offer);
   2407  SetRemoteOffer(offer);
   2408  answer = CreateAnswer();
   2409  SetLocalAnswer(answer);
   2410  SetRemoteAnswer(answer, CHECK_SUCCESS);
   2411 
   2412  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2413 
   2414  ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size());
   2415  for (size_t i = 0; i < origOffererTransceivers.size(); ++i) {
   2416    ASSERT_EQ(origOffererTransceivers[i].mRecvTrack.GetMediaType(),
   2417              newOffererTransceivers[i].mRecvTrack.GetMediaType());
   2418 
   2419    ASSERT_TRUE(Equals(origOffererTransceivers[i].mSendTrack,
   2420                       newOffererTransceivers[i].mSendTrack));
   2421    ASSERT_TRUE(Equals(origOffererTransceivers[i].mTransport,
   2422                       newOffererTransceivers[i].mTransport));
   2423 
   2424    if (origOffererTransceivers[i].mRecvTrack.GetMediaType() ==
   2425        SdpMediaSection::kApplication) {
   2426      ASSERT_TRUE(Equals(origOffererTransceivers[i].mRecvTrack,
   2427                         newOffererTransceivers[i].mRecvTrack));
   2428    } else {
   2429      // This should be the only difference
   2430      ASSERT_FALSE(Equals(origOffererTransceivers[i].mRecvTrack,
   2431                          newOffererTransceivers[i].mRecvTrack));
   2432    }
   2433  }
   2434 }
   2435 
   2436 TEST_P(JsepSessionTest, RenegotiationAnswererDisablesMsid) {
   2437  AddTracks(*mSessionOff);
   2438  std::string offer = CreateOffer();
   2439  SetLocalOffer(offer);
   2440  SetRemoteOffer(offer);
   2441  AddTracks(*mSessionAns);
   2442  std::string answer = CreateAnswer();
   2443  SetLocalAnswer(answer);
   2444  SetRemoteAnswer(answer, CHECK_SUCCESS);
   2445 
   2446  std::vector<JsepTransceiver> origOffererTransceivers =
   2447      GetTransceivers(*mSessionOff);
   2448  std::vector<JsepTransceiver> origAnswererTransceivers =
   2449      GetTransceivers(*mSessionAns);
   2450 
   2451  offer = CreateOffer();
   2452  SetLocalOffer(offer);
   2453  SetRemoteOffer(offer);
   2454  answer = CreateAnswer();
   2455  SetLocalAnswer(answer);
   2456 
   2457  DisableMsid(&answer);
   2458 
   2459  SetRemoteAnswer(answer, CHECK_SUCCESS);
   2460 
   2461  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2462 
   2463  ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size());
   2464  for (size_t i = 0; i < origOffererTransceivers.size(); ++i) {
   2465    ASSERT_EQ(origOffererTransceivers[i].mRecvTrack.GetMediaType(),
   2466              newOffererTransceivers[i].mRecvTrack.GetMediaType());
   2467 
   2468    ASSERT_TRUE(Equals(origOffererTransceivers[i].mSendTrack,
   2469                       newOffererTransceivers[i].mSendTrack));
   2470    ASSERT_TRUE(Equals(origOffererTransceivers[i].mTransport,
   2471                       newOffererTransceivers[i].mTransport));
   2472 
   2473    if (origOffererTransceivers[i].mRecvTrack.GetMediaType() ==
   2474        SdpMediaSection::kApplication) {
   2475      ASSERT_TRUE(Equals(origOffererTransceivers[i].mRecvTrack,
   2476                         newOffererTransceivers[i].mRecvTrack));
   2477    } else {
   2478      // This should be the only difference
   2479      ASSERT_FALSE(Equals(origOffererTransceivers[i].mRecvTrack,
   2480                          newOffererTransceivers[i].mRecvTrack));
   2481    }
   2482  }
   2483 }
   2484 
   2485 // Tests behavior when offerer does not use bundle on the initial offer/answer,
   2486 // but does on renegotiation.
   2487 TEST_P(JsepSessionTest, RenegotiationOffererEnablesBundle) {
   2488  AddTracks(*mSessionOff);
   2489  AddTracks(*mSessionAns);
   2490 
   2491  if (types.size() < 2) {
   2492    // No bundle will happen here.
   2493    return;
   2494  }
   2495 
   2496  std::string offer = CreateOffer();
   2497 
   2498  DisableBundle(&offer);
   2499 
   2500  SetLocalOffer(offer);
   2501  SetRemoteOffer(offer);
   2502  std::string answer = CreateAnswer();
   2503  SetLocalAnswer(answer);
   2504  SetRemoteAnswer(answer);
   2505 
   2506  std::vector<JsepTransceiver> origOffererTransceivers =
   2507      GetTransceivers(*mSessionOff);
   2508  std::vector<JsepTransceiver> origAnswererTransceivers =
   2509      GetTransceivers(*mSessionAns);
   2510 
   2511  OfferAnswer();
   2512 
   2513  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2514  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   2515 
   2516  ASSERT_EQ(newOffererTransceivers.size(), newAnswererTransceivers.size());
   2517  ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size());
   2518  ASSERT_EQ(origAnswererTransceivers.size(), newAnswererTransceivers.size());
   2519 
   2520  for (size_t i = 0; i < newOffererTransceivers.size(); ++i) {
   2521    // No bundle initially
   2522    ASSERT_FALSE(origOffererTransceivers[i].HasBundleLevel());
   2523    ASSERT_FALSE(origAnswererTransceivers[i].HasBundleLevel());
   2524    if (i != 0) {
   2525      ASSERT_FALSE(Equals(origOffererTransceivers[0].mTransport,
   2526                          origOffererTransceivers[i].mTransport));
   2527      ASSERT_FALSE(Equals(origAnswererTransceivers[0].mTransport,
   2528                          origAnswererTransceivers[i].mTransport));
   2529    }
   2530 
   2531    // Verify that bundle worked after renegotiation
   2532    ASSERT_TRUE(newOffererTransceivers[i].HasBundleLevel());
   2533    ASSERT_TRUE(newAnswererTransceivers[i].HasBundleLevel());
   2534    ASSERT_TRUE(Equals(newOffererTransceivers[0].mTransport,
   2535                       newOffererTransceivers[i].mTransport));
   2536    ASSERT_TRUE(Equals(newAnswererTransceivers[0].mTransport,
   2537                       newAnswererTransceivers[i].mTransport));
   2538  }
   2539 }
   2540 
   2541 TEST_P(JsepSessionTest, RenegotiationOffererDisablesBundleTransport) {
   2542  AddTracks(*mSessionOff);
   2543  AddTracks(*mSessionAns);
   2544 
   2545  if (types.size() < 2) {
   2546    return;
   2547  }
   2548 
   2549  OfferAnswer();
   2550 
   2551  auto stopped = GetTransceiverByLevel(*mSessionOff, 0);
   2552  stopped->Stop();
   2553  mSessionOff->SetTransceiver(*stopped);
   2554 
   2555  std::vector<JsepTransceiver> origOffererTransceivers =
   2556      GetTransceivers(*mSessionOff);
   2557  std::vector<JsepTransceiver> origAnswererTransceivers =
   2558      GetTransceivers(*mSessionAns);
   2559 
   2560  OfferAnswer(CHECK_SUCCESS);
   2561 
   2562  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2563  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   2564 
   2565  ASSERT_EQ(newOffererTransceivers.size(), newAnswererTransceivers.size());
   2566  ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size());
   2567  ASSERT_EQ(origAnswererTransceivers.size(), newAnswererTransceivers.size());
   2568 
   2569  Maybe<JsepTransceiver> ot0 = GetTransceiverByLevel(newOffererTransceivers, 0);
   2570  Maybe<JsepTransceiver> at0 =
   2571      GetTransceiverByLevel(newAnswererTransceivers, 0);
   2572  ASSERT_FALSE(ot0->HasBundleLevel());
   2573  ASSERT_FALSE(at0->HasBundleLevel());
   2574 
   2575  ASSERT_FALSE(
   2576      Equals(ot0->mTransport,
   2577             GetTransceiverByLevel(origOffererTransceivers, 0)->mTransport));
   2578  ASSERT_FALSE(
   2579      Equals(at0->mTransport,
   2580             GetTransceiverByLevel(origAnswererTransceivers, 0)->mTransport));
   2581 
   2582  ASSERT_EQ(0U, ot0->mTransport.mComponents);
   2583  ASSERT_EQ(0U, at0->mTransport.mComponents);
   2584 
   2585  for (size_t i = 1; i < types.size() - 1; ++i) {
   2586    Maybe<JsepTransceiver> ot =
   2587        GetTransceiverByLevel(newOffererTransceivers, i);
   2588    Maybe<JsepTransceiver> at =
   2589        GetTransceiverByLevel(newAnswererTransceivers, i);
   2590    ASSERT_TRUE(ot->HasBundleLevel());
   2591    ASSERT_TRUE(at->HasBundleLevel());
   2592    ASSERT_EQ(1U, ot->BundleLevel());
   2593    ASSERT_EQ(1U, at->BundleLevel());
   2594    ASSERT_FALSE(Equals(ot0->mTransport, ot->mTransport));
   2595    ASSERT_FALSE(Equals(at0->mTransport, at->mTransport));
   2596  }
   2597 }
   2598 
   2599 TEST_P(JsepSessionTest, RenegotiationAnswererDoesNotRejectStoppedTransceiver) {
   2600  AddTracks(*mSessionOff);
   2601  AddTracks(*mSessionAns);
   2602 
   2603  if (types.size() < 2) {
   2604    return;
   2605  }
   2606 
   2607  OfferAnswer();
   2608 
   2609  std::vector<JsepTransceiver> origOffererTransceivers =
   2610      GetTransceivers(*mSessionOff);
   2611  std::vector<JsepTransceiver> origAnswererTransceivers =
   2612      GetTransceivers(*mSessionAns);
   2613 
   2614  auto stopped = GetTransceiverByLevel(*mSessionAns, 0);
   2615  stopped->Stop();
   2616  mSessionAns->SetTransceiver(*stopped);
   2617  ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded());
   2618 
   2619  OfferAnswer(CHECK_SUCCESS);
   2620  ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded());
   2621 
   2622  auto newOffererTransceivers = GetTransceivers(*mSessionOff);
   2623  auto newAnswererTransceivers = GetTransceivers(*mSessionAns);
   2624 
   2625  DumpTransceivers(*mSessionAns);
   2626 
   2627  ASSERT_EQ(newOffererTransceivers.size(), newAnswererTransceivers.size());
   2628  ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size());
   2629  ASSERT_EQ(origAnswererTransceivers.size(), newAnswererTransceivers.size());
   2630 
   2631  Maybe<JsepTransceiver> ot0 = GetTransceiverByLevel(newOffererTransceivers, 0);
   2632  Maybe<JsepTransceiver> at0 =
   2633      GetTransceiverByLevel(newAnswererTransceivers, 0);
   2634  ASSERT_TRUE(ot0->HasBundleLevel());
   2635  ASSERT_TRUE(at0->HasBundleLevel());
   2636 
   2637  ASSERT_TRUE(
   2638      Equals(ot0->mTransport,
   2639             GetTransceiverByLevel(origOffererTransceivers, 0)->mTransport));
   2640  ASSERT_TRUE(
   2641      Equals(at0->mTransport,
   2642             GetTransceiverByLevel(origAnswererTransceivers, 0)->mTransport));
   2643 
   2644  ASSERT_EQ(1U, ot0->mTransport.mComponents);
   2645  ASSERT_EQ(1U, at0->mTransport.mComponents);
   2646 
   2647  for (size_t i = 1; i < newOffererTransceivers.size(); ++i) {
   2648    auto ot = GetTransceiverByLevel(newOffererTransceivers, i);
   2649    auto at = GetTransceiverByLevel(newAnswererTransceivers, i);
   2650    auto otWithTransport = GetTransceiverByLevel(newOffererTransceivers, 0);
   2651    auto atWithTransport = GetTransceiverByLevel(newAnswererTransceivers, 0);
   2652    ASSERT_TRUE(ot->HasBundleLevel());
   2653    ASSERT_TRUE(at->HasBundleLevel());
   2654    ASSERT_EQ(0U, ot->BundleLevel());
   2655    ASSERT_EQ(0U, at->BundleLevel());
   2656    ASSERT_TRUE(Equals(otWithTransport->mTransport, ot->mTransport));
   2657    ASSERT_TRUE(Equals(atWithTransport->mTransport, at->mTransport));
   2658  }
   2659 }
   2660 
   2661 TEST_P(JsepSessionTest, ParseRejectsBadMediaFormat) {
   2662  AddTracks(*mSessionOff);
   2663  if (types.front() == SdpMediaSection::MediaType::kApplication) {
   2664    return;
   2665  }
   2666  std::string offer = CreateOffer();
   2667  UniquePtr<Sdp> munge(Parse(offer));
   2668  SdpMediaSection& mediaSection = munge->GetMediaSection(0);
   2669  mediaSection.AddCodec("75", "DummyFormatVal", 8000, 1);
   2670  std::string sdpString = munge->ToString();
   2671  JsepSession::Result result =
   2672      mSessionOff->SetLocalDescription(kJsepSdpOffer, sdpString);
   2673  ASSERT_EQ(dom::PCError::OperationError, *result.mError);
   2674 }
   2675 
   2676 TEST_P(JsepSessionTest, FullCallWithCandidates) {
   2677  AddTracks(*mSessionOff);
   2678  std::string offer = CreateOffer();
   2679  SetLocalOffer(offer);
   2680  mOffCandidates->Gather(*mSessionOff);
   2681 
   2682  UniquePtr<Sdp> localOffer(
   2683      Parse(mSessionOff->GetLocalDescription(kJsepDescriptionPending)));
   2684  for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) {
   2685    std::string id = GetTransportId(*mSessionOff, i);
   2686    bool bundleOnly =
   2687        localOffer->GetMediaSection(i).GetAttributeList().HasAttribute(
   2688            SdpAttribute::kBundleOnlyAttribute);
   2689    mOffCandidates->CheckRtpCandidates(
   2690        !bundleOnly, localOffer->GetMediaSection(i), id,
   2691        "Local offer after gathering should have RTP candidates "
   2692        "(unless bundle-only)");
   2693    mOffCandidates->CheckDefaultRtpCandidate(
   2694        !bundleOnly, localOffer->GetMediaSection(i), id,
   2695        "Local offer after gathering should have a default RTP candidate "
   2696        "(unless bundle-only)");
   2697    mOffCandidates->CheckRtcpCandidates(
   2698        !bundleOnly && types[i] != SdpMediaSection::kApplication,
   2699        localOffer->GetMediaSection(i), id,
   2700        "Local offer after gathering should have RTCP candidates "
   2701        "(unless m=application or bundle-only)");
   2702    mOffCandidates->CheckDefaultRtcpCandidate(
   2703        !bundleOnly && types[i] != SdpMediaSection::kApplication,
   2704        localOffer->GetMediaSection(i), id,
   2705        "Local offer after gathering should have a default RTCP candidate "
   2706        "(unless m=application or bundle-only)");
   2707    CheckEndOfCandidates(
   2708        !bundleOnly, localOffer->GetMediaSection(i),
   2709        "Local offer after gathering should have an end-of-candidates "
   2710        "(unless bundle-only)");
   2711  }
   2712 
   2713  SetRemoteOffer(offer);
   2714  mOffCandidates->Trickle(*mSessionAns);
   2715 
   2716  UniquePtr<Sdp> remoteOffer(
   2717      Parse(mSessionAns->GetRemoteDescription(kJsepDescriptionPending)));
   2718  for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) {
   2719    std::string id = GetTransportId(*mSessionOff, i);
   2720    bool bundleOnly =
   2721        remoteOffer->GetMediaSection(i).GetAttributeList().HasAttribute(
   2722            SdpAttribute::kBundleOnlyAttribute);
   2723    mOffCandidates->CheckRtpCandidates(
   2724        !bundleOnly, remoteOffer->GetMediaSection(i), id,
   2725        "Remote offer after trickle should have RTP candidates "
   2726        "(unless bundle-only)");
   2727    mOffCandidates->CheckDefaultRtpCandidate(
   2728        false, remoteOffer->GetMediaSection(i), id,
   2729        "Remote offer after trickle should not have a default RTP candidate.");
   2730    mOffCandidates->CheckRtcpCandidates(
   2731        !bundleOnly && types[i] != SdpMediaSection::kApplication,
   2732        remoteOffer->GetMediaSection(i), id,
   2733        "Remote offer after trickle should have RTCP candidates "
   2734        "(unless m=application or bundle-only)");
   2735    mOffCandidates->CheckDefaultRtcpCandidate(
   2736        false, remoteOffer->GetMediaSection(i), id,
   2737        "Remote offer after trickle should not have a default RTCP candidate.");
   2738    CheckEndOfCandidates(
   2739        true, remoteOffer->GetMediaSection(i),
   2740        "Remote offer after trickle should have an end-of-candidates.");
   2741  }
   2742 
   2743  AddTracks(*mSessionAns);
   2744  std::string answer = CreateAnswer();
   2745  SetLocalAnswer(answer);
   2746  // This will gather candidates that mSessionAns knows it doesn't need.
   2747  // They should not be present in the SDP.
   2748  mAnsCandidates->Gather(*mSessionAns);
   2749 
   2750  UniquePtr<Sdp> localAnswer(
   2751      Parse(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)));
   2752  std::string id0 = GetTransportId(*mSessionAns, 0);
   2753  for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
   2754    std::string id = GetTransportId(*mSessionAns, i);
   2755    mAnsCandidates->CheckRtpCandidates(
   2756        i == 0, localAnswer->GetMediaSection(i), id,
   2757        "Local answer after gathering should have RTP candidates on level 0.");
   2758    mAnsCandidates->CheckDefaultRtpCandidate(
   2759        true, localAnswer->GetMediaSection(i), id0,
   2760        "Local answer after gathering should have a default RTP candidate "
   2761        "on all levels that matches transport level 0.");
   2762    mAnsCandidates->CheckRtcpCandidates(
   2763        false, localAnswer->GetMediaSection(i), id,
   2764        "Local answer after gathering should not have RTCP candidates "
   2765        "(because we're answering with rtcp-mux)");
   2766    mAnsCandidates->CheckDefaultRtcpCandidate(
   2767        false, localAnswer->GetMediaSection(i), id,
   2768        "Local answer after gathering should not have a default RTCP candidate "
   2769        "(because we're answering with rtcp-mux)");
   2770    CheckEndOfCandidates(
   2771        i == 0, localAnswer->GetMediaSection(i),
   2772        "Local answer after gathering should have an end-of-candidates only for"
   2773        " level 0.");
   2774  }
   2775 
   2776  SetRemoteAnswer(answer);
   2777  mAnsCandidates->Trickle(*mSessionOff);
   2778 
   2779  UniquePtr<Sdp> remoteAnswer(
   2780      Parse(mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent)));
   2781  for (size_t i = 0; i < remoteAnswer->GetMediaSectionCount(); ++i) {
   2782    std::string id = GetTransportId(*mSessionAns, i);
   2783    mAnsCandidates->CheckRtpCandidates(
   2784        i == 0, remoteAnswer->GetMediaSection(i), id,
   2785        "Remote answer after trickle should have RTP candidates on level 0.");
   2786    mAnsCandidates->CheckDefaultRtpCandidate(
   2787        false, remoteAnswer->GetMediaSection(i), id,
   2788        "Remote answer after trickle should not have a default RTP candidate.");
   2789    mAnsCandidates->CheckRtcpCandidates(
   2790        false, remoteAnswer->GetMediaSection(i), id,
   2791        "Remote answer after trickle should not have RTCP candidates "
   2792        "(because we're answering with rtcp-mux)");
   2793    mAnsCandidates->CheckDefaultRtcpCandidate(
   2794        false, remoteAnswer->GetMediaSection(i), id,
   2795        "Remote answer after trickle should not have a default RTCP "
   2796        "candidate.");
   2797    CheckEndOfCandidates(
   2798        true, remoteAnswer->GetMediaSection(i),
   2799        "Remote answer after trickle should have an end-of-candidates.");
   2800  }
   2801 }
   2802 
   2803 TEST_P(JsepSessionTest, RenegotiationWithCandidates) {
   2804  AddTracks(*mSessionOff);
   2805  std::string offer = CreateOffer();
   2806  SetLocalOffer(offer);
   2807  mOffCandidates->Gather(*mSessionOff);
   2808  SetRemoteOffer(offer);
   2809  mOffCandidates->Trickle(*mSessionAns);
   2810  AddTracks(*mSessionAns);
   2811  std::string answer = CreateAnswer();
   2812  SetLocalAnswer(answer);
   2813  mAnsCandidates->Gather(*mSessionAns);
   2814  SetRemoteAnswer(answer);
   2815  mAnsCandidates->Trickle(*mSessionOff);
   2816 
   2817  offer = CreateOffer();
   2818  SetLocalOffer(offer);
   2819 
   2820  UniquePtr<Sdp> parsedOffer(Parse(offer));
   2821  std::string id0 = GetTransportId(*mSessionOff, 0);
   2822  for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) {
   2823    std::string id = GetTransportId(*mSessionOff, i);
   2824    mOffCandidates->CheckRtpCandidates(
   2825        i == 0, parsedOffer->GetMediaSection(i), id,
   2826        "Local reoffer before gathering should have RTP candidates on level 0"
   2827        " only.");
   2828    mOffCandidates->CheckDefaultRtpCandidate(
   2829        i == 0, parsedOffer->GetMediaSection(i), id0,
   2830        "Local reoffer before gathering should have a default RTP candidate "
   2831        "on level 0 only.");
   2832    mOffCandidates->CheckRtcpCandidates(
   2833        false, parsedOffer->GetMediaSection(i), id,
   2834        "Local reoffer before gathering should not have RTCP candidates.");
   2835    mOffCandidates->CheckDefaultRtcpCandidate(
   2836        false, parsedOffer->GetMediaSection(i), id,
   2837        "Local reoffer before gathering should not have a default RTCP "
   2838        "candidate.");
   2839    CheckEndOfCandidates(
   2840        i == 0, parsedOffer->GetMediaSection(i),
   2841        "Local reoffer before gathering should have an end-of-candidates "
   2842        "(level 0 only)");
   2843  }
   2844 
   2845  // mSessionAns should generate a reoffer that is similar
   2846  std::string otherOffer;
   2847  JsepOfferOptions defaultOptions;
   2848  JsepSession::Result result =
   2849      mSessionAns->CreateOffer(defaultOptions, &otherOffer);
   2850  ASSERT_FALSE(result.mError.isSome());
   2851  parsedOffer = Parse(otherOffer);
   2852  id0 = GetTransportId(*mSessionAns, 0);
   2853  for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) {
   2854    std::string id = GetTransportId(*mSessionAns, i);
   2855    mAnsCandidates->CheckRtpCandidates(
   2856        i == 0, parsedOffer->GetMediaSection(i), id,
   2857        "Local reoffer before gathering should have RTP candidates on level 0"
   2858        " only. (previous answerer)");
   2859    mAnsCandidates->CheckDefaultRtpCandidate(
   2860        i == 0, parsedOffer->GetMediaSection(i), id0,
   2861        "Local reoffer before gathering should have a default RTP candidate "
   2862        "on level 0 only. (previous answerer)");
   2863    mAnsCandidates->CheckRtcpCandidates(
   2864        false, parsedOffer->GetMediaSection(i), id,
   2865        "Local reoffer before gathering should not have RTCP candidates."
   2866        " (previous answerer)");
   2867    mAnsCandidates->CheckDefaultRtcpCandidate(
   2868        false, parsedOffer->GetMediaSection(i), id,
   2869        "Local reoffer before gathering should not have a default RTCP "
   2870        "candidate. (previous answerer)");
   2871    CheckEndOfCandidates(
   2872        i == 0, parsedOffer->GetMediaSection(i),
   2873        "Local reoffer before gathering should have an end-of-candidates "
   2874        "(level 0 only)");
   2875  }
   2876 
   2877  // Ok, let's continue with the renegotiation
   2878  SetRemoteOffer(offer);
   2879 
   2880  // PeerConnection will not re-gather for RTP, but it will for RTCP in case
   2881  // the answerer decides to turn off rtcp-mux.
   2882  if (types[0] != SdpMediaSection::kApplication) {
   2883    mOffCandidates->Gather(*mSessionOff, GetTransportId(*mSessionOff, 0), RTCP);
   2884  }
   2885 
   2886  // Since the remaining levels were bundled, PeerConnection will re-gather for
   2887  // both RTP and RTCP, in case the answerer rejects bundle, provided
   2888  // bundle-only isn't being used.
   2889  UniquePtr<Sdp> localOffer(
   2890      Parse(mSessionOff->GetLocalDescription(kJsepDescriptionPending)));
   2891  for (size_t level = 1; level < types.size(); ++level) {
   2892    std::string id = GetTransportId(*mSessionOff, level);
   2893    bool bundleOnly =
   2894        localOffer->GetMediaSection(level).GetAttributeList().HasAttribute(
   2895            SdpAttribute::kBundleOnlyAttribute);
   2896    if (!id.empty() && !bundleOnly) {
   2897      mOffCandidates->Gather(*mSessionOff, id, RTP);
   2898      if (types[level] != SdpMediaSection::kApplication) {
   2899        mOffCandidates->Gather(*mSessionOff, id, RTCP);
   2900      }
   2901    }
   2902  }
   2903  mOffCandidates->FinishGathering(*mSessionOff);
   2904  localOffer = Parse(mSessionOff->GetLocalDescription(kJsepDescriptionPending));
   2905 
   2906  mOffCandidates->Trickle(*mSessionAns);
   2907 
   2908  for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) {
   2909    std::string id = GetTransportId(*mSessionOff, i);
   2910    bool bundleOnly =
   2911        localOffer->GetMediaSection(i).GetAttributeList().HasAttribute(
   2912            SdpAttribute::kBundleOnlyAttribute);
   2913    mOffCandidates->CheckRtpCandidates(
   2914        !bundleOnly, localOffer->GetMediaSection(i), id,
   2915        "Local reoffer after gathering should have RTP candidates "
   2916        "(unless bundle-only)");
   2917    mOffCandidates->CheckDefaultRtpCandidate(
   2918        !bundleOnly, localOffer->GetMediaSection(i), id,
   2919        "Local reoffer after gathering should have a default RTP candidate "
   2920        "(unless bundle-only)");
   2921    mOffCandidates->CheckRtcpCandidates(
   2922        !bundleOnly && (types[i] != SdpMediaSection::kApplication),
   2923        localOffer->GetMediaSection(i), id,
   2924        "Local reoffer after gathering should have RTCP candidates "
   2925        "(unless m=application or bundle-only)");
   2926    mOffCandidates->CheckDefaultRtcpCandidate(
   2927        !bundleOnly && (types[i] != SdpMediaSection::kApplication),
   2928        localOffer->GetMediaSection(i), id,
   2929        "Local reoffer after gathering should have a default RTCP candidate "
   2930        "(unless m=application or bundle-only)");
   2931    CheckEndOfCandidates(
   2932        !bundleOnly, localOffer->GetMediaSection(i),
   2933        "Local reoffer after gathering should have an end-of-candidates "
   2934        "(unless bundle-only)");
   2935  }
   2936 
   2937  UniquePtr<Sdp> remoteOffer(
   2938      Parse(mSessionAns->GetRemoteDescription(kJsepDescriptionPending)));
   2939  for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) {
   2940    bool bundleOnly =
   2941        remoteOffer->GetMediaSection(i).GetAttributeList().HasAttribute(
   2942            SdpAttribute::kBundleOnlyAttribute);
   2943    std::string id = GetTransportId(*mSessionOff, i);
   2944    mOffCandidates->CheckRtpCandidates(
   2945        !bundleOnly, remoteOffer->GetMediaSection(i), id,
   2946        "Remote reoffer after trickle should have RTP candidates "
   2947        "(unless bundle-only)");
   2948    mOffCandidates->CheckDefaultRtpCandidate(
   2949        i == 0, remoteOffer->GetMediaSection(i), id,
   2950        "Remote reoffer should have a default RTP candidate on level 0 "
   2951        "(because it was gathered last offer/answer).");
   2952    mOffCandidates->CheckRtcpCandidates(
   2953        !bundleOnly && types[i] != SdpMediaSection::kApplication,
   2954        remoteOffer->GetMediaSection(i), id,
   2955        "Remote reoffer after trickle should have RTCP candidates "
   2956        "(unless m=application or bundle-only)");
   2957    mOffCandidates->CheckDefaultRtcpCandidate(
   2958        false, remoteOffer->GetMediaSection(i), id,
   2959        "Remote reoffer should not have a default RTCP candidate.");
   2960    CheckEndOfCandidates(true, remoteOffer->GetMediaSection(i),
   2961                         "Remote reoffer should have an end-of-candidates.");
   2962  }
   2963 
   2964  answer = CreateAnswer();
   2965  SetLocalAnswer(answer);
   2966  SetRemoteAnswer(answer);
   2967  // No candidates should be gathered at the answerer, but default candidates
   2968  // should be set.
   2969  mAnsCandidates->FinishGathering(*mSessionAns);
   2970 
   2971  UniquePtr<Sdp> localAnswer(
   2972      Parse(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)));
   2973  id0 = GetTransportId(*mSessionAns, 0);
   2974  for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
   2975    std::string id = GetTransportId(*mSessionAns, 0);
   2976    mAnsCandidates->CheckRtpCandidates(
   2977        i == 0, localAnswer->GetMediaSection(i), id,
   2978        "Local reanswer after gathering should have RTP candidates on level "
   2979        "0.");
   2980    mAnsCandidates->CheckDefaultRtpCandidate(
   2981        true, localAnswer->GetMediaSection(i), id0,
   2982        "Local reanswer after gathering should have a default RTP candidate "
   2983        "on all levels that matches transport level 0.");
   2984    mAnsCandidates->CheckRtcpCandidates(
   2985        false, localAnswer->GetMediaSection(i), id,
   2986        "Local reanswer after gathering should not have RTCP candidates "
   2987        "(because we're reanswering with rtcp-mux)");
   2988    mAnsCandidates->CheckDefaultRtcpCandidate(
   2989        false, localAnswer->GetMediaSection(i), id,
   2990        "Local reanswer after gathering should not have a default RTCP "
   2991        "candidate (because we're reanswering with rtcp-mux)");
   2992    CheckEndOfCandidates(
   2993        i == 0, localAnswer->GetMediaSection(i),
   2994        "Local reanswer after gathering should have an end-of-candidates only "
   2995        "for level 0.");
   2996  }
   2997 
   2998  UniquePtr<Sdp> remoteAnswer(
   2999      Parse(mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent)));
   3000  for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
   3001    std::string id = GetTransportId(*mSessionAns, 0);
   3002    mAnsCandidates->CheckRtpCandidates(
   3003        i == 0, remoteAnswer->GetMediaSection(i), id,
   3004        "Remote reanswer after trickle should have RTP candidates on level 0.");
   3005    mAnsCandidates->CheckDefaultRtpCandidate(
   3006        i == 0, remoteAnswer->GetMediaSection(i), id,
   3007        "Remote reanswer should have a default RTP candidate on level 0 "
   3008        "(because it was gathered last offer/answer).");
   3009    mAnsCandidates->CheckRtcpCandidates(
   3010        false, remoteAnswer->GetMediaSection(i), id,
   3011        "Remote reanswer after trickle should not have RTCP candidates "
   3012        "(because we're reanswering with rtcp-mux)");
   3013    mAnsCandidates->CheckDefaultRtcpCandidate(
   3014        false, remoteAnswer->GetMediaSection(i), id,
   3015        "Remote reanswer after trickle should not have a default RTCP "
   3016        "candidate.");
   3017    CheckEndOfCandidates(i == 0, remoteAnswer->GetMediaSection(i),
   3018                         "Remote reanswer after trickle should have an "
   3019                         "end-of-candidates on level 0 only.");
   3020  }
   3021 }
   3022 
   3023 TEST_P(JsepSessionTest, RenegotiationAnswererSendonly) {
   3024  AddTracks(*mSessionOff);
   3025  AddTracks(*mSessionAns);
   3026  OfferAnswer();
   3027 
   3028  std::string offer = CreateOffer();
   3029  SetLocalOffer(offer);
   3030  SetRemoteOffer(offer);
   3031  std::string answer = CreateAnswer();
   3032  SetLocalAnswer(answer);
   3033 
   3034  UniquePtr<Sdp> parsedAnswer(Parse(answer));
   3035  for (size_t i = 0; i < parsedAnswer->GetMediaSectionCount(); ++i) {
   3036    SdpMediaSection& msection = parsedAnswer->GetMediaSection(i);
   3037    if (msection.GetMediaType() != SdpMediaSection::kApplication) {
   3038      msection.SetReceiving(false);
   3039    }
   3040  }
   3041 
   3042  answer = parsedAnswer->ToString();
   3043 
   3044  SetRemoteAnswer(answer);
   3045 
   3046  for (const JsepTrack& track : GetLocalTracks(*mSessionOff)) {
   3047    if (track.GetMediaType() != SdpMediaSection::kApplication) {
   3048      ASSERT_FALSE(track.GetActive());
   3049    }
   3050  }
   3051 
   3052  ASSERT_EQ(types.size(), GetTransceivers(*mSessionOff).size());
   3053 }
   3054 
   3055 TEST_P(JsepSessionTest, RenegotiationAnswererInactive) {
   3056  AddTracks(*mSessionOff);
   3057  AddTracks(*mSessionAns);
   3058  OfferAnswer();
   3059 
   3060  std::string offer = CreateOffer();
   3061  SetLocalOffer(offer);
   3062  SetRemoteOffer(offer);
   3063  std::string answer = CreateAnswer();
   3064  SetLocalAnswer(answer);
   3065 
   3066  UniquePtr<Sdp> parsedAnswer(Parse(answer));
   3067  for (size_t i = 0; i < parsedAnswer->GetMediaSectionCount(); ++i) {
   3068    SdpMediaSection& msection = parsedAnswer->GetMediaSection(i);
   3069    if (msection.GetMediaType() != SdpMediaSection::kApplication) {
   3070      msection.SetReceiving(false);
   3071      msection.SetSending(false);
   3072    }
   3073  }
   3074 
   3075  answer = parsedAnswer->ToString();
   3076 
   3077  SetRemoteAnswer(answer, CHECK_SUCCESS);  // Won't have answerer tracks
   3078 
   3079  for (const JsepTrack& track : GetLocalTracks(*mSessionOff)) {
   3080    if (track.GetMediaType() != SdpMediaSection::kApplication) {
   3081      ASSERT_FALSE(track.GetActive());
   3082    }
   3083  }
   3084 
   3085  ASSERT_EQ(types.size(), GetTransceivers(*mSessionOff).size());
   3086 }
   3087 
   3088 INSTANTIATE_TEST_SUITE_P(
   3089    Variants, JsepSessionTest,
   3090    ::testing::Values("audio", "video", "datachannel", "audio,video",
   3091                      "video,audio", "audio,datachannel", "video,datachannel",
   3092                      "video,audio,datachannel", "audio,video,datachannel",
   3093                      "datachannel,audio", "datachannel,video",
   3094                      "datachannel,audio,video", "datachannel,video,audio",
   3095                      "audio,datachannel,video", "video,datachannel,audio",
   3096                      "audio,audio", "video,video", "audio,audio,video",
   3097                      "audio,video,video", "audio,audio,video,video",
   3098                      "audio,audio,video,video,datachannel"));
   3099 
   3100 // offerToReceiveXxx variants
   3101 
   3102 TEST_F(JsepSessionTest, OfferAnswerRecvOnlyLines) {
   3103  mSessionOff->AddTransceiver(JsepTransceiver(
   3104      SdpMediaSection::kAudio, mUuidGen, SdpDirectionAttribute::kRecvonly));
   3105  mSessionOff->AddTransceiver(JsepTransceiver(
   3106      SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kRecvonly));
   3107  mSessionOff->AddTransceiver(JsepTransceiver(
   3108      SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kRecvonly));
   3109  std::string offer = CreateOffer();
   3110 
   3111  UniquePtr<Sdp> parsedOffer(Parse(offer));
   3112  ASSERT_TRUE(!!parsedOffer);
   3113 
   3114  ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount());
   3115  ASSERT_EQ(SdpMediaSection::kAudio,
   3116            parsedOffer->GetMediaSection(0).GetMediaType());
   3117  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
   3118            parsedOffer->GetMediaSection(0).GetAttributeList().GetDirection());
   3119  ASSERT_TRUE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
   3120      SdpAttribute::kSsrcAttribute));
   3121 
   3122  ASSERT_EQ(SdpMediaSection::kVideo,
   3123            parsedOffer->GetMediaSection(1).GetMediaType());
   3124  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
   3125            parsedOffer->GetMediaSection(1).GetAttributeList().GetDirection());
   3126  ASSERT_TRUE(parsedOffer->GetMediaSection(1).GetAttributeList().HasAttribute(
   3127      SdpAttribute::kSsrcAttribute));
   3128 
   3129  ASSERT_EQ(SdpMediaSection::kVideo,
   3130            parsedOffer->GetMediaSection(2).GetMediaType());
   3131  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
   3132            parsedOffer->GetMediaSection(2).GetAttributeList().GetDirection());
   3133  ASSERT_TRUE(parsedOffer->GetMediaSection(2).GetAttributeList().HasAttribute(
   3134      SdpAttribute::kSsrcAttribute));
   3135 
   3136  ASSERT_TRUE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
   3137      SdpAttribute::kRtcpMuxAttribute));
   3138  ASSERT_TRUE(parsedOffer->GetMediaSection(1).GetAttributeList().HasAttribute(
   3139      SdpAttribute::kRtcpMuxAttribute));
   3140  ASSERT_TRUE(parsedOffer->GetMediaSection(2).GetAttributeList().HasAttribute(
   3141      SdpAttribute::kRtcpMuxAttribute));
   3142 
   3143  SetLocalOffer(offer, CHECK_SUCCESS);
   3144 
   3145  AddTracks(*mSessionAns, "audio,video");
   3146  SetRemoteOffer(offer, CHECK_SUCCESS);
   3147 
   3148  std::string answer = CreateAnswer();
   3149  UniquePtr<Sdp> parsedAnswer(Parse(answer));
   3150 
   3151  ASSERT_EQ(3U, parsedAnswer->GetMediaSectionCount());
   3152  ASSERT_EQ(SdpMediaSection::kAudio,
   3153            parsedAnswer->GetMediaSection(0).GetMediaType());
   3154  ASSERT_EQ(SdpDirectionAttribute::kSendonly,
   3155            parsedAnswer->GetMediaSection(0).GetAttributeList().GetDirection());
   3156  ASSERT_EQ(SdpMediaSection::kVideo,
   3157            parsedAnswer->GetMediaSection(1).GetMediaType());
   3158  ASSERT_EQ(SdpDirectionAttribute::kSendonly,
   3159            parsedAnswer->GetMediaSection(1).GetAttributeList().GetDirection());
   3160  ASSERT_EQ(SdpMediaSection::kVideo,
   3161            parsedAnswer->GetMediaSection(2).GetMediaType());
   3162  ASSERT_EQ(SdpDirectionAttribute::kInactive,
   3163            parsedAnswer->GetMediaSection(2).GetAttributeList().GetDirection());
   3164 
   3165  SetLocalAnswer(answer, CHECK_SUCCESS);
   3166  SetRemoteAnswer(answer, CHECK_SUCCESS);
   3167 
   3168  std::vector<JsepTransceiver> transceivers(GetTransceivers(*mSessionOff));
   3169  ASSERT_EQ(3U, transceivers.size());
   3170  for (const auto& transceiver : transceivers) {
   3171    const auto& msection = parsedOffer->GetMediaSection(transceiver.GetLevel());
   3172    const auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;
   3173    ASSERT_EQ(1U, ssrcs.size());
   3174  }
   3175 }
   3176 
   3177 TEST_F(JsepSessionTest, OfferAnswerSendOnlyLines) {
   3178  AddTracks(*mSessionOff, "audio,video,video");
   3179 
   3180  SetDirection(*mSessionOff, 0, SdpDirectionAttribute::kSendonly);
   3181  SetDirection(*mSessionOff, 2, SdpDirectionAttribute::kSendonly);
   3182  std::string offer = CreateOffer();
   3183 
   3184  UniquePtr<Sdp> outputSdp(Parse(offer));
   3185  ASSERT_TRUE(!!outputSdp);
   3186 
   3187  ASSERT_EQ(3U, outputSdp->GetMediaSectionCount());
   3188  ASSERT_EQ(SdpMediaSection::kAudio,
   3189            outputSdp->GetMediaSection(0).GetMediaType());
   3190  ASSERT_EQ(SdpDirectionAttribute::kSendonly,
   3191            outputSdp->GetMediaSection(0).GetAttributeList().GetDirection());
   3192  ASSERT_EQ(SdpMediaSection::kVideo,
   3193            outputSdp->GetMediaSection(1).GetMediaType());
   3194  ASSERT_EQ(SdpDirectionAttribute::kSendrecv,
   3195            outputSdp->GetMediaSection(1).GetAttributeList().GetDirection());
   3196  ASSERT_EQ(SdpMediaSection::kVideo,
   3197            outputSdp->GetMediaSection(2).GetMediaType());
   3198  ASSERT_EQ(SdpDirectionAttribute::kSendonly,
   3199            outputSdp->GetMediaSection(2).GetAttributeList().GetDirection());
   3200 
   3201  ASSERT_TRUE(outputSdp->GetMediaSection(0).GetAttributeList().HasAttribute(
   3202      SdpAttribute::kRtcpMuxAttribute));
   3203  ASSERT_TRUE(outputSdp->GetMediaSection(1).GetAttributeList().HasAttribute(
   3204      SdpAttribute::kRtcpMuxAttribute));
   3205  ASSERT_TRUE(outputSdp->GetMediaSection(2).GetAttributeList().HasAttribute(
   3206      SdpAttribute::kRtcpMuxAttribute));
   3207 
   3208  SetLocalOffer(offer, CHECK_SUCCESS);
   3209 
   3210  AddTracks(*mSessionAns, "audio,video");
   3211  SetRemoteOffer(offer, CHECK_SUCCESS);
   3212 
   3213  std::string answer = CreateAnswer();
   3214  outputSdp = Parse(answer);
   3215 
   3216  ASSERT_EQ(3U, outputSdp->GetMediaSectionCount());
   3217  ASSERT_EQ(SdpMediaSection::kAudio,
   3218            outputSdp->GetMediaSection(0).GetMediaType());
   3219  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
   3220            outputSdp->GetMediaSection(0).GetAttributeList().GetDirection());
   3221  ASSERT_EQ(SdpMediaSection::kVideo,
   3222            outputSdp->GetMediaSection(1).GetMediaType());
   3223  ASSERT_EQ(SdpDirectionAttribute::kSendrecv,
   3224            outputSdp->GetMediaSection(1).GetAttributeList().GetDirection());
   3225  ASSERT_EQ(SdpMediaSection::kVideo,
   3226            outputSdp->GetMediaSection(2).GetMediaType());
   3227  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
   3228            outputSdp->GetMediaSection(2).GetAttributeList().GetDirection());
   3229 }
   3230 
   3231 TEST_F(JsepSessionTest, OfferToReceiveAudioNotUsed) {
   3232  mSessionOff->AddTransceiver(JsepTransceiver(
   3233      SdpMediaSection::kAudio, mUuidGen, SdpDirectionAttribute::kRecvonly));
   3234 
   3235  OfferAnswer(CHECK_SUCCESS);
   3236 
   3237  UniquePtr<Sdp> offer(
   3238      Parse(mSessionOff->GetLocalDescription(kJsepDescriptionCurrent)));
   3239  ASSERT_TRUE(offer.get());
   3240  ASSERT_EQ(1U, offer->GetMediaSectionCount());
   3241  ASSERT_EQ(SdpMediaSection::kAudio, offer->GetMediaSection(0).GetMediaType());
   3242  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
   3243            offer->GetMediaSection(0).GetAttributeList().GetDirection());
   3244 
   3245  UniquePtr<Sdp> answer(
   3246      Parse(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)));
   3247  ASSERT_TRUE(answer.get());
   3248  ASSERT_EQ(1U, answer->GetMediaSectionCount());
   3249  ASSERT_EQ(SdpMediaSection::kAudio, answer->GetMediaSection(0).GetMediaType());
   3250  ASSERT_EQ(SdpDirectionAttribute::kInactive,
   3251            answer->GetMediaSection(0).GetAttributeList().GetDirection());
   3252 }
   3253 
   3254 TEST_F(JsepSessionTest, OfferToReceiveVideoNotUsed) {
   3255  mSessionOff->AddTransceiver(JsepTransceiver(
   3256      SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kRecvonly));
   3257 
   3258  OfferAnswer(CHECK_SUCCESS);
   3259 
   3260  UniquePtr<Sdp> offer(
   3261      Parse(mSessionOff->GetLocalDescription(kJsepDescriptionCurrent)));
   3262  ASSERT_TRUE(offer.get());
   3263  ASSERT_EQ(1U, offer->GetMediaSectionCount());
   3264  ASSERT_EQ(SdpMediaSection::kVideo, offer->GetMediaSection(0).GetMediaType());
   3265  ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
   3266            offer->GetMediaSection(0).GetAttributeList().GetDirection());
   3267 
   3268  UniquePtr<Sdp> answer(
   3269      Parse(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)));
   3270  ASSERT_TRUE(answer.get());
   3271  ASSERT_EQ(1U, answer->GetMediaSectionCount());
   3272  ASSERT_EQ(SdpMediaSection::kVideo, answer->GetMediaSection(0).GetMediaType());
   3273  ASSERT_EQ(SdpDirectionAttribute::kInactive,
   3274            answer->GetMediaSection(0).GetAttributeList().GetDirection());
   3275 }
   3276 
   3277 TEST_F(JsepSessionTest, CreateOfferNoDatachannelDefault) {
   3278  JsepTransceiver audio(SdpMediaSection::kAudio, mUuidGen);
   3279  audio.mSendTrack.UpdateStreamIds(
   3280      std::vector<std::string>(1, "offerer_stream"));
   3281  mSessionOff->AddTransceiver(audio);
   3282 
   3283  JsepTransceiver video(SdpMediaSection::kVideo, mUuidGen);
   3284  video.mSendTrack.UpdateStreamIds(
   3285      std::vector<std::string>(1, "offerer_stream"));
   3286  mSessionOff->AddTransceiver(video);
   3287 
   3288  std::string offer = CreateOffer();
   3289 
   3290  UniquePtr<Sdp> outputSdp(Parse(offer));
   3291  ASSERT_TRUE(!!outputSdp);
   3292 
   3293  ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   3294  ASSERT_EQ(SdpMediaSection::kAudio,
   3295            outputSdp->GetMediaSection(0).GetMediaType());
   3296  ASSERT_EQ(SdpMediaSection::kVideo,
   3297            outputSdp->GetMediaSection(1).GetMediaType());
   3298 }
   3299 
   3300 TEST_F(JsepSessionTest, ValidateOfferedVideoCodecParams) {
   3301  types.push_back(SdpMediaSection::kAudio);
   3302  types.push_back(SdpMediaSection::kVideo);
   3303 
   3304  JsepTransceiver audio(SdpMediaSection::kAudio, mUuidGen);
   3305  audio.mSendTrack.UpdateStreamIds(
   3306      std::vector<std::string>(1, "offerer_stream"));
   3307  mSessionOff->AddTransceiver(audio);
   3308 
   3309  JsepTransceiver video(SdpMediaSection::kVideo, mUuidGen);
   3310  video.mSendTrack.UpdateStreamIds(
   3311      std::vector<std::string>(1, "offerer_stream"));
   3312  mSessionOff->AddTransceiver(video);
   3313 
   3314  std::string offer = CreateOffer();
   3315 
   3316  UniquePtr<Sdp> outputSdp(Parse(offer));
   3317  ASSERT_TRUE(!!outputSdp);
   3318 
   3319  ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   3320  const auto& video_section = outputSdp->GetMediaSection(1);
   3321  ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType());
   3322  const auto& video_attrs = video_section.GetAttributeList();
   3323  ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection());
   3324 
   3325  ASSERT_THAT(video_section.GetFormats(),
   3326              ElementsAre("120", "124", "121", "125", "126", "127", "105",
   3327                          "106", "99", "100", "123", "122", "119"));
   3328 
   3329  // Validate rtpmap
   3330  ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute));
   3331  const auto& rtpmaps = video_attrs.GetRtpmap().mRtpmaps;
   3332  std::multimap<std::string, std::string> rtpmapPtNames;
   3333  for (const auto& rtpmap : rtpmaps) {
   3334    rtpmapPtNames.insert({rtpmap.pt, rtpmap.name});
   3335  }
   3336  ASSERT_THAT(rtpmapPtNames,
   3337              UnorderedElementsAre(
   3338                  Pair("120", "VP8"), Pair("124", "rtx"), Pair("121", "VP9"),
   3339                  Pair("125", "rtx"), Pair("126", "H264"), Pair("127", "rtx"),
   3340                  Pair("105", "H264"), Pair("106", "rtx"), Pair("99", "AV1"),
   3341                  Pair("100", "rtx"), Pair("123", "ulpfec"), Pair("122", "red"),
   3342                  Pair("119", "rtx")));
   3343 
   3344  // Validate fmtps
   3345  ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kFmtpAttribute));
   3346  const auto& fmtps = video_attrs.GetFmtp().mFmtps;
   3347 
   3348  ASSERT_EQ(10U, fmtps.size());
   3349 
   3350  // VP8
   3351  const SdpFmtpAttributeList::Parameters* vp8_params =
   3352      video_section.FindFmtp("120");
   3353  ASSERT_TRUE(vp8_params);
   3354  ASSERT_EQ(SdpRtpmapAttributeList::kVP8, vp8_params->codec_type);
   3355 
   3356  const auto& parsed_vp8_params =
   3357      *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp8_params);
   3358 
   3359  ASSERT_EQ((uint32_t)12288, parsed_vp8_params.max_fs);
   3360  ASSERT_EQ((uint32_t)60, parsed_vp8_params.max_fr);
   3361 
   3362  // VP8 RTX
   3363  const SdpFmtpAttributeList::Parameters* vp8_rtx_params =
   3364      video_section.FindFmtp("124");
   3365  ASSERT_TRUE(vp8_rtx_params);
   3366  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, vp8_rtx_params->codec_type);
   3367 
   3368  const auto& parsed_vp8_rtx_params =
   3369      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(vp8_rtx_params);
   3370 
   3371  ASSERT_EQ((uint32_t)120, parsed_vp8_rtx_params.apt);
   3372 
   3373  // VP9
   3374  const SdpFmtpAttributeList::Parameters* vp9_params =
   3375      video_section.FindFmtp("121");
   3376  ASSERT_TRUE(vp9_params);
   3377  ASSERT_EQ(SdpRtpmapAttributeList::kVP9, vp9_params->codec_type);
   3378 
   3379  const auto& parsed_vp9_params =
   3380      *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp9_params);
   3381 
   3382  ASSERT_EQ((uint32_t)12288, parsed_vp9_params.max_fs);
   3383  ASSERT_EQ((uint32_t)60, parsed_vp9_params.max_fr);
   3384 
   3385  // VP9 RTX
   3386  const SdpFmtpAttributeList::Parameters* vp9_rtx_params =
   3387      video_section.FindFmtp("125");
   3388  ASSERT_TRUE(vp9_rtx_params);
   3389  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, vp9_rtx_params->codec_type);
   3390 
   3391  const auto& parsed_vp9_rtx_params =
   3392      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(vp9_rtx_params);
   3393  ASSERT_EQ((uint32_t)121, parsed_vp9_rtx_params.apt);
   3394 
   3395  // H264 packetization mode 1
   3396  const SdpFmtpAttributeList::Parameters* h264_1_params =
   3397      video_section.FindFmtp("126");
   3398  ASSERT_TRUE(h264_1_params);
   3399  ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_1_params->codec_type);
   3400 
   3401  const auto& parsed_h264_1_params =
   3402      *static_cast<const SdpFmtpAttributeList::H264Parameters*>(h264_1_params);
   3403 
   3404  ASSERT_EQ((uint32_t)0x42e01f, parsed_h264_1_params.profile_level_id);
   3405  ASSERT_TRUE(parsed_h264_1_params.level_asymmetry_allowed);
   3406  ASSERT_EQ(1U, parsed_h264_1_params.packetization_mode);
   3407 
   3408  // H264 packetization mode 1 RTX
   3409  const SdpFmtpAttributeList::Parameters* h264_1_rtx_params =
   3410      video_section.FindFmtp("127");
   3411  ASSERT_TRUE(h264_1_rtx_params);
   3412  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, h264_1_rtx_params->codec_type);
   3413 
   3414  const auto& parsed_h264_1_rtx_params =
   3415      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(
   3416          h264_1_rtx_params);
   3417 
   3418  ASSERT_EQ((uint32_t)126, parsed_h264_1_rtx_params.apt);
   3419 
   3420  // H264 packetization mode 0
   3421  const SdpFmtpAttributeList::Parameters* h264_0_params =
   3422      video_section.FindFmtp("97");
   3423  ASSERT_FALSE(h264_0_params);
   3424 
   3425  // H264 packetization mode 0 RTX
   3426  const SdpFmtpAttributeList::Parameters* h264_0_rtx_params =
   3427      video_section.FindFmtp("98");
   3428  ASSERT_FALSE(h264_0_rtx_params);
   3429 
   3430  // H264 Baseline packetization mode 1
   3431  const SdpFmtpAttributeList::Parameters* h264_baseline_1_params =
   3432      video_section.FindFmtp("105");
   3433  ASSERT_TRUE(h264_baseline_1_params);
   3434  ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_baseline_1_params->codec_type);
   3435 
   3436  const auto& parsed_h264_baseline_1_params =
   3437      *static_cast<const SdpFmtpAttributeList::H264Parameters*>(
   3438          h264_baseline_1_params);
   3439 
   3440  ASSERT_EQ((uint32_t)0x42001f, parsed_h264_baseline_1_params.profile_level_id);
   3441  ASSERT_TRUE(parsed_h264_baseline_1_params.level_asymmetry_allowed);
   3442  ASSERT_EQ(1U, parsed_h264_baseline_1_params.packetization_mode);
   3443 
   3444  // H264 Baseline packetization mode 1 RTX
   3445  const SdpFmtpAttributeList::Parameters* h264_baseline_1_rtx_params =
   3446      video_section.FindFmtp("106");
   3447  ASSERT_TRUE(h264_baseline_1_rtx_params);
   3448  ASSERT_EQ(SdpRtpmapAttributeList::kRtx,
   3449            h264_baseline_1_rtx_params->codec_type);
   3450 
   3451  const auto& parsed_h264__baseline_1_rtx_params =
   3452      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(
   3453          h264_baseline_1_rtx_params);
   3454 
   3455  ASSERT_EQ((uint32_t)105, parsed_h264__baseline_1_rtx_params.apt);
   3456 
   3457  // H264 Baseline packetization mode 0
   3458  const SdpFmtpAttributeList::Parameters* h264_baseline_0_params =
   3459      video_section.FindFmtp("103");
   3460  ASSERT_FALSE(h264_baseline_0_params);
   3461 
   3462  // H264 Baseline packetization mode 0 RTX
   3463  const SdpFmtpAttributeList::Parameters* h264__baseline_0_rtx_params =
   3464      video_section.FindFmtp("104");
   3465  ASSERT_FALSE(h264__baseline_0_rtx_params);
   3466 
   3467  // AV1 has no default FMTP parameters so there is no FMTP entry for AV1 in the
   3468  // test.
   3469  // AV1 RTX
   3470  const SdpFmtpAttributeList::Parameters* av1_rtx_params =
   3471      video_section.FindFmtp("100");
   3472  ASSERT_TRUE(av1_rtx_params);
   3473  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, av1_rtx_params->codec_type);
   3474  const auto& parsed_av1_rtx_params =
   3475      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(av1_rtx_params);
   3476  ASSERT_EQ((uint32_t)99, parsed_av1_rtx_params.apt);
   3477 
   3478  // red RTX
   3479  const SdpFmtpAttributeList::Parameters* red_rtx_params =
   3480      video_section.FindFmtp("119");
   3481  ASSERT_TRUE(red_rtx_params);
   3482  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, red_rtx_params->codec_type);
   3483 
   3484  const auto& parsed_red_rtx_params =
   3485      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(red_rtx_params);
   3486 
   3487  ASSERT_EQ((uint32_t)122, parsed_red_rtx_params.apt);
   3488 }
   3489 
   3490 TEST_F(JsepSessionTest, ValidateOfferedRecvonlyVideoCodecParams) {
   3491  types.push_back(SdpMediaSection::kAudio);
   3492  types.push_back(SdpMediaSection::kVideo);
   3493 
   3494  JsepTransceiver audio(SdpMediaSection::kAudio, mUuidGen,
   3495                        SdpDirectionAttribute::kRecvonly);
   3496  audio.mSendTrack.UpdateStreamIds(
   3497      std::vector<std::string>(1, "offerer_stream"));
   3498  mSessionOff->AddTransceiver(audio);
   3499 
   3500  JsepTransceiver video(SdpMediaSection::kVideo, mUuidGen,
   3501                        SdpDirectionAttribute::kRecvonly);
   3502  video.mSendTrack.UpdateStreamIds(
   3503      std::vector<std::string>(1, "offerer_stream"));
   3504  mSessionOff->AddTransceiver(video);
   3505 
   3506  std::string offer = CreateOffer();
   3507 
   3508  UniquePtr<Sdp> outputSdp(Parse(offer));
   3509  ASSERT_TRUE(!!outputSdp);
   3510 
   3511  ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   3512  const auto& video_section = outputSdp->GetMediaSection(1);
   3513  ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType());
   3514  const auto& video_attrs = video_section.GetAttributeList();
   3515  ASSERT_EQ(SdpDirectionAttribute::kRecvonly, video_attrs.GetDirection());
   3516 
   3517  ASSERT_THAT(
   3518      video_section.GetFormats(),
   3519      ElementsAre("120", "124", "121", "125", "126", "127", "97", "98", "105",
   3520                  "106", "103", "104", "99", "100", "123", "122", "119"));
   3521 
   3522  // Validate rtpmap
   3523  ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute));
   3524  const auto& rtpmaps = video_attrs.GetRtpmap().mRtpmaps;
   3525  std::multimap<std::string, std::string> rtpmapPtNames;
   3526  for (const auto& rtpmap : rtpmaps) {
   3527    rtpmapPtNames.insert({rtpmap.pt, rtpmap.name});
   3528  }
   3529  ASSERT_THAT(rtpmapPtNames,
   3530              UnorderedElementsAre(
   3531                  Pair("97", "H264"), Pair("98", "rtx"), Pair("103", "H264"),
   3532                  Pair("104", "rtx"), Pair("120", "VP8"), Pair("124", "rtx"),
   3533                  Pair("121", "VP9"), Pair("125", "rtx"), Pair("126", "H264"),
   3534                  Pair("127", "rtx"), Pair("105", "H264"), Pair("106", "rtx"),
   3535                  Pair("99", "AV1"), Pair("100", "rtx"), Pair("123", "ulpfec"),
   3536                  Pair("122", "red"), Pair("119", "rtx")));
   3537 
   3538  // Validate fmtps
   3539  ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kFmtpAttribute));
   3540  const auto& fmtps = video_attrs.GetFmtp().mFmtps;
   3541 
   3542  ASSERT_EQ(14U, fmtps.size());
   3543 
   3544  // VP8
   3545  const SdpFmtpAttributeList::Parameters* vp8_params =
   3546      video_section.FindFmtp("120");
   3547  ASSERT_TRUE(vp8_params);
   3548  ASSERT_EQ(SdpRtpmapAttributeList::kVP8, vp8_params->codec_type);
   3549 
   3550  const auto& parsed_vp8_params =
   3551      *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp8_params);
   3552 
   3553  ASSERT_EQ((uint32_t)12288, parsed_vp8_params.max_fs);
   3554  ASSERT_EQ((uint32_t)60, parsed_vp8_params.max_fr);
   3555 
   3556  // VP8 RTX
   3557  const SdpFmtpAttributeList::Parameters* vp8_rtx_params =
   3558      video_section.FindFmtp("124");
   3559  ASSERT_TRUE(vp8_rtx_params);
   3560  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, vp8_rtx_params->codec_type);
   3561 
   3562  const auto& parsed_vp8_rtx_params =
   3563      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(vp8_rtx_params);
   3564 
   3565  ASSERT_EQ((uint32_t)120, parsed_vp8_rtx_params.apt);
   3566 
   3567  // VP9
   3568  const SdpFmtpAttributeList::Parameters* vp9_params =
   3569      video_section.FindFmtp("121");
   3570  ASSERT_TRUE(vp9_params);
   3571  ASSERT_EQ(SdpRtpmapAttributeList::kVP9, vp9_params->codec_type);
   3572 
   3573  const auto& parsed_vp9_params =
   3574      *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp9_params);
   3575 
   3576  ASSERT_EQ((uint32_t)12288, parsed_vp9_params.max_fs);
   3577  ASSERT_EQ((uint32_t)60, parsed_vp9_params.max_fr);
   3578 
   3579  // VP9 RTX
   3580  const SdpFmtpAttributeList::Parameters* vp9_rtx_params =
   3581      video_section.FindFmtp("125");
   3582  ASSERT_TRUE(vp9_rtx_params);
   3583  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, vp9_rtx_params->codec_type);
   3584 
   3585  const auto& parsed_vp9_rtx_params =
   3586      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(vp9_rtx_params);
   3587  ASSERT_EQ((uint32_t)121, parsed_vp9_rtx_params.apt);
   3588 
   3589  // H264 packetization mode 1
   3590  const SdpFmtpAttributeList::Parameters* h264_1_params =
   3591      video_section.FindFmtp("126");
   3592  ASSERT_TRUE(h264_1_params);
   3593  ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_1_params->codec_type);
   3594 
   3595  const auto& parsed_h264_1_params =
   3596      *static_cast<const SdpFmtpAttributeList::H264Parameters*>(h264_1_params);
   3597 
   3598  ASSERT_EQ((uint32_t)0x42e01f, parsed_h264_1_params.profile_level_id);
   3599  ASSERT_TRUE(parsed_h264_1_params.level_asymmetry_allowed);
   3600  ASSERT_EQ(1U, parsed_h264_1_params.packetization_mode);
   3601 
   3602  // H264 packetization mode 1 RTX
   3603  const SdpFmtpAttributeList::Parameters* h264_1_rtx_params =
   3604      video_section.FindFmtp("127");
   3605  ASSERT_TRUE(h264_1_rtx_params);
   3606  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, h264_1_rtx_params->codec_type);
   3607 
   3608  const auto& parsed_h264_1_rtx_params =
   3609      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(
   3610          h264_1_rtx_params);
   3611 
   3612  ASSERT_EQ((uint32_t)126, parsed_h264_1_rtx_params.apt);
   3613 
   3614  // H264 packetization mode 0
   3615  const SdpFmtpAttributeList::Parameters* h264_0_params =
   3616      video_section.FindFmtp("97");
   3617  ASSERT_TRUE(h264_0_params);
   3618  ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_0_params->codec_type);
   3619 
   3620  const auto& parsed_h264_0_params =
   3621      *static_cast<const SdpFmtpAttributeList::H264Parameters*>(h264_0_params);
   3622 
   3623  ASSERT_EQ((uint32_t)0x42e01f, parsed_h264_0_params.profile_level_id);
   3624  ASSERT_TRUE(parsed_h264_0_params.level_asymmetry_allowed);
   3625  ASSERT_EQ(0U, parsed_h264_0_params.packetization_mode);
   3626 
   3627  // H264 packetization mode 0 RTX
   3628  const SdpFmtpAttributeList::Parameters* h264_0_rtx_params =
   3629      video_section.FindFmtp("98");
   3630  ASSERT_TRUE(h264_0_rtx_params);
   3631  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, h264_0_rtx_params->codec_type);
   3632 
   3633  const auto& parsed_h264_0_rtx_params =
   3634      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(
   3635          h264_0_rtx_params);
   3636 
   3637  ASSERT_EQ((uint32_t)97, parsed_h264_0_rtx_params.apt);
   3638 
   3639  // H264 Baseline packetization mode 1
   3640  const SdpFmtpAttributeList::Parameters* h264_baseline_1_params =
   3641      video_section.FindFmtp("105");
   3642  ASSERT_TRUE(h264_baseline_1_params);
   3643  ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_baseline_1_params->codec_type);
   3644 
   3645  const auto& parsed_h264_baseline_1_params =
   3646      *static_cast<const SdpFmtpAttributeList::H264Parameters*>(
   3647          h264_baseline_1_params);
   3648 
   3649  ASSERT_EQ((uint32_t)0x42001f, parsed_h264_baseline_1_params.profile_level_id);
   3650  ASSERT_TRUE(parsed_h264_baseline_1_params.level_asymmetry_allowed);
   3651  ASSERT_EQ(1U, parsed_h264_baseline_1_params.packetization_mode);
   3652 
   3653  // H264 Baseline packetization mode 1 RTX
   3654  const SdpFmtpAttributeList::Parameters* h264_baseline_1_rtx_params =
   3655      video_section.FindFmtp("106");
   3656  ASSERT_TRUE(h264_baseline_1_rtx_params);
   3657  ASSERT_EQ(SdpRtpmapAttributeList::kRtx,
   3658            h264_baseline_1_rtx_params->codec_type);
   3659 
   3660  const auto& parsed_h264__baseline_1_rtx_params =
   3661      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(
   3662          h264_baseline_1_rtx_params);
   3663 
   3664  ASSERT_EQ((uint32_t)105, parsed_h264__baseline_1_rtx_params.apt);
   3665 
   3666  // H264 Baseline packetization mode 0
   3667  const SdpFmtpAttributeList::Parameters* h264_baseline_0_params =
   3668      video_section.FindFmtp("103");
   3669  ASSERT_TRUE(h264_baseline_0_params);
   3670  ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_baseline_0_params->codec_type);
   3671 
   3672  const auto& parsed_h264_baseline_0_params =
   3673      *static_cast<const SdpFmtpAttributeList::H264Parameters*>(
   3674          h264_baseline_0_params);
   3675 
   3676  ASSERT_EQ((uint32_t)0x42001f, parsed_h264_baseline_0_params.profile_level_id);
   3677  ASSERT_TRUE(parsed_h264_baseline_0_params.level_asymmetry_allowed);
   3678  ASSERT_EQ(0U, parsed_h264_baseline_0_params.packetization_mode);
   3679 
   3680  // H264 Baseline packetization mode 0 RTX
   3681  const SdpFmtpAttributeList::Parameters* h264__baseline_0_rtx_params =
   3682      video_section.FindFmtp("104");
   3683  ASSERT_TRUE(h264__baseline_0_rtx_params);
   3684  ASSERT_EQ(SdpRtpmapAttributeList::kRtx,
   3685            h264__baseline_0_rtx_params->codec_type);
   3686 
   3687  const auto& parsed_h264_baseline_0_rtx_params =
   3688      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(
   3689          h264__baseline_0_rtx_params);
   3690 
   3691  ASSERT_EQ((uint32_t)103, parsed_h264_baseline_0_rtx_params.apt);
   3692 
   3693  // AV1 has no default FMTP parameters so there is no FMTP entry for AV1 in the
   3694  // test.
   3695  // AV1 RTX
   3696  const SdpFmtpAttributeList::Parameters* av1_rtx_params =
   3697      video_section.FindFmtp("100");
   3698  ASSERT_TRUE(av1_rtx_params);
   3699  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, av1_rtx_params->codec_type);
   3700  const auto& parsed_av1_rtx_params =
   3701      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(av1_rtx_params);
   3702  ASSERT_EQ((uint32_t)99, parsed_av1_rtx_params.apt);
   3703 
   3704  // red RTX
   3705  const SdpFmtpAttributeList::Parameters* red_rtx_params =
   3706      video_section.FindFmtp("119");
   3707  ASSERT_TRUE(red_rtx_params);
   3708  ASSERT_EQ(SdpRtpmapAttributeList::kRtx, red_rtx_params->codec_type);
   3709 
   3710  const auto& parsed_red_rtx_params =
   3711      *static_cast<const SdpFmtpAttributeList::RtxParameters*>(red_rtx_params);
   3712 
   3713  ASSERT_EQ((uint32_t)122, parsed_red_rtx_params.apt);
   3714 }
   3715 
   3716 TEST_F(JsepSessionTest, ValidateOfferedAudioCodecParams) {
   3717  types.push_back(SdpMediaSection::kAudio);
   3718  types.push_back(SdpMediaSection::kVideo);
   3719 
   3720  JsepTransceiver audio(SdpMediaSection::kAudio, mUuidGen);
   3721  audio.mSendTrack.UpdateStreamIds(
   3722      std::vector<std::string>(1, "offerer_stream"));
   3723  mSessionOff->AddTransceiver(audio);
   3724 
   3725  JsepTransceiver video(SdpMediaSection::kVideo, mUuidGen);
   3726  video.mSendTrack.UpdateStreamIds(
   3727      std::vector<std::string>(1, "offerer_stream"));
   3728  mSessionOff->AddTransceiver(video);
   3729 
   3730  std::string offer = CreateOffer();
   3731 
   3732  UniquePtr<Sdp> outputSdp(Parse(offer));
   3733  ASSERT_TRUE(!!outputSdp);
   3734 
   3735  ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   3736  auto& audio_section = outputSdp->GetMediaSection(0);
   3737  ASSERT_EQ(SdpMediaSection::kAudio, audio_section.GetMediaType());
   3738  auto& audio_attrs = audio_section.GetAttributeList();
   3739  ASSERT_EQ(SdpDirectionAttribute::kSendrecv, audio_attrs.GetDirection());
   3740  ASSERT_EQ(5U, audio_section.GetFormats().size());
   3741  ASSERT_EQ("109", audio_section.GetFormats()[0]);
   3742  ASSERT_EQ("9", audio_section.GetFormats()[1]);
   3743  ASSERT_EQ("0", audio_section.GetFormats()[2]);
   3744  ASSERT_EQ("8", audio_section.GetFormats()[3]);
   3745  ASSERT_EQ("101", audio_section.GetFormats()[4]);
   3746 
   3747  // Validate rtpmap
   3748  ASSERT_TRUE(audio_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute));
   3749  auto& rtpmaps = audio_attrs.GetRtpmap();
   3750  ASSERT_TRUE(rtpmaps.HasEntry("109"));
   3751  ASSERT_TRUE(rtpmaps.HasEntry("9"));
   3752  ASSERT_TRUE(rtpmaps.HasEntry("0"));
   3753  ASSERT_TRUE(rtpmaps.HasEntry("8"));
   3754  ASSERT_TRUE(rtpmaps.HasEntry("101"));
   3755 
   3756  auto& opus_entry = rtpmaps.GetEntry("109");
   3757  auto& g722_entry = rtpmaps.GetEntry("9");
   3758  auto& pcmu_entry = rtpmaps.GetEntry("0");
   3759  auto& pcma_entry = rtpmaps.GetEntry("8");
   3760  auto& telephone_event_entry = rtpmaps.GetEntry("101");
   3761 
   3762  ASSERT_EQ("opus", opus_entry.name);
   3763  ASSERT_EQ("G722", g722_entry.name);
   3764  ASSERT_EQ("PCMU", pcmu_entry.name);
   3765  ASSERT_EQ("PCMA", pcma_entry.name);
   3766  ASSERT_EQ("telephone-event", telephone_event_entry.name);
   3767 
   3768  // Validate fmtps
   3769  ASSERT_TRUE(audio_attrs.HasAttribute(SdpAttribute::kFmtpAttribute));
   3770  auto& fmtps = audio_attrs.GetFmtp().mFmtps;
   3771 
   3772  ASSERT_EQ(2U, fmtps.size());
   3773 
   3774  // opus
   3775  const SdpFmtpAttributeList::Parameters* opus_params =
   3776      audio_section.FindFmtp("109");
   3777  ASSERT_TRUE(opus_params);
   3778  ASSERT_EQ(SdpRtpmapAttributeList::kOpus, opus_params->codec_type);
   3779 
   3780  auto& parsed_opus_params =
   3781      *static_cast<const SdpFmtpAttributeList::OpusParameters*>(opus_params);
   3782 
   3783  ASSERT_EQ((uint32_t)48000, parsed_opus_params.maxplaybackrate);
   3784  ASSERT_EQ((uint32_t)1, parsed_opus_params.stereo);
   3785  ASSERT_EQ((uint32_t)0, parsed_opus_params.useInBandFec);
   3786  ASSERT_EQ((uint32_t)0, parsed_opus_params.maxAverageBitrate);
   3787  ASSERT_EQ((uint32_t)0, parsed_opus_params.useDTX);
   3788  ASSERT_EQ((uint32_t)0, parsed_opus_params.useCbr);
   3789  ASSERT_EQ((uint32_t)0, parsed_opus_params.frameSizeMs);
   3790  ASSERT_EQ((uint32_t)0, parsed_opus_params.minFrameSizeMs);
   3791  ASSERT_EQ((uint32_t)0, parsed_opus_params.maxFrameSizeMs);
   3792 
   3793  // dtmf
   3794  const SdpFmtpAttributeList::Parameters* dtmf_params =
   3795      audio_section.FindFmtp("101");
   3796  ASSERT_TRUE(dtmf_params);
   3797  ASSERT_EQ(SdpRtpmapAttributeList::kTelephoneEvent, dtmf_params->codec_type);
   3798 
   3799  auto& parsed_dtmf_params =
   3800      *static_cast<const SdpFmtpAttributeList::TelephoneEventParameters*>(
   3801          dtmf_params);
   3802 
   3803  ASSERT_EQ("0-15", parsed_dtmf_params.dtmfTones);
   3804 }
   3805 
   3806 TEST_F(JsepSessionTest, ValidateNoFmtpLineForRedInOfferAndAnswer) {
   3807  types.push_back(SdpMediaSection::kAudio);
   3808  types.push_back(SdpMediaSection::kVideo);
   3809 
   3810  AddTracksToStream(*mSessionOff, "offerer_stream", "audio,video");
   3811 
   3812  std::string offer = CreateOffer();
   3813 
   3814  SetLocalOffer(offer);
   3815  SetRemoteOffer(offer);
   3816 
   3817  AddTracksToStream(*mSessionAns, "answerer_stream", "audio,video");
   3818 
   3819  std::string answer = CreateAnswer();
   3820  // because parsing will throw out the malformed fmtp, make sure it is not
   3821  // in the answer sdp string
   3822  ASSERT_EQ(std::string::npos, answer.find("a=fmtp:122"));
   3823 
   3824  UniquePtr<Sdp> outputSdp(Parse(answer));
   3825  ASSERT_TRUE(!!outputSdp);
   3826 
   3827  ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   3828  auto& video_section = outputSdp->GetMediaSection(1);
   3829  ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType());
   3830  auto& video_attrs = video_section.GetAttributeList();
   3831  ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection());
   3832 
   3833  ASSERT_THAT(video_section.GetFormats(),
   3834              ElementsAre("120", "124", "121", "125", "126", "127", "105",
   3835                          "106", "99", "100", "123", "122", "119"));
   3836 
   3837  // Validate rtpmap
   3838  ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute));
   3839  const auto& rtpmaps = video_attrs.GetRtpmap().mRtpmaps;
   3840  std::vector<std::string> rtpmapPts;
   3841  rtpmapPts.reserve(rtpmaps.size());
   3842  for (const auto& rtpmap : rtpmaps) {
   3843    rtpmapPts.push_back(rtpmap.pt);
   3844  }
   3845  ASSERT_THAT(rtpmapPts, UnorderedElementsAre("120", "124", "121", "125", "126",
   3846                                              "127", "105", "106", "99", "100",
   3847                                              "123", "122", "119"));
   3848 
   3849  // Validate fmtps
   3850  ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kFmtpAttribute));
   3851  const auto& fmtps = video_attrs.GetFmtp().mFmtps;
   3852  std::vector<std::string> fmtpFormats;
   3853  fmtpFormats.reserve(fmtps.size());
   3854  for (const auto& fmtp : fmtps) {
   3855    fmtpFormats.push_back(fmtp.format);
   3856  }
   3857 
   3858  ASSERT_THAT(fmtpFormats, ElementsAre("126", "105", "120", "124", "121", "125",
   3859                                       "127", "106", "100", "119"));
   3860  SetLocalAnswer(answer);
   3861  SetRemoteAnswer(answer);
   3862 
   3863  auto offerTransceivers = GetTransceivers(*mSessionOff);
   3864  ASSERT_EQ(2U, offerTransceivers.size());
   3865  ASSERT_FALSE(IsNull(offerTransceivers[1].mSendTrack));
   3866  ASSERT_FALSE(IsNull(offerTransceivers[1].mRecvTrack));
   3867  ASSERT_TRUE(offerTransceivers[1].mSendTrack.GetNegotiatedDetails());
   3868  ASSERT_TRUE(offerTransceivers[1].mRecvTrack.GetNegotiatedDetails());
   3869  // Note that the number of recv/send codecs here differ because some codecs
   3870  // are recvonly. See SetupPreferredCodecs above.
   3871  ASSERT_EQ(7U, offerTransceivers[1]
   3872                    .mSendTrack.GetNegotiatedDetails()
   3873                    ->GetEncoding(0)
   3874                    .GetCodecs()
   3875                    .size());
   3876  ASSERT_EQ(9U, offerTransceivers[1]
   3877                    .mRecvTrack.GetNegotiatedDetails()
   3878                    ->GetEncoding(0)
   3879                    .GetCodecs()
   3880                    .size());
   3881 
   3882  auto answerTransceivers = GetTransceivers(*mSessionAns);
   3883  ASSERT_EQ(2U, answerTransceivers.size());
   3884  ASSERT_FALSE(IsNull(answerTransceivers[1].mSendTrack));
   3885  ASSERT_FALSE(IsNull(answerTransceivers[1].mRecvTrack));
   3886  ASSERT_TRUE(answerTransceivers[1].mSendTrack.GetNegotiatedDetails());
   3887  ASSERT_TRUE(answerTransceivers[1].mRecvTrack.GetNegotiatedDetails());
   3888  ASSERT_EQ(7U, answerTransceivers[1]
   3889                    .mSendTrack.GetNegotiatedDetails()
   3890                    ->GetEncoding(0)
   3891                    .GetCodecs()
   3892                    .size());
   3893  ASSERT_EQ(7U, answerTransceivers[1]
   3894                    .mRecvTrack.GetNegotiatedDetails()
   3895                    ->GetEncoding(0)
   3896                    .GetCodecs()
   3897                    .size());
   3898 }
   3899 
   3900 TEST_F(JsepSessionTest, OfferWithBundleGroupNoTags) {
   3901  AddTracks(*mSessionOff, "audio,video");
   3902  AddTracks(*mSessionAns, "audio,video");
   3903 
   3904  std::string offer = CreateOffer();
   3905  size_t i = offer.find("a=group:BUNDLE");
   3906  offer.insert(i, "a=group:BUNDLE\r\n");
   3907 
   3908  SetLocalOffer(offer, CHECK_SUCCESS);
   3909  SetRemoteOffer(offer, CHECK_SUCCESS);
   3910  std::string answer(CreateAnswer());
   3911 }
   3912 
   3913 static void Replace(const std::string& toReplace, const std::string& with,
   3914                    std::string* in) {
   3915  size_t pos = in->find(toReplace);
   3916  ASSERT_NE(std::string::npos, pos);
   3917  in->replace(pos, toReplace.size(), with);
   3918 }
   3919 
   3920 static void ReplaceAll(const std::string& toReplace, const std::string& with,
   3921                       std::string* in) {
   3922  while (in->find(toReplace) != std::string::npos) {
   3923    Replace(toReplace, with, in);
   3924  }
   3925 }
   3926 
   3927 static void GetCodec(JsepSession& session, size_t transceiverIndex,
   3928                     sdp::Direction direction, size_t encodingIndex,
   3929                     size_t codecIndex,
   3930                     UniquePtr<JsepCodecDescription>* codecOut) {
   3931  codecOut->reset();
   3932  ASSERT_LT(transceiverIndex, JsepSessionTest::GetTransceivers(session).size());
   3933  JsepTransceiver transceiver(
   3934      JsepSessionTest::GetTransceivers(session)[transceiverIndex]);
   3935  JsepTrack& track = (direction == sdp::kSend) ? transceiver.mSendTrack
   3936                                               : transceiver.mRecvTrack;
   3937  ASSERT_TRUE(track.GetNegotiatedDetails());
   3938  ASSERT_LT(encodingIndex, track.GetNegotiatedDetails()->GetEncodingCount());
   3939  ASSERT_LT(codecIndex, track.GetNegotiatedDetails()
   3940                            ->GetEncoding(encodingIndex)
   3941                            .GetCodecs()
   3942                            .size());
   3943  codecOut->reset(track.GetNegotiatedDetails()
   3944                      ->GetEncoding(encodingIndex)
   3945                      .GetCodecs()[codecIndex]
   3946                      ->Clone());
   3947 }
   3948 
   3949 static void ForceH264(JsepSession& session, uint32_t profileLevelId) {
   3950  for (auto& codec : session.Codecs()) {
   3951    if (codec->mName == "H264") {
   3952      JsepVideoCodecDescription* h264 =
   3953          static_cast<JsepVideoCodecDescription*>(codec.get());
   3954      h264->mProfileLevelId = profileLevelId;
   3955    } else {
   3956      codec->mEnabled = false;
   3957    }
   3958  }
   3959 }
   3960 
   3961 TEST_F(JsepSessionTest, TestH264Negotiation) {
   3962  ForceH264(*mSessionOff, 0x42e00b);
   3963  ForceH264(*mSessionAns, 0x42e01f);
   3964 
   3965  AddTracks(*mSessionOff, "video");
   3966  AddTracks(*mSessionAns, "video");
   3967 
   3968  std::string offer(CreateOffer());
   3969  SetLocalOffer(offer, CHECK_SUCCESS);
   3970 
   3971  SetRemoteOffer(offer, CHECK_SUCCESS);
   3972  std::string answer(CreateAnswer());
   3973 
   3974  SetRemoteAnswer(answer, CHECK_SUCCESS);
   3975  SetLocalAnswer(answer, CHECK_SUCCESS);
   3976 
   3977  UniquePtr<JsepCodecDescription> offererSendCodec;
   3978  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   3979  ASSERT_TRUE(offererSendCodec);
   3980  ASSERT_EQ("H264", offererSendCodec->mName);
   3981  const JsepVideoCodecDescription* offererVideoSendCodec(
   3982      static_cast<const JsepVideoCodecDescription*>(offererSendCodec.get()));
   3983  ASSERT_EQ((uint32_t)0x42e01f, offererVideoSendCodec->mProfileLevelId);
   3984 
   3985  UniquePtr<JsepCodecDescription> offererRecvCodec;
   3986  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   3987  ASSERT_EQ("H264", offererRecvCodec->mName);
   3988  const JsepVideoCodecDescription* offererVideoRecvCodec(
   3989      static_cast<const JsepVideoCodecDescription*>(offererRecvCodec.get()));
   3990  ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
   3991 
   3992  UniquePtr<JsepCodecDescription> answererSendCodec;
   3993  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   3994  ASSERT_TRUE(answererSendCodec);
   3995  ASSERT_EQ("H264", answererSendCodec->mName);
   3996  const JsepVideoCodecDescription* answererVideoSendCodec(
   3997      static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get()));
   3998  ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
   3999 
   4000  UniquePtr<JsepCodecDescription> answererRecvCodec;
   4001  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   4002  ASSERT_EQ("H264", answererRecvCodec->mName);
   4003  const JsepVideoCodecDescription* answererVideoRecvCodec(
   4004      static_cast<const JsepVideoCodecDescription*>(answererRecvCodec.get()));
   4005  ASSERT_EQ((uint32_t)0x42e01f, answererVideoRecvCodec->mProfileLevelId);
   4006 }
   4007 
   4008 TEST_F(JsepSessionTest, TestH264NegotiationFails) {
   4009  ForceH264(*mSessionOff, 0x42000b);
   4010  ForceH264(*mSessionAns, 0x42e01f);
   4011 
   4012  AddTracks(*mSessionOff, "video");
   4013  AddTracks(*mSessionAns, "video");
   4014 
   4015  std::string offer(CreateOffer());
   4016  SetLocalOffer(offer, CHECK_SUCCESS);
   4017 
   4018  SetRemoteOffer(offer, CHECK_SUCCESS);
   4019  std::string answer(CreateAnswer());
   4020 
   4021  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4022  SetLocalAnswer(answer, CHECK_SUCCESS);
   4023 
   4024  ASSERT_EQ(nullptr, GetNegotiatedTransceiver(*mSessionOff, 0));
   4025  ASSERT_EQ(nullptr, GetNegotiatedTransceiver(*mSessionAns, 0));
   4026 }
   4027 
   4028 TEST_F(JsepSessionTest, TestH264NegotiationOffererDefault) {
   4029  ForceH264(*mSessionOff, 0x42000d);
   4030  ForceH264(*mSessionAns, 0x42000d);
   4031 
   4032  AddTracks(*mSessionOff, "video");
   4033  AddTracks(*mSessionAns, "video");
   4034 
   4035  std::string offer(CreateOffer());
   4036  SetLocalOffer(offer, CHECK_SUCCESS);
   4037 
   4038  Replace("profile-level-id=42000d", "some-unknown-param=0", &offer);
   4039 
   4040  SetRemoteOffer(offer, CHECK_SUCCESS);
   4041  std::string answer(CreateAnswer());
   4042 
   4043  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4044  SetLocalAnswer(answer, CHECK_SUCCESS);
   4045 
   4046  UniquePtr<JsepCodecDescription> answererSendCodec;
   4047  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   4048  ASSERT_TRUE(answererSendCodec);
   4049  ASSERT_EQ("H264", answererSendCodec->mName);
   4050  const JsepVideoCodecDescription* answererVideoSendCodec(
   4051      static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get()));
   4052  ASSERT_EQ((uint32_t)0x42000A, answererVideoSendCodec->mProfileLevelId);
   4053 }
   4054 
   4055 TEST_F(JsepSessionTest, TestH264NegotiationOffererNoFmtp) {
   4056  ForceH264(*mSessionOff, 0x42000d);
   4057  ForceH264(*mSessionAns, 0x42001e);
   4058 
   4059  AddTracks(*mSessionOff, "video");
   4060  AddTracks(*mSessionAns, "video");
   4061 
   4062  std::string offer(CreateOffer());
   4063  SetLocalOffer(offer, CHECK_SUCCESS);
   4064 
   4065  Replace("a=fmtp", "a=oops", &offer);
   4066 
   4067  SetRemoteOffer(offer, CHECK_SUCCESS);
   4068  std::string answer(CreateAnswer());
   4069 
   4070  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4071  SetLocalAnswer(answer, CHECK_SUCCESS);
   4072 
   4073  UniquePtr<JsepCodecDescription> answererSendCodec;
   4074  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   4075  ASSERT_TRUE(answererSendCodec);
   4076  ASSERT_EQ("H264", answererSendCodec->mName);
   4077  const JsepVideoCodecDescription* answererVideoSendCodec(
   4078      static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get()));
   4079  ASSERT_EQ((uint32_t)0x42000A, answererVideoSendCodec->mProfileLevelId);
   4080 
   4081  UniquePtr<JsepCodecDescription> answererRecvCodec;
   4082  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   4083  ASSERT_EQ("H264", answererRecvCodec->mName);
   4084  const JsepVideoCodecDescription* answererVideoRecvCodec(
   4085      static_cast<const JsepVideoCodecDescription*>(answererRecvCodec.get()));
   4086  ASSERT_EQ((uint32_t)0x42000A, answererVideoRecvCodec->mProfileLevelId);
   4087 }
   4088 
   4089 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithLowLevel) {
   4090  ForceH264(*mSessionOff, 0x42e00b);
   4091  ForceH264(*mSessionAns, 0x42e01f);
   4092 
   4093  AddTracks(*mSessionOff, "video");
   4094  AddTracks(*mSessionAns, "video");
   4095 
   4096  std::string offer(CreateOffer());
   4097  SetLocalOffer(offer, CHECK_SUCCESS);
   4098 
   4099  Replace("level-asymmetry-allowed=1", "level-asymmetry-allowed=0", &offer);
   4100 
   4101  SetRemoteOffer(offer, CHECK_SUCCESS);
   4102  std::string answer(CreateAnswer());
   4103 
   4104  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4105  SetLocalAnswer(answer, CHECK_SUCCESS);
   4106 
   4107  // Offerer doesn't know about the shenanigans we've pulled here, so will
   4108  // behave normally, and we test the normal behavior elsewhere.
   4109 
   4110  UniquePtr<JsepCodecDescription> answererSendCodec;
   4111  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   4112  ASSERT_TRUE(answererSendCodec);
   4113  ASSERT_EQ("H264", answererSendCodec->mName);
   4114  const JsepVideoCodecDescription* answererVideoSendCodec(
   4115      static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get()));
   4116  ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
   4117 
   4118  UniquePtr<JsepCodecDescription> answererRecvCodec;
   4119  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   4120  ASSERT_EQ("H264", answererRecvCodec->mName);
   4121  const JsepVideoCodecDescription* answererVideoRecvCodec(
   4122      static_cast<const JsepVideoCodecDescription*>(answererRecvCodec.get()));
   4123  ASSERT_EQ((uint32_t)0x42e00b, answererVideoRecvCodec->mProfileLevelId);
   4124 }
   4125 
   4126 TEST_F(JsepSessionTest,
   4127       TestH264LevelAsymmetryDisallowedByOffererWithHighLevel) {
   4128  ForceH264(*mSessionOff, 0x42e01f);
   4129  ForceH264(*mSessionAns, 0x42e00b);
   4130 
   4131  AddTracks(*mSessionOff, "video");
   4132  AddTracks(*mSessionAns, "video");
   4133 
   4134  std::string offer(CreateOffer());
   4135  SetLocalOffer(offer, CHECK_SUCCESS);
   4136 
   4137  Replace("level-asymmetry-allowed=1", "level-asymmetry-allowed=0", &offer);
   4138 
   4139  SetRemoteOffer(offer, CHECK_SUCCESS);
   4140  std::string answer(CreateAnswer());
   4141 
   4142  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4143  SetLocalAnswer(answer, CHECK_SUCCESS);
   4144 
   4145  // Offerer doesn't know about the shenanigans we've pulled here, so will
   4146  // behave normally, and we test the normal behavior elsewhere.
   4147 
   4148  UniquePtr<JsepCodecDescription> answererSendCodec;
   4149  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   4150  ASSERT_TRUE(answererSendCodec);
   4151  ASSERT_EQ("H264", answererSendCodec->mName);
   4152  const JsepVideoCodecDescription* answererVideoSendCodec(
   4153      static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get()));
   4154  ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
   4155 
   4156  UniquePtr<JsepCodecDescription> answererRecvCodec;
   4157  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   4158  ASSERT_EQ("H264", answererRecvCodec->mName);
   4159  const JsepVideoCodecDescription* answererVideoRecvCodec(
   4160      static_cast<const JsepVideoCodecDescription*>(answererRecvCodec.get()));
   4161  ASSERT_EQ((uint32_t)0x42e00b, answererVideoRecvCodec->mProfileLevelId);
   4162 }
   4163 
   4164 TEST_F(JsepSessionTest,
   4165       TestH264LevelAsymmetryDisallowedByAnswererWithLowLevel) {
   4166  ForceH264(*mSessionOff, 0x42e01f);
   4167  ForceH264(*mSessionAns, 0x42e00b);
   4168 
   4169  AddTracks(*mSessionOff, "video");
   4170  AddTracks(*mSessionAns, "video");
   4171 
   4172  std::string offer(CreateOffer());
   4173  SetLocalOffer(offer, CHECK_SUCCESS);
   4174  SetRemoteOffer(offer, CHECK_SUCCESS);
   4175  std::string answer(CreateAnswer());
   4176 
   4177  Replace("level-asymmetry-allowed=1", "level-asymmetry-allowed=0", &answer);
   4178 
   4179  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4180  SetLocalAnswer(answer, CHECK_SUCCESS);
   4181 
   4182  UniquePtr<JsepCodecDescription> offererSendCodec;
   4183  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   4184  ASSERT_TRUE(offererSendCodec);
   4185  ASSERT_EQ("H264", offererSendCodec->mName);
   4186  const JsepVideoCodecDescription* offererVideoSendCodec(
   4187      static_cast<const JsepVideoCodecDescription*>(offererSendCodec.get()));
   4188  ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId);
   4189 
   4190  UniquePtr<JsepCodecDescription> offererRecvCodec;
   4191  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   4192  ASSERT_EQ("H264", offererRecvCodec->mName);
   4193  const JsepVideoCodecDescription* offererVideoRecvCodec(
   4194      static_cast<const JsepVideoCodecDescription*>(offererRecvCodec.get()));
   4195  ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
   4196 
   4197  // Answerer doesn't know we've pulled these shenanigans, it should act as if
   4198  // it did not set level-asymmetry-required, and we already check that
   4199  // elsewhere
   4200 }
   4201 
   4202 TEST_F(JsepSessionTest,
   4203       TestH264LevelAsymmetryDisallowedByAnswererWithHighLevel) {
   4204  ForceH264(*mSessionOff, 0x42e00b);
   4205  ForceH264(*mSessionAns, 0x42e01f);
   4206 
   4207  AddTracks(*mSessionOff, "video");
   4208  AddTracks(*mSessionAns, "video");
   4209 
   4210  std::string offer(CreateOffer());
   4211  SetLocalOffer(offer, CHECK_SUCCESS);
   4212  SetRemoteOffer(offer, CHECK_SUCCESS);
   4213  std::string answer(CreateAnswer());
   4214 
   4215  Replace("level-asymmetry-allowed=1", "level-asymmetry-allowed=0", &answer);
   4216 
   4217  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4218  SetLocalAnswer(answer, CHECK_SUCCESS);
   4219 
   4220  UniquePtr<JsepCodecDescription> offererSendCodec;
   4221  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   4222  ASSERT_TRUE(offererSendCodec);
   4223  ASSERT_EQ("H264", offererSendCodec->mName);
   4224  const JsepVideoCodecDescription* offererVideoSendCodec(
   4225      static_cast<const JsepVideoCodecDescription*>(offererSendCodec.get()));
   4226  ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId);
   4227 
   4228  UniquePtr<JsepCodecDescription> offererRecvCodec;
   4229  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   4230  ASSERT_EQ("H264", offererRecvCodec->mName);
   4231  const JsepVideoCodecDescription* offererVideoRecvCodec(
   4232      static_cast<const JsepVideoCodecDescription*>(offererRecvCodec.get()));
   4233  ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
   4234 
   4235  // Answerer doesn't know we've pulled these shenanigans, it should act as if
   4236  // it did not set level-asymmetry-required, and we already check that
   4237  // elsewhere
   4238 }
   4239 
   4240 TEST_P(JsepSessionTest, TestRejectMline) {
   4241  // We need to do this before adding tracks
   4242  types = BuildTypes(GetParam());
   4243 
   4244  SdpMediaSection::MediaType type = types.front();
   4245 
   4246  switch (type) {
   4247    case SdpMediaSection::kAudio:
   4248      // Sabotage audio
   4249      EnsureNegotiationFailure(types.front(), "opus");
   4250      break;
   4251    case SdpMediaSection::kVideo:
   4252      // Sabotage video
   4253      EnsureNegotiationFailure(types.front(), "H264");
   4254      break;
   4255    case SdpMediaSection::kApplication:
   4256      // Sabotage datachannel
   4257      EnsureNegotiationFailure(types.front(), "webrtc-datachannel");
   4258      break;
   4259    default:
   4260      ASSERT_TRUE(false)
   4261      << "Unknown media type";
   4262  }
   4263 
   4264  AddTracks(*mSessionOff);
   4265  AddTracks(*mSessionAns);
   4266 
   4267  std::string offer = CreateOffer();
   4268  mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
   4269  mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   4270 
   4271  std::string answer = CreateAnswer();
   4272 
   4273  UniquePtr<Sdp> outputSdp(Parse(answer));
   4274  ASSERT_TRUE(!!outputSdp);
   4275 
   4276  ASSERT_NE(0U, outputSdp->GetMediaSectionCount());
   4277  SdpMediaSection* failed_section = nullptr;
   4278 
   4279  for (size_t i = 0; i < outputSdp->GetMediaSectionCount(); ++i) {
   4280    if (outputSdp->GetMediaSection(i).GetMediaType() == type) {
   4281      failed_section = &outputSdp->GetMediaSection(i);
   4282    }
   4283  }
   4284 
   4285  ASSERT_TRUE(failed_section)
   4286  << "Failed type was entirely absent from SDP";
   4287  auto& failed_attrs = failed_section->GetAttributeList();
   4288  ASSERT_EQ(SdpDirectionAttribute::kInactive, failed_attrs.GetDirection());
   4289  ASSERT_EQ(0U, failed_section->GetPort());
   4290 
   4291  mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
   4292  mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
   4293 
   4294  size_t numRejected = std::count(types.begin(), types.end(), type);
   4295  size_t numAccepted = types.size() - numRejected;
   4296 
   4297  if (type == SdpMediaSection::MediaType::kApplication) {
   4298    ASSERT_TRUE(GetDatachannelTransceiver(*mSessionOff));
   4299    ASSERT_FALSE(
   4300        GetDatachannelTransceiver(*mSessionOff)->mRecvTrack.GetActive());
   4301    ASSERT_TRUE(GetDatachannelTransceiver(*mSessionAns));
   4302    ASSERT_FALSE(
   4303        GetDatachannelTransceiver(*mSessionAns)->mRecvTrack.GetActive());
   4304  } else {
   4305    ASSERT_EQ(types.size(), GetLocalTracks(*mSessionOff).size());
   4306    ASSERT_EQ(numAccepted, GetRemoteTracks(*mSessionOff).size());
   4307 
   4308    ASSERT_EQ(types.size(), GetLocalTracks(*mSessionAns).size());
   4309    ASSERT_EQ(types.size(), GetRemoteTracks(*mSessionAns).size());
   4310  }
   4311 }
   4312 
   4313 TEST_F(JsepSessionTest, NegotiationNoMlines) { OfferAnswer(); }
   4314 
   4315 TEST_F(JsepSessionTest, TestIceLite) {
   4316  AddTracks(*mSessionOff, "audio");
   4317  AddTracks(*mSessionAns, "audio");
   4318  std::string offer = CreateOffer();
   4319  SetLocalOffer(offer, CHECK_SUCCESS);
   4320 
   4321  UniquePtr<Sdp> parsedOffer(Parse(offer));
   4322  parsedOffer->GetAttributeList().SetAttribute(
   4323      new SdpFlagAttribute(SdpAttribute::kIceLiteAttribute));
   4324 
   4325  std::ostringstream os;
   4326  parsedOffer->Serialize(os);
   4327  SetRemoteOffer(os.str(), CHECK_SUCCESS);
   4328 
   4329  ASSERT_TRUE(mSessionAns->RemoteIsIceLite());
   4330  ASSERT_FALSE(mSessionOff->RemoteIsIceLite());
   4331 }
   4332 
   4333 TEST_F(JsepSessionTest, TestIceOptions) {
   4334  AddTracks(*mSessionOff, "audio");
   4335  AddTracks(*mSessionAns, "audio");
   4336  std::string offer = CreateOffer();
   4337  SetLocalOffer(offer, CHECK_SUCCESS);
   4338  SetRemoteOffer(offer, CHECK_SUCCESS);
   4339  std::string answer = CreateAnswer();
   4340  SetLocalAnswer(answer, CHECK_SUCCESS);
   4341  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4342 
   4343  ASSERT_EQ(1U, mSessionOff->GetIceOptions().size());
   4344  ASSERT_EQ("trickle", mSessionOff->GetIceOptions()[0]);
   4345 
   4346  ASSERT_EQ(1U, mSessionAns->GetIceOptions().size());
   4347  ASSERT_EQ("trickle", mSessionAns->GetIceOptions()[0]);
   4348 }
   4349 
   4350 TEST_F(JsepSessionTest, TestIceRestart) {
   4351  AddTracks(*mSessionOff, "audio");
   4352  AddTracks(*mSessionAns, "audio");
   4353  std::string offer = CreateOffer();
   4354  SetLocalOffer(offer, CHECK_SUCCESS);
   4355  SetRemoteOffer(offer, CHECK_SUCCESS);
   4356  std::string answer = CreateAnswer();
   4357  SetLocalAnswer(answer, CHECK_SUCCESS);
   4358  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4359 
   4360  JsepOfferOptions options;
   4361  options.mIceRestart = Some(true);
   4362 
   4363  std::string reoffer = CreateOffer(Some(options));
   4364  SetLocalOffer(reoffer, CHECK_SUCCESS);
   4365  SetRemoteOffer(reoffer, CHECK_SUCCESS);
   4366  std::string reanswer = CreateAnswer();
   4367  SetLocalAnswer(reanswer, CHECK_SUCCESS);
   4368  SetRemoteAnswer(reanswer, CHECK_SUCCESS);
   4369 
   4370  UniquePtr<Sdp> parsedOffer(Parse(offer));
   4371  ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount());
   4372  UniquePtr<Sdp> parsedReoffer(Parse(reoffer));
   4373  ASSERT_EQ(1U, parsedReoffer->GetMediaSectionCount());
   4374 
   4375  UniquePtr<Sdp> parsedAnswer(Parse(answer));
   4376  ASSERT_EQ(1U, parsedAnswer->GetMediaSectionCount());
   4377  UniquePtr<Sdp> parsedReanswer(Parse(reanswer));
   4378  ASSERT_EQ(1U, parsedReanswer->GetMediaSectionCount());
   4379 
   4380  // verify ice pwd/ufrag are present in offer/answer and reoffer/reanswer
   4381  auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList();
   4382  ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kIcePwdAttribute));
   4383  ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kIceUfragAttribute));
   4384 
   4385  auto& reofferMediaAttrs =
   4386      parsedReoffer->GetMediaSection(0).GetAttributeList();
   4387  ASSERT_TRUE(reofferMediaAttrs.HasAttribute(SdpAttribute::kIcePwdAttribute));
   4388  ASSERT_TRUE(reofferMediaAttrs.HasAttribute(SdpAttribute::kIceUfragAttribute));
   4389 
   4390  auto& answerMediaAttrs = parsedAnswer->GetMediaSection(0).GetAttributeList();
   4391  ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kIcePwdAttribute));
   4392  ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kIceUfragAttribute));
   4393 
   4394  auto& reanswerMediaAttrs =
   4395      parsedReanswer->GetMediaSection(0).GetAttributeList();
   4396  ASSERT_TRUE(reanswerMediaAttrs.HasAttribute(SdpAttribute::kIcePwdAttribute));
   4397  ASSERT_TRUE(
   4398      reanswerMediaAttrs.HasAttribute(SdpAttribute::kIceUfragAttribute));
   4399 
   4400  // make sure offer/reoffer ice pwd/ufrag changed on ice restart
   4401  ASSERT_NE(offerMediaAttrs.GetIcePwd().c_str(),
   4402            reofferMediaAttrs.GetIcePwd().c_str());
   4403  ASSERT_NE(offerMediaAttrs.GetIceUfrag().c_str(),
   4404            reofferMediaAttrs.GetIceUfrag().c_str());
   4405 
   4406  // make sure answer/reanswer ice pwd/ufrag changed on ice restart
   4407  ASSERT_NE(answerMediaAttrs.GetIcePwd().c_str(),
   4408            reanswerMediaAttrs.GetIcePwd().c_str());
   4409  ASSERT_NE(answerMediaAttrs.GetIceUfrag().c_str(),
   4410            reanswerMediaAttrs.GetIceUfrag().c_str());
   4411 
   4412  auto offererTransceivers = GetTransceivers(*mSessionOff);
   4413  auto answererTransceivers = GetTransceivers(*mSessionAns);
   4414  ASSERT_EQ(reofferMediaAttrs.GetIceUfrag(),
   4415            offererTransceivers[0].mTransport.mLocalUfrag);
   4416  ASSERT_EQ(reofferMediaAttrs.GetIceUfrag(),
   4417            answererTransceivers[0].mTransport.mIce->GetUfrag());
   4418  ASSERT_EQ(reofferMediaAttrs.GetIcePwd(),
   4419            offererTransceivers[0].mTransport.mLocalPwd);
   4420  ASSERT_EQ(reofferMediaAttrs.GetIcePwd(),
   4421            answererTransceivers[0].mTransport.mIce->GetPassword());
   4422 
   4423  ASSERT_EQ(reanswerMediaAttrs.GetIceUfrag(),
   4424            answererTransceivers[0].mTransport.mLocalUfrag);
   4425  ASSERT_EQ(reanswerMediaAttrs.GetIceUfrag(),
   4426            offererTransceivers[0].mTransport.mIce->GetUfrag());
   4427  ASSERT_EQ(reanswerMediaAttrs.GetIcePwd(),
   4428            answererTransceivers[0].mTransport.mLocalPwd);
   4429  ASSERT_EQ(reanswerMediaAttrs.GetIcePwd(),
   4430            offererTransceivers[0].mTransport.mIce->GetPassword());
   4431 }
   4432 
   4433 TEST_F(JsepSessionTest, TestAnswererIndicatingIceRestart) {
   4434  AddTracks(*mSessionOff, "audio");
   4435  AddTracks(*mSessionAns, "audio");
   4436  std::string offer = CreateOffer();
   4437  SetLocalOffer(offer, CHECK_SUCCESS);
   4438  SetRemoteOffer(offer, CHECK_SUCCESS);
   4439  std::string answer = CreateAnswer();
   4440  SetLocalAnswer(answer, CHECK_SUCCESS);
   4441  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4442 
   4443  // reoffer, but we'll improperly indicate an ice restart in the answer by
   4444  // modifying the ice pwd and ufrag
   4445  std::string reoffer = CreateOffer();
   4446  SetLocalOffer(reoffer, CHECK_SUCCESS);
   4447  SetRemoteOffer(reoffer, CHECK_SUCCESS);
   4448  std::string reanswer = CreateAnswer();
   4449 
   4450  // change the ice pwd and ufrag
   4451  ReplaceInSdp(&reanswer, "a=ice-ufrag:", "a=ice-ufrag:bad-");
   4452  ReplaceInSdp(&reanswer, "a=ice-pwd:", "a=ice-pwd:bad-");
   4453  SetLocalAnswer(reanswer, CHECK_SUCCESS);
   4454  JsepSession::Result result =
   4455      mSessionOff->SetRemoteDescription(kJsepSdpAnswer, reanswer);
   4456  ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError);
   4457 }
   4458 
   4459 TEST_F(JsepSessionTest, TestExtmap) {
   4460  AddTracks(*mSessionOff, "audio");
   4461  AddTracks(*mSessionAns, "audio");
   4462  // ssrc-audio-level will be extmap 1 for both
   4463  // csrc-audio-level will be 2 for both
   4464  // mid will be 3 for both
   4465  // video related extensions take 4 - 7
   4466  mSessionOff->AddAudioRtpExtension("foo");  // Default mapping of 8
   4467  mSessionOff->AddAudioRtpExtension("bar");  // Default mapping of 9
   4468  mSessionAns->AddAudioRtpExtension("bar");  // Default mapping of 8
   4469  std::string offer = CreateOffer();
   4470  SetLocalOffer(offer, CHECK_SUCCESS);
   4471  SetRemoteOffer(offer, CHECK_SUCCESS);
   4472  std::string answer = CreateAnswer();
   4473  SetLocalAnswer(answer, CHECK_SUCCESS);
   4474  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4475 
   4476  UniquePtr<Sdp> parsedOffer(Parse(offer));
   4477  ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount());
   4478 
   4479  auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList();
   4480  ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   4481  auto& offerExtmap = offerMediaAttrs.GetExtmap().mExtmaps;
   4482  ASSERT_EQ(5U, offerExtmap.size());
   4483  ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
   4484            offerExtmap[0].extensionname);
   4485  ASSERT_EQ(1U, offerExtmap[0].entry);
   4486  ASSERT_EQ("urn:ietf:params:rtp-hdrext:csrc-audio-level",
   4487            offerExtmap[1].extensionname);
   4488  ASSERT_EQ(2U, offerExtmap[1].entry);
   4489  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid",
   4490            offerExtmap[2].extensionname);
   4491  ASSERT_EQ(3U, offerExtmap[2].entry);
   4492  ASSERT_EQ("foo", offerExtmap[3].extensionname);
   4493  ASSERT_EQ(8U, offerExtmap[3].entry);
   4494  ASSERT_EQ("bar", offerExtmap[4].extensionname);
   4495  ASSERT_EQ(9U, offerExtmap[4].entry);
   4496 
   4497  UniquePtr<Sdp> parsedAnswer(Parse(answer));
   4498  ASSERT_EQ(1U, parsedAnswer->GetMediaSectionCount());
   4499 
   4500  auto& answerMediaAttrs = parsedAnswer->GetMediaSection(0).GetAttributeList();
   4501  ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   4502  auto& answerExtmap = answerMediaAttrs.GetExtmap().mExtmaps;
   4503  ASSERT_EQ(3U, answerExtmap.size());
   4504  ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
   4505            answerExtmap[0].extensionname);
   4506  ASSERT_EQ(1U, answerExtmap[0].entry);
   4507  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid",
   4508            answerExtmap[1].extensionname);
   4509  ASSERT_EQ(3U, answerExtmap[1].entry);
   4510  // We ensure that the entry for "bar" matches what was in the offer
   4511  ASSERT_EQ("bar", answerExtmap[2].extensionname);
   4512  ASSERT_EQ(9U, answerExtmap[2].entry);
   4513 }
   4514 
   4515 TEST_F(JsepSessionTest, TestExtmapDefaults) {
   4516  types.push_back(SdpMediaSection::kAudio);
   4517  types.push_back(SdpMediaSection::kVideo);
   4518  AddTracks(*mSessionOff, "audio,video");
   4519 
   4520  std::string offer = CreateOffer();
   4521  SetLocalOffer(offer, CHECK_SUCCESS);
   4522  SetRemoteOffer(offer, CHECK_SUCCESS);
   4523 
   4524  std::string answer = CreateAnswer();
   4525  SetLocalAnswer(answer, CHECK_SUCCESS);
   4526  SetRemoteAnswer(answer, CHECK_SUCCESS);
   4527 
   4528  UniquePtr<Sdp> parsedOffer(Parse(offer));
   4529  ASSERT_EQ(2U, parsedOffer->GetMediaSectionCount());
   4530 
   4531  auto& offerAudioMediaAttrs =
   4532      parsedOffer->GetMediaSection(0).GetAttributeList();
   4533  ASSERT_TRUE(
   4534      offerAudioMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   4535  auto& offerAudioExtmap = offerAudioMediaAttrs.GetExtmap().mExtmaps;
   4536  ASSERT_EQ(3U, offerAudioExtmap.size());
   4537 
   4538  ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
   4539            offerAudioExtmap[0].extensionname);
   4540  ASSERT_EQ(1U, offerAudioExtmap[0].entry);
   4541  ASSERT_EQ("urn:ietf:params:rtp-hdrext:csrc-audio-level",
   4542            offerAudioExtmap[1].extensionname);
   4543  ASSERT_EQ(2U, offerAudioExtmap[1].entry);
   4544  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid",
   4545            offerAudioExtmap[2].extensionname);
   4546 
   4547  auto& offerVideoMediaAttrs =
   4548      parsedOffer->GetMediaSection(1).GetAttributeList();
   4549  ASSERT_TRUE(
   4550      offerVideoMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   4551  auto& offerVideoExtmap = offerVideoMediaAttrs.GetExtmap().mExtmaps;
   4552  ASSERT_EQ(5U, offerVideoExtmap.size());
   4553 
   4554  ASSERT_EQ(3U, offerVideoExtmap[0].entry);
   4555  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid",
   4556            offerVideoExtmap[0].extensionname);
   4557  ASSERT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
   4558            offerVideoExtmap[1].extensionname);
   4559  ASSERT_EQ(4U, offerVideoExtmap[1].entry);
   4560  ASSERT_EQ("urn:ietf:params:rtp-hdrext:toffset",
   4561            offerVideoExtmap[2].extensionname);
   4562  ASSERT_EQ(5U, offerVideoExtmap[2].entry);
   4563  ASSERT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/playout-delay",
   4564            offerVideoExtmap[3].extensionname);
   4565  ASSERT_EQ(6U, offerVideoExtmap[3].entry);
   4566  ASSERT_EQ(
   4567      "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-"
   4568      "extensions-01",
   4569      offerVideoExtmap[4].extensionname);
   4570  ASSERT_EQ(7U, offerVideoExtmap[4].entry);
   4571 
   4572  UniquePtr<Sdp> parsedAnswer(Parse(answer));
   4573  ASSERT_EQ(2U, parsedAnswer->GetMediaSectionCount());
   4574 
   4575  auto& answerAudioMediaAttrs =
   4576      parsedAnswer->GetMediaSection(0).GetAttributeList();
   4577  ASSERT_TRUE(
   4578      answerAudioMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   4579  auto& answerAudioExtmap = answerAudioMediaAttrs.GetExtmap().mExtmaps;
   4580  ASSERT_EQ(2U, answerAudioExtmap.size());
   4581 
   4582  ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
   4583            answerAudioExtmap[0].extensionname);
   4584  ASSERT_EQ(1U, answerAudioExtmap[0].entry);
   4585  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid",
   4586            answerAudioExtmap[1].extensionname);
   4587  ASSERT_EQ(3U, answerAudioExtmap[1].entry);
   4588 
   4589  auto& answerVideoMediaAttrs =
   4590      parsedAnswer->GetMediaSection(1).GetAttributeList();
   4591  ASSERT_TRUE(
   4592      answerVideoMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   4593  auto& answerVideoExtmap = answerVideoMediaAttrs.GetExtmap().mExtmaps;
   4594  ASSERT_EQ(4U, answerVideoExtmap.size());
   4595 
   4596  ASSERT_EQ(3U, answerVideoExtmap[0].entry);
   4597  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid",
   4598            answerVideoExtmap[0].extensionname);
   4599  ASSERT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
   4600            answerVideoExtmap[1].extensionname);
   4601  ASSERT_EQ(4U, answerVideoExtmap[1].entry);
   4602  ASSERT_EQ("urn:ietf:params:rtp-hdrext:toffset",
   4603            answerVideoExtmap[2].extensionname);
   4604  ASSERT_EQ(5U, answerVideoExtmap[2].entry);
   4605  ASSERT_EQ(
   4606      "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-"
   4607      "extensions-01",
   4608      answerVideoExtmap[3].extensionname);
   4609  ASSERT_EQ(7U, answerVideoExtmap[3].entry);
   4610 }
   4611 
   4612 TEST_F(JsepSessionTest, TestExtmapWithDuplicates) {
   4613  AddTracks(*mSessionOff, "audio");
   4614  AddTracks(*mSessionAns, "audio");
   4615  // ssrc-audio-level will be extmap 1 for both
   4616  // csrc-audio-level will be 2 for both
   4617  // mid will be 3 for both
   4618  // video related extensions take 4 - 7
   4619  mSessionOff->AddAudioRtpExtension("foo");  // Default mapping of 8
   4620  mSessionOff->AddAudioRtpExtension("bar");  // Default mapping of 9
   4621  mSessionOff->AddAudioRtpExtension("bar");  // Should be ignored
   4622  mSessionOff->AddAudioRtpExtension("bar");  // Should be ignored
   4623  mSessionOff->AddAudioRtpExtension("baz");  // Default mapping of 10
   4624  mSessionOff->AddAudioRtpExtension("bar");  // Should be ignored
   4625 
   4626  std::string offer = CreateOffer();
   4627  UniquePtr<Sdp> parsedOffer(Parse(offer));
   4628  ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount());
   4629 
   4630  auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList();
   4631  ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   4632  auto& offerExtmap = offerMediaAttrs.GetExtmap().mExtmaps;
   4633  ASSERT_EQ(6U, offerExtmap.size());
   4634  ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
   4635            offerExtmap[0].extensionname);
   4636  ASSERT_EQ(1U, offerExtmap[0].entry);
   4637  ASSERT_EQ("urn:ietf:params:rtp-hdrext:csrc-audio-level",
   4638            offerExtmap[1].extensionname);
   4639  ASSERT_EQ(2U, offerExtmap[1].entry);
   4640  ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid",
   4641            offerExtmap[2].extensionname);
   4642  ASSERT_EQ(3U, offerExtmap[2].entry);
   4643  ASSERT_EQ("foo", offerExtmap[3].extensionname);
   4644  ASSERT_EQ(8U, offerExtmap[3].entry);
   4645  ASSERT_EQ("bar", offerExtmap[4].extensionname);
   4646  ASSERT_EQ(9U, offerExtmap[4].entry);
   4647  ASSERT_EQ("baz", offerExtmap[5].extensionname);
   4648  ASSERT_EQ(10U, offerExtmap[5].entry);
   4649 }
   4650 
   4651 TEST_F(JsepSessionTest, TestExtmapZeroId) {
   4652  AddTracks(*mSessionOff, "video");
   4653  AddTracks(*mSessionAns, "video");
   4654 
   4655  std::string sdp =
   4656      "v=0\r\n"
   4657      "o=- 6 2 IN IP4 1r\r\n"
   4658      "t=0 0a\r\n"
   4659      "a=ice-ufrag:Xp\r\n"
   4660      "a=ice-pwd:he\r\n"
   4661      "a=setup:actpass\r\n"
   4662      "a=fingerprint:sha-256 "
   4663      "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:"
   4664      "08:D2:F7:9D:F5:E2:C1:15\r\n"
   4665      "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n"
   4666      "c=IN IP4 0\r\n"
   4667      "a=rtpmap:100 VP8/90000\r\n"
   4668      "a=extmap:0 urn:ietf:params:rtp-hdrext:toffset\r\n";
   4669  auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp);
   4670  ASSERT_TRUE(result.mError == Some(dom::PCError::OperationError));
   4671  ASSERT_EQ(
   4672      "Description contains invalid extension id 0 on level 0 which is"
   4673      " unsupported until 2-byte rtp header extensions are supported in"
   4674      " webrtc.org",
   4675      mSessionAns->GetLastError());
   4676 }
   4677 
   4678 TEST_F(JsepSessionTest, TestExtmapInvalidId) {
   4679  AddTracks(*mSessionAns, "video");
   4680 
   4681  std::string sdp =
   4682      "v=0\r\n"
   4683      "o=- 6 2 IN IP4 1r\r\n"
   4684      "t=0 0a\r\n"
   4685      "a=ice-ufrag:Xp\r\n"
   4686      "a=ice-pwd:he\r\n"
   4687      "a=setup:actpass\r\n"
   4688      "a=fingerprint:sha-256 "
   4689      "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:"
   4690      "08:D2:F7:9D:F5:E2:C1:15\r\n"
   4691      "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n"
   4692      "c=IN IP4 0\r\n"
   4693      "a=rtpmap:100 VP8/90000\r\n"
   4694      "a=extmap:15 urn:ietf:params:rtp-hdrext:toffset\r\n";
   4695  auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp);
   4696  ASSERT_TRUE(result.mError == Some(dom::PCError::OperationError));
   4697  ASSERT_EQ(
   4698      "Description contains invalid extension id 15 on level 0 which is"
   4699      " unsupported until 2-byte rtp header extensions are supported in"
   4700      " webrtc.org",
   4701      mSessionAns->GetLastError());
   4702 }
   4703 
   4704 TEST_F(JsepSessionTest, TestExtmapDuplicateId) {
   4705  AddTracks(*mSessionAns, "video");
   4706 
   4707  std::string sdp =
   4708      "v=0\r\n"
   4709      "o=- 6 2 IN IP4 1r\r\n"
   4710      "t=0 0a\r\n"
   4711      "a=ice-ufrag:Xp\r\n"
   4712      "a=ice-pwd:he\r\n"
   4713      "a=setup:actpass\r\n"
   4714      "a=fingerprint:sha-256 "
   4715      "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:"
   4716      "08:D2:F7:9D:F5:E2:C1:15\r\n"
   4717      "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n"
   4718      "c=IN IP4 0\r\n"
   4719      "a=rtpmap:100 VP8/90000\r\n"
   4720      "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n"
   4721      "a=extmap:2 "
   4722      "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n";
   4723  auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp);
   4724  ASSERT_TRUE(result.mError == Some(dom::PCError::OperationError));
   4725  ASSERT_EQ("Description contains duplicate extension id 2 on level 0",
   4726            mSessionAns->GetLastError());
   4727 }
   4728 
   4729 TEST_F(JsepSessionTest, TestNegotiatedExtmapStability) {
   4730  AddTracks(*mSessionAns, "audio,video");
   4731 
   4732  std::string sdp =
   4733      "v=0\r\n"
   4734      "o=- 6 2 IN IP4 1r\r\n"
   4735      "t=0 0a\r\n"
   4736      "a=ice-ufrag:Xp\r\n"
   4737      "a=ice-pwd:he\r\n"
   4738      "a=setup:actpass\r\n"
   4739      "a=fingerprint:sha-256 "
   4740      "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:"
   4741      "08:D2:F7:9D:F5:E2:C1:15\r\n"
   4742      "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n"
   4743      "c=IN IP4 51.81.107.13\r\n"
   4744      "a=sendrecv\r\n"
   4745      "a=extmap:11 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
   4746      "a=fmtp:111 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n"
   4747      "a=mid:audio\r\n"
   4748      "a=rtcp-mux\r\n"
   4749      "a=rtpmap:111 opus/48000/2\r\n"
   4750      "a=setup:active\r\n"
   4751      "a=ssrc:3463672643 cname:{ec9a356a-8d2c-504e-9977-99070a51f929}\r\n"
   4752      "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n"
   4753      "c=IN IP4 0\r\n"
   4754      "a=sendrecv\r\n"
   4755      "a=rtpmap:100 VP8/90000\r\n"
   4756      "a=extmap:12 urn:ietf:params:rtp-hdrext:toffset\r\n"
   4757      "a=extmap:13 "
   4758      "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n";
   4759  auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp);
   4760  ASSERT_FALSE(result.mError.isSome());
   4761  auto answer = CreateAnswer();
   4762  result = mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
   4763  ASSERT_FALSE(result.mError.isSome());
   4764 
   4765  // Verify that we've negotiated the right extmap based on the unusual values
   4766  // in the offer.
   4767  auto transceivers = GetTransceivers(*mSessionAns);
   4768  ASSERT_EQ(2U, transceivers.size());
   4769  auto* audioSend = transceivers[0].mSendTrack.GetNegotiatedDetails();
   4770  auto* audioRecv = transceivers[0].mRecvTrack.GetNegotiatedDetails();
   4771  auto* videoSend = transceivers[1].mSendTrack.GetNegotiatedDetails();
   4772  auto* videoRecv = transceivers[1].mRecvTrack.GetNegotiatedDetails();
   4773  ASSERT_TRUE(audioSend);
   4774  ASSERT_TRUE(audioRecv);
   4775  ASSERT_TRUE(videoSend);
   4776  ASSERT_TRUE(videoRecv);
   4777  ASSERT_EQ(
   4778      11U,
   4779      audioSend->GetExt("urn:ietf:params:rtp-hdrext:ssrc-audio-level")->entry);
   4780  ASSERT_EQ(
   4781      11U,
   4782      audioRecv->GetExt("urn:ietf:params:rtp-hdrext:ssrc-audio-level")->entry);
   4783  ASSERT_EQ(12U,
   4784            videoSend->GetExt("urn:ietf:params:rtp-hdrext:toffset")->entry);
   4785  ASSERT_EQ(12U,
   4786            videoRecv->GetExt("urn:ietf:params:rtp-hdrext:toffset")->entry);
   4787  ASSERT_EQ(
   4788      13U,
   4789      videoSend
   4790          ->GetExt("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")
   4791          ->entry);
   4792  ASSERT_EQ(
   4793      13U,
   4794      videoRecv
   4795          ->GetExt("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")
   4796          ->entry);
   4797 
   4798  SwapOfferAnswerRoles();
   4799 
   4800  // Make sure a reoffer uses the negotiated extmap
   4801  auto reoffer = CreateOffer();
   4802  ASSERT_NE(std::string::npos, reoffer.find("a=extmap:11"));
   4803  ASSERT_NE(std::string::npos, reoffer.find("a=extmap:12"));
   4804  ASSERT_NE(std::string::npos, reoffer.find("a=extmap:13"));
   4805 }
   4806 
   4807 TEST_F(JsepSessionTest, TestNegotiatedExtmapCollision) {
   4808  AddTracks(*mSessionAns, "audio");
   4809  // ssrc-audio-level will be extmap 1 for both
   4810  // csrc-audio-level will be 2 for both
   4811  // mid will be 3 for both
   4812  mSessionAns->AddAudioRtpExtension("foo");
   4813  mSessionAns->AddAudioRtpExtension("bar");
   4814  mSessionAns->AddAudioRtpExtension("baz");
   4815 
   4816  // Set up an offer that uses the same extmap entries, but for different
   4817  // things, causing collisions.
   4818  std::string sdp =
   4819      "v=0\r\n"
   4820      "o=- 6 2 IN IP4 1r\r\n"
   4821      "t=0 0a\r\n"
   4822      "a=ice-ufrag:Xp\r\n"
   4823      "a=ice-pwd:he\r\n"
   4824      "a=setup:actpass\r\n"
   4825      "a=fingerprint:sha-256 "
   4826      "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:"
   4827      "08:D2:F7:9D:F5:E2:C1:15\r\n"
   4828      "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n"
   4829      "c=IN IP4 51.81.107.13\r\n"
   4830      "a=sendrecv\r\n"
   4831      "a=extmap:1 foo\r\n"
   4832      "a=extmap:2 bar\r\n"
   4833      "a=extmap:3 baz\r\n"
   4834      "a=extmap:11 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
   4835      "a=fmtp:111 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n"
   4836      "a=mid:audio\r\n"
   4837      "a=rtcp-mux\r\n"
   4838      "a=rtpmap:111 opus/48000/2\r\n"
   4839      "a=setup:active\r\n"
   4840      "a=ssrc:3463672643 cname:{ec9a356a-8d2c-504e-9977-99070a51f929}\r\n";
   4841  auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp);
   4842  ASSERT_FALSE(result.mError.isSome());
   4843  auto answer = CreateAnswer();
   4844  result = mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
   4845  ASSERT_FALSE(result.mError.isSome());
   4846 
   4847  // Verify that we've negotiated the right extmap based on the unusual values
   4848  // in the offer.
   4849  auto transceivers = GetTransceivers(*mSessionAns);
   4850  ASSERT_EQ(1U, transceivers.size());
   4851  auto* audioSend = transceivers[0].mSendTrack.GetNegotiatedDetails();
   4852  auto* audioRecv = transceivers[0].mRecvTrack.GetNegotiatedDetails();
   4853  ASSERT_TRUE(audioSend);
   4854  ASSERT_TRUE(audioRecv);
   4855  ASSERT_EQ(1U, audioSend->GetExt("foo")->entry);
   4856  ASSERT_EQ(1U, audioRecv->GetExt("foo")->entry);
   4857  ASSERT_EQ(2U, audioSend->GetExt("bar")->entry);
   4858  ASSERT_EQ(2U, audioRecv->GetExt("bar")->entry);
   4859  ASSERT_EQ(3U, audioSend->GetExt("baz")->entry);
   4860  ASSERT_EQ(3U, audioRecv->GetExt("baz")->entry);
   4861  ASSERT_EQ(
   4862      11U,
   4863      audioSend->GetExt("urn:ietf:params:rtp-hdrext:ssrc-audio-level")->entry);
   4864  ASSERT_EQ(
   4865      11U,
   4866      audioRecv->GetExt("urn:ietf:params:rtp-hdrext:ssrc-audio-level")->entry);
   4867  SwapOfferAnswerRoles();
   4868 
   4869  // Make sure a reoffer uses the negotiated extmap
   4870  auto reoffer = CreateOffer();
   4871  ASSERT_NE(std::string::npos, reoffer.find("a=extmap:1 foo"));
   4872  ASSERT_NE(std::string::npos, reoffer.find("a=extmap:2 bar"));
   4873  ASSERT_NE(std::string::npos, reoffer.find("a=extmap:3 baz"));
   4874  ASSERT_NE(
   4875      std::string::npos,
   4876      reoffer.find("a=extmap:11 urn:ietf:params:rtp-hdrext:ssrc-audio-level"));
   4877 
   4878  // Ensure no duplicates
   4879  ASSERT_EQ(std::string::npos,
   4880            reoffer.find("a=extmap:1 ", reoffer.find("a=extmap:1 ") + 1));
   4881  ASSERT_EQ(std::string::npos,
   4882            reoffer.find("a=extmap:2 ", reoffer.find("a=extmap:2 ") + 1));
   4883  ASSERT_EQ(std::string::npos,
   4884            reoffer.find("a=extmap:3 ", reoffer.find("a=extmap:3 ") + 1));
   4885  ASSERT_EQ(std::string::npos,
   4886            reoffer.find("a=extmap:11 ", reoffer.find("a=extmap:11 ") + 1));
   4887 }
   4888 
   4889 TEST_F(JsepSessionTest, TestExtmapAnswerChangesId) {
   4890  types.push_back(SdpMediaSection::kAudio);
   4891  AddTracks(*mSessionOff, "audio");
   4892  AddTracks(*mSessionAns, "audio");
   4893 
   4894  std::string offer = CreateOffer();
   4895  SetLocalOffer(offer, ALL_CHECKS);
   4896  SetRemoteOffer(offer, ALL_CHECKS);
   4897 
   4898  std::string answer = CreateAnswer();
   4899  std::string mungedAnswer =
   4900      SetExtmap(answer, "urn:ietf:params:rtp-hdrext:sdes:mid", 14);
   4901  JsepSession::Result result =
   4902      mSessionOff->SetRemoteDescription(kJsepSdpAnswer, mungedAnswer);
   4903  ASSERT_TRUE(result.mError.isSome());
   4904  ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError);
   4905  ASSERT_NE(mSessionOff->GetLastError().find(
   4906                "Answer changed id for extmap attribute at level 0"),
   4907            std::string::npos);
   4908 }
   4909 
   4910 TEST_F(JsepSessionTest, TestExtmapChangeId) {
   4911  types.push_back(SdpMediaSection::kAudio);
   4912  AddTracks(*mSessionOff, "audio");
   4913  AddTracks(*mSessionAns, "audio");
   4914 
   4915  OfferAnswer();
   4916 
   4917  // RFC 5285 does not seem to forbid giving a pre-existing extension a new id,
   4918  // as long as that id has never been used before
   4919  {
   4920    std::string offer = CreateOffer();
   4921    SetLocalOffer(offer, ALL_CHECKS);
   4922    uint16_t oldId = 0;
   4923    std::string mungedOffer =
   4924        SetExtmap(offer, "urn:ietf:params:rtp-hdrext:sdes:mid", 14, &oldId);
   4925    ASSERT_NE(oldId, 0);
   4926    SetRemoteOffer(mungedOffer, ALL_CHECKS);
   4927 
   4928    std::string answer = CreateAnswer();
   4929    SetLocalAnswer(answer, ALL_CHECKS);
   4930 
   4931    std::string mungedAnswer =
   4932        SetExtmap(answer, "urn:ietf:params:rtp-hdrext:sdes:mid", oldId);
   4933    SetRemoteAnswer(mungedAnswer, ALL_CHECKS);
   4934  }
   4935 
   4936  // Make sure going back to the previous id works
   4937  OfferAnswer();
   4938 }
   4939 
   4940 TEST_F(JsepSessionTest, TestExtmapSwap) {
   4941  types.push_back(SdpMediaSection::kAudio);
   4942  AddTracks(*mSessionOff, "audio");
   4943  AddTracks(*mSessionAns, "audio");
   4944 
   4945  OfferAnswer();
   4946 
   4947  std::string offer = CreateOffer();
   4948  uint16_t midId = GetExtmap(offer, "urn:ietf:params:rtp-hdrext:sdes:mid");
   4949  uint16_t ssrcLevelId = 0;
   4950  std::string mungedOffer =
   4951      SetExtmap(offer, "urn:ietf:params:rtp-hdrext:ssrc-audio-level", midId,
   4952                &ssrcLevelId);
   4953  mungedOffer = SetExtmap(mungedOffer, "urn:ietf:params:rtp-hdrext:sdes:mid",
   4954                          ssrcLevelId);
   4955 
   4956  JsepSession::Result result =
   4957      mSessionAns->SetRemoteDescription(kJsepSdpOffer, mungedOffer);
   4958  ASSERT_TRUE(result.mError.isSome());
   4959  ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError);
   4960  ASSERT_NE(mSessionAns->GetLastError().find(
   4961                "Remote description attempted to remap RTP extension id"),
   4962            std::string::npos);
   4963 }
   4964 
   4965 TEST_F(JsepSessionTest, TestExtmapReuse) {
   4966  types.push_back(SdpMediaSection::kAudio);
   4967  AddTracks(*mSessionOff, "audio");
   4968  AddTracks(*mSessionAns, "audio");
   4969 
   4970  OfferAnswer();
   4971 
   4972  std::string offer = CreateOffer();
   4973  UniquePtr<Sdp> munge(Parse(offer));
   4974  ASSERT_EQ(1U, munge->GetMediaSectionCount());
   4975 
   4976  auto& offerMediaAttrs = munge->GetMediaSection(0).GetAttributeList();
   4977  ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   4978  auto offerExtmap = offerMediaAttrs.GetExtmap();
   4979  for (auto& ext : offerExtmap.mExtmaps) {
   4980    if (ext.extensionname == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") {
   4981      ext.extensionname = "foo";
   4982    }
   4983  }
   4984 
   4985  offerMediaAttrs.SetAttribute(offerExtmap.Clone());
   4986 
   4987  std::string sdpString = munge->ToString();
   4988  JsepSession::Result result =
   4989      mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdpString);
   4990  ASSERT_TRUE(result.mError.isSome());
   4991  ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError);
   4992  ASSERT_NE(mSessionAns->GetLastError().find(
   4993                "Remote description attempted to remap RTP extension id"),
   4994            std::string::npos);
   4995 }
   4996 
   4997 TEST_F(JsepSessionTest, TestExtmapReuseAfterRenegotiation) {
   4998  types.push_back(SdpMediaSection::kAudio);
   4999  AddTracks(*mSessionOff, "audio");
   5000  AddTracks(*mSessionAns, "audio");
   5001 
   5002  OfferAnswer();
   5003 
   5004  // Renegotiate without ssrc-audio-level
   5005  {
   5006    std::string offer = CreateOffer();
   5007    SetLocalOffer(offer, ALL_CHECKS);
   5008    // Passing 0 removes urn:ietf:params:rtp-hdrext:ssrc-audio-level
   5009    std::string mungedOffer =
   5010        SetExtmap(offer, "urn:ietf:params:rtp-hdrext:ssrc-audio-level", 0);
   5011    SetRemoteOffer(mungedOffer, ALL_CHECKS);
   5012 
   5013    std::string answer = CreateAnswer();
   5014    SetLocalAnswer(answer, ALL_CHECKS);
   5015    SetRemoteAnswer(answer, ALL_CHECKS);
   5016  }
   5017 
   5018  // Make sure trying to reuse the id for ssrc-audio-level fails, even though we
   5019  // did not use it last round.
   5020  {
   5021    std::string offer = CreateOffer();
   5022    UniquePtr<Sdp> munge(Parse(offer));
   5023    ASSERT_EQ(1U, munge->GetMediaSectionCount());
   5024 
   5025    auto& offerMediaAttrs = munge->GetMediaSection(0).GetAttributeList();
   5026    ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   5027    auto offerExtmap = offerMediaAttrs.GetExtmap();
   5028    for (auto& ext : offerExtmap.mExtmaps) {
   5029      if (ext.extensionname == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") {
   5030        ext.extensionname = "foo";
   5031      }
   5032    }
   5033 
   5034    offerMediaAttrs.SetAttribute(offerExtmap.Clone());
   5035 
   5036    std::string sdpString = munge->ToString();
   5037    JsepSession::Result result =
   5038        mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdpString);
   5039    ASSERT_TRUE(result.mError.isSome());
   5040    ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError);
   5041    ASSERT_NE(mSessionAns->GetLastError().find(
   5042                  "Remote description attempted to remap RTP extension id"),
   5043              std::string::npos);
   5044  }
   5045 }
   5046 
   5047 TEST_F(JsepSessionTest, TestRtcpFbStar) {
   5048  AddTracks(*mSessionOff, "video");
   5049  AddTracks(*mSessionAns, "video");
   5050 
   5051  std::string offer = CreateOffer();
   5052 
   5053  UniquePtr<Sdp> parsedOffer(Parse(offer));
   5054  auto* rtcpfbs = new SdpRtcpFbAttributeList;
   5055  rtcpfbs->PushEntry("*", SdpRtcpFbAttributeList::kNack);
   5056  parsedOffer->GetMediaSection(0).GetAttributeList().SetAttribute(rtcpfbs);
   5057  offer = parsedOffer->ToString();
   5058 
   5059  SetLocalOffer(offer, CHECK_SUCCESS);
   5060  SetRemoteOffer(offer, CHECK_SUCCESS);
   5061  std::string answer = CreateAnswer();
   5062  SetLocalAnswer(answer, CHECK_SUCCESS);
   5063  SetRemoteAnswer(answer, CHECK_SUCCESS);
   5064 
   5065  ASSERT_EQ(1U, GetRemoteTracks(*mSessionAns).size());
   5066  JsepTrack track = GetRemoteTracks(*mSessionAns)[0];
   5067  ASSERT_TRUE(track.GetNegotiatedDetails());
   5068  auto* details = track.GetNegotiatedDetails();
   5069  for (const auto& codec : details->GetEncoding(0).GetCodecs()) {
   5070    const JsepVideoCodecDescription* videoCodec =
   5071        static_cast<const JsepVideoCodecDescription*>(codec.get());
   5072    ASSERT_EQ(1U, videoCodec->mNackFbTypes.size());
   5073    ASSERT_EQ("", videoCodec->mNackFbTypes[0]);
   5074  }
   5075 }
   5076 
   5077 TEST_F(JsepSessionTest, TestUniqueReceivePayloadTypes) {
   5078  // The audio payload types will all appear more than once.
   5079  // For the offerer, only one video m-section will be receiving, so those
   5080  // video payload types will be unique.
   5081  // On the other hand, the answerer will have two video m-sections receiving,
   5082  // so those _won't_ be unique.
   5083  AddTracks(*mSessionOff, "audio,audio,video,video");
   5084  AddTracks(*mSessionAns, "audio,audio,video");
   5085 
   5086  std::string offer = CreateOffer();
   5087  SetLocalOffer(offer, CHECK_SUCCESS);
   5088  SetRemoteOffer(offer, CHECK_SUCCESS);
   5089  std::string answer = CreateAnswer();
   5090  SetLocalAnswer(answer, CHECK_SUCCESS);
   5091  SetRemoteAnswer(answer, CHECK_SUCCESS);
   5092 
   5093  auto offerTransceivers = GetTransceivers(*mSessionOff);
   5094  auto answerTransceivers = GetTransceivers(*mSessionAns);
   5095  ASSERT_EQ(4U, offerTransceivers.size());
   5096  ASSERT_EQ(4U, answerTransceivers.size());
   5097 
   5098  ASSERT_FALSE(IsNull(offerTransceivers[0].mRecvTrack));
   5099  ASSERT_TRUE(offerTransceivers[0].mRecvTrack.GetNegotiatedDetails());
   5100  ASSERT_THAT(offerTransceivers[0].mRecvTrack.GetUniqueReceivePayloadTypes(),
   5101              UnorderedElementsAre());
   5102  ASSERT_THAT(offerTransceivers[0].mRecvTrack.GetOtherReceivePayloadTypes(),
   5103              UnorderedElementsAre(0, 8, 9, 101, 109, 97, 99, 103, 105, 120,
   5104                                   121, 122, 123, 126));
   5105 
   5106  ASSERT_FALSE(IsNull(offerTransceivers[1].mRecvTrack));
   5107  ASSERT_TRUE(offerTransceivers[1].mRecvTrack.GetNegotiatedDetails());
   5108  ASSERT_THAT(offerTransceivers[1].mRecvTrack.GetUniqueReceivePayloadTypes(),
   5109              UnorderedElementsAre());
   5110  ASSERT_THAT(offerTransceivers[1].mRecvTrack.GetOtherReceivePayloadTypes(),
   5111              UnorderedElementsAre(0, 8, 9, 101, 109, 97, 99, 103, 105, 120,
   5112                                   121, 122, 123, 126));
   5113 
   5114  ASSERT_FALSE(IsNull(offerTransceivers[2].mRecvTrack));
   5115  ASSERT_TRUE(offerTransceivers[2].mRecvTrack.GetNegotiatedDetails());
   5116  ASSERT_THAT(offerTransceivers[2].mRecvTrack.GetUniqueReceivePayloadTypes(),
   5117              UnorderedElementsAre(97, 99, 103, 105, 120, 121, 122, 123, 126));
   5118  ASSERT_THAT(offerTransceivers[2].mRecvTrack.GetOtherReceivePayloadTypes(),
   5119              UnorderedElementsAre(0, 8, 9, 101, 109));
   5120 
   5121  ASSERT_TRUE(IsNull(offerTransceivers[3].mRecvTrack));
   5122  ASSERT_TRUE(offerTransceivers[3].mRecvTrack.GetNegotiatedDetails());
   5123  ASSERT_THAT(offerTransceivers[3].mRecvTrack.GetUniqueReceivePayloadTypes(),
   5124              UnorderedElementsAre());
   5125  ASSERT_THAT(offerTransceivers[3].mRecvTrack.GetOtherReceivePayloadTypes(),
   5126              UnorderedElementsAre());
   5127 
   5128  ASSERT_FALSE(IsNull(answerTransceivers[0].mRecvTrack));
   5129  ASSERT_TRUE(answerTransceivers[0].mRecvTrack.GetNegotiatedDetails());
   5130  ASSERT_THAT(answerTransceivers[0].mRecvTrack.GetUniqueReceivePayloadTypes(),
   5131              UnorderedElementsAre());
   5132  ASSERT_THAT(answerTransceivers[0].mRecvTrack.GetOtherReceivePayloadTypes(),
   5133              UnorderedElementsAre(0, 8, 9, 101, 109, 99, 105, 120, 121, 122,
   5134                                   123, 126));
   5135 
   5136  ASSERT_FALSE(IsNull(answerTransceivers[1].mRecvTrack));
   5137  ASSERT_TRUE(answerTransceivers[1].mRecvTrack.GetNegotiatedDetails());
   5138  ASSERT_THAT(answerTransceivers[1].mRecvTrack.GetUniqueReceivePayloadTypes(),
   5139              UnorderedElementsAre());
   5140  ASSERT_THAT(answerTransceivers[1].mRecvTrack.GetOtherReceivePayloadTypes(),
   5141              UnorderedElementsAre(0, 8, 9, 101, 109, 99, 105, 120, 121, 122,
   5142                                   123, 126));
   5143 
   5144  // Answerer is receiving two video streams with the same payload types.
   5145  // Neither recv track should have unique pts.
   5146  ASSERT_FALSE(IsNull(answerTransceivers[2].mRecvTrack));
   5147  ASSERT_TRUE(answerTransceivers[2].mRecvTrack.GetNegotiatedDetails());
   5148  ASSERT_THAT(answerTransceivers[2].mRecvTrack.GetUniqueReceivePayloadTypes(),
   5149              UnorderedElementsAre());
   5150  ASSERT_THAT(answerTransceivers[2].mRecvTrack.GetOtherReceivePayloadTypes(),
   5151              UnorderedElementsAre(0, 8, 9, 101, 109, 99, 105, 120, 121, 122,
   5152                                   123, 126));
   5153 
   5154  ASSERT_FALSE(IsNull(answerTransceivers[3].mRecvTrack));
   5155  ASSERT_TRUE(answerTransceivers[3].mRecvTrack.GetNegotiatedDetails());
   5156  ASSERT_THAT(answerTransceivers[3].mRecvTrack.GetUniqueReceivePayloadTypes(),
   5157              UnorderedElementsAre());
   5158  ASSERT_THAT(answerTransceivers[3].mRecvTrack.GetOtherReceivePayloadTypes(),
   5159              UnorderedElementsAre(0, 8, 9, 101, 109, 99, 105, 120, 121, 122,
   5160                                   123, 126));
   5161 }
   5162 
   5163 TEST_F(JsepSessionTest, UnknownFingerprintAlgorithm) {
   5164  types.push_back(SdpMediaSection::kAudio);
   5165  AddTracks(*mSessionOff, "audio");
   5166  AddTracks(*mSessionAns, "audio");
   5167 
   5168  std::string offer(CreateOffer());
   5169  SetLocalOffer(offer);
   5170  ReplaceAll("fingerprint:sha", "fingerprint:foo", &offer);
   5171  JsepSession::Result result =
   5172      mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   5173  ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError);
   5174  ASSERT_NE("", mSessionAns->GetLastError());
   5175 }
   5176 
   5177 TEST(H264ProfileLevelIdTest, TestLevelComparisons)
   5178 {
   5179  ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x421D0B),   // 1b
   5180            JsepVideoCodecDescription::GetSaneH264Level(0x420D0B));  // 1.1
   5181  ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x420D0A),   // 1.0
   5182            JsepVideoCodecDescription::GetSaneH264Level(0x421D0B));  // 1b
   5183  ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x420D0A),   // 1.0
   5184            JsepVideoCodecDescription::GetSaneH264Level(0x420D0B));  // 1.1
   5185 
   5186  ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x640009),   // 1b
   5187            JsepVideoCodecDescription::GetSaneH264Level(0x64000B));  // 1.1
   5188  ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x64000A),   // 1.0
   5189            JsepVideoCodecDescription::GetSaneH264Level(0x640009));  // 1b
   5190  ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x64000A),   // 1.0
   5191            JsepVideoCodecDescription::GetSaneH264Level(0x64000B));  // 1.1
   5192 }
   5193 
   5194 TEST(H264ProfileLevelIdTest, TestLevelSetting)
   5195 {
   5196  uint32_t profileLevelId = 0x420D0A;
   5197  JsepVideoCodecDescription::SetSaneH264Level(
   5198      JsepVideoCodecDescription::GetSaneH264Level(0x42100B), &profileLevelId);
   5199  ASSERT_EQ((uint32_t)0x421D0B, profileLevelId);
   5200 
   5201  JsepVideoCodecDescription::SetSaneH264Level(
   5202      JsepVideoCodecDescription::GetSaneH264Level(0x42000A), &profileLevelId);
   5203  ASSERT_EQ((uint32_t)0x420D0A, profileLevelId);
   5204 
   5205  profileLevelId = 0x6E100A;
   5206  JsepVideoCodecDescription::SetSaneH264Level(
   5207      JsepVideoCodecDescription::GetSaneH264Level(0x640009), &profileLevelId);
   5208  ASSERT_EQ((uint32_t)0x6E1009, profileLevelId);
   5209 
   5210  JsepVideoCodecDescription::SetSaneH264Level(
   5211      JsepVideoCodecDescription::GetSaneH264Level(0x64000B), &profileLevelId);
   5212  ASSERT_EQ((uint32_t)0x6E100B, profileLevelId);
   5213 }
   5214 
   5215 TEST_F(JsepSessionTest, StronglyPreferredCodec) {
   5216  for (auto& codec : mSessionAns->Codecs()) {
   5217    if (codec->mName == "H264") {
   5218      codec->mStronglyPreferred = true;
   5219    }
   5220  }
   5221 
   5222  types.push_back(SdpMediaSection::kVideo);
   5223  AddTracks(*mSessionOff, "video");
   5224  AddTracks(*mSessionAns, "video");
   5225 
   5226  OfferAnswer();
   5227 
   5228  UniquePtr<JsepCodecDescription> codec;
   5229  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   5230  ASSERT_TRUE(codec);
   5231  ASSERT_EQ("H264", codec->mName);
   5232  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   5233  ASSERT_TRUE(codec);
   5234  ASSERT_EQ("H264", codec->mName);
   5235 }
   5236 
   5237 TEST_F(JsepSessionTest, LowDynamicPayloadType) {
   5238  SetPayloadTypeNumber(*mSessionOff, "opus", "12");
   5239  types.push_back(SdpMediaSection::kAudio);
   5240  AddTracks(*mSessionOff, "audio");
   5241  AddTracks(*mSessionAns, "audio");
   5242 
   5243  OfferAnswer();
   5244  UniquePtr<JsepCodecDescription> codec;
   5245  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   5246  ASSERT_TRUE(codec);
   5247  ASSERT_EQ("opus", codec->mName);
   5248  ASSERT_EQ("12", codec->mDefaultPt);
   5249  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   5250  ASSERT_TRUE(codec);
   5251  ASSERT_EQ("opus", codec->mName);
   5252  ASSERT_EQ("12", codec->mDefaultPt);
   5253 }
   5254 
   5255 TEST_F(JsepSessionTest, TestOfferPTAsymmetry) {
   5256  SetPayloadTypeNumber(*mSessionAns, "opus", "105");
   5257  types.push_back(SdpMediaSection::kAudio);
   5258  AddTracks(*mSessionOff, "audio");
   5259  AddTracks(*mSessionAns, "audio");
   5260  JsepOfferOptions options;
   5261 
   5262  // Ensure that mSessionAns is appropriately configured. Also ensure that
   5263  // creating an offer with 105 does not prompt mSessionAns to ignore the
   5264  // PT in the offer.
   5265  std::string offer;
   5266  JsepSession::Result result = mSessionAns->CreateOffer(options, &offer);
   5267  ASSERT_FALSE(result.mError.isSome());
   5268  ASSERT_NE(std::string::npos, offer.find("a=rtpmap:105 opus")) << offer;
   5269 
   5270  OfferAnswer();
   5271 
   5272  // Answerer should use what the offerer suggested
   5273  UniquePtr<JsepCodecDescription> codec;
   5274  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   5275  ASSERT_TRUE(codec);
   5276  ASSERT_EQ("opus", codec->mName);
   5277  ASSERT_EQ("109", codec->mDefaultPt);
   5278  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   5279  ASSERT_TRUE(codec);
   5280  ASSERT_EQ("opus", codec->mName);
   5281  ASSERT_EQ("109", codec->mDefaultPt);
   5282 
   5283  // Answerer should not change when it reoffers
   5284  result = mSessionAns->CreateOffer(options, &offer);
   5285  ASSERT_FALSE(result.mError.isSome());
   5286  ASSERT_NE(std::string::npos, offer.find("a=rtpmap:109 opus")) << offer;
   5287 }
   5288 
   5289 TEST_F(JsepSessionTest, TestAnswerPTAsymmetry) {
   5290  // JsepSessionImpl will never answer with an asymmetric payload type
   5291  // (tested in TestOfferPTAsymmetry), so we have to rewrite SDP a little.
   5292  types.push_back(SdpMediaSection::kAudio);
   5293  AddTracks(*mSessionOff, "audio");
   5294  AddTracks(*mSessionAns, "audio");
   5295 
   5296  std::string offer = CreateOffer();
   5297  SetLocalOffer(offer);
   5298 
   5299  Replace("a=rtpmap:109 opus", "a=rtpmap:105 opus", &offer);
   5300  Replace("m=audio 9 UDP/TLS/RTP/SAVPF 109", "m=audio 9 UDP/TLS/RTP/SAVPF 105",
   5301          &offer);
   5302  ReplaceAll("a=fmtp:109", "a=fmtp:105", &offer);
   5303  SetRemoteOffer(offer);
   5304 
   5305  std::string answer = CreateAnswer();
   5306  SetLocalAnswer(answer);
   5307  SetRemoteAnswer(answer);
   5308 
   5309  UniquePtr<JsepCodecDescription> codec;
   5310  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &codec);
   5311  ASSERT_TRUE(codec);
   5312  ASSERT_EQ("opus", codec->mName);
   5313  ASSERT_EQ("105", codec->mDefaultPt);
   5314  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &codec);
   5315  ASSERT_TRUE(codec);
   5316  ASSERT_EQ("opus", codec->mName);
   5317  ASSERT_EQ("109", codec->mDefaultPt);
   5318 
   5319  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   5320  ASSERT_TRUE(codec);
   5321  ASSERT_EQ("opus", codec->mName);
   5322  ASSERT_EQ("105", codec->mDefaultPt);
   5323  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   5324  ASSERT_TRUE(codec);
   5325  ASSERT_EQ("opus", codec->mName);
   5326  ASSERT_EQ("105", codec->mDefaultPt);
   5327 
   5328  // Offerer should use 105 for reoffers
   5329  offer = CreateOffer();
   5330  ASSERT_NE(std::string::npos, offer.find("a=rtpmap:105 opus")) << offer;
   5331  ASSERT_EQ(std::string::npos, offer.find("a=rtpmap:109 opus")) << offer;
   5332  ASSERT_NE(std::string::npos, offer.find("a=fmtp:105")) << offer;
   5333  ASSERT_EQ(std::string::npos, offer.find("a=fmtp:109")) << offer;
   5334 }
   5335 
   5336 TEST_F(JsepSessionTest, PayloadTypeClash) {
   5337  // Set up a scenario where mSessionAns' favorite codec (opus) is unsupported
   5338  // by mSessionOff, and mSessionOff uses that payload type for something else.
   5339  SetCodecEnabled(*mSessionOff, "opus", false);
   5340  SetPayloadTypeNumber(*mSessionOff, "opus", "0");
   5341  SetPayloadTypeNumber(*mSessionOff, "G722", "109");
   5342  SetPayloadTypeNumber(*mSessionAns, "opus", "109");
   5343  types.push_back(SdpMediaSection::kAudio);
   5344  AddTracks(*mSessionOff, "audio");
   5345  AddTracks(*mSessionAns, "audio");
   5346 
   5347  OfferAnswer();
   5348  UniquePtr<JsepCodecDescription> codec;
   5349  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   5350  ASSERT_TRUE(codec);
   5351  ASSERT_EQ("G722", codec->mName);
   5352  ASSERT_EQ("109", codec->mDefaultPt);
   5353  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   5354  ASSERT_TRUE(codec);
   5355  ASSERT_EQ("G722", codec->mName);
   5356  ASSERT_EQ("109", codec->mDefaultPt);
   5357 
   5358  // Now, make sure that mSessionAns does not put a=rtpmap:109 opus in a
   5359  // reoffer, since pt 109 is taken for PCMU (the answerer still supports opus,
   5360  // and will reoffer it, but it should choose a new payload type for it)
   5361  JsepOfferOptions options;
   5362  std::string reoffer;
   5363  JsepSession::Result result = mSessionAns->CreateOffer(options, &reoffer);
   5364  ASSERT_FALSE(result.mError.isSome());
   5365  ASSERT_EQ(std::string::npos, reoffer.find("a=rtpmap:109 opus")) << reoffer;
   5366  ASSERT_NE(std::string::npos, reoffer.find(" opus")) << reoffer;
   5367 }
   5368 
   5369 TEST_P(JsepSessionTest, TestGlareRollback) {
   5370  AddTracks(*mSessionOff);
   5371  AddTracks(*mSessionAns);
   5372  JsepOfferOptions options;
   5373 
   5374  std::string offer;
   5375  ASSERT_FALSE(mSessionAns->CreateOffer(options, &offer).mError.isSome());
   5376  ASSERT_FALSE(
   5377      mSessionAns->SetLocalDescription(kJsepSdpOffer, offer).mError.isSome());
   5378  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionAns->GetState());
   5379 
   5380  ASSERT_FALSE(mSessionOff->CreateOffer(options, &offer).mError.isSome());
   5381  ASSERT_FALSE(
   5382      mSessionOff->SetLocalDescription(kJsepSdpOffer, offer).mError.isSome());
   5383  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   5384 
   5385  ASSERT_EQ(dom::PCError::InvalidStateError,
   5386            *mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer).mError);
   5387  ASSERT_FALSE(
   5388      mSessionAns->SetLocalDescription(kJsepSdpRollback, "").mError.isSome());
   5389  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   5390 
   5391  SetRemoteOffer(offer);
   5392 
   5393  std::string answer = CreateAnswer();
   5394  SetLocalAnswer(answer);
   5395  SetRemoteAnswer(answer);
   5396 }
   5397 
   5398 TEST_P(JsepSessionTest, TestRejectOfferRollback) {
   5399  AddTracks(*mSessionOff);
   5400  AddTracks(*mSessionAns);
   5401 
   5402  std::string offer = CreateOffer();
   5403  SetLocalOffer(offer);
   5404  SetRemoteOffer(offer);
   5405 
   5406  ASSERT_FALSE(
   5407      mSessionAns->SetRemoteDescription(kJsepSdpRollback, "").mError.isSome());
   5408  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   5409  for (const auto& transceiver : GetTransceivers(*mSessionAns)) {
   5410    ASSERT_EQ(0U, transceiver.mRecvTrack.GetStreamIds().size());
   5411  }
   5412 
   5413  ASSERT_FALSE(
   5414      mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError.isSome());
   5415  ASSERT_EQ(kJsepStateStable, mSessionOff->GetState());
   5416 
   5417  OfferAnswer();
   5418 }
   5419 
   5420 TEST_P(JsepSessionTest, TestInvalidRollback) {
   5421  AddTracks(*mSessionOff);
   5422  AddTracks(*mSessionAns);
   5423 
   5424  ASSERT_EQ(dom::PCError::InvalidStateError,
   5425            *mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError);
   5426  ASSERT_EQ(dom::PCError::InvalidStateError,
   5427            *mSessionOff->SetRemoteDescription(kJsepSdpRollback, "").mError);
   5428 
   5429  std::string offer = CreateOffer();
   5430  ASSERT_EQ(dom::PCError::InvalidStateError,
   5431            *mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError);
   5432  ASSERT_EQ(dom::PCError::InvalidStateError,
   5433            *mSessionOff->SetRemoteDescription(kJsepSdpRollback, "").mError);
   5434 
   5435  SetLocalOffer(offer);
   5436  ASSERT_EQ(dom::PCError::InvalidStateError,
   5437            *mSessionOff->SetRemoteDescription(kJsepSdpRollback, "").mError);
   5438 
   5439  SetRemoteOffer(offer);
   5440  ASSERT_EQ(dom::PCError::InvalidStateError,
   5441            *mSessionAns->SetLocalDescription(kJsepSdpRollback, "").mError);
   5442 
   5443  std::string answer = CreateAnswer();
   5444  ASSERT_EQ(dom::PCError::InvalidStateError,
   5445            *mSessionAns->SetLocalDescription(kJsepSdpRollback, "").mError);
   5446 
   5447  SetLocalAnswer(answer);
   5448  ASSERT_EQ(dom::PCError::InvalidStateError,
   5449            *mSessionAns->SetLocalDescription(kJsepSdpRollback, "").mError);
   5450  ASSERT_EQ(dom::PCError::InvalidStateError,
   5451            *mSessionAns->SetRemoteDescription(kJsepSdpRollback, "").mError);
   5452 
   5453  SetRemoteAnswer(answer);
   5454  ASSERT_EQ(dom::PCError::InvalidStateError,
   5455            *mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError);
   5456  ASSERT_EQ(dom::PCError::InvalidStateError,
   5457            *mSessionOff->SetRemoteDescription(kJsepSdpRollback, "").mError);
   5458 }
   5459 
   5460 size_t GetActiveTransportCount(const JsepSession& session) {
   5461  size_t activeTransportCount = 0;
   5462  for (const auto& transceiver : JsepSessionTest::GetTransceivers(session)) {
   5463    if (!transceiver.HasBundleLevel() ||
   5464        (transceiver.BundleLevel() == transceiver.GetLevel())) {
   5465      activeTransportCount += transceiver.mTransport.mComponents;
   5466    }
   5467  }
   5468  return activeTransportCount;
   5469 }
   5470 
   5471 TEST_P(JsepSessionTest, TestBalancedBundle) {
   5472  AddTracks(*mSessionOff);
   5473  AddTracks(*mSessionAns);
   5474 
   5475  mSessionOff->SetBundlePolicy(kBundleBalanced);
   5476 
   5477  std::string offer = CreateOffer();
   5478  UniquePtr<Sdp> parsedOffer = std::move(SipccSdpParser().Parse(offer)->Sdp());
   5479 
   5480  ASSERT_TRUE(parsedOffer.get());
   5481 
   5482  std::map<SdpMediaSection::MediaType, SdpMediaSection*> firstByType;
   5483 
   5484  for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) {
   5485    SdpMediaSection& msection(parsedOffer->GetMediaSection(i));
   5486    bool firstOfType = !firstByType.count(msection.GetMediaType());
   5487    if (firstOfType) {
   5488      firstByType[msection.GetMediaType()] = &msection;
   5489    }
   5490    ASSERT_EQ(!firstOfType, msection.GetAttributeList().HasAttribute(
   5491                                SdpAttribute::kBundleOnlyAttribute));
   5492  }
   5493 
   5494  SetLocalOffer(offer);
   5495  SetRemoteOffer(offer);
   5496  std::string answer = CreateAnswer();
   5497  SetLocalAnswer(answer);
   5498  SetRemoteAnswer(answer);
   5499 
   5500  CheckTransceiversAreBundled(*mSessionOff, "Offerer transceivers");
   5501  CheckTransceiversAreBundled(*mSessionAns, "Answerer transceivers");
   5502  EXPECT_EQ(1U, GetActiveTransportCount(*mSessionOff));
   5503  EXPECT_EQ(1U, GetActiveTransportCount(*mSessionAns));
   5504 }
   5505 
   5506 TEST_P(JsepSessionTest, TestMaxBundle) {
   5507  AddTracks(*mSessionOff);
   5508  AddTracks(*mSessionAns);
   5509 
   5510  mSessionOff->SetBundlePolicy(kBundleMaxBundle);
   5511  std::string offer = CreateOffer();
   5512  UniquePtr<Sdp> parsedOffer = std::move(SipccSdpParser().Parse(offer)->Sdp());
   5513 
   5514  ASSERT_TRUE(parsedOffer.get());
   5515 
   5516  ASSERT_FALSE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
   5517      SdpAttribute::kBundleOnlyAttribute));
   5518  ASSERT_NE(0U, parsedOffer->GetMediaSection(0).GetPort());
   5519  for (size_t i = 1; i < parsedOffer->GetMediaSectionCount(); ++i) {
   5520    ASSERT_TRUE(parsedOffer->GetMediaSection(i).GetAttributeList().HasAttribute(
   5521        SdpAttribute::kBundleOnlyAttribute));
   5522    ASSERT_EQ(0U, parsedOffer->GetMediaSection(i).GetPort());
   5523  }
   5524 
   5525  SetLocalOffer(offer);
   5526  for (const auto& transceiver : GetTransceivers(*mSessionOff)) {
   5527    if (transceiver.GetLevel() == 0) {
   5528      // We do not set the bundle-level in have-local-offer unless the
   5529      // m-section is bundle-only.
   5530      ASSERT_FALSE(transceiver.HasBundleLevel());
   5531    } else {
   5532      ASSERT_TRUE(transceiver.HasBundleLevel());
   5533      ASSERT_EQ(0U, transceiver.BundleLevel());
   5534    }
   5535    ASSERT_NE("", transceiver.mTransport.mTransportId);
   5536  }
   5537 
   5538  SetRemoteOffer(offer);
   5539  std::string answer = CreateAnswer();
   5540  SetLocalAnswer(answer);
   5541  SetRemoteAnswer(answer);
   5542 
   5543  CheckTransceiversAreBundled(*mSessionOff, "Offerer transceivers");
   5544  CheckTransceiversAreBundled(*mSessionAns, "Answerer transceivers");
   5545  EXPECT_EQ(1U, GetActiveTransportCount(*mSessionOff));
   5546  EXPECT_EQ(1U, GetActiveTransportCount(*mSessionAns));
   5547 }
   5548 
   5549 TEST_F(JsepSessionTest, TestNonDefaultProtocol) {
   5550  AddTracks(*mSessionOff, "audio,video,datachannel");
   5551  AddTracks(*mSessionAns, "audio,video,datachannel");
   5552 
   5553  std::string offer;
   5554  ASSERT_FALSE(
   5555      mSessionOff->CreateOffer(JsepOfferOptions(), &offer).mError.isSome());
   5556  offer.replace(offer.find("UDP/TLS/RTP/SAVPF"), strlen("UDP/TLS/RTP/SAVPF"),
   5557                "RTP/SAVPF");
   5558  offer.replace(offer.find("UDP/TLS/RTP/SAVPF"), strlen("UDP/TLS/RTP/SAVPF"),
   5559                "RTP/SAVPF");
   5560  mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
   5561  mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   5562 
   5563  std::string answer;
   5564  mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer);
   5565  UniquePtr<Sdp> parsedAnswer = Parse(answer);
   5566  ASSERT_EQ(3U, parsedAnswer->GetMediaSectionCount());
   5567  ASSERT_EQ(SdpMediaSection::kRtpSavpf,
   5568            parsedAnswer->GetMediaSection(0).GetProtocol());
   5569  ASSERT_EQ(SdpMediaSection::kRtpSavpf,
   5570            parsedAnswer->GetMediaSection(1).GetProtocol());
   5571 
   5572  mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
   5573  mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
   5574 
   5575  // Make sure reoffer uses the same protocol as before
   5576  mSessionOff->CreateOffer(JsepOfferOptions(), &offer);
   5577  UniquePtr<Sdp> parsedOffer = Parse(offer);
   5578  ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount());
   5579  ASSERT_EQ(SdpMediaSection::kRtpSavpf,
   5580            parsedOffer->GetMediaSection(0).GetProtocol());
   5581  ASSERT_EQ(SdpMediaSection::kRtpSavpf,
   5582            parsedOffer->GetMediaSection(1).GetProtocol());
   5583 
   5584  // Make sure reoffer from other side uses the same protocol as before
   5585  mSessionAns->CreateOffer(JsepOfferOptions(), &offer);
   5586  parsedOffer = Parse(offer);
   5587  ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount());
   5588  ASSERT_EQ(SdpMediaSection::kRtpSavpf,
   5589            parsedOffer->GetMediaSection(0).GetProtocol());
   5590  ASSERT_EQ(SdpMediaSection::kRtpSavpf,
   5591            parsedOffer->GetMediaSection(1).GetProtocol());
   5592 }
   5593 
   5594 TEST_F(JsepSessionTest, CreateOfferNoVideoStreamRecvVideo) {
   5595  types.push_back(SdpMediaSection::kAudio);
   5596  AddTracks(*mSessionOff, "audio");
   5597 
   5598  JsepOfferOptions options;
   5599  options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   5600  options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   5601 
   5602  CreateOffer(Some(options));
   5603 }
   5604 
   5605 TEST_F(JsepSessionTest, CreateOfferNoAudioStreamRecvAudio) {
   5606  types.push_back(SdpMediaSection::kVideo);
   5607  AddTracks(*mSessionOff, "video");
   5608 
   5609  JsepOfferOptions options;
   5610  options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   5611  options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   5612 
   5613  CreateOffer(Some(options));
   5614 }
   5615 
   5616 TEST_F(JsepSessionTest, CreateOfferNoVideoStream) {
   5617  types.push_back(SdpMediaSection::kAudio);
   5618  AddTracks(*mSessionOff, "audio");
   5619 
   5620  JsepOfferOptions options;
   5621  options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   5622  options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U));
   5623 
   5624  CreateOffer(Some(options));
   5625 }
   5626 
   5627 TEST_F(JsepSessionTest, CreateOfferNoAudioStream) {
   5628  types.push_back(SdpMediaSection::kVideo);
   5629  AddTracks(*mSessionOff, "video");
   5630 
   5631  JsepOfferOptions options;
   5632  options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U));
   5633  options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   5634 
   5635  CreateOffer(Some(options));
   5636 }
   5637 
   5638 TEST_F(JsepSessionTest, CreateOfferDontReceiveAudio) {
   5639  types.push_back(SdpMediaSection::kAudio);
   5640  types.push_back(SdpMediaSection::kVideo);
   5641  AddTracks(*mSessionOff, "audio,video");
   5642 
   5643  JsepOfferOptions options;
   5644  options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U));
   5645  options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   5646 
   5647  CreateOffer(Some(options));
   5648 }
   5649 
   5650 TEST_F(JsepSessionTest, CreateOfferDontReceiveVideo) {
   5651  types.push_back(SdpMediaSection::kAudio);
   5652  types.push_back(SdpMediaSection::kVideo);
   5653  AddTracks(*mSessionOff, "audio,video");
   5654 
   5655  JsepOfferOptions options;
   5656  options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   5657  options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U));
   5658 
   5659  CreateOffer(Some(options));
   5660 }
   5661 
   5662 TEST_F(JsepSessionTest, CreateOfferRemoveAudioTrack) {
   5663  types.push_back(SdpMediaSection::kAudio);
   5664  types.push_back(SdpMediaSection::kVideo);
   5665  AddTracks(*mSessionOff, "audio,video");
   5666 
   5667  SetDirection(*mSessionOff, 1, SdpDirectionAttribute::kSendonly);
   5668  JsepTrack removedTrack = RemoveTrack(*mSessionOff, 0);
   5669  ASSERT_FALSE(IsNull(removedTrack));
   5670 
   5671  CreateOffer();
   5672 }
   5673 
   5674 TEST_F(JsepSessionTest, CreateOfferDontReceiveAudioRemoveAudioTrack) {
   5675  types.push_back(SdpMediaSection::kAudio);
   5676  types.push_back(SdpMediaSection::kVideo);
   5677  AddTracks(*mSessionOff, "audio,video");
   5678 
   5679  SetDirection(*mSessionOff, 0, SdpDirectionAttribute::kSendonly);
   5680  JsepTrack removedTrack = RemoveTrack(*mSessionOff, 0);
   5681  ASSERT_FALSE(IsNull(removedTrack));
   5682 
   5683  CreateOffer();
   5684 }
   5685 
   5686 TEST_F(JsepSessionTest, CreateOfferDontReceiveVideoRemoveVideoTrack) {
   5687  types.push_back(SdpMediaSection::kAudio);
   5688  types.push_back(SdpMediaSection::kVideo);
   5689  AddTracks(*mSessionOff, "audio,video");
   5690 
   5691  JsepOfferOptions options;
   5692  options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   5693  options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U));
   5694 
   5695  JsepTrack removedTrack = RemoveTrack(*mSessionOff, 0);
   5696  ASSERT_FALSE(IsNull(removedTrack));
   5697 
   5698  CreateOffer(Some(options));
   5699 }
   5700 
   5701 MOZ_RUNINIT static const std::string strSampleCandidate =
   5702    "a=candidate:1 1 UDP 2130706431 192.168.2.1 50005 typ host\r\n";
   5703 
   5704 static const unsigned short nSamplelevel = 2;
   5705 
   5706 TEST_F(JsepSessionTest, CreateOfferAddCandidate) {
   5707  types.push_back(SdpMediaSection::kAudio);
   5708  AddTracks(*mSessionOff, "audio");
   5709  std::string offer = CreateOffer();
   5710  SetLocalOffer(offer);
   5711 
   5712  uint16_t level;
   5713  std::string mid;
   5714  bool skipped;
   5715  nsresult rv;
   5716  rv = mSessionOff->AddLocalIceCandidate(strSampleCandidate,
   5717                                         GetTransportId(*mSessionOff, 0), "",
   5718                                         &level, &mid, &skipped);
   5719  ASSERT_EQ(NS_OK, rv);
   5720 }
   5721 
   5722 TEST_F(JsepSessionTest, AddIceCandidateEarly) {
   5723  uint16_t level;
   5724  std::string mid;
   5725  bool skipped;
   5726  nsresult rv;
   5727  rv = mSessionOff->AddLocalIceCandidate(strSampleCandidate,
   5728                                         GetTransportId(*mSessionOff, 0), "",
   5729                                         &level, &mid, &skipped);
   5730 
   5731  // This can't succeed without a local description
   5732  ASSERT_NE(NS_OK, rv);
   5733 }
   5734 
   5735 TEST_F(JsepSessionTest, OfferAnswerDontAddAudioStreamOnAnswerNoOptions) {
   5736  types.push_back(SdpMediaSection::kAudio);
   5737  types.push_back(SdpMediaSection::kVideo);
   5738  AddTracks(*mSessionOff, "audio,video");
   5739  AddTracks(*mSessionAns, "video");
   5740 
   5741  JsepOfferOptions options;
   5742  options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   5743  options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   5744 
   5745  CreateOffer(Some(options));
   5746  std::string offer = CreateOffer(Some(options));
   5747  SetLocalOffer(offer);
   5748  SetRemoteOffer(offer);
   5749  std::string answer = CreateAnswer();
   5750  SetLocalAnswer(answer, CHECK_SUCCESS);
   5751  SetRemoteAnswer(answer, CHECK_SUCCESS);
   5752 }
   5753 
   5754 TEST_F(JsepSessionTest, OfferAnswerDontAddVideoStreamOnAnswerNoOptions) {
   5755  types.push_back(SdpMediaSection::kAudio);
   5756  types.push_back(SdpMediaSection::kVideo);
   5757  AddTracks(*mSessionOff, "audio,video");
   5758  AddTracks(*mSessionAns, "audio");
   5759 
   5760  JsepOfferOptions options;
   5761  options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   5762  options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   5763 
   5764  CreateOffer(Some(options));
   5765  std::string offer = CreateOffer(Some(options));
   5766  SetLocalOffer(offer);
   5767  SetRemoteOffer(offer);
   5768  std::string answer = CreateAnswer();
   5769  SetLocalAnswer(answer, CHECK_SUCCESS);
   5770  SetRemoteAnswer(answer, CHECK_SUCCESS);
   5771 }
   5772 
   5773 TEST_F(JsepSessionTest, OfferAnswerDontAddAudioVideoStreamsOnAnswerNoOptions) {
   5774  types.push_back(SdpMediaSection::kAudio);
   5775  types.push_back(SdpMediaSection::kVideo);
   5776  AddTracks(*mSessionOff, "audio,video");
   5777  AddTracks(*mSessionAns);
   5778 
   5779  JsepOfferOptions options;
   5780  options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   5781  options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   5782 
   5783  OfferAnswer();
   5784 }
   5785 
   5786 TEST_F(JsepSessionTest, OfferAndAnswerWithExtraCodec) {
   5787  types.push_back(SdpMediaSection::kAudio);
   5788  AddTracks(*mSessionOff, "audio");
   5789  AddTracks(*mSessionAns, "audio");
   5790  std::string offer = CreateOffer();
   5791  SetLocalOffer(offer);
   5792  SetRemoteOffer(offer);
   5793  std::string answer = CreateAnswer();
   5794 
   5795  UniquePtr<Sdp> munge = Parse(answer);
   5796  SdpMediaSection& mediaSection = munge->GetMediaSection(0);
   5797  mediaSection.AddCodec("8", "PCMA", 8000, 1);
   5798  std::string sdpString = munge->ToString();
   5799 
   5800  SetLocalAnswer(sdpString);
   5801  SetRemoteAnswer(answer);
   5802 }
   5803 
   5804 TEST_F(JsepSessionTest, AddCandidateInHaveLocalOffer) {
   5805  types.push_back(SdpMediaSection::kAudio);
   5806  AddTracks(*mSessionOff, "audio");
   5807  std::string offer = CreateOffer();
   5808  SetLocalOffer(offer);
   5809 
   5810  std::string mid;
   5811  std::string transportId;
   5812  JsepSession::Result result = mSessionOff->AddRemoteIceCandidate(
   5813      strSampleCandidate, mid, Some(nSamplelevel), "", &transportId);
   5814  ASSERT_EQ(dom::PCError::InvalidStateError, *result.mError);
   5815 }
   5816 
   5817 TEST_F(JsepSessionTest, SetLocalWithoutCreateOffer) {
   5818  types.push_back(SdpMediaSection::kAudio);
   5819  AddTracks(*mSessionOff, "audio");
   5820  AddTracks(*mSessionAns, "audio");
   5821 
   5822  std::string offer = CreateOffer();
   5823  JsepSession::Result result =
   5824      mSessionAns->SetLocalDescription(kJsepSdpOffer, offer);
   5825  ASSERT_EQ(dom::PCError::InvalidModificationError, *result.mError);
   5826 }
   5827 
   5828 TEST_F(JsepSessionTest, SetLocalWithoutCreateAnswer) {
   5829  types.push_back(SdpMediaSection::kAudio);
   5830  AddTracks(*mSessionOff, "audio");
   5831  AddTracks(*mSessionAns, "audio");
   5832 
   5833  std::string offer = CreateOffer();
   5834  SetRemoteOffer(offer);
   5835  JsepSession::Result result =
   5836      mSessionAns->SetLocalDescription(kJsepSdpAnswer, offer);
   5837  ASSERT_EQ(dom::PCError::InvalidModificationError, *result.mError);
   5838 }
   5839 
   5840 // Test for Bug 843595
   5841 TEST_F(JsepSessionTest, missingUfrag) {
   5842  types.push_back(SdpMediaSection::kAudio);
   5843  AddTracks(*mSessionOff, "audio");
   5844  AddTracks(*mSessionAns, "audio");
   5845  std::string offer = CreateOffer();
   5846  std::string ufrag = "ice-ufrag";
   5847  std::size_t pos = offer.find(ufrag);
   5848  ASSERT_NE(pos, std::string::npos);
   5849  offer.replace(pos, ufrag.length(), "ice-ufrog");
   5850  JsepSession::Result result =
   5851      mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   5852  ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError);
   5853 }
   5854 
   5855 TEST_F(JsepSessionTest, AudioOnlyCalleeNoRtcpMux) {
   5856  types.push_back(SdpMediaSection::kAudio);
   5857  AddTracks(*mSessionOff, "audio");
   5858  AddTracks(*mSessionAns, "audio");
   5859  std::string offer = CreateOffer();
   5860  std::string rtcp_mux = "a=rtcp-mux\r\n";
   5861  std::size_t pos = offer.find(rtcp_mux);
   5862  ASSERT_NE(pos, std::string::npos);
   5863  offer.replace(pos, rtcp_mux.length(), "");
   5864  SetLocalOffer(offer);
   5865  SetRemoteOffer(offer);
   5866  std::string answer = CreateAnswer();
   5867  pos = answer.find(rtcp_mux);
   5868  ASSERT_EQ(pos, std::string::npos);
   5869 }
   5870 
   5871 // This test comes from Bug 810220
   5872 TEST_F(JsepSessionTest, AudioOnlyG711Call) {
   5873  std::string offer =
   5874      "v=0\r\n"
   5875      "o=- 1 1 IN IP4 148.147.200.251\r\n"
   5876      "s=-\r\n"
   5877      "b=AS:64\r\n"
   5878      "t=0 0\r\n"
   5879      "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:"
   5880      "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n"
   5881      "m=audio 9000 UDP/TLS/RTP/SAVPF 0 8 126\r\n"
   5882      "c=IN IP4 148.147.200.251\r\n"
   5883      "b=TIAS:64000\r\n"
   5884      "a=rtpmap:0 PCMU/8000\r\n"
   5885      "a=rtpmap:8 PCMA/8000\r\n"
   5886      "a=rtpmap:126 telephone-event/8000\r\n"
   5887      "a=candidate:0 1 udp 2130706432 148.147.200.251 9000 typ host\r\n"
   5888      "a=candidate:0 2 udp 2130706432 148.147.200.251 9005 typ host\r\n"
   5889      "a=ice-ufrag:cYuakxkEKH+RApYE\r\n"
   5890      "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n"
   5891      "a=setup:active\r\n"
   5892      "a=sendrecv\r\n";
   5893 
   5894  types.push_back(SdpMediaSection::kAudio);
   5895  AddTracks(*mSessionAns, "audio");
   5896  SetRemoteOffer(offer, CHECK_SUCCESS);
   5897  std::string answer = CreateAnswer();
   5898 
   5899  // They didn't offer opus, so our answer shouldn't include it.
   5900  ASSERT_EQ(answer.find(" opus/"), std::string::npos);
   5901 
   5902  // They also didn't offer video or application
   5903  ASSERT_EQ(answer.find("video"), std::string::npos);
   5904  ASSERT_EQ(answer.find("application"), std::string::npos);
   5905 
   5906  // We should answer with PCMU and telephone-event
   5907  ASSERT_NE(answer.find(" PCMU/8000"), std::string::npos);
   5908 
   5909  // Double-check the directionality
   5910  ASSERT_NE(answer.find("\r\na=sendrecv"), std::string::npos);
   5911 }
   5912 
   5913 TEST_F(JsepSessionTest, AudioOnlyG722Only) {
   5914  types.push_back(SdpMediaSection::kAudio);
   5915  AddTracks(*mSessionOff, "audio");
   5916  AddTracks(*mSessionAns, "audio");
   5917  std::string offer = CreateOffer();
   5918  SetLocalOffer(offer);
   5919 
   5920  std::string audio = "m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101\r\n";
   5921  std::size_t pos = offer.find(audio);
   5922  ASSERT_NE(pos, std::string::npos);
   5923  offer.replace(pos, audio.length(), "m=audio 65375 UDP/TLS/RTP/SAVPF 9\r\n");
   5924  SetRemoteOffer(offer);
   5925 
   5926  std::string answer = CreateAnswer();
   5927  SetLocalAnswer(answer);
   5928  ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
   5929                .find("UDP/TLS/RTP/SAVPF 9\r"),
   5930            std::string::npos);
   5931  ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
   5932                .find("a=rtpmap:9 G722/8000"),
   5933            std::string::npos);
   5934 }
   5935 
   5936 TEST_F(JsepSessionTest, AudioOnlyG722Rejected) {
   5937  types.push_back(SdpMediaSection::kAudio);
   5938  AddTracks(*mSessionOff, "audio");
   5939  AddTracks(*mSessionAns, "audio");
   5940  std::string offer = CreateOffer();
   5941  SetLocalOffer(offer);
   5942 
   5943  std::string audio = "m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101\r\n";
   5944  std::size_t pos = offer.find(audio);
   5945  ASSERT_NE(pos, std::string::npos);
   5946  offer.replace(pos, audio.length(), "m=audio 65375 UDP/TLS/RTP/SAVPF 0 8\r\n");
   5947  SetRemoteOffer(offer);
   5948 
   5949  std::string answer = CreateAnswer();
   5950  SetLocalAnswer(answer);
   5951  SetRemoteAnswer(answer);
   5952 
   5953  ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
   5954                .find("UDP/TLS/RTP/SAVPF 0 8\r"),
   5955            std::string::npos);
   5956  ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
   5957                .find("a=rtpmap:0 PCMU/8000"),
   5958            std::string::npos);
   5959  ASSERT_EQ(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
   5960                .find("a=rtpmap:109 opus/48000/2"),
   5961            std::string::npos);
   5962  ASSERT_EQ(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
   5963                .find("a=rtpmap:9 G722/8000"),
   5964            std::string::npos);
   5965 }
   5966 
   5967 // This test doesn't make sense for bundle
   5968 TEST_F(JsepSessionTest, DISABLED_FullCallAudioNoMuxVideoMux) {
   5969  types.push_back(SdpMediaSection::kAudio);
   5970  AddTracks(*mSessionOff, "audio,video");
   5971  AddTracks(*mSessionAns, "audio,video");
   5972  std::string offer = CreateOffer();
   5973  SetLocalOffer(offer);
   5974  std::string rtcp_mux = "a=rtcp-mux\r\n";
   5975  std::size_t pos = offer.find(rtcp_mux);
   5976  ASSERT_NE(pos, std::string::npos);
   5977  offer.replace(pos, rtcp_mux.length(), "");
   5978  SetRemoteOffer(offer);
   5979  std::string answer = CreateAnswer();
   5980 
   5981  size_t match = mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
   5982                     .find("\r\na=rtcp-mux");
   5983  ASSERT_NE(match, std::string::npos);
   5984  match = mSessionAns->GetLocalDescription(kJsepDescriptionCurrent)
   5985              .find("\r\na=rtcp-mux", match + 1);
   5986  ASSERT_EQ(match, std::string::npos);
   5987 }
   5988 
   5989 // Disabled pending resolution of bug 818640.
   5990 // Actually, this test is completely broken; you can't just call
   5991 // SetRemote/CreateAnswer over and over again.
   5992 TEST_F(JsepSessionTest, DISABLED_OfferAllDynamicTypes) {
   5993  types.push_back(SdpMediaSection::kAudio);
   5994  AddTracks(*mSessionAns, "audio");
   5995 
   5996  std::string offer;
   5997  for (int i = 96; i < 128; i++) {
   5998    std::stringstream ss;
   5999    ss << i;
   6000    std::cout << "Trying dynamic pt = " << i << std::endl;
   6001    offer =
   6002        "v=0\r\n"
   6003        "o=- 1 1 IN IP4 148.147.200.251\r\n"
   6004        "s=-\r\n"
   6005        "b=AS:64\r\n"
   6006        "t=0 0\r\n"
   6007        "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:"
   6008        "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n"
   6009        "m=audio 9000 RTP/AVP " +
   6010        ss.str() +
   6011        "\r\n"
   6012        "c=IN IP4 148.147.200.251\r\n"
   6013        "b=TIAS:64000\r\n"
   6014        "a=rtpmap:" +
   6015        ss.str() +
   6016        " opus/48000/2\r\n"
   6017        "a=candidate:0 1 udp 2130706432 148.147.200.251 9000 typ host\r\n"
   6018        "a=candidate:0 2 udp 2130706432 148.147.200.251 9005 typ host\r\n"
   6019        "a=ice-ufrag:cYuakxkEKH+RApYE\r\n"
   6020        "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n"
   6021        "a=sendrecv\r\n";
   6022 
   6023    SetRemoteOffer(offer, CHECK_SUCCESS);
   6024    std::string answer = CreateAnswer();
   6025    ASSERT_NE(answer.find(ss.str() + " opus/"), std::string::npos);
   6026  }
   6027 }
   6028 
   6029 TEST_F(JsepSessionTest, ipAddrAnyOffer) {
   6030  std::string offer =
   6031      "v=0\r\n"
   6032      "o=- 1 1 IN IP4 127.0.0.1\r\n"
   6033      "s=-\r\n"
   6034      "b=AS:64\r\n"
   6035      "t=0 0\r\n"
   6036      "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:"
   6037      "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n"
   6038      "m=audio 9000 UDP/TLS/RTP/SAVPF 99\r\n"
   6039      "c=IN IP4 0.0.0.0\r\n"
   6040      "a=rtpmap:99 opus/48000/2\r\n"
   6041      "a=ice-ufrag:cYuakxkEKH+RApYE\r\n"
   6042      "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n"
   6043      "a=setup:active\r\n"
   6044      "a=sendrecv\r\n";
   6045 
   6046  types.push_back(SdpMediaSection::kAudio);
   6047  AddTracks(*mSessionAns, "audio");
   6048  SetRemoteOffer(offer, CHECK_SUCCESS);
   6049  std::string answer = CreateAnswer();
   6050 
   6051  ASSERT_NE(answer.find("a=sendrecv"), std::string::npos);
   6052 }
   6053 
   6054 static void CreateSDPForBigOTests(std::string& offer,
   6055                                  const std::string& number) {
   6056  offer =
   6057      "v=0\r\n"
   6058      "o=- ";
   6059  offer += number;
   6060  offer += " ";
   6061  offer += number;
   6062  offer +=
   6063      " IN IP4 127.0.0.1\r\n"
   6064      "s=-\r\n"
   6065      "b=AS:64\r\n"
   6066      "t=0 0\r\n"
   6067      "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:"
   6068      "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n"
   6069      "m=audio 9000 RTP/AVP 99\r\n"
   6070      "c=IN IP4 0.0.0.0\r\n"
   6071      "a=rtpmap:99 opus/48000/2\r\n"
   6072      "a=ice-ufrag:cYuakxkEKH+RApYE\r\n"
   6073      "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n"
   6074      "a=setup:active\r\n"
   6075      "a=sendrecv\r\n";
   6076 }
   6077 
   6078 TEST_F(JsepSessionTest, BigOValues) {
   6079  std::string offer;
   6080 
   6081  CreateSDPForBigOTests(offer, "12345678901234567");
   6082 
   6083  types.push_back(SdpMediaSection::kAudio);
   6084  AddTracks(*mSessionAns, "audio");
   6085  SetRemoteOffer(offer, CHECK_SUCCESS);
   6086 }
   6087 
   6088 TEST_F(JsepSessionTest, BigOValuesExtraChars) {
   6089  std::string offer;
   6090 
   6091  CreateSDPForBigOTests(offer, "12345678901234567FOOBAR");
   6092 
   6093  types.push_back(SdpMediaSection::kAudio);
   6094  AddTracks(*mSessionAns, "audio");
   6095  // The signaling state will remain "stable" because the unparsable
   6096  // SDP leads to a failure in SetRemoteDescription.
   6097  SetRemoteOffer(offer, NO_CHECKS);
   6098  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6099 }
   6100 
   6101 TEST_F(JsepSessionTest, BigOValuesTooBig) {
   6102  std::string offer;
   6103 
   6104  CreateSDPForBigOTests(offer, "18446744073709551615");
   6105  types.push_back(SdpMediaSection::kAudio);
   6106  AddTracks(*mSessionAns, "audio");
   6107 
   6108  // The signaling state will remain "stable" because the unparsable
   6109  // SDP leads to a failure in SetRemoteDescription.
   6110  SetRemoteOffer(offer, NO_CHECKS);
   6111  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6112 }
   6113 
   6114 TEST_F(JsepSessionTest, SetLocalAnswerInStable) {
   6115  types.push_back(SdpMediaSection::kAudio);
   6116  AddTracks(*mSessionOff, "audio");
   6117  std::string offer = CreateOffer();
   6118 
   6119  // The signaling state will remain "stable" because the
   6120  // SetLocalDescription call fails.
   6121  SetLocalAnswer(offer, NO_CHECKS);
   6122  ASSERT_EQ(kJsepStateStable, mSessionOff->GetState());
   6123 }
   6124 
   6125 TEST_F(JsepSessionTest, SetRemoteAnswerInStable) {
   6126  const std::string answer =
   6127      "v=0\r\n"
   6128      "o=Mozilla-SIPUA 4949 0 IN IP4 10.86.255.143\r\n"
   6129      "s=SIP Call\r\n"
   6130      "t=0 0\r\n"
   6131      "a=ice-ufrag:qkEP\r\n"
   6132      "a=ice-pwd:ed6f9GuHjLcoCN6sC/Eh7fVl\r\n"
   6133      "m=audio 16384 RTP/AVP 0 8 9 101\r\n"
   6134      "c=IN IP4 10.86.255.143\r\n"
   6135      "a=rtpmap:0 PCMU/8000\r\n"
   6136      "a=rtpmap:8 PCMA/8000\r\n"
   6137      "a=rtpmap:9 G722/8000\r\n"
   6138      "a=rtpmap:101 telephone-event/8000\r\n"
   6139      "a=fmtp:101 0-15\r\n"
   6140      "a=sendrecv\r\n"
   6141      "a=candidate:1 1 UDP 2130706431 192.168.2.1 50005 typ host\r\n"
   6142      "a=candidate:2 2 UDP 2130706431 192.168.2.2 50006 typ host\r\n"
   6143      "m=video 1024 RTP/AVP 97\r\n"
   6144      "c=IN IP4 10.86.255.143\r\n"
   6145      "a=rtpmap:120 VP8/90000\r\n"
   6146      "a=fmtp:97 profile-level-id=42E00C\r\n"
   6147      "a=sendrecv\r\n"
   6148      "a=candidate:1 1 UDP 2130706431 192.168.2.3 50007 typ host\r\n"
   6149      "a=candidate:2 2 UDP 2130706431 192.168.2.4 50008 typ host\r\n";
   6150 
   6151  // The signaling state will remain "stable" because the
   6152  // SetRemoteDescription call fails.
   6153  JsepSession::Result result =
   6154      mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
   6155  ASSERT_EQ(dom::PCError::InvalidStateError, *result.mError);
   6156  ASSERT_EQ(kJsepStateStable, mSessionOff->GetState());
   6157 }
   6158 
   6159 TEST_F(JsepSessionTest, SetLocalAnswerInHaveLocalOffer) {
   6160  types.push_back(SdpMediaSection::kAudio);
   6161  AddTracks(*mSessionOff, "audio");
   6162  std::string offer = CreateOffer();
   6163 
   6164  SetLocalOffer(offer);
   6165  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6166 
   6167  // The signaling state will remain "have-local-offer" because the
   6168  // SetLocalDescription call fails.
   6169  JsepSession::Result result =
   6170      mSessionOff->SetLocalDescription(kJsepSdpAnswer, offer);
   6171  ASSERT_EQ(dom::PCError::InvalidModificationError, *result.mError);
   6172  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6173 }
   6174 
   6175 TEST_F(JsepSessionTest, SetRemoteOfferInHaveLocalOffer) {
   6176  types.push_back(SdpMediaSection::kAudio);
   6177  AddTracks(*mSessionOff, "audio");
   6178  std::string offer = CreateOffer();
   6179 
   6180  SetLocalOffer(offer);
   6181  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6182 
   6183  // The signaling state will remain "have-local-offer" because the
   6184  // SetRemoteDescription call fails.
   6185  JsepSession::Result result =
   6186      mSessionOff->SetRemoteDescription(kJsepSdpOffer, offer);
   6187  ASSERT_EQ(dom::PCError::InvalidStateError, *result.mError);
   6188  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6189 }
   6190 
   6191 TEST_F(JsepSessionTest, SetLocalOfferInHaveRemoteOffer) {
   6192  types.push_back(SdpMediaSection::kAudio);
   6193  AddTracks(*mSessionOff, "audio");
   6194  std::string offer = CreateOffer();
   6195 
   6196  SetRemoteOffer(offer);
   6197  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   6198 
   6199  // The signaling state will remain "have-remote-offer" because the
   6200  // SetLocalDescription call fails.
   6201  JsepSession::Result result =
   6202      mSessionAns->SetLocalDescription(kJsepSdpOffer, offer);
   6203  ASSERT_EQ(dom::PCError::InvalidModificationError, *result.mError);
   6204  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   6205 }
   6206 
   6207 TEST_F(JsepSessionTest, SetRemoteAnswerInHaveRemoteOffer) {
   6208  types.push_back(SdpMediaSection::kAudio);
   6209  AddTracks(*mSessionOff, "audio");
   6210  std::string offer = CreateOffer();
   6211 
   6212  SetRemoteOffer(offer);
   6213  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   6214 
   6215  // The signaling state will remain "have-remote-offer" because the
   6216  // SetRemoteDescription call fails.
   6217  JsepSession::Result result =
   6218      mSessionAns->SetRemoteDescription(kJsepSdpAnswer, offer);
   6219  ASSERT_EQ(dom::PCError::InvalidStateError, *result.mError);
   6220 
   6221  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   6222 }
   6223 
   6224 TEST_F(JsepSessionTest, RtcpFbInOffer) {
   6225  types.push_back(SdpMediaSection::kAudio);
   6226  types.push_back(SdpMediaSection::kVideo);
   6227  AddTracks(*mSessionOff, "audio,video");
   6228  std::string offer = CreateOffer();
   6229 
   6230  std::map<std::string, bool> expected;
   6231  expected["nack"] = false;
   6232  expected["nack pli"] = false;
   6233  expected["ccm fir"] = false;
   6234  expected["goog-remb"] = false;
   6235  expected["transport-cc"] = false;
   6236 
   6237  size_t prev = 0;
   6238  size_t found = 0;
   6239  for (;;) {
   6240    found = offer.find('\n', found + 1);
   6241    if (found == std::string::npos) break;
   6242 
   6243    std::string line = offer.substr(prev, (found - prev));
   6244 
   6245    // ensure no other rtcp-fb values are present
   6246    if (line.find("a=rtcp-fb:") != std::string::npos) {
   6247      size_t space = line.find(' ');
   6248      // strip trailing \r\n
   6249      std::string value = line.substr(space + 1, line.length() - space - 2);
   6250      std::map<std::string, bool>::iterator entry = expected.find(value);
   6251      ASSERT_NE(entry, expected.end());
   6252      entry->second = true;
   6253    }
   6254 
   6255    prev = found + 1;
   6256  }
   6257 
   6258  // ensure all values are present
   6259  for (std::map<std::string, bool>::iterator it = expected.begin();
   6260       it != expected.end(); ++it) {
   6261    ASSERT_EQ(it->second, true);
   6262  }
   6263 }
   6264 
   6265 // In this test we will change the offer SDP's a=setup value
   6266 // from actpass to passive. This will force the answer to do active.
   6267 TEST_F(JsepSessionTest, AudioCallForceDtlsRoles) {
   6268  types.push_back(SdpMediaSection::kAudio);
   6269  AddTracks(*mSessionOff, "audio");
   6270  AddTracks(*mSessionAns, "audio");
   6271  std::string offer = CreateOffer();
   6272 
   6273  std::string actpass = "\r\na=setup:actpass";
   6274  size_t match = offer.find(actpass);
   6275  ASSERT_NE(match, std::string::npos);
   6276  offer.replace(match, actpass.length(), "\r\na=setup:passive");
   6277 
   6278  SetLocalOffer(offer);
   6279  SetRemoteOffer(offer);
   6280  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   6281  std::string answer = CreateAnswer();
   6282  match = answer.find("\r\na=setup:active");
   6283  ASSERT_NE(match, std::string::npos);
   6284 
   6285  SetLocalAnswer(answer);
   6286  SetRemoteAnswer(answer);
   6287  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6288 }
   6289 
   6290 // In this test we will change the offer SDP's a=setup value
   6291 // from actpass to active. This will force the answer to do passive.
   6292 TEST_F(JsepSessionTest, AudioCallReverseDtlsRoles) {
   6293  types.push_back(SdpMediaSection::kAudio);
   6294  AddTracks(*mSessionOff, "audio");
   6295  AddTracks(*mSessionAns, "audio");
   6296  std::string offer = CreateOffer();
   6297 
   6298  std::string actpass = "\r\na=setup:actpass";
   6299  size_t match = offer.find(actpass);
   6300  ASSERT_NE(match, std::string::npos);
   6301  offer.replace(match, actpass.length(), "\r\na=setup:active");
   6302 
   6303  SetLocalOffer(offer);
   6304  SetRemoteOffer(offer);
   6305  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   6306  std::string answer = CreateAnswer();
   6307  match = answer.find("\r\na=setup:passive");
   6308  ASSERT_NE(match, std::string::npos);
   6309 
   6310  SetLocalAnswer(answer);
   6311  SetRemoteAnswer(answer);
   6312  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6313 }
   6314 
   6315 // In this test we will change the answer SDP's a=setup value
   6316 // from active to passive.  This will make both sides do
   6317 // active and should not connect.
   6318 TEST_F(JsepSessionTest, AudioCallMismatchDtlsRoles) {
   6319  types.push_back(SdpMediaSection::kAudio);
   6320  AddTracks(*mSessionOff, "audio");
   6321  AddTracks(*mSessionAns, "audio");
   6322  std::string offer = CreateOffer();
   6323 
   6324  std::string actpass = "\r\na=setup:actpass";
   6325  size_t match = offer.find(actpass);
   6326  ASSERT_NE(match, std::string::npos);
   6327  SetLocalOffer(offer);
   6328  SetRemoteOffer(offer);
   6329  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   6330  std::string answer = CreateAnswer();
   6331  SetLocalAnswer(answer);
   6332 
   6333  std::string active = "\r\na=setup:active";
   6334  match = answer.find(active);
   6335  ASSERT_NE(match, std::string::npos);
   6336  answer.replace(match, active.length(), "\r\na=setup:passive");
   6337  SetRemoteAnswer(answer);
   6338 
   6339  // This is as good as it gets in a JSEP test (w/o starting DTLS)
   6340  ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient,
   6341            GetTransceivers(*mSessionOff)[0].mTransport.mDtls->GetRole());
   6342  ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient,
   6343            GetTransceivers(*mSessionAns)[0].mTransport.mDtls->GetRole());
   6344 }
   6345 
   6346 // Verify that missing a=setup in offer gets rejected
   6347 TEST_F(JsepSessionTest, AudioCallOffererNoSetup) {
   6348  types.push_back(SdpMediaSection::kAudio);
   6349  AddTracks(*mSessionOff, "audio");
   6350  AddTracks(*mSessionAns, "audio");
   6351  std::string offer = CreateOffer();
   6352  SetLocalOffer(offer);
   6353 
   6354  std::string actpass = "\r\na=setup:actpass";
   6355  size_t match = offer.find(actpass);
   6356  ASSERT_NE(match, std::string::npos);
   6357  offer.replace(match, actpass.length(), "");
   6358 
   6359  // The signaling state will remain "stable" because the unparsable
   6360  // SDP leads to a failure in SetRemoteDescription.
   6361  SetRemoteOffer(offer, NO_CHECKS);
   6362  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6363  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6364 }
   6365 
   6366 // In this test we will change the answer SDP to remove the
   6367 // a=setup line, which results in active being assumed.
   6368 TEST_F(JsepSessionTest, AudioCallAnswerNoSetup) {
   6369  types.push_back(SdpMediaSection::kAudio);
   6370  AddTracks(*mSessionOff, "audio");
   6371  AddTracks(*mSessionAns, "audio");
   6372  std::string offer = CreateOffer();
   6373  size_t match = offer.find("\r\na=setup:actpass");
   6374  ASSERT_NE(match, std::string::npos);
   6375 
   6376  SetLocalOffer(offer);
   6377  SetRemoteOffer(offer);
   6378  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   6379  std::string answer = CreateAnswer();
   6380  SetLocalAnswer(answer);
   6381 
   6382  std::string active = "\r\na=setup:active";
   6383  match = answer.find(active);
   6384  ASSERT_NE(match, std::string::npos);
   6385  answer.replace(match, active.length(), "");
   6386  SetRemoteAnswer(answer);
   6387  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6388 
   6389  // This is as good as it gets in a JSEP test (w/o starting DTLS)
   6390  ASSERT_EQ(JsepDtlsTransport::kJsepDtlsServer,
   6391            GetTransceivers(*mSessionOff)[0].mTransport.mDtls->GetRole());
   6392  ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient,
   6393            GetTransceivers(*mSessionAns)[0].mTransport.mDtls->GetRole());
   6394 }
   6395 
   6396 // Verify that 'holdconn' gets rejected
   6397 TEST_F(JsepSessionTest, AudioCallDtlsRoleHoldconn) {
   6398  types.push_back(SdpMediaSection::kAudio);
   6399  AddTracks(*mSessionOff, "audio");
   6400  AddTracks(*mSessionAns, "audio");
   6401  std::string offer = CreateOffer();
   6402  SetLocalOffer(offer);
   6403 
   6404  std::string actpass = "\r\na=setup:actpass";
   6405  size_t match = offer.find(actpass);
   6406  ASSERT_NE(match, std::string::npos);
   6407  offer.replace(match, actpass.length(), "\r\na=setup:holdconn");
   6408 
   6409  // The signaling state will remain "stable" because the unparsable
   6410  // SDP leads to a failure in SetRemoteDescription.
   6411  SetRemoteOffer(offer, NO_CHECKS);
   6412  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6413  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6414 }
   6415 
   6416 // Verify that 'actpass' in answer gets rejected
   6417 TEST_F(JsepSessionTest, AudioCallAnswererUsesActpass) {
   6418  types.push_back(SdpMediaSection::kAudio);
   6419  AddTracks(*mSessionOff, "audio");
   6420  AddTracks(*mSessionAns, "audio");
   6421  std::string offer = CreateOffer();
   6422  SetLocalOffer(offer);
   6423  SetRemoteOffer(offer);
   6424  std::string answer = CreateAnswer();
   6425  SetLocalAnswer(answer);
   6426 
   6427  std::string active = "\r\na=setup:active";
   6428  size_t match = answer.find(active);
   6429  ASSERT_NE(match, std::string::npos);
   6430  answer.replace(match, active.length(), "\r\na=setup:actpass");
   6431 
   6432  // The signaling state will remain "stable" because the unparsable
   6433  // SDP leads to a failure in SetRemoteDescription.
   6434  SetRemoteAnswer(answer, NO_CHECKS);
   6435  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6436  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6437 }
   6438 
   6439 // Verify that 'actpass' in reoffer from previous answerer doesn't result
   6440 // in a role switch.
   6441 TEST_F(JsepSessionTest, AudioCallPreviousAnswererUsesActpassInReoffer) {
   6442  types.push_back(SdpMediaSection::kAudio);
   6443  AddTracks(*mSessionOff, "audio");
   6444  AddTracks(*mSessionAns, "audio");
   6445 
   6446  OfferAnswer();
   6447 
   6448  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   6449  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   6450 
   6451  SwapOfferAnswerRoles();
   6452 
   6453  OfferAnswer();
   6454 
   6455  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   6456  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kPassive);
   6457 }
   6458 
   6459 // Disabled: See Bug 1329028
   6460 TEST_F(JsepSessionTest, DISABLED_AudioCallOffererAttemptsSetupRoleSwitch) {
   6461  types.push_back(SdpMediaSection::kAudio);
   6462  AddTracks(*mSessionOff, "audio");
   6463  AddTracks(*mSessionAns, "audio");
   6464 
   6465  OfferAnswer();
   6466 
   6467  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   6468  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   6469 
   6470  std::string reoffer = CreateOffer();
   6471  SetLocalOffer(reoffer);
   6472 
   6473  std::string actpass = "\r\na=setup:actpass";
   6474  size_t match = reoffer.find(actpass);
   6475  ASSERT_NE(match, std::string::npos);
   6476  reoffer.replace(match, actpass.length(), "\r\na=setup:active");
   6477 
   6478  // This is expected to fail.
   6479  SetRemoteOffer(reoffer, NO_CHECKS);
   6480  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6481  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6482 }
   6483 
   6484 // Disabled: See Bug 1329028
   6485 TEST_F(JsepSessionTest, DISABLED_AudioCallAnswererAttemptsSetupRoleSwitch) {
   6486  types.push_back(SdpMediaSection::kAudio);
   6487  AddTracks(*mSessionOff, "audio");
   6488  AddTracks(*mSessionAns, "audio");
   6489 
   6490  OfferAnswer();
   6491 
   6492  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
   6493  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
   6494 
   6495  std::string reoffer = CreateOffer();
   6496  SetLocalOffer(reoffer);
   6497  SetRemoteOffer(reoffer);
   6498 
   6499  std::string reanswer = CreateAnswer();
   6500  SetLocalAnswer(reanswer);
   6501 
   6502  std::string actpass = "\r\na=setup:active";
   6503  size_t match = reanswer.find(actpass);
   6504  ASSERT_NE(match, std::string::npos);
   6505  reanswer.replace(match, actpass.length(), "\r\na=setup:passive");
   6506 
   6507  // This is expected to fail.
   6508  SetRemoteAnswer(reanswer, NO_CHECKS);
   6509  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
   6510  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
   6511 }
   6512 
   6513 // Remove H.264 CB and VP8 from offer, check answer negotiates H.264 B
   6514 TEST_F(JsepSessionTest, OfferWithOnlyH264Baseline) {
   6515  for (auto& codec : mSessionOff->Codecs()) {
   6516    if (codec->mName != "H264" || codec->mDefaultPt == "126" ||
   6517        codec->mDefaultPt == "97") {
   6518      codec->mEnabled = false;
   6519    }
   6520  }
   6521 
   6522  types.push_back(SdpMediaSection::kAudio);
   6523  types.push_back(SdpMediaSection::kVideo);
   6524  AddTracks(*mSessionOff, "audio,video");
   6525  AddTracks(*mSessionAns, "audio,video");
   6526  std::string offer = CreateOffer();
   6527 
   6528  ASSERT_EQ(offer.find("a=rtpmap:126 H264/90000"), std::string::npos);
   6529  ASSERT_EQ(offer.find("a=rtpmap:97 H264/90000"), std::string::npos);
   6530  ASSERT_EQ(offer.find("a=rtpmap:120 VP8/90000"), std::string::npos);
   6531 
   6532  SetLocalOffer(offer);
   6533  SetRemoteOffer(offer);
   6534  std::string answer = CreateAnswer();
   6535  size_t match = answer.find("\r\na=setup:active");
   6536  ASSERT_NE(match, std::string::npos);
   6537 
   6538  // validate answer SDP
   6539  ASSERT_NE(answer.find("a=rtpmap:105 H264/90000"), std::string::npos);
   6540  ASSERT_NE(answer.find("a=rtcp-fb:105 nack"), std::string::npos);
   6541  ASSERT_NE(answer.find("a=rtcp-fb:105 nack pli"), std::string::npos);
   6542  ASSERT_NE(answer.find("a=rtcp-fb:105 ccm fir"), std::string::npos);
   6543  // Ensure VP8 and H264 CB removed
   6544  ASSERT_EQ(answer.find("a=rtpmap:126 H264/90000"), std::string::npos);
   6545  ASSERT_EQ(answer.find("a=rtpmap:97 H264/90000"), std::string::npos);
   6546  ASSERT_EQ(answer.find("a=rtpmap:120 VP8/90000"), std::string::npos);
   6547  ASSERT_EQ(answer.find("a=rtcp-fb:120"), std::string::npos);
   6548  ASSERT_EQ(answer.find("a=rtcp-fb:126"), std::string::npos);
   6549  ASSERT_EQ(answer.find("a=rtcp-fb:97"), std::string::npos);
   6550 }
   6551 
   6552 // Test negotiating an answer which has only H.264 P1
   6553 // Which means replace VP8 with H.264 P1 in answer
   6554 TEST_F(JsepSessionTest, AnswerWithoutVP8) {
   6555  types.push_back(SdpMediaSection::kAudio);
   6556  types.push_back(SdpMediaSection::kVideo);
   6557  AddTracks(*mSessionOff, "audio,video");
   6558  AddTracks(*mSessionAns, "audio,video");
   6559  std::string offer = CreateOffer();
   6560  SetLocalOffer(offer);
   6561  SetRemoteOffer(offer);
   6562 
   6563  for (auto& codec : mSessionOff->Codecs()) {
   6564    if (codec->mName != "H264" || codec->mDefaultPt == "126") {
   6565      codec->mEnabled = false;
   6566    }
   6567  }
   6568 
   6569  std::string answer = CreateAnswer();
   6570 
   6571  SetLocalAnswer(answer);
   6572  SetRemoteAnswer(answer);
   6573 }
   6574 
   6575 // Ok. Hear me out.
   6576 // The JSEP spec specifies very different behavior for the following two cases:
   6577 // 1. AddTrack caused a transceiver to be created.
   6578 // 2. The transceiver was not created as a side-effect of AddTrack.
   6579 //
   6580 // All together now...
   6581 //
   6582 // SADFACE :(
   6583 //
   6584 // Ok, enough of that. The upshot is we need to test two different codepaths for
   6585 // the same thing here. Most of this unit-test suite tests the "magic" case
   6586 // (case 1 above). Case 2 (the non-magic case) is simpler, so we have just a
   6587 // handful of tests.
   6588 TEST_F(JsepSessionTest, OffererNoAddTrackMagic) {
   6589  types = BuildTypes("audio,video");
   6590  AddTracks(*mSessionOff, NO_ADDTRACK_MAGIC);
   6591  AddTracks(*mSessionAns);
   6592 
   6593  // Offerer's transceivers aren't "magic"; they will not associate with the
   6594  // remote side's m-sections automatically. But, since they went into the
   6595  // offer, everything works normally.
   6596  OfferAnswer();
   6597 
   6598  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6599  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6600 }
   6601 
   6602 TEST_F(JsepSessionTest, AnswererNoAddTrackMagic) {
   6603  types = BuildTypes("audio,video");
   6604  AddTracks(*mSessionOff);
   6605  AddTracks(*mSessionAns, NO_ADDTRACK_MAGIC);
   6606 
   6607  OfferAnswer(CHECK_SUCCESS);
   6608 
   6609  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6610  // Since answerer's transceivers aren't "magic", they cannot automatically be
   6611  // attached to the offerer's m-sections.
   6612  ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size());
   6613 
   6614  SwapOfferAnswerRoles();
   6615 
   6616  OfferAnswer(CHECK_SUCCESS);
   6617  ASSERT_EQ(4U, GetTransceivers(*mSessionOff).size());
   6618  ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size());
   6619 }
   6620 
   6621 // JSEP has rules about when a disabled m-section can be reused; the gist is
   6622 // that the m-section has to be negotiated disabled, then it becomes a candidate
   6623 // for reuse on the next renegotiation. Stopping a transceiver does not allow
   6624 // you to reuse on the next negotiation.
   6625 TEST_F(JsepSessionTest, OffererRecycle) {
   6626  types = BuildTypes("audio,video");
   6627  AddTracks(*mSessionOff);
   6628  AddTracks(*mSessionAns);
   6629 
   6630  OfferAnswer();
   6631 
   6632  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6633  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6634  GetTransceivers(*mSessionOff)[0].Stop();
   6635  AddTracks(*mSessionOff, "audio");
   6636  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6637  ASSERT_TRUE(mSessionOff->CheckNegotiationNeeded());
   6638 
   6639  OfferAnswer(CHECK_SUCCESS);
   6640 
   6641  ASSERT_FALSE(mSessionOff->CheckNegotiationNeeded());
   6642  // It is too soon to recycle msection 0, so the new track should have been
   6643  // given a new msection.
   6644  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6645  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel());
   6646  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6647  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6648  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6649  ASSERT_EQ(2U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6650  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6651 
   6652  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   6653  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   6654  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6655  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6656  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6657  ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[2].GetLevel());
   6658  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   6659 
   6660  UniquePtr<Sdp> offer = GetParsedLocalDescription(*mSessionOff);
   6661  ASSERT_EQ(3U, offer->GetMediaSectionCount());
   6662  ValidateDisabledMSection(&offer->GetMediaSection(0));
   6663 
   6664  UniquePtr<Sdp> answer = GetParsedLocalDescription(*mSessionAns);
   6665  ASSERT_EQ(3U, answer->GetMediaSectionCount());
   6666  ValidateDisabledMSection(&answer->GetMediaSection(0));
   6667 
   6668  // Ok. Now renegotiating should recycle m-section 0.
   6669  AddTracks(*mSessionOff, "audio");
   6670  ASSERT_EQ(4U, GetTransceivers(*mSessionOff).size());
   6671  OfferAnswer(CHECK_SUCCESS);
   6672 
   6673  // Transceiver 3 should now be attached to m-section 0
   6674  ASSERT_EQ(4U, GetTransceivers(*mSessionOff).size());
   6675  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel());
   6676  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6677  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6678  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6679  ASSERT_EQ(2U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6680  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6681  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[3].GetLevel());
   6682  ASSERT_FALSE(GetTransceivers(*mSessionOff)[3].IsStopped());
   6683 
   6684  ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size());
   6685  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel());
   6686  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6687  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6688  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6689  ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[2].GetLevel());
   6690  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   6691  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[3].GetLevel());
   6692  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsStopped());
   6693 }
   6694 
   6695 TEST_F(JsepSessionTest, RecycleAnswererStopsTransceiver) {
   6696  types = BuildTypes("audio,video");
   6697  AddTracks(*mSessionOff);
   6698  AddTracks(*mSessionAns);
   6699 
   6700  OfferAnswer();
   6701 
   6702  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6703  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6704  GetTransceivers(*mSessionAns)[0].Stop();
   6705  ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded());
   6706 
   6707  OfferAnswer(CHECK_SUCCESS);
   6708 
   6709  // Answerer is not allowed to handle Stop(); it needs to be done in an offer
   6710  ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded());
   6711  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6712  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel());
   6713  // webrtc-pc pulled a fast one and modified JSEP to say that the answerer
   6714  // does not reject when it has a stopped transceiver. That's an offerer thing
   6715  // only.
   6716  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6717  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6718  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6719 
   6720  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6721  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   6722  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopping());
   6723  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6724  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6725  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopping());
   6726  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6727 
   6728  auto offer = GetParsedLocalDescription(*mSessionOff);
   6729  ASSERT_EQ(2U, offer->GetMediaSectionCount());
   6730 
   6731  auto answer = GetParsedLocalDescription(*mSessionAns);
   6732  ASSERT_EQ(2U, answer->GetMediaSectionCount());
   6733 
   6734  // Switching roles and renegotiating will cause the m-section to be rejected
   6735  SwapOfferAnswerRoles();
   6736  OfferAnswer();
   6737 
   6738  ASSERT_FALSE(mSessionAns->CheckNegotiationNeeded());
   6739  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6740  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel());
   6741  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6742  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6743  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6744 
   6745  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6746  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   6747  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6748  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6749  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6750 
   6751  offer = GetParsedLocalDescription(*mSessionOff);
   6752  ASSERT_EQ(2U, offer->GetMediaSectionCount());
   6753 
   6754  answer = GetParsedLocalDescription(*mSessionAns);
   6755  ASSERT_EQ(2U, answer->GetMediaSectionCount());
   6756 
   6757  ValidateDisabledMSection(&offer->GetMediaSection(0));
   6758  ValidateDisabledMSection(&offer->GetMediaSection(0));
   6759  ValidateDisabledMSection(&answer->GetMediaSection(0));
   6760 
   6761  // Renegotiating should recycle m-section 0.
   6762  AddTracks(*mSessionOff, "audio");
   6763  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6764  OfferAnswer(CHECK_SUCCESS);
   6765 
   6766  // Transceiver 3 should now be attached to m-section 0
   6767  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6768  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel());
   6769  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6770  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6771  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6772  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6773  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6774 
   6775  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   6776  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel());
   6777  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6778  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6779  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6780  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[2].GetLevel());
   6781  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   6782 }
   6783 
   6784 // TODO: Have a test where offerer stops, and answerer adds a track and reoffers
   6785 // once Nils' role swap code lands.
   6786 
   6787 // TODO: Have a test where answerer stops and adds a track.
   6788 
   6789 TEST_F(JsepSessionTest, OffererRecycleNoMagic) {
   6790  types = BuildTypes("audio,video");
   6791  AddTracks(*mSessionOff);
   6792  AddTracks(*mSessionAns);
   6793 
   6794  OfferAnswer();
   6795 
   6796  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6797  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6798  GetTransceivers(*mSessionOff)[0].Stop();
   6799 
   6800  OfferAnswer(CHECK_SUCCESS);
   6801 
   6802  // Ok. Now renegotiating should recycle m-section 0.
   6803  AddTracks(*mSessionOff, "audio", NO_ADDTRACK_MAGIC);
   6804  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6805  OfferAnswer(CHECK_SUCCESS);
   6806 
   6807  // Transceiver 2 should now be attached to m-section 0
   6808  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6809  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel());
   6810  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6811  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6812  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6813  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6814  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6815 
   6816  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   6817  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel());
   6818  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6819  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6820  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6821  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[2].GetLevel());
   6822  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   6823 }
   6824 
   6825 TEST_F(JsepSessionTest, OffererRecycleNoMagicAnswererStopsTransceiver) {
   6826  types = BuildTypes("audio,video");
   6827  AddTracks(*mSessionOff);
   6828  AddTracks(*mSessionAns);
   6829 
   6830  OfferAnswer();
   6831 
   6832  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6833  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6834  GetTransceivers(*mSessionAns)[0].Stop();
   6835 
   6836  SwapOfferAnswerRoles();
   6837  OfferAnswer(CHECK_SUCCESS);
   6838 
   6839  // Ok. Now renegotiating should recycle m-section 0.
   6840  AddTracks(*mSessionOff, "audio", NO_ADDTRACK_MAGIC);
   6841  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6842  OfferAnswer(CHECK_SUCCESS);
   6843 
   6844  // Transceiver 2 should now be attached to m-section 0
   6845  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6846  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel());
   6847  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6848  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6849  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6850  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6851  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6852 
   6853  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   6854  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel());
   6855  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6856  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6857  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6858  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[2].GetLevel());
   6859  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   6860 }
   6861 
   6862 TEST_F(JsepSessionTest, RecycleRollback) {
   6863  types = BuildTypes("audio,video");
   6864  AddTracks(*mSessionOff);
   6865  AddTracks(*mSessionAns);
   6866 
   6867  OfferAnswer();
   6868 
   6869  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6870  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6871  GetTransceivers(*mSessionOff)[0].Stop();
   6872 
   6873  OfferAnswer(CHECK_SUCCESS);
   6874 
   6875  AddTracks(*mSessionOff, "audio");
   6876 
   6877  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6878  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel());
   6879  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6880  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated());
   6881  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6882  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6883  ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated());
   6884  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].HasLevel());
   6885  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6886  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsAssociated());
   6887 
   6888  std::string offer = CreateOffer();
   6889  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6890  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel());
   6891  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6892  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated());
   6893  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6894  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6895  ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated());
   6896  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6897  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6898  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsAssociated());
   6899 
   6900  SetLocalOffer(offer, CHECK_SUCCESS);
   6901 
   6902  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6903  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel());
   6904  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6905  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated());
   6906  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6907  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6908  ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated());
   6909  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6910  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6911  // This should now be associated
   6912  ASSERT_TRUE(GetTransceivers(*mSessionOff)[2].IsAssociated());
   6913 
   6914  ASSERT_FALSE(
   6915      mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError.isSome());
   6916 
   6917  // Rollback should not change the levels of any of these, since those are set
   6918  // in CreateOffer.
   6919  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6920  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel());
   6921  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6922  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated());
   6923  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6924  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6925  ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated());
   6926  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6927  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6928  // This should no longer be associated
   6929  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsAssociated());
   6930 }
   6931 
   6932 TEST_F(JsepSessionTest, AddTrackMagicWithNullReplaceTrack) {
   6933  types = BuildTypes("audio,video");
   6934  AddTracks(*mSessionOff);
   6935  AddTracks(*mSessionAns);
   6936 
   6937  OfferAnswer();
   6938 
   6939  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   6940  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   6941 
   6942  AddTracks(*mSessionAns, "audio");
   6943  AddTracks(*mSessionOff, "audio");
   6944 
   6945  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   6946  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   6947  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6948  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   6949  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6950  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6951  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   6952  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel());
   6953  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   6954  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   6955  ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   6956 
   6957  // Ok, transceiver 2 is "magical". Ensure it still has this "magical"
   6958  // auto-matching property even if we null it out with replaceTrack.
   6959  GetTransceivers(*mSessionAns)[2].mSendTrack.ClearStreamIds();
   6960  GetTransceivers(*mSessionAns)[2].mJsDirection =
   6961      SdpDirectionAttribute::Direction::kRecvonly;
   6962 
   6963  OfferAnswer(CHECK_SUCCESS);
   6964 
   6965  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   6966  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   6967  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   6968  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   6969  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   6970  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   6971  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   6972  ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[2].GetLevel());
   6973  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   6974  ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   6975  ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   6976 
   6977  ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size());
   6978  ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel());
   6979  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsStopped());
   6980  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsAssociated());
   6981  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel());
   6982  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped());
   6983  ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated());
   6984  ASSERT_EQ(2U, GetTransceivers(*mSessionOff)[2].GetLevel());
   6985  ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped());
   6986  ASSERT_TRUE(GetTransceivers(*mSessionOff)[2].IsAssociated());
   6987  ASSERT_TRUE(GetTransceivers(*mSessionOff)[2].HasAddTrackMagic());
   6988 }
   6989 
   6990 // Flipside of AddTrackMagicWithNullReplaceTrack; we want to check that
   6991 // auto-matching does not work for transceivers that were created without a
   6992 // track, but were later given a track with replaceTrack.
   6993 TEST_F(JsepSessionTest, NoAddTrackMagicReplaceTrack) {
   6994  types = BuildTypes("audio,video");
   6995  AddTracks(*mSessionOff);
   6996  AddTracks(*mSessionAns);
   6997 
   6998  OfferAnswer();
   6999 
   7000  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   7001  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   7002  AddTracks(*mSessionOff, "audio");
   7003  mSessionAns->AddTransceiver(
   7004      JsepTransceiver(SdpMediaSection::MediaType::kAudio, mUuidGen));
   7005 
   7006  GetTransceivers(*mSessionAns)[2].mSendTrack.UpdateStreamIds({"newstream"});
   7007 
   7008  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   7009  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   7010  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   7011  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   7012  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   7013  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   7014  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   7015  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel());
   7016  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   7017  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   7018  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   7019 
   7020  OfferAnswer(CHECK_SUCCESS);
   7021 
   7022  ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size());
   7023  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   7024  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   7025  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   7026  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   7027  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   7028  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   7029  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel());
   7030  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   7031  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   7032  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   7033  ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[3].GetLevel());
   7034  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsStopped());
   7035  ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsAssociated());
   7036 }
   7037 
   7038 TEST_F(JsepSessionTest, AddTrackDoesNotMakeTransceiverMagical) {
   7039  types = BuildTypes("audio,video");
   7040  AddTracks(*mSessionOff);
   7041  AddTracks(*mSessionAns);
   7042 
   7043  OfferAnswer();
   7044 
   7045  ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size());
   7046  ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size());
   7047  AddTracks(*mSessionOff, "audio");
   7048  mSessionAns->AddTransceiver(
   7049      JsepTransceiver(SdpMediaSection::MediaType::kAudio, mUuidGen));
   7050 
   7051  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   7052  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   7053  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   7054  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   7055  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   7056  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   7057  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   7058  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel());
   7059  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   7060  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   7061  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   7062 
   7063  // This attaches a track to the third transceiver, but does _not_ set the
   7064  // addTrack magic bit, meaning it will not auto-pair with the track added
   7065  // to the offerer.
   7066  AddTracks(*mSessionAns, "audio");
   7067 
   7068  ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size());
   7069  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   7070  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   7071  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   7072  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   7073  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   7074  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   7075  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel());
   7076  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   7077  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   7078  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   7079 
   7080  OfferAnswer(CHECK_SUCCESS);
   7081 
   7082  // The offer's new transceiver does not pair up with the transceiver we added
   7083  ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size());
   7084  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel());
   7085  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   7086  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   7087  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel());
   7088  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   7089  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   7090  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel());
   7091  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   7092  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   7093  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   7094  ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[3].GetLevel());
   7095  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsStopped());
   7096  ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsAssociated());
   7097  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].HasAddTrackMagic());
   7098 }
   7099 
   7100 TEST_F(JsepSessionTest, ComplicatedRemoteRollback) {
   7101  AddTracks(*mSessionOff, "audio,audio,audio,video");
   7102  AddTracks(*mSessionAns, "video,video");
   7103 
   7104  std::string offer = CreateOffer();
   7105  SetLocalOffer(offer, CHECK_SUCCESS);
   7106  SetRemoteOffer(offer, CHECK_SUCCESS);
   7107 
   7108  // Three recvonly for audio, one sendrecv for video, and one (unmapped) for
   7109  // the second video track.
   7110  ASSERT_EQ(5U, GetTransceivers(*mSessionAns).size());
   7111  // First video transceiver; auto matched with offer
   7112  ASSERT_EQ(3U, GetTransceivers(*mSessionAns)[0].GetLevel());
   7113  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   7114  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   7115  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].HasAddTrackMagic());
   7116  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].OnlyExistsBecauseOfSetRemote());
   7117 
   7118  // Second video transceiver, not matched with offer
   7119  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].HasLevel());
   7120  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   7121  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   7122  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].HasAddTrackMagic());
   7123  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].OnlyExistsBecauseOfSetRemote());
   7124 
   7125  // Audio transceiver, created due to application of SetRemote
   7126  ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[2].GetLevel());
   7127  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   7128  ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   7129  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   7130  ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].OnlyExistsBecauseOfSetRemote());
   7131 
   7132  // Audio transceiver, created due to application of SetRemote
   7133  ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[3].GetLevel());
   7134  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsStopped());
   7135  ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsAssociated());
   7136  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].HasAddTrackMagic());
   7137  ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].OnlyExistsBecauseOfSetRemote());
   7138 
   7139  // Audio transceiver, created due to application of SetRemote
   7140  ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[4].GetLevel());
   7141  ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].IsStopped());
   7142  ASSERT_TRUE(GetTransceivers(*mSessionAns)[4].IsAssociated());
   7143  ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].HasAddTrackMagic());
   7144  ASSERT_TRUE(GetTransceivers(*mSessionAns)[4].OnlyExistsBecauseOfSetRemote());
   7145 
   7146  // This will prevent rollback from eating this transceiver, even though we
   7147  // call replaceTrack(null) on it.
   7148  AddTracks(*mSessionAns, "audio");
   7149  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   7150  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].OnlyExistsBecauseOfSetRemote());
   7151  GetTransceivers(*mSessionAns)[2].mSendTrack.ClearStreamIds();
   7152  GetTransceivers(*mSessionAns)[2].mJsDirection =
   7153      SdpDirectionAttribute::Direction::kRecvonly;
   7154 
   7155  // We do nothing with the second audio transceiver; when we rollback, it will
   7156  // be marked as removed.
   7157 
   7158  // This will not cause the third audio transceiver to stick around; having a
   7159  // track is _not_ enough to preserve it. It must have addTrack "magic"!
   7160  GetTransceivers(*mSessionAns)[4].mSendTrack.UpdateStreamIds({"newstream"});
   7161 
   7162  // Create a fourth audio transceiver. Rollback will leave it alone, since we
   7163  // created it.
   7164  mSessionAns->AddTransceiver(
   7165      JsepTransceiver(SdpMediaSection::MediaType::kAudio, mUuidGen,
   7166                      SdpDirectionAttribute::Direction::kRecvonly));
   7167 
   7168  ASSERT_FALSE(
   7169      mSessionAns->SetRemoteDescription(kJsepSdpRollback, "").mError.isSome());
   7170 
   7171  // Two of these (3 and 4) will be marked removed, if this all worked
   7172  ASSERT_EQ(6U, GetTransceivers(*mSessionAns).size());
   7173 
   7174  // First video transceiver
   7175  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel());
   7176  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped());
   7177  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsAssociated());
   7178  ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].HasAddTrackMagic());
   7179  ASSERT_FALSE(IsNull(GetTransceivers(*mSessionAns)[0].mSendTrack));
   7180  ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsRemoved());
   7181 
   7182  // Second video transceiver
   7183  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].HasLevel());
   7184  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped());
   7185  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsAssociated());
   7186  ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].HasAddTrackMagic());
   7187  ASSERT_FALSE(IsNull(GetTransceivers(*mSessionAns)[1].mSendTrack));
   7188  ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsRemoved());
   7189 
   7190  // First audio transceiver, kept because AddTrack touched it, even though we
   7191  // removed the send track after. Gets magic because that's what other browsers
   7192  // do (ugh).
   7193  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel());
   7194  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped());
   7195  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated());
   7196  ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic());
   7197  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].OnlyExistsBecauseOfSetRemote());
   7198  ASSERT_TRUE(IsNull(GetTransceivers(*mSessionAns)[2].mSendTrack));
   7199  ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsRemoved());
   7200 
   7201  // Second audio transceiver should be gone.
   7202  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].HasLevel());
   7203  ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsStopped());
   7204  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsAssociated());
   7205  ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].HasAddTrackMagic());
   7206  ASSERT_TRUE(IsNull(GetTransceivers(*mSessionAns)[3].mSendTrack));
   7207  ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsRemoved());
   7208 
   7209  // Third audio transceiver should also be gone.
   7210  ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].HasLevel());
   7211  ASSERT_TRUE(GetTransceivers(*mSessionAns)[4].IsStopped());
   7212  ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].IsAssociated());
   7213  ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].HasAddTrackMagic());
   7214  ASSERT_TRUE(IsNull(GetTransceivers(*mSessionAns)[4].mSendTrack));
   7215  ASSERT_TRUE(GetTransceivers(*mSessionAns)[4].IsRemoved());
   7216 
   7217  // Fourth audio transceiver, created after SetRemote
   7218  ASSERT_FALSE(GetTransceivers(*mSessionAns)[5].HasLevel());
   7219  ASSERT_FALSE(GetTransceivers(*mSessionAns)[5].IsStopped());
   7220  ASSERT_FALSE(GetTransceivers(*mSessionAns)[5].IsAssociated());
   7221  ASSERT_FALSE(GetTransceivers(*mSessionAns)[5].HasAddTrackMagic());
   7222  ASSERT_TRUE(
   7223      GetTransceivers(*mSessionAns)[5].mSendTrack.GetStreamIds().empty());
   7224 }
   7225 
   7226 TEST_F(JsepSessionTest, LocalRollback) {
   7227  AddTracks(*mSessionOff, "audio,video");
   7228  AddTracks(*mSessionAns, "audio,video");
   7229 
   7230  std::string offer = CreateOffer();
   7231  SetLocalOffer(offer, CHECK_SUCCESS);
   7232 
   7233  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsAssociated());
   7234  ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated());
   7235  ASSERT_FALSE(
   7236      mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError.isSome());
   7237  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated());
   7238  ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsAssociated());
   7239 }
   7240 
   7241 TEST_F(JsepSessionTest, JsStopsTransceiverBeforeAnswer) {
   7242  AddTracks(*mSessionOff, "audio,video");
   7243  AddTracks(*mSessionAns, "audio,video");
   7244 
   7245  std::string offer = CreateOffer();
   7246  SetLocalOffer(offer, CHECK_SUCCESS);
   7247  SetRemoteOffer(offer, CHECK_SUCCESS);
   7248 
   7249  std::string answer = CreateAnswer();
   7250  SetLocalAnswer(answer, CHECK_SUCCESS);
   7251 
   7252  // Now JS decides to stop a transceiver. This should have absolutely no effect
   7253  // on JSEP this negotiation. Any effects will be on the JS side of things.
   7254  // JSEP _will_ advertise that negotiation is needed.
   7255 
   7256  GetTransceivers(*mSessionOff)[0].Stop();
   7257  SetRemoteAnswer(answer, CHECK_SUCCESS);
   7258 
   7259  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopping());
   7260  ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsStopped());
   7261  ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[0].mTransport.mComponents);
   7262  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].mSendTrack.GetActive());
   7263  ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].mRecvTrack.GetActive());
   7264  ASSERT_TRUE(mSessionOff->CheckNegotiationNeeded());
   7265 }
   7266 
   7267 TEST_F(JsepSessionTest, TestOfferPTAsymmetryRtxApt) {
   7268  for (auto& codec : mSessionAns->Codecs()) {
   7269    if (codec->mName == "VP8") {
   7270      JsepVideoCodecDescription* vp8 =
   7271          static_cast<JsepVideoCodecDescription*>(codec.get());
   7272      vp8->EnableRtx("42");
   7273      break;
   7274    }
   7275  }
   7276 
   7277  types.push_back(SdpMediaSection::kVideo);
   7278  AddTracks(*mSessionOff, "video");
   7279  AddTracks(*mSessionAns, "video");
   7280  JsepOfferOptions options;
   7281 
   7282  // Ensure that mSessionAns is appropriately configured.
   7283  std::string offer;
   7284  JsepSession::Result result = mSessionAns->CreateOffer(options, &offer);
   7285  ASSERT_FALSE(result.mError.isSome());
   7286  ASSERT_NE(std::string::npos, offer.find("a=rtpmap:42 rtx")) << offer;
   7287 
   7288  OfferAnswer();
   7289 
   7290  // Answerer should use what the offerer suggested
   7291  UniquePtr<JsepCodecDescription> codec;
   7292  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   7293  ASSERT_TRUE(codec);
   7294  ASSERT_EQ("VP8", codec->mName);
   7295  JsepVideoCodecDescription* vp8 =
   7296      static_cast<JsepVideoCodecDescription*>(codec.get());
   7297  ASSERT_EQ("120", vp8->mDefaultPt);
   7298  ASSERT_EQ("124", vp8->mRtxPayloadType);
   7299  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   7300  ASSERT_TRUE(codec);
   7301  ASSERT_EQ("VP8", codec->mName);
   7302  vp8 = static_cast<JsepVideoCodecDescription*>(codec.get());
   7303  ASSERT_EQ("120", vp8->mDefaultPt);
   7304  ASSERT_EQ("124", vp8->mRtxPayloadType);
   7305 
   7306  // Answerer should not change back when it reoffers
   7307  result = mSessionAns->CreateOffer(options, &offer);
   7308  ASSERT_FALSE(result.mError.isSome());
   7309  ASSERT_NE(std::string::npos, offer.find("a=rtpmap:124 rtx")) << offer;
   7310 }
   7311 
   7312 TEST_F(JsepSessionTest, TestAnswerPTAsymmetryRtx) {
   7313  // JsepSessionImpl will never answer with an asymmetric payload type
   7314  // (tested in TestOfferPTAsymmetry), so we have to rewrite SDP a little.
   7315  types.push_back(SdpMediaSection::kVideo);
   7316  AddTracks(*mSessionOff, "video");
   7317  AddTracks(*mSessionAns, "video");
   7318 
   7319  std::string offer = CreateOffer();
   7320  SetLocalOffer(offer);
   7321 
   7322  Replace("a=rtpmap:120 VP8", "a=rtpmap:119 VP8", &offer);
   7323  Replace("m=video 9 UDP/TLS/RTP/SAVPF 120", "m=video 9 UDP/TLS/RTP/SAVPF 119",
   7324          &offer);
   7325  ReplaceAll("a=fmtp:120", "a=fmtp:119", &offer);
   7326  ReplaceAll("a=fmtp:122 120", "a=fmtp:122 119", &offer);
   7327  ReplaceAll("a=fmtp:124 apt=120", "a=fmtp:124 apt=119", &offer);
   7328  ReplaceAll("a=rtcp-fb:120", "a=rtcp-fb:119", &offer);
   7329 
   7330  SetRemoteOffer(offer);
   7331 
   7332  std::string answer = CreateAnswer();
   7333  SetLocalAnswer(answer);
   7334  SetRemoteAnswer(answer);
   7335 
   7336  UniquePtr<JsepCodecDescription> codec;
   7337  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &codec);
   7338  ASSERT_TRUE(codec);
   7339  ASSERT_EQ("VP8", codec->mName);
   7340  ASSERT_EQ("119", codec->mDefaultPt);
   7341  JsepVideoCodecDescription* vp8 =
   7342      static_cast<JsepVideoCodecDescription*>(codec.get());
   7343  ASSERT_EQ("124", vp8->mRtxPayloadType);
   7344  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &codec);
   7345  ASSERT_TRUE(codec);
   7346  ASSERT_EQ("VP8", codec->mName);
   7347  ASSERT_EQ("120", codec->mDefaultPt);
   7348  vp8 = static_cast<JsepVideoCodecDescription*>(codec.get());
   7349  ASSERT_EQ("124", vp8->mRtxPayloadType);
   7350 
   7351  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   7352  ASSERT_TRUE(codec);
   7353  ASSERT_EQ("VP8", codec->mName);
   7354  ASSERT_EQ("119", codec->mDefaultPt);
   7355  vp8 = static_cast<JsepVideoCodecDescription*>(codec.get());
   7356  ASSERT_EQ("124", vp8->mRtxPayloadType);
   7357  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   7358  ASSERT_TRUE(codec);
   7359  ASSERT_EQ("VP8", codec->mName);
   7360  ASSERT_EQ("119", codec->mDefaultPt);
   7361  vp8 = static_cast<JsepVideoCodecDescription*>(codec.get());
   7362  ASSERT_EQ("124", vp8->mRtxPayloadType);
   7363 }
   7364 
   7365 TEST_F(JsepSessionTest, TestAnswerPTAsymmetryRtxApt) {
   7366  // JsepSessionImpl will never answer with an asymmetric payload type
   7367  // so we have to rewrite SDP a little.
   7368  types.push_back(SdpMediaSection::kVideo);
   7369  AddTracks(*mSessionOff, "video");
   7370  AddTracks(*mSessionAns, "video");
   7371 
   7372  std::string offer = CreateOffer();
   7373  SetLocalOffer(offer);
   7374 
   7375  Replace("a=rtpmap:124 rtx", "a=rtpmap:42 rtx", &offer);
   7376  Replace("m=video 9 UDP/TLS/RTP/SAVPF 120 124",
   7377          "m=video 9 UDP/TLS/RTP/SAVPF 120 42", &offer);
   7378  ReplaceAll("a=fmtp:124", "a=fmtp:42", &offer);
   7379 
   7380  SetRemoteOffer(offer);
   7381 
   7382  std::string answer = CreateAnswer();
   7383  SetLocalAnswer(answer);
   7384  SetRemoteAnswer(answer);
   7385 
   7386  UniquePtr<JsepCodecDescription> codec;
   7387  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &codec);
   7388  ASSERT_TRUE(codec);
   7389  ASSERT_EQ("VP8", codec->mName);
   7390  ASSERT_EQ("120", codec->mDefaultPt);
   7391  JsepVideoCodecDescription* vp8 =
   7392      static_cast<JsepVideoCodecDescription*>(codec.get());
   7393  ASSERT_EQ("42", vp8->mRtxPayloadType);
   7394  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &codec);
   7395  ASSERT_TRUE(codec);
   7396  ASSERT_EQ("VP8", codec->mName);
   7397  ASSERT_EQ("120", codec->mDefaultPt);
   7398  vp8 = static_cast<JsepVideoCodecDescription*>(codec.get());
   7399  ASSERT_EQ("124", vp8->mRtxPayloadType);
   7400 
   7401  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   7402  ASSERT_TRUE(codec);
   7403  ASSERT_EQ("VP8", codec->mName);
   7404  vp8 = static_cast<JsepVideoCodecDescription*>(codec.get());
   7405  ASSERT_EQ("120", vp8->mDefaultPt);
   7406  ASSERT_EQ("42", vp8->mRtxPayloadType);
   7407  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   7408  ASSERT_TRUE(codec);
   7409  ASSERT_EQ("VP8", codec->mName);
   7410  vp8 = static_cast<JsepVideoCodecDescription*>(codec.get());
   7411  ASSERT_EQ("120", vp8->mDefaultPt);
   7412  ASSERT_EQ("42", vp8->mRtxPayloadType);
   7413 }
   7414 
   7415 TEST_F(JsepSessionTest, TestOfferNoRtx) {
   7416  for (auto& codec : mSessionOff->Codecs()) {
   7417    if (codec->Type() == SdpMediaSection::kVideo) {
   7418      JsepVideoCodecDescription* videoCodec =
   7419          static_cast<JsepVideoCodecDescription*>(codec.get());
   7420      videoCodec->mRtxEnabled = false;
   7421    }
   7422  }
   7423 
   7424  types.push_back(SdpMediaSection::kVideo);
   7425  AddTracks(*mSessionOff, "video");
   7426  AddTracks(*mSessionAns, "video");
   7427  JsepOfferOptions options;
   7428 
   7429  std::string offer;
   7430  JsepSession::Result result = mSessionOff->CreateOffer(options, &offer);
   7431  ASSERT_FALSE(result.mError.isSome());
   7432  ASSERT_EQ(std::string::npos, offer.find("rtx")) << offer;
   7433 
   7434  OfferAnswer();
   7435 
   7436  // Answerer should use what the offerer suggested
   7437  UniquePtr<JsepCodecDescription> codec;
   7438  for (size_t i = 0; i < 4; ++i) {
   7439    GetCodec(*mSessionAns, 0, sdp::kSend, 0, i, &codec);
   7440    ASSERT_TRUE(codec);
   7441    JsepVideoCodecDescription* videoCodec =
   7442        static_cast<JsepVideoCodecDescription*>(codec.get());
   7443    ASSERT_FALSE(videoCodec->mRtxEnabled);
   7444    GetCodec(*mSessionAns, 0, sdp::kRecv, 0, i, &codec);
   7445    ASSERT_TRUE(codec);
   7446    videoCodec = static_cast<JsepVideoCodecDescription*>(codec.get());
   7447    ASSERT_FALSE(videoCodec->mRtxEnabled);
   7448  }
   7449 }
   7450 
   7451 TEST_F(JsepSessionTest, TestOneWayRtx) {
   7452  for (auto& codec : mSessionAns->Codecs()) {
   7453    if (codec->Type() == SdpMediaSection::kVideo) {
   7454      JsepVideoCodecDescription* videoCodec =
   7455          static_cast<JsepVideoCodecDescription*>(codec.get());
   7456      videoCodec->mRtxEnabled = false;
   7457    }
   7458  }
   7459 
   7460  types.push_back(SdpMediaSection::kVideo);
   7461  AddTracks(*mSessionOff, "video");
   7462  AddTracks(*mSessionAns, "video");
   7463  JsepOfferOptions options;
   7464 
   7465  std::string offer;
   7466  JsepSession::Result result = mSessionAns->CreateOffer(options, &offer);
   7467  ASSERT_FALSE(result.mError.isSome());
   7468  ASSERT_EQ(std::string::npos, offer.find("rtx")) << offer;
   7469 
   7470  OfferAnswer();
   7471 
   7472  // If the answerer does not support rtx, the offerer should not send it,
   7473  // but it is too late to turn off recv on the offerer side.
   7474  UniquePtr<JsepCodecDescription> codec;
   7475  for (size_t i = 0; i < 4; ++i) {
   7476    GetCodec(*mSessionOff, 0, sdp::kSend, 0, i, &codec);
   7477    ASSERT_TRUE(codec);
   7478    JsepVideoCodecDescription* videoCodec =
   7479        static_cast<JsepVideoCodecDescription*>(codec.get());
   7480    ASSERT_FALSE(videoCodec->mRtxEnabled);
   7481    GetCodec(*mSessionOff, 0, sdp::kRecv, 0, i, &codec);
   7482    ASSERT_TRUE(codec);
   7483    videoCodec = static_cast<JsepVideoCodecDescription*>(codec.get());
   7484    ASSERT_TRUE(videoCodec->mRtxEnabled);
   7485  }
   7486 }
   7487 
   7488 TEST_F(JsepSessionTest, TestRtxNoSsrcGroup) {
   7489  mSessionOff->AddTransceiver(JsepTransceiver(
   7490      SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kRecvonly));
   7491 
   7492  OfferAnswer(CHECK_SUCCESS);
   7493 
   7494  std::string offer = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent);
   7495  ASSERT_EQ(std::string::npos, offer.find("FID")) << offer;
   7496 
   7497  std::string answer =
   7498      mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent);
   7499  ASSERT_EQ(std::string::npos, answer.find("FID")) << answer;
   7500 }
   7501 
   7502 TEST_F(JsepSessionTest, TestRtxSsrcGroupOnlyOffered) {
   7503  mSessionOff->AddTransceiver(JsepTransceiver(
   7504      SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kSendonly));
   7505 
   7506  OfferAnswer(CHECK_SUCCESS);
   7507 
   7508  std::string offer = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent);
   7509  ASSERT_NE(std::string::npos, offer.find("FID")) << offer;
   7510 
   7511  std::string answer =
   7512      mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent);
   7513  ASSERT_EQ(std::string::npos, answer.find("FID")) << answer;
   7514 }
   7515 
   7516 TEST_F(JsepSessionTest, TestOfferRtxNoMsid) {
   7517  for (auto& codec : mSessionOff->Codecs()) {
   7518    if (codec->mName == "VP8") {
   7519      JsepVideoCodecDescription* vp8 =
   7520          static_cast<JsepVideoCodecDescription*>(codec.get());
   7521      vp8->EnableRtx("42");
   7522      break;
   7523    }
   7524  }
   7525 
   7526  types.push_back(SdpMediaSection::kVideo);
   7527  AddTracks(*mSessionOff, "video");
   7528 
   7529  mSessionOff->ForEachTransceiver([this](JsepTransceiver& aTransceiver) {
   7530    if (!IsNull(aTransceiver.mSendTrack)) {
   7531      std::vector<std::string> empty;
   7532      aTransceiver.mSendTrack.UpdateStreamIds(empty);
   7533    }
   7534  });
   7535 
   7536  // MSID stream absence should not influence FID ssrc-group
   7537  JsepOfferOptions options;
   7538  std::string offer;
   7539  JsepSession::Result result = mSessionOff->CreateOffer(options, &offer);
   7540  ASSERT_FALSE(result.mError.isSome());
   7541  ASSERT_NE(std::string::npos, offer.find("FID")) << offer;
   7542 }
   7543 
   7544 TEST_F(JsepSessionTest, TestRedRtxAddedToVideoCodec) {
   7545  types.push_back(SdpMediaSection::kVideo);
   7546  AddTracks(*mSessionOff, "video");
   7547  AddTracks(*mSessionAns, "video");
   7548 
   7549  OfferAnswer();
   7550 
   7551  std::vector<sdp::Direction> directions = {sdp::kSend, sdp::kRecv};
   7552 
   7553  for (auto direction : directions) {
   7554    UniquePtr<JsepCodecDescription> codec;
   7555    std::set<std::string> payloadTypes;
   7556    std::string redPt, ulpfecPt, redRtxPt;
   7557    for (size_t i = 0; i < 4; ++i) {
   7558      GetCodec(*mSessionOff, 0, direction, 0, i, &codec);
   7559      ASSERT_TRUE(codec);
   7560      JsepVideoCodecDescription* videoCodec =
   7561          static_cast<JsepVideoCodecDescription*>(codec.get());
   7562 
   7563      // Ensure RED, ULPFEC, and RTX RED are not empty which validates that
   7564      // EnableFEC worked.
   7565      ASSERT_FALSE(videoCodec->mREDPayloadType.empty());
   7566      ASSERT_FALSE(videoCodec->mULPFECPayloadType.empty());
   7567      ASSERT_FALSE(videoCodec->mREDRTXPayloadType.empty());
   7568      ASSERT_TRUE(payloadTypes.insert(videoCodec->mDefaultPt).second);
   7569      ASSERT_TRUE(payloadTypes.insert(videoCodec->mRtxPayloadType).second);
   7570      // ULPFEC, RED, and RED RTX payload types are the same for each codec, so
   7571      // we only check them for the first one.
   7572      if (i == 0) {
   7573        ASSERT_TRUE(payloadTypes.insert(videoCodec->mREDPayloadType).second)
   7574        << "RED is using a duplicate payload type.";
   7575        ASSERT_TRUE(payloadTypes.insert(videoCodec->mULPFECPayloadType).second)
   7576        << "ULPFEC is using a duplicate payload type.";
   7577        ASSERT_TRUE(payloadTypes.insert(videoCodec->mREDRTXPayloadType).second)
   7578        << "RED RTX is using a duplicate payload type.";
   7579        redPt = videoCodec->mREDPayloadType;
   7580        ulpfecPt = videoCodec->mULPFECPayloadType;
   7581        redRtxPt = videoCodec->mREDRTXPayloadType;
   7582      } else {
   7583        ASSERT_TRUE(redPt == videoCodec->mREDPayloadType);
   7584        ASSERT_TRUE(ulpfecPt == videoCodec->mULPFECPayloadType);
   7585        ASSERT_TRUE(redRtxPt == videoCodec->mREDRTXPayloadType);
   7586      }
   7587    }
   7588  }
   7589 }
   7590 
   7591 TEST_P(JsepSessionTest, TestNegotiatedDetailsToVideoCodecConfigs) {
   7592  AddTracks(*mSessionOff);
   7593  AddTracks(*mSessionAns);
   7594  OfferAnswer();
   7595 
   7596  // Check all the video tracks to ensure negotiated details is added to
   7597  // VideoCodecConfig. Especially information related to FEC, RED, and RED RTX.
   7598  std::vector<JsepTrack> tracks;
   7599  for (const auto& transceiver : GetTransceivers(*mSessionOff)) {
   7600    tracks.push_back(transceiver.mSendTrack);
   7601    tracks.push_back(transceiver.mRecvTrack);
   7602  }
   7603 
   7604  for (const JsepTrack& track : tracks) {
   7605    if (track.GetMediaType() != SdpMediaSection::kVideo) {
   7606      continue;
   7607    }
   7608 
   7609    const auto& details(*track.GetNegotiatedDetails());
   7610    std::vector<VideoCodecConfig> videoConfigs;
   7611    dom::RTCRtpTransceiver::NegotiatedDetailsToVideoCodecConfigs(details,
   7612                                                                 &videoConfigs);
   7613    ASSERT_FALSE(videoConfigs.empty());
   7614    ASSERT_EQ(1U, details.GetEncodingCount());
   7615 
   7616    const JsepTrackEncoding& encoding = details.GetEncoding(0);
   7617 
   7618    ASSERT_EQ(encoding.GetCodecs().size(), videoConfigs.size());
   7619    // Since encodings and videoConfigs is the same size and order we can loop
   7620    // through them both at the same time and validate that videoConfigs
   7621    // contains the expected encoding data from negotiated details.
   7622    for (unsigned int i = 0; i < videoConfigs.size(); i++) {
   7623      const JsepVideoCodecDescription& codec =
   7624          static_cast<const JsepVideoCodecDescription&>(
   7625              *encoding.GetCodecs().at(i));
   7626      const auto& config = videoConfigs.at(i);
   7627 
   7628      uint16_t payloadType;
   7629      ASSERT_TRUE(codec.GetPtAsInt(&payloadType));
   7630      ASSERT_EQ(payloadType, config.mType);
   7631      ASSERT_EQ(codec.mName, config.mName);
   7632      ASSERT_EQ(codec.RtcpFbRembIsSet(), config.mRembFbSet);
   7633      ASSERT_EQ(codec.mFECEnabled, config.mFECFbSet);
   7634      ASSERT_EQ(codec.RtcpFbTransportCCIsSet(), config.mTransportCCFbSet);
   7635      ASSERT_EQ(details.GetTias(), config.mTias);
   7636      ASSERT_EQ(codec.mConstraints, config.mEncodingConstraints);
   7637 
   7638      if (codec.mName == "H264") {
   7639        ASSERT_EQ((codec.mProfileLevelId & 0x00FF0000) >> 16, config.mProfile);
   7640        ASSERT_EQ((codec.mProfileLevelId & 0x0000FF00) >> 8,
   7641                  config.mConstraints);
   7642        ASSERT_EQ(codec.mProfileLevelId & 0x000000FF, config.mLevel);
   7643        ASSERT_EQ(codec.mPacketizationMode, config.mPacketizationMode);
   7644        ASSERT_EQ(codec.mSpropParameterSets, config.mSpropParameterSets);
   7645      }
   7646 
   7647      if (codec.mFECEnabled) {
   7648        uint16_t redPayloadType, ulpFecPayloadType, redRtxPayloadType;
   7649 
   7650        ASSERT_TRUE(
   7651            SdpHelper::GetPtAsInt(codec.mREDPayloadType, &redPayloadType));
   7652        ASSERT_TRUE(SdpHelper::GetPtAsInt(codec.mULPFECPayloadType,
   7653                                          &ulpFecPayloadType));
   7654        ASSERT_TRUE(SdpHelper::GetPtAsInt(codec.mREDRTXPayloadType,
   7655                                          &redRtxPayloadType));
   7656 
   7657        ASSERT_EQ(redPayloadType, config.mREDPayloadType);
   7658        ASSERT_EQ(ulpFecPayloadType, config.mULPFECPayloadType);
   7659        ASSERT_EQ(redRtxPayloadType, config.mREDRTXPayloadType);
   7660      }
   7661 
   7662      if (codec.mRtxEnabled) {
   7663        uint16_t rtxPayloadType;
   7664 
   7665        ASSERT_TRUE(
   7666            SdpHelper::GetPtAsInt(codec.mRtxPayloadType, &rtxPayloadType));
   7667        ASSERT_EQ(rtxPayloadType, config.mRTXPayloadType);
   7668      }
   7669    }
   7670  }
   7671 }
   7672 
   7673 TEST_F(JsepSessionTest, TestDuplicatePayloadTypes) {
   7674  for (auto& codec : mSessionOff->Codecs()) {
   7675    if (codec->Type() == SdpMediaSection::kVideo) {
   7676      JsepVideoCodecDescription* videoCodec =
   7677          static_cast<JsepVideoCodecDescription*>(codec.get());
   7678      videoCodec->mRtxPayloadType = "97";
   7679      videoCodec->EnableFec("97", "97", "97");
   7680    }
   7681  }
   7682 
   7683  types.push_back(SdpMediaSection::kVideo);
   7684  AddTracks(*mSessionOff, "video");
   7685  AddTracks(*mSessionAns, "video");
   7686 
   7687  OfferAnswer();
   7688 
   7689  std::vector<sdp::Direction> directions = {sdp::kSend, sdp::kRecv};
   7690  for (auto direction : directions) {
   7691    UniquePtr<JsepCodecDescription> codec;
   7692    std::set<std::string> payloadTypes;
   7693    std::string redPt, ulpfecPt, redRtxPt;
   7694    for (size_t i = 0; i < 4; ++i) {
   7695      GetCodec(*mSessionOff, 0, direction, 0, i, &codec);
   7696      ASSERT_TRUE(codec);
   7697      JsepVideoCodecDescription* videoCodec =
   7698          static_cast<JsepVideoCodecDescription*>(codec.get());
   7699      ASSERT_TRUE(payloadTypes.insert(videoCodec->mDefaultPt).second);
   7700      ASSERT_TRUE(payloadTypes.insert(videoCodec->mRtxPayloadType).second);
   7701      // ULPFEC and RED payload types are the same for each codec, so we only
   7702      // check them for the first one.
   7703      if (i == 0) {
   7704        ASSERT_TRUE(payloadTypes.insert(videoCodec->mREDPayloadType).second);
   7705        ASSERT_TRUE(payloadTypes.insert(videoCodec->mULPFECPayloadType).second);
   7706        ASSERT_TRUE(payloadTypes.insert(videoCodec->mREDRTXPayloadType).second);
   7707        redPt = videoCodec->mREDPayloadType;
   7708        ulpfecPt = videoCodec->mULPFECPayloadType;
   7709        redRtxPt = videoCodec->mREDRTXPayloadType;
   7710      } else {
   7711        ASSERT_TRUE(redPt == videoCodec->mREDPayloadType);
   7712        ASSERT_TRUE(ulpfecPt == videoCodec->mULPFECPayloadType);
   7713        ASSERT_TRUE(redRtxPt == videoCodec->mREDRTXPayloadType);
   7714      }
   7715    }
   7716  }
   7717 }
   7718 
   7719 TEST_F(JsepSessionTest, TestTransportAttributeValidation) {
   7720  const std::string sdpTemplate =
   7721      "v=0\r\n"
   7722      "o=- 6 2 IN IP4 1r\r\n"
   7723      "t=0 0a\r\n"
   7724      "a=group:BUNDLE audio video\r\n"
   7725      "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n"
   7726      "c=IN IP4 51.81.107.13\r\n"
   7727      "a=ice-ufrag:Xp\r\n"
   7728      "a=ice-pwd:he\r\n"
   7729      "a=setup:actpass\r\n"
   7730      "a=fingerprint:sha-256 "
   7731      "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:"
   7732      "08:D2:F7:9D:F5:E2:C1:15\r\n"
   7733      "a=sendrecv\r\n"
   7734      "a=extmap:11 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"
   7735      "a=fmtp:111 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n"
   7736      "a=mid:audio\r\n"
   7737      "a=rtcp-mux\r\n"
   7738      "a=rtpmap:111 opus/48000/2\r\n"
   7739      "a=ssrc:3463672643 cname:{ec9a356a-8d2c-504e-9977-99070a51f929}\r\n"
   7740      "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n"
   7741      "c=IN IP4 51.81.107.13\r\n"
   7742      "a=rtpmap:100 VP8/90000\r\n"
   7743      "a=extmap:1 urn:ietf:params:rtp-hdrext:toffset\r\n"
   7744      "a=mid:video\r\n"
   7745      "a=ice-ufrag:Xp\r\n"
   7746      "a=ice-pwd:he\r\n"
   7747      "a=setup:actpass\r\n"
   7748      "a=fingerprint:sha-256 "
   7749      "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:"
   7750      "08:D2:F7:9D:F5:E2:C1:15\r\n";
   7751 
   7752  // Control case
   7753  {
   7754    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdpTemplate);
   7755    ASSERT_FALSE(result.mError.isSome());
   7756  }
   7757 
   7758  // Missing ufrag
   7759  {
   7760    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7761    parsed->GetMediaSection(0).GetAttributeList().RemoveAttribute(
   7762        SdpAttribute::kIceUfragAttribute);
   7763    auto sdp = parsed->ToString();
   7764    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7765    ASSERT_TRUE(result.mError.isSome());
   7766  }
   7767 
   7768  // Missing pwd, bundle tag
   7769  {
   7770    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7771    parsed->GetMediaSection(0).GetAttributeList().RemoveAttribute(
   7772        SdpAttribute::kIcePwdAttribute);
   7773    auto sdp = parsed->ToString();
   7774    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7775    ASSERT_TRUE(result.mError.isSome());
   7776  }
   7777 
   7778  // Missing setup, bundle tag
   7779  {
   7780    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7781    parsed->GetMediaSection(0).GetAttributeList().RemoveAttribute(
   7782        SdpAttribute::kSetupAttribute);
   7783    auto sdp = parsed->ToString();
   7784    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7785    ASSERT_TRUE(result.mError.isSome());
   7786  }
   7787 
   7788  // Invalid setup attribute (holdconn), bundle tag
   7789  {
   7790    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7791    parsed->GetMediaSection(0).GetAttributeList().SetAttribute(
   7792        new SdpSetupAttribute(SdpSetupAttribute::kHoldconn));
   7793    auto sdp = parsed->ToString();
   7794    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7795    ASSERT_TRUE(result.mError.isSome());
   7796  }
   7797 
   7798  // Missing fingerprint, bundle tag
   7799  {
   7800    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7801    parsed->GetMediaSection(0).GetAttributeList().RemoveAttribute(
   7802        SdpAttribute::kFingerprintAttribute);
   7803    auto sdp = parsed->ToString();
   7804    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7805    ASSERT_TRUE(result.mError.isSome());
   7806  }
   7807 
   7808  // Unknown fingerprint algorithm
   7809  {
   7810    std::string mungedSdp = sdpTemplate;
   7811    ReplaceAll("fingerprint:sha", "fingerprint:foo", &mungedSdp);
   7812    UniquePtr<Sdp> parsed = Parse(mungedSdp);
   7813    auto sdp = parsed->ToString();
   7814    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7815    ASSERT_TRUE(result.mError.isSome());
   7816  }
   7817 
   7818  // Missing pwd, bundled msection without bundle-only
   7819  {
   7820    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7821    parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute(
   7822        SdpAttribute::kIcePwdAttribute);
   7823    auto sdp = parsed->ToString();
   7824    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7825    ASSERT_TRUE(result.mError.isSome());
   7826  }
   7827 
   7828  // Missing setup, bundled msection without bundle-only
   7829  {
   7830    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7831    parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute(
   7832        SdpAttribute::kSetupAttribute);
   7833    auto sdp = parsed->ToString();
   7834    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7835    ASSERT_TRUE(result.mError.isSome());
   7836  }
   7837 
   7838  // Missing fingerprint, bundled msection without bundle-only
   7839  {
   7840    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7841    parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute(
   7842        SdpAttribute::kFingerprintAttribute);
   7843    auto sdp = parsed->ToString();
   7844    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7845    ASSERT_TRUE(result.mError.isSome());
   7846  }
   7847 
   7848  // Missing ufrag attribute, bundle-only msection
   7849  {
   7850    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7851    parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute(
   7852        SdpAttribute::kIceUfragAttribute);
   7853    parsed->GetMediaSection(1).GetAttributeList().SetAttribute(
   7854        new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
   7855    auto sdp = parsed->ToString();
   7856    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7857    ASSERT_FALSE(result.mError.isSome());
   7858  }
   7859 
   7860  // Missing pwd attribute, bundle-only msection
   7861  {
   7862    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7863    parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute(
   7864        SdpAttribute::kIcePwdAttribute);
   7865    parsed->GetMediaSection(1).GetAttributeList().SetAttribute(
   7866        new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
   7867    auto sdp = parsed->ToString();
   7868    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7869    ASSERT_FALSE(result.mError.isSome());
   7870  }
   7871 
   7872  // Missing fingerprint attribute, bundle-only msection
   7873  {
   7874    UniquePtr<Sdp> parsed = Parse(sdpTemplate);
   7875    parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute(
   7876        SdpAttribute::kFingerprintAttribute);
   7877    parsed->GetMediaSection(1).GetAttributeList().SetAttribute(
   7878        new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
   7879    auto sdp = parsed->ToString();
   7880    auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp);
   7881    ASSERT_FALSE(result.mError.isSome());
   7882  }
   7883 }
   7884 
   7885 TEST_F(JsepSessionTest, TestBundleSupportWithZeroPort) {
   7886  AddTracks(*mSessionOff, "audio,video,video,datachannel");
   7887  AddTracks(*mSessionAns, "audio,video,video,datachannel");
   7888 
   7889  std::string offer;
   7890  mSessionOff->CreateOffer(JsepOfferOptions(), &offer);
   7891 
   7892  mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
   7893  mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   7894 
   7895  std::string answer;
   7896  mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer);
   7897 
   7898  // Manipulate the bundling on the answer
   7899  UniquePtr<Sdp> sdp(Parse(answer));
   7900  ASSERT_TRUE(!!sdp);
   7901  size_t num_m_sections = sdp->GetMediaSectionCount();
   7902  for (size_t i = 0; i < num_m_sections; ++i) {
   7903    auto& msection = sdp->GetMediaSection(i);
   7904    const SdpAttributeList& attrs = msection.GetAttributeList();
   7905    // If this is not the last msection then ensure we have bundle only set and
   7906    // port 0.
   7907    if (!attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute) &&
   7908        i < num_m_sections - 1) {
   7909      sdp->GetMediaSection(i).GetAttributeList().SetAttribute(
   7910          new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
   7911      sdp->GetMediaSection(i).SetPort(0);
   7912    } else {
   7913      // For the last msection setting port to non 0 and removing bundle only if
   7914      // it existed.
   7915      if (attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute)) {
   7916        sdp->GetMediaSection(i).GetAttributeList().RemoveAttribute(
   7917            SdpAttribute::kBundleOnlyAttribute);
   7918      }
   7919      sdp->GetMediaSection(i).SetPort(9);
   7920    }
   7921  }
   7922  auto answerSdp = sdp->ToString();
   7923 
   7924  mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answerSdp);
   7925 
   7926  // Ensure all the transcievers are still active bug 1923416
   7927  for (const auto& offerTransceiver : GetTransceivers(*mSessionOff)) {
   7928    ASSERT_TRUE(offerTransceiver.mRecvTrack.GetActive());
   7929    ASSERT_TRUE(offerTransceiver.mSendTrack.GetActive());
   7930  }
   7931 }
   7932 
   7933 TEST_F(JsepSessionTest, ExtmapAllowMixedTrueWhenPrensentAtSessionLevel) {
   7934  AddTracks(*mSessionOff, "audio,video,datachannel");
   7935  AddTracks(*mSessionAns, "audio,video,datachannel");
   7936  std::string offer;
   7937  mSessionOff->CreateOffer(JsepOfferOptions(), &offer);
   7938 
   7939  // Remove extmap-allow-mixed from the media level
   7940  ReplaceAll("a=extmap-allow-mixed\r\n", "", &offer);
   7941 
   7942  // Add extmap-allow-mixed to the session level
   7943  Replace("m=audio", "a=extmap-allow-mixed\r\nm=audio", &offer);
   7944 
   7945  mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
   7946  mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   7947 
   7948  std::string answer;
   7949  mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer);
   7950 
   7951  mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
   7952  mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
   7953 
   7954  ASSERT_TRUE(ExtmapAllowMixed(*mSessionOff));
   7955  ASSERT_TRUE(ExtmapAllowMixed(*mSessionAns));
   7956 
   7957  mSessionAns->ForEachTransceiver([](JsepTransceiver& aTransceiver) {
   7958    if (aTransceiver.mSendTrack.GetMediaType()) {
   7959      ASSERT_TRUE(aTransceiver.mSendTrack.GetNegotiatedDetails()
   7960                      ->GetRtpRtcpConfig()
   7961                      .GetExtmapAllowMixed());
   7962    }
   7963  });
   7964 }
   7965 
   7966 TEST_F(JsepSessionTest, ExtmapAllowMixedCheckDoNotDefaultToSessionLevel) {
   7967  AddTracks(*mSessionOff, "audio,video,datachannel");
   7968  AddTracks(*mSessionAns, "audio,video,datachannel");
   7969 
   7970  std::string offer;
   7971  mSessionOff->CreateOffer(JsepOfferOptions(), &offer);
   7972 
   7973  mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
   7974  mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   7975 
   7976  std::string answer;
   7977  mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer);
   7978 
   7979  mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
   7980  mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
   7981 
   7982  ASSERT_FALSE(ExtmapAllowMixed(*mSessionOff));
   7983  ASSERT_FALSE(ExtmapAllowMixed(*mSessionAns));
   7984 }
   7985 
   7986 }  // namespace mozilla