Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/webrtc/libwebrtcglue/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 74 kB image not shown  

Quelle  VideoConduit.cpp   Sprache: C

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "VideoConduit.h"

#include <algorithm>
#include <cmath>

#include "common/browser_logging/CSFLog.h"
#include "common/YuvStamper.h"
#include "MediaConduitControl.h"
#include "nsIGfxInfo.h"
#include "nsServiceManagerUtils.h"
#include "RtpRtcpConfig.h"
#include "transport/SrtpFlow.h"  // For SRTP_MAX_EXPANSION
#include "Tracing.h"
#include "VideoStreamFactory.h"
#include "WebrtcCallWrapper.h"
#include "libwebrtcglue/FrameTransformer.h"
#include "libwebrtcglue/FrameTransformerProxy.h"
#include "mozilla/StateMirroring.h"
#include "mozilla/RefPtr.h"
#include "nsThreadUtils.h"
#include "mozilla/Maybe.h"
#include "mozilla/ErrorResult.h"
#include <string>
#include <utility>
#include <vector>

// libwebrtc includes
#include "api/transport/bitrate_settings.h"
#include "api/video_codecs/h264_profile_level_id.h"
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_codec.h"
#include "media/base/media_constants.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/ref_counted_object.h"

#include "api/call/transport.h"
#include "api/media_types.h"
#include "api/rtp_headers.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/transport/rtp/rtp_source.h"
#include "api/video_codecs/video_encoder.h"
#include "api/video/video_codec_constants.h"
#include "api/video/video_codec_type.h"
#include "api/video/video_frame_buffer.h"
#include "api/video/video_sink_interface.h"
#include "api/video/video_source_interface.h"
#include <utility>
#include "call/call.h"
#include "call/rtp_config.h"
#include "call/video_receive_stream.h"
#include "call/video_send_stream.h"
#include "CodecConfig.h"
#include "common_video/include/video_frame_buffer_pool.h"
#include "domstubs.h"
#include <iomanip>
#include <ios>
#include "jsapi/RTCStatsReport.h"
#include <limits>
#include "MainThreadUtils.h"
#include <map>
#include "MediaConduitErrors.h"
#include "MediaConduitInterface.h"
#include "MediaEventSource.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/DataMutex.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/RTCStatsReportBinding.h"
#include "mozilla/fallible.h"
#include "mozilla/mozalloc_oom.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Mutex.h"
#include "mozilla/ProfilerState.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/ReverseIterator.h"
#include "mozilla/StateWatching.h"
#include "mozilla/glean/DomMediaWebrtcMetrics.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsIDirectTaskDispatcher.h"
#include "nsISerialEventTarget.h"
#include "nsStringFwd.h"
#include "PerformanceRecorder.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/network/sent_packet.h"
#include <sstream>
#include <stdint.h>
#include "transport/mediapacket.h"
#include "video/config/video_encoder_config.h"
#include "WebrtcVideoCodecFactory.h"

#ifdef MOZ_WIDGET_ANDROID
#  include "VideoEngine.h"
#endif

// for ntohs
#ifdef _MSC_VER
#  include "Winsock2.h"
#else
#  include <netinet/in.h>
#endif

#define INVALID_RTP_PAYLOAD 255  // valid payload types are 0 to 127

namespace mozilla {

namespace {

const char* vcLogTag = "WebrtcVideoSessionConduit";
#ifdef LOGTAG
#  undef LOGTAG
#endif
#define LOGTAG vcLogTag

using namespace cricket;
using LocalDirection = MediaSessionConduitLocalDirection;

const int kNullPayloadType = -1;
const char kRtcpFbCcmParamTmmbr[] = "tmmbr";

template <class t>
void ConstrainPreservingAspectRatioExact(uint32_t max_fs, t* width, t* height) {
  // We could try to pick a better starting divisor, but it won't make any real
  // performance difference.
  for (size_t d = 1; d < std::min(*width, *height); ++d) {
    if ((*width % d) || (*height % d)) {
      continue;  // Not divisible
    }

    if (((*width) * (*height)) / (d * d) <= max_fs) {
      *width /= d;
      *height /= d;
      return;
    }
  }

  *width = 0;
  *height = 0;
}

/**
 * Perform validation on the codecConfig to be applied
 */

MediaConduitErrorCode ValidateCodecConfig(const VideoCodecConfig& codecInfo) {
  if (codecInfo.mName.empty()) {
    CSFLogError(LOGTAG, "%s Empty Payload Name ", __FUNCTION__);
    return kMediaConduitMalformedArgument;
  }

  return kMediaConduitNoError;
}

webrtc::VideoCodecType SupportedCodecType(webrtc::VideoCodecType aType) {
  switch (aType) {
    case webrtc::VideoCodecType::kVideoCodecVP8:
    case webrtc::VideoCodecType::kVideoCodecVP9:
    case webrtc::VideoCodecType::kVideoCodecH264:
    case webrtc::VideoCodecType::kVideoCodecAV1:
      return aType;
    default:
      return webrtc::VideoCodecType::kVideoCodecGeneric;
  }
  // NOTREACHED
}

// Call thread only.
rtc::scoped_refptr<webrtc::VideoEncoderConfig::EncoderSpecificSettings>
ConfigureVideoEncoderSettings(const VideoCodecConfig& aConfig,
                              const WebrtcVideoConduit* aConduit,
                              webrtc::CodecParameterMap& aParameters) {
  bool is_screencast =
      aConduit->CodecMode() == webrtc::VideoCodecMode::kScreensharing;
  // No automatic resizing when using simulcast or screencast.
  bool automatic_resize = !is_screencast && aConfig.mEncodings.size() <= 1;
  bool denoising;
  bool codec_default_denoising = false;
  if (is_screencast) {
    denoising = false;
  } else {
    // Use codec default if video_noise_reduction is unset.
    denoising = aConduit->Denoising();
    codec_default_denoising = !denoising;
  }

  using Av1Config = JsepVideoCodecDescription::Av1Config;
  aConfig.mAv1Config.apply([&](const Av1Config& config) {
    MOZ_ASSERT(aConfig.mName == kAv1CodecName);
    config.mProfile.apply([&](uint8_t value) {
      aParameters[kAv1FmtpProfile] = std::to_string(value);
    });
    config.mLevelIdx.apply([&](uint8_t value) {
      aParameters[kAv1FmtpLevelIdx] = std::to_string(value);
    });
    config.mTier.apply([&](uint8_t value) {
      aParameters[kAv1FmtpTier] = std::to_string(value);
    });
  });

  if (aConfig.mName == kH264CodecName) {
    aParameters[kH264FmtpPacketizationMode] =
        std::to_string(aConfig.mPacketizationMode);
    {
      std::stringstream ss;
      ss << std::hex << std::setfill('0');
      ss << std::setw(2) << static_cast<uint32_t>(aConfig.mProfile);
      ss << std::setw(2) << static_cast<uint32_t>(aConfig.mConstraints);
      ss << std::setw(2) << static_cast<uint32_t>(aConfig.mLevel);
      std::string profileLevelId = ss.str();
      auto parsedProfileLevelId =
          webrtc::ParseH264ProfileLevelId(profileLevelId.c_str());
      MOZ_DIAGNOSTIC_ASSERT(parsedProfileLevelId);
      if (parsedProfileLevelId) {
        aParameters[kH264FmtpProfileLevelId] = profileLevelId;
      }
    }
    aParameters[kH264FmtpSpropParameterSets] = aConfig.mSpropParameterSets;
  }
  if (aConfig.mName == kVp8CodecName) {
    webrtc::VideoCodecVP8 vp8_settings =
        webrtc::VideoEncoder::GetDefaultVp8Settings();
    vp8_settings.automaticResizeOn = automatic_resize;
    // VP8 denoising is enabled by default.
    vp8_settings.denoisingOn = codec_default_denoising ? true : denoising;
    return rtc::scoped_refptr<
        webrtc::VideoEncoderConfig::EncoderSpecificSettings>(
        new rtc::RefCountedObject<
            webrtc::VideoEncoderConfig::Vp8EncoderSpecificSettings>(
            vp8_settings));
  }
  if (aConfig.mName == kVp9CodecName) {
    webrtc::VideoCodecVP9 vp9_settings =
        webrtc::VideoEncoder::GetDefaultVp9Settings();
    if (!is_screencast) {
      // Always configure only 1 spatial layer for screencapture as libwebrtc
      // has some special requirements when SVC is active. For non-screencapture
      // the spatial layers are experimentally configurable via a pref.
      vp9_settings.numberOfSpatialLayers = aConduit->SpatialLayers();
    }
    // VP9 denoising is disabled by default.
    vp9_settings.denoisingOn = codec_default_denoising ? false : denoising;
    return rtc::scoped_refptr<
        webrtc::VideoEncoderConfig::EncoderSpecificSettings>(
        new rtc::RefCountedObject<
            webrtc::VideoEncoderConfig::Vp9EncoderSpecificSettings>(
            vp9_settings));
  }
  return nullptr;
}

uint32_t GenerateRandomSSRC() {
  uint32_t ssrc;
  do {
    SECStatus rv = PK11_GenerateRandom(reinterpret_cast<unsigned char*>(&ssrc),
                                       sizeof(ssrc));
    MOZ_RELEASE_ASSERT(rv == SECSuccess);
  } while (ssrc == 0);  // webrtc.org code has fits if you select an SSRC of 0

  return ssrc;
}

// TODO: Make this a defaulted operator when we have c++20 (bug 1731036).
bool operator!=(
    const webrtc::VideoReceiveStreamInterface::Config::Rtp& aThis,
    const webrtc::VideoReceiveStreamInterface::Config::Rtp& aOther) {
  return aThis.remote_ssrc != aOther.remote_ssrc ||
         aThis.local_ssrc != aOther.local_ssrc ||
         aThis.rtcp_mode != aOther.rtcp_mode ||
         aThis.rtcp_xr.receiver_reference_time_report !=
             aOther.rtcp_xr.receiver_reference_time_report ||
         aThis.remb != aOther.remb || aThis.tmmbr != aOther.tmmbr ||
         aThis.keyframe_method != aOther.keyframe_method ||
         aThis.lntf.enabled != aOther.lntf.enabled ||
         aThis.nack.rtp_history_ms != aOther.nack.rtp_history_ms ||
         aThis.ulpfec_payload_type != aOther.ulpfec_payload_type ||
         aThis.red_payload_type != aOther.red_payload_type ||
         aThis.rtx_ssrc != aOther.rtx_ssrc ||
         aThis.protected_by_flexfec != aOther.protected_by_flexfec ||
         aThis.rtx_associated_payload_types !=
             aOther.rtx_associated_payload_types ||
         aThis.raw_payload_types != aOther.raw_payload_types;
}

#ifdef DEBUG
// TODO: Make this a defaulted operator when we have c++20 (bug 1731036).
bool operator==(
    const webrtc::VideoReceiveStreamInterface::Config::Rtp& aThis,
    const webrtc::VideoReceiveStreamInterface::Config::Rtp& aOther) {
  return !(aThis != aOther);
}
#endif

// TODO: Make this a defaulted operator when we have c++20 (bug 1731036).
bool operator!=(const webrtc::RtpConfig& aThis,
                const webrtc::RtpConfig& aOther) {
  return aThis.ssrcs != aOther.ssrcs || aThis.rids != aOther.rids ||
         aThis.mid != aOther.mid || aThis.rtcp_mode != aOther.rtcp_mode ||
         aThis.max_packet_size != aOther.max_packet_size ||
         aThis.extmap_allow_mixed != aOther.extmap_allow_mixed ||
         aThis.extensions != aOther.extensions ||
         aThis.payload_name != aOther.payload_name ||
         aThis.payload_type != aOther.payload_type ||
         aThis.raw_payload != aOther.raw_payload ||
         aThis.lntf.enabled != aOther.lntf.enabled ||
         aThis.nack.rtp_history_ms != aOther.nack.rtp_history_ms ||
         !(aThis.ulpfec == aOther.ulpfec) ||
         aThis.flexfec.payload_type != aOther.flexfec.payload_type ||
         aThis.flexfec.ssrc != aOther.flexfec.ssrc ||
         aThis.flexfec.protected_media_ssrcs !=
             aOther.flexfec.protected_media_ssrcs ||
         aThis.rtx.ssrcs != aOther.rtx.ssrcs ||
         aThis.rtx.payload_type != aOther.rtx.payload_type ||
         aThis.c_name != aOther.c_name;
}

#ifdef DEBUG
// TODO: Make this a defaulted operator when we have c++20 (bug 1731036).
bool operator==(const webrtc::RtpConfig& aThis,
                const webrtc::RtpConfig& aOther) {
  return !(aThis != aOther);
}
#endif

}  // namespace

void RecvSinkProxy::OnFrame(const webrtc::VideoFrame& aFrame) {
  mOwner->OnRecvFrame(aFrame);
}

void SendSinkProxy::OnFrame(const webrtc::VideoFrame& aFrame) {
  MOZ_ALWAYS_SUCCEEDS(mOwner->mCallThread->Dispatch(NS_NewRunnableFunction(
      __FUNCTION__,
      [owner = RefPtr(mOwner), aFrame] { owner->OnSendFrame(aFrame); })));
}

/**
 * Factory Method for VideoConduit
 */

RefPtr<VideoSessionConduit> VideoSessionConduit::Create(
    RefPtr<WebrtcCallWrapper> aCall, nsCOMPtr<nsISerialEventTarget> aStsThread,
    Options aOptions, std::string aPCHandle,
    const TrackingId& aRecvTrackingId) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aCall, "missing required parameter: aCall");
  CSFLogVerbose(LOGTAG, "%s", __FUNCTION__);

  if (!aCall) {
    return nullptr;
  }

  auto obj = MakeRefPtr<WebrtcVideoConduit>(
      std::move(aCall), std::move(aStsThread), std::move(aOptions),
      std::move(aPCHandle), aRecvTrackingId);
  if (obj->Init() != kMediaConduitNoError) {
    CSFLogError(LOGTAG, "%s VideoConduit Init Failed ", __FUNCTION__);
    return nullptr;
  }
  CSFLogVerbose(LOGTAG, "%s Successfully created VideoConduit ", __FUNCTION__);
  return obj.forget();
}

