tor-browser

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

JsepTrack.cpp (27974B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "jsep/JsepTrack.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "jsep/JsepCodecDescription.h"
     10 #include "jsep/JsepTrackEncoding.h"
     11 
     12 namespace mozilla {
     13 void JsepTrack::GetNegotiatedPayloadTypes(
     14    std::vector<uint16_t>* payloadTypes) const {
     15  if (!mNegotiatedDetails) {
     16    return;
     17  }
     18 
     19  for (const auto& encoding : mNegotiatedDetails->mEncodings) {
     20    GetPayloadTypes(encoding->GetCodecs(), payloadTypes);
     21  }
     22 
     23  // Prune out dupes
     24  std::sort(payloadTypes->begin(), payloadTypes->end());
     25  auto newEnd = std::unique(payloadTypes->begin(), payloadTypes->end());
     26  payloadTypes->erase(newEnd, payloadTypes->end());
     27 }
     28 
     29 /* static */
     30 void JsepTrack::GetPayloadTypes(
     31    const std::vector<UniquePtr<JsepCodecDescription>>& codecs,
     32    std::vector<uint16_t>* payloadTypes) {
     33  for (const auto& codec : codecs) {
     34    uint16_t pt;
     35    if (!codec->GetPtAsInt(&pt)) {
     36      MOZ_ASSERT(false);
     37      continue;
     38    }
     39    payloadTypes->push_back(pt);
     40  }
     41 }
     42 
     43 void JsepTrack::EnsureNoDuplicatePayloadTypes(
     44    std::vector<UniquePtr<JsepCodecDescription>>* codecs) {
     45  std::set<std::string> uniquePayloadTypes;
     46  for (auto& codec : *codecs) {
     47    codec->EnsureNoDuplicatePayloadTypes(uniquePayloadTypes);
     48  }
     49 }
     50 
     51 void JsepTrack::EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber) {
     52  while (mSsrcs.size() < aNumber) {
     53    uint32_t ssrc, rtxSsrc;
     54    if (!ssrcGenerator.GenerateSsrc(&ssrc) ||
     55        !ssrcGenerator.GenerateSsrc(&rtxSsrc)) {
     56      return;
     57    }
     58    mSsrcs.push_back(ssrc);
     59    mSsrcToRtxSsrc[ssrc] = rtxSsrc;
     60    MOZ_ASSERT(mSsrcs.size() == mSsrcToRtxSsrc.size());
     61  }
     62 }
     63 
     64 void JsepTrack::PopulateCodecs(
     65    const std::vector<UniquePtr<JsepCodecDescription>>& prototype,
     66    bool aUsePreferredCodecsOrder) {
     67  mPrototypeCodecs.clear();
     68  mUsePreferredCodecsOrder = aUsePreferredCodecsOrder;
     69  for (const auto& prototypeCodec : prototype) {
     70    if (prototypeCodec->Type() == mType) {
     71      mPrototypeCodecs.emplace_back(prototypeCodec->Clone());
     72      mPrototypeCodecs.back()->mDirection = mDirection;
     73    }
     74  }
     75 
     76  EnsureNoDuplicatePayloadTypes(&mPrototypeCodecs);
     77 }
     78 
     79 void JsepTrack::AddToOffer(SsrcGenerator& ssrcGenerator,
     80                           SdpMediaSection* offer) {
     81  AddToMsection(mPrototypeCodecs, offer);
     82 
     83  for (const auto& codec : mPrototypeCodecs) {
     84    uint16_t pt;
     85    if (SdpHelper::GetPtAsInt(codec->mDefaultPt, &pt)) {
     86      mReceivePayloadTypes.push_back(pt);
     87    }
     88  }
     89 
     90  if (mDirection == sdp::kSend) {
     91    std::vector<std::string> rids;
     92    if (offer->IsSending()) {
     93      rids = mRids;
     94    }
     95 
     96    AddToMsection(rids, sdp::kSend, ssrcGenerator,
     97                  IsRtxEnabled(mPrototypeCodecs), offer);
     98  }
     99 }
    100 
    101 void JsepTrack::AddToAnswer(const SdpMediaSection& offer,
    102                            SsrcGenerator& ssrcGenerator,
    103                            SdpMediaSection* answer) {
    104  // We do not modify mPrototypeCodecs here, since we're only creating an
    105  // answer. Once offer/answer concludes, we will update mPrototypeCodecs.
    106  std::vector<UniquePtr<JsepCodecDescription>> codecs =
    107      NegotiateCodecs(offer, true, Nothing());
    108  if (codecs.empty()) {
    109    return;
    110  }
    111 
    112  AddToMsection(codecs, answer);
    113 
    114  if (mDirection == sdp::kSend) {
    115    AddToMsection(mRids, sdp::kSend, ssrcGenerator, IsRtxEnabled(codecs),
    116                  answer);
    117  }
    118 }
    119 
    120 void JsepTrack::SetRids(const std::vector<std::string>& aRids) {
    121  MOZ_ASSERT(!aRids.empty());
    122  if (!mRids.empty()) {
    123    return;
    124  }
    125  mRids = aRids;
    126 }
    127 
    128 void JsepTrack::SetMaxEncodings(size_t aMax) {
    129  mMaxEncodings = aMax;
    130  if (mRids.size() > mMaxEncodings) {
    131    mRids.resize(mMaxEncodings);
    132  }
    133 }
    134 
    135 void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
    136                                   const SdpMediaSection& aMsection) {
    137  mInHaveRemote = true;
    138  MOZ_ASSERT(mDirection == sdp::kRecv);
    139  MOZ_ASSERT(aMsection.GetMediaType() !=
    140             SdpMediaSection::MediaType::kApplication);
    141  std::string error;
    142  SdpHelper helper(&error);
    143 
    144  mRemoteSetSendBit = aMsection.IsSending();
    145  if (!mRemoteSetSendBit) {
    146    mReceptive = false;
    147  }
    148 
    149  if (aMsection.IsSending()) {
    150    (void)helper.GetIdsFromMsid(aSdp, aMsection, &mStreamIds);
    151  } else {
    152    mStreamIds.clear();
    153  }
    154 
    155  // We do this whether or not the track is active
    156  SetCNAME(helper.GetCNAME(aMsection));
    157  mSsrcs.clear();
    158  // Storage of mSsrcs and mSsrcToRtxSsrc could be improved, see Bug 1990364
    159  // Each `a=ssrc ssrc-attr:value` line can contain the same SSRC. We should
    160  // only add unique SSRCs to mSsrcs.
    161  std::set<uint32_t> ssrcsSet;
    162  if (aMsection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
    163    for (const auto& s : aMsection.GetAttributeList().GetSsrc().mSsrcs) {
    164      if (ssrcsSet.find(s.ssrc) != ssrcsSet.end()) {
    165        continue;
    166      }
    167      ssrcsSet.insert(s.ssrc);
    168      // Preserve order of ssrcs as they appear in the m-section
    169      mSsrcs.push_back(s.ssrc);
    170    }
    171  }
    172 
    173  // Use FID ssrc-group to associate rtx ssrcs with "regular" ssrcs. Despite
    174  // not being part of RFC 4588, this is how rtx is negotiated by libwebrtc
    175  // and jitsi.
    176  mSsrcToRtxSsrc.clear();
    177  if (aMsection.GetAttributeList().HasAttribute(
    178          SdpAttribute::kSsrcGroupAttribute)) {
    179    for (const auto& group :
    180         aMsection.GetAttributeList().GetSsrcGroup().mSsrcGroups) {
    181      if (group.semantics == SdpSsrcGroupAttributeList::kFid &&
    182          group.ssrcs.size() == 2) {
    183        // Ensure we have a "regular" ssrc for each rtx ssrc.
    184        if (std::find(mSsrcs.begin(), mSsrcs.end(), group.ssrcs[0]) !=
    185            mSsrcs.end()) {
    186          mSsrcToRtxSsrc[group.ssrcs[0]] = group.ssrcs[1];
    187 
    188          // Remove rtx ssrcs from mSsrcs
    189          auto res = std::remove_if(
    190              mSsrcs.begin(), mSsrcs.end(),
    191              [group](uint32_t ssrc) { return ssrc == group.ssrcs[1]; });
    192          mSsrcs.erase(res, mSsrcs.end());
    193        }
    194      }
    195    }
    196  }
    197 }
    198 
    199 void JsepTrack::RecvTrackSetLocal(const SdpMediaSection& aMsection) {
    200  MOZ_ASSERT(mDirection == sdp::kRecv);
    201 
    202  // TODO: Should more stuff live in here? Anything that needs to happen when we
    203  // decide we're ready to receive packets should probably go in here.
    204  mReceptive = aMsection.IsReceiving();
    205 }
    206 
    207 void JsepTrack::SendTrackSetRemote(SsrcGenerator& aSsrcGenerator,
    208                                   const SdpMediaSection& aRemoteMsection) {
    209  mInHaveRemote = true;
    210  if (mType == SdpMediaSection::kApplication) {
    211    return;
    212  }
    213 
    214  std::vector<SdpRidAttributeList::Rid> rids;
    215 
    216  // TODO: Current language in webrtc-pc is completely broken, and so I will
    217  // not be quoting it here.
    218  if ((mType == SdpMediaSection::kVideo) &&
    219      aRemoteMsection.GetAttributeList().HasAttribute(
    220          SdpAttribute::kSimulcastAttribute)) {
    221    // Note: webrtc-pc does not appear to support the full IETF simulcast
    222    // spec. In particular, the IETF simulcast spec supports requesting
    223    // multiple different sets of encodings. For example, "a=simulcast:send
    224    // 1,2;3,4;5,6" means that there are three simulcast streams, the first of
    225    // which can use either rid 1 or 2 (but not both), the second of which can
    226    // use rid 3 or 4 (but not both), and the third of which can use rid 5 or
    227    // 6 (but not both). webrtc-pc does not support this either/or stuff for
    228    // rid; each simulcast stream gets exactly one rid.
    229    // Also, webrtc-pc does not support the '~' pause syntax at all
    230    // See https://github.com/w3c/webrtc-pc/issues/2769
    231    GetRids(aRemoteMsection, sdp::kRecv, &rids);
    232  }
    233 
    234  if (mRids.empty()) {
    235    // Initial configuration
    236    for (const auto& ridAttr : rids) {
    237      // TODO: Spec might change, making a length > 16 invalid SDP.
    238      std::string dummy;
    239      if (SdpRidAttributeList::CheckRidValidity(ridAttr.id, &dummy) &&
    240          ridAttr.id.size() <= SdpRidAttributeList::kMaxRidLength) {
    241        mRids.push_back(ridAttr.id);
    242      }
    243    }
    244    if (mRids.size() > mMaxEncodings) {
    245      mRids.resize(mMaxEncodings);
    246    }
    247  } else {
    248    // JSEP is allowed to remove or reorder rids. RTCRtpSender won't pay
    249    // attention to reordering.
    250    std::vector<std::string> newRids;
    251    for (const auto& ridAttr : rids) {
    252      for (const auto& oldRid : mRids) {
    253        if (oldRid == ridAttr.id) {
    254          newRids.push_back(oldRid);
    255          break;
    256        }
    257      }
    258    }
    259    mRids = std::move(newRids);
    260  }
    261 
    262  if (mRids.empty()) {
    263    mRids.push_back("");
    264  }
    265 
    266  UpdateSsrcs(aSsrcGenerator, mRids.size());
    267 }
    268 
    269 void JsepTrack::AddToMsection(
    270    const std::vector<UniquePtr<JsepCodecDescription>>& codecs,
    271    SdpMediaSection* msection) const {
    272  MOZ_ASSERT(msection->GetMediaType() == mType);
    273  MOZ_ASSERT(!codecs.empty());
    274 
    275  for (const auto& codec : codecs) {
    276    codec->AddToMediaSection(*msection);
    277  }
    278 
    279  if ((mDirection == sdp::kSend) && (mType != SdpMediaSection::kApplication) &&
    280      msection->IsSending()) {
    281    if (mStreamIds.empty()) {
    282      msection->AddMsid("-", mTrackId);
    283    } else {
    284      for (const std::string& streamId : mStreamIds) {
    285        msection->AddMsid(streamId, mTrackId);
    286      }
    287    }
    288  }
    289 }
    290 
    291 void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
    292  MOZ_ASSERT(mDirection == sdp::kSend);
    293  MOZ_ASSERT(mType != SdpMediaSection::kApplication);
    294  size_t numSsrcs = std::max<size_t>(encodings, 1U);
    295 
    296  EnsureSsrcs(ssrcGenerator, numSsrcs);
    297  PruneSsrcs(numSsrcs);
    298  if (mNegotiatedDetails && mNegotiatedDetails->GetEncodingCount() > numSsrcs) {
    299    mNegotiatedDetails->TruncateEncodings(numSsrcs);
    300  }
    301 
    302  MOZ_ASSERT(!mSsrcs.empty());
    303 }
    304 
    305 void JsepTrack::PruneSsrcs(size_t aNumSsrcs) {
    306  mSsrcs.resize(aNumSsrcs);
    307 
    308  // We might have duplicate entries in mSsrcs, so we need to resize first and
    309  // then remove ummatched rtx ssrcs.
    310  auto itor = mSsrcToRtxSsrc.begin();
    311  while (itor != mSsrcToRtxSsrc.end()) {
    312    if (std::find(mSsrcs.begin(), mSsrcs.end(), itor->first) == mSsrcs.end()) {
    313      itor = mSsrcToRtxSsrc.erase(itor);
    314    } else {
    315      ++itor;
    316    }
    317  }
    318 }
    319 
    320 bool JsepTrack::IsRtxEnabled(
    321    const std::vector<UniquePtr<JsepCodecDescription>>& codecs) const {
    322  for (const auto& codec : codecs) {
    323    if (codec->Type() == SdpMediaSection::kVideo &&
    324        static_cast<const JsepVideoCodecDescription*>(codec.get())
    325            ->mRtxEnabled) {
    326      return true;
    327    }
    328  }
    329 
    330  return false;
    331 }
    332 
    333 void JsepTrack::AddToMsection(const std::vector<std::string>& aRids,
    334                              sdp::Direction direction,
    335                              SsrcGenerator& ssrcGenerator, bool rtxEnabled,
    336                              SdpMediaSection* msection) {
    337  if (aRids.size() > 1) {
    338    UniquePtr<SdpSimulcastAttribute> simulcast(new SdpSimulcastAttribute);
    339    UniquePtr<SdpRidAttributeList> ridAttrs(new SdpRidAttributeList);
    340    for (const std::string& rid : aRids) {
    341      SdpRidAttributeList::Rid ridAttr;
    342      ridAttr.id = rid;
    343      ridAttr.direction = direction;
    344      ridAttrs->mRids.push_back(ridAttr);
    345 
    346      SdpSimulcastAttribute::Version version;
    347      version.choices.push_back(SdpSimulcastAttribute::Encoding(rid, false));
    348      if (direction == sdp::kSend) {
    349        simulcast->sendVersions.push_back(version);
    350      } else {
    351        simulcast->recvVersions.push_back(version);
    352      }
    353    }
    354 
    355    msection->GetAttributeList().SetAttribute(simulcast.release());
    356    msection->GetAttributeList().SetAttribute(ridAttrs.release());
    357  }
    358 
    359  bool requireRtxSsrcs = rtxEnabled && msection->IsSending();
    360 
    361  if (mType != SdpMediaSection::kApplication && mDirection == sdp::kSend) {
    362    UpdateSsrcs(ssrcGenerator, aRids.size());
    363 
    364    if (requireRtxSsrcs) {
    365      MOZ_ASSERT(mSsrcs.size() == mSsrcToRtxSsrc.size());
    366      std::vector<uint32_t> allSsrcs;
    367      UniquePtr<SdpSsrcGroupAttributeList> group(new SdpSsrcGroupAttributeList);
    368      for (const auto& ssrc : mSsrcs) {
    369        const auto rtxSsrc = mSsrcToRtxSsrc[ssrc];
    370        allSsrcs.push_back(ssrc);
    371        allSsrcs.push_back(rtxSsrc);
    372        group->PushEntry(SdpSsrcGroupAttributeList::kFid, {ssrc, rtxSsrc});
    373      }
    374      msection->SetSsrcs(allSsrcs, mCNAME);
    375      msection->GetAttributeList().SetAttribute(group.release());
    376    } else {
    377      msection->SetSsrcs(mSsrcs, mCNAME);
    378    }
    379  }
    380 }
    381 
    382 void JsepTrack::GetRids(const SdpMediaSection& msection,
    383                        sdp::Direction direction,
    384                        std::vector<SdpRidAttributeList::Rid>* rids) const {
    385  rids->clear();
    386  if (!msection.GetAttributeList().HasAttribute(
    387          SdpAttribute::kSimulcastAttribute)) {
    388    return;
    389  }
    390 
    391  const SdpSimulcastAttribute& simulcast(
    392      msection.GetAttributeList().GetSimulcast());
    393 
    394  const SdpSimulcastAttribute::Versions* versions = nullptr;
    395  switch (direction) {
    396    case sdp::kSend:
    397      versions = &simulcast.sendVersions;
    398      break;
    399    case sdp::kRecv:
    400      versions = &simulcast.recvVersions;
    401      break;
    402  }
    403 
    404  if (!versions->IsSet()) {
    405    return;
    406  }
    407 
    408  // RFC 8853 does not seem to forbid duplicate rids in a simulcast attribute.
    409  // So, while this is obviously silly, we should be prepared for it and
    410  // ignore those duplicate rids.
    411  std::set<std::string> uniqueRids;
    412  for (const SdpSimulcastAttribute::Version& version : *versions) {
    413    if (!version.choices.empty() && !uniqueRids.count(version.choices[0].rid)) {
    414      // We validate that rids are present (and sane) elsewhere.
    415      rids->push_back(*msection.FindRid(version.choices[0].rid));
    416      uniqueRids.insert(version.choices[0].rid);
    417    }
    418  }
    419 }
    420 
    421 void JsepTrack::CreateEncodings(
    422    const SdpMediaSection& remote,
    423    const std::vector<UniquePtr<JsepCodecDescription>>& negotiatedCodecs,
    424    JsepTrackNegotiatedDetails* negotiatedDetails) {
    425  negotiatedDetails->mTias = remote.GetBandwidth("TIAS");
    426 
    427  webrtc::RtcpMode rtcpMode = webrtc::RtcpMode::kCompound;
    428  // rtcp-rsize (video only)
    429  if (remote.GetMediaType() == SdpMediaSection::kVideo &&
    430      remote.GetAttributeList().HasAttribute(
    431          SdpAttribute::kRtcpRsizeAttribute)) {
    432    rtcpMode = webrtc::RtcpMode::kReducedSize;
    433  }
    434  // extmap-allow-mixed which can be at the media level or the session level
    435  constexpr bool SESSION_FALLBACK = true;
    436  bool extmapAllowMixed = remote.GetAttributeList().HasAttribute(
    437      SdpAttribute::kExtmapAllowMixedAttribute, SESSION_FALLBACK);
    438  negotiatedDetails->mRtpRtcpConf = RtpRtcpConfig(rtcpMode, extmapAllowMixed);
    439 
    440  // TODO add support for b=AS if TIAS is not set (bug 976521)
    441 
    442  if (mRids.empty()) {
    443    mRids.push_back("");
    444  }
    445 
    446  size_t numEncodings = mRids.size();
    447 
    448  // Drop SSRCs if fewer RIDs were offered than we have encodings
    449  if (mSsrcs.size() > numEncodings) {
    450    PruneSsrcs(numEncodings);
    451  }
    452 
    453  // For each stream make sure we have an encoding, and configure
    454  // that encoding appropriately.
    455  for (size_t i = 0; i < numEncodings; ++i) {
    456    UniquePtr<JsepTrackEncoding> encoding(new JsepTrackEncoding);
    457    if (mRids.size() > i) {
    458      encoding->mRid = mRids[i];
    459    }
    460    for (const auto& codec : negotiatedCodecs) {
    461      encoding->AddCodec(*codec);
    462    }
    463    negotiatedDetails->mEncodings.push_back(std::move(encoding));
    464  }
    465 }
    466 
    467 std::vector<UniquePtr<JsepCodecDescription>> JsepTrack::GetCodecClones() const {
    468  std::vector<UniquePtr<JsepCodecDescription>> clones;
    469  for (const auto& codec : mPrototypeCodecs) {
    470    clones.emplace_back(codec->Clone());
    471  }
    472  return clones;
    473 }
    474 
    475 static bool CompareCodec(const UniquePtr<JsepCodecDescription>& lhs,
    476                         const UniquePtr<JsepCodecDescription>& rhs) {
    477  return lhs->mStronglyPreferred && !rhs->mStronglyPreferred;
    478 }
    479 
    480 void JsepTrack::MaybeStoreCodecToLog(const std::string& codec,
    481                                     SdpMediaSection::MediaType type) {
    482  // We are logging ulpfec and red elsewhere and will not log rtx.
    483  if (!nsCRT::strcasecmp(codec.c_str(), "ulpfec") ||
    484      !nsCRT::strcasecmp(codec.c_str(), "red") ||
    485      !nsCRT::strcasecmp(codec.c_str(), "rtx")) {
    486    return;
    487  }
    488 
    489  if (type == SdpMediaSection::kVideo) {
    490    if (nsCRT::strcasestr(codec.c_str(), "fec") && mFecCodec.empty()) {
    491      mFecCodec = codec;
    492    } else if (!nsCRT::strcasestr(codec.c_str(), "fec") &&
    493               mVideoPreferredCodec.empty()) {
    494      mVideoPreferredCodec = codec;
    495    }
    496  } else if (type == SdpMediaSection::kAudio && mAudioPreferredCodec.empty()) {
    497    mAudioPreferredCodec = codec;
    498  }
    499 }
    500 
    501 std::vector<UniquePtr<JsepCodecDescription>> JsepTrack::NegotiateCodecs(
    502    const SdpMediaSection& remote, bool remoteIsOffer,
    503    Maybe<const SdpMediaSection&> local) {
    504  // See Bug 1927371 - :ng
    505  // This also effects the ordering of codecs in RTCRTPSender::GetParameters,
    506  // but occurs after the initial local description codec ordering is
    507  // established in the SDP. We should protect against this in the future by
    508  // applying the same ordering logic to the initial local description.
    509 
    510  std::vector<UniquePtr<JsepCodecDescription>> negotiatedCodecs;
    511  std::vector<UniquePtr<JsepCodecDescription>> newPrototypeCodecs;
    512  // "Pseudo codecs" include things that aren't actually stand-alone codecs (ie;
    513  // ulpfec, red, rtx).
    514  std::vector<UniquePtr<JsepCodecDescription>> negotiatedPseudoCodecs;
    515  std::vector<UniquePtr<JsepCodecDescription>> newPrototypePseudoCodecs;
    516 
    517  std::vector<std::string> remoteFormats;
    518 
    519  // If setCodecPreferences has been used we need to ensure the order of codecs
    520  // matches what was set.
    521  if (mUsePreferredCodecsOrder) {
    522    for (auto& codec : mPrototypeCodecs) {
    523      if (!codec || !codec->mEnabled) {
    524        continue;
    525      }
    526      for (const std::string& fmt : remote.GetFormats()) {
    527        if (!codec->Matches(fmt, remote)) {
    528          continue;
    529        }
    530        remoteFormats.push_back(fmt);
    531        break;
    532      }
    533    }
    534  } else {
    535    remoteFormats = remote.GetFormats();
    536  }
    537 
    538  // Outer loop establishes the remote side's preference
    539  for (const std::string& fmt : remoteFormats) {
    540    // Decide if we want to store this codec for logging.
    541    const auto* entry = remote.FindRtpmap(fmt);
    542    if (entry) {
    543      MaybeStoreCodecToLog(entry->name, remote.GetMediaType());
    544    }
    545 
    546    for (auto& codec : mPrototypeCodecs) {
    547      if (!codec || !codec->mEnabled || !codec->Matches(fmt, remote)) {
    548        continue;
    549      }
    550 
    551      // First codec of ours that matches. See if we can negotiate it.
    552      UniquePtr<JsepCodecDescription> clone(codec->Clone());
    553      if (clone->Negotiate(fmt, remote, remoteIsOffer, local)) {
    554        // If negotiation succeeded, remember the payload type the other side
    555        // used for reoffers.
    556        codec->mDefaultPt = fmt;
    557 
    558        // Remember whether we negotiated rtx and the associated pt for later.
    559        if (codec->Type() == SdpMediaSection::kVideo) {
    560          JsepVideoCodecDescription* videoCodec =
    561              static_cast<JsepVideoCodecDescription*>(codec.get());
    562          JsepVideoCodecDescription* cloneVideoCodec =
    563              static_cast<JsepVideoCodecDescription*>(clone.get());
    564          bool useRtx =
    565              mRtxIsAllowed &&
    566              Preferences::GetBool("media.peerconnection.video.use_rtx", false);
    567          videoCodec->mRtxEnabled = useRtx && cloneVideoCodec->mRtxEnabled;
    568          videoCodec->mRtxPayloadType = cloneVideoCodec->mRtxPayloadType;
    569        }
    570 
    571        // Moves the codec out of mPrototypeCodecs, leaving an empty
    572        // UniquePtr, so we don't use it again. Also causes successfully
    573        // negotiated codecs to be placed up front in the future.
    574        if (codec->mName == "red" || codec->mName == "ulpfec" ||
    575            codec->mName == "rtx") {
    576          newPrototypePseudoCodecs.emplace_back(std::move(codec));
    577          negotiatedPseudoCodecs.emplace_back(std::move(clone));
    578        } else {
    579          newPrototypeCodecs.emplace_back(std::move(codec));
    580          negotiatedCodecs.emplace_back(std::move(clone));
    581        }
    582        break;
    583      }
    584    }
    585  }
    586 
    587  // If we are the offerer we need to be prepared to receive all codecs we
    588  // offered even codecs missing from the answer. To achieve this we add the
    589  // remaining codecs from mPrototypeCodecs to the back of the negotiated
    590  // Codecs/PseudoCodecs.
    591  for (auto& codec : mPrototypeCodecs) {
    592    bool codecEnabled = codec && codec->mEnabled;
    593    bool addAllCodecs =
    594        !remoteIsOffer && mDirection != sdp::kSend && remote.IsSending();
    595    // 0 is a valid PT but not a dynamic PT so we validate if we see 0 that it
    596    // is for PCMU
    597    bool validPT = codecEnabled && (codec->mDefaultPt.compare("0") != 0 ||
    598                                    (codec->mName.compare("PCMU") == 0));
    599    if (!codecEnabled || (!addAllCodecs || !validPT)) {
    600      continue;
    601    }
    602 
    603    UniquePtr<JsepCodecDescription> clone(codec->Clone());
    604    // Moves the codec out of mPrototypeCodecs, leaving an empty
    605    // UniquePtr, so we don't use it again.
    606    if (codec->mName == "red" || codec->mName == "ulpfec" ||
    607        codec->mName == "rtx") {
    608      newPrototypePseudoCodecs.emplace_back(std::move(codec));
    609      negotiatedPseudoCodecs.emplace_back(std::move(clone));
    610    } else {
    611      newPrototypeCodecs.emplace_back(std::move(codec));
    612      negotiatedCodecs.emplace_back(std::move(clone));
    613    }
    614  }
    615 
    616  if (negotiatedCodecs.empty()) {
    617    // We don't have any real codecs. Clearing so we don't attempt to create a
    618    // connection signaling only RED and/or ULPFEC.
    619    negotiatedPseudoCodecs.clear();
    620  }
    621 
    622  // Put the pseudo codecs at the back
    623  for (auto& pseudoCodec : negotiatedPseudoCodecs) {
    624    negotiatedCodecs.emplace_back(std::move(pseudoCodec));
    625  }
    626 
    627  for (auto& pseudoCodec : newPrototypePseudoCodecs) {
    628    newPrototypeCodecs.emplace_back(std::move(pseudoCodec));
    629  }
    630 
    631  // newPrototypeCodecs contains just the negotiated stuff so far. Add the rest.
    632  for (auto& codec : mPrototypeCodecs) {
    633    if (codec) {
    634      newPrototypeCodecs.emplace_back(std::move(codec));
    635    }
    636  }
    637 
    638  // Negotiated stuff is up front, so it will take precedence when ensuring
    639  // there are no duplicate payload types.
    640  EnsureNoDuplicatePayloadTypes(&newPrototypeCodecs);
    641  std::swap(newPrototypeCodecs, mPrototypeCodecs);
    642 
    643  // Find the (potential) red codec and ulpfec codec or telephone-event
    644  JsepVideoCodecDescription* red = nullptr;
    645  JsepVideoCodecDescription* ulpfec = nullptr;
    646  JsepAudioCodecDescription* dtmf = nullptr;
    647  // We can safely cast here since JsepTrack has a MediaType and only codecs
    648  // that match that MediaType (kAudio or kVideo) are added.
    649  for (auto& codec : negotiatedCodecs) {
    650    if (codec->mName == "red") {
    651      red = static_cast<JsepVideoCodecDescription*>(codec.get());
    652    } else if (codec->mName == "ulpfec") {
    653      ulpfec = static_cast<JsepVideoCodecDescription*>(codec.get());
    654    } else if (codec->mName == "telephone-event") {
    655      dtmf = static_cast<JsepAudioCodecDescription*>(codec.get());
    656    }
    657  }
    658  // Video FEC is indicated by the existence of the red and ulpfec
    659  // codecs and not an attribute on the particular video codec (like in
    660  // a rtcpfb attr). If we see both red and ulpfec codecs, we enable FEC
    661  // on all the other codecs.
    662  if (red && ulpfec) {
    663    for (auto& codec : negotiatedCodecs) {
    664      if (codec->mName != "red" && codec->mName != "ulpfec") {
    665        JsepVideoCodecDescription* videoCodec =
    666            static_cast<JsepVideoCodecDescription*>(codec.get());
    667        videoCodec->EnableFec(red->mDefaultPt, ulpfec->mDefaultPt,
    668                              red->mRtxPayloadType);
    669      }
    670    }
    671  }
    672 
    673  // Dtmf support is indicated by the existence of the telephone-event
    674  // codec, and not an attribute on the particular audio codec (like in a
    675  // rtcpfb attr). If we see the telephone-event codec, we enabled dtmf
    676  // support on all the other audio codecs.
    677  if (dtmf) {
    678    for (auto& codec : negotiatedCodecs) {
    679      JsepAudioCodecDescription* audioCodec =
    680          static_cast<JsepAudioCodecDescription*>(codec.get());
    681      audioCodec->mDtmfEnabled = true;
    682    }
    683  }
    684 
    685  // Make sure strongly preferred codecs are up front, overriding the remote
    686  // side's preference.
    687  std::stable_sort(negotiatedCodecs.begin(), negotiatedCodecs.end(),
    688                   CompareCodec);
    689 
    690  if (!red) {
    691    // No red, remove ulpfec
    692    negotiatedCodecs.erase(
    693        std::remove_if(negotiatedCodecs.begin(), negotiatedCodecs.end(),
    694                       [ulpfec](const UniquePtr<JsepCodecDescription>& codec) {
    695                         return codec.get() == ulpfec;
    696                       }),
    697        negotiatedCodecs.end());
    698    // Make sure there's no dangling ptr here
    699    ulpfec = nullptr;
    700  }
    701 
    702  return negotiatedCodecs;
    703 }
    704 
    705 nsresult JsepTrack::Negotiate(const SdpMediaSection& answer,
    706                              const SdpMediaSection& remote,
    707                              const SdpMediaSection& local) {
    708  std::vector<UniquePtr<JsepCodecDescription>> negotiatedCodecs =
    709      NegotiateCodecs(remote, &answer != &remote, SomeRef(local));
    710 
    711  if (negotiatedCodecs.empty()) {
    712    return NS_ERROR_FAILURE;
    713  }
    714 
    715  UniquePtr<JsepTrackNegotiatedDetails> negotiatedDetails =
    716      MakeUnique<JsepTrackNegotiatedDetails>();
    717 
    718  CreateEncodings(remote, negotiatedCodecs, negotiatedDetails.get());
    719 
    720  if (answer.GetAttributeList().HasAttribute(SdpAttribute::kExtmapAttribute)) {
    721    for (auto& extmapAttr : answer.GetAttributeList().GetExtmap().mExtmaps) {
    722      SdpDirectionAttribute::Direction direction = extmapAttr.direction;
    723      if (&remote == &answer) {
    724        // Answer is remote, we need to flip this.
    725        direction = reverse(direction);
    726      }
    727 
    728      if (direction & mDirection) {
    729        negotiatedDetails->mExtmap[extmapAttr.extensionname] = extmapAttr;
    730      }
    731    }
    732  }
    733 
    734  mInHaveRemote = false;
    735  mNegotiatedDetails = std::move(negotiatedDetails);
    736  return NS_OK;
    737 }
    738 
    739 // When doing bundle, if all else fails we can try to figure out which m-line a
    740 // given RTP packet belongs to by looking at the payload type field. This only
    741 // works, however, if that payload type appeared in only one m-section.
    742 // We figure that out here.
    743 /* static */
    744 void JsepTrack::SetReceivePayloadTypes(std::vector<JsepTrack*>& tracks,
    745                                       bool localOffer) {
    746  // Maps payload types to:
    747  // - Nothing() temporarily when just initialized
    748  // - Some(nullptr) when the payload type is registered to multiple tracks
    749  // - Some(track) when the payload type is unique on track
    750  std::map<uint16_t, Maybe<JsepTrack*>> payloadTypeToTrackCount;
    751 
    752  for (JsepTrack* track : tracks) {
    753    track->mUniqueReceivePayloadTypes.clear();
    754    track->mOtherReceivePayloadTypes.clear();
    755 
    756    if (track->GetMediaType() == SdpMediaSection::kApplication) {
    757      continue;
    758    }
    759 
    760    std::vector<uint16_t> payloadTypesForTrack;
    761    if (!localOffer) {
    762      auto* details = track->GetNegotiatedDetails();
    763      if (!details) {
    764        // Can happen if negotiation fails on a track
    765        continue;
    766      }
    767      track->GetNegotiatedPayloadTypes(&payloadTypesForTrack);
    768    } else {
    769      payloadTypesForTrack = track->mReceivePayloadTypes;
    770    }
    771 
    772    for (uint16_t pt : payloadTypesForTrack) {
    773      // Note std::map::operator[] inserts a default-initialized value, i.e.
    774      // Nothing(), if one doesn't exist.
    775      auto& entry = payloadTypeToTrackCount[pt];
    776      entry = entry
    777                  // If unique, i.e. Some(track), set it to Some(nullptr)
    778                  .andThen([](JsepTrack*) { return Some<JsepTrack*>(nullptr); })
    779                  // If not unique, i.e. Nothing(), set it to Some(track)
    780                  .orElse([track] { return Some(track); });
    781    }
    782  }
    783 
    784  for (const auto& [key, track] : payloadTypeToTrackCount) {
    785    const auto pt = AssertedCast<uint8_t>(key);
    786    JsepTrack* uniqueTrack = *track;
    787    if (uniqueTrack) {
    788      uniqueTrack->mUniqueReceivePayloadTypes.push_back(pt);
    789    }
    790    for (JsepTrack* track : tracks) {
    791      if (track != uniqueTrack) {
    792        track->mOtherReceivePayloadTypes.push_back(pt);
    793      }
    794    }
    795  }
    796 }
    797 
    798 }  // namespace mozilla