tor-browser

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

RTCRtpSender.cpp (86016B)


      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 "RTCRtpSender.h"
      6 
      7 #include <stdint.h>
      8 
      9 #include <algorithm>
     10 #include <iterator>
     11 #include <set>
     12 #include <sstream>
     13 #include <string>
     14 #include <utility>
     15 #include <vector>
     16 
     17 #include "MainThreadUtils.h"
     18 #include "PeerConnectionImpl.h"
     19 #include "RTCRtpTransceiver.h"
     20 #include "RTCStatsReport.h"
     21 #include "api/rtp_parameters.h"
     22 #include "api/units/time_delta.h"
     23 #include "api/units/timestamp.h"
     24 #include "api/video/video_codec_constants.h"
     25 #include "api/video_codecs/video_codec.h"
     26 #include "call/audio_send_stream.h"
     27 #include "call/call.h"
     28 #include "call/video_send_stream.h"
     29 #include "js/RootingAPI.h"
     30 #include "jsep/JsepTransceiver.h"
     31 #include "libwebrtcglue/CodecConfig.h"
     32 #include "libwebrtcglue/MediaConduitControl.h"
     33 #include "libwebrtcglue/MediaConduitInterface.h"
     34 #include "modules/rtp_rtcp/include/report_block_data.h"
     35 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
     36 #include "mozilla/AbstractThread.h"
     37 #include "mozilla/AlreadyAddRefed.h"
     38 #include "mozilla/Assertions.h"
     39 #include "mozilla/Attributes.h"
     40 #include "mozilla/Logging.h"
     41 #include "mozilla/Maybe.h"
     42 #include "mozilla/MozPromise.h"
     43 #include "mozilla/OwningNonNull.h"
     44 #include "mozilla/RefPtr.h"
     45 #include "mozilla/StateMirroring.h"
     46 #include "mozilla/StateWatching.h"
     47 #include "mozilla/UniquePtr.h"
     48 #include "mozilla/dom/MediaStreamTrack.h"
     49 #include "mozilla/dom/MediaStreamTrackBinding.h"
     50 #include "mozilla/dom/Nullable.h"
     51 #include "mozilla/dom/Promise.h"
     52 #include "mozilla/dom/RTCRtpParametersBinding.h"
     53 #include "mozilla/dom/RTCRtpScriptTransform.h"
     54 #include "mozilla/dom/RTCRtpSenderBinding.h"
     55 #include "mozilla/dom/RTCStatsReportBinding.h"
     56 #include "mozilla/dom/VideoStreamTrack.h"
     57 #include "mozilla/fallible.h"
     58 #include "mozilla/glean/DomMediaWebrtcMetrics.h"
     59 #include "mozilla/mozalloc_oom.h"
     60 #include "nsCOMPtr.h"
     61 #include "nsContentUtils.h"
     62 #include "nsCycleCollectionParticipant.h"
     63 #include "nsDebug.h"
     64 #include "nsISupports.h"
     65 #include "nsLiteralString.h"
     66 #include "nsPIDOMWindow.h"
     67 #include "nsString.h"
     68 #include "nsStringFwd.h"
     69 #include "nsTArray.h"
     70 #include "nsThreadUtils.h"
     71 #include "nsWrapperCache.h"
     72 #include "sdp/SdpAttribute.h"
     73 #include "sdp/SdpEnum.h"
     74 #include "system_wrappers/include/clock.h"
     75 
     76 namespace mozilla::dom {
     77 
     78 LazyLogModule gSenderLog("RTCRtpSender");
     79 
     80 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(RTCRtpSender)
     81 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(RTCRtpSender)
     82  tmp->Unlink();
     83  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     84 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     85 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(RTCRtpSender)
     86  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow, mPc, mSenderTrack, mTransceiver,
     87                                    mStreams, mTransform, mDtmf)
     88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     89 
     90 NS_IMPL_CYCLE_COLLECTING_ADDREF(RTCRtpSender)
     91 NS_IMPL_CYCLE_COLLECTING_RELEASE(RTCRtpSender)
     92 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCRtpSender)
     93  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     94  NS_INTERFACE_MAP_ENTRY(nsISupports)
     95 NS_INTERFACE_MAP_END
     96 
     97 #define INIT_CANONICAL(name, val) \
     98  name(AbstractThread::MainThread(), val, "RTCRtpSender::" #name " (Canonical)")
     99 
    100 RTCRtpSender::RTCRtpSender(nsPIDOMWindowInner* aWindow, PeerConnectionImpl* aPc,
    101                           MediaTransportHandler* aTransportHandler,
    102                           AbstractThread* aCallThread,
    103                           nsISerialEventTarget* aStsThread,
    104                           MediaSessionConduit* aConduit,
    105                           dom::MediaStreamTrack* aTrack,
    106                           const Sequence<RTCRtpEncodingParameters>& aEncodings,
    107                           RTCRtpTransceiver* aTransceiver)
    108    : mWatchManager(this, AbstractThread::MainThread()),
    109      mWindow(aWindow),
    110      mPc(aPc),
    111      mSenderTrack(aTrack),
    112      mTransportHandler(aTransportHandler),
    113      mTransceiver(aTransceiver),
    114      INIT_CANONICAL(mSsrcs, Ssrcs()),
    115      INIT_CANONICAL(mVideoRtxSsrcs, Ssrcs()),
    116      INIT_CANONICAL(mLocalRtpExtensions, RtpExtList()),
    117      INIT_CANONICAL(mAudioCodec, Nothing()),
    118      INIT_CANONICAL(mVideoCodec, Nothing()),
    119      INIT_CANONICAL(mVideoRtpRtcpConfig, Nothing()),
    120      INIT_CANONICAL(mVideoCodecMode, webrtc::VideoCodecMode::kRealtimeVideo),
    121      INIT_CANONICAL(mCname, std::string()),
    122      INIT_CANONICAL(mTransmitting, false),
    123      INIT_CANONICAL(mFrameTransformerProxy, nullptr),
    124      INIT_CANONICAL(mVideoDegradationPreference,
    125                     webrtc::DegradationPreference::DISABLED) {
    126  mPipeline = MediaPipelineTransmit::Create(
    127      mPc->GetHandle(), aTransportHandler, aCallThread, aStsThread,
    128      aConduit->type() == MediaSessionConduit::VIDEO, aConduit);
    129  mPipeline->InitControl(this);
    130 
    131  if (aConduit->type() == MediaSessionConduit::AUDIO) {
    132    mDtmf = new RTCDTMFSender(aWindow, mTransceiver);
    133  }
    134  mPipeline->SetTrack(mSenderTrack);
    135 
    136  mozilla::glean::rtcrtpsender::count.Add(1);
    137 
    138  if (mPc->ShouldAllowOldSetParameters()) {
    139    mAllowOldSetParameters = true;
    140    mozilla::glean::rtcrtpsender::count_setparameters_compat.Add(1);
    141  }
    142 
    143  if (aEncodings.Length()) {
    144    // This sender was created by addTransceiver with sendEncodings.
    145    mParameters.mEncodings = aEncodings;
    146    mSimulcastEnvelopeSet = true;
    147    mozilla::glean::rtcrtpsender::used_sendencodings.AddToNumerator(1);
    148  } else {
    149    // This sender was created by addTrack, sRD(offer), or addTransceiver
    150    // without sendEncodings.
    151    RTCRtpEncodingParameters defaultEncoding;
    152    defaultEncoding.mActive = true;
    153    if (aConduit->type() == MediaSessionConduit::VIDEO) {
    154      defaultEncoding.mScaleResolutionDownBy.Construct(1.0f);
    155    }
    156    (void)mParameters.mEncodings.AppendElement(defaultEncoding, fallible);
    157    UpdateRestorableEncodings(mParameters.mEncodings);
    158    MaybeGetJsepRids();
    159  }
    160 
    161  mParameters.mCodecs.Construct();
    162 
    163  if (mDtmf) {
    164    mWatchManager.Watch(mTransmitting, &RTCRtpSender::UpdateDtmfSender);
    165  }
    166 }
    167 
    168 #undef INIT_CANONICAL
    169 
    170 RTCRtpSender::~RTCRtpSender() = default;
    171 
    172 JSObject* RTCRtpSender::WrapObject(JSContext* aCx,
    173                                   JS::Handle<JSObject*> aGivenProto) {
    174  return RTCRtpSender_Binding::Wrap(aCx, this, aGivenProto);
    175 }
    176 
    177 RTCDtlsTransport* RTCRtpSender::GetTransport() const {
    178  if (!mTransceiver) {
    179    return nullptr;
    180  }
    181  return mTransceiver->GetDtlsTransport();
    182 }
    183 
    184 RTCDTMFSender* RTCRtpSender::GetDtmf() const { return mDtmf; }
    185 
    186 already_AddRefed<Promise> RTCRtpSender::GetStats(ErrorResult& aError) {
    187  RefPtr<Promise> promise = MakePromise(aError);
    188  if (aError.Failed()) {
    189    return nullptr;
    190  }
    191  if (NS_WARN_IF(!mPipeline)) {
    192    // TODO(bug 1056433): When we stop nulling this out when the PC is closed
    193    // (or when the transceiver is stopped), we can remove this code. We
    194    // resolve instead of reject in order to make this eventual change in
    195    // behavior a little smaller.
    196    promise->MaybeResolve(new RTCStatsReport(mWindow));
    197    return promise.forget();
    198  }
    199 
    200  mTransceiver->ChainToDomPromiseWithCodecStats(GetStatsInternal(), promise);
    201  return promise.forget();
    202 }
    203 
    204 nsTArray<RefPtr<dom::RTCStatsPromise>> RTCRtpSender::GetStatsInternal(
    205    bool aSkipIceStats) {
    206  MOZ_ASSERT(NS_IsMainThread());
    207  nsTArray<RefPtr<RTCStatsPromise>> promises(2);
    208  if (!mPipeline) {
    209    return promises;
    210  }
    211 
    212  nsAutoString trackName;
    213  if (auto track = mPipeline->GetTrack()) {
    214    track->GetId(trackName);
    215  }
    216 
    217  std::string mid = mTransceiver->GetMidAscii();
    218  std::map<uint32_t, std::string> videoSsrcToRidMap;
    219  const auto encodings = mVideoCodec.Ref().andThen(
    220      [](const auto& aCodec) { return SomeRef(aCodec.mEncodings); });
    221  if (encodings && !encodings->empty() && encodings->front().rid != "") {
    222    for (size_t i = 0; i < std::min(mSsrcs.Ref().size(), encodings->size());
    223         ++i) {
    224      videoSsrcToRidMap.insert({mSsrcs.Ref()[i], (*encodings)[i].rid});
    225    }
    226  }
    227 
    228  {
    229    // Add bandwidth estimation stats
    230    promises.AppendElement(InvokeAsync(
    231        mPipeline->mCallThread, __func__,
    232        [conduit = mPipeline->mConduit, trackName]() mutable {
    233          auto report = MakeUnique<dom::RTCStatsCollection>();
    234          Maybe<webrtc::Call::Stats> stats = conduit->GetCallStats();
    235          stats.apply([&](const auto aStats) {
    236            dom::RTCBandwidthEstimationInternal bw;
    237            bw.mTrackIdentifier = trackName;
    238            bw.mSendBandwidthBps.Construct(aStats.send_bandwidth_bps / 8);
    239            bw.mMaxPaddingBps.Construct(aStats.max_padding_bitrate_bps / 8);
    240            bw.mReceiveBandwidthBps.Construct(aStats.recv_bandwidth_bps / 8);
    241            bw.mPacerDelayMs.Construct(aStats.pacer_delay_ms);
    242            if (aStats.rtt_ms >= 0) {
    243              bw.mRttMs.Construct(aStats.rtt_ms);
    244            }
    245            if (!report->mBandwidthEstimations.AppendElement(std::move(bw),
    246                                                             fallible)) {
    247              mozalloc_handle_oom(0);
    248            }
    249          });
    250          return RTCStatsPromise::CreateAndResolve(std::move(report), __func__);
    251        }));
    252  }
    253 
    254  const bool isSending = mTransceiver->HasBeenUsedToSend();
    255 
    256  promises.AppendElement(InvokeAsync(
    257      mPipeline->mCallThread, __func__,
    258      [pipeline = mPipeline, trackName, mid = std::move(mid),
    259       videoSsrcToRidMap = std::move(videoSsrcToRidMap), isSending,
    260       audioCodec = mAudioCodec.Ref()] {
    261        auto report = MakeUnique<dom::RTCStatsCollection>();
    262        auto asAudio = pipeline->mConduit->AsAudioSessionConduit();
    263        auto asVideo = pipeline->mConduit->AsVideoSessionConduit();
    264 
    265        nsString kind = asVideo.isNothing() ? u"audio"_ns : u"video"_ns;
    266        nsString idstr = kind + u"_"_ns;
    267        idstr.AppendInt(static_cast<uint32_t>(pipeline->Level()));
    268        const bool isSendStable =
    269            !pipeline->mConduit->IsShutdown() && isSending;
    270        for (uint32_t ssrc : pipeline->mConduit->GetLocalSSRCs()) {
    271          nsString localId = u"outbound_rtp_"_ns + idstr + u"_"_ns;
    272          localId.AppendInt(ssrc);
    273          nsString remoteId;
    274          Maybe<uint16_t> base_seq =
    275              pipeline->mConduit->RtpSendBaseSeqFor(ssrc);
    276 
    277          auto constructCommonRemoteInboundRtpStats =
    278              [&](RTCRemoteInboundRtpStreamStats& aRemote,
    279                  const webrtc::ReportBlockData& aRtcpData) {
    280                remoteId = u"outbound_rtcp_"_ns + idstr + u"_"_ns;
    281                remoteId.AppendInt(ssrc);
    282                aRemote.mTimestamp.Construct(
    283                    RTCStatsTimestamp::FromNtp(
    284                        pipeline->GetTimestampMaker(),
    285                        webrtc::Timestamp::Micros(
    286                            aRtcpData.report_block_timestamp_utc().us()) +
    287                            webrtc::TimeDelta::Seconds(webrtc::kNtpJan1970))
    288                        .ToDom());
    289                aRemote.mId.Construct(remoteId);
    290                aRemote.mType.Construct(RTCStatsType::Remote_inbound_rtp);
    291                aRemote.mSsrc = ssrc;
    292                aRemote.mKind = kind;
    293                aRemote.mMediaType.Construct(
    294                    kind);  // mediaType is the old name for kind.
    295                aRemote.mLocalId.Construct(localId);
    296                if (base_seq) {
    297                  if (aRtcpData.extended_highest_sequence_number() <
    298                      *base_seq) {
    299                    aRemote.mPacketsReceived.Construct(0);
    300                    aRemote.mPacketsLost.Construct(0);
    301                  } else {
    302                    aRemote.mPacketsReceived.Construct(
    303                        aRtcpData.extended_highest_sequence_number() -
    304                        aRtcpData.cumulative_lost() - *base_seq + 1);
    305                    aRemote.mPacketsLost.Construct(aRtcpData.cumulative_lost());
    306                  }
    307                }
    308                if (aRtcpData.has_rtt()) {
    309                  aRemote.mRoundTripTime.Construct(
    310                      aRtcpData.last_rtt().template ms<double>() / 1000.0);
    311                }
    312                aRemote.mTotalRoundTripTime.Construct(
    313                    aRtcpData.sum_rtts().template ms<double>() / 1000.0);
    314                aRemote.mFractionLost.Construct(
    315                    static_cast<float>(aRtcpData.fraction_lost_raw()) /
    316                    (1 << 8));
    317                aRemote.mRoundTripTimeMeasurements.Construct(
    318                    aRtcpData.num_rtts());
    319              };
    320 
    321          auto constructCommonOutboundRtpStats =
    322              [&](RTCOutboundRtpStreamStats& aLocal) {
    323                aLocal.mSsrc = ssrc;
    324                aLocal.mTimestamp.Construct(
    325                    pipeline->GetTimestampMaker().GetNow().ToDom());
    326                aLocal.mId.Construct(localId);
    327                aLocal.mType.Construct(RTCStatsType::Outbound_rtp);
    328                aLocal.mKind = kind;
    329                aLocal.mMediaType.Construct(
    330                    kind);  // mediaType is the old name for kind.
    331                if (remoteId.Length()) {
    332                  aLocal.mRemoteId.Construct(remoteId);
    333                }
    334                if (!mid.empty()) {
    335                  aLocal.mMid.Construct(NS_ConvertUTF8toUTF16(mid).get());
    336                }
    337              };
    338 
    339          auto constructCommonMediaSourceStats =
    340              [&](RTCMediaSourceStats& aStats) {
    341                nsString id = u"mediasource_"_ns + idstr + trackName;
    342                aStats.mTimestamp.Construct(
    343                    pipeline->GetTimestampMaker().GetNow().ToDom());
    344                aStats.mId.Construct(id);
    345                aStats.mType.Construct(RTCStatsType::Media_source);
    346                aStats.mTrackIdentifier = trackName;
    347                aStats.mKind = kind;
    348              };
    349 
    350          asAudio.apply([&](auto& aConduit) {
    351            Maybe<webrtc::AudioSendStream::Stats> audioStats =
    352                aConduit->GetSenderStats();
    353            if (audioStats.isNothing()) {
    354              return;
    355            }
    356 
    357            if (!isSendStable) {
    358              // See
    359              // https://www.w3.org/TR/webrtc-stats/#the-rtp-statistics-hierarchy
    360              // for a complete description of the RTP statistics lifetime
    361              // rules.
    362              return;
    363            }
    364 
    365            // First, fill in remote stat with rtcp receiver data, if present.
    366            // ReceiverReports have less information than SenderReports, so fill
    367            // in what we can.
    368            Maybe<webrtc::ReportBlockData> reportBlockData;
    369            {
    370              if (const auto remoteSsrc = aConduit->GetRemoteSSRC();
    371                  remoteSsrc) {
    372                for (auto& data : audioStats->report_block_datas) {
    373                  if (data.source_ssrc() == ssrc &&
    374                      data.sender_ssrc() == *remoteSsrc) {
    375                    reportBlockData.emplace(data);
    376                    break;
    377                  }
    378                }
    379              }
    380            }
    381            reportBlockData.apply([&](auto& aReportBlockData) {
    382              RTCRemoteInboundRtpStreamStats remote;
    383              constructCommonRemoteInboundRtpStats(remote, aReportBlockData);
    384              if (aReportBlockData.jitter() >= 0 && audioCodec) {
    385                remote.mJitter.Construct(
    386                    aReportBlockData.jitter(audioCodec->mFreq)
    387                        .template ms<double>() /
    388                    1000.0);
    389              }
    390              if (!report->mRemoteInboundRtpStreamStats.AppendElement(
    391                      std::move(remote), fallible)) {
    392                mozalloc_handle_oom(0);
    393              }
    394            });
    395 
    396            // Then, fill in local side (with cross-link to remote only if
    397            // present)
    398            RTCOutboundRtpStreamStats local;
    399            constructCommonOutboundRtpStats(local);
    400            local.mPacketsSent.Construct(audioStats->packets_sent);
    401            local.mBytesSent.Construct(audioStats->payload_bytes_sent);
    402            local.mNackCount.Construct(
    403                audioStats->rtcp_packet_type_counts.nack_packets);
    404            local.mHeaderBytesSent.Construct(
    405                audioStats->header_and_padding_bytes_sent);
    406            local.mRetransmittedPacketsSent.Construct(
    407                audioStats->retransmitted_packets_sent);
    408            local.mRetransmittedBytesSent.Construct(
    409                audioStats->retransmitted_bytes_sent);
    410            /*
    411             * Potential new stats that are now available upstream.
    412             * Note: when we last tried exposing this we were getting
    413             * targetBitrate for audio was ending up as 0. We did not
    414             * investigate why.
    415            local.mTargetBitrate.Construct(audioStats->target_bitrate_bps);
    416             */
    417            if (!report->mOutboundRtpStreamStats.AppendElement(std::move(local),
    418                                                               fallible)) {
    419              mozalloc_handle_oom(0);
    420            }
    421 
    422            // TODO(bug 1804678): Use RTCAudioSourceStats
    423            RTCMediaSourceStats mediaSourceStats;
    424            constructCommonMediaSourceStats(mediaSourceStats);
    425            if (!report->mMediaSourceStats.AppendElement(
    426                    std::move(mediaSourceStats), fallible)) {
    427              mozalloc_handle_oom(0);
    428            }
    429          });
    430 
    431          asVideo.apply([&](auto& aConduit) {
    432            Maybe<webrtc::VideoSendStream::Stats> videoStats =
    433                aConduit->GetSenderStats();
    434            if (videoStats.isNothing()) {
    435              return;
    436            }
    437 
    438            Maybe<webrtc::VideoSendStream::StreamStats> streamStats;
    439            auto kv = videoStats->substreams.find(ssrc);
    440            if (kv != videoStats->substreams.end()) {
    441              streamStats = Some(kv->second);
    442            }
    443 
    444            if (!streamStats || !isSendStable) {
    445              // By spec: "The lifetime of all RTP monitored objects starts
    446              // when the RTP stream is first used: When the first RTP packet
    447              // is sent or received on the SSRC it represents"
    448              return;
    449            }
    450 
    451            aConduit->GetAssociatedLocalRtxSSRC(ssrc).apply(
    452                [&](const auto rtxSsrc) {
    453                  auto kv = videoStats->substreams.find(rtxSsrc);
    454                  if (kv != videoStats->substreams.end()) {
    455                    streamStats->rtp_stats.Add(kv->second.rtp_stats);
    456                  }
    457                });
    458 
    459            // First, fill in remote stat with rtcp receiver data, if present.
    460            // ReceiverReports have less information than SenderReports, so fill
    461            // in what we can.
    462            if (streamStats->report_block_data) {
    463              const webrtc::ReportBlockData& rtcpReportData =
    464                  *streamStats->report_block_data;
    465              RTCRemoteInboundRtpStreamStats remote;
    466              remote.mJitter.Construct(
    467                  static_cast<double>(rtcpReportData.jitter()) /
    468                  webrtc::kVideoPayloadTypeFrequency);
    469              constructCommonRemoteInboundRtpStats(remote, rtcpReportData);
    470              if (!report->mRemoteInboundRtpStreamStats.AppendElement(
    471                      std::move(remote), fallible)) {
    472                mozalloc_handle_oom(0);
    473              }
    474            }
    475 
    476            // Then, fill in local side (with cross-link to remote only if
    477            // present)
    478            RTCOutboundRtpStreamStats local;
    479            constructCommonOutboundRtpStats(local);
    480            if (auto it = videoSsrcToRidMap.find(ssrc);
    481                it != videoSsrcToRidMap.end() && it->second != "") {
    482              local.mRid.Construct(NS_ConvertUTF8toUTF16(it->second).get());
    483            }
    484            local.mPacketsSent.Construct(
    485                streamStats->rtp_stats.transmitted.packets);
    486            local.mBytesSent.Construct(
    487                streamStats->rtp_stats.transmitted.payload_bytes);
    488            local.mNackCount.Construct(
    489                streamStats->rtcp_packet_type_counts.nack_packets);
    490            local.mFirCount.Construct(
    491                streamStats->rtcp_packet_type_counts.fir_packets);
    492            local.mPliCount.Construct(
    493                streamStats->rtcp_packet_type_counts.pli_packets);
    494            local.mFramesEncoded.Construct(streamStats->frames_encoded);
    495            if (streamStats->qp_sum) {
    496              local.mQpSum.Construct(*streamStats->qp_sum);
    497            }
    498            local.mHeaderBytesSent.Construct(
    499                streamStats->rtp_stats.transmitted.header_bytes +
    500                streamStats->rtp_stats.transmitted.padding_bytes);
    501            local.mRetransmittedPacketsSent.Construct(
    502                streamStats->rtp_stats.retransmitted.packets);
    503            local.mRetransmittedBytesSent.Construct(
    504                streamStats->rtp_stats.retransmitted.payload_bytes);
    505            local.mTotalEncodedBytesTarget.Construct(
    506                videoStats->total_encoded_bytes_target);
    507            local.mFrameWidth.Construct(streamStats->width);
    508            local.mFrameHeight.Construct(streamStats->height);
    509            local.mFramesPerSecond.Construct(streamStats->encode_frame_rate);
    510            local.mFramesSent.Construct(streamStats->frames_encoded);
    511            local.mHugeFramesSent.Construct(streamStats->huge_frames_sent);
    512            local.mTotalEncodeTime.Construct(
    513                double(streamStats->total_encode_time_ms) / 1000.);
    514            /*
    515             * Potential new stats that are now available upstream.
    516            local.mTargetBitrate.Construct(videoStats->target_media_bitrate_bps);
    517             */
    518            if (!report->mOutboundRtpStreamStats.AppendElement(std::move(local),
    519                                                               fallible)) {
    520              mozalloc_handle_oom(0);
    521            }
    522 
    523            RTCVideoSourceStats videoSourceStats;
    524            constructCommonMediaSourceStats(videoSourceStats);
    525            // webrtc::VideoSendStream::Stats does not have width/height. We
    526            // might be able to get this somewhere else?
    527            // videoStats->frames is not documented, but looking at the
    528            // implementation it appears to be the number of frames inputted to
    529            // the encoder, which ought to work.
    530            videoSourceStats.mFrames.Construct(videoStats->frames);
    531            videoSourceStats.mFramesPerSecond.Construct(
    532                videoStats->input_frame_rate);
    533            auto resolution = aConduit->GetLastResolution();
    534            resolution.apply([&](const auto& aResolution) {
    535              videoSourceStats.mWidth.Construct(aResolution.width);
    536              videoSourceStats.mHeight.Construct(aResolution.height);
    537            });
    538            if (!report->mVideoSourceStats.AppendElement(
    539                    std::move(videoSourceStats), fallible)) {
    540              mozalloc_handle_oom(0);
    541            }
    542          });
    543        }
    544 
    545        return RTCStatsPromise::CreateAndResolve(std::move(report), __func__);
    546      }));
    547 
    548  if (!aSkipIceStats && GetJsepTransceiver().mTransport.mComponents) {
    549    promises.AppendElement(mTransportHandler->GetIceStats(
    550        GetJsepTransceiver().mTransport.mTransportId,
    551        mPipeline->GetTimestampMaker().GetNow().ToDom()));
    552  }
    553 
    554  return promises;
    555 }
    556 
    557 void RTCRtpSender::GetCapabilities(const GlobalObject&, const nsAString& aKind,
    558                                   Nullable<dom::RTCRtpCapabilities>& aResult) {
    559  PeerConnectionImpl::GetCapabilities(aKind, aResult, sdp::Direction::kSend);
    560 }
    561 
    562 void RTCRtpSender::WarnAboutBadSetParameters(const nsCString& aError) {
    563  nsCString warning(
    564      "WARNING! Invalid setParameters call detected! The good news? Firefox "
    565      "supports sendEncodings in addTransceiver now, so we ask that you switch "
    566      "over to using the parameters code you use for other browsers. Thank you "
    567      "for your patience and support. The specific error was: ");
    568  warning += aError;
    569  mPc->SendWarningToConsole(warning);
    570 }
    571 
    572 nsCString RTCRtpSender::GetEffectiveTLDPlus1() const {
    573  return mPc->GetEffectiveTLDPlus1();
    574 }
    575 
    576 already_AddRefed<Promise> RTCRtpSender::SetParameters(
    577    const dom::RTCRtpSendParameters& aParameters, ErrorResult& aError) {
    578  dom::RTCRtpSendParameters paramsCopy(aParameters);
    579  // When the setParameters method is called, the user agent MUST run the
    580  // following steps:
    581  // Let parameters be the method's first argument.
    582  // Let sender be the RTCRtpSender object on which setParameters is invoked.
    583  // Let transceiver be the RTCRtpTransceiver object associated with sender
    584  // (i.e.sender is transceiver.[[Sender]]).
    585 
    586  RefPtr<dom::Promise> p = MakePromise(aError);
    587  if (aError.Failed()) {
    588    return nullptr;
    589  }
    590 
    591  if (mPc->IsClosed()) {
    592    p->MaybeRejectWithInvalidStateError("Peer connection is closed");
    593    return p.forget();
    594  }
    595 
    596  // If transceiver.[[Stopping]] is true, return a promise rejected with a newly
    597  // created InvalidStateError.
    598  if (mTransceiver->Stopping()) {
    599    p->MaybeRejectWithInvalidStateError(
    600        "This sender's transceiver is stopping/stopped");
    601    return p.forget();
    602  }
    603 
    604  // If sender.[[LastReturnedParameters]] is null, return a promise rejected
    605  // with a newly created InvalidStateError.
    606  if (!mLastReturnedParameters.isSome()) {
    607    nsCString error;
    608    if (mLastTransactionId.isSome() && paramsCopy.mTransactionId.WasPassed() &&
    609        *mLastTransactionId == paramsCopy.mTransactionId.Value()) {
    610      error =
    611          "Event loop was relinquished between getParameters and setParameters "
    612          "calls";
    613    } else {
    614      error = "Cannot call setParameters without first calling getParameters";
    615    }
    616 
    617    if (mAllowOldSetParameters) {
    618      if (!mHaveWarnedBecauseNoGetParameters) {
    619        mHaveWarnedBecauseNoGetParameters = true;
    620        mozilla::glean::rtcrtpsender_setparameters::warn_no_getparameters
    621            .AddToNumerator(1);
    622      }
    623      WarnAboutBadSetParameters(error);
    624    } else {
    625      if (!mHaveFailedBecauseNoGetParameters) {
    626        mHaveFailedBecauseNoGetParameters = true;
    627        mozilla::glean::rtcrtpsender_setparameters::fail_no_getparameters
    628            .AddToNumerator(1);
    629      }
    630      p->MaybeRejectWithInvalidStateError(error);
    631      return p.forget();
    632    }
    633  }
    634 
    635  // According to the spec, our consistency checking is based on
    636  // [[LastReturnedParameters]], but if we're letting
    637  // [[LastReturnedParameters]]==null slide, we still want to do
    638  // consistency checking on _something_ so we can warn implementers if they
    639  // are messing that up also. Just find something, _anything_, to do that
    640  // checking with.
    641  // TODO(bug 1803388): Remove this stuff once it is no longer needed.
    642  // TODO(bug 1803389): Remove the glean errors once they are no longer needed.
    643  Maybe<RTCRtpSendParameters> oldParams;
    644  if (mAllowOldSetParameters) {
    645    if (mLastReturnedParameters.isSome()) {
    646      oldParams = mLastReturnedParameters;
    647    } else if (mPendingParameters.isSome()) {
    648      oldParams = mPendingParameters;
    649    } else {
    650      oldParams = Some(mParameters);
    651    }
    652    MOZ_ASSERT(oldParams.isSome());
    653  } else {
    654    oldParams = mLastReturnedParameters;
    655  }
    656  MOZ_ASSERT(oldParams.isSome());
    657 
    658  // Validate parameters by running the following steps:
    659  // Let encodings be parameters.encodings.
    660  // Let codecs be parameters.codecs.
    661  // Let N be the number of RTCRtpEncodingParameters stored in
    662  // sender.[[SendEncodings]].
    663  // If any of the following conditions are met,
    664  // return a promise rejected with a newly created InvalidModificationError:
    665 
    666  bool pendingRidChangeFromCompatMode = false;
    667  // encodings.length is different from N.
    668  if (paramsCopy.mEncodings.Length() != oldParams->mEncodings.Length()) {
    669    nsCString error("Cannot change the number of encodings with setParameters");
    670    if (!mAllowOldSetParameters) {
    671      if (!mHaveFailedBecauseEncodingCountChange) {
    672        mHaveFailedBecauseEncodingCountChange = true;
    673        mozilla::glean::rtcrtpsender_setparameters::fail_length_changed
    674            .AddToNumerator(1);
    675      }
    676      p->MaybeRejectWithInvalidModificationError(error);
    677      return p.forget();
    678    }
    679    // Make sure we don't use the old rids in SyncToJsep while we wait for the
    680    // queued task below to update mParameters.
    681    pendingRidChangeFromCompatMode = true;
    682    mSimulcastEnvelopeSet = true;
    683    if (!mHaveWarnedBecauseEncodingCountChange) {
    684      mHaveWarnedBecauseEncodingCountChange = true;
    685      mozilla::glean::rtcrtpsender_setparameters::warn_length_changed
    686          .AddToNumerator(1);
    687    }
    688    WarnAboutBadSetParameters(error);
    689  } else {
    690    // encodings has been re-ordered.
    691    for (size_t i = 0; i < paramsCopy.mEncodings.Length(); ++i) {
    692      const auto& oldEncoding = oldParams->mEncodings[i];
    693      const auto& newEncoding = paramsCopy.mEncodings[i];
    694      if (oldEncoding.mRid != newEncoding.mRid) {
    695        nsCString error("Cannot change rid, or reorder encodings");
    696        if (!mHaveFailedBecauseRidChange) {
    697          mHaveFailedBecauseRidChange = true;
    698          mozilla::glean::rtcrtpsender_setparameters::fail_rid_changed
    699              .AddToNumerator(1);
    700        }
    701        p->MaybeRejectWithInvalidModificationError(error);
    702        return p.forget();
    703      }
    704    }
    705  }
    706 
    707  // TODO(bug 1803388): Handle this in webidl, once we stop allowing the old
    708  // setParameters style.
    709  if (!paramsCopy.mTransactionId.WasPassed()) {
    710    nsCString error("transactionId is not set!");
    711    if (!mAllowOldSetParameters) {
    712      if (!mHaveFailedBecauseNoTransactionId) {
    713        mHaveFailedBecauseNoTransactionId = true;
    714        mozilla::glean::rtcrtpsender_setparameters::fail_no_transactionid
    715            .AddToNumerator(1);
    716      }
    717      p->MaybeRejectWithTypeError(error);
    718      return p.forget();
    719    }
    720    if (!mHaveWarnedBecauseNoTransactionId) {
    721      mHaveWarnedBecauseNoTransactionId = true;
    722      mozilla::glean::rtcrtpsender_setparameters::warn_no_transactionid
    723          .AddToNumerator(1);
    724    }
    725    WarnAboutBadSetParameters(error);
    726  } else if (oldParams->mTransactionId.WasPassed() &&
    727             oldParams->mTransactionId != paramsCopy.mTransactionId) {
    728    // Any parameter in parameters is marked as a Read-only parameter (such as
    729    // RID) and has a value that is different from the corresponding parameter
    730    // value in sender.[[LastReturnedParameters]]. Note that this also applies
    731    // to transactionId.
    732    // Don't throw this error if letting the "stale getParameters" case slide.
    733    nsCString error(
    734        "Cannot change transaction id: call getParameters, modify the result, "
    735        "and then call setParameters");
    736    if (!mHaveFailedBecauseStaleTransactionId) {
    737      mHaveFailedBecauseStaleTransactionId = true;
    738      mozilla::glean::rtcrtpsender_setparameters::fail_stale_transactionid
    739          .AddToNumerator(1);
    740    }
    741    p->MaybeRejectWithInvalidModificationError(error);
    742    return p.forget();
    743  }
    744 
    745  // This could conceivably happen if we are allowing the old setParameters
    746  // behavior.
    747  if (!paramsCopy.mEncodings.Length()) {
    748    nsCString error("Cannot set an empty encodings array");
    749    if (!mAllowOldSetParameters) {
    750      if (!mHaveFailedBecauseNoEncodings) {
    751        mHaveFailedBecauseNoEncodings = true;
    752        mozilla::glean::rtcrtpsender_setparameters::fail_no_encodings
    753            .AddToNumerator(1);
    754      }
    755 
    756      p->MaybeRejectWithInvalidModificationError(error);
    757      return p.forget();
    758    }
    759    // TODO: Add some warning telemetry here
    760    WarnAboutBadSetParameters(error);
    761    // Just don't do this; it's stupid.
    762    paramsCopy.mEncodings = oldParams->mEncodings;
    763  }
    764 
    765  if (!(oldParams->mCodecs == paramsCopy.mCodecs)) {
    766    nsCString error("RTCRtpParameters.codecs is a read-only parameter");
    767    if (!mAllowOldSetParameters) {
    768      p->MaybeRejectWithInvalidModificationError(error);
    769      return p.forget();
    770    }
    771    WarnAboutBadSetParameters(error);
    772  }
    773 
    774  // Coverts a list of JsepCodecDescription to a list of
    775  // dom::RTCRtpCodecParameters
    776  auto toDomCodecParametersList =
    777      [](const std::vector<UniquePtr<JsepCodecDescription>>& aJsepCodec)
    778      -> dom::Sequence<RTCRtpCodecParameters> {
    779    dom::Sequence<RTCRtpCodecParameters> codecs;
    780    for (const auto& codec : aJsepCodec) {
    781      if (codec) {
    782        auto type = codec->Type();
    783        std::string typeStr;
    784        switch (type) {
    785          case SdpMediaSection::MediaType::kApplication:
    786            typeStr = "application";
    787            break;
    788          case SdpMediaSection::MediaType::kAudio:
    789            typeStr = "audio";
    790            break;
    791          case SdpMediaSection::MediaType::kVideo:
    792            typeStr = "video";
    793            break;
    794          case SdpMediaSection::MediaType::kMessage:
    795            typeStr = "message";
    796            break;
    797          case SdpMediaSection::MediaType::kText:
    798            typeStr = "text";
    799            break;
    800          default:
    801            MOZ_CRASH("Unexpected SdpMediaSection::MediaType");
    802        };
    803        if (typeStr == "audio" || typeStr == "video") {
    804          dom::RTCRtpCodecParameters domCodec;
    805          RTCRtpTransceiver::ToDomRtpCodecParameters(*codec, &domCodec);
    806          (void)codecs.AppendElement(domCodec, fallible);
    807        }
    808      }
    809    }
    810    return codecs;
    811  };
    812 
    813  // TODO: Verify remaining read-only parameters
    814  // headerExtensions (bug 1765851)
    815  // rtcp (bug 1765852)
    816 
    817  // CheckAndRectifyEncodings handles the following steps:
    818  // If transceiver kind is "audio", remove the scaleResolutionDownBy member
    819  // from all encodings that contain one.
    820  //
    821  // If transceiver kind is "video", and any encoding in encodings contains a
    822  // scaleResolutionDownBy member whose value is less than 1.0, return a
    823  // promise rejected with a newly created RangeError.
    824  //
    825  // Verify that each encoding in encodings has a maxFramerate member whose
    826  // value is greater than or equal to 0.0. If one of the maxFramerate values
    827  // does not meet this requirement, return a promise rejected with a newly
    828  // created RangeError.
    829 
    830  auto choosableCodecs =
    831      mParameters.mCodecs.WasPassed() && mParameters.mCodecs.Value().Length()
    832          ? mParameters.mCodecs.Value()
    833          : Sequence<RTCRtpCodecParameters>();
    834  if (choosableCodecs.Length() == 0) {
    835    // If choosableCodecs is still an empty list, set choosableCodecs to the
    836    // list of implemented send codecs for transceiver's kind.
    837    std::vector<UniquePtr<JsepCodecDescription>> codecs;
    838    if (mTransceiver->IsVideo()) {
    839      auto useRtx =
    840          Preferences::GetBool("media.peerconnection.video.use_rtx", false)
    841              ? OverrideRtxPreference::OverrideWithEnabled
    842              : OverrideRtxPreference::OverrideWithDisabled;
    843      PeerConnectionImpl::GetDefaultVideoCodecs(codecs, useRtx);
    844    } else {
    845      PeerConnectionImpl::GetDefaultAudioCodecs(codecs);
    846    }
    847    choosableCodecs = toDomCodecParametersList(codecs);
    848  }
    849  ErrorResult rv;
    850  CheckAndRectifyEncodings(paramsCopy.mEncodings, mTransceiver->IsVideo(),
    851                           dom::Optional(choosableCodecs), true, false,
    852                           MatchGetCapabilities::NO, rv);
    853  if (rv.Failed()) {
    854    if (!mHaveFailedBecauseOtherError) {
    855      mHaveFailedBecauseOtherError = true;
    856      mozilla::glean::rtcrtpsender_setparameters::fail_other.AddToNumerator(1);
    857    }
    858    p->MaybeReject(std::move(rv));
    859    return p.forget();
    860  }
    861 
    862  // If transceiver kind is "video", then for each encoding in encodings that
    863  // doesn't contain a scaleResolutionDownBy member, add a
    864  // scaleResolutionDownBy member with the value 1.0.
    865  if (mTransceiver->IsVideo()) {
    866    for (auto& encoding : paramsCopy.mEncodings) {
    867      if (!encoding.mScaleResolutionDownBy.WasPassed()) {
    868        encoding.mScaleResolutionDownBy.Construct(1.0);
    869      }
    870    }
    871  }
    872 
    873  // Let p be a new promise. (see above)
    874 
    875  // In parallel, configure the media stack to use parameters to transmit
    876  // sender.[[SenderTrack]].
    877  // Right now this is infallible. That may change someday.
    878 
    879  // We need to put this in a member variable, since MaybeUpdateConduit needs it
    880  // This also allows PeerConnectionImpl to detect when there is a pending
    881  // setParameters, which has implcations for the handling of
    882  // setRemoteDescription.
    883  mPendingRidChangeFromCompatMode = pendingRidChangeFromCompatMode;
    884  mPendingParameters = Some(paramsCopy);
    885  uint32_t serialNumber = ++mNumSetParametersCalls;
    886  MaybeUpdateConduit();
    887 
    888  // If we have a degradation value passed convert it from a
    889  // dom::RTCDegradationPreference to a webrtc::DegradationPreference and set
    890  // mVideoDegradationPreference which will trigger the onconfigure change in
    891  // the videoConduit.
    892  if (paramsCopy.mDegradationPreference.WasPassed()) {
    893    const auto degradationPreference = [&] {
    894      switch (paramsCopy.mDegradationPreference.Value()) {
    895        case mozilla::dom::RTCDegradationPreference::Balanced:
    896          return webrtc::DegradationPreference::BALANCED;
    897        case mozilla::dom::RTCDegradationPreference::Maintain_framerate:
    898          return webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
    899        case mozilla::dom::RTCDegradationPreference::Maintain_resolution:
    900          return webrtc::DegradationPreference::MAINTAIN_RESOLUTION;
    901      }
    902      MOZ_CRASH("Unexpected RTCDegradationPreference");
    903    };
    904    mVideoDegradationPreference = degradationPreference();
    905  } else {
    906    // Default to disabled when unset to allow for correct degradation
    907    mVideoDegradationPreference = webrtc::DegradationPreference::DISABLED;
    908  }
    909 
    910  // If the media stack is successfully configured with parameters,
    911  // queue a task to run the following steps:
    912  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
    913      __func__,
    914      [this, self = RefPtr<RTCRtpSender>(this), p, paramsCopy, serialNumber] {
    915        // Set sender.[[LastReturnedParameters]] to null.
    916        mLastReturnedParameters = Nothing();
    917        // Set sender.[[SendEncodings]] to parameters.encodings.
    918        mParameters.mEncodings = paramsCopy.mEncodings;
    919        UpdateRestorableEncodings(mParameters.mEncodings);
    920        // If we had a valid degradation preference passed in store it in
    921        // mParameters so we can return it if needed via GetParameters calls.
    922        mParameters.mDegradationPreference.Reset();
    923        if (paramsCopy.mDegradationPreference.WasPassed()) {
    924          mParameters.mDegradationPreference.Construct(
    925              paramsCopy.mDegradationPreference.Value());
    926        }
    927        // Only clear mPendingParameters if it matches; there could have been
    928        // back-to-back calls to setParameters, and we only want to clear this
    929        // if no subsequent setParameters is pending.
    930        if (serialNumber == mNumSetParametersCalls) {
    931          mPendingParameters = Nothing();
    932          // Ok, nothing has called SyncToJsep while this async task was
    933          // pending. No need for special handling anymore.
    934          mPendingRidChangeFromCompatMode = false;
    935        }
    936        MOZ_ASSERT(mParameters.mEncodings.Length());
    937        // Resolve p with undefined.
    938        p->MaybeResolveWithUndefined();
    939      }));
    940 
    941  // Return p.
    942  return p.forget();
    943 }
    944 
    945 using FmtpParamKey = nsString;
    946 using FmtpParamValue = nsString;
    947 
    948 // Helper type for the codec dictionary matching functions.
    949 // This stores the level & (h264) subprofile separately from the parameter set.
    950 // Which parameters are considered part of the level is codec-specific.
    951 // All of the matching logic is kept out of this struct, instead it all lives
    952 // in the DoesCodecParameterMatchCodec function. This is to make it easier to
    953 // read against the spec.
    954 struct ParametersAndLevel {
    955  Maybe<std::set<std::tuple<FmtpParamKey, FmtpParamValue>>> mSet = Nothing();
    956  Maybe<uint32_t> mLevel = Nothing();
    957  Maybe<uint32_t> mSubprofile = Nothing();
    958 
    959  // Helper function to get the default level for a codec.
    960  static Maybe<uint32_t> DefaultLevelForCodec(const nsString& aMimeType) {
    961    // AV1 has a defined default level-idx of 5, which is omittable by spec.
    962    if (aMimeType.LowerCaseEqualsASCII("video/av1")) {
    963      return Some(5);
    964    }
    965    // https://datatracker.ietf.org/doc/html/rfc6184 defines a default value
    966    // for this parameter, 0x420010.
    967    if (aMimeType.LowerCaseEqualsASCII("video/h264")) {
    968      return Some(JsepVideoCodecDescription::GetSaneH264Level(0x420010));
    969    }
    970    // VP8 and VP9 are not defined to have a level parameter. Though they do
    971    // have a profile-id, which doesn't seem to be used in our negotiation.
    972    if (aMimeType.LowerCaseEqualsASCII("video/vp8") ||
    973        aMimeType.LowerCaseEqualsASCII("video/vp9")) {
    974      return Nothing();  // A little pedantic...
    975    }
    976    return Nothing();
    977  }
    978 
    979  static Maybe<uint32_t> DefaultSubprofileForCodec(const nsString& aMimeType) {
    980    // See DefaultLevelCodec for comments on the default level.
    981    if (aMimeType.LowerCaseEqualsASCII("video/h264")) {
    982      return Some(JsepVideoCodecDescription::GetSubprofile(0x420010));
    983    }
    984    return Nothing();
    985  }
    986 
    987  // A helper function to extract the level from a parameter set in a codec-
    988  // specific way. If the level cannot be extracted, Nothing() is returned.
    989  static Maybe<uint32_t> ExtractLevel(const nsString& aMimeType,
    990                                      const FmtpParamKey& aKey,
    991                                      const FmtpParamValue& aValue) {
    992    if (aMimeType.LowerCaseEqualsASCII("video/h264") &&
    993        aKey.LowerCaseEqualsASCII("profile-level-id")) {
    994      // The level is the last two characters of the value.
    995      nsresult rv;
    996      // Note the radix
    997      auto val = aValue.ToUnsignedInteger(&rv, 16);
    998      if (NS_FAILED(rv)) {
    999        return Nothing();
   1000      }
   1001      return Some(JsepVideoCodecDescription::GetSaneH264Level(val));
   1002    }
   1003    if (aMimeType.LowerCaseEqualsASCII("video/av1") &&
   1004        aKey.EqualsLiteral("level-idx")) {
   1005      nsresult rv;
   1006      auto val = aValue.ToUnsignedInteger(&rv);
   1007      if (NS_FAILED(rv)) {
   1008        return Nothing();
   1009      }
   1010      return Some(val);
   1011    }
   1012    return Nothing();
   1013  }
   1014 
   1015  // Helper function to get the subprofile for a codec.
   1016  static Maybe<uint32_t> ExtractSubprofile(const nsString& aMimeType,
   1017                                           const FmtpParamKey& aKey,
   1018                                           const FmtpParamValue& aValue) {
   1019    if (aMimeType.LowerCaseEqualsASCII("video/h264") &&
   1020        aKey.EqualsLiteral("profile-level-id")) {
   1021      nsresult rv;
   1022      auto val = aValue.ToUnsignedInteger(&rv, 16);
   1023      if (NS_FAILED(rv)) {
   1024        return Nothing();
   1025      }
   1026      return Some(JsepVideoCodecDescription::GetSubprofile(val));
   1027    }
   1028    return Nothing();
   1029  }
   1030 };
   1031 
   1032 // We can not directly compare H264 or AV1 FMTP parameter sets, since the level
   1033 // and subprofile information must be treated seperately as a hiearchical value.
   1034 //  So we need to seperate the regular parameters from profile-level-id for
   1035 // H264, and levelidx for AV1. This is done by parsing the FMTP line into a set
   1036 // of key-value pairs and a level/subprofile value. If the FMTP line is not in
   1037 // a key-value pair format, then we return an empty parameter set.
   1038 ParametersAndLevel FmtpToParametersAndLevel(const nsString& aMimeType,
   1039                                            const nsString& aFmtp) {
   1040  auto resultParams = std::set<std::tuple<FmtpParamKey, FmtpParamValue>>();
   1041  Maybe<uint32_t> resultLevel = Nothing();
   1042  Maybe<uint32_t> resultSubprofile = Nothing();
   1043  nsTArray<nsString> parts;
   1044  for (const auto& kvp : aFmtp.Split(';')) {
   1045    auto parts = nsTArray<nsString>();
   1046    for (const auto& part : kvp.Split('=')) {
   1047      parts.AppendElement(part);
   1048    }
   1049    // To be a valid key-value pair, there must be exactly two parts.
   1050    if (parts.Length() == 2) {
   1051      // Check to see if it is the level parameter.
   1052      auto level =
   1053          ParametersAndLevel::ExtractLevel(aMimeType, parts[0], parts[1]);
   1054      if (level.isNothing()) {
   1055        // If it is not the level parameter, then it is a regular parameter.
   1056        // We store the key-value pair in the result parameter set.
   1057        resultParams.insert(std::make_tuple(parts[0], parts[1]));
   1058      } else {
   1059        // It is the level parameter, so we do not store it in the result
   1060        // parameter set. Instead we store it in the result level, and
   1061        // subprofile (if provided).
   1062        resultSubprofile = ParametersAndLevel::ExtractSubprofile(
   1063            aMimeType, parts[0], parts[1]);
   1064        // Store the level separately
   1065        resultLevel = level;
   1066      }
   1067    } else {
   1068      // This is not a valid key-value pair FMTP line, so we do not have
   1069      // parameters.
   1070      return ParametersAndLevel{
   1071          .mSet = Nothing(),
   1072          .mLevel = resultLevel.orElse([&]() -> Maybe<uint32_t> {
   1073            return ParametersAndLevel::DefaultLevelForCodec(aMimeType);
   1074          }),
   1075          .mSubprofile = resultSubprofile,
   1076      };
   1077    }
   1078  }
   1079  return ParametersAndLevel{
   1080      .mSet = Some(resultParams),
   1081      .mLevel = resultLevel.orElse([&]() -> Maybe<uint32_t> {
   1082        return ParametersAndLevel::DefaultLevelForCodec(aMimeType);
   1083      }),
   1084      .mSubprofile = resultSubprofile,
   1085  };
   1086 }
   1087 
   1088 bool DoesCodecParameterMatchCodec(const RTCRtpCodec& aCodec1,
   1089                                  const RTCRtpCodec& aCodec2,
   1090                                  const bool aIgnoreLevels) {
   1091  const auto compare = [](const nsString& aStr1, const nsString& aStr2) {
   1092    return NS_LossyConvertUTF16toASCII(aStr1).EqualsIgnoreCase(
   1093        NS_LossyConvertUTF16toASCII(aStr2).get());
   1094  };
   1095  if (!compare(aCodec1.mMimeType, aCodec2.mMimeType)) {
   1096    return false;
   1097  }
   1098  if (aCodec1.mClockRate != aCodec2.mClockRate) {
   1099    return false;
   1100  }
   1101 
   1102  if (aCodec1.mChannels != aCodec2.mChannels) {
   1103    return false;
   1104  }
   1105  // To match both or neither should have a sdpFmtpLine.
   1106  if (aCodec1.mSdpFmtpLine.WasPassed() != aCodec2.mSdpFmtpLine.WasPassed()) {
   1107    return false;
   1108  }
   1109  // If both have a sdpFmtpLine, compare them, and conditionally take levels
   1110  // into account.
   1111  if (aCodec1.mSdpFmtpLine.WasPassed() && aCodec2.mSdpFmtpLine.WasPassed()) {
   1112    // Get the key-value pairs from the sdpFmtpLine if they are in a key-value
   1113    // pair format.
   1114    const auto pset1 = FmtpToParametersAndLevel(aCodec1.mMimeType,
   1115                                                aCodec1.mSdpFmtpLine.Value());
   1116    const auto pset2 = FmtpToParametersAndLevel(aCodec2.mMimeType,
   1117                                                aCodec2.mSdpFmtpLine.Value());
   1118    if (pset1.mSet.isNothing() || pset2.mSet.isNothing()) {
   1119      // If either or both are not in a key-value pair format, they should be
   1120      // compared using string equality.
   1121      if (aCodec1.mSdpFmtpLine != aCodec2.mSdpFmtpLine) {
   1122        return false;
   1123      }
   1124    } else {
   1125      // If both are in a key-value pair format, compare the key-value pairs.
   1126      const auto& set1 = pset1.mSet.value();
   1127      const auto& set2 = pset2.mSet.value();
   1128 
   1129      if (set1.size() != set2.size()) {
   1130        return false;
   1131      }
   1132      if (!aIgnoreLevels && (pset1.mLevel != pset2.mLevel ||
   1133                             pset1.mSubprofile != pset2.mSubprofile)) {
   1134        return false;
   1135      }
   1136      // Compare pair-wise the two parameter sets.
   1137      for (const auto& pair : set1) {
   1138        if (set2.find(pair) == set2.end()) {
   1139          return false;
   1140        }
   1141      }
   1142    }
   1143  }
   1144  return true;
   1145 }
   1146 
   1147 // static
   1148 void RTCRtpSender::CheckAndRectifyEncodings(
   1149    Sequence<RTCRtpEncodingParameters>& aEncodings, bool aVideo,
   1150    const Optional<Sequence<RTCRtpCodecParameters>>& aCodecs,
   1151    const bool aIgnoreLevels, const bool aCodecErasure,
   1152    const MatchGetCapabilities aMatchGetCapabilities, ErrorResult& aRv) {
   1153  // If any encoding contains a rid member whose value does not conform to the
   1154  // grammar requirements specified in Section 10 of [RFC8851], throw a
   1155  // TypeError.
   1156  for (const auto& encoding : aEncodings) {
   1157    if (encoding.mRid.WasPassed()) {
   1158      std::string utf8Rid = NS_ConvertUTF16toUTF8(encoding.mRid.Value()).get();
   1159      std::string error;
   1160      if (!SdpRidAttributeList::CheckRidValidity(utf8Rid, &error)) {
   1161        aRv.ThrowTypeError(nsCString(error));
   1162        return;
   1163      }
   1164      if (utf8Rid.size() > SdpRidAttributeList::kMaxRidLength) {
   1165        std::ostringstream ss;
   1166        ss << "Rid can be at most " << SdpRidAttributeList::kMaxRidLength
   1167           << " characters long (due to internal limitations)";
   1168        aRv.ThrowTypeError(nsCString(ss.str()));
   1169        return;
   1170      }
   1171    }
   1172  }
   1173 
   1174  // Post-negotiation we should have a list of aCodecs, and for any encoding in
   1175  // aEncodings that is using a codec that is not in aCodecs, we erase the codec
   1176  // field from the encoding during set session description.
   1177  // Yes. Really. This is what the spec says.
   1178  // https://w3c.github.io/webrtc-pc/#set-the-session-description 4.6.13
   1179  // Let codecs be transceiver.[[Sender]].[[SendCodecs]].
   1180  //  If codecs is not an empty list:
   1181  //    For each encoding in transceiver.[[Sender]].[[SendEncodings]], if
   1182  //    encoding.codec does not match any entry in codecs, using the codec
   1183  //    dictionary match algorithm with ignoreLevels set to true, remove
   1184  //    encoding.codec.
   1185  if (aCodecs.WasPassed() && aCodecs.Value().Length()) {
   1186    for (auto& encoding : aEncodings) {
   1187      if (encoding.mCodec.WasPassed()) {
   1188        bool matched = false;
   1189        for (const auto& codec : aCodecs.Value()) {
   1190          if (DoesCodecParameterMatchCodec(encoding.mCodec.Value(), codec,
   1191                                           aIgnoreLevels)) {
   1192            matched = true;
   1193            break;
   1194          }
   1195        }
   1196        if (!matched) {
   1197          if (aCodecErasure) {
   1198            encoding.mCodec.Reset();
   1199          } else {
   1200            std::stringstream ss;
   1201            ss << "Codec " << encoding.mCodec.Value().mMimeType
   1202               << " not found in send codecs";
   1203            aRv.ThrowInvalidModificationError(nsCString(ss.str()));
   1204            return;
   1205          }
   1206        }
   1207      }
   1208    }
   1209  }
   1210 
   1211  // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver
   1212  // Step 8.3
   1213  // If any encoding contains a codec member whose value does not match any
   1214  // codec in RTCRtpSender.getCapabilities(kind).codecs, throw an
   1215  // OperationError.
   1216  if (aMatchGetCapabilities == MatchGetCapabilities::YES) {
   1217    MOZ_ASSERT(aCodecs.WasPassed(),
   1218               "aCodecs must be passed if aMatchGetCapabilities is YES");
   1219 
   1220    bool found = false;
   1221    for (const auto& encoding : aEncodings) {
   1222      if (encoding.mCodec.WasPassed()) {
   1223        for (const auto& codec : aCodecs.Value()) {
   1224          if (DoesCodecParameterMatchCodec(encoding.mCodec.Value(), codec,
   1225                                           aIgnoreLevels)) {
   1226            found = true;
   1227            break;
   1228          }
   1229        }
   1230        if (!found) {
   1231          aRv.ThrowOperationError("Codec not found in codecs");
   1232          return;
   1233        }
   1234      }
   1235    }
   1236  }
   1237 
   1238  if (aEncodings.Length() > 1) {
   1239    // If some but not all encodings contain a rid member, throw a TypeError.
   1240    // rid must be set if there is more than one encoding
   1241    // NOTE: Since rid is read-only, and the number of encodings cannot grow,
   1242    // this should never happen in setParameters.
   1243    for (const auto& encoding : aEncodings) {
   1244      if (!encoding.mRid.WasPassed()) {
   1245        aRv.ThrowTypeError("Missing rid");
   1246        return;
   1247      }
   1248    }
   1249 
   1250    // If any encoding contains a rid member whose value is the same as that of
   1251    // a rid contained in another encoding in sendEncodings, throw a TypeError.
   1252    // NOTE: Since rid is read-only, and the number of encodings cannot grow,
   1253    // this should never happen in setParameters.
   1254    std::set<nsString> uniqueRids;
   1255    for (const auto& encoding : aEncodings) {
   1256      if (uniqueRids.count(encoding.mRid.Value())) {
   1257        aRv.ThrowTypeError("Duplicate rid");
   1258        return;
   1259      }
   1260      uniqueRids.insert(encoding.mRid.Value());
   1261    }
   1262  }
   1263  // TODO: ptime/adaptivePtime validation (bug 1733647)
   1264 
   1265  // If kind is "audio", remove the scaleResolutionDownBy member from all
   1266  // encodings that contain one.
   1267  if (!aVideo) {
   1268    for (auto& encoding : aEncodings) {
   1269      if (encoding.mScaleResolutionDownBy.WasPassed()) {
   1270        encoding.mScaleResolutionDownBy.Reset();
   1271      }
   1272      if (encoding.mMaxFramerate.WasPassed()) {
   1273        encoding.mMaxFramerate.Reset();
   1274      }
   1275    }
   1276  }
   1277 
   1278  // If any encoding contains a scaleResolutionDownBy member whose value is
   1279  // less than 1.0, throw a RangeError.
   1280  for (const auto& encoding : aEncodings) {
   1281    if (encoding.mScaleResolutionDownBy.WasPassed()) {
   1282      if (encoding.mScaleResolutionDownBy.Value() < 1.0f) {
   1283        aRv.ThrowRangeError("scaleResolutionDownBy must be >= 1.0");
   1284        return;
   1285      }
   1286    }
   1287  }
   1288 
   1289  // Verify that the value of each maxFramerate member in sendEncodings that is
   1290  // defined is greater than 0.0. If one of the maxFramerate values does not
   1291  // meet this requirement, throw a RangeError.
   1292  for (const auto& encoding : aEncodings) {
   1293    if (encoding.mMaxFramerate.WasPassed()) {
   1294      if (encoding.mMaxFramerate.Value() < 0.0f) {
   1295        aRv.ThrowRangeError("maxFramerate must be non-negative");
   1296        return;
   1297      }
   1298    }
   1299  }
   1300 }
   1301 
   1302 void RTCRtpSender::GetParameters(RTCRtpSendParameters& aParameters) {
   1303  MOZ_ASSERT(mParameters.mEncodings.Length());
   1304  // If sender.[[LastReturnedParameters]] is not null, return
   1305  // sender.[[LastReturnedParameters]], and abort these steps.
   1306  if (mLastReturnedParameters.isSome()) {
   1307    aParameters = *mLastReturnedParameters;
   1308    return;
   1309  }
   1310 
   1311  // Let result be a new RTCRtpSendParameters dictionary constructed as follows:
   1312 
   1313  // transactionId is set to a new unique identifier
   1314  aParameters.mTransactionId.Construct(mPc->GenerateUUID());
   1315 
   1316  // encodings is set to the value of the [[SendEncodings]] internal slot.
   1317  aParameters.mEncodings = mParameters.mEncodings;
   1318 
   1319  // The headerExtensions sequence is populated based on the header extensions
   1320  // that have been negotiated for sending
   1321  // TODO(bug 1765851): We do not support this yet
   1322  // aParameters.mHeaderExtensions.Construct();
   1323 
   1324  // rtcp.cname is set to the CNAME of the associated RTCPeerConnection.
   1325  // rtcp.reducedSize is set to true if reduced-size RTCP has been negotiated
   1326  // for sending, and false otherwise.
   1327  // TODO(bug 1765852): We do not support this yet
   1328  aParameters.mRtcp.Construct();
   1329  aParameters.mRtcp.Value().mCname.Construct();
   1330  aParameters.mRtcp.Value().mReducedSize.Construct(false);
   1331  if (mParameters.mDegradationPreference.WasPassed()) {
   1332    aParameters.mDegradationPreference.Construct(
   1333        mParameters.mDegradationPreference.Value());
   1334  }
   1335  aParameters.mHeaderExtensions.Construct();
   1336  if (mParameters.mCodecs.WasPassed()) {
   1337    aParameters.mCodecs.Construct(mParameters.mCodecs.Value());
   1338  }
   1339 
   1340  // Set sender.[[LastReturnedParameters]] to result.
   1341  mLastReturnedParameters = Some(aParameters);
   1342 
   1343  // Used to help with warning strings
   1344  mLastTransactionId = Some(aParameters.mTransactionId.Value());
   1345 
   1346  // Queue a task that sets sender.[[LastReturnedParameters]] to null.
   1347  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   1348      __func__, [this, self = RefPtr<RTCRtpSender>(this)] {
   1349        mLastReturnedParameters = Nothing();
   1350      }));
   1351 }
   1352 
   1353 bool operator==(const RTCRtpEncodingParameters& a1,
   1354                const RTCRtpEncodingParameters& a2) {
   1355  // webidl does not generate types that are equality comparable
   1356  return a1.mActive == a2.mActive && a1.mMaxBitrate == a2.mMaxBitrate &&
   1357         a1.mMaxFramerate == a2.mMaxFramerate && a1.mPriority == a2.mPriority &&
   1358         a1.mRid == a2.mRid &&
   1359         a1.mScaleResolutionDownBy == a2.mScaleResolutionDownBy;
   1360 }
   1361 
   1362 // static
   1363 void RTCRtpSender::ApplyJsEncodingToConduitEncoding(
   1364    const RTCRtpEncodingParameters& aJsEncoding,
   1365    VideoCodecConfig::Encoding* aConduitEncoding) {
   1366  aConduitEncoding->active = aJsEncoding.mActive;
   1367  if (aJsEncoding.mMaxBitrate.WasPassed()) {
   1368    aConduitEncoding->constraints.maxBr = aJsEncoding.mMaxBitrate.Value();
   1369  }
   1370  if (aJsEncoding.mMaxFramerate.WasPassed()) {
   1371    aConduitEncoding->constraints.maxFps =
   1372        Some(aJsEncoding.mMaxFramerate.Value());
   1373  }
   1374  if (aJsEncoding.mScaleResolutionDownBy.WasPassed()) {
   1375    // Optional does not have a valueOr, despite being based on Maybe
   1376    // :(
   1377    aConduitEncoding->constraints.scaleDownBy =
   1378        aJsEncoding.mScaleResolutionDownBy.Value();
   1379  } else {
   1380    aConduitEncoding->constraints.scaleDownBy = 1.0f;
   1381  }
   1382 }
   1383 
   1384 void RTCRtpSender::UpdateRestorableEncodings(
   1385    const Sequence<RTCRtpEncodingParameters>& aEncodings) {
   1386  MOZ_ASSERT(aEncodings.Length());
   1387 
   1388  if (GetJsepTransceiver().mSendTrack.GetNegotiatedDetails()) {
   1389    // Once initial negotiation completes, we are no longer allowed to restore
   1390    // the unicast encoding.
   1391    mUnicastEncoding.reset();
   1392  } else if (mParameters.mEncodings.Length() == 1 &&
   1393             !mParameters.mEncodings[0].mRid.WasPassed()) {
   1394    // If we have not completed the initial negotiation, and we currently are
   1395    // ridless unicast, we need to save our unicast encoding in case a
   1396    // rollback occurs.
   1397    mUnicastEncoding = Some(mParameters.mEncodings[0]);
   1398  }
   1399 }
   1400 
   1401 Sequence<RTCRtpEncodingParameters> RTCRtpSender::ToSendEncodings(
   1402    const std::vector<std::string>& aRids) const {
   1403  MOZ_ASSERT(!aRids.empty());
   1404 
   1405  Sequence<RTCRtpEncodingParameters> result;
   1406  // If sendEncodings is given as input to this algorithm, and is non-empty,
   1407  // set the [[SendEncodings]] slot to sendEncodings.
   1408  for (const auto& rid : aRids) {
   1409    MOZ_ASSERT(!rid.empty());
   1410    RTCRtpEncodingParameters encoding;
   1411    encoding.mActive = true;
   1412    encoding.mRid.Construct(NS_ConvertUTF8toUTF16(rid.c_str()));
   1413    (void)result.AppendElement(encoding, fallible);
   1414  }
   1415 
   1416  // If sendEncodings is non-empty, set each encoding's scaleResolutionDownBy
   1417  // to 2^(length of sendEncodings - encoding index - 1).
   1418  if (mTransceiver->IsVideo()) {
   1419    double scale = 1.0f;
   1420    for (auto it = result.rbegin(); it != result.rend(); ++it) {
   1421      it->mScaleResolutionDownBy.Construct(scale);
   1422      scale *= 2;
   1423    }
   1424  }
   1425 
   1426  return result;
   1427 }
   1428 
   1429 void RTCRtpSender::MaybeGetJsepRids() {
   1430  MOZ_ASSERT(!mSimulcastEnvelopeSet);
   1431  MOZ_ASSERT(mParameters.mEncodings.Length());
   1432 
   1433  auto jsepRids = GetJsepTransceiver().mSendTrack.GetRids();
   1434  if (!jsepRids.empty()) {
   1435    UpdateRestorableEncodings(mParameters.mEncodings);
   1436    if (jsepRids.size() != 1 || !jsepRids[0].empty()) {
   1437      // JSEP is using at least one rid. Stomp our single ridless encoding
   1438      mParameters.mEncodings = ToSendEncodings(jsepRids);
   1439    }
   1440    mSimulcastEnvelopeSet = true;
   1441    mSimulcastEnvelopeSetByJSEP = true;
   1442  }
   1443 }
   1444 
   1445 Sequence<RTCRtpEncodingParameters> RTCRtpSender::GetMatchingEncodings(
   1446    const std::vector<std::string>& aRids) const {
   1447  Sequence<RTCRtpEncodingParameters> result;
   1448 
   1449  if (!aRids.empty() && !aRids[0].empty()) {
   1450    // Simulcast, or unicast with rid
   1451    for (const auto& encoding : mParameters.mEncodings) {
   1452      for (const auto& rid : aRids) {
   1453        auto utf16Rid = NS_ConvertUTF8toUTF16(rid.c_str());
   1454        if (!encoding.mRid.WasPassed() || (utf16Rid == encoding.mRid.Value())) {
   1455          auto encodingCopy(encoding);
   1456          if (!encodingCopy.mRid.WasPassed()) {
   1457            encodingCopy.mRid.Construct(NS_ConvertUTF8toUTF16(rid.c_str()));
   1458          }
   1459          (void)result.AppendElement(encodingCopy, fallible);
   1460          break;
   1461        }
   1462      }
   1463    }
   1464  }
   1465 
   1466  // If we're allowing the old setParameters behavior, we _might_ be able to
   1467  // get into this situation even if there were rids above. Be extra careful.
   1468  // Under normal circumstances, this just handles the ridless case.
   1469  if (!result.Length()) {
   1470    // Unicast with no specified rid. Restore mUnicastEncoding, if
   1471    // it exists, otherwise pick the first encoding.
   1472    if (mUnicastEncoding.isSome()) {
   1473      (void)result.AppendElement(*mUnicastEncoding, fallible);
   1474    } else {
   1475      (void)result.AppendElement(mParameters.mEncodings[0], fallible);
   1476    }
   1477  }
   1478 
   1479  return result;
   1480 }
   1481 
   1482 void RTCRtpSender::SetStreams(
   1483    const Sequence<OwningNonNull<DOMMediaStream>>& aStreams, ErrorResult& aRv) {
   1484  if (mPc->IsClosed()) {
   1485    aRv.ThrowInvalidStateError(
   1486        "Cannot call setStreams if the peer connection is closed");
   1487    return;
   1488  }
   1489 
   1490  SetStreamsImpl(aStreams);
   1491  mPc->UpdateNegotiationNeeded();
   1492 }
   1493 
   1494 void RTCRtpSender::SetStreamsImpl(
   1495    const Sequence<OwningNonNull<DOMMediaStream>>& aStreams) {
   1496  mStreams.Clear();
   1497  std::set<nsString> ids;
   1498  for (const auto& stream : aStreams) {
   1499    nsString id;
   1500    stream->GetId(id);
   1501    if (!ids.count(id)) {
   1502      ids.insert(id);
   1503      mStreams.AppendElement(stream);
   1504    }
   1505  }
   1506 }
   1507 
   1508 void RTCRtpSender::GetStreams(nsTArray<RefPtr<DOMMediaStream>>& aStreams) {
   1509  aStreams = mStreams.Clone();
   1510 }
   1511 
   1512 class ReplaceTrackOperation final : public PeerConnectionImpl::Operation {
   1513 public:
   1514  ReplaceTrackOperation(PeerConnectionImpl* aPc,
   1515                        const RefPtr<RTCRtpTransceiver>& aTransceiver,
   1516                        const RefPtr<MediaStreamTrack>& aTrack,
   1517                        ErrorResult& aError);
   1518  NS_DECL_ISUPPORTS_INHERITED
   1519  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ReplaceTrackOperation,
   1520                                           PeerConnectionImpl::Operation)
   1521 
   1522 private:
   1523  MOZ_CAN_RUN_SCRIPT
   1524  RefPtr<dom::Promise> CallImpl(ErrorResult& aError) override;
   1525  ~ReplaceTrackOperation() = default;
   1526  RefPtr<RTCRtpTransceiver> mTransceiver;
   1527  RefPtr<MediaStreamTrack> mNewTrack;
   1528 };
   1529 
   1530 NS_IMPL_CYCLE_COLLECTION_INHERITED(ReplaceTrackOperation,
   1531                                   PeerConnectionImpl::Operation, mTransceiver,
   1532                                   mNewTrack)
   1533 
   1534 NS_IMPL_ADDREF_INHERITED(ReplaceTrackOperation, PeerConnectionImpl::Operation)
   1535 NS_IMPL_RELEASE_INHERITED(ReplaceTrackOperation, PeerConnectionImpl::Operation)
   1536 
   1537 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReplaceTrackOperation)
   1538 NS_INTERFACE_MAP_END_INHERITING(PeerConnectionImpl::Operation)
   1539 
   1540 ReplaceTrackOperation::ReplaceTrackOperation(
   1541    PeerConnectionImpl* aPc, const RefPtr<RTCRtpTransceiver>& aTransceiver,
   1542    const RefPtr<MediaStreamTrack>& aTrack, ErrorResult& aError)
   1543    : PeerConnectionImpl::Operation(aPc, aError),
   1544      mTransceiver(aTransceiver),
   1545      mNewTrack(aTrack) {}
   1546 
   1547 RefPtr<dom::Promise> ReplaceTrackOperation::CallImpl(ErrorResult& aError) {
   1548  RefPtr<RTCRtpSender> sender = mTransceiver->Sender();
   1549  // If transceiver.[[Stopping]] is true, return a promise rejected with a
   1550  // newly created InvalidStateError.
   1551  if (mTransceiver->Stopped() || mTransceiver->Stopping()) {
   1552    RefPtr<dom::Promise> error = sender->MakePromise(aError);
   1553    if (aError.Failed()) {
   1554      return nullptr;
   1555    }
   1556    MOZ_LOG(gSenderLog, LogLevel::Debug,
   1557            ("%s Cannot call replaceTrack when transceiver is stopping",
   1558             __FUNCTION__));
   1559    error->MaybeRejectWithInvalidStateError(
   1560        "Cannot call replaceTrack when transceiver is stopping");
   1561    return error;
   1562  }
   1563 
   1564  // Let p be a new promise.
   1565  RefPtr<dom::Promise> p = sender->MakePromise(aError);
   1566  if (aError.Failed()) {
   1567    return nullptr;
   1568  }
   1569 
   1570  if (!sender->SeamlessTrackSwitch(mNewTrack)) {
   1571    MOZ_LOG(gSenderLog, LogLevel::Info,
   1572            ("%s Could not seamlessly replace track", __FUNCTION__));
   1573    p->MaybeRejectWithInvalidModificationError(
   1574        "Could not seamlessly replace track");
   1575    return p;
   1576  }
   1577 
   1578  // Queue a task that runs the following steps:
   1579  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   1580      __func__, [p, sender, track = mNewTrack]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
   1581        // If connection.[[IsClosed]] is true, abort these steps.
   1582        // Set sender.[[SenderTrack]] to withTrack.
   1583        if (sender->SetSenderTrackWithClosedCheck(track)) {
   1584          // Resolve p with undefined.
   1585          p->MaybeResolveWithUndefined();
   1586        }
   1587      }));
   1588 
   1589  // Return p.
   1590  return p;
   1591 }
   1592 
   1593 already_AddRefed<dom::Promise> RTCRtpSender::ReplaceTrack(
   1594    dom::MediaStreamTrack* aWithTrack, ErrorResult& aError) {
   1595  // If withTrack is non-null and withTrack.kind differs from the transceiver
   1596  // kind of transceiver, return a promise rejected with a newly created
   1597  // TypeError.
   1598  if (aWithTrack) {
   1599    nsString newKind;
   1600    aWithTrack->GetKind(newKind);
   1601    nsString oldKind;
   1602    mTransceiver->GetKind(oldKind);
   1603    if (newKind != oldKind) {
   1604      RefPtr<dom::Promise> error = MakePromise(aError);
   1605      if (aError.Failed()) {
   1606        return nullptr;
   1607      }
   1608      error->MaybeRejectWithTypeError(
   1609          "Cannot replaceTrack with a different kind!");
   1610      return error.forget();
   1611    }
   1612  }
   1613 
   1614  MOZ_LOG(gSenderLog, LogLevel::Debug,
   1615          ("%s[%s]: %s (%p to %p)", mPc->GetHandle().c_str(), GetMid().c_str(),
   1616           __FUNCTION__, mSenderTrack.get(), aWithTrack));
   1617 
   1618  // Return the result of chaining the following steps to connection's
   1619  // operations chain:
   1620  RefPtr<PeerConnectionImpl::Operation> op =
   1621      new ReplaceTrackOperation(mPc, mTransceiver, aWithTrack, aError);
   1622  if (aError.Failed()) {
   1623    return nullptr;
   1624  }
   1625  // Static analysis forces us to use a temporary.
   1626  auto pc = mPc;
   1627  return pc->Chain(op, aError);
   1628 }
   1629 
   1630 nsPIDOMWindowInner* RTCRtpSender::GetParentObject() const { return mWindow; }
   1631 
   1632 already_AddRefed<dom::Promise> RTCRtpSender::MakePromise(
   1633    ErrorResult& aError) const {
   1634  return mPc->MakePromise(aError);
   1635 }
   1636 
   1637 bool RTCRtpSender::SeamlessTrackSwitch(
   1638    const RefPtr<MediaStreamTrack>& aWithTrack) {
   1639  // We do not actually update mSenderTrack here! Spec says that happens in a
   1640  // queued task after this is done (this happens in
   1641  // SetSenderTrackWithClosedCheck).
   1642 
   1643  mPipeline->SetTrack(aWithTrack);
   1644 
   1645  MaybeUpdateConduit();
   1646 
   1647  // There may eventually be cases where a renegotiation is necessary to switch.
   1648  return true;
   1649 }
   1650 
   1651 void RTCRtpSender::SetTrack(const RefPtr<MediaStreamTrack>& aTrack) {
   1652  // Used for RTCPeerConnection.removeTrack and RTCPeerConnection.addTrack
   1653  if (mTransceiver->Stopping()) {
   1654    return;
   1655  }
   1656  mSenderTrack = aTrack;
   1657  SeamlessTrackSwitch(aTrack);
   1658  if (aTrack) {
   1659    // RFC says (in the section on remote rollback):
   1660    // However, an RtpTransceiver MUST NOT be removed if a track was attached
   1661    // to the RtpTransceiver via the addTrack method.
   1662    mSenderTrackSetByAddTrack = true;
   1663  }
   1664 }
   1665 
   1666 bool RTCRtpSender::SetSenderTrackWithClosedCheck(
   1667    const RefPtr<MediaStreamTrack>& aTrack) {
   1668  if (!mPc->IsClosed()) {
   1669    mSenderTrack = aTrack;
   1670    return true;
   1671  }
   1672 
   1673  return false;
   1674 }
   1675 
   1676 void RTCRtpSender::Shutdown() {
   1677  MOZ_ASSERT(NS_IsMainThread());
   1678  mWatchManager.Shutdown();
   1679  mPipeline->Shutdown();
   1680  mPipeline = nullptr;
   1681  if (mTransform) {
   1682    mTransform->GetProxy().SetSender(nullptr);
   1683  }
   1684 }
   1685 
   1686 void RTCRtpSender::BreakCycles() {
   1687  mWindow = nullptr;
   1688  mPc = nullptr;
   1689  mSenderTrack = nullptr;
   1690  mTransceiver = nullptr;
   1691  mStreams.Clear();
   1692  mDtmf = nullptr;
   1693 }
   1694 
   1695 void RTCRtpSender::Unlink() {
   1696  if (mTransceiver) {
   1697    mTransceiver->Unlink();
   1698  }
   1699 }
   1700 
   1701 void RTCRtpSender::UpdateTransport() {
   1702  MOZ_ASSERT(NS_IsMainThread());
   1703  if (!mHaveSetupTransport) {
   1704    mPipeline->SetLevel(GetJsepTransceiver().GetLevel());
   1705    mHaveSetupTransport = true;
   1706  }
   1707 
   1708  mPipeline->UpdateTransport_m(GetJsepTransceiver().mTransport.mTransportId,
   1709                               nullptr, true);
   1710 }
   1711 
   1712 void RTCRtpSender::MaybeUpdateConduit() {
   1713  // NOTE(pkerr) - the Call API requires the both local_ssrc and remote_ssrc be
   1714  // set to a non-zero value or the CreateVideo...Stream call will fail.
   1715  if (NS_WARN_IF(GetJsepTransceiver().mSendTrack.GetSsrcs().empty())) {
   1716    MOZ_ASSERT(
   1717        false,
   1718        "No local ssrcs! This is a bug in the jsep engine, and should never "
   1719        "happen!");
   1720    return;
   1721  }
   1722 
   1723  if (!mPipeline) {
   1724    return;
   1725  }
   1726 
   1727  bool wasTransmitting = mTransmitting;
   1728 
   1729  if (mPipeline->mConduit->type() == MediaSessionConduit::VIDEO) {
   1730    Maybe<VideoConfig> newConfig = GetNewVideoConfig();
   1731    if (newConfig.isSome()) {
   1732      ApplyVideoConfig(*newConfig);
   1733    }
   1734  } else {
   1735    Maybe<AudioConfig> newConfig = GetNewAudioConfig();
   1736    if (newConfig.isSome()) {
   1737      ApplyAudioConfig(*newConfig);
   1738    }
   1739  }
   1740 
   1741  if (!mSenderTrack && !wasTransmitting && mTransmitting) {
   1742    MOZ_LOG(gSenderLog, LogLevel::Debug,
   1743            ("%s[%s]: %s Starting transmit conduit without send track!",
   1744             mPc->GetHandle().c_str(), GetMid().c_str(), __FUNCTION__));
   1745  }
   1746 }
   1747 
   1748 void RTCRtpSender::UpdateParametersCodecs() {
   1749  mParameters.mCodecs.Reset();
   1750  mParameters.mCodecs.Construct();
   1751 
   1752  if (GetJsepTransceiver().mSendTrack.GetNegotiatedDetails()) {
   1753    const JsepTrackNegotiatedDetails details(
   1754        *GetJsepTransceiver().mSendTrack.GetNegotiatedDetails());
   1755    if (details.GetEncodingCount()) {
   1756      for (const auto& jsepCodec : details.GetEncoding(0).GetCodecs()) {
   1757        if (!jsepCodec->mEnabled ||
   1758            !jsepCodec->DirectionSupported(sdp::kSend)) {
   1759          // This codec is disabled or sending it is unsupported.
   1760          continue;
   1761        }
   1762        RTCRtpCodecParameters codec;
   1763        RTCRtpTransceiver::ToDomRtpCodecParameters(*jsepCodec, &codec);
   1764        (void)mParameters.mCodecs.Value().AppendElement(codec, fallible);
   1765        if (jsepCodec->Type() == SdpMediaSection::kVideo) {
   1766          const JsepVideoCodecDescription& videoJsepCodec =
   1767              static_cast<JsepVideoCodecDescription&>(*jsepCodec);
   1768          // In our JSEP implementation, RTX is an addon to an existing codec,
   1769          // not a codec object in its own right. webrtc-pc treats RTX as a
   1770          // separate codec, however.
   1771          if (videoJsepCodec.mRtxEnabled) {
   1772            RTCRtpCodecParameters rtx;
   1773            RTCRtpTransceiver::ToDomRtpCodecParametersRtx(videoJsepCodec, &rtx);
   1774            (void)mParameters.mCodecs.Value().AppendElement(rtx, fallible);
   1775          }
   1776        }
   1777      }
   1778    }
   1779 
   1780    // Check to see if we have a codec that is not in the codecs list.
   1781    const auto& hasCodecMatch =
   1782        [&](const RTCRtpEncodingParameters& param) -> bool {
   1783      if (mParameters.mCodecs.WasPassed()) {
   1784        for (const auto& codec : mParameters.mCodecs.Value()) {
   1785          if (DoesCodecParameterMatchCodec(param.mCodec.Value(), codec, true)) {
   1786            return true;
   1787          }
   1788        }
   1789      }
   1790      return false;
   1791    };
   1792    // If we have a codec that is not in the codecs list, remove it from the
   1793    // encoding.
   1794    for (auto& encoding : mParameters.mEncodings) {
   1795      if (encoding.mCodec.WasPassed() && !hasCodecMatch(encoding)) {
   1796        encoding.mCodec.Reset();
   1797      }
   1798    }
   1799  }
   1800 }
   1801 
   1802 void RTCRtpSender::SyncFromJsep(const JsepTransceiver& aJsepTransceiver) {
   1803  if (!mSimulcastEnvelopeSet) {
   1804    // JSEP is establishing the simulcast envelope for the first time, right now
   1805    // This is the addTrack (or addTransceiver without sendEncodings) case.
   1806    MaybeGetJsepRids();
   1807  } else if (!aJsepTransceiver.mSendTrack.GetNegotiatedDetails() ||
   1808             !aJsepTransceiver.mSendTrack.IsInHaveRemote()) {
   1809    // Spec says that we do not update our encodings until we're in stable,
   1810    // _unless_ this is the first negotiation.
   1811    std::vector<std::string> rids = aJsepTransceiver.mSendTrack.GetRids();
   1812    if (mSimulcastEnvelopeSetByJSEP && rids.empty()) {
   1813      // JSEP previously set the simulcast envelope, but now it has no opinion
   1814      // regarding unicast/simulcast. This can only happen on rollback of the
   1815      // initial remote offer.
   1816      mParameters.mEncodings = GetMatchingEncodings(rids);
   1817      MOZ_ASSERT(mParameters.mEncodings.Length());
   1818      mSimulcastEnvelopeSetByJSEP = false;
   1819      mSimulcastEnvelopeSet = false;
   1820    } else if (!rids.empty()) {
   1821      // JSEP has an opinion on the simulcast envelope, which trumps anything
   1822      // we have already.
   1823      mParameters.mEncodings = GetMatchingEncodings(rids);
   1824      MOZ_ASSERT(mParameters.mEncodings.Length());
   1825    }
   1826  }
   1827  UpdateParametersCodecs();
   1828 
   1829  MaybeUpdateConduit();
   1830 }
   1831 
   1832 void RTCRtpSender::SyncToJsep(JsepTransceiver& aJsepTransceiver) const {
   1833  std::vector<std::string> streamIds;
   1834  for (const auto& stream : mStreams) {
   1835    nsString wideStreamId;
   1836    stream->GetId(wideStreamId);
   1837    std::string streamId = NS_ConvertUTF16toUTF8(wideStreamId).get();
   1838    MOZ_ASSERT(!streamId.empty());
   1839    streamIds.push_back(streamId);
   1840  }
   1841 
   1842  aJsepTransceiver.mSendTrack.UpdateStreamIds(streamIds);
   1843 
   1844  if (mSimulcastEnvelopeSet) {
   1845    std::vector<std::string> rids;
   1846    Maybe<RTCRtpSendParameters> parameters;
   1847    if (mPendingRidChangeFromCompatMode) {
   1848      // *sigh* If we have just let a setParameters change our rids, but we have
   1849      // not yet updated mParameters because the queued task hasn't run yet,
   1850      // we want to set the _new_ rids on the JsepTrack. So, we are forced to
   1851      // grab them from mPendingParameters.
   1852      parameters = mPendingParameters;
   1853    } else {
   1854      parameters = Some(mParameters);
   1855    }
   1856    for (const auto& encoding : parameters->mEncodings) {
   1857      if (encoding.mRid.WasPassed()) {
   1858        rids.push_back(NS_ConvertUTF16toUTF8(encoding.mRid.Value()).get());
   1859      } else {
   1860        rids.push_back("");
   1861      }
   1862    }
   1863 
   1864    aJsepTransceiver.mSendTrack.SetRids(rids);
   1865  }
   1866 
   1867  if (mTransceiver->IsVideo()) {
   1868    aJsepTransceiver.mSendTrack.SetMaxEncodings(webrtc::kMaxSimulcastStreams);
   1869  } else {
   1870    aJsepTransceiver.mSendTrack.SetMaxEncodings(1);
   1871  }
   1872 
   1873  if (mSenderTrackSetByAddTrack) {
   1874    aJsepTransceiver.SetOnlyExistsBecauseOfSetRemote(false);
   1875  }
   1876 }
   1877 
   1878 template <typename CodecConfigT>
   1879 CodecConfigT& findMatchingCodec(
   1880    std::vector<CodecConfigT>& aCodecs,
   1881    Maybe<const RTCRtpEncodingParameters&> aParameters) {
   1882  MOZ_ASSERT(aCodecs.size());
   1883 
   1884  if (!aParameters || !aParameters->mCodec.WasPassed()) {
   1885    return aCodecs[0];
   1886  }
   1887 
   1888  for (auto& codec : aCodecs) {
   1889    if (aParameters->mCodec.Value().mMimeType.EqualsIgnoreCase(
   1890            codec.MimeType())) {
   1891      return codec;
   1892    }
   1893  }
   1894 
   1895  return aCodecs[0];
   1896 }
   1897 
   1898 Maybe<RTCRtpSender::VideoConfig> RTCRtpSender::GetNewVideoConfig() {
   1899  // It is possible for SDP to signal that there is a send track, but there not
   1900  // actually be a send track, according to the specification; all that needs to
   1901  // happen is for the transceiver to be configured to send...
   1902  if (!GetJsepTransceiver().mSendTrack.GetNegotiatedDetails()) {
   1903    return Nothing();
   1904  }
   1905 
   1906  VideoConfig oldConfig;
   1907  oldConfig.mSsrcs = mSsrcs;
   1908  oldConfig.mLocalRtpExtensions = mLocalRtpExtensions;
   1909  oldConfig.mCname = mCname;
   1910  oldConfig.mTransmitting = mTransmitting;
   1911  oldConfig.mVideoRtxSsrcs = mVideoRtxSsrcs;
   1912  oldConfig.mVideoCodec = mVideoCodec;
   1913  oldConfig.mVideoRtpRtcpConfig = mVideoRtpRtcpConfig;
   1914  oldConfig.mVideoCodecMode = mVideoCodecMode;
   1915 
   1916  VideoConfig newConfig(oldConfig);
   1917 
   1918  UpdateBaseConfig(&newConfig);
   1919 
   1920  newConfig.mVideoRtxSsrcs = GetJsepTransceiver().mSendTrack.GetRtxSsrcs();
   1921 
   1922  const JsepTrackNegotiatedDetails details(
   1923      *GetJsepTransceiver().mSendTrack.GetNegotiatedDetails());
   1924 
   1925  if (mSenderTrack) {
   1926    RefPtr<mozilla::dom::VideoStreamTrack> videotrack =
   1927        mSenderTrack->AsVideoStreamTrack();
   1928 
   1929    if (!videotrack) {
   1930      MOZ_CRASH(
   1931          "In ConfigureVideoCodecMode, mSenderTrack is not video! This should "
   1932          "never happen!");
   1933    }
   1934 
   1935    dom::MediaSourceEnum source = videotrack->GetSource().GetMediaSource();
   1936    switch (source) {
   1937      case dom::MediaSourceEnum::Browser:
   1938      case dom::MediaSourceEnum::Screen:
   1939      case dom::MediaSourceEnum::Window:
   1940      case dom::MediaSourceEnum::Application:
   1941        newConfig.mVideoCodecMode = webrtc::VideoCodecMode::kScreensharing;
   1942        break;
   1943 
   1944      case dom::MediaSourceEnum::Camera:
   1945      case dom::MediaSourceEnum::Other:
   1946        // Other is used by canvas capture, which we treat as realtime video.
   1947        // This seems debatable, but we've been doing it this way for a long
   1948        // time, so this is likely fine.
   1949        newConfig.mVideoCodecMode = webrtc::VideoCodecMode::kRealtimeVideo;
   1950        break;
   1951 
   1952      case dom::MediaSourceEnum::Microphone:
   1953      case dom::MediaSourceEnum::AudioCapture:
   1954        MOZ_ASSERT(false);
   1955        break;
   1956    }
   1957  }
   1958 
   1959  std::vector<VideoCodecConfig> configs;
   1960  RTCRtpTransceiver::NegotiatedDetailsToVideoCodecConfigs(details, &configs);
   1961 
   1962  if (configs.empty()) {
   1963    // TODO: Are we supposed to plumb this error back to JS? This does not
   1964    // seem like a failure to set an answer, it just means that codec
   1965    // negotiation failed. For now, we're just doing the same thing we do
   1966    // if negotiation as a whole failed.
   1967    MOZ_LOG(gSenderLog, LogLevel::Error,
   1968            ("%s[%s]: %s  No video codecs were negotiated (send).",
   1969             mPc->GetHandle().c_str(), GetMid().c_str(), __FUNCTION__));
   1970    return Nothing();
   1971  }
   1972 
   1973  // Spec says that we start using new parameters right away, _before_ we
   1974  // update the parameters that are visible to JS (ie; mParameters).
   1975  const auto& parameters = mPendingParameters.refOr(mParameters);
   1976  const auto& encodings = parameters.mEncodings;
   1977  newConfig.mVideoCodec = Some(findMatchingCodec(
   1978      configs, encodings.IsEmpty() ? Nothing() : SomeRef(encodings[0])));
   1979  for (VideoCodecConfig::Encoding& conduitEncoding :
   1980       newConfig.mVideoCodec->mEncodings) {
   1981    for (const RTCRtpEncodingParameters& jsEncoding : parameters.mEncodings) {
   1982      std::string rid;
   1983      if (jsEncoding.mRid.WasPassed()) {
   1984        rid = NS_ConvertUTF16toUTF8(jsEncoding.mRid.Value()).get();
   1985      }
   1986      if (conduitEncoding.rid == rid) {
   1987        ApplyJsEncodingToConduitEncoding(jsEncoding, &conduitEncoding);
   1988        break;
   1989      }
   1990    }
   1991  }
   1992 
   1993  if (!mHaveLoggedUlpfecInfo) {
   1994    bool ulpfecNegotiated = false;
   1995    for (const auto& codec : configs) {
   1996      if (nsCRT::strcasestr(codec.mName.c_str(), "ulpfec")) {
   1997        ulpfecNegotiated = true;
   1998      }
   1999    }
   2000    mozilla::glean::codec_stats::ulpfec_negotiated
   2001        .Get(ulpfecNegotiated ? "negotiated"_ns : "not_negotiated"_ns)
   2002        .Add(1);
   2003    mHaveLoggedUlpfecInfo = true;
   2004  }
   2005 
   2006  // Log codec information we are tracking
   2007  if (!mHaveLoggedOtherFec &&
   2008      !GetJsepTransceiver().mSendTrack.GetFecCodecName().empty()) {
   2009    mozilla::glean::codec_stats::other_fec_signaled
   2010        .Get(nsDependentCString(
   2011            GetJsepTransceiver().mSendTrack.GetFecCodecName().c_str()))
   2012        .Add(1);
   2013    mHaveLoggedOtherFec = true;
   2014  }
   2015  if (!mHaveLoggedVideoPreferredCodec &&
   2016      !GetJsepTransceiver().mSendTrack.GetVideoPreferredCodec().empty()) {
   2017    mozilla::glean::codec_stats::video_preferred_codec
   2018        .Get(nsDependentCString(
   2019            GetJsepTransceiver().mSendTrack.GetVideoPreferredCodec().c_str()))
   2020        .Add(1);
   2021    mHaveLoggedVideoPreferredCodec = true;
   2022  }
   2023 
   2024  newConfig.mVideoRtpRtcpConfig = Some(details.GetRtpRtcpConfig());
   2025 
   2026  if (newConfig == oldConfig) {
   2027    MOZ_LOG(gSenderLog, LogLevel::Debug,
   2028            ("%s[%s]: %s  No change in video config", mPc->GetHandle().c_str(),
   2029             GetMid().c_str(), __FUNCTION__));
   2030    return Nothing();
   2031  }
   2032 
   2033  if (newConfig.mVideoCodec.isSome()) {
   2034    MOZ_ASSERT(newConfig.mSsrcs.size() ==
   2035               newConfig.mVideoCodec->mEncodings.size());
   2036  }
   2037  return Some(newConfig);
   2038 }
   2039 
   2040 Maybe<RTCRtpSender::AudioConfig> RTCRtpSender::GetNewAudioConfig() {
   2041  AudioConfig oldConfig;
   2042  oldConfig.mSsrcs = mSsrcs;
   2043  oldConfig.mLocalRtpExtensions = mLocalRtpExtensions;
   2044  oldConfig.mCname = mCname;
   2045  oldConfig.mTransmitting = mTransmitting;
   2046  oldConfig.mAudioCodec = mAudioCodec;
   2047 
   2048  AudioConfig newConfig(oldConfig);
   2049 
   2050  UpdateBaseConfig(&newConfig);
   2051 
   2052  if (GetJsepTransceiver().mSendTrack.GetNegotiatedDetails() &&
   2053      GetJsepTransceiver().mSendTrack.GetActive()) {
   2054    const auto& details(
   2055        *GetJsepTransceiver().mSendTrack.GetNegotiatedDetails());
   2056 
   2057    std::vector<AudioCodecConfig> configs;
   2058    RTCRtpTransceiver::NegotiatedDetailsToAudioCodecConfigs(details, &configs);
   2059    if (configs.empty()) {
   2060      // TODO: Are we supposed to plumb this error back to JS? This does not
   2061      // seem like a failure to set an answer, it just means that codec
   2062      // negotiation failed. For now, we're just doing the same thing we do
   2063      // if negotiation as a whole failed.
   2064      MOZ_LOG(gSenderLog, LogLevel::Error,
   2065              ("%s[%s]: %s No audio codecs were negotiated (send)",
   2066               mPc->GetHandle().c_str(), GetMid().c_str(), __FUNCTION__));
   2067      return Nothing();
   2068    }
   2069 
   2070    const auto& parameters = mPendingParameters.refOr(mParameters);
   2071    const auto& encodings = parameters.mEncodings;
   2072    auto encoding = encodings.IsEmpty() ? Nothing() : SomeRef(encodings[0]);
   2073    AudioCodecConfig& sendCodec = findMatchingCodec(configs, encoding);
   2074 
   2075    if (encoding) {
   2076      if (encoding->mMaxBitrate.WasPassed()) {
   2077        sendCodec.mEncodingConstraints.maxBitrateBps.emplace(
   2078            encoding->mMaxBitrate.Value());
   2079      }
   2080    }
   2081 
   2082    std::vector<AudioCodecConfig> dtmfConfigs;
   2083    std::copy_if(
   2084        configs.begin(), configs.end(), std::back_inserter(dtmfConfigs),
   2085        [](const auto& value) { return value.mName == "telephone-event"; });
   2086    if (!dtmfConfigs.empty()) {
   2087      // There is at least one telephone-event codec.
   2088      // We primarily choose the codec whose frequency matches the send codec.
   2089      // Secondarily we choose the one with the lowest frequency.
   2090      auto dtmfIterator =
   2091          std::find_if(dtmfConfigs.begin(), dtmfConfigs.end(),
   2092                       [&sendCodec](const auto& dtmfCodec) {
   2093                         return dtmfCodec.mFreq == sendCodec.mFreq;
   2094                       });
   2095      if (dtmfIterator == dtmfConfigs.end()) {
   2096        dtmfIterator = std::min_element(
   2097            dtmfConfigs.begin(), dtmfConfigs.end(),
   2098            [](const auto& a, const auto& b) { return a.mFreq < b.mFreq; });
   2099      }
   2100      MOZ_ASSERT(dtmfIterator != dtmfConfigs.end());
   2101      newConfig.mDtmfPt = dtmfIterator->mType;
   2102      newConfig.mDtmfFreq = dtmfIterator->mFreq;
   2103    }
   2104 
   2105    newConfig.mAudioCodec = Some(sendCodec);
   2106  }
   2107 
   2108  if (!mHaveLoggedAudioPreferredCodec &&
   2109      !GetJsepTransceiver().mSendTrack.GetAudioPreferredCodec().empty()) {
   2110    mozilla::glean::codec_stats::audio_preferred_codec
   2111        .Get(nsDependentCString(
   2112            GetJsepTransceiver().mSendTrack.GetAudioPreferredCodec().c_str()))
   2113        .Add(1);
   2114    mHaveLoggedAudioPreferredCodec = true;
   2115  }
   2116 
   2117  if (newConfig == oldConfig) {
   2118    MOZ_LOG(gSenderLog, LogLevel::Debug,
   2119            ("%s[%s]: %s  No change in audio config", mPc->GetHandle().c_str(),
   2120             GetMid().c_str(), __FUNCTION__));
   2121    return Nothing();
   2122  }
   2123 
   2124  return Some(newConfig);
   2125 }
   2126 
   2127 void RTCRtpSender::UpdateBaseConfig(BaseConfig* aConfig) {
   2128  aConfig->mSsrcs = GetJsepTransceiver().mSendTrack.GetSsrcs();
   2129  aConfig->mCname = GetJsepTransceiver().mSendTrack.GetCNAME();
   2130 
   2131  if (GetJsepTransceiver().mSendTrack.GetNegotiatedDetails() &&
   2132      GetJsepTransceiver().mSendTrack.GetActive()) {
   2133    const auto& details(
   2134        *GetJsepTransceiver().mSendTrack.GetNegotiatedDetails());
   2135    {
   2136      std::vector<webrtc::RtpExtension> extmaps;
   2137      // @@NG read extmap from track
   2138      details.ForEachRTPHeaderExtension(
   2139          [&extmaps](const SdpExtmapAttributeList::Extmap& extmap) {
   2140            extmaps.emplace_back(extmap.extensionname, extmap.entry);
   2141          });
   2142      aConfig->mLocalRtpExtensions = extmaps;
   2143    }
   2144  }
   2145  // RTCRtpTransceiver::IsSending is updated after negotiation completes, in a
   2146  // queued task (which we may be in right now). Don't use
   2147  // JsepTrack::GetActive, because that updates before the queued task, which
   2148  // is too early for some of the things we interact with here (eg;
   2149  // RTCDTMFSender).
   2150  aConfig->mTransmitting = mTransceiver->IsSending();
   2151 }
   2152 
   2153 void RTCRtpSender::ApplyVideoConfig(const VideoConfig& aConfig) {
   2154  if (aConfig.mVideoCodec.isSome()) {
   2155    MOZ_ASSERT(aConfig.mSsrcs.size() == aConfig.mVideoCodec->mEncodings.size());
   2156  }
   2157 
   2158  mSsrcs = aConfig.mSsrcs;
   2159  mCname = aConfig.mCname;
   2160  mLocalRtpExtensions = aConfig.mLocalRtpExtensions;
   2161 
   2162  mVideoRtxSsrcs = aConfig.mVideoRtxSsrcs;
   2163  mVideoCodec = aConfig.mVideoCodec;
   2164  mVideoRtpRtcpConfig = aConfig.mVideoRtpRtcpConfig;
   2165  mVideoCodecMode = aConfig.mVideoCodecMode;
   2166 
   2167  mTransmitting = aConfig.mTransmitting;
   2168 }
   2169 
   2170 void RTCRtpSender::ApplyAudioConfig(const AudioConfig& aConfig) {
   2171  mSsrcs = aConfig.mSsrcs;
   2172  mCname = aConfig.mCname;
   2173  mLocalRtpExtensions = aConfig.mLocalRtpExtensions;
   2174 
   2175  mAudioCodec = aConfig.mAudioCodec;
   2176 
   2177  if (aConfig.mDtmfPt >= 0) {
   2178    mDtmf->SetPayloadType(aConfig.mDtmfPt, aConfig.mDtmfFreq);
   2179  }
   2180 
   2181  mTransmitting = aConfig.mTransmitting;
   2182 }
   2183 
   2184 void RTCRtpSender::Stop() {
   2185  MOZ_ASSERT(mTransceiver->Stopping());
   2186  mTransmitting = false;
   2187 }
   2188 
   2189 bool RTCRtpSender::HasTrack(const dom::MediaStreamTrack* aTrack) const {
   2190  if (!mSenderTrack) {
   2191    return false;
   2192  }
   2193 
   2194  if (!aTrack) {
   2195    return true;
   2196  }
   2197 
   2198  return mSenderTrack.get() == aTrack;
   2199 }
   2200 
   2201 RefPtr<MediaPipelineTransmit> RTCRtpSender::GetPipeline() const {
   2202  return mPipeline;
   2203 }
   2204 
   2205 std::string RTCRtpSender::GetMid() const { return mTransceiver->GetMidAscii(); }
   2206 
   2207 JsepTransceiver& RTCRtpSender::GetJsepTransceiver() {
   2208  return mTransceiver->GetJsepTransceiver();
   2209 }
   2210 
   2211 void RTCRtpSender::UpdateDtmfSender() {
   2212  if (!mDtmf) {
   2213    return;
   2214  }
   2215 
   2216  if (mTransmitting) {
   2217    return;
   2218  }
   2219 
   2220  mDtmf->StopPlayout();
   2221 }
   2222 
   2223 void RTCRtpSender::SetTransform(RTCRtpScriptTransform* aTransform,
   2224                                ErrorResult& aError) {
   2225  if (aTransform == mTransform.get()) {
   2226    // Ok... smile and nod
   2227    // TODO: Depending on spec, this might throw
   2228    // https://github.com/w3c/webrtc-encoded-transform/issues/189
   2229    return;
   2230  }
   2231 
   2232  if (aTransform && aTransform->IsClaimed()) {
   2233    aError.ThrowInvalidStateError("transform has already been used elsewhere");
   2234    return;
   2235  }
   2236 
   2237  // Seamless switch for frames
   2238  if (aTransform) {
   2239    mFrameTransformerProxy = &aTransform->GetProxy();
   2240  } else {
   2241    mFrameTransformerProxy = nullptr;
   2242  }
   2243 
   2244  if (mTransform) {
   2245    mTransform->GetProxy().SetSender(nullptr);
   2246  }
   2247 
   2248  mTransform = const_cast<RTCRtpScriptTransform*>(aTransform);
   2249 
   2250  if (mTransform) {
   2251    mTransform->GetProxy().SetSender(this);
   2252    mTransform->SetClaimed();
   2253  }
   2254 }
   2255 
   2256 bool RTCRtpSender::GenerateKeyFrame(const Maybe<std::string>& aRid) {
   2257  if (!mTransform || !mPipeline) {
   2258    return false;
   2259  }
   2260 
   2261  mPipeline->mConduit->AsVideoSessionConduit().apply([&](const auto& conduit) {
   2262    conduit->GenerateKeyFrame(aRid, &mTransform->GetProxy());
   2263  });
   2264  return true;
   2265 }
   2266 
   2267 }  // namespace mozilla::dom
   2268 
   2269 #undef LOGTAG