#define INIT_MIRROR(name, val) \
  name(aCallThread, val, "WebrtcVideoConduit::Control::" #name " (Mirror)")
WebrtcVideoConduit::Control::Control(const RefPtr<AbstractThread>& aCallThread)
    : INIT_MIRROR(mReceiving, false),
      INIT_MIRROR(mTransmitting, false),
      INIT_MIRROR(mLocalSsrcs, Ssrcs()),
      INIT_MIRROR(mLocalRtxSsrcs, Ssrcs()),
      INIT_MIRROR(mLocalCname, std::string()),
      INIT_MIRROR(mMid, std::string()),
      INIT_MIRROR(mRemoteSsrc, 0),
      INIT_MIRROR(mRemoteRtxSsrc, 0),
      INIT_MIRROR(mSyncGroup, std::string()),
      INIT_MIRROR(mLocalRecvRtpExtensions, RtpExtList()),
      INIT_MIRROR(mLocalSendRtpExtensions, RtpExtList()),
      INIT_MIRROR(mSendCodec, Nothing()),
      INIT_MIRROR(mSendRtpRtcpConfig, Nothing()),
      INIT_MIRROR(mRecvCodecs, std::vector<VideoCodecConfig>()),
      INIT_MIRROR(mRecvRtpRtcpConfig, Nothing()),
      INIT_MIRROR(mCodecMode, webrtc::VideoCodecMode::kRealtimeVideo),
      INIT_MIRROR(mFrameTransformerProxySend, nullptr),
      INIT_MIRROR(mFrameTransformerProxyRecv, nullptr) {}
#undef INIT_MIRROR

WebrtcVideoConduit::WebrtcVideoConduit(
    RefPtr<WebrtcCallWrapper> aCall, nsCOMPtr<nsISerialEventTarget> aStsThread,
    Options aOptions, std::string aPCHandle, const TrackingId& aRecvTrackingId)
    : mRendererMonitor("WebrtcVideoConduit::mRendererMonitor"),
      mCallThread(aCall->mCallThread),
      mStsThread(std::move(aStsThread)),
      mControl(aCall->mCallThread),
      mWatchManager(this, aCall->mCallThread),
      mMutex("WebrtcVideoConduit::mMutex"),
      mDecoderFactory(MakeUnique<WebrtcVideoDecoderFactory>(
          mCallThread.get(), aPCHandle, aRecvTrackingId)),
      mEncoderFactory(MakeUnique<WebrtcVideoEncoderFactory>(
          mCallThread.get(), std::move(aPCHandle))),
      mRecvSinkProxy(this),
      mSendSinkProxy(this),
      mEngineTransmitting(false),
      mEngineReceiving(false),
      mVideoLatencyTestEnable(aOptions.mVideoLatencyTestEnable),
      mMinBitrate(aOptions.mMinBitrate),
      mStartBitrate(aOptions.mStartBitrate),
      mPrefMaxBitrate(aOptions.mPrefMaxBitrate),
      mMinBitrateEstimate(aOptions.mMinBitrateEstimate),
      mDenoising(aOptions.mDenoising),
      mLockScaling(aOptions.mLockScaling),
      mSpatialLayers(aOptions.mSpatialLayers),
      mTemporalLayers(aOptions.mTemporalLayers),
      mCall(std::move(aCall)),
      mSendTransport(this),
      mRecvTransport(this),
      mSendStreamConfig(&mSendTransport),
      mVideoStreamFactory("WebrtcVideoConduit::mVideoStreamFactory"),
      mRecvStreamConfig(&mRecvTransport) {
  mRecvStreamConfig.rtp.rtcp_event_observer = this;
}

WebrtcVideoConduit::~WebrtcVideoConduit() {
  CSFLogDebug(LOGTAG, "%s ", __FUNCTION__);
  MOZ_ASSERT(!mSendStream && !mRecvStream,
             "Call DeleteStreams prior to ~WebrtcVideoConduit.");
}

#define CONNECT(aCanonical, aMirror)                                       \
  do {                                                                     \
    /* Ensure the watchmanager is wired up before the mirror receives its  \
     * initial mirrored value. */

    mCall->mCallThread->DispatchStateChange(                               \
        NS_NewRunnableFunction(__func__, [this, self = RefPtr(this)] {     \
          mWatchManager.Watch(aMirror,                                     \
                              &WebrtcVideoConduit::OnControlConfigChange); \
        }));                                                               \
    (aCanonical).ConnectMirror(&(aMirror));                                \
  } while (0)

void WebrtcVideoConduit::InitControl(VideoConduitControlInterface* aControl) {
  MOZ_ASSERT(NS_IsMainThread());

  CONNECT(aControl->CanonicalReceiving(), mControl.mReceiving);
  CONNECT(aControl->CanonicalTransmitting(), mControl.mTransmitting);
  CONNECT(aControl->CanonicalLocalSsrcs(), mControl.mLocalSsrcs);
  CONNECT(aControl->CanonicalLocalVideoRtxSsrcs(), mControl.mLocalRtxSsrcs);
  CONNECT(aControl->CanonicalLocalCname(), mControl.mLocalCname);
  CONNECT(aControl->CanonicalMid(), mControl.mMid);
  CONNECT(aControl->CanonicalRemoteSsrc(), mControl.mRemoteSsrc);
  CONNECT(aControl->CanonicalRemoteVideoRtxSsrc(), mControl.mRemoteRtxSsrc);
  CONNECT(aControl->CanonicalSyncGroup(), mControl.mSyncGroup);
  CONNECT(aControl->CanonicalLocalRecvRtpExtensions(),
          mControl.mLocalRecvRtpExtensions);
  CONNECT(aControl->CanonicalLocalSendRtpExtensions(),
          mControl.mLocalSendRtpExtensions);
  CONNECT(aControl->CanonicalVideoSendCodec(), mControl.mSendCodec);
  CONNECT(aControl->CanonicalVideoSendRtpRtcpConfig(),
          mControl.mSendRtpRtcpConfig);
  CONNECT(aControl->CanonicalVideoRecvCodecs(), mControl.mRecvCodecs);
  CONNECT(aControl->CanonicalVideoRecvRtpRtcpConfig(),
          mControl.mRecvRtpRtcpConfig);
  CONNECT(aControl->CanonicalVideoCodecMode(), mControl.mCodecMode);
  CONNECT(aControl->CanonicalFrameTransformerProxySend(),
          mControl.mFrameTransformerProxySend);
  CONNECT(aControl->CanonicalFrameTransformerProxyRecv(),
          mControl.mFrameTransformerProxyRecv);
}

#undef CONNECT

void WebrtcVideoConduit::OnControlConfigChange() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());

  bool encoderReconfigureNeeded = false;
  bool remoteSsrcUpdateNeeded = false;
  bool sendStreamRecreationNeeded = false;
  bool sendSourceUpdateNeeded = false;

  if (mControl.mRemoteSsrc.Ref() != mControl.mConfiguredRemoteSsrc) {
    mControl.mConfiguredRemoteSsrc = mControl.mRemoteSsrc;
    remoteSsrcUpdateNeeded = true;
  }

  if (mControl.mRemoteRtxSsrc.Ref() != mControl.mConfiguredRemoteRtxSsrc) {
    mControl.mConfiguredRemoteRtxSsrc = mControl.mRemoteRtxSsrc;
    remoteSsrcUpdateNeeded = true;
  }

  if (mControl.mSyncGroup.Ref() != mRecvStreamConfig.sync_group) {
    mRecvStreamConfig.sync_group = mControl.mSyncGroup;
  }

  if (const auto [codecConfigList, rtpRtcpConfig] = std::make_pair(
          mControl.mRecvCodecs.Ref(), mControl.mRecvRtpRtcpConfig.Ref());
      !codecConfigList.empty() && rtpRtcpConfig.isSome() &&
      (codecConfigList != mControl.mConfiguredRecvCodecs ||
       rtpRtcpConfig != mControl.mConfiguredRecvRtpRtcpConfig)) {
    mControl.mConfiguredRecvCodecs = codecConfigList;
    mControl.mConfiguredRecvRtpRtcpConfig = rtpRtcpConfig;

    webrtc::VideoReceiveStreamInterface::Config::Rtp newRtp(
        mRecvStreamConfig.rtp);
    MOZ_ASSERT(newRtp == mRecvStreamConfig.rtp);
    newRtp.rtx_associated_payload_types.clear();
    newRtp.rtx_ssrc = 0;
    newRtp.rtcp_mode = rtpRtcpConfig->GetRtcpMode();
    newRtp.nack.rtp_history_ms = 0;
    newRtp.remb = false;
    newRtp.tmmbr = false;
    newRtp.keyframe_method = webrtc::KeyFrameReqMethod::kNone;
    newRtp.ulpfec_payload_type = kNullPayloadType;
    newRtp.red_payload_type = kNullPayloadType;
    bool use_fec = false;
    bool configuredH264 = false;
    std::vector<webrtc::VideoReceiveStreamInterface::Decoder> recv_codecs;

    // Try Applying the codecs in the list
    // we treat as success if at least one codec was applied and reception was
    // started successfully.
    for (const auto& codec_config : codecConfigList) {
      if (auto condError = ValidateCodecConfig(codec_config);
          condError != kMediaConduitNoError) {
        CSFLogError(LOGTAG, "Invalid recv codec config for %s decoder: %i",
                    codec_config.mName.c_str(), condError);
        continue;
      }

      if (codec_config.mName == kH264CodecName) {
        // TODO(bug 1200768): We can only handle configuring one recv H264 codec
        if (configuredH264) {
          continue;
        }
        configuredH264 = true;
      }

      if (codec_config.mName == kUlpfecCodecName) {
        newRtp.ulpfec_payload_type = codec_config.mType;
        continue;
      }

      // Set RTX associated PT here so we can set it for RED without additional
      // checks for things like preventing creating an unncessary decoder for
      // RED. This assumes that any codecs created with RTX enabled
      // (including those not found in SupportedCodecType) intend to use it.
      if (codec_config.RtxPayloadTypeIsSet()) {
        newRtp.rtx_associated_payload_types[codec_config.mRTXPayloadType] =
            codec_config.mType;
      }

      if (codec_config.mName == kRedCodecName) {
        newRtp.red_payload_type = codec_config.mType;
        continue;
      }

      if (SupportedCodecType(
              webrtc::PayloadStringToCodecType(codec_config.mName)) ==
          webrtc::VideoCodecType::kVideoCodecGeneric) {
        CSFLogError(LOGTAG, "%s Unknown decoder type: %s", __FUNCTION__,
                    codec_config.mName.c_str());
        continue;
      }

      // Check for the keyframe request type: PLI is preferred over FIR, and FIR
      // is preferred over none.
      if (codec_config.RtcpFbNackIsSet(kRtcpFbNackParamPli)) {
        newRtp.keyframe_method = webrtc::KeyFrameReqMethod::kPliRtcp;
      } else if (newRtp.keyframe_method !=
                     webrtc::KeyFrameReqMethod::kPliRtcp &&
                 codec_config.RtcpFbCcmIsSet(kRtcpFbCcmParamFir)) {
        newRtp.keyframe_method = webrtc::KeyFrameReqMethod::kFirRtcp;
      }

      // What if codec A has Nack and REMB, and codec B has TMMBR, and codec C
      // has none? In practice, that's not a useful configuration, and
      // VideoReceiveStream::Config can't represent that, so simply union the
      // (boolean) settings
      if (codec_config.RtcpFbNackIsSet(kParamValueEmpty)) {
        newRtp.nack.rtp_history_ms = 1000;
      }
      newRtp.tmmbr |= codec_config.RtcpFbCcmIsSet(kRtcpFbCcmParamTmmbr);
      newRtp.remb |= codec_config.RtcpFbRembIsSet();
      use_fec |= codec_config.RtcpFbFECIsSet();

      auto& decoder = recv_codecs.emplace_back();
      decoder.video_format = webrtc::SdpVideoFormat(codec_config.mName);
      decoder.payload_type = codec_config.mType;
    }

    if (!use_fec) {
      // Reset to defaults
      newRtp.ulpfec_payload_type = kNullPayloadType;
      newRtp.red_payload_type = kNullPayloadType;
    }

    // TODO: This would be simpler, but for some reason gives
    //       "error: invalid operands to binary expression
    //       ('webrtc::VideoReceiveStreamInterface::Decoder' and
    //       'webrtc::VideoReceiveStreamInterface::Decoder')"
    // if (recv_codecs != mRecvStreamConfig.decoders) {
    if (!std::equal(recv_codecs.begin(), recv_codecs.end(),
                    mRecvStreamConfig.decoders.begin(),
                    mRecvStreamConfig.decoders.end(),
                    [](const auto& aLeft, const auto& aRight) {
                      return aLeft == aRight;
                    })) {
      if (recv_codecs.empty()) {
        CSFLogError(LOGTAG, "%s Found no valid receive codecs", __FUNCTION__);
      }
      mRecvStreamConfig.decoders = std::move(recv_codecs);
    }

    if (mRecvStreamConfig.rtp != newRtp) {
      mRecvStreamConfig.rtp = newRtp;
    }
  }

  {
    // mSendStreamConfig and other members need the lock
    MutexAutoLock lock(mMutex);
    if (mControl.mLocalSsrcs.Ref() != mSendStreamConfig.rtp.ssrcs) {
      mSendStreamConfig.rtp.ssrcs = mControl.mLocalSsrcs;
      sendStreamRecreationNeeded = true;

      const uint32_t localSsrc = mSendStreamConfig.rtp.ssrcs.empty()
                                     ? 0
                                     : mSendStreamConfig.rtp.ssrcs.front();
      if (localSsrc != mRecvStreamConfig.rtp.local_ssrc) {
        mRecvStreamConfig.rtp.local_ssrc = localSsrc;
      }
    }

    {
      Ssrcs localRtxSsrcs = mControl.mLocalRtxSsrcs.Ref();
      if (!mControl.mSendCodec.Ref()
               .map([](const auto& aCodec) {
                 return aCodec.RtxPayloadTypeIsSet();
               })
               .valueOr(false)) {
        localRtxSsrcs.clear();
      }
      if (localRtxSsrcs != mSendStreamConfig.rtp.rtx.ssrcs) {
        mSendStreamConfig.rtp.rtx.ssrcs = localRtxSsrcs;
        sendStreamRecreationNeeded = true;
      }
    }

    if (mControl.mLocalCname.Ref() != mSendStreamConfig.rtp.c_name) {
      mSendStreamConfig.rtp.c_name = mControl.mLocalCname;
      sendStreamRecreationNeeded = true;
    }

    if (mControl.mMid.Ref() != mSendStreamConfig.rtp.mid) {
      mSendStreamConfig.rtp.mid = mControl.mMid;
      sendStreamRecreationNeeded = true;
    }

    if (mControl.mLocalSendRtpExtensions.Ref() !=
        mSendStreamConfig.rtp.extensions) {
      mSendStreamConfig.rtp.extensions = mControl.mLocalSendRtpExtensions;
      sendStreamRecreationNeeded = true;
    }

    if (const auto [codecConfig, rtpRtcpConfig] = std::make_pair(
            mControl.mSendCodec.Ref(), mControl.mSendRtpRtcpConfig.Ref());
        codecConfig.isSome() && rtpRtcpConfig.isSome() &&
        (codecConfig != mControl.mConfiguredSendCodec ||
         rtpRtcpConfig != mControl.mConfiguredSendRtpRtcpConfig)) {
      CSFLogDebug(LOGTAG, "Configuring codec %s", codecConfig->mName.c_str());

      if (mControl.mConfiguredSendCodec.isSome() &&
          (mControl.mConfiguredSendCodec->mName != codecConfig->mName)) {
        // This tends to happen if the codec is changed mid call.
        // We need to delete the stream now, if we continue to setup the new
        // codec before deleting the send stream libwebrtc will throw erros.
        DeleteSendStream();
      }
      mControl.mConfiguredSendCodec = codecConfig;
      mControl.mConfiguredSendRtpRtcpConfig = rtpRtcpConfig;

      if (ValidateCodecConfig(*codecConfig) == kMediaConduitNoError) {
        encoderReconfigureNeeded = true;
        sendSourceUpdateNeeded = true;

        mCurSendCodecConfig = codecConfig;

        size_t streamCount = std::min(codecConfig->mEncodings.size(),
                                      (size_t)webrtc::kMaxSimulcastStreams);
        MOZ_RELEASE_ASSERT(streamCount >= 1,
                           "streamCount should be at least one");

        CSFLogDebug(LOGTAG,
                    "Updating send codec for VideoConduit:%p stream count:%zu",
                    this, streamCount);

        // So we can comply with b=TIAS/b=AS/maxbr=X when input resolution
        // changes
        MOZ_ASSERT(codecConfig->mTias < INT_MAX);
        mNegotiatedMaxBitrate = static_cast<int>(codecConfig->mTias);

        if (!mLastSize && mMinBitrateEstimate != 0) {
          // Only do this at the start; use "have we sent a frame" as a
          // reasonable stand-in. min <= start <= max (but all three parameters
          // are optional)
          webrtc::BitrateSettings settings;
          settings.min_bitrate_bps = mMinBitrateEstimate;
          settings.start_bitrate_bps = mMinBitrateEstimate;
          mCall->Call()->SetClientBitratePreferences(settings);
        }

        // XXX parse the encoded SPS/PPS data and set
        // spsData/spsLen/ppsData/ppsLen
        mEncoderConfig.video_format =
            webrtc::SdpVideoFormat(codecConfig->mName);
        mEncoderConfig.encoder_specific_settings =
            ConfigureVideoEncoderSettings(
                *codecConfig, this, mEncoderConfig.video_format.parameters);

        mEncoderConfig.codec_type = SupportedCodecType(
            webrtc::PayloadStringToCodecType(codecConfig->mName));
        MOZ_RELEASE_ASSERT(mEncoderConfig.codec_type !=
                           webrtc::VideoCodecType::kVideoCodecGeneric);

        mEncoderConfig.content_type =
            mControl.mCodecMode.Ref() == webrtc::VideoCodecMode::kRealtimeVideo
                ? webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo
                : webrtc::VideoEncoderConfig::ContentType::kScreen;

        mEncoderConfig.frame_drop_enabled = true;

        mEncoderConfig.min_transmit_bitrate_bps = mMinBitrate;

        // Set the max bitrate, defaulting to 10Mbps, checking:
        // - pref
        // - b=TIAS
        // - codec constraints
        // - encoding parameter if there's a single stream
        int maxBps = KBPS(10000);
        maxBps = MinIgnoreZero(maxBps, mPrefMaxBitrate);
        maxBps = MinIgnoreZero(maxBps, mNegotiatedMaxBitrate);
        maxBps = MinIgnoreZero(
            maxBps, static_cast<int>(codecConfig->mEncodingConstraints.maxBr));
        if (codecConfig->mEncodings.size() == 1) {
          maxBps = MinIgnoreZero(
              maxBps,
              static_cast<int>(codecConfig->mEncodings[0].constraints.maxBr));
        }
        mEncoderConfig.max_bitrate_bps = maxBps;

        // TODO this is for webrtc-priority, but needs plumbing bits
        mEncoderConfig.bitrate_priority = 1.0;

        // Populate simulcast_layers with their config.
        mEncoderConfig.simulcast_layers.clear();
        const auto& codecConstraints = codecConfig->mEncodingConstraints;
        for (size_t idx = 0; idx < streamCount; ++idx) {
          webrtc::VideoStream video_stream;
          const auto& encoding = codecConfig->mEncodings[idx];
          const auto& encodingConstraints = encoding.constraints;

          video_stream.active = encoding.active;

          // Dimensions set here are used by libwebrtc to set the maximum to
          // request from the source, to support scaleDownTo without another
          // encoder reconfiguration on the first frame.
          // We don't support scaleDownTo but use max-width and max-height if
          // signaled.
          video_stream.width = codecConfig->mEncodingConstraints.maxWidth;
          video_stream.height = codecConfig->mEncodingConstraints.maxHeight;

          // Max framerate is also used to cap the source, to avoid processing
          // frames that will have to be dropped. Our signals here are both
          // RTCRtpEncodingParameters.maxFramerate (per encoding) and max-fr
          // for supported codecs.
          video_stream.max_framerate = static_cast<int>(([&]() {
            if (codecConstraints.maxFps && encodingConstraints.maxFps) {
              return std::min(*codecConstraints.maxFps,
                              *encodingConstraints.maxFps);
            }
            return codecConstraints.maxFps
                .orElse([&] { return encodingConstraints.maxFps; })
                .valueOr(-1);
          })());

          // At this time, other values are not used until after
          // CreateEncoderStreams(). We fill these in directly from the codec
          // config in VideoStreamFactory.

          mEncoderConfig.simulcast_layers.push_back(video_stream);
        }

        // Expected max number of encodings
        mEncoderConfig.number_of_streams =
            mEncoderConfig.simulcast_layers.size();

        // libwebrtc disables this by default.
        mSendStreamConfig.suspend_below_min_bitrate = false;

        webrtc::RtpConfig newRtp = mSendStreamConfig.rtp;
        MOZ_ASSERT(newRtp == mSendStreamConfig.rtp);
        newRtp.payload_name = codecConfig->mName;
        newRtp.payload_type = codecConfig->mType;
        newRtp.rtcp_mode = rtpRtcpConfig->GetRtcpMode();
        newRtp.max_packet_size = kVideoMtu;
        newRtp.rtx.payload_type = codecConfig->RtxPayloadTypeIsSet()
                                      ? codecConfig->mRTXPayloadType
                                      : kNullPayloadType;

        {
          // See Bug 1297058, enabling FEC when basic NACK is to be enabled in
          // H.264 is problematic
          const bool useFECDefaults =
              !codecConfig->RtcpFbFECIsSet() ||
              (codecConfig->mName == kH264CodecName &&
               codecConfig->RtcpFbNackIsSet(kParamValueEmpty));
          newRtp.ulpfec.ulpfec_payload_type =
              useFECDefaults ? kNullPayloadType
                             : codecConfig->mULPFECPayloadType;
          newRtp.ulpfec.red_payload_type =
              useFECDefaults ? kNullPayloadType : codecConfig->mREDPayloadType;
          newRtp.ulpfec.red_rtx_payload_type =
              useFECDefaults ? kNullPayloadType
                             : codecConfig->mREDRTXPayloadType;
        }

        newRtp.nack.rtp_history_ms =
            codecConfig->RtcpFbNackIsSet(kParamValueEmpty) ? 1000 : 0;

        newRtp.rids.clear();
        if (!codecConfig->mEncodings.empty() &&
            !codecConfig->mEncodings[0].rid.empty()) {
          for (const auto& encoding : codecConfig->mEncodings) {
            newRtp.rids.push_back(encoding.rid);
          }
        }

        if (mSendStreamConfig.rtp != newRtp) {
          mSendStreamConfig.rtp = newRtp;
          sendStreamRecreationNeeded = true;
        }
      }
    }

    {
      const auto& mode = mControl.mCodecMode.Ref();
      MOZ_ASSERT(mode == webrtc::VideoCodecMode::kRealtimeVideo ||
                 mode == webrtc::VideoCodecMode::kScreensharing);

      auto contentType =
          mode == webrtc::VideoCodecMode::kRealtimeVideo
              ? webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo
              : webrtc::VideoEncoderConfig::ContentType::kScreen;

      if (contentType != mEncoderConfig.content_type) {
        encoderReconfigureNeeded = true;
        sendSourceUpdateNeeded = true;
      }
    }

    if (mControl.mConfiguredFrameTransformerProxySend.get() !=
        mControl.mFrameTransformerProxySend.Ref().get()) {
      mControl.mConfiguredFrameTransformerProxySend =
          mControl.mFrameTransformerProxySend.Ref();
      if (!mSendStreamConfig.frame_transformer) {
        mSendStreamConfig.frame_transformer =
            new rtc::RefCountedObject<FrameTransformer>(true);
        sendStreamRecreationNeeded = true;
      }
      static_cast<FrameTransformer*>(mSendStreamConfig.frame_transformer.get())
          ->SetProxy(mControl.mConfiguredFrameTransformerProxySend);
    }

    if (mControl.mConfiguredFrameTransformerProxyRecv.get() !=
        mControl.mFrameTransformerProxyRecv.Ref().get()) {
      mControl.mConfiguredFrameTransformerProxyRecv =
          mControl.mFrameTransformerProxyRecv.Ref();
      if (!mRecvStreamConfig.frame_transformer) {
        mRecvStreamConfig.frame_transformer =
            new rtc::RefCountedObject<FrameTransformer>(true);
      }
      static_cast<FrameTransformer*>(mRecvStreamConfig.frame_transformer.get())
          ->SetProxy(mControl.mConfiguredFrameTransformerProxyRecv);
      // No flag to set, we always recreate recv streams
    }

    if (remoteSsrcUpdateNeeded) {
      SetRemoteSSRCConfig(mControl.mConfiguredRemoteSsrc,
                          mControl.mConfiguredRemoteRtxSsrc);
    }

    // Handle un-signalled SSRCs by creating random ones and then when they
    // actually get set, we'll destroy and recreate.
    if (mControl.mReceiving || mControl.mTransmitting) {
      const auto remoteSsrc = mRecvStreamConfig.rtp.remote_ssrc;
      const auto localSsrc = mRecvStreamConfig.rtp.local_ssrc;
      const auto localSsrcs = mSendStreamConfig.rtp.ssrcs;
      EnsureLocalSSRC();
      if (mControl.mReceiving) {
        EnsureRemoteSSRC();
      }
      if (localSsrc != mRecvStreamConfig.rtp.local_ssrc ||
          remoteSsrc != mRecvStreamConfig.rtp.remote_ssrc) {
      }
      if (localSsrcs != mSendStreamConfig.rtp.ssrcs) {
        sendStreamRecreationNeeded = true;
      }
    }

    // Recreate receiving streams
    if (mControl.mReceiving) {
      DeleteRecvStream();
      CreateRecvStream();
    }
    if (sendStreamRecreationNeeded) {
      encoderReconfigureNeeded = false;
      DeleteSendStream();
    }
    if (mControl.mTransmitting) {
      CreateSendStream();
    }
  }

  // We make sure to not hold the lock while stopping/starting/reconfiguring
  // streams, so as to not cause deadlocks. These methods can cause our platform
  // codecs to dispatch sync runnables to main, and main may grab the lock.

  if (mSendStream) {
    if (encoderReconfigureNeeded) {
      MOZ_DIAGNOSTIC_ASSERT(
          mSendStreamConfig.rtp.ssrcs.size() ==
              mEncoderConfig.number_of_streams,
          "Each video substream must have a corresponding ssrc.");
      mEncoderConfig.video_stream_factory = CreateVideoStreamFactory();
      mSendStream->ReconfigureVideoEncoder(mEncoderConfig.Copy());
    }
    if (sendSourceUpdateNeeded && mTrackSource) {
      mSendStream->SetSource(mTrackSource, DegradationPreference());
    }
  }

  if (!mControl.mReceiving) {
    StopReceiving();
  }
  if (!mControl.mTransmitting) {
    StopTransmitting();
  }

  if (mControl.mReceiving) {
    StartReceiving();
  }
  if (mControl.mTransmitting) {
    StartTransmitting();
  }
}

std::vector<unsigned int> WebrtcVideoConduit::GetLocalSSRCs() const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  return mSendStreamConfig.rtp.ssrcs;
}

Maybe<Ssrc> WebrtcVideoConduit::GetAssociatedLocalRtxSSRC(Ssrc aSsrc) const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  for (size_t i = 0; i < mSendStreamConfig.rtp.ssrcs.size() &&
                     i < mSendStreamConfig.rtp.rtx.ssrcs.size();
       ++i) {
    if (mSendStreamConfig.rtp.ssrcs[i] == aSsrc) {
      return Some(mSendStreamConfig.rtp.rtx.ssrcs[i]);
    }
  }
  return Nothing();
}

Maybe<gfx::IntSize> WebrtcVideoConduit::GetLastResolution() const {
  MutexAutoLock lock(mMutex);
  return mLastSize;
}

void WebrtcVideoConduit::DeleteSendStream() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertCurrentThreadOwns();

  if (!mSendStream) {
    return;
  }

  mCall->Call()->DestroyVideoSendStream(mSendStream);
  mEngineTransmitting = false;
  mSendStream = nullptr;

  // Reset base_seqs in case ssrcs get re-used.
  mRtpSendBaseSeqs.clear();
}

void WebrtcVideoConduit::CreateSendStream() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertCurrentThreadOwns();

  if (mSendStream) {
    return;
  }

  glean::webrtc_video::send_codec_used
      .Get(nsDependentCString(mSendStreamConfig.rtp.payload_name.c_str()))
      .Add(1);

  mEncoderConfig.video_stream_factory = CreateVideoStreamFactory();

  mSendStreamConfig.encoder_settings.encoder_factory = mEncoderFactory.get();
  mSendStreamConfig.encoder_settings.bitrate_allocator_factory =
      mCall->mVideoBitrateAllocatorFactory.get();

  MOZ_DIAGNOSTIC_ASSERT(
      mSendStreamConfig.rtp.ssrcs.size() == mEncoderConfig.number_of_streams,
      "Each video substream must have a corresponding ssrc.");

  mSendStream = mCall->Call()->CreateVideoSendStream(mSendStreamConfig.Copy(),
                                                     mEncoderConfig.Copy());

  if (mTrackSource) {
    mSendStream->SetSource(mTrackSource, DegradationPreference());
  }
}

void WebrtcVideoConduit::DeleteRecvStream() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertCurrentThreadOwns();

  if (!mRecvStream) {
    return;
  }

  mCall->Call()->DestroyVideoReceiveStream(mRecvStream);
  mEngineReceiving = false;
  mRecvStream = nullptr;
}

void WebrtcVideoConduit::CreateRecvStream() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertCurrentThreadOwns();

  if (mRecvStream) {
    return;
  }

  mRecvStreamConfig.renderer = &mRecvSinkProxy;

  for (auto& decoder : mRecvStreamConfig.decoders) {
    glean::webrtc_video::recv_codec_used
        .Get(nsDependentCString(decoder.video_format.name.c_str()))
        .Add(1);
  }

  mRecvStreamConfig.decoder_factory = mDecoderFactory.get();

  mRecvStream =
      mCall->Call()->CreateVideoReceiveStream(mRecvStreamConfig.Copy());
  // Ensure that we set the jitter buffer target on this stream.
  mRecvStream->SetBaseMinimumPlayoutDelayMs(mJitterBufferTargetMs);

  CSFLogDebug(LOGTAG, "Created VideoReceiveStream %p for SSRC %u (0x%x)",
              mRecvStream, mRecvStreamConfig.rtp.remote_ssrc,
              mRecvStreamConfig.rtp.remote_ssrc);
}

void WebrtcVideoConduit::NotifyUnsetCurrentRemoteSSRC() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  CSFLogDebug(LOGTAG, "%s (%p): Unsetting SSRC %u in other conduits",
              __FUNCTION__, this, mRecvStreamConfig.rtp.remote_ssrc);
  mCall->UnregisterConduit(this);
  mCall->UnsetRemoteSSRC(mRecvStreamConfig.rtp.remote_ssrc);
  mCall->RegisterConduit(this);
}

void WebrtcVideoConduit::SetRemoteSSRCConfig(uint32_t aSsrc,
                                             uint32_t aRtxSsrc) {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());

  CSFLogDebug(LOGTAG, "%s: SSRC %u (0x%x)", __FUNCTION__, aSsrc, aSsrc);

  if (mRecvStreamConfig.rtp.remote_ssrc != aSsrc) {
    nsCOMPtr<nsIDirectTaskDispatcher> dtd = do_QueryInterface(mCallThread);
    MOZ_ALWAYS_SUCCEEDS(dtd->DispatchDirectTask(NewRunnableMethod(
        "WebrtcVideoConduit::NotifyUnsetCurrentRemoteSSRC"this,
        &WebrtcVideoConduit::NotifyUnsetCurrentRemoteSSRC)));
  }

  mRecvSSRC = mRecvStreamConfig.rtp.remote_ssrc = aSsrc;
  // If we have no associated PT then ensure we dont have an rtx_ssrc set.
  mRecvStreamConfig.rtp.rtx_ssrc =
      mRecvStreamConfig.rtp.rtx_associated_payload_types.empty() ? 0 : aRtxSsrc;
}

void WebrtcVideoConduit::SetRemoteSSRCAndRestartAsNeeded(uint32_t aSsrc,
                                                         uint32_t aRtxSsrc) {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());

  if (mRecvStreamConfig.rtp.remote_ssrc == aSsrc &&
      mRecvStreamConfig.rtp.rtx_ssrc == aRtxSsrc) {
    return;
  }

  SetRemoteSSRCConfig(aSsrc, aRtxSsrc);

  const bool wasReceiving = mEngineReceiving;
  const bool hadRecvStream = mRecvStream;

  StopReceiving();

  if (hadRecvStream) {
    MutexAutoLock lock(mMutex);
    DeleteRecvStream();
    CreateRecvStream();
  }

  if (wasReceiving) {
    StartReceiving();
  }
}

void WebrtcVideoConduit::EnsureRemoteSSRC() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertCurrentThreadOwns();

  const auto& ssrcs = mSendStreamConfig.rtp.ssrcs;
  if (mRecvStreamConfig.rtp.remote_ssrc != 0 &&
      std::find(ssrcs.begin(), ssrcs.end(),
                mRecvStreamConfig.rtp.remote_ssrc) == ssrcs.end()) {
    return;
  }

  uint32_t ssrc;
  do {
    ssrc = GenerateRandomSSRC();
  } while (
      NS_WARN_IF(std::find(ssrcs.begin(), ssrcs.end(), ssrc) != ssrcs.end()));
  CSFLogDebug(LOGTAG, "VideoConduit %p: Generated remote SSRC %u"this, ssrc);
  SetRemoteSSRCConfig(ssrc, 0);
}

void WebrtcVideoConduit::EnsureLocalSSRC() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertCurrentThreadOwns();

  auto& ssrcs = mSendStreamConfig.rtp.ssrcs;
  if (ssrcs.empty()) {
    ssrcs.push_back(GenerateRandomSSRC());
  }

  // Reverse-iterating here so that the first dupe in `ssrcs` always wins.
  for (auto& ssrc : Reversed(ssrcs)) {
    if (ssrc != 0 && ssrc != mRecvStreamConfig.rtp.remote_ssrc &&
        std::count(ssrcs.begin(), ssrcs.end(), ssrc) == 1) {
      continue;
    }
    do {
      ssrc = GenerateRandomSSRC();
    } while (NS_WARN_IF(ssrc == mRecvStreamConfig.rtp.remote_ssrc) ||
             NS_WARN_IF(std::count(ssrcs.begin(), ssrcs.end(), ssrc) > 1));
    CSFLogDebug(LOGTAG, "%s (%p): Generated local SSRC %u", __FUNCTION__, this,
                ssrc);
  }
  mRecvStreamConfig.rtp.local_ssrc = ssrcs[0];
}

void WebrtcVideoConduit::UnsetRemoteSSRC(uint32_t aSsrc) {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertNotCurrentThreadOwns();

  if (mRecvStreamConfig.rtp.remote_ssrc != aSsrc &&
      mRecvStreamConfig.rtp.rtx_ssrc != aSsrc) {
    return;
  }

  const auto& ssrcs = mSendStreamConfig.rtp.ssrcs;
  uint32_t our_ssrc = 0;
  do {
    our_ssrc = GenerateRandomSSRC();
  } while (NS_WARN_IF(our_ssrc == aSsrc) ||
           NS_WARN_IF(std::find(ssrcs.begin(), ssrcs.end(), our_ssrc) !=
                      ssrcs.end()));

  CSFLogDebug(LOGTAG, "%s (%p): Generated remote SSRC %u", __FUNCTION__, this,
              our_ssrc);

  // There is a (tiny) chance that this new random ssrc will collide with some
  // other conduit's remote ssrc, in which case that conduit will choose a new
  // one.
  SetRemoteSSRCAndRestartAsNeeded(our_ssrc, 0);
}

/*static*/
unsigned WebrtcVideoConduit::ToLibwebrtcMaxFramerate(
    const Maybe<double>& aMaxFramerate) {
  Maybe<unsigned> negotiatedMaxFps;
  if (aMaxFramerate.isSome()) {
    // libwebrtc does not handle non-integer max framerate.
    unsigned integerMaxFps = static_cast<unsigned>(std::round(*aMaxFramerate));
    // libwebrtc crashes with a max framerate of 0, even though the
    // spec says this is valid. For now, we treat this as no limit.
    if (integerMaxFps) {
      negotiatedMaxFps = Some(integerMaxFps);
    }
  }
  // We do not use DEFAULT_VIDEO_MAX_FRAMERATE here; that is used at the very
  // end in VideoStreamFactory, once codec-wide and per-encoding limits are
  // known.
  return negotiatedMaxFps.refOr(std::numeric_limits<unsigned int>::max());
}

Maybe<Ssrc> WebrtcVideoConduit::GetRemoteSSRC() const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  // libwebrtc uses 0 to mean a lack of SSRC. That is not to spec.
  return mRecvStreamConfig.rtp.remote_ssrc == 0
             ? Nothing()
             : Some(mRecvStreamConfig.rtp.remote_ssrc);
}

Maybe<webrtc::VideoReceiveStreamInterface::Stats>
WebrtcVideoConduit::GetReceiverStats() const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  if (!mRecvStream) {
    return Nothing();
  }
  return Some(mRecvStream->GetStats());
}

Maybe<webrtc::VideoSendStream::Stats> WebrtcVideoConduit::GetSenderStats()
    const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  if (!mSendStream) {
    return Nothing();
  }
  return Some(mSendStream->GetStats());
}

Maybe<webrtc::Call::Stats> WebrtcVideoConduit::GetCallStats() const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  if (!mCall->Call()) {
    return Nothing();
  }
  return Some(mCall->Call()->GetStats());
}

MediaConduitErrorCode WebrtcVideoConduit::Init() {
  MOZ_ASSERT(NS_IsMainThread());

  CSFLogDebug(LOGTAG, "%s this=%p", __FUNCTION__, this);

#ifdef MOZ_WIDGET_ANDROID
  if (mozilla::camera::VideoEngine::SetAndroidObjects() != 0) {
    CSFLogError(LOGTAG, "%s: could not set Android objects", __FUNCTION__);
    return kMediaConduitSessionNotInited;
  }
#endif  // MOZ_WIDGET_ANDROID

  mSendPluginCreated = mEncoderFactory->CreatedGmpPluginEvent().Connect(
      GetMainThreadSerialEventTarget(),
      [self = detail::RawPtr(this)](uint64_t aPluginID) {
        self.get()->mSendCodecPluginIDs.AppendElement(aPluginID);
      });
  mSendPluginReleased = mEncoderFactory->ReleasedGmpPluginEvent().Connect(
      GetMainThreadSerialEventTarget(),
      [self = detail::RawPtr(this)](uint64_t aPluginID) {
        self.get()->mSendCodecPluginIDs.RemoveElement(aPluginID);
      });
  mRecvPluginCreated = mDecoderFactory->CreatedGmpPluginEvent().Connect(
      GetMainThreadSerialEventTarget(),
      [self = detail::RawPtr(this)](uint64_t aPluginID) {
        self.get()->mRecvCodecPluginIDs.AppendElement(aPluginID);
      });
  mRecvPluginReleased = mDecoderFactory->ReleasedGmpPluginEvent().Connect(
      GetMainThreadSerialEventTarget(),
      [self = detail::RawPtr(this)](uint64_t aPluginID) {
        self.get()->mRecvCodecPluginIDs.RemoveElement(aPluginID);
      });

  MOZ_ALWAYS_SUCCEEDS(mCallThread->Dispatch(NS_NewRunnableFunction(
      __func__, [this, self = RefPtr<WebrtcVideoConduit>(this)] {
        mCall->RegisterConduit(this);
      })));

  CSFLogDebug(LOGTAG, "%s Initialization Done", __FUNCTION__);
  return kMediaConduitNoError;
}

RefPtr<GenericPromise> WebrtcVideoConduit::Shutdown() {
  MOZ_ASSERT(NS_IsMainThread());

  mSendPluginCreated.DisconnectIfExists();
  mSendPluginReleased.DisconnectIfExists();
  mRecvPluginCreated.DisconnectIfExists();
  mRecvPluginReleased.DisconnectIfExists();

  return InvokeAsync(
      mCallThread, __func__, [this, self = RefPtr<WebrtcVideoConduit>(this)] {
        if (mSendBitrate.NumDataValues() > 0) {
          glean::webrtc::video_encoder_bitrate_avg_per_call_kbps
              .AccumulateSingleSample(
                  static_cast<unsigned>(mSendBitrate.Mean() / 1000));
          glean::webrtc::video_encoder_bitrate_std_dev_per_call_kbps
              .AccumulateSingleSample(static_cast<unsigned>(
                  mSendBitrate.StandardDeviation() / 1000));
          mSendBitrate.Clear();
        }
        if (mSendFramerate.NumDataValues() > 0) {
          glean::webrtc::video_encoder_framerate_avg_per_call
              .AccumulateSingleSample(
                  static_cast<unsigned>(mSendFramerate.Mean()));
          glean::webrtc::video_encoder_framerate_10x_std_dev_per_call
              .AccumulateSingleSample(static_cast<unsigned>(
                  mSendFramerate.StandardDeviation() * 10));
          mSendFramerate.Clear();
        }

        if (mRecvBitrate.NumDataValues() > 0) {
          glean::webrtc::video_decoder_bitrate_avg_per_call_kbps
              .AccumulateSingleSample(
                  static_cast<unsigned>(mRecvBitrate.Mean() / 1000));
          glean::webrtc::video_decoder_bitrate_std_dev_per_call_kbps
              .AccumulateSingleSample(static_cast<unsigned>(
                  mRecvBitrate.StandardDeviation() / 1000));
          mRecvBitrate.Clear();
        }
        if (mRecvFramerate.NumDataValues() > 0) {
          glean::webrtc::video_decoder_framerate_avg_per_call
              .AccumulateSingleSample(
                  static_cast<unsigned>(mRecvFramerate.Mean()));
          glean::webrtc::video_decoder_framerate_10x_std_dev_per_call
              .AccumulateSingleSample(static_cast<unsigned>(
                  mRecvFramerate.StandardDeviation() * 10));
          mRecvFramerate.Clear();
        }

        mControl.mReceiving.DisconnectIfConnected();
        mControl.mTransmitting.DisconnectIfConnected();
        mControl.mLocalSsrcs.DisconnectIfConnected();
        mControl.mLocalRtxSsrcs.DisconnectIfConnected();
        mControl.mLocalCname.DisconnectIfConnected();
        mControl.mMid.DisconnectIfConnected();
        mControl.mRemoteSsrc.DisconnectIfConnected();
        mControl.mRemoteRtxSsrc.DisconnectIfConnected();
        mControl.mSyncGroup.DisconnectIfConnected();
        mControl.mLocalRecvRtpExtensions.DisconnectIfConnected();
        mControl.mLocalSendRtpExtensions.DisconnectIfConnected();
        mControl.mSendCodec.DisconnectIfConnected();
        mControl.mSendRtpRtcpConfig.DisconnectIfConnected();
        mControl.mRecvCodecs.DisconnectIfConnected();
        mControl.mRecvRtpRtcpConfig.DisconnectIfConnected();
        mControl.mCodecMode.DisconnectIfConnected();
        mControl.mFrameTransformerProxySend.DisconnectIfConnected();
        mControl.mFrameTransformerProxyRecv.DisconnectIfConnected();
        mWatchManager.Shutdown();

        if (mTrackSource) {
          mTrackSource->RemoveSink(&mSendSinkProxy);
        }

        mCall->UnregisterConduit(this);
        mDecoderFactory->DisconnectAll();
        mEncoderFactory->DisconnectAll();
        {
          MutexAutoLock lock(mMutex);
          DeleteSendStream();
          DeleteRecvStream();
        }

        return GenericPromise::CreateAndResolve(true, __func__);
      });
}

webrtc::VideoCodecMode WebrtcVideoConduit::CodecMode() const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  return mControl.mCodecMode;
}

webrtc::DegradationPreference WebrtcVideoConduit::DegradationPreference()
    const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  if (mLockScaling || CodecMode() == webrtc::VideoCodecMode::kScreensharing) {
    return webrtc::DegradationPreference::MAINTAIN_RESOLUTION;
  }
  // Fall back to MAINTAIN_FRAMERATE by default. This is what libwebrtc/Chrome
  // uses, because BALANCED hasn't been tuned yet. See
  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/media/engine/webrtc_video_engine.cc;l=1939;drc=7c2b25f6a19cfeeea67f0f43ed33617840bab33d
  return webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
}

MediaConduitErrorCode WebrtcVideoConduit::AttachRenderer(
    RefPtr<mozilla::VideoRenderer> aVideoRenderer) {
  MOZ_ASSERT(NS_IsMainThread());

  CSFLogDebug(LOGTAG, "%s", __FUNCTION__);

  // null renderer
  if (!aVideoRenderer) {
    CSFLogError(LOGTAG, "%s NULL Renderer", __FUNCTION__);
    MOZ_ASSERT(false);
    return kMediaConduitInvalidRenderer;
  }

  // This function is called only from main, so we only need to protect against
  // modifying mRenderer while any webrtc.org code is trying to use it.
  {
    ReentrantMonitorAutoEnter enter(mRendererMonitor);
    mRenderer = aVideoRenderer;
    // Make sure the renderer knows the resolution
    mRenderer->FrameSizeChange(mReceivingWidth, mReceivingHeight);
  }

  return kMediaConduitNoError;
}

void WebrtcVideoConduit::DetachRenderer() {
  MOZ_ASSERT(NS_IsMainThread());

  ReentrantMonitorAutoEnter enter(mRendererMonitor);
  if (mRenderer) {
    mRenderer = nullptr;
  }
}

rtc::RefCountedObject<mozilla::VideoStreamFactory>*
WebrtcVideoConduit::CreateVideoStreamFactory() {
  auto videoStreamFactory = mVideoStreamFactory.Lock();
  *videoStreamFactory = new rtc::RefCountedObject<VideoStreamFactory>(
      *mCurSendCodecConfig, mMinBitrate, mStartBitrate, mPrefMaxBitrate,
      mNegotiatedMaxBitrate);
  return videoStreamFactory->get();
}

void WebrtcVideoConduit::OnSendFrame(const webrtc::VideoFrame& aFrame) {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  // XXX Google uses a "timestamp_aligner" to translate timestamps from the
  // camera via TranslateTimestamp(); we should look at doing the same.  This
  // avoids sampling error when capturing frames, but google had to deal with
  // some broken cameras, include Logitech c920's IIRC.

  const gfx::IntSize size{aFrame.width(), aFrame.height()};

  CSFLogVerbose(LOGTAG, "WebrtcVideoConduit %p %s (send SSRC %u (0x%x))"this,
                __FUNCTION__, mSendStreamConfig.rtp.ssrcs.front(),
                mSendStreamConfig.rtp.ssrcs.front());

  if (Some(size) != mLastSize) {
    MOZ_ASSERT(size != gfx::IntSize(0, 0));
    // Note coverity will flag this since it thinks they can be 0
    MOZ_ASSERT(mCurSendCodecConfig);

    {
      MutexAutoLock lock(mMutex);
      mLastSize = Some(size);
    }
  }

  MOZ_ASSERT(!aFrame.color_space(), "Unexpected use of color space");
  MOZ_ASSERT(!aFrame.has_update_rect(), "Unexpected use of update rect");

#ifdef MOZ_REAL_TIME_TRACING
  if (profiler_is_active()) {
    MutexAutoLock lock(mMutex);
    nsAutoCStringN<256> ssrcsCommaSeparated;
    bool first = true;
    for (auto ssrc : mSendStreamConfig.rtp.ssrcs) {
      if (!first) {
        ssrcsCommaSeparated.AppendASCII(", ");
      } else {
        first = false;
      }
      ssrcsCommaSeparated.AppendInt(ssrc);
    }

    const webrtc::Timestamp currentTimestamp =
        webrtc::Timestamp::Micros(aFrame.timestamp_us());
    // The first frame has a delta of zero.
    webrtc::TimeDelta timestampDelta =
        mLastTimestampSend.isSome()
            ? currentTimestamp - mLastTimestampSend.value()
            : webrtc::TimeDelta::Zero();
    mLastTimestampSend = Some(currentTimestamp);
    TRACE_COMMENT("VideoConduit::OnSendFrame (async)",
                  "t-delta=%.1fms, ssrcs=%s", timestampDelta.ms<double>(),
                  ssrcsCommaSeparated.get());
  }
#endif
}

void WebrtcVideoConduit::SetTrackSource(
    webrtc::VideoTrackSourceInterface* aSource) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ALWAYS_SUCCEEDS(mCallThread->Dispatch(NS_NewRunnableFunction(
      __func__, [this, self = RefPtr(this), src = RefPtr(aSource)] {
        if (mTrackSource) {
          mTrackSource->RemoveSink(&mSendSinkProxy);
        }
        if (src) {
          src->AddOrUpdateSink(&mSendSinkProxy, {});
        }
        mTrackSource = src;
        if (mSendStream) {
          mSendStream->SetSource(mTrackSource, DegradationPreference());
        }
      })));
}

// Transport Layer Callbacks

void WebrtcVideoConduit::DeliverPacket(rtc::CopyOnWriteBuffer packet,
                                       PacketType type) {
  // Currently unused.
  MOZ_ASSERT(false);
}

void WebrtcVideoConduit::OnRtpReceived(webrtc::RtpPacketReceived&& aPacket,
                                       webrtc::RTPHeader&& aHeader) {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());

  mRemoteSendSSRC = aHeader.ssrc;

  if (mAllowSsrcChange || mRecvStreamConfig.rtp.remote_ssrc == 0) {
    bool switchRequired = mRecvStreamConfig.rtp.remote_ssrc != aHeader.ssrc;
    if (switchRequired) {
      // Handle the unknown ssrc (and ssrc-not-signaled case).

      // We need to check that the newly received ssrc is not already
      // associated with ulpfec or rtx. This is how webrtc.org handles
      // things, see https://codereview.webrtc.org/1226093002.
      const webrtc::VideoReceiveStreamInterface::Config::Rtp& rtp =
          mRecvStreamConfig.rtp;
      switchRequired =
          rtp.rtx_associated_payload_types.find(aHeader.payloadType) ==
              rtp.rtx_associated_payload_types.end() &&
          rtp.ulpfec_payload_type != aHeader.payloadType;
    }

    if (switchRequired) {
      CSFLogInfo(LOGTAG, "VideoConduit %p: Switching remote SSRC from %u to %u",
                 this, mRecvStreamConfig.rtp.remote_ssrc, aHeader.ssrc);
      SetRemoteSSRCAndRestartAsNeeded(aHeader.ssrc, 0);
    }
  }

  CSFLogVerbose(LOGTAG, "%s: seq# %u, Len %zu, SSRC %u (0x%x) ", __FUNCTION__,
                aPacket.SequenceNumber(), aPacket.size(), aPacket.Ssrc(),
                aPacket.Ssrc());

  // Libwebrtc commit cde4b67d9d now expect calls to
  // SourceTracker::GetSources() to happen on the call thread.  We'll
  // grab the value now while on the call thread, and dispatch to main
  // to store the cached value if we have new source information.
  // See Bug 1845621.
  std::vector<webrtc::RtpSource> sources;
  if (mRecvStream) {
    sources = mRecvStream->GetSources();
  }

  bool needsCacheUpdate = false;
  {
    MutexAutoLock lock(mMutex);
    needsCacheUpdate = sources != mRtpSources;
  }

  // only dispatch to main if we have new data
  if (needsCacheUpdate) {
    GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
        __func__, [this, rtpSources = std::move(sources),
                   self = RefPtr<WebrtcVideoConduit>(this)]() {
          MutexAutoLock lock(mMutex);
          mRtpSources = rtpSources;
        }));
  }

  mRtpPacketEvent.Notify();
  if (mCall->Call()) {
    mCall->Call()->Receiver()->DeliverRtpPacket(
        webrtc::MediaType::VIDEO, std::move(aPacket),
        [self = RefPtr<WebrtcVideoConduit>(this)](
            const webrtc::RtpPacketReceived& packet) {
          CSFLogVerbose(
              LOGTAG,
              "VideoConduit %p: failed demuxing packet, ssrc: %u seq: %u",
              self.get(), packet.Ssrc(), packet.SequenceNumber());
          return false;
        });
  }
}

Maybe<uint16_t> WebrtcVideoConduit::RtpSendBaseSeqFor(uint32_t aSsrc) const {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  auto it = mRtpSendBaseSeqs.find(aSsrc);
  if (it == mRtpSendBaseSeqs.end()) {
    return Nothing();
  }
  return Some(it->second);
}

const dom::RTCStatsTimestampMaker& WebrtcVideoConduit::GetTimestampMaker()
    const {
  return mCall->GetTimestampMaker();
}

void WebrtcVideoConduit::StopTransmitting() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertNotCurrentThreadOwns();

  if (!mEngineTransmitting) {
    return;
  }

  if (mSendStream) {
    CSFLogDebug(LOGTAG, "%s Stopping send stream", __FUNCTION__);
    mSendStream->Stop();
  }

  mEngineTransmitting = false;
}

void WebrtcVideoConduit::StartTransmitting() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  MOZ_ASSERT(mSendStream);
  mMutex.AssertNotCurrentThreadOwns();

  if (mEngineTransmitting) {
    return;
  }

  CSFLogDebug(LOGTAG, "%s Starting send stream", __FUNCTION__);

  mSendStream->Start();
  // XXX File a bug to consider hooking this up to the state of mtransport
  mCall->Call()->SignalChannelNetworkState(webrtc::MediaType::VIDEO,
                                           webrtc::kNetworkUp);
  mEngineTransmitting = true;
}

void WebrtcVideoConduit::StopReceiving() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  mMutex.AssertNotCurrentThreadOwns();

  // Are we receiving already? If so, stop receiving and playout
  // since we can't apply new recv codec when the engine is playing.
  if (!mEngineReceiving) {
    return;
  }

  if (mRecvStream) {
    CSFLogDebug(LOGTAG, "%s Stopping receive stream", __FUNCTION__);
    mRecvStream->Stop();
  }

  mEngineReceiving = false;
}

void WebrtcVideoConduit::StartReceiving() {
  MOZ_ASSERT(mCallThread->IsOnCurrentThread());
  MOZ_ASSERT(mRecvStream);
  mMutex.AssertNotCurrentThreadOwns();

  if (mEngineReceiving) {
    return;
  }

  CSFLogDebug(LOGTAG, "%s Starting receive stream (SSRC %u (0x%x))",
              __FUNCTION__, mRecvStreamConfig.rtp.remote_ssrc,
              mRecvStreamConfig.rtp.remote_ssrc);
  // Start Receiving on the video engine
  mRecvStream->Start();

  // XXX File a bug to consider hooking this up to the state of mtransport
  mCall->Call()->SignalChannelNetworkState(webrtc::MediaType::VIDEO,
                                           webrtc::kNetworkUp);
  mEngineReceiving = true;
}

bool WebrtcVideoConduit::SendRtp(const uint8_t* aData, size_t aLength,
                                 const webrtc::PacketOptions& aOptions) {
  MOZ_ASSERT(aLength >= 12);
  const uint16_t seqno = ntohs(*((uint16_t*)&aData[2]));
  const uint32_t ssrc = ntohl(*((uint32_t*)&aData[8]));

  CSFLogVerbose(
      LOGTAG,
      "VideoConduit %p: Sending RTP Packet seq# %u, len %zu, SSRC %u (0x%x)",
      this, seqno, aLength, ssrc, ssrc);

  if (!mTransportActive) {
    CSFLogError(LOGTAG, "VideoConduit %p: RTP Packet Send Failed"this);
    return false;
  }

  MediaPacket packet;
  packet.Copy(aData, aLength, aLength + SRTP_MAX_EXPANSION);
  packet.SetType(MediaPacket::RTP);
  mSenderRtpSendEvent.Notify(std::move(packet));

  // Parse the sequence number of the first rtp packet as base_seq.
  const auto inserted = mRtpSendBaseSeqs_n.insert({ssrc, seqno}).second;

  if (inserted || aOptions.packet_id >= 0) {
    int64_t now_ms = PR_Now() / 1000;
    MOZ_ALWAYS_SUCCEEDS(mCallThread->Dispatch(NS_NewRunnableFunction(
        __func__, [this, self = RefPtr<WebrtcVideoConduit>(this),
                   packet_id = aOptions.packet_id, now_ms, ssrc, seqno] {
          mRtpSendBaseSeqs.insert({ssrc, seqno});
          if (packet_id >= 0) {
            if (mCall->Call()) {
              // TODO: This notification should ideally happen after the
              // transport layer has sent the packet on the wire.
              mCall->Call()->OnSentPacket({packet_id, now_ms});
            }
          }
        })));
  }
  return true;
}

bool WebrtcVideoConduit::SendSenderRtcp(const uint8_t* aData, size_t aLength) {
  CSFLogVerbose(
      LOGTAG,
      "VideoConduit %p: Sending RTCP SR Packet, len %zu, SSRC %u (0x%x)"this,
      aLength, (uint32_t)ntohl(*((uint32_t*)&aData[4])),
      (uint32_t)ntohl(*((uint32_t*)&aData[4])));

  if (!mTransportActive) {
    CSFLogError(LOGTAG, "VideoConduit %p: RTCP SR Packet Send Failed"this);
    return false;
  }

  MediaPacket packet;
  packet.Copy(aData, aLength, aLength + SRTP_MAX_EXPANSION);
  packet.SetType(MediaPacket::RTCP);
  mSenderRtcpSendEvent.Notify(std::move(packet));
  return true;
}

bool WebrtcVideoConduit::SendReceiverRtcp(const uint8_t* aData,
                                          size_t aLength) {
  CSFLogVerbose(
      LOGTAG,
      "VideoConduit %p: Sending RTCP RR Packet, len %zu, SSRC %u (0x%x)"this,
      aLength, (uint32_t)ntohl(*((uint32_t*)&aData[4])),
      (uint32_t)ntohl(*((uint32_t*)&aData[4])));

  if (!mTransportActive) {
    CSFLogError(LOGTAG, "VideoConduit %p: RTCP RR Packet Send Failed"this);
    return false;
  }

  MediaPacket packet;
  packet.Copy(aData, aLength, aLength + SRTP_MAX_EXPANSION);
  packet.SetType(MediaPacket::RTCP);
  mReceiverRtcpSendEvent.Notify(std::move(packet));
  return true;
}

void WebrtcVideoConduit::OnRecvFrame(const webrtc::VideoFrame& aFrame) {
  const uint32_t localRecvSsrc = mRecvSSRC;
  const uint32_t remoteSendSsrc = mRemoteSendSSRC;

  CSFLogVerbose(
      LOGTAG,
      "VideoConduit %p: Rendering frame, Remote SSRC %u (0x%x), size %ux%u",
      thisstatic_cast<uint32_t>(remoteSendSsrc),
      static_cast<uint32_t>(remoteSendSsrc), aFrame.width(), aFrame.height());
  ReentrantMonitorAutoEnter enter(mRendererMonitor);

  if (!mRenderer) {
    CSFLogError(LOGTAG, "VideoConduit %p: Cannot render frame, no renderer",
                this);
    return;
  }

  bool needsNewHistoryElement = mReceivedFrameHistory.mEntries.IsEmpty();

  if (mReceivingWidth != aFrame.width() ||
      mReceivingHeight != aFrame.height()) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=94 G=92

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.