Split WebRtcVideoMediaChannel into Send and Receive
This completes the split-channel work for the Video side.
Note: For ease of review, the implementations in the .cc
file have not been sorted between sender and receiver. This
can be done in a later purely-editorial CL.
Bug: webrtc:13931
Change-Id: I36cf015d5facb1eed368070cb204a8763ac19a9c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/307180
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40207}
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 9f7219d..8c7fad1 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -77,6 +77,15 @@
// needed.
constexpr char kAv1xCodecName[] = "AV1X";
+// This constant is really an on/off, lower-level configurable NACK history
+// duration hasn't been implemented.
+const int kNackHistoryMs = 1000;
+
+const int kDefaultRtcpReceiverReportSsrc = 1;
+
+// Minimum time interval for logging stats.
+const int64_t kStatsLogIntervalMs = 10000;
+
const char* StreamTypeToString(
webrtc::VideoSendStream::StreamStats::StreamType type) {
switch (type) {
@@ -502,3058 +511,12 @@
}
}
-} // namespace
-
-// This constant is really an on/off, lower-level configurable NACK history
-// duration hasn't been implemented.
-static const int kNackHistoryMs = 1000;
-
-static const int kDefaultRtcpReceiverReportSsrc = 1;
-
-// Minimum time interval for logging stats.
-static const int64_t kStatsLogIntervalMs = 10000;
-
-std::map<uint32_t, webrtc::VideoSendStream::StreamStats>
-MergeInfoAboutOutboundRtpSubstreamsForTesting(
- const std::map<uint32_t, webrtc::VideoSendStream::StreamStats>&
- substreams) {
- return MergeInfoAboutOutboundRtpSubstreams(substreams);
-}
-
-rtc::scoped_refptr<webrtc::VideoEncoderConfig::EncoderSpecificSettings>
-WebRtcVideoChannel::WebRtcVideoSendStream::ConfigureVideoEncoderSettings(
- const VideoCodec& codec) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- bool is_screencast = parameters_.options.is_screencast.value_or(false);
- // No automatic resizing when using simulcast or screencast, or when
- // disabled by field trial flag.
- bool automatic_resize = !disable_automatic_resize_ && !is_screencast &&
- (parameters_.config.rtp.ssrcs.size() == 1 ||
- NumActiveStreams(rtp_parameters_) == 1);
-
- bool denoising;
- bool codec_default_denoising = false;
- if (is_screencast) {
- denoising = false;
- } else {
- // Use codec default if video_noise_reduction is unset.
- codec_default_denoising = !parameters_.options.video_noise_reduction;
- denoising = parameters_.options.video_noise_reduction.value_or(false);
- }
-
- if (absl::EqualsIgnoreCase(codec.name, kH264CodecName)) {
- return nullptr;
- }
- if (absl::EqualsIgnoreCase(codec.name, 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::make_ref_counted<
- webrtc::VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8_settings);
- }
- if (absl::EqualsIgnoreCase(codec.name, kVp9CodecName)) {
- webrtc::VideoCodecVP9 vp9_settings =
- webrtc::VideoEncoder::GetDefaultVp9Settings();
-
- vp9_settings.numberOfSpatialLayers = std::min<unsigned char>(
- parameters_.config.rtp.ssrcs.size(), kConferenceMaxNumSpatialLayers);
- vp9_settings.numberOfTemporalLayers =
- std::min<unsigned char>(parameters_.config.rtp.ssrcs.size() > 1
- ? kConferenceDefaultNumTemporalLayers
- : 1,
- kConferenceMaxNumTemporalLayers);
-
- // VP9 denoising is disabled by default.
- vp9_settings.denoisingOn = codec_default_denoising ? true : denoising;
- // Disable automatic resize if more than one spatial layer is requested.
- bool vp9_automatic_resize = automatic_resize;
- absl::optional<int> num_spatial_layers =
- NumSpatialLayersFromEncoding(rtp_parameters_, /*idx=*/0);
- if (num_spatial_layers && *num_spatial_layers > 1) {
- vp9_automatic_resize = false;
- }
- vp9_settings.automaticResizeOn = vp9_automatic_resize;
- if (!is_screencast) {
- webrtc::FieldTrialFlag interlayer_pred_experiment_enabled("Enabled");
- webrtc::FieldTrialEnum<webrtc::InterLayerPredMode> inter_layer_pred_mode(
- "inter_layer_pred_mode", webrtc::InterLayerPredMode::kOnKeyPic,
- {{"off", webrtc::InterLayerPredMode::kOff},
- {"on", webrtc::InterLayerPredMode::kOn},
- {"onkeypic", webrtc::InterLayerPredMode::kOnKeyPic}});
- webrtc::FieldTrialFlag force_flexible_mode("FlexibleMode");
- webrtc::ParseFieldTrial(
- {&interlayer_pred_experiment_enabled, &inter_layer_pred_mode,
- &force_flexible_mode},
- call_->trials().Lookup("WebRTC-Vp9InterLayerPred"));
- if (interlayer_pred_experiment_enabled) {
- vp9_settings.interLayerPred = inter_layer_pred_mode;
- } else {
- // Limit inter-layer prediction to key pictures by default.
- vp9_settings.interLayerPred = webrtc::InterLayerPredMode::kOnKeyPic;
- }
- vp9_settings.flexibleMode = force_flexible_mode.Get();
- } else {
- // Multiple spatial layers vp9 screenshare needs flexible mode.
- vp9_settings.flexibleMode = vp9_settings.numberOfSpatialLayers > 1;
- vp9_settings.interLayerPred = webrtc::InterLayerPredMode::kOn;
- }
- return rtc::make_ref_counted<
- webrtc::VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9_settings);
- }
- return nullptr;
-}
-
-WebRtcVideoEngine::WebRtcVideoEngine(
- std::unique_ptr<webrtc::VideoEncoderFactory> video_encoder_factory,
- std::unique_ptr<webrtc::VideoDecoderFactory> video_decoder_factory,
- const webrtc::FieldTrialsView& trials)
- : decoder_factory_(std::move(video_decoder_factory)),
- encoder_factory_(std::move(video_encoder_factory)),
- trials_(trials) {
- RTC_DLOG(LS_INFO) << "WebRtcVideoEngine::WebRtcVideoEngine()";
-}
-
-WebRtcVideoEngine::~WebRtcVideoEngine() {
- RTC_DLOG(LS_INFO) << "WebRtcVideoEngine::~WebRtcVideoEngine";
-}
-
-VideoMediaChannel* WebRtcVideoEngine::CreateMediaChannel(
- MediaChannel::Role role,
- webrtc::Call* call,
- const MediaConfig& config,
- const VideoOptions& options,
- const webrtc::CryptoOptions& crypto_options,
- webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) {
- RTC_LOG(LS_INFO) << "CreateMediaChannel. Options: " << options.ToString();
- std::unique_ptr<VideoMediaSendChannelInterface> send_channel;
- std::unique_ptr<VideoMediaReceiveChannelInterface> receive_channel;
- if (role == MediaChannel::Role::kSend || role == MediaChannel::Role::kBoth) {
- send_channel = std::make_unique<WebRtcVideoChannel>(
- MediaChannel::Role::kSend, call, config, options, crypto_options,
- encoder_factory_.get(), decoder_factory_.get(),
- video_bitrate_allocator_factory);
- }
- if (role == MediaChannel::Role::kReceive ||
- role == MediaChannel::Role::kBoth) {
- receive_channel = std::make_unique<WebRtcVideoChannel>(
- MediaChannel::Role::kReceive, call, config, options, crypto_options,
- encoder_factory_.get(), decoder_factory_.get(),
- video_bitrate_allocator_factory);
- }
- return new VideoMediaShimChannel(std::move(send_channel),
- std::move(receive_channel));
-}
-
-std::vector<VideoCodec> WebRtcVideoEngine::send_codecs(bool include_rtx) const {
- return GetPayloadTypesAndDefaultCodecs(encoder_factory_.get(),
- /*is_decoder_factory=*/false,
- include_rtx, trials_);
-}
-
-std::vector<VideoCodec> WebRtcVideoEngine::recv_codecs(bool include_rtx) const {
- return GetPayloadTypesAndDefaultCodecs(decoder_factory_.get(),
- /*is_decoder_factory=*/true,
- include_rtx, trials_);
-}
-
-std::vector<webrtc::RtpHeaderExtensionCapability>
-WebRtcVideoEngine::GetRtpHeaderExtensions() const {
- std::vector<webrtc::RtpHeaderExtensionCapability> result;
- int id = 1;
- for (const auto& uri :
- {webrtc::RtpExtension::kTimestampOffsetUri,
- webrtc::RtpExtension::kAbsSendTimeUri,
- webrtc::RtpExtension::kVideoRotationUri,
- webrtc::RtpExtension::kTransportSequenceNumberUri,
- webrtc::RtpExtension::kPlayoutDelayUri,
- webrtc::RtpExtension::kVideoContentTypeUri,
- webrtc::RtpExtension::kVideoTimingUri,
- webrtc::RtpExtension::kColorSpaceUri, webrtc::RtpExtension::kMidUri,
- webrtc::RtpExtension::kRidUri, webrtc::RtpExtension::kRepairedRidUri}) {
- result.emplace_back(uri, id++, webrtc::RtpTransceiverDirection::kSendRecv);
- }
- result.emplace_back(webrtc::RtpExtension::kGenericFrameDescriptorUri00, id++,
- IsEnabled(trials_, "WebRTC-GenericDescriptorAdvertised")
- ? webrtc::RtpTransceiverDirection::kSendRecv
- : webrtc::RtpTransceiverDirection::kStopped);
- result.emplace_back(
- webrtc::RtpExtension::kDependencyDescriptorUri, id++,
- IsEnabled(trials_, "WebRTC-DependencyDescriptorAdvertised")
- ? webrtc::RtpTransceiverDirection::kSendRecv
- : webrtc::RtpTransceiverDirection::kStopped);
-
- result.emplace_back(
- webrtc::RtpExtension::kVideoLayersAllocationUri, id++,
- IsEnabled(trials_, "WebRTC-VideoLayersAllocationAdvertised")
- ? webrtc::RtpTransceiverDirection::kSendRecv
- : webrtc::RtpTransceiverDirection::kStopped);
-
- result.emplace_back(
- webrtc::RtpExtension::kVideoFrameTrackingIdUri, id++,
- IsEnabled(trials_, "WebRTC-VideoFrameTrackingIdAdvertised")
- ? webrtc::RtpTransceiverDirection::kSendRecv
- : webrtc::RtpTransceiverDirection::kStopped);
-
- return result;
-}
-
-WebRtcVideoChannel::WebRtcVideoChannel(
- MediaChannel::Role role,
- webrtc::Call* call,
- const MediaConfig& config,
- const VideoOptions& options,
- const webrtc::CryptoOptions& crypto_options,
- webrtc::VideoEncoderFactory* encoder_factory,
- webrtc::VideoDecoderFactory* decoder_factory,
- webrtc::VideoBitrateAllocatorFactory* bitrate_allocator_factory)
- : VideoMediaChannel(role, call->network_thread(), config.enable_dscp),
- worker_thread_(call->worker_thread()),
- sending_(false),
- receiving_(false),
- call_(call),
- default_sink_(nullptr),
- video_config_(config.video),
- encoder_factory_(encoder_factory),
- decoder_factory_(decoder_factory),
- bitrate_allocator_factory_(bitrate_allocator_factory),
- default_send_options_(options),
- last_send_stats_log_ms_(-1),
- last_receive_stats_log_ms_(-1),
- discard_unknown_ssrc_packets_(
- IsEnabled(call_->trials(),
- "WebRTC-Video-DiscardPacketsWithUnknownSsrc")),
- crypto_options_(crypto_options) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc;
- recv_codecs_ = MapCodecs(GetPayloadTypesAndDefaultCodecs(
- decoder_factory_, /*is_decoder_factory=*/true,
- /*include_rtx=*/true, call_->trials()));
- recv_flexfec_payload_type_ =
- recv_codecs_.empty() ? 0 : recv_codecs_.front().flexfec_payload_type;
-}
-
-WebRtcVideoChannel::~WebRtcVideoChannel() {
- for (auto& kv : send_streams_)
- delete kv.second;
- for (auto& kv : receive_streams_)
- delete kv.second;
-}
-
-std::vector<WebRtcVideoChannel::VideoCodecSettings>
-WebRtcVideoChannel::SelectSendVideoCodecs(
- const std::vector<VideoCodecSettings>& remote_mapped_codecs) const {
- std::vector<webrtc::SdpVideoFormat> sdp_formats =
- encoder_factory_ ? encoder_factory_->GetImplementations()
- : std::vector<webrtc::SdpVideoFormat>();
-
- // The returned vector holds the VideoCodecSettings in term of preference.
- // They are orderd by receive codec preference first and local implementation
- // preference second.
- std::vector<VideoCodecSettings> encoders;
- for (const VideoCodecSettings& remote_codec : remote_mapped_codecs) {
- for (auto format_it = sdp_formats.begin();
- format_it != sdp_formats.end();) {
- // For H264, we will limit the encode level to the remote offered level
- // regardless if level asymmetry is allowed or not. This is strictly not
- // following the spec in https://tools.ietf.org/html/rfc6184#section-8.2.2
- // since we should limit the encode level to the lower of local and remote
- // level when level asymmetry is not allowed.
- if (format_it->IsSameCodec(
- {remote_codec.codec.name, remote_codec.codec.params})) {
- encoders.push_back(remote_codec);
-
- // To allow the VideoEncoderFactory to keep information about which
- // implementation to instantitate when CreateEncoder is called the two
- // parmeter sets are merged.
- encoders.back().codec.params.insert(format_it->parameters.begin(),
- format_it->parameters.end());
-
- format_it = sdp_formats.erase(format_it);
- } else {
- ++format_it;
- }
- }
- }
-
- return encoders;
-}
-
-bool WebRtcVideoChannel::NonFlexfecReceiveCodecsHaveChanged(
- std::vector<VideoCodecSettings> before,
- std::vector<VideoCodecSettings> after) {
- // The receive codec order doesn't matter, so we sort the codecs before
- // comparing. This is necessary because currently the
- // only way to change the send codec is to munge SDP, which causes
- // the receive codec list to change order, which causes the streams
- // to be recreates which causes a "blink" of black video. In order
- // to support munging the SDP in this way without recreating receive
- // streams, we ignore the order of the received codecs so that
- // changing the order doesn't cause this "blink".
- auto comparison = [](const VideoCodecSettings& codec1,
- const VideoCodecSettings& codec2) {
- return codec1.codec.id > codec2.codec.id;
- };
- absl::c_sort(before, comparison);
- absl::c_sort(after, comparison);
-
- // Changes in FlexFEC payload type are handled separately in
- // WebRtcVideoChannel::GetChangedRecvParameters, so disregard FlexFEC in the
- // comparison here.
- return !absl::c_equal(before, after,
- VideoCodecSettings::EqualsDisregardingFlexfec);
-}
-
-bool WebRtcVideoChannel::GetChangedSendParameters(
- const VideoSendParameters& params,
- ChangedSendParameters* changed_params) const {
- if (!ValidateCodecFormats(params.codecs) ||
- !ValidateRtpExtensions(params.extensions, send_rtp_extensions_)) {
- return false;
- }
-
- std::vector<VideoCodecSettings> negotiated_codecs =
- SelectSendVideoCodecs(MapCodecs(params.codecs));
-
- // We should only fail here if send direction is enabled.
- if (params.is_stream_active && negotiated_codecs.empty()) {
- RTC_LOG(LS_ERROR) << "No video codecs supported.";
- return false;
- }
-
- // Never enable sending FlexFEC, unless we are in the experiment.
- if (!IsEnabled(call_->trials(), "WebRTC-FlexFEC-03")) {
- for (VideoCodecSettings& codec : negotiated_codecs)
- codec.flexfec_payload_type = -1;
- }
-
- if (negotiated_codecs_ != negotiated_codecs) {
- if (negotiated_codecs.empty()) {
- changed_params->send_codec = absl::nullopt;
- } else if (send_codec() != negotiated_codecs.front()) {
- changed_params->send_codec = negotiated_codecs.front();
- }
- changed_params->negotiated_codecs = std::move(negotiated_codecs);
- }
-
- // Handle RTP header extensions.
- if (params.extmap_allow_mixed != ExtmapAllowMixed()) {
- changed_params->extmap_allow_mixed = params.extmap_allow_mixed;
- }
- std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(
- params.extensions, webrtc::RtpExtension::IsSupportedForVideo, true,
- call_->trials());
- if (send_rtp_extensions_ != filtered_extensions) {
- changed_params->rtp_header_extensions =
- absl::optional<std::vector<webrtc::RtpExtension>>(filtered_extensions);
- }
-
- if (params.mid != send_params_.mid) {
- changed_params->mid = params.mid;
- }
-
- // Handle max bitrate.
- if (params.max_bandwidth_bps != send_params_.max_bandwidth_bps &&
- params.max_bandwidth_bps >= -1) {
- // 0 or -1 uncaps max bitrate.
- // TODO(pbos): Reconsider how 0 should be treated. It is not mentioned as a
- // special value and might very well be used for stopping sending.
- changed_params->max_bandwidth_bps =
- params.max_bandwidth_bps == 0 ? -1 : params.max_bandwidth_bps;
- }
-
- // Handle conference mode.
- if (params.conference_mode != send_params_.conference_mode) {
- changed_params->conference_mode = params.conference_mode;
- }
-
- // Handle RTCP mode.
- if (params.rtcp.reduced_size != send_params_.rtcp.reduced_size) {
- changed_params->rtcp_mode = params.rtcp.reduced_size
- ? webrtc::RtcpMode::kReducedSize
- : webrtc::RtcpMode::kCompound;
- }
-
- return true;
-}
-
-bool WebRtcVideoChannel::SetSendParameters(const VideoSendParameters& params) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetSendParameters");
- RTC_LOG(LS_INFO) << "SetSendParameters: " << params.ToString();
- ChangedSendParameters changed_params;
- if (!GetChangedSendParameters(params, &changed_params)) {
- return false;
- }
-
- if (changed_params.negotiated_codecs) {
- for (const auto& send_codec : *changed_params.negotiated_codecs)
- RTC_LOG(LS_INFO) << "Negotiated codec: " << send_codec.codec.ToString();
- }
-
- send_params_ = params;
- return ApplyChangedParams(changed_params);
-}
-
-void WebRtcVideoChannel::RequestEncoderFallback() {
- if (!worker_thread_->IsCurrent()) {
- worker_thread_->PostTask(
- SafeTask(task_safety_.flag(), [this] { RequestEncoderFallback(); }));
- return;
- }
-
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (negotiated_codecs_.size() <= 1) {
- RTC_LOG(LS_WARNING) << "Encoder failed but no fallback codec is available";
- return;
- }
-
- ChangedSendParameters params;
- params.negotiated_codecs = negotiated_codecs_;
- params.negotiated_codecs->erase(params.negotiated_codecs->begin());
- params.send_codec = params.negotiated_codecs->front();
- ApplyChangedParams(params);
-}
-
-void WebRtcVideoChannel::RequestEncoderSwitch(
- const webrtc::SdpVideoFormat& format,
- bool allow_default_fallback) {
- if (!worker_thread_->IsCurrent()) {
- worker_thread_->PostTask(
- SafeTask(task_safety_.flag(), [this, format, allow_default_fallback] {
- RequestEncoderSwitch(format, allow_default_fallback);
- }));
- return;
- }
-
- RTC_DCHECK_RUN_ON(&thread_checker_);
-
- for (const VideoCodecSettings& codec_setting : negotiated_codecs_) {
- if (format.IsSameCodec(
- {codec_setting.codec.name, codec_setting.codec.params})) {
- VideoCodecSettings new_codec_setting = codec_setting;
- for (const auto& kv : format.parameters) {
- new_codec_setting.codec.params[kv.first] = kv.second;
- }
-
- if (send_codec() == new_codec_setting) {
- // Already using this codec, no switch required.
- return;
- }
-
- ChangedSendParameters params;
- params.send_codec = new_codec_setting;
- ApplyChangedParams(params);
- return;
- }
- }
-
- RTC_LOG(LS_WARNING) << "Failed to switch encoder to: " << format.ToString()
- << ". Is default fallback allowed: "
- << allow_default_fallback;
-
- if (allow_default_fallback) {
- RequestEncoderFallback();
- }
-}
-
-bool WebRtcVideoChannel::ApplyChangedParams(
- const ChangedSendParameters& changed_params) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (changed_params.negotiated_codecs)
- negotiated_codecs_ = *changed_params.negotiated_codecs;
-
- if (changed_params.send_codec)
- send_codec() = changed_params.send_codec;
-
- if (changed_params.extmap_allow_mixed) {
- SetExtmapAllowMixed(*changed_params.extmap_allow_mixed);
- }
- if (changed_params.rtp_header_extensions) {
- send_rtp_extensions_ = *changed_params.rtp_header_extensions;
- }
-
- if (changed_params.send_codec || changed_params.max_bandwidth_bps) {
- if (send_params_.max_bandwidth_bps == -1) {
- // Unset the global max bitrate (max_bitrate_bps) if max_bandwidth_bps is
- // -1, which corresponds to no "b=AS" attribute in SDP. Note that the
- // global max bitrate may be set below in GetBitrateConfigForCodec, from
- // the codec max bitrate.
- // TODO(pbos): This should be reconsidered (codec max bitrate should
- // probably not affect global call max bitrate).
- bitrate_config_.max_bitrate_bps = -1;
- }
-
- if (send_codec()) {
- // TODO(holmer): Changing the codec parameters shouldn't necessarily mean
- // that we change the min/max of bandwidth estimation. Reevaluate this.
- bitrate_config_ = GetBitrateConfigForCodec(send_codec()->codec);
- if (!changed_params.send_codec) {
- // If the codec isn't changing, set the start bitrate to -1 which means
- // "unchanged" so that BWE isn't affected.
- bitrate_config_.start_bitrate_bps = -1;
- }
- }
-
- if (send_params_.max_bandwidth_bps >= 0) {
- // Note that max_bandwidth_bps intentionally takes priority over the
- // bitrate config for the codec. This allows FEC to be applied above the
- // codec target bitrate.
- // TODO(pbos): Figure out whether b=AS means max bitrate for this
- // WebRtcVideoChannel (in which case we're good), or per sender (SSRC),
- // in which case this should not set a BitrateConstraints but rather
- // reconfigure all senders.
- bitrate_config_.max_bitrate_bps = send_params_.max_bandwidth_bps == 0
- ? -1
- : send_params_.max_bandwidth_bps;
- }
-
- call_->GetTransportControllerSend()->SetSdpBitrateParameters(
- bitrate_config_);
- }
-
- for (auto& kv : send_streams_) {
- kv.second->SetSendParameters(changed_params);
- }
- if (role() == MediaChannel::Role::kBoth) {
- if (changed_params.send_codec || changed_params.rtcp_mode) {
- // Update receive feedback parameters from new codec or RTCP mode.
- if (send_codec()) {
- SetReceiverFeedbackParameters(
- HasLntf(send_codec()->codec), HasNack(send_codec()->codec),
- send_params_.rtcp.reduced_size ? webrtc::RtcpMode::kReducedSize
- : webrtc::RtcpMode::kCompound,
- send_codec()->rtx_time);
- }
- }
- } else {
- if (changed_params.send_codec || changed_params.rtcp_mode) {
- if (send_codec_changed_callback_) {
- send_codec_changed_callback_();
- }
- }
- }
- return true;
-}
-
-void WebRtcVideoChannel::SetReceiverFeedbackParameters(
- bool lntf_enabled,
- bool nack_enabled,
- webrtc::RtcpMode rtcp_mode,
- absl::optional<int> rtx_time) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
-
- RTC_DCHECK(role() == MediaChannel::Role::kReceive ||
- role() == MediaChannel::Role::kBoth);
- // Update receive feedback parameters from new codec or RTCP mode.
- for (auto& kv : receive_streams_) {
- RTC_DCHECK(kv.second != nullptr);
- kv.second->SetFeedbackParameters(lntf_enabled, nack_enabled, rtcp_mode,
- rtx_time);
- }
- // Store for future creation of receive streams
- rtp_config_.lntf.enabled = lntf_enabled;
- if (nack_enabled) {
- rtp_config_.nack.rtp_history_ms = kNackHistoryMs;
- } else {
- rtp_config_.nack.rtp_history_ms = 0;
- }
- rtp_config_.rtcp_mode = rtcp_mode;
- // Note: There is no place in config to store rtx_time.
-}
-
-webrtc::RtpParameters WebRtcVideoChannel::GetRtpSendParameters(
- uint32_t ssrc) const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- auto it = send_streams_.find(ssrc);
- if (it == send_streams_.end()) {
- RTC_LOG(LS_WARNING) << "Attempting to get RTP send parameters for stream "
- "with ssrc "
- << ssrc << " which doesn't exist.";
- return webrtc::RtpParameters();
- }
-
- webrtc::RtpParameters rtp_params = it->second->GetRtpParameters();
- // Need to add the common list of codecs to the send stream-specific
- // RTP parameters.
- for (const VideoCodec& codec : send_params_.codecs) {
- if (send_codec() && send_codec()->codec.id == codec.id) {
- // Put the current send codec to the front of the codecs list.
- RTC_DCHECK_EQ(codec.name, send_codec()->codec.name);
- rtp_params.codecs.insert(rtp_params.codecs.begin(),
- codec.ToCodecParameters());
- } else {
- rtp_params.codecs.push_back(codec.ToCodecParameters());
- }
- }
-
- return rtp_params;
-}
-
-webrtc::RTCError WebRtcVideoChannel::SetRtpSendParameters(
- uint32_t ssrc,
- const webrtc::RtpParameters& parameters,
- webrtc::SetParametersCallback callback) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetRtpSendParameters");
- auto it = send_streams_.find(ssrc);
- if (it == send_streams_.end()) {
- RTC_LOG(LS_ERROR) << "Attempting to set RTP send parameters for stream "
- "with ssrc "
- << ssrc << " which doesn't exist.";
- return webrtc::InvokeSetParametersCallback(
- callback, webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR));
- }
-
- // TODO(deadbeef): Handle setting parameters with a list of codecs in a
- // different order (which should change the send codec).
- webrtc::RtpParameters current_parameters = GetRtpSendParameters(ssrc);
- if (current_parameters.codecs != parameters.codecs) {
- RTC_DLOG(LS_ERROR) << "Using SetParameters to change the set of codecs "
- "is not currently supported.";
- return webrtc::InvokeSetParametersCallback(
- callback, webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR));
- }
-
- if (!parameters.encodings.empty()) {
- // Note that these values come from:
- // https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-16#section-5
- // TODO(deadbeef): Change values depending on whether we are sending a
- // keyframe or non-keyframe.
- rtc::DiffServCodePoint new_dscp = rtc::DSCP_DEFAULT;
- switch (parameters.encodings[0].network_priority) {
- case webrtc::Priority::kVeryLow:
- new_dscp = rtc::DSCP_CS1;
- break;
- case webrtc::Priority::kLow:
- new_dscp = rtc::DSCP_DEFAULT;
- break;
- case webrtc::Priority::kMedium:
- new_dscp = rtc::DSCP_AF42;
- break;
- case webrtc::Priority::kHigh:
- new_dscp = rtc::DSCP_AF41;
- break;
- }
- SetPreferredDscp(new_dscp);
- }
-
- return it->second->SetRtpParameters(parameters, std::move(callback));
-}
-
-webrtc::RtpParameters WebRtcVideoChannel::GetRtpReceiveParameters(
- uint32_t ssrc) const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- webrtc::RtpParameters rtp_params;
- auto it = receive_streams_.find(ssrc);
- if (it == receive_streams_.end()) {
- RTC_LOG(LS_WARNING)
- << "Attempting to get RTP receive parameters for stream "
- "with SSRC "
- << ssrc << " which doesn't exist.";
- return webrtc::RtpParameters();
- }
- rtp_params = it->second->GetRtpParameters();
- rtp_params.header_extensions = recv_rtp_extensions_;
-
- // Add codecs, which any stream is prepared to receive.
- for (const VideoCodec& codec : recv_params_.codecs) {
- rtp_params.codecs.push_back(codec.ToCodecParameters());
- }
-
- return rtp_params;
-}
-
-webrtc::RtpParameters WebRtcVideoChannel::GetDefaultRtpReceiveParameters()
- const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- webrtc::RtpParameters rtp_params;
- if (!default_sink_) {
- // Getting parameters on a default, unsignaled video receive stream but
- // because we've not configured to receive such a stream, `encodings` is
- // empty.
- return rtp_params;
- }
- rtp_params.encodings.emplace_back();
-
- // Add codecs, which any stream is prepared to receive.
- for (const VideoCodec& codec : recv_params_.codecs) {
- rtp_params.codecs.push_back(codec.ToCodecParameters());
- }
-
- return rtp_params;
-}
-
-bool WebRtcVideoChannel::GetChangedRecvParameters(
- const VideoRecvParameters& params,
- ChangedRecvParameters* changed_params) const {
- if (!ValidateCodecFormats(params.codecs) ||
- !ValidateRtpExtensions(params.extensions, recv_rtp_extensions_)) {
- return false;
- }
-
- // Handle receive codecs.
- const std::vector<VideoCodecSettings> mapped_codecs =
- MapCodecs(params.codecs);
- if (mapped_codecs.empty()) {
- RTC_LOG(LS_ERROR)
- << "GetChangedRecvParameters called without any video codecs.";
- return false;
- }
-
- // Verify that every mapped codec is supported locally.
- if (params.is_stream_active) {
- const std::vector<VideoCodec> local_supported_codecs =
- GetPayloadTypesAndDefaultCodecs(decoder_factory_,
- /*is_decoder_factory=*/true,
- /*include_rtx=*/true, call_->trials());
- for (const VideoCodecSettings& mapped_codec : mapped_codecs) {
- if (!FindMatchingCodec(local_supported_codecs, mapped_codec.codec)) {
- RTC_LOG(LS_ERROR)
- << "GetChangedRecvParameters called with unsupported video codec: "
- << mapped_codec.codec.ToString();
- return false;
- }
- }
- }
-
- if (NonFlexfecReceiveCodecsHaveChanged(recv_codecs_, mapped_codecs)) {
- changed_params->codec_settings =
- absl::optional<std::vector<VideoCodecSettings>>(mapped_codecs);
- }
-
- // Handle RTP header extensions.
- std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(
- params.extensions, webrtc::RtpExtension::IsSupportedForVideo, false,
- call_->trials());
- if (filtered_extensions != recv_rtp_extensions_) {
- changed_params->rtp_header_extensions =
- absl::optional<std::vector<webrtc::RtpExtension>>(filtered_extensions);
- }
-
- int flexfec_payload_type = mapped_codecs.front().flexfec_payload_type;
- if (flexfec_payload_type != recv_flexfec_payload_type_) {
- changed_params->flexfec_payload_type = flexfec_payload_type;
- }
-
- return true;
-}
-
-bool WebRtcVideoChannel::SetRecvParameters(const VideoRecvParameters& params) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetRecvParameters");
- RTC_LOG(LS_INFO) << "SetRecvParameters: " << params.ToString();
- ChangedRecvParameters changed_params;
- if (!GetChangedRecvParameters(params, &changed_params)) {
- return false;
- }
- if (changed_params.flexfec_payload_type) {
- RTC_DLOG(LS_INFO) << "Changing FlexFEC payload type (recv) from "
- << recv_flexfec_payload_type_ << " to "
- << *changed_params.flexfec_payload_type;
- recv_flexfec_payload_type_ = *changed_params.flexfec_payload_type;
- }
- if (changed_params.rtp_header_extensions) {
- recv_rtp_extensions_ = *changed_params.rtp_header_extensions;
- recv_rtp_extension_map_ =
- webrtc::RtpHeaderExtensionMap(recv_rtp_extensions_);
- }
- if (changed_params.codec_settings) {
- RTC_DLOG(LS_INFO) << "Changing recv codecs from "
- << CodecSettingsVectorToString(recv_codecs_) << " to "
- << CodecSettingsVectorToString(
- *changed_params.codec_settings);
- recv_codecs_ = *changed_params.codec_settings;
- }
-
- for (auto& kv : receive_streams_) {
- kv.second->SetRecvParameters(changed_params);
- }
- recv_params_ = params;
- return true;
-}
-
-std::string WebRtcVideoChannel::CodecSettingsVectorToString(
- const std::vector<VideoCodecSettings>& codecs) {
- rtc::StringBuilder out;
- out << "{";
- for (size_t i = 0; i < codecs.size(); ++i) {
- out << codecs[i].codec.ToString();
- if (i != codecs.size() - 1) {
- out << ", ";
- }
- }
- out << "}";
- return out.Release();
-}
-
-void WebRtcVideoChannel::ExtractCodecInformation(
- rtc::ArrayView<const VideoCodecSettings> recv_codecs,
- std::map<int, int>& rtx_associated_payload_types,
- std::set<int>& raw_payload_types,
- std::vector<webrtc::VideoReceiveStreamInterface::Decoder>& decoders) {
- RTC_DCHECK(!recv_codecs.empty());
- RTC_DCHECK(rtx_associated_payload_types.empty());
- RTC_DCHECK(raw_payload_types.empty());
- RTC_DCHECK(decoders.empty());
-
- for (const VideoCodecSettings& recv_codec : recv_codecs) {
- decoders.emplace_back(
- webrtc::SdpVideoFormat(recv_codec.codec.name, recv_codec.codec.params),
- recv_codec.codec.id);
- rtx_associated_payload_types.emplace(recv_codec.rtx_payload_type,
- recv_codec.codec.id);
- if (recv_codec.codec.packetization == kPacketizationParamRaw) {
- raw_payload_types.insert(recv_codec.codec.id);
- }
- }
-}
-
-void WebRtcVideoChannel::SetReceiverReportSsrc(uint32_t ssrc) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (ssrc == rtcp_receiver_report_ssrc_)
- return;
-
- rtcp_receiver_report_ssrc_ = ssrc;
- for (auto& [unused, receive_stream] : receive_streams_)
- receive_stream->SetLocalSsrc(ssrc);
-}
-
-void WebRtcVideoChannel::ChooseReceiverReportSsrc(
- const std::set<uint32_t>& choices) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- // If we can continue using the current receiver report, do so.
- if (choices.find(rtcp_receiver_report_ssrc_) != choices.end()) {
- return;
- }
- // Go back to the default if list has been emptied.
- if (choices.empty()) {
- SetReceiverReportSsrc(kDefaultRtcpReceiverReportSsrc);
- return;
- }
- // Any number is as good as any other.
- SetReceiverReportSsrc(*choices.begin());
-}
-
-bool WebRtcVideoChannel::GetSendCodec(VideoCodec* codec) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (!send_codec()) {
- RTC_LOG(LS_VERBOSE) << "GetSendCodec: No send codec set.";
- return false;
- }
- *codec = send_codec()->codec;
- return true;
-}
-
-void WebRtcVideoChannel::SetReceive(bool receive) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetReceive");
- RTC_LOG(LS_VERBOSE) << "SetReceive: " << (receive ? "true" : "false");
- for (const auto& kv : receive_streams_) {
- if (receive) {
- kv.second->StartReceiveStream();
- } else {
- kv.second->StopReceiveStream();
- }
- }
- receiving_ = receive;
-}
-
-bool WebRtcVideoChannel::SetSend(bool send) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetSend");
- RTC_LOG(LS_VERBOSE) << "SetSend: " << (send ? "true" : "false");
- if (send && !send_codec()) {
- RTC_DLOG(LS_ERROR) << "SetSend(true) called before setting codec.";
- return false;
- }
- for (const auto& kv : send_streams_) {
- kv.second->SetSend(send);
- }
- sending_ = send;
- return true;
-}
-
-bool WebRtcVideoChannel::SetVideoSend(
- uint32_t ssrc,
- const VideoOptions* options,
- rtc::VideoSourceInterface<webrtc::VideoFrame>* source) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- TRACE_EVENT0("webrtc", "SetVideoSend");
- RTC_DCHECK(ssrc != 0);
- RTC_LOG(LS_INFO) << "SetVideoSend (ssrc= " << ssrc << ", options: "
- << (options ? options->ToString() : "nullptr")
- << ", source = " << (source ? "(source)" : "nullptr") << ")";
-
- const auto& kv = send_streams_.find(ssrc);
- if (kv == send_streams_.end()) {
- // Allow unknown ssrc only if source is null.
- RTC_CHECK(source == nullptr);
- RTC_LOG(LS_ERROR) << "No sending stream on ssrc " << ssrc;
- return false;
- }
-
- return kv->second->SetVideoSend(options, source);
-}
-
-bool WebRtcVideoChannel::ValidateSendSsrcAvailability(
- const StreamParams& sp) const {
- for (uint32_t ssrc : sp.ssrcs) {
- if (send_ssrcs_.find(ssrc) != send_ssrcs_.end()) {
- RTC_LOG(LS_ERROR) << "Send stream with SSRC '" << ssrc
- << "' already exists.";
- return false;
- }
- }
- return true;
-}
-
-bool WebRtcVideoChannel::ValidateReceiveSsrcAvailability(
- const StreamParams& sp) const {
- for (uint32_t ssrc : sp.ssrcs) {
- if (receive_ssrcs_.find(ssrc) != receive_ssrcs_.end()) {
- RTC_LOG(LS_ERROR) << "Receive stream with SSRC '" << ssrc
- << "' already exists.";
- return false;
- }
- }
- return true;
-}
-
-bool WebRtcVideoChannel::AddSendStream(const StreamParams& sp) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- RTC_DCHECK(role() == MediaChannel::Role::kSend ||
- role() == MediaChannel::Role::kBoth);
-
- RTC_LOG(LS_INFO) << "AddSendStream: " << sp.ToString();
- if (!ValidateStreamParams(sp))
- return false;
-
- if (!ValidateSendSsrcAvailability(sp))
- return false;
-
- for (uint32_t used_ssrc : sp.ssrcs)
- send_ssrcs_.insert(used_ssrc);
-
- webrtc::VideoSendStream::Config config(this);
-
- for (const RidDescription& rid : sp.rids()) {
- config.rtp.rids.push_back(rid.rid);
- }
-
- config.suspend_below_min_bitrate = video_config_.suspend_below_min_bitrate;
- config.periodic_alr_bandwidth_probing =
- video_config_.periodic_alr_bandwidth_probing;
- config.encoder_settings.experiment_cpu_load_estimator =
- video_config_.experiment_cpu_load_estimator;
- config.encoder_settings.encoder_factory = encoder_factory_;
- config.encoder_settings.bitrate_allocator_factory =
- bitrate_allocator_factory_;
- config.encoder_settings.encoder_switch_request_callback = this;
- config.crypto_options = crypto_options_;
- config.rtp.extmap_allow_mixed = ExtmapAllowMixed();
- config.rtcp_report_interval_ms = video_config_.rtcp_report_interval_ms;
- config.rtp.enable_send_packet_batching =
- video_config_.enable_send_packet_batching;
-
- WebRtcVideoSendStream* stream = new WebRtcVideoSendStream(
- call_, sp, std::move(config), default_send_options_,
- video_config_.enable_cpu_adaptation, bitrate_config_.max_bitrate_bps,
- send_codec(), send_rtp_extensions_, send_params_);
-
- uint32_t ssrc = sp.first_ssrc();
- RTC_DCHECK(ssrc != 0);
- send_streams_[ssrc] = stream;
-
- // If legacy kBoth mode, tell my receiver part about its SSRC.
- // In kSend mode, this is the responsibility of the caller.
- if (role() == MediaChannel::Role::kBoth) {
- ChooseReceiverReportSsrc(send_ssrcs_);
- } else {
- if (ssrc_list_changed_callback_) {
- ssrc_list_changed_callback_(send_ssrcs_);
- }
- }
-
- if (sending_) {
- stream->SetSend(true);
- }
-
- return true;
-}
-
-bool WebRtcVideoChannel::RemoveSendStream(uint32_t ssrc) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- RTC_LOG(LS_INFO) << "RemoveSendStream: " << ssrc;
-
- WebRtcVideoSendStream* removed_stream;
- auto it = send_streams_.find(ssrc);
- if (it == send_streams_.end()) {
- return false;
- }
-
- for (uint32_t old_ssrc : it->second->GetSsrcs())
- send_ssrcs_.erase(old_ssrc);
-
- removed_stream = it->second;
- send_streams_.erase(it);
-
- // Switch receiver report SSRCs, the one in use is no longer valid.
- if (role() == MediaChannel::Role::kBoth) {
- ChooseReceiverReportSsrc(send_ssrcs_);
- } else {
- if (ssrc_list_changed_callback_) {
- ssrc_list_changed_callback_(send_ssrcs_);
- }
- }
-
- delete removed_stream;
-
- return true;
-}
-
-void WebRtcVideoChannel::DeleteReceiveStream(
- WebRtcVideoChannel::WebRtcVideoReceiveStream* stream) {
- for (uint32_t old_ssrc : stream->GetSsrcs())
- receive_ssrcs_.erase(old_ssrc);
- delete stream;
-}
-
-bool WebRtcVideoChannel::AddRecvStream(const StreamParams& sp) {
- return AddRecvStream(sp, false);
-}
-
-bool WebRtcVideoChannel::AddRecvStream(const StreamParams& sp,
- bool default_stream) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
-
- RTC_LOG(LS_INFO) << "AddRecvStream"
- << (default_stream ? " (default stream)" : "") << ": "
- << sp.ToString();
- if (!sp.has_ssrcs()) {
- // This is a StreamParam with unsignaled SSRCs. Store it, so it can be used
- // later when we know the SSRC on the first packet arrival.
- unsignaled_stream_params_ = sp;
- return true;
- }
-
- if (!ValidateStreamParams(sp))
- return false;
-
- for (uint32_t ssrc : sp.ssrcs) {
- // Remove running stream if this was a default stream.
- const auto& prev_stream = receive_streams_.find(ssrc);
- if (prev_stream != receive_streams_.end()) {
- if (default_stream || !prev_stream->second->IsDefaultStream()) {
- RTC_LOG(LS_ERROR) << "Receive stream for SSRC '" << ssrc
- << "' already exists.";
- return false;
- }
- DeleteReceiveStream(prev_stream->second);
- receive_streams_.erase(prev_stream);
- }
- }
-
- if (!ValidateReceiveSsrcAvailability(sp))
- return false;
-
- for (uint32_t used_ssrc : sp.ssrcs)
- receive_ssrcs_.insert(used_ssrc);
-
- webrtc::VideoReceiveStreamInterface::Config config(this, decoder_factory_);
- webrtc::FlexfecReceiveStream::Config flexfec_config(this);
- ConfigureReceiverRtp(&config, &flexfec_config, sp);
-
- config.crypto_options = crypto_options_;
- config.enable_prerenderer_smoothing =
- video_config_.enable_prerenderer_smoothing;
- if (!sp.stream_ids().empty()) {
- config.sync_group = sp.stream_ids()[0];
- }
-
- if (unsignaled_frame_transformer_ && !config.frame_transformer)
- config.frame_transformer = unsignaled_frame_transformer_;
-
- auto receive_stream =
- new WebRtcVideoReceiveStream(call_, sp, std::move(config), default_stream,
- recv_codecs_, flexfec_config);
- if (receiving_) {
- receive_stream->StartReceiveStream();
- }
- receive_streams_[sp.first_ssrc()] = receive_stream;
- return true;
-}
-
-void WebRtcVideoChannel::ConfigureReceiverRtp(
- webrtc::VideoReceiveStreamInterface::Config* config,
- webrtc::FlexfecReceiveStream::Config* flexfec_config,
- const StreamParams& sp) const {
- uint32_t ssrc = sp.first_ssrc();
-
- config->rtp.remote_ssrc = ssrc;
- config->rtp.local_ssrc = rtcp_receiver_report_ssrc_;
-
- // TODO(pbos): This protection is against setting the same local ssrc as
- // remote which is not permitted by the lower-level API. RTCP requires a
- // corresponding sender SSRC. Figure out what to do when we don't have
- // (receive-only) or know a good local SSRC.
- if (config->rtp.remote_ssrc == config->rtp.local_ssrc) {
- if (config->rtp.local_ssrc != kDefaultRtcpReceiverReportSsrc) {
- config->rtp.local_ssrc = kDefaultRtcpReceiverReportSsrc;
- } else {
- config->rtp.local_ssrc = kDefaultRtcpReceiverReportSsrc + 1;
- }
- }
-
- if (role() == MediaChannel::Role::kBoth) {
- // Whether or not the receive stream sends reduced size RTCP is determined
- // by the send params.
- // TODO(deadbeef): Once we change "send_params" to "sender_params" and
- // "recv_params" to "receiver_params", we should get this out of
- // receiver_params_.
- config->rtp.rtcp_mode = send_params_.rtcp.reduced_size
- ? webrtc::RtcpMode::kReducedSize
- : webrtc::RtcpMode::kCompound;
- // rtx-time (RFC 4588) is a declarative attribute similar to rtcp-rsize and
- // determined by the sender / send codec.
- if (send_codec() && send_codec()->rtx_time) {
- config->rtp.nack.rtp_history_ms = *send_codec()->rtx_time;
- }
- } else {
- // The mode and rtx time is determined by a call to the configuration
- // function.
- config->rtp.rtcp_mode = rtp_config_.rtcp_mode;
- }
-
- sp.GetFidSsrc(ssrc, &config->rtp.rtx_ssrc);
-
- // TODO(brandtr): Generalize when we add support for multistream protection.
- flexfec_config->payload_type = recv_flexfec_payload_type_;
- if (!IsDisabled(call_->trials(), "WebRTC-FlexFEC-03-Advertised") &&
- sp.GetFecFrSsrc(ssrc, &flexfec_config->rtp.remote_ssrc)) {
- flexfec_config->protected_media_ssrcs = {ssrc};
- flexfec_config->rtp.local_ssrc = config->rtp.local_ssrc;
- flexfec_config->rtcp_mode = config->rtp.rtcp_mode;
- }
-}
-
-bool WebRtcVideoChannel::RemoveRecvStream(uint32_t ssrc) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- RTC_LOG(LS_INFO) << "RemoveRecvStream: " << ssrc;
-
- auto stream = receive_streams_.find(ssrc);
- if (stream == receive_streams_.end()) {
- RTC_LOG(LS_ERROR) << "Stream not found for ssrc: " << ssrc;
- return false;
- }
- DeleteReceiveStream(stream->second);
- receive_streams_.erase(stream);
-
- return true;
-}
-
-void WebRtcVideoChannel::ResetUnsignaledRecvStream() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- RTC_LOG(LS_INFO) << "ResetUnsignaledRecvStream.";
- unsignaled_stream_params_ = StreamParams();
- last_unsignalled_ssrc_creation_time_ms_ = absl::nullopt;
-
- // Delete any created default streams. This is needed to avoid SSRC collisions
- // in Call's RtpDemuxer, in the case that `this` has created a default video
- // receiver, and then some other WebRtcVideoChannel gets the SSRC signaled
- // in the corresponding Unified Plan "m=" section.
- auto it = receive_streams_.begin();
- while (it != receive_streams_.end()) {
- if (it->second->IsDefaultStream()) {
- DeleteReceiveStream(it->second);
- receive_streams_.erase(it++);
- } else {
- ++it;
- }
- }
-}
-
-absl::optional<uint32_t> WebRtcVideoChannel::GetUnsignaledSsrc() const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- absl::optional<uint32_t> ssrc;
- for (auto it = receive_streams_.begin(); it != receive_streams_.end(); ++it) {
- if (it->second->IsDefaultStream()) {
- ssrc.emplace(it->first);
- break;
- }
- }
- return ssrc;
-}
-
-void WebRtcVideoChannel::OnDemuxerCriteriaUpdatePending() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- ++demuxer_criteria_id_;
-}
-
-void WebRtcVideoChannel::OnDemuxerCriteriaUpdateComplete() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- ++demuxer_criteria_completed_id_;
-}
-
-bool WebRtcVideoChannel::SetSink(
- uint32_t ssrc,
- rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- RTC_LOG(LS_INFO) << "SetSink: ssrc:" << ssrc << " "
- << (sink ? "(ptr)" : "nullptr");
-
- auto it = receive_streams_.find(ssrc);
- if (it == receive_streams_.end()) {
- return false;
- }
-
- it->second->SetSink(sink);
- return true;
-}
-
-void WebRtcVideoChannel::SetDefaultSink(
- rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- RTC_LOG(LS_INFO) << "SetDefaultSink: " << (sink ? "(ptr)" : "nullptr");
- default_sink_ = sink;
-}
-
-bool WebRtcVideoChannel::GetSendStats(VideoMediaSendInfo* info) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- TRACE_EVENT0("webrtc", "WebRtcVideoChannel::GetSendStats");
-
- info->Clear();
- if (send_streams_.empty()) {
- return true;
- }
-
- // Log stats periodically.
- bool log_stats = false;
- int64_t now_ms = rtc::TimeMillis();
- if (last_send_stats_log_ms_ == -1 ||
- now_ms - last_send_stats_log_ms_ > kStatsLogIntervalMs) {
- last_send_stats_log_ms_ = now_ms;
- log_stats = true;
- }
-
- info->Clear();
- FillSenderStats(info, log_stats);
- FillSendCodecStats(info);
- // TODO(holmer): We should either have rtt available as a metric on
- // VideoSend/ReceiveStreams, or we should remove rtt from VideoSenderInfo.
- webrtc::Call::Stats stats = call_->GetStats();
- if (stats.rtt_ms != -1) {
- for (size_t i = 0; i < info->senders.size(); ++i) {
- info->senders[i].rtt_ms = stats.rtt_ms;
- }
- for (size_t i = 0; i < info->aggregated_senders.size(); ++i) {
- info->aggregated_senders[i].rtt_ms = stats.rtt_ms;
- }
- }
-
- if (log_stats)
- RTC_LOG(LS_INFO) << stats.ToString(now_ms);
-
- return true;
-}
-bool WebRtcVideoChannel::GetReceiveStats(VideoMediaReceiveInfo* info) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- TRACE_EVENT0("webrtc", "WebRtcVideoChannel::GetReceiveStats");
-
- info->Clear();
- if (receive_streams_.empty()) {
- return true;
- }
-
- // Log stats periodically.
- bool log_stats = false;
- int64_t now_ms = rtc::TimeMillis();
- if (last_receive_stats_log_ms_ == -1 ||
- now_ms - last_receive_stats_log_ms_ > kStatsLogIntervalMs) {
- last_receive_stats_log_ms_ = now_ms;
- log_stats = true;
- }
-
- FillReceiverStats(info, log_stats);
- FillReceiveCodecStats(info);
-
- return true;
-}
-
-void WebRtcVideoChannel::FillSenderStats(VideoMediaSendInfo* video_media_info,
- bool log_stats) {
- for (const auto& it : send_streams_) {
- auto infos = it.second->GetPerLayerVideoSenderInfos(log_stats);
- if (infos.empty())
- continue;
- video_media_info->aggregated_senders.push_back(
- it.second->GetAggregatedVideoSenderInfo(infos));
- for (auto&& info : infos) {
- video_media_info->senders.push_back(info);
- }
- }
-}
-
-void WebRtcVideoChannel::FillReceiverStats(
- VideoMediaReceiveInfo* video_media_info,
- bool log_stats) {
- for (const auto& it : receive_streams_) {
- video_media_info->receivers.push_back(
- it.second->GetVideoReceiverInfo(log_stats));
- }
-}
-
-void WebRtcVideoChannel::FillBitrateInfo(BandwidthEstimationInfo* bwe_info) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- for (const auto& it : send_streams_) {
- it.second->FillBitrateInfo(bwe_info);
- }
-}
-
-void WebRtcVideoChannel::FillSendCodecStats(
- VideoMediaSendInfo* video_media_info) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (!send_codec()) {
- return;
- }
- // Note: since RTP stats don't account for RTX and FEC separately (see
- // https://w3c.github.io/webrtc-stats/#dom-rtcstatstype-outbound-rtp)
- // we can omit the codec information for those here and only insert the
- // primary codec that is being used to send here.
- video_media_info->send_codecs.insert(std::make_pair(
- send_codec()->codec.id, send_codec()->codec.ToCodecParameters()));
-}
-
-void WebRtcVideoChannel::FillReceiveCodecStats(
- VideoMediaReceiveInfo* video_media_info) {
- for (const auto& receiver : video_media_info->receivers) {
- auto codec =
- absl::c_find_if(recv_params_.codecs, [&receiver](const VideoCodec& c) {
- return receiver.codec_payload_type &&
- *receiver.codec_payload_type == c.id;
- });
- if (codec != recv_params_.codecs.end()) {
- video_media_info->receive_codecs.insert(
- std::make_pair(codec->id, codec->ToCodecParameters()));
- }
- }
-}
-
-void WebRtcVideoChannel::OnPacketReceived(
- const webrtc::RtpPacketReceived& packet) {
- // Note: the network_thread_checker may refer to the worker thread if the two
- // threads are combined, but this is either always true or always false
- // depending on configuration set at object initialization.
- RTC_DCHECK_RUN_ON(&network_thread_checker_);
-
- // TODO(crbug.com/1373439): Stop posting to the worker thread when the
- // combined network/worker project launches.
- if (webrtc::TaskQueueBase::Current() != worker_thread_) {
- worker_thread_->PostTask(
- SafeTask(task_safety_.flag(), [this, packet = packet]() mutable {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- ProcessReceivedPacket(std::move(packet));
- }));
- } else {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- ProcessReceivedPacket(packet);
- }
-}
-
-bool WebRtcVideoChannel::MaybeCreateDefaultReceiveStream(
- const webrtc::RtpPacketReceived& packet) {
- if (discard_unknown_ssrc_packets_) {
- return false;
- }
-
- if (packet.PayloadType() == recv_flexfec_payload_type_) {
- return false;
- }
-
- // Ignore unknown ssrcs if there is a demuxer criteria update pending.
- // During a demuxer update we may receive ssrcs that were recently
- // removed or we may receve ssrcs that were recently configured for a
- // different video channel.
- if (demuxer_criteria_id_ != demuxer_criteria_completed_id_) {
- return false;
- }
-
- // See if this payload_type is registered as one that usually gets its
- // own SSRC (RTX) or at least is safe to drop either way (FEC). If it
- // is, and it wasn't handled above by DeliverPacket, that means we don't
- // know what stream it associates with, and we shouldn't ever create an
- // implicit channel for these.
- bool is_rtx_payload = false;
- for (auto& codec : recv_codecs_) {
- if (packet.PayloadType() == codec.ulpfec.red_rtx_payload_type ||
- packet.PayloadType() == codec.ulpfec.ulpfec_payload_type) {
- return false;
- }
-
- if (packet.PayloadType() == codec.rtx_payload_type) {
- is_rtx_payload = true;
- break;
- }
- }
-
- if (is_rtx_payload) {
- // As we don't support receiving simulcast there can only be one RTX
- // stream, which will be associated with unsignaled media stream.
- absl::optional<uint32_t> current_default_ssrc = GetUnsignaledSsrc();
- if (current_default_ssrc) {
- FindReceiveStream(*current_default_ssrc)->UpdateRtxSsrc(packet.Ssrc());
- } else {
- // Received unsignaled RTX packet before a media packet. Create a default
- // stream with a "random" SSRC and the RTX SSRC from the packet. The
- // stream will be recreated on the first media packet, unless we are
- // extremely lucky and used the right media SSRC.
- ReCreateDefaultReceiveStream(/*ssrc =*/14795, /*rtx_ssrc=*/packet.Ssrc());
- }
- return true;
- } else {
- // Ignore unknown ssrcs if we recently created an unsignalled receive
- // stream since this shouldn't happen frequently. Getting into a state
- // of creating decoders on every packet eats up processing time (e.g.
- // https://crbug.com/1069603) and this cooldown prevents that.
- if (last_unsignalled_ssrc_creation_time_ms_.has_value()) {
- int64_t now_ms = rtc::TimeMillis();
- if (now_ms - last_unsignalled_ssrc_creation_time_ms_.value() <
- kUnsignaledSsrcCooldownMs) {
- // We've already created an unsignalled ssrc stream within the last
- // 0.5 s, ignore with a warning.
- RTC_LOG(LS_WARNING)
- << "Another unsignalled ssrc packet arrived shortly after the "
- << "creation of an unsignalled ssrc stream. Dropping packet.";
- return false;
- }
- }
- }
- // RTX SSRC not yet known.
- ReCreateDefaultReceiveStream(packet.Ssrc(), absl::nullopt);
- last_unsignalled_ssrc_creation_time_ms_ = rtc::TimeMillis();
- return true;
-}
-
-void WebRtcVideoChannel::ReCreateDefaultReceiveStream(
- uint32_t ssrc,
- absl::optional<uint32_t> rtx_ssrc) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
-
- absl::optional<uint32_t> default_recv_ssrc = GetUnsignaledSsrc();
- if (default_recv_ssrc) {
- RTC_LOG(LS_INFO) << "Destroying old default receive stream for SSRC="
- << ssrc << ".";
- RemoveRecvStream(*default_recv_ssrc);
- }
-
- StreamParams sp = unsignaled_stream_params();
- sp.ssrcs.push_back(ssrc);
- if (rtx_ssrc) {
- sp.AddFidSsrc(ssrc, *rtx_ssrc);
- }
- RTC_LOG(LS_INFO) << "Creating default receive stream for SSRC=" << ssrc
- << ".";
- if (!AddRecvStream(sp, /*default_stream=*/true)) {
- RTC_LOG(LS_WARNING) << "Could not create default receive stream.";
- }
-
- // SSRC 0 returns default_recv_base_minimum_delay_ms.
- const int unsignaled_ssrc = 0;
- int default_recv_base_minimum_delay_ms =
- GetBaseMinimumPlayoutDelayMs(unsignaled_ssrc).value_or(0);
- // Set base minimum delay if it was set before for the default receive
- // stream.
- SetBaseMinimumPlayoutDelayMs(ssrc, default_recv_base_minimum_delay_ms);
- SetSink(ssrc, default_sink_);
-}
-
-void WebRtcVideoChannel::OnPacketSent(const rtc::SentPacket& sent_packet) {
- RTC_DCHECK_RUN_ON(&network_thread_checker_);
- // TODO(tommi): We shouldn't need to go through call_ to deliver this
- // notification. We should already have direct access to
- // video_send_delay_stats_ and transport_send_ptr_ via `stream_`.
- // So we should be able to remove OnSentPacket from Call and handle this per
- // channel instead. At the moment Call::OnSentPacket calls OnSentPacket for
- // the video stats, for all sent packets, including audio, which causes
- // unnecessary lookups.
- call_->OnSentPacket(sent_packet);
-}
-
-void WebRtcVideoChannel::OnReadyToSend(bool ready) {
- RTC_DCHECK_RUN_ON(&network_thread_checker_);
- RTC_LOG(LS_VERBOSE) << "OnReadyToSend: " << (ready ? "Ready." : "Not ready.");
- call_->SignalChannelNetworkState(
- webrtc::MediaType::VIDEO,
- ready ? webrtc::kNetworkUp : webrtc::kNetworkDown);
-}
-
-void WebRtcVideoChannel::OnNetworkRouteChanged(
- absl::string_view transport_name,
- const rtc::NetworkRoute& network_route) {
- RTC_DCHECK_RUN_ON(&network_thread_checker_);
- worker_thread_->PostTask(SafeTask(
- task_safety_.flag(),
- [this, name = std::string(transport_name), route = network_route] {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- webrtc::RtpTransportControllerSendInterface* transport =
- call_->GetTransportControllerSend();
- transport->OnNetworkRouteChanged(name, route);
- transport->OnTransportOverheadChanged(route.packet_overhead);
- }));
-}
-
-void WebRtcVideoChannel::SetInterface(MediaChannelNetworkInterface* iface) {
- RTC_DCHECK_RUN_ON(&network_thread_checker_);
- MediaChannel::SetInterface(iface);
- // Set the RTP recv/send buffer to a bigger size.
- MediaChannel::SetOption(MediaChannelNetworkInterface::ST_RTP,
- rtc::Socket::OPT_RCVBUF, kVideoRtpRecvBufferSize);
-
- // Speculative change to increase the outbound socket buffer size.
- // In b/15152257, we are seeing a significant number of packets discarded
- // due to lack of socket buffer space, although it's not yet clear what the
- // ideal value should be.
- const std::string group_name_send_buf_size =
- call_->trials().Lookup("WebRTC-SendBufferSizeBytes");
- int send_buffer_size = kVideoRtpSendBufferSize;
- if (!group_name_send_buf_size.empty() &&
- (sscanf(group_name_send_buf_size.c_str(), "%d", &send_buffer_size) != 1 ||
- send_buffer_size <= 0)) {
- RTC_LOG(LS_WARNING) << "Invalid send buffer size: "
- << group_name_send_buf_size;
- send_buffer_size = kVideoRtpSendBufferSize;
- }
-
- MediaChannel::SetOption(MediaChannelNetworkInterface::ST_RTP,
- rtc::Socket::OPT_SNDBUF, send_buffer_size);
-}
-
-void WebRtcVideoChannel::SetFrameDecryptor(
- uint32_t ssrc,
- rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- auto matching_stream = receive_streams_.find(ssrc);
- if (matching_stream != receive_streams_.end()) {
- matching_stream->second->SetFrameDecryptor(frame_decryptor);
- }
-}
-
-void WebRtcVideoChannel::SetFrameEncryptor(
- uint32_t ssrc,
- rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- auto matching_stream = send_streams_.find(ssrc);
- if (matching_stream != send_streams_.end()) {
- matching_stream->second->SetFrameEncryptor(frame_encryptor);
- } else {
- RTC_LOG(LS_ERROR) << "No stream found to attach frame encryptor";
- }
-}
-
-void WebRtcVideoChannel::SetEncoderSelector(
- uint32_t ssrc,
- webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- auto matching_stream = send_streams_.find(ssrc);
- if (matching_stream != send_streams_.end()) {
- matching_stream->second->SetEncoderSelector(encoder_selector);
- } else {
- RTC_LOG(LS_ERROR) << "No stream found to attach encoder selector";
- }
-}
-
-void WebRtcVideoChannel::SetVideoCodecSwitchingEnabled(bool enabled) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- allow_codec_switching_ = enabled;
- if (allow_codec_switching_) {
- RTC_LOG(LS_INFO) << "Encoder switching enabled.";
- }
-}
-
-bool WebRtcVideoChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
- int delay_ms) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- absl::optional<uint32_t> default_ssrc = GetUnsignaledSsrc();
-
- // SSRC of 0 represents the default receive stream.
- if (ssrc == 0) {
- default_recv_base_minimum_delay_ms_ = delay_ms;
- }
-
- if (ssrc == 0 && !default_ssrc) {
- return true;
- }
-
- if (ssrc == 0 && default_ssrc) {
- ssrc = default_ssrc.value();
- }
-
- auto stream = receive_streams_.find(ssrc);
- if (stream != receive_streams_.end()) {
- stream->second->SetBaseMinimumPlayoutDelayMs(delay_ms);
- return true;
- } else {
- RTC_LOG(LS_ERROR) << "No stream found to set base minimum playout delay";
- return false;
- }
-}
-
-absl::optional<int> WebRtcVideoChannel::GetBaseMinimumPlayoutDelayMs(
- uint32_t ssrc) const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- // SSRC of 0 represents the default receive stream.
- if (ssrc == 0) {
- return default_recv_base_minimum_delay_ms_;
- }
-
- auto stream = receive_streams_.find(ssrc);
- if (stream != receive_streams_.end()) {
- return stream->second->GetBaseMinimumPlayoutDelayMs();
- } else {
- RTC_LOG(LS_ERROR) << "No stream found to get base minimum playout delay";
- return absl::nullopt;
- }
-}
-
-std::vector<webrtc::RtpSource> WebRtcVideoChannel::GetSources(
- uint32_t ssrc) const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- auto it = receive_streams_.find(ssrc);
- if (it == receive_streams_.end()) {
- // TODO(bugs.webrtc.org/9781): Investigate standard compliance
- // with sources for streams that has been removed.
- RTC_LOG(LS_ERROR) << "Attempting to get contributing sources for SSRC:"
- << ssrc << " which doesn't exist.";
- return {};
- }
- return it->second->GetSources();
-}
-
-bool WebRtcVideoChannel::SendRtp(const uint8_t* data,
- size_t len,
- const webrtc::PacketOptions& options) {
- MediaChannel::SendRtp(data, len, options);
- return true;
-}
-
-bool WebRtcVideoChannel::SendRtcp(const uint8_t* data, size_t len) {
- MediaChannel::SendRtcp(data, len);
- return true;
-}
-
-WebRtcVideoChannel::WebRtcVideoSendStream::VideoSendStreamParameters::
- VideoSendStreamParameters(
- webrtc::VideoSendStream::Config config,
- const VideoOptions& options,
- int max_bitrate_bps,
- const absl::optional<VideoCodecSettings>& codec_settings)
- : config(std::move(config)),
- options(options),
- max_bitrate_bps(max_bitrate_bps),
- conference_mode(false),
- codec_settings(codec_settings) {}
-
-WebRtcVideoChannel::WebRtcVideoSendStream::WebRtcVideoSendStream(
- webrtc::Call* call,
- const StreamParams& sp,
- webrtc::VideoSendStream::Config config,
- const VideoOptions& options,
- bool enable_cpu_overuse_detection,
- int max_bitrate_bps,
- const absl::optional<VideoCodecSettings>& codec_settings,
- const absl::optional<std::vector<webrtc::RtpExtension>>& rtp_extensions,
- // TODO(deadbeef): Don't duplicate information between send_params,
- // rtp_extensions, options, etc.
- const VideoSendParameters& send_params)
- : worker_thread_(call->worker_thread()),
- ssrcs_(sp.ssrcs),
- ssrc_groups_(sp.ssrc_groups),
- call_(call),
- enable_cpu_overuse_detection_(enable_cpu_overuse_detection),
- source_(nullptr),
- stream_(nullptr),
- parameters_(std::move(config), options, max_bitrate_bps, codec_settings),
- rtp_parameters_(CreateRtpParametersWithEncodings(sp)),
- sending_(false),
- disable_automatic_resize_(
- IsEnabled(call->trials(), "WebRTC-Video-DisableAutomaticResize")) {
- // Maximum packet size may come in RtpConfig from external transport, for
- // example from QuicTransportInterface implementation, so do not exceed
- // given max_packet_size.
- parameters_.config.rtp.max_packet_size =
- std::min<size_t>(parameters_.config.rtp.max_packet_size, kVideoMtu);
- parameters_.conference_mode = send_params.conference_mode;
-
- sp.GetPrimarySsrcs(¶meters_.config.rtp.ssrcs);
-
- // ValidateStreamParams should prevent this from happening.
- RTC_CHECK(!parameters_.config.rtp.ssrcs.empty());
- rtp_parameters_.encodings[0].ssrc = parameters_.config.rtp.ssrcs[0];
-
- // RTX.
- sp.GetFidSsrcs(parameters_.config.rtp.ssrcs,
- ¶meters_.config.rtp.rtx.ssrcs);
-
- // FlexFEC SSRCs.
- // TODO(brandtr): This code needs to be generalized when we add support for
- // multistream protection.
- if (IsEnabled(call_->trials(), "WebRTC-FlexFEC-03")) {
- uint32_t flexfec_ssrc;
- bool flexfec_enabled = false;
- for (uint32_t primary_ssrc : parameters_.config.rtp.ssrcs) {
- if (sp.GetFecFrSsrc(primary_ssrc, &flexfec_ssrc)) {
- if (flexfec_enabled) {
- RTC_LOG(LS_INFO)
- << "Multiple FlexFEC streams in local SDP, but "
- "our implementation only supports a single FlexFEC "
- "stream. Will not enable FlexFEC for proposed "
- "stream with SSRC: "
- << flexfec_ssrc << ".";
- continue;
- }
-
- flexfec_enabled = true;
- parameters_.config.rtp.flexfec.ssrc = flexfec_ssrc;
- parameters_.config.rtp.flexfec.protected_media_ssrcs = {primary_ssrc};
- }
- }
- }
-
- parameters_.config.rtp.c_name = sp.cname;
- if (rtp_extensions) {
- parameters_.config.rtp.extensions = *rtp_extensions;
- rtp_parameters_.header_extensions = *rtp_extensions;
- }
- parameters_.config.rtp.rtcp_mode = send_params.rtcp.reduced_size
- ? webrtc::RtcpMode::kReducedSize
- : webrtc::RtcpMode::kCompound;
- parameters_.config.rtp.mid = send_params.mid;
- rtp_parameters_.rtcp.reduced_size = send_params.rtcp.reduced_size;
-
- if (codec_settings) {
- SetCodec(*codec_settings);
- }
-}
-
-WebRtcVideoChannel::WebRtcVideoSendStream::~WebRtcVideoSendStream() {
- if (stream_ != NULL) {
- call_->DestroyVideoSendStream(stream_);
- }
-}
-
-bool WebRtcVideoChannel::WebRtcVideoSendStream::SetVideoSend(
- const VideoOptions* options,
- rtc::VideoSourceInterface<webrtc::VideoFrame>* source) {
- TRACE_EVENT0("webrtc", "WebRtcVideoSendStream::SetVideoSend");
- RTC_DCHECK_RUN_ON(&thread_checker_);
-
- if (options) {
- VideoOptions old_options = parameters_.options;
- parameters_.options.SetAll(*options);
- if (parameters_.options.is_screencast.value_or(false) !=
- old_options.is_screencast.value_or(false) &&
- parameters_.codec_settings) {
- // If screen content settings change, we may need to recreate the codec
- // instance so that the correct type is used.
-
- SetCodec(*parameters_.codec_settings);
- // Mark screenshare parameter as being updated, then test for any other
- // changes that may require codec reconfiguration.
- old_options.is_screencast = options->is_screencast;
- }
- if (parameters_.options != old_options) {
- ReconfigureEncoder(nullptr);
- }
- }
-
- if (source_ && stream_) {
- stream_->SetSource(nullptr, webrtc::DegradationPreference::DISABLED);
- }
- // Switch to the new source.
- source_ = source;
- if (source && stream_) {
- stream_->SetSource(source_, GetDegradationPreference());
- }
- return true;
-}
-
-webrtc::DegradationPreference
-WebRtcVideoChannel::WebRtcVideoSendStream::GetDegradationPreference() const {
- // Do not adapt resolution for screen content as this will likely
- // result in blurry and unreadable text.
- // `this` acts like a VideoSource to make sure SinkWants are handled on the
- // correct thread.
- if (!enable_cpu_overuse_detection_) {
- return webrtc::DegradationPreference::DISABLED;
- }
-
- webrtc::DegradationPreference degradation_preference;
- if (rtp_parameters_.degradation_preference.has_value()) {
- degradation_preference = *rtp_parameters_.degradation_preference;
- } else {
- if (parameters_.options.content_hint ==
- webrtc::VideoTrackInterface::ContentHint::kFluid) {
- degradation_preference =
- webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
- } else if (parameters_.options.is_screencast.value_or(false) ||
- parameters_.options.content_hint ==
- webrtc::VideoTrackInterface::ContentHint::kDetailed ||
- parameters_.options.content_hint ==
- webrtc::VideoTrackInterface::ContentHint::kText) {
- degradation_preference =
- webrtc::DegradationPreference::MAINTAIN_RESOLUTION;
- } else if (IsEnabled(call_->trials(), "WebRTC-Video-BalancedDegradation")) {
- // Standard wants balanced by default, but it needs to be tuned first.
- degradation_preference = webrtc::DegradationPreference::BALANCED;
- } else {
- // Keep MAINTAIN_FRAMERATE by default until BALANCED has been tuned for
- // all codecs and launched.
- degradation_preference =
- webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
- }
- }
-
- return degradation_preference;
-}
-
-const std::vector<uint32_t>&
-WebRtcVideoChannel::WebRtcVideoSendStream::GetSsrcs() const {
- return ssrcs_;
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::SetCodec(
- const VideoCodecSettings& codec_settings) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- FallbackToDefaultScalabilityModeIfNotSupported(
- codec_settings.codec, parameters_.config, rtp_parameters_.encodings);
-
- parameters_.encoder_config = CreateVideoEncoderConfig(codec_settings.codec);
- RTC_DCHECK_GT(parameters_.encoder_config.number_of_streams, 0);
-
- parameters_.config.rtp.payload_name = codec_settings.codec.name;
- parameters_.config.rtp.payload_type = codec_settings.codec.id;
- parameters_.config.rtp.raw_payload =
- codec_settings.codec.packetization == kPacketizationParamRaw;
- parameters_.config.rtp.ulpfec = codec_settings.ulpfec;
- parameters_.config.rtp.flexfec.payload_type =
- codec_settings.flexfec_payload_type;
-
- // Set RTX payload type if RTX is enabled.
- if (!parameters_.config.rtp.rtx.ssrcs.empty()) {
- if (codec_settings.rtx_payload_type == -1) {
- RTC_LOG(LS_WARNING)
- << "RTX SSRCs configured but there's no configured RTX "
- "payload type. Ignoring.";
- parameters_.config.rtp.rtx.ssrcs.clear();
- } else {
- parameters_.config.rtp.rtx.payload_type = codec_settings.rtx_payload_type;
- }
- }
-
- const bool has_lntf = HasLntf(codec_settings.codec);
- parameters_.config.rtp.lntf.enabled = has_lntf;
- parameters_.config.encoder_settings.capabilities.loss_notification = has_lntf;
-
- parameters_.config.rtp.nack.rtp_history_ms =
- HasNack(codec_settings.codec) ? kNackHistoryMs : 0;
-
- parameters_.codec_settings = codec_settings;
-
- // TODO(bugs.webrtc.org/8830): Avoid recreation, it should be enough to call
- // ReconfigureEncoder.
- RTC_LOG(LS_INFO) << "RecreateWebRtcStream (send) because of SetCodec.";
- RecreateWebRtcStream();
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::SetSendParameters(
- const ChangedSendParameters& params) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- // `recreate_stream` means construction-time parameters have changed and the
- // sending stream needs to be reset with the new config.
- bool recreate_stream = false;
- if (params.rtcp_mode) {
- parameters_.config.rtp.rtcp_mode = *params.rtcp_mode;
- rtp_parameters_.rtcp.reduced_size =
- parameters_.config.rtp.rtcp_mode == webrtc::RtcpMode::kReducedSize;
- recreate_stream = true;
- }
- if (params.extmap_allow_mixed) {
- parameters_.config.rtp.extmap_allow_mixed = *params.extmap_allow_mixed;
- recreate_stream = true;
- }
- if (params.rtp_header_extensions) {
- parameters_.config.rtp.extensions = *params.rtp_header_extensions;
- rtp_parameters_.header_extensions = *params.rtp_header_extensions;
- recreate_stream = true;
- }
- if (params.mid) {
- parameters_.config.rtp.mid = *params.mid;
- recreate_stream = true;
- }
- if (params.max_bandwidth_bps) {
- parameters_.max_bitrate_bps = *params.max_bandwidth_bps;
- ReconfigureEncoder(nullptr);
- }
- if (params.conference_mode) {
- parameters_.conference_mode = *params.conference_mode;
- }
-
- // Set codecs and options.
- if (params.send_codec) {
- SetCodec(*params.send_codec);
- recreate_stream = false; // SetCodec has already recreated the stream.
- } else if (params.conference_mode && parameters_.codec_settings) {
- SetCodec(*parameters_.codec_settings);
- recreate_stream = false; // SetCodec has already recreated the stream.
- }
- if (recreate_stream) {
- RTC_LOG(LS_INFO)
- << "RecreateWebRtcStream (send) because of SetSendParameters";
- RecreateWebRtcStream();
- }
-}
-
-webrtc::RTCError WebRtcVideoChannel::WebRtcVideoSendStream::SetRtpParameters(
- const webrtc::RtpParameters& new_parameters,
- webrtc::SetParametersCallback callback) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- // This is checked higher in the stack (RtpSender), so this is only checking
- // for users accessing the private APIs or tests, not specification
- // conformance.
- // TODO(orphis): Migrate tests to later make this a DCHECK only
- webrtc::RTCError error = CheckRtpParametersInvalidModificationAndValues(
- rtp_parameters_, new_parameters);
- if (!error.ok()) {
- // Error is propagated to the callback at a higher level
- return error;
- }
-
- bool new_param = false;
- for (size_t i = 0; i < rtp_parameters_.encodings.size(); ++i) {
- if ((new_parameters.encodings[i].min_bitrate_bps !=
- rtp_parameters_.encodings[i].min_bitrate_bps) ||
- (new_parameters.encodings[i].max_bitrate_bps !=
- rtp_parameters_.encodings[i].max_bitrate_bps) ||
- (new_parameters.encodings[i].max_framerate !=
- rtp_parameters_.encodings[i].max_framerate) ||
- (new_parameters.encodings[i].scale_resolution_down_by !=
- rtp_parameters_.encodings[i].scale_resolution_down_by) ||
- (new_parameters.encodings[i].num_temporal_layers !=
- rtp_parameters_.encodings[i].num_temporal_layers) ||
- (new_parameters.encodings[i].requested_resolution !=
- rtp_parameters_.encodings[i].requested_resolution) ||
- (new_parameters.encodings[i].scalability_mode !=
- rtp_parameters_.encodings[i].scalability_mode)) {
- new_param = true;
- break;
- }
- }
-
- bool new_degradation_preference = false;
- if (new_parameters.degradation_preference !=
- rtp_parameters_.degradation_preference) {
- new_degradation_preference = true;
- }
-
- // Some fields (e.g. bitrate priority) only need to update the bitrate
- // allocator which is updated via ReconfigureEncoder (however, note that the
- // actual encoder should only be reconfigured if needed).
- bool reconfigure_encoder =
- new_param || (new_parameters.encodings[0].bitrate_priority !=
- rtp_parameters_.encodings[0].bitrate_priority);
-
- // Note that the simulcast encoder adapter relies on the fact that layers
- // de/activation triggers encoder reinitialization.
- bool new_send_state = false;
- for (size_t i = 0; i < rtp_parameters_.encodings.size(); ++i) {
- bool new_active = IsLayerActive(new_parameters.encodings[i]);
- bool old_active = IsLayerActive(rtp_parameters_.encodings[i]);
- if (new_active != old_active) {
- new_send_state = true;
- }
- }
- rtp_parameters_ = new_parameters;
- // Codecs are currently handled at the WebRtcVideoChannel level.
- rtp_parameters_.codecs.clear();
- if (reconfigure_encoder || new_send_state) {
- // Callback responsibility is delegated to ReconfigureEncoder()
- ReconfigureEncoder(std::move(callback));
- callback = nullptr;
- }
- if (new_send_state) {
- UpdateSendState();
- }
- if (new_degradation_preference) {
- if (source_ && stream_) {
- stream_->SetSource(source_, GetDegradationPreference());
- }
- }
- return webrtc::InvokeSetParametersCallback(callback, webrtc::RTCError::OK());
-}
-
-webrtc::RtpParameters
-WebRtcVideoChannel::WebRtcVideoSendStream::GetRtpParameters() const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- return rtp_parameters_;
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::SetFrameEncryptor(
- rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- parameters_.config.frame_encryptor = frame_encryptor;
- if (stream_) {
- RTC_LOG(LS_INFO)
- << "RecreateWebRtcStream (send) because of SetFrameEncryptor, ssrc="
- << parameters_.config.rtp.ssrcs[0];
- RecreateWebRtcStream();
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::SetEncoderSelector(
- webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- parameters_.config.encoder_selector = encoder_selector;
- if (stream_) {
- RTC_LOG(LS_INFO)
- << "RecreateWebRtcStream (send) because of SetEncoderSelector, ssrc="
- << parameters_.config.rtp.ssrcs[0];
- RecreateWebRtcStream();
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::UpdateSendState() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (sending_) {
- RTC_DCHECK(stream_ != nullptr);
- size_t num_layers = rtp_parameters_.encodings.size();
- if (parameters_.encoder_config.number_of_streams == 1) {
- // SVC is used. Only one simulcast layer is present.
- num_layers = 1;
- }
- std::vector<bool> active_layers(num_layers);
- for (size_t i = 0; i < num_layers; ++i) {
- active_layers[i] = IsLayerActive(rtp_parameters_.encodings[i]);
- }
- if (parameters_.encoder_config.number_of_streams == 1 &&
- rtp_parameters_.encodings.size() > 1) {
- // SVC is used.
- // The only present simulcast layer should be active if any of the
- // configured SVC layers is active.
- active_layers[0] =
- absl::c_any_of(rtp_parameters_.encodings,
- [](const auto& encoding) { return encoding.active; });
- }
- // This updates what simulcast layers are sending, and possibly starts
- // or stops the VideoSendStream.
- stream_->StartPerRtpStream(active_layers);
- } else {
- if (stream_ != nullptr) {
- stream_->Stop();
- }
- }
-}
-
-webrtc::VideoEncoderConfig
-WebRtcVideoChannel::WebRtcVideoSendStream::CreateVideoEncoderConfig(
- const VideoCodec& codec) const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- webrtc::VideoEncoderConfig encoder_config;
- encoder_config.codec_type = webrtc::PayloadStringToCodecType(codec.name);
- encoder_config.video_format =
- webrtc::SdpVideoFormat(codec.name, codec.params);
-
- bool is_screencast = parameters_.options.is_screencast.value_or(false);
- if (is_screencast) {
- encoder_config.min_transmit_bitrate_bps =
- 1000 * parameters_.options.screencast_min_bitrate_kbps.value_or(0);
- encoder_config.content_type =
- webrtc::VideoEncoderConfig::ContentType::kScreen;
- } else {
- encoder_config.min_transmit_bitrate_bps = 0;
- encoder_config.content_type =
- webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo;
- }
-
- // By default, the stream count for the codec configuration should match the
- // number of negotiated ssrcs but this may be capped below depending on the
- // `legacy_scalability_mode` and codec used.
- encoder_config.number_of_streams = parameters_.config.rtp.ssrcs.size();
- bool legacy_scalability_mode = true;
- // TODO(https://crbug.com/webrtc/14884): This is only used as a kill-switch
- // in case of serious bugs - when this reaches Stable, delete the field trial.
- if (!call_->trials().IsDisabled("WebRTC-AllowDisablingLegacyScalability")) {
- for (const webrtc::RtpEncodingParameters& encoding :
- rtp_parameters_.encodings) {
- if (encoding.scalability_mode.has_value() &&
- encoding.scale_resolution_down_by.has_value()) {
- legacy_scalability_mode = false;
- break;
- }
- }
- }
- // Maybe limit the number of simulcast layers depending on
- // `legacy_scalability_mode`, codec types (VP9/AV1). This path only exists
- // for backwards compatibility and will one day be deleted. If you want SVC,
- // please specify with the `scalability_mode` API instead amd disabling all
- // but one encoding.
- if (IsCodecDisabledForSimulcast(legacy_scalability_mode,
- encoder_config.codec_type)) {
- encoder_config.number_of_streams = 1;
- }
-
- // parameters_.max_bitrate comes from the max bitrate set at the SDP
- // (m-section) level with the attribute "b=AS." Note that we override this
- // value below if the RtpParameters max bitrate set with
- // RtpSender::SetParameters has a lower value.
- int stream_max_bitrate = parameters_.max_bitrate_bps;
- // When simulcast is enabled (when there are multiple encodings),
- // encodings[i].max_bitrate_bps will be enforced by
- // encoder_config.simulcast_layers[i].max_bitrate_bps. Otherwise, it's
- // enforced by stream_max_bitrate, taking the minimum of the two maximums
- // (one coming from SDP, the other coming from RtpParameters).
- if (rtp_parameters_.encodings[0].max_bitrate_bps &&
- rtp_parameters_.encodings.size() == 1) {
- stream_max_bitrate =
- MinPositive(*(rtp_parameters_.encodings[0].max_bitrate_bps),
- parameters_.max_bitrate_bps);
- }
-
- // The codec max bitrate comes from the "x-google-max-bitrate" parameter
- // attribute set in the SDP for a specific codec. As done in
- // WebRtcVideoChannel::SetSendParameters, this value does not override the
- // stream max_bitrate set above.
- int codec_max_bitrate_kbps;
- if (codec.GetParam(kCodecParamMaxBitrate, &codec_max_bitrate_kbps) &&
- stream_max_bitrate == -1) {
- stream_max_bitrate = codec_max_bitrate_kbps * 1000;
- }
- encoder_config.max_bitrate_bps = stream_max_bitrate;
-
- // The encoder config's default bitrate priority is set to 1.0,
- // unless it is set through the sender's encoding parameters.
- // The bitrate priority, which is used in the bitrate allocation, is done
- // on a per sender basis, so we use the first encoding's value.
- encoder_config.bitrate_priority =
- rtp_parameters_.encodings[0].bitrate_priority;
-
- // Application-controlled state is held in the encoder_config's
- // simulcast_layers. Currently this is used to control which simulcast layers
- // are active and for configuring the min/max bitrate and max framerate.
- // The encoder_config's simulcast_layers is also used for non-simulcast (when
- // there is a single layer).
- RTC_DCHECK_GE(rtp_parameters_.encodings.size(),
- encoder_config.number_of_streams);
- RTC_DCHECK_GT(encoder_config.number_of_streams, 0);
-
- // Copy all provided constraints.
- encoder_config.simulcast_layers.resize(rtp_parameters_.encodings.size());
- for (size_t i = 0; i < encoder_config.simulcast_layers.size(); ++i) {
- encoder_config.simulcast_layers[i].active =
- rtp_parameters_.encodings[i].active;
- encoder_config.simulcast_layers[i].scalability_mode =
- webrtc::ScalabilityModeFromString(
- rtp_parameters_.encodings[i].scalability_mode.value_or(""));
- if (rtp_parameters_.encodings[i].min_bitrate_bps) {
- encoder_config.simulcast_layers[i].min_bitrate_bps =
- *rtp_parameters_.encodings[i].min_bitrate_bps;
- }
- if (rtp_parameters_.encodings[i].max_bitrate_bps) {
- encoder_config.simulcast_layers[i].max_bitrate_bps =
- *rtp_parameters_.encodings[i].max_bitrate_bps;
- }
- if (rtp_parameters_.encodings[i].max_framerate) {
- encoder_config.simulcast_layers[i].max_framerate =
- *rtp_parameters_.encodings[i].max_framerate;
- }
- if (rtp_parameters_.encodings[i].scale_resolution_down_by) {
- encoder_config.simulcast_layers[i].scale_resolution_down_by =
- *rtp_parameters_.encodings[i].scale_resolution_down_by;
- }
- if (rtp_parameters_.encodings[i].num_temporal_layers) {
- encoder_config.simulcast_layers[i].num_temporal_layers =
- *rtp_parameters_.encodings[i].num_temporal_layers;
- }
- encoder_config.simulcast_layers[i].requested_resolution =
- rtp_parameters_.encodings[i].requested_resolution;
- }
-
- encoder_config.legacy_conference_mode = parameters_.conference_mode;
-
- encoder_config.is_quality_scaling_allowed =
- !disable_automatic_resize_ && !is_screencast &&
- (parameters_.config.rtp.ssrcs.size() == 1 ||
- NumActiveStreams(rtp_parameters_) == 1);
-
- // Ensure frame dropping is always enabled.
- encoder_config.frame_drop_enabled = true;
-
- int max_qp = kDefaultQpMax;
- codec.GetParam(kCodecParamMaxQuantization, &max_qp);
- encoder_config.max_qp = max_qp;
-
- return encoder_config;
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::ReconfigureEncoder(
- webrtc::SetParametersCallback callback) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (!stream_) {
- // The webrtc::VideoSendStream `stream_` has not yet been created but other
- // parameters has changed.
- webrtc::InvokeSetParametersCallback(callback, webrtc::RTCError::OK());
- return;
- }
-
- RTC_DCHECK_GT(parameters_.encoder_config.number_of_streams, 0);
-
- RTC_CHECK(parameters_.codec_settings);
- VideoCodecSettings codec_settings = *parameters_.codec_settings;
-
- FallbackToDefaultScalabilityModeIfNotSupported(
- codec_settings.codec, parameters_.config, rtp_parameters_.encodings);
-
- // Latest config, with and without encoder specfic settings.
- webrtc::VideoEncoderConfig encoder_config =
- CreateVideoEncoderConfig(codec_settings.codec);
- encoder_config.encoder_specific_settings =
- ConfigureVideoEncoderSettings(codec_settings.codec);
- webrtc::VideoEncoderConfig encoder_config_with_specifics =
- encoder_config.Copy();
- encoder_config.encoder_specific_settings = nullptr;
-
- // When switching between legacy SVC (3 encodings interpreted as 1 stream with
- // 3 spatial layers) and the standard API (3 encodings = 3 streams and spatial
- // layers specified by `scalability_mode`), the number of streams can change.
- bool num_streams_changed = parameters_.encoder_config.number_of_streams !=
- encoder_config.number_of_streams;
- bool scalability_mode_used = !codec_settings.codec.scalability_modes.empty();
- bool scalability_modes = absl::c_any_of(
- rtp_parameters_.encodings,
- [](const auto& e) { return e.scalability_mode.has_value(); });
-
- parameters_.encoder_config = std::move(encoder_config);
-
- if (num_streams_changed && (scalability_mode_used != scalability_modes)) {
- // The app is switching between legacy and standard modes, recreate instead
- // of reconfiguring to avoid number of streams not matching in lower layers.
- RecreateWebRtcStream();
- webrtc::InvokeSetParametersCallback(callback, webrtc::RTCError::OK());
- return;
- }
-
- stream_->ReconfigureVideoEncoder(std::move(encoder_config_with_specifics),
- std::move(callback));
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::SetSend(bool send) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- sending_ = send;
- UpdateSendState();
-}
-
-std::vector<VideoSenderInfo>
-WebRtcVideoChannel::WebRtcVideoSendStream::GetPerLayerVideoSenderInfos(
- bool log_stats) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- VideoSenderInfo common_info;
- if (parameters_.codec_settings) {
- common_info.codec_name = parameters_.codec_settings->codec.name;
- common_info.codec_payload_type = parameters_.codec_settings->codec.id;
- }
- std::vector<VideoSenderInfo> infos;
- webrtc::VideoSendStream::Stats stats;
- if (stream_ == nullptr) {
- for (uint32_t ssrc : parameters_.config.rtp.ssrcs) {
- common_info.add_ssrc(ssrc);
- }
- infos.push_back(common_info);
- return infos;
- } else {
- stats = stream_->GetStats();
- if (log_stats)
- RTC_LOG(LS_INFO) << stats.ToString(rtc::TimeMillis());
-
- // Metrics that are in common for all substreams.
- common_info.adapt_changes = stats.number_of_cpu_adapt_changes;
- common_info.adapt_reason =
- stats.cpu_limited_resolution ? ADAPTREASON_CPU : ADAPTREASON_NONE;
- common_info.has_entered_low_resolution = stats.has_entered_low_resolution;
-
- // Get bandwidth limitation info from stream_->GetStats().
- // Input resolution (output from video_adapter) can be further scaled down
- // or higher video layer(s) can be dropped due to bitrate constraints.
- // Note, adapt_changes only include changes from the video_adapter.
- if (stats.bw_limited_resolution)
- common_info.adapt_reason |= ADAPTREASON_BANDWIDTH;
-
- common_info.quality_limitation_reason = stats.quality_limitation_reason;
- common_info.quality_limitation_durations_ms =
- stats.quality_limitation_durations_ms;
- common_info.quality_limitation_resolution_changes =
- stats.quality_limitation_resolution_changes;
- common_info.encoder_implementation_name = stats.encoder_implementation_name;
- common_info.target_bitrate = stats.target_media_bitrate_bps;
- common_info.ssrc_groups = ssrc_groups_;
- common_info.frames = stats.frames;
- common_info.framerate_input = stats.input_frame_rate;
- common_info.avg_encode_ms = stats.avg_encode_time_ms;
- common_info.encode_usage_percent = stats.encode_usage_percent;
- common_info.nominal_bitrate = stats.media_bitrate_bps;
- common_info.content_type = stats.content_type;
- common_info.aggregated_framerate_sent = stats.encode_frame_rate;
- common_info.aggregated_huge_frames_sent = stats.huge_frames_sent;
- common_info.power_efficient_encoder = stats.power_efficient_encoder;
-
- // The normal case is that substreams are present, handled below. But if
- // substreams are missing (can happen before negotiated/connected where we
- // have no stats yet) a single outbound-rtp is created representing any and
- // all layers.
- if (stats.substreams.empty()) {
- for (uint32_t ssrc : parameters_.config.rtp.ssrcs) {
- common_info.add_ssrc(ssrc);
- }
- common_info.active =
- IsActiveFromEncodings(absl::nullopt, rtp_parameters_.encodings);
- common_info.framerate_sent = stats.encode_frame_rate;
- common_info.frames_encoded = stats.frames_encoded;
- common_info.total_encode_time_ms = stats.total_encode_time_ms;
- common_info.total_encoded_bytes_target = stats.total_encoded_bytes_target;
- common_info.frames_sent = stats.frames_encoded;
- common_info.huge_frames_sent = stats.huge_frames_sent;
- infos.push_back(common_info);
- return infos;
- }
- }
- // Merge `stats.substreams`, which may contain additional SSRCs for RTX or
- // Flexfec, with media SSRCs. This results in a set of substreams that match
- // with the outbound-rtp stats objects.
- auto outbound_rtp_substreams =
- MergeInfoAboutOutboundRtpSubstreams(stats.substreams);
- // If SVC is used, one stream is configured but multiple encodings exist. This
- // is not spec-compliant, but it is how we've implemented SVC so this affects
- // how the RTP stream's "active" value is determined.
- bool is_svc = (parameters_.encoder_config.number_of_streams == 1 &&
- rtp_parameters_.encodings.size() > 1);
- for (const auto& pair : outbound_rtp_substreams) {
- auto info = common_info;
- uint32_t ssrc = pair.first;
- info.add_ssrc(ssrc);
- info.rid = parameters_.config.rtp.GetRidForSsrc(ssrc);
- info.active = IsActiveFromEncodings(
- !is_svc ? absl::optional<uint32_t>(ssrc) : absl::nullopt,
- rtp_parameters_.encodings);
- auto stream_stats = pair.second;
- RTC_DCHECK_EQ(stream_stats.type,
- webrtc::VideoSendStream::StreamStats::StreamType::kMedia);
- info.payload_bytes_sent = stream_stats.rtp_stats.transmitted.payload_bytes;
- info.header_and_padding_bytes_sent =
- stream_stats.rtp_stats.transmitted.header_bytes +
- stream_stats.rtp_stats.transmitted.padding_bytes;
- info.packets_sent = stream_stats.rtp_stats.transmitted.packets;
- info.total_packet_send_delay +=
- stream_stats.rtp_stats.transmitted.total_packet_delay;
- info.send_frame_width = stream_stats.width;
- info.send_frame_height = stream_stats.height;
- info.key_frames_encoded = stream_stats.frame_counts.key_frames;
- info.framerate_sent = stream_stats.encode_frame_rate;
- info.frames_encoded = stream_stats.frames_encoded;
- info.frames_sent = stream_stats.frames_encoded;
- info.retransmitted_bytes_sent =
- stream_stats.rtp_stats.retransmitted.payload_bytes;
- info.retransmitted_packets_sent =
- stream_stats.rtp_stats.retransmitted.packets;
- info.firs_received = stream_stats.rtcp_packet_type_counts.fir_packets;
- info.nacks_received = stream_stats.rtcp_packet_type_counts.nack_packets;
- info.plis_received = stream_stats.rtcp_packet_type_counts.pli_packets;
- if (stream_stats.report_block_data.has_value()) {
- info.packets_lost = stream_stats.report_block_data->cumulative_lost();
- info.fraction_lost = stream_stats.report_block_data->fraction_lost();
- info.report_block_datas.push_back(*stream_stats.report_block_data);
- }
- info.qp_sum = stream_stats.qp_sum;
- info.total_encode_time_ms = stream_stats.total_encode_time_ms;
- info.total_encoded_bytes_target = stream_stats.total_encoded_bytes_target;
- info.huge_frames_sent = stream_stats.huge_frames_sent;
- info.scalability_mode = stream_stats.scalability_mode;
- infos.push_back(info);
- }
- return infos;
-}
-
-VideoSenderInfo
-WebRtcVideoChannel::WebRtcVideoSendStream::GetAggregatedVideoSenderInfo(
- const std::vector<VideoSenderInfo>& infos) const {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- RTC_CHECK(!infos.empty());
- if (infos.size() == 1) {
- return infos[0];
- }
- VideoSenderInfo info = infos[0];
- info.local_stats.clear();
- for (uint32_t ssrc : parameters_.config.rtp.ssrcs) {
- info.add_ssrc(ssrc);
- }
- info.framerate_sent = info.aggregated_framerate_sent;
- info.huge_frames_sent = info.aggregated_huge_frames_sent;
-
- for (size_t i = 1; i < infos.size(); i++) {
- info.key_frames_encoded += infos[i].key_frames_encoded;
- info.payload_bytes_sent += infos[i].payload_bytes_sent;
- info.header_and_padding_bytes_sent +=
- infos[i].header_and_padding_bytes_sent;
- info.packets_sent += infos[i].packets_sent;
- info.total_packet_send_delay += infos[i].total_packet_send_delay;
- info.retransmitted_bytes_sent += infos[i].retransmitted_bytes_sent;
- info.retransmitted_packets_sent += infos[i].retransmitted_packets_sent;
- info.packets_lost += infos[i].packets_lost;
- if (infos[i].send_frame_width > info.send_frame_width)
- info.send_frame_width = infos[i].send_frame_width;
- if (infos[i].send_frame_height > info.send_frame_height)
- info.send_frame_height = infos[i].send_frame_height;
- info.firs_received += infos[i].firs_received;
- info.nacks_received += infos[i].nacks_received;
- info.plis_received += infos[i].plis_received;
- if (infos[i].report_block_datas.size())
- info.report_block_datas.push_back(infos[i].report_block_datas[0]);
- if (infos[i].qp_sum) {
- if (!info.qp_sum) {
- info.qp_sum = 0;
- }
- info.qp_sum = *info.qp_sum + *infos[i].qp_sum;
- }
- info.frames_encoded += infos[i].frames_encoded;
- info.frames_sent += infos[i].frames_sent;
- info.total_encode_time_ms += infos[i].total_encode_time_ms;
- info.total_encoded_bytes_target += infos[i].total_encoded_bytes_target;
- }
- return info;
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::FillBitrateInfo(
- BandwidthEstimationInfo* bwe_info) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (stream_ == NULL) {
- return;
- }
- webrtc::VideoSendStream::Stats stats = stream_->GetStats();
- for (const auto& it : stats.substreams) {
- bwe_info->transmit_bitrate += it.second.total_bitrate_bps;
- bwe_info->retransmit_bitrate += it.second.retransmit_bitrate_bps;
- }
- bwe_info->target_enc_bitrate += stats.target_media_bitrate_bps;
- bwe_info->actual_enc_bitrate += stats.media_bitrate_bps;
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::
- SetEncoderToPacketizerFrameTransformer(
- rtc::scoped_refptr<webrtc::FrameTransformerInterface>
- frame_transformer) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- parameters_.config.frame_transformer = std::move(frame_transformer);
- if (stream_)
- RecreateWebRtcStream();
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::RecreateWebRtcStream() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (stream_ != NULL) {
- call_->DestroyVideoSendStream(stream_);
- }
-
- RTC_CHECK(parameters_.codec_settings);
- RTC_DCHECK_EQ((parameters_.encoder_config.content_type ==
- webrtc::VideoEncoderConfig::ContentType::kScreen),
- parameters_.options.is_screencast.value_or(false))
- << "encoder content type inconsistent with screencast option";
- parameters_.encoder_config.encoder_specific_settings =
- ConfigureVideoEncoderSettings(parameters_.codec_settings->codec);
-
- webrtc::VideoSendStream::Config config = parameters_.config.Copy();
- if (!config.rtp.rtx.ssrcs.empty() && config.rtp.rtx.payload_type == -1) {
- RTC_LOG(LS_WARNING) << "RTX SSRCs configured but there's no configured RTX "
- "payload type the set codec. Ignoring RTX.";
- config.rtp.rtx.ssrcs.clear();
- }
- if (parameters_.encoder_config.number_of_streams == 1) {
- // SVC is used instead of simulcast. Remove unnecessary SSRCs.
- if (config.rtp.ssrcs.size() > 1) {
- config.rtp.ssrcs.resize(1);
- if (config.rtp.rtx.ssrcs.size() > 1) {
- config.rtp.rtx.ssrcs.resize(1);
- }
- }
- }
- stream_ = call_->CreateVideoSendStream(std::move(config),
- parameters_.encoder_config.Copy());
-
- parameters_.encoder_config.encoder_specific_settings = NULL;
-
- // Calls stream_->StartPerRtpStream() to start the VideoSendStream
- // if necessary conditions are met.
- UpdateSendState();
-
- // Attach the source after starting the send stream to prevent frames from
- // being injected into a not-yet initializated video stream encoder.
- if (source_) {
- stream_->SetSource(source_, GetDegradationPreference());
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoSendStream::GenerateKeyFrame(
- const std::vector<std::string>& rids) {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- if (stream_ != NULL) {
- stream_->GenerateKeyFrame(rids);
- } else {
- RTC_LOG(LS_WARNING)
- << "Absent send stream; ignoring request to generate keyframe.";
- }
-}
-
-WebRtcVideoChannel::WebRtcVideoReceiveStream::WebRtcVideoReceiveStream(
- webrtc::Call* call,
- const StreamParams& sp,
- webrtc::VideoReceiveStreamInterface::Config config,
- bool default_stream,
- const std::vector<VideoCodecSettings>& recv_codecs,
- const webrtc::FlexfecReceiveStream::Config& flexfec_config)
- : call_(call),
- stream_params_(sp),
- stream_(NULL),
- default_stream_(default_stream),
- config_(std::move(config)),
- flexfec_config_(flexfec_config),
- flexfec_stream_(nullptr),
- sink_(NULL),
- first_frame_timestamp_(-1),
- estimated_remote_start_ntp_time_ms_(0),
- receiving_(false) {
- RTC_DCHECK(config_.decoder_factory);
- RTC_DCHECK(config_.decoders.empty())
- << "Decoder info is supplied via `recv_codecs`";
-
- ExtractCodecInformation(recv_codecs, config_.rtp.rtx_associated_payload_types,
- config_.rtp.raw_payload_types, config_.decoders);
- const VideoCodecSettings& codec = recv_codecs.front();
- config_.rtp.ulpfec_payload_type = codec.ulpfec.ulpfec_payload_type;
- config_.rtp.red_payload_type = codec.ulpfec.red_payload_type;
- config_.rtp.lntf.enabled = HasLntf(codec.codec);
- config_.rtp.nack.rtp_history_ms = HasNack(codec.codec) ? kNackHistoryMs : 0;
- if (codec.rtx_time && config_.rtp.nack.rtp_history_ms != 0) {
- config_.rtp.nack.rtp_history_ms = *codec.rtx_time;
- }
-
- config_.rtp.rtcp_xr.receiver_reference_time_report = HasRrtr(codec.codec);
-
- if (codec.ulpfec.red_rtx_payload_type != -1) {
- config_.rtp
- .rtx_associated_payload_types[codec.ulpfec.red_rtx_payload_type] =
- codec.ulpfec.red_payload_type;
- }
-
- config_.renderer = this;
- flexfec_config_.payload_type = flexfec_config.payload_type;
-
- CreateReceiveStream();
-}
-
-WebRtcVideoChannel::WebRtcVideoReceiveStream::~WebRtcVideoReceiveStream() {
- call_->DestroyVideoReceiveStream(stream_);
- if (flexfec_stream_)
- call_->DestroyFlexfecReceiveStream(flexfec_stream_);
-}
-
-webrtc::VideoReceiveStreamInterface&
-WebRtcVideoChannel::WebRtcVideoReceiveStream::stream() {
- RTC_DCHECK(stream_);
- return *stream_;
-}
-
-webrtc::FlexfecReceiveStream*
-WebRtcVideoChannel::WebRtcVideoReceiveStream::flexfec_stream() {
- return flexfec_stream_;
-}
-
-const std::vector<uint32_t>&
-WebRtcVideoChannel::WebRtcVideoReceiveStream::GetSsrcs() const {
- return stream_params_.ssrcs;
-}
-
-std::vector<webrtc::RtpSource>
-WebRtcVideoChannel::WebRtcVideoReceiveStream::GetSources() {
- RTC_DCHECK(stream_);
- return stream_->GetSources();
-}
-
-webrtc::RtpParameters
-WebRtcVideoChannel::WebRtcVideoReceiveStream::GetRtpParameters() const {
- webrtc::RtpParameters rtp_parameters;
-
- std::vector<uint32_t> primary_ssrcs;
- stream_params_.GetPrimarySsrcs(&primary_ssrcs);
- for (uint32_t ssrc : primary_ssrcs) {
- rtp_parameters.encodings.emplace_back();
- rtp_parameters.encodings.back().ssrc = ssrc;
- }
-
- rtp_parameters.rtcp.reduced_size =
- config_.rtp.rtcp_mode == webrtc::RtcpMode::kReducedSize;
-
- return rtp_parameters;
-}
-
-bool WebRtcVideoChannel::WebRtcVideoReceiveStream::ReconfigureCodecs(
- const std::vector<VideoCodecSettings>& recv_codecs) {
- RTC_DCHECK(stream_);
- RTC_DCHECK(!recv_codecs.empty());
-
- std::map<int, int> rtx_associated_payload_types;
- std::set<int> raw_payload_types;
- std::vector<webrtc::VideoReceiveStreamInterface::Decoder> decoders;
- ExtractCodecInformation(recv_codecs, rtx_associated_payload_types,
- raw_payload_types, decoders);
-
- const auto& codec = recv_codecs.front();
-
- if (config_.rtp.red_payload_type != codec.ulpfec.red_payload_type ||
- config_.rtp.ulpfec_payload_type != codec.ulpfec.ulpfec_payload_type) {
- config_.rtp.ulpfec_payload_type = codec.ulpfec.ulpfec_payload_type;
- config_.rtp.red_payload_type = codec.ulpfec.red_payload_type;
- stream_->SetProtectionPayloadTypes(config_.rtp.red_payload_type,
- config_.rtp.ulpfec_payload_type);
- }
-
- const bool has_lntf = HasLntf(codec.codec);
- if (config_.rtp.lntf.enabled != has_lntf) {
- config_.rtp.lntf.enabled = has_lntf;
- stream_->SetLossNotificationEnabled(has_lntf);
- }
-
- int new_history_ms = config_.rtp.nack.rtp_history_ms;
- const int rtp_history_ms = HasNack(codec.codec) ? kNackHistoryMs : 0;
- if (rtp_history_ms != config_.rtp.nack.rtp_history_ms) {
- new_history_ms = rtp_history_ms;
- }
-
- // The rtx-time parameter can be used to override the hardcoded default for
- // the NACK buffer length.
- if (codec.rtx_time && new_history_ms != 0) {
- new_history_ms = *codec.rtx_time;
- }
-
- if (config_.rtp.nack.rtp_history_ms != new_history_ms) {
- config_.rtp.nack.rtp_history_ms = new_history_ms;
- stream_->SetNackHistory(webrtc::TimeDelta::Millis(new_history_ms));
- }
-
- const bool has_rtr = HasRrtr(codec.codec);
- if (has_rtr != config_.rtp.rtcp_xr.receiver_reference_time_report) {
- config_.rtp.rtcp_xr.receiver_reference_time_report = has_rtr;
- stream_->SetRtcpXr(config_.rtp.rtcp_xr);
- }
-
- if (codec.ulpfec.red_rtx_payload_type != -1) {
- rtx_associated_payload_types[codec.ulpfec.red_rtx_payload_type] =
- codec.ulpfec.red_payload_type;
- }
-
- if (config_.rtp.rtx_associated_payload_types !=
- rtx_associated_payload_types) {
- stream_->SetAssociatedPayloadTypes(rtx_associated_payload_types);
- rtx_associated_payload_types.swap(config_.rtp.rtx_associated_payload_types);
- }
-
- bool recreate_needed = false;
-
- if (raw_payload_types != config_.rtp.raw_payload_types) {
- raw_payload_types.swap(config_.rtp.raw_payload_types);
- recreate_needed = true;
- }
-
- if (decoders != config_.decoders) {
- decoders.swap(config_.decoders);
- recreate_needed = true;
- }
-
- return recreate_needed;
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetFeedbackParameters(
- bool lntf_enabled,
- bool nack_enabled,
- webrtc::RtcpMode rtcp_mode,
- absl::optional<int> rtx_time) {
- RTC_DCHECK(stream_);
-
- if (config_.rtp.rtcp_mode != rtcp_mode) {
- config_.rtp.rtcp_mode = rtcp_mode;
- stream_->SetRtcpMode(rtcp_mode);
-
- flexfec_config_.rtcp_mode = rtcp_mode;
- if (flexfec_stream_) {
- flexfec_stream_->SetRtcpMode(rtcp_mode);
- }
- }
-
- config_.rtp.lntf.enabled = lntf_enabled;
- stream_->SetLossNotificationEnabled(lntf_enabled);
-
- int nack_history_ms = nack_enabled ? rtx_time.value_or(kNackHistoryMs) : 0;
- config_.rtp.nack.rtp_history_ms = nack_history_ms;
- stream_->SetNackHistory(webrtc::TimeDelta::Millis(nack_history_ms));
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetFlexFecPayload(
- int payload_type) {
- // TODO(bugs.webrtc.org/11993, tommi): See if it is better to always have a
- // flexfec stream object around and instead of recreating the video stream,
- // reconfigure the flexfec object from within the rtp callback (soon to be on
- // the network thread).
- if (flexfec_stream_) {
- if (flexfec_stream_->payload_type() == payload_type) {
- RTC_DCHECK_EQ(flexfec_config_.payload_type, payload_type);
- return;
- }
-
- flexfec_config_.payload_type = payload_type;
- flexfec_stream_->SetPayloadType(payload_type);
-
- if (payload_type == -1) {
- stream_->SetFlexFecProtection(nullptr);
- call_->DestroyFlexfecReceiveStream(flexfec_stream_);
- flexfec_stream_ = nullptr;
- }
- } else if (payload_type != -1) {
- flexfec_config_.payload_type = payload_type;
- if (flexfec_config_.IsCompleteAndEnabled()) {
- flexfec_stream_ = call_->CreateFlexfecReceiveStream(flexfec_config_);
- stream_->SetFlexFecProtection(flexfec_stream_);
- }
- } else {
- // Noop. No flexfec stream exists and "new" payload_type == -1.
- RTC_DCHECK(!flexfec_config_.IsCompleteAndEnabled());
- flexfec_config_.payload_type = payload_type;
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetRecvParameters(
- const ChangedRecvParameters& params) {
- RTC_DCHECK(stream_);
- bool video_needs_recreation = false;
- if (params.codec_settings) {
- video_needs_recreation = ReconfigureCodecs(*params.codec_settings);
- }
-
- if (params.flexfec_payload_type)
- SetFlexFecPayload(*params.flexfec_payload_type);
-
- if (video_needs_recreation) {
- RecreateReceiveStream();
- } else {
- RTC_DLOG_F(LS_INFO) << "No receive stream recreate needed.";
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::RecreateReceiveStream() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- RTC_DCHECK(stream_);
- absl::optional<int> base_minimum_playout_delay_ms;
- absl::optional<webrtc::VideoReceiveStreamInterface::RecordingState>
- recording_state;
- if (stream_) {
- base_minimum_playout_delay_ms = stream_->GetBaseMinimumPlayoutDelayMs();
- recording_state = stream_->SetAndGetRecordingState(
- webrtc::VideoReceiveStreamInterface::RecordingState(),
- /*generate_key_frame=*/false);
- call_->DestroyVideoReceiveStream(stream_);
- stream_ = nullptr;
- }
-
- if (flexfec_stream_) {
- call_->DestroyFlexfecReceiveStream(flexfec_stream_);
- flexfec_stream_ = nullptr;
- }
-
- CreateReceiveStream();
-
- if (base_minimum_playout_delay_ms) {
- stream_->SetBaseMinimumPlayoutDelayMs(
- base_minimum_playout_delay_ms.value());
- }
- if (recording_state) {
- stream_->SetAndGetRecordingState(std::move(*recording_state),
- /*generate_key_frame=*/false);
- }
- if (receiving_) {
- StartReceiveStream();
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::CreateReceiveStream() {
- RTC_DCHECK(!stream_);
- RTC_DCHECK(!flexfec_stream_);
- if (flexfec_config_.IsCompleteAndEnabled()) {
- flexfec_stream_ = call_->CreateFlexfecReceiveStream(flexfec_config_);
- }
-
- webrtc::VideoReceiveStreamInterface::Config config = config_.Copy();
- config.rtp.protected_by_flexfec = (flexfec_stream_ != nullptr);
- config.rtp.packet_sink_ = flexfec_stream_;
- stream_ = call_->CreateVideoReceiveStream(std::move(config));
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::StartReceiveStream() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- receiving_ = true;
- stream_->Start();
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::StopReceiveStream() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- receiving_ = false;
- stream_->Stop();
- RecreateReceiveStream();
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::OnFrame(
- const webrtc::VideoFrame& frame) {
- webrtc::MutexLock lock(&sink_lock_);
-
- int64_t time_now_ms = rtc::TimeMillis();
- if (first_frame_timestamp_ < 0)
- first_frame_timestamp_ = time_now_ms;
- int64_t elapsed_time_ms = time_now_ms - first_frame_timestamp_;
- if (frame.ntp_time_ms() > 0)
- estimated_remote_start_ntp_time_ms_ = frame.ntp_time_ms() - elapsed_time_ms;
-
- if (sink_ == NULL) {
- RTC_LOG(LS_WARNING)
- << "VideoReceiveStreamInterface not connected to a VideoSink.";
- return;
- }
-
- sink_->OnFrame(frame);
-}
-
-bool WebRtcVideoChannel::WebRtcVideoReceiveStream::IsDefaultStream() const {
- return default_stream_;
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetFrameDecryptor(
- rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
- config_.frame_decryptor = frame_decryptor;
- if (stream_) {
- RTC_LOG(LS_INFO)
- << "Setting FrameDecryptor (recv) because of SetFrameDecryptor, "
- "remote_ssrc="
- << config_.rtp.remote_ssrc;
- stream_->SetFrameDecryptor(frame_decryptor);
- }
-}
-
-bool WebRtcVideoChannel::WebRtcVideoReceiveStream::SetBaseMinimumPlayoutDelayMs(
- int delay_ms) {
- return stream_ ? stream_->SetBaseMinimumPlayoutDelayMs(delay_ms) : false;
-}
-
-int WebRtcVideoChannel::WebRtcVideoReceiveStream::GetBaseMinimumPlayoutDelayMs()
- const {
- return stream_ ? stream_->GetBaseMinimumPlayoutDelayMs() : 0;
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetSink(
- rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
- webrtc::MutexLock lock(&sink_lock_);
- sink_ = sink;
-}
-
-VideoReceiverInfo
-WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo(
- bool log_stats) {
- VideoReceiverInfo info;
- info.ssrc_groups = stream_params_.ssrc_groups;
- info.add_ssrc(config_.rtp.remote_ssrc);
- webrtc::VideoReceiveStreamInterface::Stats stats = stream_->GetStats();
- info.decoder_implementation_name = stats.decoder_implementation_name;
- info.power_efficient_decoder = stats.power_efficient_decoder;
- if (stats.current_payload_type != -1) {
- info.codec_payload_type = stats.current_payload_type;
- auto decoder_it = absl::c_find_if(config_.decoders, [&](const auto& d) {
- return d.payload_type == stats.current_payload_type;
- });
- if (decoder_it != config_.decoders.end())
- info.codec_name = decoder_it->video_format.name;
- }
- info.payload_bytes_received = stats.rtp_stats.packet_counter.payload_bytes;
- info.header_and_padding_bytes_received =
- stats.rtp_stats.packet_counter.header_bytes +
- stats.rtp_stats.packet_counter.padding_bytes;
- info.packets_received = stats.rtp_stats.packet_counter.packets;
- info.packets_lost = stats.rtp_stats.packets_lost;
- info.jitter_ms = stats.rtp_stats.jitter / (kVideoCodecClockrate / 1000);
-
- info.framerate_received = stats.network_frame_rate;
- info.framerate_decoded = stats.decode_frame_rate;
- info.framerate_output = stats.render_frame_rate;
- info.frame_width = stats.width;
- info.frame_height = stats.height;
-
- {
- webrtc::MutexLock frame_cs(&sink_lock_);
- info.capture_start_ntp_time_ms = estimated_remote_start_ntp_time_ms_;
- }
-
- info.decode_ms = stats.decode_ms;
- info.max_decode_ms = stats.max_decode_ms;
- info.current_delay_ms = stats.current_delay_ms;
- info.target_delay_ms = stats.target_delay_ms;
- info.jitter_buffer_ms = stats.jitter_buffer_ms;
- info.jitter_buffer_delay_seconds =
- stats.jitter_buffer_delay.seconds<double>();
- info.jitter_buffer_target_delay_seconds =
- stats.jitter_buffer_target_delay.seconds<double>();
- info.jitter_buffer_emitted_count = stats.jitter_buffer_emitted_count;
- info.jitter_buffer_minimum_delay_seconds =
- stats.jitter_buffer_minimum_delay.seconds<double>();
- info.min_playout_delay_ms = stats.min_playout_delay_ms;
- info.render_delay_ms = stats.render_delay_ms;
- info.frames_received =
- stats.frame_counts.key_frames + stats.frame_counts.delta_frames;
- info.frames_dropped = stats.frames_dropped;
- info.frames_decoded = stats.frames_decoded;
- info.key_frames_decoded = stats.frame_counts.key_frames;
- info.frames_rendered = stats.frames_rendered;
- info.qp_sum = stats.qp_sum;
- info.total_decode_time = stats.total_decode_time;
- info.total_processing_delay = stats.total_processing_delay;
- info.total_assembly_time = stats.total_assembly_time;
- info.frames_assembled_from_multiple_packets =
- stats.frames_assembled_from_multiple_packets;
- info.last_packet_received_timestamp_ms =
- stats.rtp_stats.last_packet_received_timestamp_ms;
- info.estimated_playout_ntp_timestamp_ms =
- stats.estimated_playout_ntp_timestamp_ms;
- info.first_frame_received_to_decoded_ms =
- stats.first_frame_received_to_decoded_ms;
- info.total_inter_frame_delay = stats.total_inter_frame_delay;
- info.total_squared_inter_frame_delay = stats.total_squared_inter_frame_delay;
- info.interframe_delay_max_ms = stats.interframe_delay_max_ms;
- info.freeze_count = stats.freeze_count;
- info.pause_count = stats.pause_count;
- info.total_freezes_duration_ms = stats.total_freezes_duration_ms;
- info.total_pauses_duration_ms = stats.total_pauses_duration_ms;
-
- info.content_type = stats.content_type;
-
- info.firs_sent = stats.rtcp_packet_type_counts.fir_packets;
- info.plis_sent = stats.rtcp_packet_type_counts.pli_packets;
- info.nacks_sent = stats.rtcp_packet_type_counts.nack_packets;
- // TODO(bugs.webrtc.org/10662): Add stats for LNTF.
-
- info.timing_frame_info = stats.timing_frame_info;
-
- if (stats.rtx_rtp_stats.has_value()) {
- info.retransmitted_packets_received =
- stats.rtx_rtp_stats->packet_counter.packets;
- info.retransmitted_bytes_received =
- stats.rtx_rtp_stats->packet_counter.payload_bytes;
- // RTX information gets added to primary counters.
- info.payload_bytes_received +=
- stats.rtx_rtp_stats->packet_counter.payload_bytes;
- info.header_and_padding_bytes_received +=
- stats.rtx_rtp_stats->packet_counter.header_bytes +
- stats.rtx_rtp_stats->packet_counter.padding_bytes;
- info.packets_received += stats.rtx_rtp_stats->packet_counter.packets;
- }
-
- if (log_stats)
- RTC_LOG(LS_INFO) << stats.ToString(rtc::TimeMillis());
-
- return info;
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::
- SetRecordableEncodedFrameCallback(
- std::function<void(const webrtc::RecordableEncodedFrame&)> callback) {
- if (stream_) {
- stream_->SetAndGetRecordingState(
- webrtc::VideoReceiveStreamInterface::RecordingState(
- std::move(callback)),
- /*generate_key_frame=*/true);
- } else {
- RTC_LOG(LS_ERROR) << "Absent receive stream; ignoring setting encoded "
- "frame sink";
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::
- ClearRecordableEncodedFrameCallback() {
- if (stream_) {
- stream_->SetAndGetRecordingState(
- webrtc::VideoReceiveStreamInterface::RecordingState(),
- /*generate_key_frame=*/false);
- } else {
- RTC_LOG(LS_ERROR) << "Absent receive stream; ignoring clearing encoded "
- "frame sink";
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::GenerateKeyFrame() {
- if (stream_) {
- stream_->GenerateKeyFrame();
- } else {
- RTC_LOG(LS_ERROR)
- << "Absent receive stream; ignoring key frame generation request.";
- }
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::
- SetDepacketizerToDecoderFrameTransformer(
- rtc::scoped_refptr<webrtc::FrameTransformerInterface>
- frame_transformer) {
- config_.frame_transformer = frame_transformer;
- if (stream_)
- stream_->SetDepacketizerToDecoderFrameTransformer(frame_transformer);
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetLocalSsrc(uint32_t ssrc) {
- config_.rtp.local_ssrc = ssrc;
- call_->OnLocalSsrcUpdated(stream(), ssrc);
- if (flexfec_stream_)
- call_->OnLocalSsrcUpdated(*flexfec_stream_, ssrc);
-}
-
-void WebRtcVideoChannel::WebRtcVideoReceiveStream::UpdateRtxSsrc(
- uint32_t ssrc) {
- stream_->UpdateRtxSsrc(ssrc);
-}
-
-WebRtcVideoChannel::VideoCodecSettings::VideoCodecSettings()
- : flexfec_payload_type(-1), rtx_payload_type(-1) {}
-
-bool WebRtcVideoChannel::VideoCodecSettings::operator==(
- const WebRtcVideoChannel::VideoCodecSettings& other) const {
- return codec == other.codec && ulpfec == other.ulpfec &&
- flexfec_payload_type == other.flexfec_payload_type &&
- rtx_payload_type == other.rtx_payload_type &&
- rtx_time == other.rtx_time;
-}
-
-bool WebRtcVideoChannel::VideoCodecSettings::EqualsDisregardingFlexfec(
- const WebRtcVideoChannel::VideoCodecSettings& a,
- const WebRtcVideoChannel::VideoCodecSettings& b) {
- return a.codec == b.codec && a.ulpfec == b.ulpfec &&
- a.rtx_payload_type == b.rtx_payload_type && a.rtx_time == b.rtx_time;
-}
-
-bool WebRtcVideoChannel::VideoCodecSettings::operator!=(
- const WebRtcVideoChannel::VideoCodecSettings& other) const {
- return !(*this == other);
-}
-
-std::vector<WebRtcVideoChannel::VideoCodecSettings>
-WebRtcVideoChannel::MapCodecs(const std::vector<VideoCodec>& codecs) {
+// Generate the list of codec parameters to pass down based on the negotiated
+// "codecs". Note that VideoCodecSettings correspond to concrete codecs like
+// VP8, VP9, H264 while VideoCodecs correspond also to "virtual" codecs like
+// RTX, ULPFEC, FLEXFEC.
+std::vector<VideoCodecSettings> MapCodecs(
+ const std::vector<VideoCodec>& codecs) {
if (codecs.empty()) {
return {};
}
@@ -3693,8 +656,3045 @@
return video_codecs;
}
-WebRtcVideoChannel::WebRtcVideoReceiveStream*
-WebRtcVideoChannel::FindReceiveStream(uint32_t ssrc) {
+bool NonFlexfecReceiveCodecsHaveChanged(std::vector<VideoCodecSettings> before,
+ std::vector<VideoCodecSettings> after) {
+ // The receive codec order doesn't matter, so we sort the codecs before
+ // comparing. This is necessary because currently the
+ // only way to change the send codec is to munge SDP, which causes
+ // the receive codec list to change order, which causes the streams
+ // to be recreates which causes a "blink" of black video. In order
+ // to support munging the SDP in this way without recreating receive
+ // streams, we ignore the order of the received codecs so that
+ // changing the order doesn't cause this "blink".
+ auto comparison = [](const VideoCodecSettings& codec1,
+ const VideoCodecSettings& codec2) {
+ return codec1.codec.id > codec2.codec.id;
+ };
+ absl::c_sort(before, comparison);
+ absl::c_sort(after, comparison);
+
+ // Changes in FlexFEC payload type are handled separately in
+ // WebRtcVideoChannel::GetChangedRecvParameters, so disregard FlexFEC in the
+ // comparison here.
+ return !absl::c_equal(before, after,
+ VideoCodecSettings::EqualsDisregardingFlexfec);
+}
+
+std::string CodecSettingsVectorToString(
+ const std::vector<VideoCodecSettings>& codecs) {
+ rtc::StringBuilder out;
+ out << "{";
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ out << codecs[i].codec.ToString();
+ if (i != codecs.size() - 1) {
+ out << ", ";
+ }
+ }
+ out << "}";
+ return out.Release();
+}
+
+void ExtractCodecInformation(
+ rtc::ArrayView<const VideoCodecSettings> recv_codecs,
+ std::map<int, int>& rtx_associated_payload_types,
+ std::set<int>& raw_payload_types,
+ std::vector<webrtc::VideoReceiveStreamInterface::Decoder>& decoders) {
+ RTC_DCHECK(!recv_codecs.empty());
+ RTC_DCHECK(rtx_associated_payload_types.empty());
+ RTC_DCHECK(raw_payload_types.empty());
+ RTC_DCHECK(decoders.empty());
+
+ for (const VideoCodecSettings& recv_codec : recv_codecs) {
+ decoders.emplace_back(
+ webrtc::SdpVideoFormat(recv_codec.codec.name, recv_codec.codec.params),
+ recv_codec.codec.id);
+ rtx_associated_payload_types.emplace(recv_codec.rtx_payload_type,
+ recv_codec.codec.id);
+ if (recv_codec.codec.packetization == kPacketizationParamRaw) {
+ raw_payload_types.insert(recv_codec.codec.id);
+ }
+ }
+}
+
+} // namespace
+
+std::map<uint32_t, webrtc::VideoSendStream::StreamStats>
+MergeInfoAboutOutboundRtpSubstreamsForTesting(
+ const std::map<uint32_t, webrtc::VideoSendStream::StreamStats>&
+ substreams) {
+ return MergeInfoAboutOutboundRtpSubstreams(substreams);
+}
+
+rtc::scoped_refptr<webrtc::VideoEncoderConfig::EncoderSpecificSettings>
+WebRtcVideoSendChannel::WebRtcVideoSendStream::ConfigureVideoEncoderSettings(
+ const VideoCodec& codec) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ bool is_screencast = parameters_.options.is_screencast.value_or(false);
+ // No automatic resizing when using simulcast or screencast, or when
+ // disabled by field trial flag.
+ bool automatic_resize = !disable_automatic_resize_ && !is_screencast &&
+ (parameters_.config.rtp.ssrcs.size() == 1 ||
+ NumActiveStreams(rtp_parameters_) == 1);
+
+ bool denoising;
+ bool codec_default_denoising = false;
+ if (is_screencast) {
+ denoising = false;
+ } else {
+ // Use codec default if video_noise_reduction is unset.
+ codec_default_denoising = !parameters_.options.video_noise_reduction;
+ denoising = parameters_.options.video_noise_reduction.value_or(false);
+ }
+
+ if (absl::EqualsIgnoreCase(codec.name, kH264CodecName)) {
+ return nullptr;
+ }
+ if (absl::EqualsIgnoreCase(codec.name, 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::make_ref_counted<
+ webrtc::VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8_settings);
+ }
+ if (absl::EqualsIgnoreCase(codec.name, kVp9CodecName)) {
+ webrtc::VideoCodecVP9 vp9_settings =
+ webrtc::VideoEncoder::GetDefaultVp9Settings();
+
+ vp9_settings.numberOfSpatialLayers = std::min<unsigned char>(
+ parameters_.config.rtp.ssrcs.size(), kConferenceMaxNumSpatialLayers);
+ vp9_settings.numberOfTemporalLayers =
+ std::min<unsigned char>(parameters_.config.rtp.ssrcs.size() > 1
+ ? kConferenceDefaultNumTemporalLayers
+ : 1,
+ kConferenceMaxNumTemporalLayers);
+
+ // VP9 denoising is disabled by default.
+ vp9_settings.denoisingOn = codec_default_denoising ? true : denoising;
+ // Disable automatic resize if more than one spatial layer is requested.
+ bool vp9_automatic_resize = automatic_resize;
+ absl::optional<int> num_spatial_layers =
+ NumSpatialLayersFromEncoding(rtp_parameters_, /*idx=*/0);
+ if (num_spatial_layers && *num_spatial_layers > 1) {
+ vp9_automatic_resize = false;
+ }
+ vp9_settings.automaticResizeOn = vp9_automatic_resize;
+ if (!is_screencast) {
+ webrtc::FieldTrialFlag interlayer_pred_experiment_enabled("Enabled");
+ webrtc::FieldTrialEnum<webrtc::InterLayerPredMode> inter_layer_pred_mode(
+ "inter_layer_pred_mode", webrtc::InterLayerPredMode::kOnKeyPic,
+ {{"off", webrtc::InterLayerPredMode::kOff},
+ {"on", webrtc::InterLayerPredMode::kOn},
+ {"onkeypic", webrtc::InterLayerPredMode::kOnKeyPic}});
+ webrtc::FieldTrialFlag force_flexible_mode("FlexibleMode");
+ webrtc::ParseFieldTrial(
+ {&interlayer_pred_experiment_enabled, &inter_layer_pred_mode,
+ &force_flexible_mode},
+ call_->trials().Lookup("WebRTC-Vp9InterLayerPred"));
+ if (interlayer_pred_experiment_enabled) {
+ vp9_settings.interLayerPred = inter_layer_pred_mode;
+ } else {
+ // Limit inter-layer prediction to key pictures by default.
+ vp9_settings.interLayerPred = webrtc::InterLayerPredMode::kOnKeyPic;
+ }
+ vp9_settings.flexibleMode = force_flexible_mode.Get();
+ } else {
+ // Multiple spatial layers vp9 screenshare needs flexible mode.
+ vp9_settings.flexibleMode = vp9_settings.numberOfSpatialLayers > 1;
+ vp9_settings.interLayerPred = webrtc::InterLayerPredMode::kOn;
+ }
+ return rtc::make_ref_counted<
+ webrtc::VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9_settings);
+ }
+ return nullptr;
+}
+
+WebRtcVideoEngine::WebRtcVideoEngine(
+ std::unique_ptr<webrtc::VideoEncoderFactory> video_encoder_factory,
+ std::unique_ptr<webrtc::VideoDecoderFactory> video_decoder_factory,
+ const webrtc::FieldTrialsView& trials)
+ : decoder_factory_(std::move(video_decoder_factory)),
+ encoder_factory_(std::move(video_encoder_factory)),
+ trials_(trials) {
+ RTC_DLOG(LS_INFO) << "WebRtcVideoEngine::WebRtcVideoEngine()";
+}
+
+WebRtcVideoEngine::~WebRtcVideoEngine() {
+ RTC_DLOG(LS_INFO) << "WebRtcVideoEngine::~WebRtcVideoEngine";
+}
+
+VideoMediaChannel* WebRtcVideoEngine::CreateMediaChannel(
+ MediaChannel::Role role,
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const VideoOptions& options,
+ const webrtc::CryptoOptions& crypto_options,
+ webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) {
+ RTC_LOG(LS_INFO) << "CreateMediaChannel. Options: " << options.ToString();
+ std::unique_ptr<VideoMediaSendChannelInterface> send_channel;
+ std::unique_ptr<VideoMediaReceiveChannelInterface> receive_channel;
+ if (role == MediaChannel::Role::kSend || role == MediaChannel::Role::kBoth) {
+ send_channel = std::make_unique<WebRtcVideoSendChannel>(
+ call, config, options, crypto_options, encoder_factory_.get(),
+ decoder_factory_.get(), video_bitrate_allocator_factory);
+ }
+ if (role == MediaChannel::Role::kReceive ||
+ role == MediaChannel::Role::kBoth) {
+ receive_channel = std::make_unique<WebRtcVideoReceiveChannel>(
+ call, config, options, crypto_options, decoder_factory_.get());
+ }
+ return new VideoMediaShimChannel(std::move(send_channel),
+ std::move(receive_channel));
+}
+
+std::vector<VideoCodec> WebRtcVideoEngine::send_codecs(bool include_rtx) const {
+ return GetPayloadTypesAndDefaultCodecs(encoder_factory_.get(),
+ /*is_decoder_factory=*/false,
+ include_rtx, trials_);
+}
+
+std::vector<VideoCodec> WebRtcVideoEngine::recv_codecs(bool include_rtx) const {
+ return GetPayloadTypesAndDefaultCodecs(decoder_factory_.get(),
+ /*is_decoder_factory=*/true,
+ include_rtx, trials_);
+}
+
+std::vector<webrtc::RtpHeaderExtensionCapability>
+WebRtcVideoEngine::GetRtpHeaderExtensions() const {
+ std::vector<webrtc::RtpHeaderExtensionCapability> result;
+ int id = 1;
+ for (const auto& uri :
+ {webrtc::RtpExtension::kTimestampOffsetUri,
+ webrtc::RtpExtension::kAbsSendTimeUri,
+ webrtc::RtpExtension::kVideoRotationUri,
+ webrtc::RtpExtension::kTransportSequenceNumberUri,
+ webrtc::RtpExtension::kPlayoutDelayUri,
+ webrtc::RtpExtension::kVideoContentTypeUri,
+ webrtc::RtpExtension::kVideoTimingUri,
+ webrtc::RtpExtension::kColorSpaceUri, webrtc::RtpExtension::kMidUri,
+ webrtc::RtpExtension::kRidUri, webrtc::RtpExtension::kRepairedRidUri}) {
+ result.emplace_back(uri, id++, webrtc::RtpTransceiverDirection::kSendRecv);
+ }
+ result.emplace_back(webrtc::RtpExtension::kGenericFrameDescriptorUri00, id++,
+ IsEnabled(trials_, "WebRTC-GenericDescriptorAdvertised")
+ ? webrtc::RtpTransceiverDirection::kSendRecv
+ : webrtc::RtpTransceiverDirection::kStopped);
+ result.emplace_back(
+ webrtc::RtpExtension::kDependencyDescriptorUri, id++,
+ IsEnabled(trials_, "WebRTC-DependencyDescriptorAdvertised")
+ ? webrtc::RtpTransceiverDirection::kSendRecv
+ : webrtc::RtpTransceiverDirection::kStopped);
+
+ result.emplace_back(
+ webrtc::RtpExtension::kVideoLayersAllocationUri, id++,
+ IsEnabled(trials_, "WebRTC-VideoLayersAllocationAdvertised")
+ ? webrtc::RtpTransceiverDirection::kSendRecv
+ : webrtc::RtpTransceiverDirection::kStopped);
+
+ result.emplace_back(
+ webrtc::RtpExtension::kVideoFrameTrackingIdUri, id++,
+ IsEnabled(trials_, "WebRTC-VideoFrameTrackingIdAdvertised")
+ ? webrtc::RtpTransceiverDirection::kSendRecv
+ : webrtc::RtpTransceiverDirection::kStopped);
+
+ return result;
+}
+
+WebRtcVideoSendChannel::WebRtcVideoSendChannel(
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const VideoOptions& options,
+ const webrtc::CryptoOptions& crypto_options,
+ webrtc::VideoEncoderFactory* encoder_factory,
+ webrtc::VideoDecoderFactory* decoder_factory,
+ webrtc::VideoBitrateAllocatorFactory* bitrate_allocator_factory)
+ : MediaChannelUtil(call->network_thread(), config.enable_dscp),
+ worker_thread_(call->worker_thread()),
+ sending_(false),
+ receiving_(false),
+ call_(call),
+ default_sink_(nullptr),
+ video_config_(config.video),
+ encoder_factory_(encoder_factory),
+ decoder_factory_(decoder_factory),
+ bitrate_allocator_factory_(bitrate_allocator_factory),
+ default_send_options_(options),
+ last_send_stats_log_ms_(-1),
+ last_receive_stats_log_ms_(-1),
+ discard_unknown_ssrc_packets_(
+ IsEnabled(call_->trials(),
+ "WebRTC-Video-DiscardPacketsWithUnknownSsrc")),
+ crypto_options_(crypto_options) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc;
+ recv_codecs_ = MapCodecs(GetPayloadTypesAndDefaultCodecs(
+ decoder_factory_, /*is_decoder_factory=*/true,
+ /*include_rtx=*/true, call_->trials()));
+ recv_flexfec_payload_type_ =
+ recv_codecs_.empty() ? 0 : recv_codecs_.front().flexfec_payload_type;
+}
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveChannel(
+ webrtc::Call* call,
+ const MediaConfig& config,
+ const VideoOptions& options,
+ const webrtc::CryptoOptions& crypto_options,
+ webrtc::VideoDecoderFactory* decoder_factory)
+ : MediaChannelUtil(call->network_thread(), config.enable_dscp),
+ worker_thread_(call->worker_thread()),
+ receiving_(false),
+ call_(call),
+ default_sink_(nullptr),
+ video_config_(config.video),
+ decoder_factory_(decoder_factory),
+ default_send_options_(options),
+ last_receive_stats_log_ms_(-1),
+ discard_unknown_ssrc_packets_(
+ IsEnabled(call_->trials(),
+ "WebRTC-Video-DiscardPacketsWithUnknownSsrc")),
+ crypto_options_(crypto_options) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc;
+ recv_codecs_ = MapCodecs(GetPayloadTypesAndDefaultCodecs(
+ decoder_factory_, /*is_decoder_factory=*/true,
+ /*include_rtx=*/true, call_->trials()));
+ recv_flexfec_payload_type_ =
+ recv_codecs_.empty() ? 0 : recv_codecs_.front().flexfec_payload_type;
+}
+
+WebRtcVideoSendChannel::~WebRtcVideoSendChannel() {
+ for (auto& kv : send_streams_)
+ delete kv.second;
+}
+
+WebRtcVideoReceiveChannel::~WebRtcVideoReceiveChannel() {
+ for (auto& kv : receive_streams_)
+ delete kv.second;
+}
+
+std::vector<VideoCodecSettings> WebRtcVideoSendChannel::SelectSendVideoCodecs(
+ const std::vector<VideoCodecSettings>& remote_mapped_codecs) const {
+ std::vector<webrtc::SdpVideoFormat> sdp_formats =
+ encoder_factory_ ? encoder_factory_->GetImplementations()
+ : std::vector<webrtc::SdpVideoFormat>();
+
+ // The returned vector holds the VideoCodecSettings in term of preference.
+ // They are orderd by receive codec preference first and local implementation
+ // preference second.
+ std::vector<VideoCodecSettings> encoders;
+ for (const VideoCodecSettings& remote_codec : remote_mapped_codecs) {
+ for (auto format_it = sdp_formats.begin();
+ format_it != sdp_formats.end();) {
+ // For H264, we will limit the encode level to the remote offered level
+ // regardless if level asymmetry is allowed or not. This is strictly not
+ // following the spec in https://tools.ietf.org/html/rfc6184#section-8.2.2
+ // since we should limit the encode level to the lower of local and remote
+ // level when level asymmetry is not allowed.
+ if (format_it->IsSameCodec(
+ {remote_codec.codec.name, remote_codec.codec.params})) {
+ encoders.push_back(remote_codec);
+
+ // To allow the VideoEncoderFactory to keep information about which
+ // implementation to instantitate when CreateEncoder is called the two
+ // parmeter sets are merged.
+ encoders.back().codec.params.insert(format_it->parameters.begin(),
+ format_it->parameters.end());
+
+ format_it = sdp_formats.erase(format_it);
+ } else {
+ ++format_it;
+ }
+ }
+ }
+
+ return encoders;
+}
+
+bool WebRtcVideoSendChannel::GetChangedSendParameters(
+ const VideoSendParameters& params,
+ ChangedSendParameters* changed_params) const {
+ if (!ValidateCodecFormats(params.codecs) ||
+ !ValidateRtpExtensions(params.extensions, send_rtp_extensions_)) {
+ return false;
+ }
+
+ std::vector<VideoCodecSettings> negotiated_codecs =
+ SelectSendVideoCodecs(MapCodecs(params.codecs));
+
+ // We should only fail here if send direction is enabled.
+ if (params.is_stream_active && negotiated_codecs.empty()) {
+ RTC_LOG(LS_ERROR) << "No video codecs supported.";
+ return false;
+ }
+
+ // Never enable sending FlexFEC, unless we are in the experiment.
+ if (!IsEnabled(call_->trials(), "WebRTC-FlexFEC-03")) {
+ for (VideoCodecSettings& codec : negotiated_codecs)
+ codec.flexfec_payload_type = -1;
+ }
+
+ if (negotiated_codecs_ != negotiated_codecs) {
+ if (negotiated_codecs.empty()) {
+ changed_params->send_codec = absl::nullopt;
+ } else if (send_codec() != negotiated_codecs.front()) {
+ changed_params->send_codec = negotiated_codecs.front();
+ }
+ changed_params->negotiated_codecs = std::move(negotiated_codecs);
+ }
+
+ // Handle RTP header extensions.
+ if (params.extmap_allow_mixed != ExtmapAllowMixed()) {
+ changed_params->extmap_allow_mixed = params.extmap_allow_mixed;
+ }
+ std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(
+ params.extensions, webrtc::RtpExtension::IsSupportedForVideo, true,
+ call_->trials());
+ if (send_rtp_extensions_ != filtered_extensions) {
+ changed_params->rtp_header_extensions =
+ absl::optional<std::vector<webrtc::RtpExtension>>(filtered_extensions);
+ }
+
+ if (params.mid != send_params_.mid) {
+ changed_params->mid = params.mid;
+ }
+
+ // Handle max bitrate.
+ if (params.max_bandwidth_bps != send_params_.max_bandwidth_bps &&
+ params.max_bandwidth_bps >= -1) {
+ // 0 or -1 uncaps max bitrate.
+ // TODO(pbos): Reconsider how 0 should be treated. It is not mentioned as a
+ // special value and might very well be used for stopping sending.
+ changed_params->max_bandwidth_bps =
+ params.max_bandwidth_bps == 0 ? -1 : params.max_bandwidth_bps;
+ }
+
+ // Handle conference mode.
+ if (params.conference_mode != send_params_.conference_mode) {
+ changed_params->conference_mode = params.conference_mode;
+ }
+
+ // Handle RTCP mode.
+ if (params.rtcp.reduced_size != send_params_.rtcp.reduced_size) {
+ changed_params->rtcp_mode = params.rtcp.reduced_size
+ ? webrtc::RtcpMode::kReducedSize
+ : webrtc::RtcpMode::kCompound;
+ }
+
+ return true;
+}
+
+bool WebRtcVideoSendChannel::SetSendParameters(
+ const VideoSendParameters& params) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetSendParameters");
+ RTC_LOG(LS_INFO) << "SetSendParameters: " << params.ToString();
+ ChangedSendParameters changed_params;
+ if (!GetChangedSendParameters(params, &changed_params)) {
+ return false;
+ }
+
+ if (changed_params.negotiated_codecs) {
+ for (const auto& send_codec : *changed_params.negotiated_codecs)
+ RTC_LOG(LS_INFO) << "Negotiated codec: " << send_codec.codec.ToString();
+ }
+
+ send_params_ = params;
+ return ApplyChangedParams(changed_params);
+}
+
+void WebRtcVideoSendChannel::RequestEncoderFallback() {
+ if (!worker_thread_->IsCurrent()) {
+ worker_thread_->PostTask(
+ SafeTask(task_safety_.flag(), [this] { RequestEncoderFallback(); }));
+ return;
+ }
+
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (negotiated_codecs_.size() <= 1) {
+ RTC_LOG(LS_WARNING) << "Encoder failed but no fallback codec is available";
+ return;
+ }
+
+ ChangedSendParameters params;
+ params.negotiated_codecs = negotiated_codecs_;
+ params.negotiated_codecs->erase(params.negotiated_codecs->begin());
+ params.send_codec = params.negotiated_codecs->front();
+ ApplyChangedParams(params);
+}
+
+void WebRtcVideoSendChannel::RequestEncoderSwitch(
+ const webrtc::SdpVideoFormat& format,
+ bool allow_default_fallback) {
+ if (!worker_thread_->IsCurrent()) {
+ worker_thread_->PostTask(
+ SafeTask(task_safety_.flag(), [this, format, allow_default_fallback] {
+ RequestEncoderSwitch(format, allow_default_fallback);
+ }));
+ return;
+ }
+
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+
+ for (const VideoCodecSettings& codec_setting : negotiated_codecs_) {
+ if (format.IsSameCodec(
+ {codec_setting.codec.name, codec_setting.codec.params})) {
+ VideoCodecSettings new_codec_setting = codec_setting;
+ for (const auto& kv : format.parameters) {
+ new_codec_setting.codec.params[kv.first] = kv.second;
+ }
+
+ if (send_codec() == new_codec_setting) {
+ // Already using this codec, no switch required.
+ return;
+ }
+
+ ChangedSendParameters params;
+ params.send_codec = new_codec_setting;
+ ApplyChangedParams(params);
+ return;
+ }
+ }
+
+ RTC_LOG(LS_WARNING) << "Failed to switch encoder to: " << format.ToString()
+ << ". Is default fallback allowed: "
+ << allow_default_fallback;
+
+ if (allow_default_fallback) {
+ RequestEncoderFallback();
+ }
+}
+
+bool WebRtcVideoSendChannel::ApplyChangedParams(
+ const ChangedSendParameters& changed_params) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (changed_params.negotiated_codecs)
+ negotiated_codecs_ = *changed_params.negotiated_codecs;
+
+ if (changed_params.send_codec)
+ send_codec() = changed_params.send_codec;
+
+ if (changed_params.extmap_allow_mixed) {
+ SetExtmapAllowMixed(*changed_params.extmap_allow_mixed);
+ }
+ if (changed_params.rtp_header_extensions) {
+ send_rtp_extensions_ = *changed_params.rtp_header_extensions;
+ }
+
+ if (changed_params.send_codec || changed_params.max_bandwidth_bps) {
+ if (send_params_.max_bandwidth_bps == -1) {
+ // Unset the global max bitrate (max_bitrate_bps) if max_bandwidth_bps is
+ // -1, which corresponds to no "b=AS" attribute in SDP. Note that the
+ // global max bitrate may be set below in GetBitrateConfigForCodec, from
+ // the codec max bitrate.
+ // TODO(pbos): This should be reconsidered (codec max bitrate should
+ // probably not affect global call max bitrate).
+ bitrate_config_.max_bitrate_bps = -1;
+ }
+
+ if (send_codec()) {
+ // TODO(holmer): Changing the codec parameters shouldn't necessarily mean
+ // that we change the min/max of bandwidth estimation. Reevaluate this.
+ bitrate_config_ = GetBitrateConfigForCodec(send_codec()->codec);
+ if (!changed_params.send_codec) {
+ // If the codec isn't changing, set the start bitrate to -1 which means
+ // "unchanged" so that BWE isn't affected.
+ bitrate_config_.start_bitrate_bps = -1;
+ }
+ }
+
+ if (send_params_.max_bandwidth_bps >= 0) {
+ // Note that max_bandwidth_bps intentionally takes priority over the
+ // bitrate config for the codec. This allows FEC to be applied above the
+ // codec target bitrate.
+ // TODO(pbos): Figure out whether b=AS means max bitrate for this
+ // WebRtcVideoChannel (in which case we're good), or per sender (SSRC),
+ // in which case this should not set a BitrateConstraints but rather
+ // reconfigure all senders.
+ bitrate_config_.max_bitrate_bps = send_params_.max_bandwidth_bps == 0
+ ? -1
+ : send_params_.max_bandwidth_bps;
+ }
+
+ call_->GetTransportControllerSend()->SetSdpBitrateParameters(
+ bitrate_config_);
+ }
+
+ for (auto& kv : send_streams_) {
+ kv.second->SetSendParameters(changed_params);
+ }
+ if (changed_params.send_codec || changed_params.rtcp_mode) {
+ if (send_codec_changed_callback_) {
+ send_codec_changed_callback_();
+ }
+ }
+ return true;
+}
+
+void WebRtcVideoReceiveChannel::SetReceiverFeedbackParameters(
+ bool lntf_enabled,
+ bool nack_enabled,
+ webrtc::RtcpMode rtcp_mode,
+ absl::optional<int> rtx_time) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+
+ // Update receive feedback parameters from new codec or RTCP mode.
+ for (auto& kv : receive_streams_) {
+ RTC_DCHECK(kv.second != nullptr);
+ kv.second->SetFeedbackParameters(lntf_enabled, nack_enabled, rtcp_mode,
+ rtx_time);
+ }
+ // Store for future creation of receive streams
+ rtp_config_.lntf.enabled = lntf_enabled;
+ if (nack_enabled) {
+ rtp_config_.nack.rtp_history_ms = kNackHistoryMs;
+ } else {
+ rtp_config_.nack.rtp_history_ms = 0;
+ }
+ rtp_config_.rtcp_mode = rtcp_mode;
+ // Note: There is no place in config to store rtx_time.
+}
+
+webrtc::RtpParameters WebRtcVideoSendChannel::GetRtpSendParameters(
+ uint32_t ssrc) const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ auto it = send_streams_.find(ssrc);
+ if (it == send_streams_.end()) {
+ RTC_LOG(LS_WARNING) << "Attempting to get RTP send parameters for stream "
+ "with ssrc "
+ << ssrc << " which doesn't exist.";
+ return webrtc::RtpParameters();
+ }
+
+ webrtc::RtpParameters rtp_params = it->second->GetRtpParameters();
+ // Need to add the common list of codecs to the send stream-specific
+ // RTP parameters.
+ for (const VideoCodec& codec : send_params_.codecs) {
+ if (send_codec() && send_codec()->codec.id == codec.id) {
+ // Put the current send codec to the front of the codecs list.
+ RTC_DCHECK_EQ(codec.name, send_codec()->codec.name);
+ rtp_params.codecs.insert(rtp_params.codecs.begin(),
+ codec.ToCodecParameters());
+ } else {
+ rtp_params.codecs.push_back(codec.ToCodecParameters());
+ }
+ }
+
+ return rtp_params;
+}
+
+webrtc::RTCError WebRtcVideoSendChannel::SetRtpSendParameters(
+ uint32_t ssrc,
+ const webrtc::RtpParameters& parameters,
+ webrtc::SetParametersCallback callback) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetRtpSendParameters");
+ auto it = send_streams_.find(ssrc);
+ if (it == send_streams_.end()) {
+ RTC_LOG(LS_ERROR) << "Attempting to set RTP send parameters for stream "
+ "with ssrc "
+ << ssrc << " which doesn't exist.";
+ return webrtc::InvokeSetParametersCallback(
+ callback, webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR));
+ }
+
+ // TODO(deadbeef): Handle setting parameters with a list of codecs in a
+ // different order (which should change the send codec).
+ webrtc::RtpParameters current_parameters = GetRtpSendParameters(ssrc);
+ if (current_parameters.codecs != parameters.codecs) {
+ RTC_DLOG(LS_ERROR) << "Using SetParameters to change the set of codecs "
+ "is not currently supported.";
+ return webrtc::InvokeSetParametersCallback(
+ callback, webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR));
+ }
+
+ if (!parameters.encodings.empty()) {
+ // Note that these values come from:
+ // https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-16#section-5
+ // TODO(deadbeef): Change values depending on whether we are sending a
+ // keyframe or non-keyframe.
+ rtc::DiffServCodePoint new_dscp = rtc::DSCP_DEFAULT;
+ switch (parameters.encodings[0].network_priority) {
+ case webrtc::Priority::kVeryLow:
+ new_dscp = rtc::DSCP_CS1;
+ break;
+ case webrtc::Priority::kLow:
+ new_dscp = rtc::DSCP_DEFAULT;
+ break;
+ case webrtc::Priority::kMedium:
+ new_dscp = rtc::DSCP_AF42;
+ break;
+ case webrtc::Priority::kHigh:
+ new_dscp = rtc::DSCP_AF41;
+ break;
+ }
+ SetPreferredDscp(new_dscp);
+ }
+
+ return it->second->SetRtpParameters(parameters, std::move(callback));
+}
+
+webrtc::RtpParameters WebRtcVideoReceiveChannel::GetRtpReceiveParameters(
+ uint32_t ssrc) const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ webrtc::RtpParameters rtp_params;
+ auto it = receive_streams_.find(ssrc);
+ if (it == receive_streams_.end()) {
+ RTC_LOG(LS_WARNING)
+ << "Attempting to get RTP receive parameters for stream "
+ "with SSRC "
+ << ssrc << " which doesn't exist.";
+ return webrtc::RtpParameters();
+ }
+ rtp_params = it->second->GetRtpParameters();
+ rtp_params.header_extensions = recv_rtp_extensions_;
+
+ // Add codecs, which any stream is prepared to receive.
+ for (const VideoCodec& codec : recv_params_.codecs) {
+ rtp_params.codecs.push_back(codec.ToCodecParameters());
+ }
+
+ return rtp_params;
+}
+
+webrtc::RtpParameters
+WebRtcVideoReceiveChannel::GetDefaultRtpReceiveParameters() const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ webrtc::RtpParameters rtp_params;
+ if (!default_sink_) {
+ // Getting parameters on a default, unsignaled video receive stream but
+ // because we've not configured to receive such a stream, `encodings` is
+ // empty.
+ return rtp_params;
+ }
+ rtp_params.encodings.emplace_back();
+
+ // Add codecs, which any stream is prepared to receive.
+ for (const VideoCodec& codec : recv_params_.codecs) {
+ rtp_params.codecs.push_back(codec.ToCodecParameters());
+ }
+
+ return rtp_params;
+}
+
+bool WebRtcVideoReceiveChannel::GetChangedRecvParameters(
+ const VideoRecvParameters& params,
+ ChangedRecvParameters* changed_params) const {
+ if (!ValidateCodecFormats(params.codecs) ||
+ !ValidateRtpExtensions(params.extensions, recv_rtp_extensions_)) {
+ return false;
+ }
+
+ // Handle receive codecs.
+ const std::vector<VideoCodecSettings> mapped_codecs =
+ MapCodecs(params.codecs);
+ if (mapped_codecs.empty()) {
+ RTC_LOG(LS_ERROR)
+ << "GetChangedRecvParameters called without any video codecs.";
+ return false;
+ }
+
+ // Verify that every mapped codec is supported locally.
+ if (params.is_stream_active) {
+ const std::vector<VideoCodec> local_supported_codecs =
+ GetPayloadTypesAndDefaultCodecs(decoder_factory_,
+ /*is_decoder_factory=*/true,
+ /*include_rtx=*/true, call_->trials());
+ for (const VideoCodecSettings& mapped_codec : mapped_codecs) {
+ if (!FindMatchingCodec(local_supported_codecs, mapped_codec.codec)) {
+ RTC_LOG(LS_ERROR)
+ << "GetChangedRecvParameters called with unsupported video codec: "
+ << mapped_codec.codec.ToString();
+ return false;
+ }
+ }
+ }
+
+ if (NonFlexfecReceiveCodecsHaveChanged(recv_codecs_, mapped_codecs)) {
+ changed_params->codec_settings =
+ absl::optional<std::vector<VideoCodecSettings>>(mapped_codecs);
+ }
+
+ // Handle RTP header extensions.
+ std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(
+ params.extensions, webrtc::RtpExtension::IsSupportedForVideo, false,
+ call_->trials());
+ if (filtered_extensions != recv_rtp_extensions_) {
+ changed_params->rtp_header_extensions =
+ absl::optional<std::vector<webrtc::RtpExtension>>(filtered_extensions);
+ }
+
+ int flexfec_payload_type = mapped_codecs.front().flexfec_payload_type;
+ if (flexfec_payload_type != recv_flexfec_payload_type_) {
+ changed_params->flexfec_payload_type = flexfec_payload_type;
+ }
+
+ return true;
+}
+
+bool WebRtcVideoReceiveChannel::SetRecvParameters(
+ const VideoRecvParameters& params) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetRecvParameters");
+ RTC_LOG(LS_INFO) << "SetRecvParameters: " << params.ToString();
+ ChangedRecvParameters changed_params;
+ if (!GetChangedRecvParameters(params, &changed_params)) {
+ return false;
+ }
+ if (changed_params.flexfec_payload_type) {
+ RTC_DLOG(LS_INFO) << "Changing FlexFEC payload type (recv) from "
+ << recv_flexfec_payload_type_ << " to "
+ << *changed_params.flexfec_payload_type;
+ recv_flexfec_payload_type_ = *changed_params.flexfec_payload_type;
+ }
+ if (changed_params.rtp_header_extensions) {
+ recv_rtp_extensions_ = *changed_params.rtp_header_extensions;
+ recv_rtp_extension_map_ =
+ webrtc::RtpHeaderExtensionMap(recv_rtp_extensions_);
+ }
+ if (changed_params.codec_settings) {
+ RTC_DLOG(LS_INFO) << "Changing recv codecs from "
+ << CodecSettingsVectorToString(recv_codecs_) << " to "
+ << CodecSettingsVectorToString(
+ *changed_params.codec_settings);
+ recv_codecs_ = *changed_params.codec_settings;
+ }
+
+ for (auto& kv : receive_streams_) {
+ kv.second->SetRecvParameters(changed_params);
+ }
+ recv_params_ = params;
+ return true;
+}
+
+void WebRtcVideoReceiveChannel::SetReceiverReportSsrc(uint32_t ssrc) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (ssrc == rtcp_receiver_report_ssrc_)
+ return;
+
+ rtcp_receiver_report_ssrc_ = ssrc;
+ for (auto& [unused, receive_stream] : receive_streams_)
+ receive_stream->SetLocalSsrc(ssrc);
+}
+
+void WebRtcVideoReceiveChannel::ChooseReceiverReportSsrc(
+ const std::set<uint32_t>& choices) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ // If we can continue using the current receiver report, do so.
+ if (choices.find(rtcp_receiver_report_ssrc_) != choices.end()) {
+ return;
+ }
+ // Go back to the default if list has been emptied.
+ if (choices.empty()) {
+ SetReceiverReportSsrc(kDefaultRtcpReceiverReportSsrc);
+ return;
+ }
+ // Any number is as good as any other.
+ SetReceiverReportSsrc(*choices.begin());
+}
+
+bool WebRtcVideoSendChannel::GetSendCodec(VideoCodec* codec) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (!send_codec()) {
+ RTC_LOG(LS_VERBOSE) << "GetSendCodec: No send codec set.";
+ return false;
+ }
+ *codec = send_codec()->codec;
+ return true;
+}
+
+void WebRtcVideoReceiveChannel::SetReceive(bool receive) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetReceive");
+ RTC_LOG(LS_VERBOSE) << "SetReceive: " << (receive ? "true" : "false");
+ for (const auto& kv : receive_streams_) {
+ if (receive) {
+ kv.second->StartReceiveStream();
+ } else {
+ kv.second->StopReceiveStream();
+ }
+ }
+ receiving_ = receive;
+}
+
+bool WebRtcVideoSendChannel::SetSend(bool send) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ TRACE_EVENT0("webrtc", "WebRtcVideoChannel::SetSend");
+ RTC_LOG(LS_VERBOSE) << "SetSend: " << (send ? "true" : "false");
+ if (send && !send_codec()) {
+ RTC_DLOG(LS_ERROR) << "SetSend(true) called before setting codec.";
+ return false;
+ }
+ for (const auto& kv : send_streams_) {
+ kv.second->SetSend(send);
+ }
+ sending_ = send;
+ return true;
+}
+
+bool WebRtcVideoSendChannel::SetVideoSend(
+ uint32_t ssrc,
+ const VideoOptions* options,
+ rtc::VideoSourceInterface<webrtc::VideoFrame>* source) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ TRACE_EVENT0("webrtc", "SetVideoSend");
+ RTC_DCHECK(ssrc != 0);
+ RTC_LOG(LS_INFO) << "SetVideoSend (ssrc= " << ssrc << ", options: "
+ << (options ? options->ToString() : "nullptr")
+ << ", source = " << (source ? "(source)" : "nullptr") << ")";
+
+ const auto& kv = send_streams_.find(ssrc);
+ if (kv == send_streams_.end()) {
+ // Allow unknown ssrc only if source is null.
+ RTC_CHECK(source == nullptr);
+ RTC_LOG(LS_ERROR) << "No sending stream on ssrc " << ssrc;
+ return false;
+ }
+
+ return kv->second->SetVideoSend(options, source);
+}
+
+bool WebRtcVideoSendChannel::ValidateSendSsrcAvailability(
+ const StreamParams& sp) const {
+ for (uint32_t ssrc : sp.ssrcs) {
+ if (send_ssrcs_.find(ssrc) != send_ssrcs_.end()) {
+ RTC_LOG(LS_ERROR) << "Send stream with SSRC '" << ssrc
+ << "' already exists.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool WebRtcVideoReceiveChannel::ValidateReceiveSsrcAvailability(
+ const StreamParams& sp) const {
+ for (uint32_t ssrc : sp.ssrcs) {
+ if (receive_ssrcs_.find(ssrc) != receive_ssrcs_.end()) {
+ RTC_LOG(LS_ERROR) << "Receive stream with SSRC '" << ssrc
+ << "' already exists.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool WebRtcVideoSendChannel::AddSendStream(const StreamParams& sp) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+
+ RTC_LOG(LS_INFO) << "AddSendStream: " << sp.ToString();
+ if (!ValidateStreamParams(sp))
+ return false;
+
+ if (!ValidateSendSsrcAvailability(sp))
+ return false;
+
+ for (uint32_t used_ssrc : sp.ssrcs)
+ send_ssrcs_.insert(used_ssrc);
+
+ webrtc::VideoSendStream::Config config(this);
+
+ for (const RidDescription& rid : sp.rids()) {
+ config.rtp.rids.push_back(rid.rid);
+ }
+
+ config.suspend_below_min_bitrate = video_config_.suspend_below_min_bitrate;
+ config.periodic_alr_bandwidth_probing =
+ video_config_.periodic_alr_bandwidth_probing;
+ config.encoder_settings.experiment_cpu_load_estimator =
+ video_config_.experiment_cpu_load_estimator;
+ config.encoder_settings.encoder_factory = encoder_factory_;
+ config.encoder_settings.bitrate_allocator_factory =
+ bitrate_allocator_factory_;
+ config.encoder_settings.encoder_switch_request_callback = this;
+ config.crypto_options = crypto_options_;
+ config.rtp.extmap_allow_mixed = ExtmapAllowMixed();
+ config.rtcp_report_interval_ms = video_config_.rtcp_report_interval_ms;
+ config.rtp.enable_send_packet_batching =
+ video_config_.enable_send_packet_batching;
+
+ WebRtcVideoSendStream* stream = new WebRtcVideoSendStream(
+ call_, sp, std::move(config), default_send_options_,
+ video_config_.enable_cpu_adaptation, bitrate_config_.max_bitrate_bps,
+ send_codec(), send_rtp_extensions_, send_params_);
+
+ uint32_t ssrc = sp.first_ssrc();
+ RTC_DCHECK(ssrc != 0);
+ send_streams_[ssrc] = stream;
+
+ if (ssrc_list_changed_callback_) {
+ ssrc_list_changed_callback_(send_ssrcs_);
+ }
+
+ if (sending_) {
+ stream->SetSend(true);
+ }
+
+ return true;
+}
+
+bool WebRtcVideoSendChannel::RemoveSendStream(uint32_t ssrc) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ RTC_LOG(LS_INFO) << "RemoveSendStream: " << ssrc;
+
+ WebRtcVideoSendStream* removed_stream;
+ auto it = send_streams_.find(ssrc);
+ if (it == send_streams_.end()) {
+ return false;
+ }
+
+ for (uint32_t old_ssrc : it->second->GetSsrcs())
+ send_ssrcs_.erase(old_ssrc);
+
+ removed_stream = it->second;
+ send_streams_.erase(it);
+
+ // Switch receiver report SSRCs, in case the one in use is no longer valid.
+ if (ssrc_list_changed_callback_) {
+ ssrc_list_changed_callback_(send_ssrcs_);
+ }
+
+ delete removed_stream;
+
+ return true;
+}
+
+void WebRtcVideoReceiveChannel::DeleteReceiveStream(
+ WebRtcVideoReceiveStream* stream) {
+ for (uint32_t old_ssrc : stream->GetSsrcs())
+ receive_ssrcs_.erase(old_ssrc);
+ delete stream;
+}
+
+bool WebRtcVideoReceiveChannel::AddRecvStream(const StreamParams& sp) {
+ return AddRecvStream(sp, false);
+}
+
+bool WebRtcVideoReceiveChannel::AddRecvStream(const StreamParams& sp,
+ bool default_stream) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+
+ RTC_LOG(LS_INFO) << "AddRecvStream"
+ << (default_stream ? " (default stream)" : "") << ": "
+ << sp.ToString();
+ if (!sp.has_ssrcs()) {
+ // This is a StreamParam with unsignaled SSRCs. Store it, so it can be used
+ // later when we know the SSRC on the first packet arrival.
+ unsignaled_stream_params_ = sp;
+ return true;
+ }
+
+ if (!ValidateStreamParams(sp))
+ return false;
+
+ for (uint32_t ssrc : sp.ssrcs) {
+ // Remove running stream if this was a default stream.
+ const auto& prev_stream = receive_streams_.find(ssrc);
+ if (prev_stream != receive_streams_.end()) {
+ if (default_stream || !prev_stream->second->IsDefaultStream()) {
+ RTC_LOG(LS_ERROR) << "Receive stream for SSRC '" << ssrc
+ << "' already exists.";
+ return false;
+ }
+ DeleteReceiveStream(prev_stream->second);
+ receive_streams_.erase(prev_stream);
+ }
+ }
+
+ if (!ValidateReceiveSsrcAvailability(sp))
+ return false;
+
+ for (uint32_t used_ssrc : sp.ssrcs)
+ receive_ssrcs_.insert(used_ssrc);
+
+ webrtc::VideoReceiveStreamInterface::Config config(this, decoder_factory_);
+ webrtc::FlexfecReceiveStream::Config flexfec_config(this);
+ ConfigureReceiverRtp(&config, &flexfec_config, sp);
+
+ config.crypto_options = crypto_options_;
+ config.enable_prerenderer_smoothing =
+ video_config_.enable_prerenderer_smoothing;
+ if (!sp.stream_ids().empty()) {
+ config.sync_group = sp.stream_ids()[0];
+ }
+
+ if (unsignaled_frame_transformer_ && !config.frame_transformer)
+ config.frame_transformer = unsignaled_frame_transformer_;
+
+ auto receive_stream =
+ new WebRtcVideoReceiveStream(call_, sp, std::move(config), default_stream,
+ recv_codecs_, flexfec_config);
+ if (receiving_) {
+ receive_stream->StartReceiveStream();
+ }
+ receive_streams_[sp.first_ssrc()] = receive_stream;
+ return true;
+}
+
+void WebRtcVideoReceiveChannel::ConfigureReceiverRtp(
+ webrtc::VideoReceiveStreamInterface::Config* config,
+ webrtc::FlexfecReceiveStream::Config* flexfec_config,
+ const StreamParams& sp) const {
+ uint32_t ssrc = sp.first_ssrc();
+
+ config->rtp.remote_ssrc = ssrc;
+ config->rtp.local_ssrc = rtcp_receiver_report_ssrc_;
+
+ // TODO(pbos): This protection is against setting the same local ssrc as
+ // remote which is not permitted by the lower-level API. RTCP requires a
+ // corresponding sender SSRC. Figure out what to do when we don't have
+ // (receive-only) or know a good local SSRC.
+ if (config->rtp.remote_ssrc == config->rtp.local_ssrc) {
+ if (config->rtp.local_ssrc != kDefaultRtcpReceiverReportSsrc) {
+ config->rtp.local_ssrc = kDefaultRtcpReceiverReportSsrc;
+ } else {
+ config->rtp.local_ssrc = kDefaultRtcpReceiverReportSsrc + 1;
+ }
+ }
+
+ // The mode and rtx time is determined by a call to the configuration
+ // function.
+ config->rtp.rtcp_mode = rtp_config_.rtcp_mode;
+
+ sp.GetFidSsrc(ssrc, &config->rtp.rtx_ssrc);
+
+ // TODO(brandtr): Generalize when we add support for multistream protection.
+ flexfec_config->payload_type = recv_flexfec_payload_type_;
+ if (!IsDisabled(call_->trials(), "WebRTC-FlexFEC-03-Advertised") &&
+ sp.GetFecFrSsrc(ssrc, &flexfec_config->rtp.remote_ssrc)) {
+ flexfec_config->protected_media_ssrcs = {ssrc};
+ flexfec_config->rtp.local_ssrc = config->rtp.local_ssrc;
+ flexfec_config->rtcp_mode = config->rtp.rtcp_mode;
+ }
+}
+
+bool WebRtcVideoReceiveChannel::RemoveRecvStream(uint32_t ssrc) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ RTC_LOG(LS_INFO) << "RemoveRecvStream: " << ssrc;
+
+ auto stream = receive_streams_.find(ssrc);
+ if (stream == receive_streams_.end()) {
+ RTC_LOG(LS_ERROR) << "Stream not found for ssrc: " << ssrc;
+ return false;
+ }
+ DeleteReceiveStream(stream->second);
+ receive_streams_.erase(stream);
+
+ return true;
+}
+
+void WebRtcVideoReceiveChannel::ResetUnsignaledRecvStream() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ RTC_LOG(LS_INFO) << "ResetUnsignaledRecvStream.";
+ unsignaled_stream_params_ = StreamParams();
+ last_unsignalled_ssrc_creation_time_ms_ = absl::nullopt;
+
+ // Delete any created default streams. This is needed to avoid SSRC collisions
+ // in Call's RtpDemuxer, in the case that `this` has created a default video
+ // receiver, and then some other WebRtcVideoChannel gets the SSRC signaled
+ // in the corresponding Unified Plan "m=" section.
+ auto it = receive_streams_.begin();
+ while (it != receive_streams_.end()) {
+ if (it->second->IsDefaultStream()) {
+ DeleteReceiveStream(it->second);
+ receive_streams_.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+}
+
+absl::optional<uint32_t> WebRtcVideoReceiveChannel::GetUnsignaledSsrc() const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ absl::optional<uint32_t> ssrc;
+ for (auto it = receive_streams_.begin(); it != receive_streams_.end(); ++it) {
+ if (it->second->IsDefaultStream()) {
+ ssrc.emplace(it->first);
+ break;
+ }
+ }
+ return ssrc;
+}
+
+void WebRtcVideoReceiveChannel::OnDemuxerCriteriaUpdatePending() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ ++demuxer_criteria_id_;
+}
+
+void WebRtcVideoReceiveChannel::OnDemuxerCriteriaUpdateComplete() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ ++demuxer_criteria_completed_id_;
+}
+
+bool WebRtcVideoReceiveChannel::SetSink(
+ uint32_t ssrc,
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ RTC_LOG(LS_INFO) << "SetSink: ssrc:" << ssrc << " "
+ << (sink ? "(ptr)" : "nullptr");
+
+ auto it = receive_streams_.find(ssrc);
+ if (it == receive_streams_.end()) {
+ return false;
+ }
+
+ it->second->SetSink(sink);
+ return true;
+}
+
+void WebRtcVideoReceiveChannel::SetDefaultSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ RTC_LOG(LS_INFO) << "SetDefaultSink: " << (sink ? "(ptr)" : "nullptr");
+ default_sink_ = sink;
+}
+
+bool WebRtcVideoSendChannel::GetStats(VideoMediaSendInfo* info) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ TRACE_EVENT0("webrtc", "WebRtcVideoChannel::GetSendStats");
+
+ info->Clear();
+ if (send_streams_.empty()) {
+ return true;
+ }
+
+ // Log stats periodically.
+ bool log_stats = false;
+ int64_t now_ms = rtc::TimeMillis();
+ if (last_send_stats_log_ms_ == -1 ||
+ now_ms - last_send_stats_log_ms_ > kStatsLogIntervalMs) {
+ last_send_stats_log_ms_ = now_ms;
+ log_stats = true;
+ }
+
+ info->Clear();
+ FillSenderStats(info, log_stats);
+ FillSendCodecStats(info);
+ // TODO(holmer): We should either have rtt available as a metric on
+ // VideoSend/ReceiveStreams, or we should remove rtt from VideoSenderInfo.
+ webrtc::Call::Stats stats = call_->GetStats();
+ if (stats.rtt_ms != -1) {
+ for (size_t i = 0; i < info->senders.size(); ++i) {
+ info->senders[i].rtt_ms = stats.rtt_ms;
+ }
+ for (size_t i = 0; i < info->aggregated_senders.size(); ++i) {
+ info->aggregated_senders[i].rtt_ms = stats.rtt_ms;
+ }
+ }
+
+ if (log_stats)
+ RTC_LOG(LS_INFO) << stats.ToString(now_ms);
+
+ return true;
+}
+bool WebRtcVideoReceiveChannel::GetStats(VideoMediaReceiveInfo* info) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ TRACE_EVENT0("webrtc", "WebRtcVideoReceiveChannel::GetStats");
+
+ info->Clear();
+ if (receive_streams_.empty()) {
+ return true;
+ }
+
+ // Log stats periodically.
+ bool log_stats = false;
+ int64_t now_ms = rtc::TimeMillis();
+ if (last_receive_stats_log_ms_ == -1 ||
+ now_ms - last_receive_stats_log_ms_ > kStatsLogIntervalMs) {
+ last_receive_stats_log_ms_ = now_ms;
+ log_stats = true;
+ }
+
+ FillReceiverStats(info, log_stats);
+ FillReceiveCodecStats(info);
+
+ return true;
+}
+
+void WebRtcVideoSendChannel::FillSenderStats(
+ VideoMediaSendInfo* video_media_info,
+ bool log_stats) {
+ for (const auto& it : send_streams_) {
+ auto infos = it.second->GetPerLayerVideoSenderInfos(log_stats);
+ if (infos.empty())
+ continue;
+ video_media_info->aggregated_senders.push_back(
+ it.second->GetAggregatedVideoSenderInfo(infos));
+ for (auto&& info : infos) {
+ video_media_info->senders.push_back(info);
+ }
+ }
+}
+
+void WebRtcVideoReceiveChannel::FillReceiverStats(
+ VideoMediaReceiveInfo* video_media_info,
+ bool log_stats) {
+ for (const auto& it : receive_streams_) {
+ video_media_info->receivers.push_back(
+ it.second->GetVideoReceiverInfo(log_stats));
+ }
+}
+
+void WebRtcVideoSendChannel::FillBitrateInfo(
+ BandwidthEstimationInfo* bwe_info) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ for (const auto& it : send_streams_) {
+ it.second->FillBitrateInfo(bwe_info);
+ }
+}
+
+void WebRtcVideoSendChannel::FillSendCodecStats(
+ VideoMediaSendInfo* video_media_info) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (!send_codec()) {
+ return;
+ }
+ // Note: since RTP stats don't account for RTX and FEC separately (see
+ // https://w3c.github.io/webrtc-stats/#dom-rtcstatstype-outbound-rtp)
+ // we can omit the codec information for those here and only insert the
+ // primary codec that is being used to send here.
+ video_media_info->send_codecs.insert(std::make_pair(
+ send_codec()->codec.id, send_codec()->codec.ToCodecParameters()));
+}
+
+void WebRtcVideoReceiveChannel::FillReceiveCodecStats(
+ VideoMediaReceiveInfo* video_media_info) {
+ for (const auto& receiver : video_media_info->receivers) {
+ auto codec =
+ absl::c_find_if(recv_params_.codecs, [&receiver](const VideoCodec& c) {
+ return receiver.codec_payload_type &&
+ *receiver.codec_payload_type == c.id;
+ });
+ if (codec != recv_params_.codecs.end()) {
+ video_media_info->receive_codecs.insert(
+ std::make_pair(codec->id, codec->ToCodecParameters()));
+ }
+ }
+}
+
+void WebRtcVideoReceiveChannel::OnPacketReceived(
+ const webrtc::RtpPacketReceived& packet) {
+ // Note: the network_thread_checker may refer to the worker thread if the two
+ // threads are combined, but this is either always true or always false
+ // depending on configuration set at object initialization.
+ RTC_DCHECK_RUN_ON(&network_thread_checker_);
+
+ // TODO(crbug.com/1373439): Stop posting to the worker thread when the
+ // combined network/worker project launches.
+ if (webrtc::TaskQueueBase::Current() != worker_thread_) {
+ worker_thread_->PostTask(
+ SafeTask(task_safety_.flag(), [this, packet = packet]() mutable {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ ProcessReceivedPacket(std::move(packet));
+ }));
+ } else {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ ProcessReceivedPacket(packet);
+ }
+}
+
+bool WebRtcVideoReceiveChannel::MaybeCreateDefaultReceiveStream(
+ const webrtc::RtpPacketReceived& packet) {
+ if (discard_unknown_ssrc_packets_) {
+ return false;
+ }
+
+ if (packet.PayloadType() == recv_flexfec_payload_type_) {
+ return false;
+ }
+
+ // Ignore unknown ssrcs if there is a demuxer criteria update pending.
+ // During a demuxer update we may receive ssrcs that were recently
+ // removed or we may receve ssrcs that were recently configured for a
+ // different video channel.
+ if (demuxer_criteria_id_ != demuxer_criteria_completed_id_) {
+ return false;
+ }
+
+ // See if this payload_type is registered as one that usually gets its
+ // own SSRC (RTX) or at least is safe to drop either way (FEC). If it
+ // is, and it wasn't handled above by DeliverPacket, that means we don't
+ // know what stream it associates with, and we shouldn't ever create an
+ // implicit channel for these.
+ bool is_rtx_payload = false;
+ for (auto& codec : recv_codecs_) {
+ if (packet.PayloadType() == codec.ulpfec.red_rtx_payload_type ||
+ packet.PayloadType() == codec.ulpfec.ulpfec_payload_type) {
+ return false;
+ }
+
+ if (packet.PayloadType() == codec.rtx_payload_type) {
+ is_rtx_payload = true;
+ break;
+ }
+ }
+
+ if (is_rtx_payload) {
+ // As we don't support receiving simulcast there can only be one RTX
+ // stream, which will be associated with unsignaled media stream.
+ absl::optional<uint32_t> current_default_ssrc = GetUnsignaledSsrc();
+ if (current_default_ssrc) {
+ FindReceiveStream(*current_default_ssrc)->UpdateRtxSsrc(packet.Ssrc());
+ } else {
+ // Received unsignaled RTX packet before a media packet. Create a default
+ // stream with a "random" SSRC and the RTX SSRC from the packet. The
+ // stream will be recreated on the first media packet, unless we are
+ // extremely lucky and used the right media SSRC.
+ ReCreateDefaultReceiveStream(/*ssrc =*/14795, /*rtx_ssrc=*/packet.Ssrc());
+ }
+ return true;
+ } else {
+ // Ignore unknown ssrcs if we recently created an unsignalled receive
+ // stream since this shouldn't happen frequently. Getting into a state
+ // of creating decoders on every packet eats up processing time (e.g.
+ // https://crbug.com/1069603) and this cooldown prevents that.
+ if (last_unsignalled_ssrc_creation_time_ms_.has_value()) {
+ int64_t now_ms = rtc::TimeMillis();
+ if (now_ms - last_unsignalled_ssrc_creation_time_ms_.value() <
+ kUnsignaledSsrcCooldownMs) {
+ // We've already created an unsignalled ssrc stream within the last
+ // 0.5 s, ignore with a warning.
+ RTC_LOG(LS_WARNING)
+ << "Another unsignalled ssrc packet arrived shortly after the "
+ << "creation of an unsignalled ssrc stream. Dropping packet.";
+ return false;
+ }
+ }
+ }
+ // RTX SSRC not yet known.
+ ReCreateDefaultReceiveStream(packet.Ssrc(), absl::nullopt);
+ last_unsignalled_ssrc_creation_time_ms_ = rtc::TimeMillis();
+ return true;
+}
+
+void WebRtcVideoReceiveChannel::ReCreateDefaultReceiveStream(
+ uint32_t ssrc,
+ absl::optional<uint32_t> rtx_ssrc) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+
+ absl::optional<uint32_t> default_recv_ssrc = GetUnsignaledSsrc();
+ if (default_recv_ssrc) {
+ RTC_LOG(LS_INFO) << "Destroying old default receive stream for SSRC="
+ << ssrc << ".";
+ RemoveRecvStream(*default_recv_ssrc);
+ }
+
+ StreamParams sp = unsignaled_stream_params();
+ sp.ssrcs.push_back(ssrc);
+ if (rtx_ssrc) {
+ sp.AddFidSsrc(ssrc, *rtx_ssrc);
+ }
+ RTC_LOG(LS_INFO) << "Creating default receive stream for SSRC=" << ssrc
+ << ".";
+ if (!AddRecvStream(sp, /*default_stream=*/true)) {
+ RTC_LOG(LS_WARNING) << "Could not create default receive stream.";
+ }
+
+ // SSRC 0 returns default_recv_base_minimum_delay_ms.
+ const int unsignaled_ssrc = 0;
+ int default_recv_base_minimum_delay_ms =
+ GetBaseMinimumPlayoutDelayMs(unsignaled_ssrc).value_or(0);
+ // Set base minimum delay if it was set before for the default receive
+ // stream.
+ SetBaseMinimumPlayoutDelayMs(ssrc, default_recv_base_minimum_delay_ms);
+ SetSink(ssrc, default_sink_);
+}
+
+void WebRtcVideoSendChannel::OnPacketSent(const rtc::SentPacket& sent_packet) {
+ RTC_DCHECK_RUN_ON(&network_thread_checker_);
+ // TODO(tommi): We shouldn't need to go through call_ to deliver this
+ // notification. We should already have direct access to
+ // video_send_delay_stats_ and transport_send_ptr_ via `stream_`.
+ // So we should be able to remove OnSentPacket from Call and handle this per
+ // channel instead. At the moment Call::OnSentPacket calls OnSentPacket for
+ // the video stats, for all sent packets, including audio, which causes
+ // unnecessary lookups.
+ call_->OnSentPacket(sent_packet);
+}
+
+void WebRtcVideoSendChannel::OnReadyToSend(bool ready) {
+ RTC_DCHECK_RUN_ON(&network_thread_checker_);
+ RTC_LOG(LS_VERBOSE) << "OnReadyToSend: " << (ready ? "Ready." : "Not ready.");
+ call_->SignalChannelNetworkState(
+ webrtc::MediaType::VIDEO,
+ ready ? webrtc::kNetworkUp : webrtc::kNetworkDown);
+}
+
+void WebRtcVideoSendChannel::OnNetworkRouteChanged(
+ absl::string_view transport_name,
+ const rtc::NetworkRoute& network_route) {
+ RTC_DCHECK_RUN_ON(&network_thread_checker_);
+ worker_thread_->PostTask(SafeTask(
+ task_safety_.flag(),
+ [this, name = std::string(transport_name), route = network_route] {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ webrtc::RtpTransportControllerSendInterface* transport =
+ call_->GetTransportControllerSend();
+ transport->OnNetworkRouteChanged(name, route);
+ transport->OnTransportOverheadChanged(route.packet_overhead);
+ }));
+}
+
+void WebRtcVideoReceiveChannel::SetInterface(
+ MediaChannelNetworkInterface* iface) {
+ RTC_DCHECK_RUN_ON(&network_thread_checker_);
+ MediaChannelUtil::SetInterface(iface);
+ // Set the RTP recv/send buffer to a bigger size.
+ MediaChannelUtil::SetOption(MediaChannelNetworkInterface::ST_RTP,
+ rtc::Socket::OPT_RCVBUF, kVideoRtpRecvBufferSize);
+}
+
+void WebRtcVideoSendChannel::SetInterface(MediaChannelNetworkInterface* iface) {
+ RTC_DCHECK_RUN_ON(&network_thread_checker_);
+ MediaChannelUtil::SetInterface(iface);
+
+ // Speculative change to increase the outbound socket buffer size.
+ // In b/15152257, we are seeing a significant number of packets discarded
+ // due to lack of socket buffer space, although it's not yet clear what the
+ // ideal value should be.
+ const std::string group_name_send_buf_size =
+ call_->trials().Lookup("WebRTC-SendBufferSizeBytes");
+ int send_buffer_size = kVideoRtpSendBufferSize;
+ if (!group_name_send_buf_size.empty() &&
+ (sscanf(group_name_send_buf_size.c_str(), "%d", &send_buffer_size) != 1 ||
+ send_buffer_size <= 0)) {
+ RTC_LOG(LS_WARNING) << "Invalid send buffer size: "
+ << group_name_send_buf_size;
+ send_buffer_size = kVideoRtpSendBufferSize;
+ }
+
+ MediaChannelUtil::SetOption(MediaChannelNetworkInterface::ST_RTP,
+ rtc::Socket::OPT_SNDBUF, send_buffer_size);
+}
+
+void WebRtcVideoReceiveChannel::SetFrameDecryptor(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ auto matching_stream = receive_streams_.find(ssrc);
+ if (matching_stream != receive_streams_.end()) {
+ matching_stream->second->SetFrameDecryptor(frame_decryptor);
+ }
+}
+
+void WebRtcVideoSendChannel::SetFrameEncryptor(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ auto matching_stream = send_streams_.find(ssrc);
+ if (matching_stream != send_streams_.end()) {
+ matching_stream->second->SetFrameEncryptor(frame_encryptor);
+ } else {
+ RTC_LOG(LS_ERROR) << "No stream found to attach frame encryptor";
+ }
+}
+
+void WebRtcVideoSendChannel::SetEncoderSelector(
+ uint32_t ssrc,
+ webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ auto matching_stream = send_streams_.find(ssrc);
+ if (matching_stream != send_streams_.end()) {
+ matching_stream->second->SetEncoderSelector(encoder_selector);
+ } else {
+ RTC_LOG(LS_ERROR) << "No stream found to attach encoder selector";
+ }
+}
+
+void WebRtcVideoSendChannel::SetVideoCodecSwitchingEnabled(bool enabled) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ allow_codec_switching_ = enabled;
+ if (allow_codec_switching_) {
+ RTC_LOG(LS_INFO) << "Encoder switching enabled.";
+ }
+}
+
+bool WebRtcVideoReceiveChannel::SetBaseMinimumPlayoutDelayMs(uint32_t ssrc,
+ int delay_ms) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ absl::optional<uint32_t> default_ssrc = GetUnsignaledSsrc();
+
+ // SSRC of 0 represents the default receive stream.
+ if (ssrc == 0) {
+ default_recv_base_minimum_delay_ms_ = delay_ms;
+ }
+
+ if (ssrc == 0 && !default_ssrc) {
+ return true;
+ }
+
+ if (ssrc == 0 && default_ssrc) {
+ ssrc = default_ssrc.value();
+ }
+
+ auto stream = receive_streams_.find(ssrc);
+ if (stream != receive_streams_.end()) {
+ stream->second->SetBaseMinimumPlayoutDelayMs(delay_ms);
+ return true;
+ } else {
+ RTC_LOG(LS_ERROR) << "No stream found to set base minimum playout delay";
+ return false;
+ }
+}
+
+absl::optional<int> WebRtcVideoReceiveChannel::GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ // SSRC of 0 represents the default receive stream.
+ if (ssrc == 0) {
+ return default_recv_base_minimum_delay_ms_;
+ }
+
+ auto stream = receive_streams_.find(ssrc);
+ if (stream != receive_streams_.end()) {
+ return stream->second->GetBaseMinimumPlayoutDelayMs();
+ } else {
+ RTC_LOG(LS_ERROR) << "No stream found to get base minimum playout delay";
+ return absl::nullopt;
+ }
+}
+
+std::vector<webrtc::RtpSource> WebRtcVideoReceiveChannel::GetSources(
+ uint32_t ssrc) const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ auto it = receive_streams_.find(ssrc);
+ if (it == receive_streams_.end()) {
+ // TODO(bugs.webrtc.org/9781): Investigate standard compliance
+ // with sources for streams that has been removed.
+ RTC_LOG(LS_ERROR) << "Attempting to get contributing sources for SSRC:"
+ << ssrc << " which doesn't exist.";
+ return {};
+ }
+ return it->second->GetSources();
+}
+
+bool WebRtcVideoSendChannel::SendRtp(const uint8_t* data,
+ size_t len,
+ const webrtc::PacketOptions& options) {
+ MediaChannelUtil::SendRtp(data, len, options);
+ return true;
+}
+
+bool WebRtcVideoSendChannel::SendRtcp(const uint8_t* data, size_t len) {
+ MediaChannelUtil::SendRtcp(data, len);
+ return true;
+}
+
+WebRtcVideoSendChannel::WebRtcVideoSendStream::VideoSendStreamParameters::
+ VideoSendStreamParameters(
+ webrtc::VideoSendStream::Config config,
+ const VideoOptions& options,
+ int max_bitrate_bps,
+ const absl::optional<VideoCodecSettings>& codec_settings)
+ : config(std::move(config)),
+ options(options),
+ max_bitrate_bps(max_bitrate_bps),
+ conference_mode(false),
+ codec_settings(codec_settings) {}
+
+WebRtcVideoSendChannel::WebRtcVideoSendStream::WebRtcVideoSendStream(
+ webrtc::Call* call,
+ const StreamParams& sp,
+ webrtc::VideoSendStream::Config config,
+ const VideoOptions& options,
+ bool enable_cpu_overuse_detection,
+ int max_bitrate_bps,
+ const absl::optional<VideoCodecSettings>& codec_settings,
+ const absl::optional<std::vector<webrtc::RtpExtension>>& rtp_extensions,
+ // TODO(deadbeef): Don't duplicate information between send_params,
+ // rtp_extensions, options, etc.
+ const VideoSendParameters& send_params)
+ : worker_thread_(call->worker_thread()),
+ ssrcs_(sp.ssrcs),
+ ssrc_groups_(sp.ssrc_groups),
+ call_(call),
+ enable_cpu_overuse_detection_(enable_cpu_overuse_detection),
+ source_(nullptr),
+ stream_(nullptr),
+ parameters_(std::move(config), options, max_bitrate_bps, codec_settings),
+ rtp_parameters_(CreateRtpParametersWithEncodings(sp)),
+ sending_(false),
+ disable_automatic_resize_(
+ IsEnabled(call->trials(), "WebRTC-Video-DisableAutomaticResize")) {
+ // Maximum packet size may come in RtpConfig from external transport, for
+ // example from QuicTransportInterface implementation, so do not exceed
+ // given max_packet_size.
+ parameters_.config.rtp.max_packet_size =
+ std::min<size_t>(parameters_.config.rtp.max_packet_size, kVideoMtu);
+ parameters_.conference_mode = send_params.conference_mode;
+
+ sp.GetPrimarySsrcs(¶meters_.config.rtp.ssrcs);
+
+ // ValidateStreamParams should prevent this from happening.
+ RTC_CHECK(!parameters_.config.rtp.ssrcs.empty());
+ rtp_parameters_.encodings[0].ssrc = parameters_.config.rtp.ssrcs[0];
+
+ // RTX.
+ sp.GetFidSsrcs(parameters_.config.rtp.ssrcs,
+ ¶meters_.config.rtp.rtx.ssrcs);
+
+ // FlexFEC SSRCs.
+ // TODO(brandtr): This code needs to be generalized when we add support for
+ // multistream protection.
+ if (IsEnabled(call_->trials(), "WebRTC-FlexFEC-03")) {
+ uint32_t flexfec_ssrc;
+ bool flexfec_enabled = false;
+ for (uint32_t primary_ssrc : parameters_.config.rtp.ssrcs) {
+ if (sp.GetFecFrSsrc(primary_ssrc, &flexfec_ssrc)) {
+ if (flexfec_enabled) {
+ RTC_LOG(LS_INFO)
+ << "Multiple FlexFEC streams in local SDP, but "
+ "our implementation only supports a single FlexFEC "
+ "stream. Will not enable FlexFEC for proposed "
+ "stream with SSRC: "
+ << flexfec_ssrc << ".";
+ continue;
+ }
+
+ flexfec_enabled = true;
+ parameters_.config.rtp.flexfec.ssrc = flexfec_ssrc;
+ parameters_.config.rtp.flexfec.protected_media_ssrcs = {primary_ssrc};
+ }
+ }
+ }
+
+ parameters_.config.rtp.c_name = sp.cname;
+ if (rtp_extensions) {
+ parameters_.config.rtp.extensions = *rtp_extensions;
+ rtp_parameters_.header_extensions = *rtp_extensions;
+ }
+ parameters_.config.rtp.rtcp_mode = send_params.rtcp.reduced_size
+ ? webrtc::RtcpMode::kReducedSize
+ : webrtc::RtcpMode::kCompound;
+ parameters_.config.rtp.mid = send_params.mid;
+ rtp_parameters_.rtcp.reduced_size = send_params.rtcp.reduced_size;
+
+ if (codec_settings) {
+ SetCodec(*codec_settings);
+ }
+}
+
+WebRtcVideoSendChannel::WebRtcVideoSendStream::~WebRtcVideoSendStream() {
+ if (stream_ != NULL) {
+ call_->DestroyVideoSendStream(stream_);
+ }
+}
+
+bool WebRtcVideoSendChannel::WebRtcVideoSendStream::SetVideoSend(
+ const VideoOptions* options,
+ rtc::VideoSourceInterface<webrtc::VideoFrame>* source) {
+ TRACE_EVENT0("webrtc", "WebRtcVideoSendStream::SetVideoSend");
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+
+ if (options) {
+ VideoOptions old_options = parameters_.options;
+ parameters_.options.SetAll(*options);
+ if (parameters_.options.is_screencast.value_or(false) !=
+ old_options.is_screencast.value_or(false) &&
+ parameters_.codec_settings) {
+ // If screen content settings change, we may need to recreate the codec
+ // instance so that the correct type is used.
+
+ SetCodec(*parameters_.codec_settings);
+ // Mark screenshare parameter as being updated, then test for any other
+ // changes that may require codec reconfiguration.
+ old_options.is_screencast = options->is_screencast;
+ }
+ if (parameters_.options != old_options) {
+ ReconfigureEncoder(nullptr);
+ }
+ }
+
+ if (source_ && stream_) {
+ stream_->SetSource(nullptr, webrtc::DegradationPreference::DISABLED);
+ }
+ // Switch to the new source.
+ source_ = source;
+ if (source && stream_) {
+ stream_->SetSource(source_, GetDegradationPreference());
+ }
+ return true;
+}
+
+webrtc::DegradationPreference
+WebRtcVideoSendChannel::WebRtcVideoSendStream::GetDegradationPreference()
+ const {
+ // Do not adapt resolution for screen content as this will likely
+ // result in blurry and unreadable text.
+ // `this` acts like a VideoSource to make sure SinkWants are handled on the
+ // correct thread.
+ if (!enable_cpu_overuse_detection_) {
+ return webrtc::DegradationPreference::DISABLED;
+ }
+
+ webrtc::DegradationPreference degradation_preference;
+ if (rtp_parameters_.degradation_preference.has_value()) {
+ degradation_preference = *rtp_parameters_.degradation_preference;
+ } else {
+ if (parameters_.options.content_hint ==
+ webrtc::VideoTrackInterface::ContentHint::kFluid) {
+ degradation_preference =
+ webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
+ } else if (parameters_.options.is_screencast.value_or(false) ||
+ parameters_.options.content_hint ==
+ webrtc::VideoTrackInterface::ContentHint::kDetailed ||
+ parameters_.options.content_hint ==
+ webrtc::VideoTrackInterface::ContentHint::kText) {
+ degradation_preference =
+ webrtc::DegradationPreference::MAINTAIN_RESOLUTION;
+ } else if (IsEnabled(call_->trials(), "WebRTC-Video-BalancedDegradation")) {
+ // Standard wants balanced by default, but it needs to be tuned first.
+ degradation_preference = webrtc::DegradationPreference::BALANCED;
+ } else {
+ // Keep MAINTAIN_FRAMERATE by default until BALANCED has been tuned for
+ // all codecs and launched.
+ degradation_preference =
+ webrtc::DegradationPreference::MAINTAIN_FRAMERATE;
+ }
+ }
+
+ return degradation_preference;
+}
+
+const std::vector<uint32_t>&
+WebRtcVideoSendChannel::WebRtcVideoSendStream::GetSsrcs() const {
+ return ssrcs_;
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::SetCodec(
+ const VideoCodecSettings& codec_settings) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ FallbackToDefaultScalabilityModeIfNotSupported(
+ codec_settings.codec, parameters_.config, rtp_parameters_.encodings);
+
+ parameters_.encoder_config = CreateVideoEncoderConfig(codec_settings.codec);
+ RTC_DCHECK_GT(parameters_.encoder_config.number_of_streams, 0);
+
+ parameters_.config.rtp.payload_name = codec_settings.codec.name;
+ parameters_.config.rtp.payload_type = codec_settings.codec.id;
+ parameters_.config.rtp.raw_payload =
+ codec_settings.codec.packetization == kPacketizationParamRaw;
+ parameters_.config.rtp.ulpfec = codec_settings.ulpfec;
+ parameters_.config.rtp.flexfec.payload_type =
+ codec_settings.flexfec_payload_type;
+
+ // Set RTX payload type if RTX is enabled.
+ if (!parameters_.config.rtp.rtx.ssrcs.empty()) {
+ if (codec_settings.rtx_payload_type == -1) {
+ RTC_LOG(LS_WARNING)
+ << "RTX SSRCs configured but there's no configured RTX "
+ "payload type. Ignoring.";
+ parameters_.config.rtp.rtx.ssrcs.clear();
+ } else {
+ parameters_.config.rtp.rtx.payload_type = codec_settings.rtx_payload_type;
+ }
+ }
+
+ const bool has_lntf = HasLntf(codec_settings.codec);
+ parameters_.config.rtp.lntf.enabled = has_lntf;
+ parameters_.config.encoder_settings.capabilities.loss_notification = has_lntf;
+
+ parameters_.config.rtp.nack.rtp_history_ms =
+ HasNack(codec_settings.codec) ? kNackHistoryMs : 0;
+
+ parameters_.codec_settings = codec_settings;
+
+ // TODO(bugs.webrtc.org/8830): Avoid recreation, it should be enough to call
+ // ReconfigureEncoder.
+ RTC_LOG(LS_INFO) << "RecreateWebRtcStream (send) because of SetCodec.";
+ RecreateWebRtcStream();
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::SetSendParameters(
+ const ChangedSendParameters& params) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ // `recreate_stream` means construction-time parameters have changed and the
+ // sending stream needs to be reset with the new config.
+ bool recreate_stream = false;
+ if (params.rtcp_mode) {
+ parameters_.config.rtp.rtcp_mode = *params.rtcp_mode;
+ rtp_parameters_.rtcp.reduced_size =
+ parameters_.config.rtp.rtcp_mode == webrtc::RtcpMode::kReducedSize;
+ recreate_stream = true;
+ }
+ if (params.extmap_allow_mixed) {
+ parameters_.config.rtp.extmap_allow_mixed = *params.extmap_allow_mixed;
+ recreate_stream = true;
+ }
+ if (params.rtp_header_extensions) {
+ parameters_.config.rtp.extensions = *params.rtp_header_extensions;
+ rtp_parameters_.header_extensions = *params.rtp_header_extensions;
+ recreate_stream = true;
+ }
+ if (params.mid) {
+ parameters_.config.rtp.mid = *params.mid;
+ recreate_stream = true;
+ }
+ if (params.max_bandwidth_bps) {
+ parameters_.max_bitrate_bps = *params.max_bandwidth_bps;
+ ReconfigureEncoder(nullptr);
+ }
+ if (params.conference_mode) {
+ parameters_.conference_mode = *params.conference_mode;
+ }
+
+ // Set codecs and options.
+ if (params.send_codec) {
+ SetCodec(*params.send_codec);
+ recreate_stream = false; // SetCodec has already recreated the stream.
+ } else if (params.conference_mode && parameters_.codec_settings) {
+ SetCodec(*parameters_.codec_settings);
+ recreate_stream = false; // SetCodec has already recreated the stream.
+ }
+ if (recreate_stream) {
+ RTC_LOG(LS_INFO)
+ << "RecreateWebRtcStream (send) because of SetSendParameters";
+ RecreateWebRtcStream();
+ }
+}
+
+webrtc::RTCError
+WebRtcVideoSendChannel::WebRtcVideoSendStream::SetRtpParameters(
+ const webrtc::RtpParameters& new_parameters,
+ webrtc::SetParametersCallback callback) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ // This is checked higher in the stack (RtpSender), so this is only checking
+ // for users accessing the private APIs or tests, not specification
+ // conformance.
+ // TODO(orphis): Migrate tests to later make this a DCHECK only
+ webrtc::RTCError error = CheckRtpParametersInvalidModificationAndValues(
+ rtp_parameters_, new_parameters);
+ if (!error.ok()) {
+ // Error is propagated to the callback at a higher level
+ return error;
+ }
+
+ bool new_param = false;
+ for (size_t i = 0; i < rtp_parameters_.encodings.size(); ++i) {
+ if ((new_parameters.encodings[i].min_bitrate_bps !=
+ rtp_parameters_.encodings[i].min_bitrate_bps) ||
+ (new_parameters.encodings[i].max_bitrate_bps !=
+ rtp_parameters_.encodings[i].max_bitrate_bps) ||
+ (new_parameters.encodings[i].max_framerate !=
+ rtp_parameters_.encodings[i].max_framerate) ||
+ (new_parameters.encodings[i].scale_resolution_down_by !=
+ rtp_parameters_.encodings[i].scale_resolution_down_by) ||
+ (new_parameters.encodings[i].num_temporal_layers !=
+ rtp_parameters_.encodings[i].num_temporal_layers) ||
+ (new_parameters.encodings[i].requested_resolution !=
+ rtp_parameters_.encodings[i].requested_resolution) ||
+ (new_parameters.encodings[i].scalability_mode !=
+ rtp_parameters_.encodings[i].scalability_mode)) {
+ new_param = true;
+ break;
+ }
+ }
+
+ bool new_degradation_preference = false;
+ if (new_parameters.degradation_preference !=
+ rtp_parameters_.degradation_preference) {
+ new_degradation_preference = true;
+ }
+
+ // Some fields (e.g. bitrate priority) only need to update the bitrate
+ // allocator which is updated via ReconfigureEncoder (however, note that the
+ // actual encoder should only be reconfigured if needed).
+ bool reconfigure_encoder =
+ new_param || (new_parameters.encodings[0].bitrate_priority !=
+ rtp_parameters_.encodings[0].bitrate_priority);
+
+ // Note that the simulcast encoder adapter relies on the fact that layers
+ // de/activation triggers encoder reinitialization.
+ bool new_send_state = false;
+ for (size_t i = 0; i < rtp_parameters_.encodings.size(); ++i) {
+ bool new_active = IsLayerActive(new_parameters.encodings[i]);
+ bool old_active = IsLayerActive(rtp_parameters_.encodings[i]);
+ if (new_active != old_active) {
+ new_send_state = true;
+ }
+ }
+ rtp_parameters_ = new_parameters;
+ // Codecs are currently handled at the WebRtcVideoChannel level.
+ rtp_parameters_.codecs.clear();
+ if (reconfigure_encoder || new_send_state) {
+ // Callback responsibility is delegated to ReconfigureEncoder()
+ ReconfigureEncoder(std::move(callback));
+ callback = nullptr;
+ }
+ if (new_send_state) {
+ UpdateSendState();
+ }
+ if (new_degradation_preference) {
+ if (source_ && stream_) {
+ stream_->SetSource(source_, GetDegradationPreference());
+ }
+ }
+ return webrtc::InvokeSetParametersCallback(callback, webrtc::RTCError::OK());
+}
+
+webrtc::RtpParameters
+WebRtcVideoSendChannel::WebRtcVideoSendStream::GetRtpParameters() const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ return rtp_parameters_;
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::SetFrameEncryptor(
+ rtc::scoped_refptr<webrtc::FrameEncryptorInterface> frame_encryptor) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ parameters_.config.frame_encryptor = frame_encryptor;
+ if (stream_) {
+ RTC_LOG(LS_INFO)
+ << "RecreateWebRtcStream (send) because of SetFrameEncryptor, ssrc="
+ << parameters_.config.rtp.ssrcs[0];
+ RecreateWebRtcStream();
+ }
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::SetEncoderSelector(
+ webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ parameters_.config.encoder_selector = encoder_selector;
+ if (stream_) {
+ RTC_LOG(LS_INFO)
+ << "RecreateWebRtcStream (send) because of SetEncoderSelector, ssrc="
+ << parameters_.config.rtp.ssrcs[0];
+ RecreateWebRtcStream();
+ }
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::UpdateSendState() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (sending_) {
+ RTC_DCHECK(stream_ != nullptr);
+ size_t num_layers = rtp_parameters_.encodings.size();
+ if (parameters_.encoder_config.number_of_streams == 1) {
+ // SVC is used. Only one simulcast layer is present.
+ num_layers = 1;
+ }
+ std::vector<bool> active_layers(num_layers);
+ for (size_t i = 0; i < num_layers; ++i) {
+ active_layers[i] = IsLayerActive(rtp_parameters_.encodings[i]);
+ }
+ if (parameters_.encoder_config.number_of_streams == 1 &&
+ rtp_parameters_.encodings.size() > 1) {
+ // SVC is used.
+ // The only present simulcast layer should be active if any of the
+ // configured SVC layers is active.
+ active_layers[0] =
+ absl::c_any_of(rtp_parameters_.encodings,
+ [](const auto& encoding) { return encoding.active; });
+ }
+ // This updates what simulcast layers are sending, and possibly starts
+ // or stops the VideoSendStream.
+ stream_->StartPerRtpStream(active_layers);
+ } else {
+ if (stream_ != nullptr) {
+ stream_->Stop();
+ }
+ }
+}
+
+webrtc::VideoEncoderConfig
+WebRtcVideoSendChannel::WebRtcVideoSendStream::CreateVideoEncoderConfig(
+ const VideoCodec& codec) const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ webrtc::VideoEncoderConfig encoder_config;
+ encoder_config.codec_type = webrtc::PayloadStringToCodecType(codec.name);
+ encoder_config.video_format =
+ webrtc::SdpVideoFormat(codec.name, codec.params);
+
+ bool is_screencast = parameters_.options.is_screencast.value_or(false);
+ if (is_screencast) {
+ encoder_config.min_transmit_bitrate_bps =
+ 1000 * parameters_.options.screencast_min_bitrate_kbps.value_or(0);
+ encoder_config.content_type =
+ webrtc::VideoEncoderConfig::ContentType::kScreen;
+ } else {
+ encoder_config.min_transmit_bitrate_bps = 0;
+ encoder_config.content_type =
+ webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo;
+ }
+
+ // By default, the stream count for the codec configuration should match the
+ // number of negotiated ssrcs but this may be capped below depending on the
+ // `legacy_scalability_mode` and codec used.
+ encoder_config.number_of_streams = parameters_.config.rtp.ssrcs.size();
+ bool legacy_scalability_mode = true;
+ // TODO(https://crbug.com/webrtc/14884): This is only used as a kill-switch
+ // in case of serious bugs - when this reaches Stable, delete the field trial.
+ if (!call_->trials().IsDisabled("WebRTC-AllowDisablingLegacyScalability")) {
+ for (const webrtc::RtpEncodingParameters& encoding :
+ rtp_parameters_.encodings) {
+ if (encoding.scalability_mode.has_value() &&
+ encoding.scale_resolution_down_by.has_value()) {
+ legacy_scalability_mode = false;
+ break;
+ }
+ }
+ }
+ // Maybe limit the number of simulcast layers depending on
+ // `legacy_scalability_mode`, codec types (VP9/AV1). This path only exists
+ // for backwards compatibility and will one day be deleted. If you want SVC,
+ // please specify with the `scalability_mode` API instead amd disabling all
+ // but one encoding.
+ if (IsCodecDisabledForSimulcast(legacy_scalability_mode,
+ encoder_config.codec_type)) {
+ encoder_config.number_of_streams = 1;
+ }
+
+ // parameters_.max_bitrate comes from the max bitrate set at the SDP
+ // (m-section) level with the attribute "b=AS." Note that we override this
+ // value below if the RtpParameters max bitrate set with
+ // RtpSender::SetParameters has a lower value.
+ int stream_max_bitrate = parameters_.max_bitrate_bps;
+ // When simulcast is enabled (when there are multiple encodings),
+ // encodings[i].max_bitrate_bps will be enforced by
+ // encoder_config.simulcast_layers[i].max_bitrate_bps. Otherwise, it's
+ // enforced by stream_max_bitrate, taking the minimum of the two maximums
+ // (one coming from SDP, the other coming from RtpParameters).
+ if (rtp_parameters_.encodings[0].max_bitrate_bps &&
+ rtp_parameters_.encodings.size() == 1) {
+ stream_max_bitrate =
+ MinPositive(*(rtp_parameters_.encodings[0].max_bitrate_bps),
+ parameters_.max_bitrate_bps);
+ }
+
+ // The codec max bitrate comes from the "x-google-max-bitrate" parameter
+ // attribute set in the SDP for a specific codec. As done in
+ // WebRtcVideoChannel::SetSendParameters, this value does not override the
+ // stream max_bitrate set above.
+ int codec_max_bitrate_kbps;
+ if (codec.GetParam(kCodecParamMaxBitrate, &codec_max_bitrate_kbps) &&
+ stream_max_bitrate == -1) {
+ stream_max_bitrate = codec_max_bitrate_kbps * 1000;
+ }
+ encoder_config.max_bitrate_bps = stream_max_bitrate;
+
+ // The encoder config's default bitrate priority is set to 1.0,
+ // unless it is set through the sender's encoding parameters.
+ // The bitrate priority, which is used in the bitrate allocation, is done
+ // on a per sender basis, so we use the first encoding's value.
+ encoder_config.bitrate_priority =
+ rtp_parameters_.encodings[0].bitrate_priority;
+
+ // Application-controlled state is held in the encoder_config's
+ // simulcast_layers. Currently this is used to control which simulcast layers
+ // are active and for configuring the min/max bitrate and max framerate.
+ // The encoder_config's simulcast_layers is also used for non-simulcast (when
+ // there is a single layer).
+ RTC_DCHECK_GE(rtp_parameters_.encodings.size(),
+ encoder_config.number_of_streams);
+ RTC_DCHECK_GT(encoder_config.number_of_streams, 0);
+
+ // Copy all provided constraints.
+ encoder_config.simulcast_layers.resize(rtp_parameters_.encodings.size());
+ for (size_t i = 0; i < encoder_config.simulcast_layers.size(); ++i) {
+ encoder_config.simulcast_layers[i].active =
+ rtp_parameters_.encodings[i].active;
+ encoder_config.simulcast_layers[i].scalability_mode =
+ webrtc::ScalabilityModeFromString(
+ rtp_parameters_.encodings[i].scalability_mode.value_or(""));
+ if (rtp_parameters_.encodings[i].min_bitrate_bps) {
+ encoder_config.simulcast_layers[i].min_bitrate_bps =
+ *rtp_parameters_.encodings[i].min_bitrate_bps;
+ }
+ if (rtp_parameters_.encodings[i].max_bitrate_bps) {
+ encoder_config.simulcast_layers[i].max_bitrate_bps =
+ *rtp_parameters_.encodings[i].max_bitrate_bps;
+ }
+ if (rtp_parameters_.encodings[i].max_framerate) {
+ encoder_config.simulcast_layers[i].max_framerate =
+ *rtp_parameters_.encodings[i].max_framerate;
+ }
+ if (rtp_parameters_.encodings[i].scale_resolution_down_by) {
+ encoder_config.simulcast_layers[i].scale_resolution_down_by =
+ *rtp_parameters_.encodings[i].scale_resolution_down_by;
+ }
+ if (rtp_parameters_.encodings[i].num_temporal_layers) {
+ encoder_config.simulcast_layers[i].num_temporal_layers =
+ *rtp_parameters_.encodings[i].num_temporal_layers;
+ }
+ encoder_config.simulcast_layers[i].requested_resolution =
+ rtp_parameters_.encodings[i].requested_resolution;
+ }
+
+ encoder_config.legacy_conference_mode = parameters_.conference_mode;
+
+ encoder_config.is_quality_scaling_allowed =
+ !disable_automatic_resize_ && !is_screencast &&
+ (parameters_.config.rtp.ssrcs.size() == 1 ||
+ NumActiveStreams(rtp_parameters_) == 1);
+
+ // Ensure frame dropping is always enabled.
+ encoder_config.frame_drop_enabled = true;
+
+ int max_qp = kDefaultQpMax;
+ codec.GetParam(kCodecParamMaxQuantization, &max_qp);
+ encoder_config.max_qp = max_qp;
+
+ return encoder_config;
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::ReconfigureEncoder(
+ webrtc::SetParametersCallback callback) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (!stream_) {
+ // The webrtc::VideoSendStream `stream_` has not yet been created but other
+ // parameters has changed.
+ webrtc::InvokeSetParametersCallback(callback, webrtc::RTCError::OK());
+ return;
+ }
+
+ RTC_DCHECK_GT(parameters_.encoder_config.number_of_streams, 0);
+
+ RTC_CHECK(parameters_.codec_settings);
+ VideoCodecSettings codec_settings = *parameters_.codec_settings;
+
+ FallbackToDefaultScalabilityModeIfNotSupported(
+ codec_settings.codec, parameters_.config, rtp_parameters_.encodings);
+
+ // Latest config, with and without encoder specfic settings.
+ webrtc::VideoEncoderConfig encoder_config =
+ CreateVideoEncoderConfig(codec_settings.codec);
+ encoder_config.encoder_specific_settings =
+ ConfigureVideoEncoderSettings(codec_settings.codec);
+ webrtc::VideoEncoderConfig encoder_config_with_specifics =
+ encoder_config.Copy();
+ encoder_config.encoder_specific_settings = nullptr;
+
+ // When switching between legacy SVC (3 encodings interpreted as 1 stream with
+ // 3 spatial layers) and the standard API (3 encodings = 3 streams and spatial
+ // layers specified by `scalability_mode`), the number of streams can change.
+ bool num_streams_changed = parameters_.encoder_config.number_of_streams !=
+ encoder_config.number_of_streams;
+ bool scalability_mode_used = !codec_settings.codec.scalability_modes.empty();
+ bool scalability_modes = absl::c_any_of(
+ rtp_parameters_.encodings,
+ [](const auto& e) { return e.scalability_mode.has_value(); });
+
+ parameters_.encoder_config = std::move(encoder_config);
+
+ if (num_streams_changed && (scalability_mode_used != scalability_modes)) {
+ // The app is switching between legacy and standard modes, recreate instead
+ // of reconfiguring to avoid number of streams not matching in lower layers.
+ RecreateWebRtcStream();
+ webrtc::InvokeSetParametersCallback(callback, webrtc::RTCError::OK());
+ return;
+ }
+
+ stream_->ReconfigureVideoEncoder(std::move(encoder_config_with_specifics),
+ std::move(callback));
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::SetSend(bool send) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ sending_ = send;
+ UpdateSendState();
+}
+
+std::vector<VideoSenderInfo>
+WebRtcVideoSendChannel::WebRtcVideoSendStream::GetPerLayerVideoSenderInfos(
+ bool log_stats) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ VideoSenderInfo common_info;
+ if (parameters_.codec_settings) {
+ common_info.codec_name = parameters_.codec_settings->codec.name;
+ common_info.codec_payload_type = parameters_.codec_settings->codec.id;
+ }
+ std::vector<VideoSenderInfo> infos;
+ webrtc::VideoSendStream::Stats stats;
+ if (stream_ == nullptr) {
+ for (uint32_t ssrc : parameters_.config.rtp.ssrcs) {
+ common_info.add_ssrc(ssrc);
+ }
+ infos.push_back(common_info);
+ return infos;
+ } else {
+ stats = stream_->GetStats();
+ if (log_stats)
+ RTC_LOG(LS_INFO) << stats.ToString(rtc::TimeMillis());
+
+ // Metrics that are in common for all substreams.
+ common_info.adapt_changes = stats.number_of_cpu_adapt_changes;
+ common_info.adapt_reason =
+ stats.cpu_limited_resolution ? ADAPTREASON_CPU : ADAPTREASON_NONE;
+ common_info.has_entered_low_resolution = stats.has_entered_low_resolution;
+
+ // Get bandwidth limitation info from stream_->GetStats().
+ // Input resolution (output from video_adapter) can be further scaled down
+ // or higher video layer(s) can be dropped due to bitrate constraints.
+ // Note, adapt_changes only include changes from the video_adapter.
+ if (stats.bw_limited_resolution)
+ common_info.adapt_reason |= ADAPTREASON_BANDWIDTH;
+
+ common_info.quality_limitation_reason = stats.quality_limitation_reason;
+ common_info.quality_limitation_durations_ms =
+ stats.quality_limitation_durations_ms;
+ common_info.quality_limitation_resolution_changes =
+ stats.quality_limitation_resolution_changes;
+ common_info.encoder_implementation_name = stats.encoder_implementation_name;
+ common_info.target_bitrate = stats.target_media_bitrate_bps;
+ common_info.ssrc_groups = ssrc_groups_;
+ common_info.frames = stats.frames;
+ common_info.framerate_input = stats.input_frame_rate;
+ common_info.avg_encode_ms = stats.avg_encode_time_ms;
+ common_info.encode_usage_percent = stats.encode_usage_percent;
+ common_info.nominal_bitrate = stats.media_bitrate_bps;
+ common_info.content_type = stats.content_type;
+ common_info.aggregated_framerate_sent = stats.encode_frame_rate;
+ common_info.aggregated_huge_frames_sent = stats.huge_frames_sent;
+ common_info.power_efficient_encoder = stats.power_efficient_encoder;
+
+ // The normal case is that substreams are present, handled below. But if
+ // substreams are missing (can happen before negotiated/connected where we
+ // have no stats yet) a single outbound-rtp is created representing any and
+ // all layers.
+ if (stats.substreams.empty()) {
+ for (uint32_t ssrc : parameters_.config.rtp.ssrcs) {
+ common_info.add_ssrc(ssrc);
+ }
+ common_info.active =
+ IsActiveFromEncodings(absl::nullopt, rtp_parameters_.encodings);
+ common_info.framerate_sent = stats.encode_frame_rate;
+ common_info.frames_encoded = stats.frames_encoded;
+ common_info.total_encode_time_ms = stats.total_encode_time_ms;
+ common_info.total_encoded_bytes_target = stats.total_encoded_bytes_target;
+ common_info.frames_sent = stats.frames_encoded;
+ common_info.huge_frames_sent = stats.huge_frames_sent;
+ infos.push_back(common_info);
+ return infos;
+ }
+ }
+ // Merge `stats.substreams`, which may contain additional SSRCs for RTX or
+ // Flexfec, with media SSRCs. This results in a set of substreams that match
+ // with the outbound-rtp stats objects.
+ auto outbound_rtp_substreams =
+ MergeInfoAboutOutboundRtpSubstreams(stats.substreams);
+ // If SVC is used, one stream is configured but multiple encodings exist. This
+ // is not spec-compliant, but it is how we've implemented SVC so this affects
+ // how the RTP stream's "active" value is determined.
+ bool is_svc = (parameters_.encoder_config.number_of_streams == 1 &&
+ rtp_parameters_.encodings.size() > 1);
+ for (const auto& pair : outbound_rtp_substreams) {
+ auto info = common_info;
+ uint32_t ssrc = pair.first;
+ info.add_ssrc(ssrc);
+ info.rid = parameters_.config.rtp.GetRidForSsrc(ssrc);
+ info.active = IsActiveFromEncodings(
+ !is_svc ? absl::optional<uint32_t>(ssrc) : absl::nullopt,
+ rtp_parameters_.encodings);
+ auto stream_stats = pair.second;
+ RTC_DCHECK_EQ(stream_stats.type,
+ webrtc::VideoSendStream::StreamStats::StreamType::kMedia);
+ info.payload_bytes_sent = stream_stats.rtp_stats.transmitted.payload_bytes;
+ info.header_and_padding_bytes_sent =
+ stream_stats.rtp_stats.transmitted.header_bytes +
+ stream_stats.rtp_stats.transmitted.padding_bytes;
+ info.packets_sent = stream_stats.rtp_stats.transmitted.packets;
+ info.total_packet_send_delay +=
+ stream_stats.rtp_stats.transmitted.total_packet_delay;
+ info.send_frame_width = stream_stats.width;
+ info.send_frame_height = stream_stats.height;
+ info.key_frames_encoded = stream_stats.frame_counts.key_frames;
+ info.framerate_sent = stream_stats.encode_frame_rate;
+ info.frames_encoded = stream_stats.frames_encoded;
+ info.frames_sent = stream_stats.frames_encoded;
+ info.retransmitted_bytes_sent =
+ stream_stats.rtp_stats.retransmitted.payload_bytes;
+ info.retransmitted_packets_sent =
+ stream_stats.rtp_stats.retransmitted.packets;
+ info.firs_received = stream_stats.rtcp_packet_type_counts.fir_packets;
+ info.nacks_received = stream_stats.rtcp_packet_type_counts.nack_packets;
+ info.plis_received = stream_stats.rtcp_packet_type_counts.pli_packets;
+ if (stream_stats.report_block_data.has_value()) {
+ info.packets_lost = stream_stats.report_block_data->cumulative_lost();
+ info.fraction_lost = stream_stats.report_block_data->fraction_lost();
+ info.report_block_datas.push_back(*stream_stats.report_block_data);
+ }
+ info.qp_sum = stream_stats.qp_sum;
+ info.total_encode_time_ms = stream_stats.total_encode_time_ms;
+ info.total_encoded_bytes_target = stream_stats.total_encoded_bytes_target;
+ info.huge_frames_sent = stream_stats.huge_frames_sent;
+ info.scalability_mode = stream_stats.scalability_mode;
+ infos.push_back(info);
+ }
+ return infos;
+}
+
+VideoSenderInfo
+WebRtcVideoSendChannel::WebRtcVideoSendStream::GetAggregatedVideoSenderInfo(
+ const std::vector<VideoSenderInfo>& infos) const {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ RTC_CHECK(!infos.empty());
+ if (infos.size() == 1) {
+ return infos[0];
+ }
+ VideoSenderInfo info = infos[0];
+ info.local_stats.clear();
+ for (uint32_t ssrc : parameters_.config.rtp.ssrcs) {
+ info.add_ssrc(ssrc);
+ }
+ info.framerate_sent = info.aggregated_framerate_sent;
+ info.huge_frames_sent = info.aggregated_huge_frames_sent;
+
+ for (size_t i = 1; i < infos.size(); i++) {
+ info.key_frames_encoded += infos[i].key_frames_encoded;
+ info.payload_bytes_sent += infos[i].payload_bytes_sent;
+ info.header_and_padding_bytes_sent +=
+ infos[i].header_and_padding_bytes_sent;
+ info.packets_sent += infos[i].packets_sent;
+ info.total_packet_send_delay += infos[i].total_packet_send_delay;
+ info.retransmitted_bytes_sent += infos[i].retransmitted_bytes_sent;
+ info.retransmitted_packets_sent += infos[i].retransmitted_packets_sent;
+ info.packets_lost += infos[i].packets_lost;
+ if (infos[i].send_frame_width > info.send_frame_width)
+ info.send_frame_width = infos[i].send_frame_width;
+ if (infos[i].send_frame_height > info.send_frame_height)
+ info.send_frame_height = infos[i].send_frame_height;
+ info.firs_received += infos[i].firs_received;
+ info.nacks_received += infos[i].nacks_received;
+ info.plis_received += infos[i].plis_received;
+ if (infos[i].report_block_datas.size())
+ info.report_block_datas.push_back(infos[i].report_block_datas[0]);
+ if (infos[i].qp_sum) {
+ if (!info.qp_sum) {
+ info.qp_sum = 0;
+ }
+ info.qp_sum = *info.qp_sum + *infos[i].qp_sum;
+ }
+ info.frames_encoded += infos[i].frames_encoded;
+ info.frames_sent += infos[i].frames_sent;
+ info.total_encode_time_ms += infos[i].total_encode_time_ms;
+ info.total_encoded_bytes_target += infos[i].total_encoded_bytes_target;
+ }
+ return info;
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::FillBitrateInfo(
+ BandwidthEstimationInfo* bwe_info) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (stream_ == NULL) {
+ return;
+ }
+ webrtc::VideoSendStream::Stats stats = stream_->GetStats();
+ for (const auto& it : stats.substreams) {
+ bwe_info->transmit_bitrate += it.second.total_bitrate_bps;
+ bwe_info->retransmit_bitrate += it.second.retransmit_bitrate_bps;
+ }
+ bwe_info->target_enc_bitrate += stats.target_media_bitrate_bps;
+ bwe_info->actual_enc_bitrate += stats.media_bitrate_bps;
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::
+ SetEncoderToPacketizerFrameTransformer(
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface>
+ frame_transformer) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ parameters_.config.frame_transformer = std::move(frame_transformer);
+ if (stream_)
+ RecreateWebRtcStream();
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::RecreateWebRtcStream() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (stream_ != NULL) {
+ call_->DestroyVideoSendStream(stream_);
+ }
+
+ RTC_CHECK(parameters_.codec_settings);
+ RTC_DCHECK_EQ((parameters_.encoder_config.content_type ==
+ webrtc::VideoEncoderConfig::ContentType::kScreen),
+ parameters_.options.is_screencast.value_or(false))
+ << "encoder content type inconsistent with screencast option";
+ parameters_.encoder_config.encoder_specific_settings =
+ ConfigureVideoEncoderSettings(parameters_.codec_settings->codec);
+
+ webrtc::VideoSendStream::Config config = parameters_.config.Copy();
+ if (!config.rtp.rtx.ssrcs.empty() && config.rtp.rtx.payload_type == -1) {
+ RTC_LOG(LS_WARNING) << "RTX SSRCs configured but there's no configured RTX "
+ "payload type the set codec. Ignoring RTX.";
+ config.rtp.rtx.ssrcs.clear();
+ }
+ if (parameters_.encoder_config.number_of_streams == 1) {
+ // SVC is used instead of simulcast. Remove unnecessary SSRCs.
+ if (config.rtp.ssrcs.size() > 1) {
+ config.rtp.ssrcs.resize(1);
+ if (config.rtp.rtx.ssrcs.size() > 1) {
+ config.rtp.rtx.ssrcs.resize(1);
+ }
+ }
+ }
+ stream_ = call_->CreateVideoSendStream(std::move(config),
+ parameters_.encoder_config.Copy());
+
+ parameters_.encoder_config.encoder_specific_settings = NULL;
+
+ // Calls stream_->StartPerRtpStream() to start the VideoSendStream
+ // if necessary conditions are met.
+ UpdateSendState();
+
+ // Attach the source after starting the send stream to prevent frames from
+ // being injected into a not-yet initializated video stream encoder.
+ if (source_) {
+ stream_->SetSource(source_, GetDegradationPreference());
+ }
+}
+
+void WebRtcVideoSendChannel::WebRtcVideoSendStream::GenerateKeyFrame(
+ const std::vector<std::string>& rids) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ if (stream_ != NULL) {
+ stream_->GenerateKeyFrame(rids);
+ } else {
+ RTC_LOG(LS_WARNING)
+ << "Absent send stream; ignoring request to generate keyframe.";
+ }
+}
+
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::WebRtcVideoReceiveStream(
+ webrtc::Call* call,
+ const StreamParams& sp,
+ webrtc::VideoReceiveStreamInterface::Config config,
+ bool default_stream,
+ const std::vector<VideoCodecSettings>& recv_codecs,
+ const webrtc::FlexfecReceiveStream::Config& flexfec_config)
+ : call_(call),
+ stream_params_(sp),
+ stream_(NULL),
+ default_stream_(default_stream),
+ config_(std::move(config)),
+ flexfec_config_(flexfec_config),
+ flexfec_stream_(nullptr),
+ sink_(NULL),
+ first_frame_timestamp_(-1),
+ estimated_remote_start_ntp_time_ms_(0),
+ receiving_(false) {
+ RTC_DCHECK(config_.decoder_factory);
+ RTC_DCHECK(config_.decoders.empty())
+ << "Decoder info is supplied via `recv_codecs`";
+
+ ExtractCodecInformation(recv_codecs, config_.rtp.rtx_associated_payload_types,
+ config_.rtp.raw_payload_types, config_.decoders);
+ const VideoCodecSettings& codec = recv_codecs.front();
+ config_.rtp.ulpfec_payload_type = codec.ulpfec.ulpfec_payload_type;
+ config_.rtp.red_payload_type = codec.ulpfec.red_payload_type;
+ config_.rtp.lntf.enabled = HasLntf(codec.codec);
+ config_.rtp.nack.rtp_history_ms = HasNack(codec.codec) ? kNackHistoryMs : 0;
+ if (codec.rtx_time && config_.rtp.nack.rtp_history_ms != 0) {
+ config_.rtp.nack.rtp_history_ms = *codec.rtx_time;
+ }
+
+ config_.rtp.rtcp_xr.receiver_reference_time_report = HasRrtr(codec.codec);
+
+ if (codec.ulpfec.red_rtx_payload_type != -1) {
+ config_.rtp
+ .rtx_associated_payload_types[codec.ulpfec.red_rtx_payload_type] =
+ codec.ulpfec.red_payload_type;
+ }
+
+ config_.renderer = this;
+ flexfec_config_.payload_type = flexfec_config.payload_type;
+
+ CreateReceiveStream();
+}
+
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
+ ~WebRtcVideoReceiveStream() {
+ call_->DestroyVideoReceiveStream(stream_);
+ if (flexfec_stream_)
+ call_->DestroyFlexfecReceiveStream(flexfec_stream_);
+}
+
+webrtc::VideoReceiveStreamInterface&
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::stream() {
+ RTC_DCHECK(stream_);
+ return *stream_;
+}
+
+webrtc::FlexfecReceiveStream*
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::flexfec_stream() {
+ return flexfec_stream_;
+}
+
+const std::vector<uint32_t>&
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::GetSsrcs() const {
+ return stream_params_.ssrcs;
+}
+
+std::vector<webrtc::RtpSource>
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::GetSources() {
+ RTC_DCHECK(stream_);
+ return stream_->GetSources();
+}
+
+webrtc::RtpParameters
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::GetRtpParameters() const {
+ webrtc::RtpParameters rtp_parameters;
+
+ std::vector<uint32_t> primary_ssrcs;
+ stream_params_.GetPrimarySsrcs(&primary_ssrcs);
+ for (uint32_t ssrc : primary_ssrcs) {
+ rtp_parameters.encodings.emplace_back();
+ rtp_parameters.encodings.back().ssrc = ssrc;
+ }
+
+ rtp_parameters.rtcp.reduced_size =
+ config_.rtp.rtcp_mode == webrtc::RtcpMode::kReducedSize;
+
+ return rtp_parameters;
+}
+
+bool WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::ReconfigureCodecs(
+ const std::vector<VideoCodecSettings>& recv_codecs) {
+ RTC_DCHECK(stream_);
+ RTC_DCHECK(!recv_codecs.empty());
+
+ std::map<int, int> rtx_associated_payload_types;
+ std::set<int> raw_payload_types;
+ std::vector<webrtc::VideoReceiveStreamInterface::Decoder> decoders;
+ ExtractCodecInformation(recv_codecs, rtx_associated_payload_types,
+ raw_payload_types, decoders);
+
+ const auto& codec = recv_codecs.front();
+
+ if (config_.rtp.red_payload_type != codec.ulpfec.red_payload_type ||
+ config_.rtp.ulpfec_payload_type != codec.ulpfec.ulpfec_payload_type) {
+ config_.rtp.ulpfec_payload_type = codec.ulpfec.ulpfec_payload_type;
+ config_.rtp.red_payload_type = codec.ulpfec.red_payload_type;
+ stream_->SetProtectionPayloadTypes(config_.rtp.red_payload_type,
+ config_.rtp.ulpfec_payload_type);
+ }
+
+ const bool has_lntf = HasLntf(codec.codec);
+ if (config_.rtp.lntf.enabled != has_lntf) {
+ config_.rtp.lntf.enabled = has_lntf;
+ stream_->SetLossNotificationEnabled(has_lntf);
+ }
+
+ int new_history_ms = config_.rtp.nack.rtp_history_ms;
+ const int rtp_history_ms = HasNack(codec.codec) ? kNackHistoryMs : 0;
+ if (rtp_history_ms != config_.rtp.nack.rtp_history_ms) {
+ new_history_ms = rtp_history_ms;
+ }
+
+ // The rtx-time parameter can be used to override the hardcoded default for
+ // the NACK buffer length.
+ if (codec.rtx_time && new_history_ms != 0) {
+ new_history_ms = *codec.rtx_time;
+ }
+
+ if (config_.rtp.nack.rtp_history_ms != new_history_ms) {
+ config_.rtp.nack.rtp_history_ms = new_history_ms;
+ stream_->SetNackHistory(webrtc::TimeDelta::Millis(new_history_ms));
+ }
+
+ const bool has_rtr = HasRrtr(codec.codec);
+ if (has_rtr != config_.rtp.rtcp_xr.receiver_reference_time_report) {
+ config_.rtp.rtcp_xr.receiver_reference_time_report = has_rtr;
+ stream_->SetRtcpXr(config_.rtp.rtcp_xr);
+ }
+
+ if (codec.ulpfec.red_rtx_payload_type != -1) {
+ rtx_associated_payload_types[codec.ulpfec.red_rtx_payload_type] =
+ codec.ulpfec.red_payload_type;
+ }
+
+ if (config_.rtp.rtx_associated_payload_types !=
+ rtx_associated_payload_types) {
+ stream_->SetAssociatedPayloadTypes(rtx_associated_payload_types);
+ rtx_associated_payload_types.swap(config_.rtp.rtx_associated_payload_types);
+ }
+
+ bool recreate_needed = false;
+
+ if (raw_payload_types != config_.rtp.raw_payload_types) {
+ raw_payload_types.swap(config_.rtp.raw_payload_types);
+ recreate_needed = true;
+ }
+
+ if (decoders != config_.decoders) {
+ decoders.swap(config_.decoders);
+ recreate_needed = true;
+ }
+
+ return recreate_needed;
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::SetFeedbackParameters(
+ bool lntf_enabled,
+ bool nack_enabled,
+ webrtc::RtcpMode rtcp_mode,
+ absl::optional<int> rtx_time) {
+ RTC_DCHECK(stream_);
+
+ if (config_.rtp.rtcp_mode != rtcp_mode) {
+ config_.rtp.rtcp_mode = rtcp_mode;
+ stream_->SetRtcpMode(rtcp_mode);
+
+ flexfec_config_.rtcp_mode = rtcp_mode;
+ if (flexfec_stream_) {
+ flexfec_stream_->SetRtcpMode(rtcp_mode);
+ }
+ }
+
+ config_.rtp.lntf.enabled = lntf_enabled;
+ stream_->SetLossNotificationEnabled(lntf_enabled);
+
+ int nack_history_ms = nack_enabled ? rtx_time.value_or(kNackHistoryMs) : 0;
+ config_.rtp.nack.rtp_history_ms = nack_history_ms;
+ stream_->SetNackHistory(webrtc::TimeDelta::Millis(nack_history_ms));
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::SetFlexFecPayload(
+ int payload_type) {
+ // TODO(bugs.webrtc.org/11993, tommi): See if it is better to always have a
+ // flexfec stream object around and instead of recreating the video stream,
+ // reconfigure the flexfec object from within the rtp callback (soon to be on
+ // the network thread).
+ if (flexfec_stream_) {
+ if (flexfec_stream_->payload_type() == payload_type) {
+ RTC_DCHECK_EQ(flexfec_config_.payload_type, payload_type);
+ return;
+ }
+
+ flexfec_config_.payload_type = payload_type;
+ flexfec_stream_->SetPayloadType(payload_type);
+
+ if (payload_type == -1) {
+ stream_->SetFlexFecProtection(nullptr);
+ call_->DestroyFlexfecReceiveStream(flexfec_stream_);
+ flexfec_stream_ = nullptr;
+ }
+ } else if (payload_type != -1) {
+ flexfec_config_.payload_type = payload_type;
+ if (flexfec_config_.IsCompleteAndEnabled()) {
+ flexfec_stream_ = call_->CreateFlexfecReceiveStream(flexfec_config_);
+ stream_->SetFlexFecProtection(flexfec_stream_);
+ }
+ } else {
+ // Noop. No flexfec stream exists and "new" payload_type == -1.
+ RTC_DCHECK(!flexfec_config_.IsCompleteAndEnabled());
+ flexfec_config_.payload_type = payload_type;
+ }
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::SetRecvParameters(
+ const ChangedRecvParameters& params) {
+ RTC_DCHECK(stream_);
+ bool video_needs_recreation = false;
+ if (params.codec_settings) {
+ video_needs_recreation = ReconfigureCodecs(*params.codec_settings);
+ }
+
+ if (params.flexfec_payload_type)
+ SetFlexFecPayload(*params.flexfec_payload_type);
+
+ if (video_needs_recreation) {
+ RecreateReceiveStream();
+ } else {
+ RTC_DLOG_F(LS_INFO) << "No receive stream recreate needed.";
+ }
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
+ RecreateReceiveStream() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ RTC_DCHECK(stream_);
+ absl::optional<int> base_minimum_playout_delay_ms;
+ absl::optional<webrtc::VideoReceiveStreamInterface::RecordingState>
+ recording_state;
+ if (stream_) {
+ base_minimum_playout_delay_ms = stream_->GetBaseMinimumPlayoutDelayMs();
+ recording_state = stream_->SetAndGetRecordingState(
+ webrtc::VideoReceiveStreamInterface::RecordingState(),
+ /*generate_key_frame=*/false);
+ call_->DestroyVideoReceiveStream(stream_);
+ stream_ = nullptr;
+ }
+
+ if (flexfec_stream_) {
+ call_->DestroyFlexfecReceiveStream(flexfec_stream_);
+ flexfec_stream_ = nullptr;
+ }
+
+ CreateReceiveStream();
+
+ if (base_minimum_playout_delay_ms) {
+ stream_->SetBaseMinimumPlayoutDelayMs(
+ base_minimum_playout_delay_ms.value());
+ }
+ if (recording_state) {
+ stream_->SetAndGetRecordingState(std::move(*recording_state),
+ /*generate_key_frame=*/false);
+ }
+ if (receiving_) {
+ StartReceiveStream();
+ }
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
+ CreateReceiveStream() {
+ RTC_DCHECK(!stream_);
+ RTC_DCHECK(!flexfec_stream_);
+ if (flexfec_config_.IsCompleteAndEnabled()) {
+ flexfec_stream_ = call_->CreateFlexfecReceiveStream(flexfec_config_);
+ }
+
+ webrtc::VideoReceiveStreamInterface::Config config = config_.Copy();
+ config.rtp.protected_by_flexfec = (flexfec_stream_ != nullptr);
+ config.rtp.packet_sink_ = flexfec_stream_;
+ stream_ = call_->CreateVideoReceiveStream(std::move(config));
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::StartReceiveStream() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ receiving_ = true;
+ stream_->Start();
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::StopReceiveStream() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ receiving_ = false;
+ stream_->Stop();
+ RecreateReceiveStream();
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::OnFrame(
+ const webrtc::VideoFrame& frame) {
+ webrtc::MutexLock lock(&sink_lock_);
+
+ int64_t time_now_ms = rtc::TimeMillis();
+ if (first_frame_timestamp_ < 0)
+ first_frame_timestamp_ = time_now_ms;
+ int64_t elapsed_time_ms = time_now_ms - first_frame_timestamp_;
+ if (frame.ntp_time_ms() > 0)
+ estimated_remote_start_ntp_time_ms_ = frame.ntp_time_ms() - elapsed_time_ms;
+
+ if (sink_ == NULL) {
+ RTC_LOG(LS_WARNING)
+ << "VideoReceiveStreamInterface not connected to a VideoSink.";
+ return;
+ }
+
+ sink_->OnFrame(frame);
+}
+
+bool WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::IsDefaultStream()
+ const {
+ return default_stream_;
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::SetFrameDecryptor(
+ rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
+ config_.frame_decryptor = frame_decryptor;
+ if (stream_) {
+ RTC_LOG(LS_INFO)
+ << "Setting FrameDecryptor (recv) because of SetFrameDecryptor, "
+ "remote_ssrc="
+ << config_.rtp.remote_ssrc;
+ stream_->SetFrameDecryptor(frame_decryptor);
+ }
+}
+
+bool WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
+ SetBaseMinimumPlayoutDelayMs(int delay_ms) {
+ return stream_ ? stream_->SetBaseMinimumPlayoutDelayMs(delay_ms) : false;
+}
+
+int WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
+ GetBaseMinimumPlayoutDelayMs() const {
+ return stream_ ? stream_->GetBaseMinimumPlayoutDelayMs() : 0;
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::SetSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
+ webrtc::MutexLock lock(&sink_lock_);
+ sink_ = sink;
+}
+
+VideoReceiverInfo
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo(
+ bool log_stats) {
+ VideoReceiverInfo info;
+ info.ssrc_groups = stream_params_.ssrc_groups;
+ info.add_ssrc(config_.rtp.remote_ssrc);
+ webrtc::VideoReceiveStreamInterface::Stats stats = stream_->GetStats();
+ info.decoder_implementation_name = stats.decoder_implementation_name;
+ info.power_efficient_decoder = stats.power_efficient_decoder;
+ if (stats.current_payload_type != -1) {
+ info.codec_payload_type = stats.current_payload_type;
+ auto decoder_it = absl::c_find_if(config_.decoders, [&](const auto& d) {
+ return d.payload_type == stats.current_payload_type;
+ });
+ if (decoder_it != config_.decoders.end())
+ info.codec_name = decoder_it->video_format.name;
+ }
+ info.payload_bytes_received = stats.rtp_stats.packet_counter.payload_bytes;
+ info.header_and_padding_bytes_received =
+ stats.rtp_stats.packet_counter.header_bytes +
+ stats.rtp_stats.packet_counter.padding_bytes;
+ info.packets_received = stats.rtp_stats.packet_counter.packets;
+ info.packets_lost = stats.rtp_stats.packets_lost;
+ info.jitter_ms = stats.rtp_stats.jitter / (kVideoCodecClockrate / 1000);
+
+ info.framerate_received = stats.network_frame_rate;
+ info.framerate_decoded = stats.decode_frame_rate;
+ info.framerate_output = stats.render_frame_rate;
+ info.frame_width = stats.width;
+ info.frame_height = stats.height;
+
+ {
+ webrtc::MutexLock frame_cs(&sink_lock_);
+ info.capture_start_ntp_time_ms = estimated_remote_start_ntp_time_ms_;
+ }
+
+ info.decode_ms = stats.decode_ms;
+ info.max_decode_ms = stats.max_decode_ms;
+ info.current_delay_ms = stats.current_delay_ms;
+ info.target_delay_ms = stats.target_delay_ms;
+ info.jitter_buffer_ms = stats.jitter_buffer_ms;
+ info.jitter_buffer_delay_seconds =
+ stats.jitter_buffer_delay.seconds<double>();
+ info.jitter_buffer_target_delay_seconds =
+ stats.jitter_buffer_target_delay.seconds<double>();
+ info.jitter_buffer_emitted_count = stats.jitter_buffer_emitted_count;
+ info.jitter_buffer_minimum_delay_seconds =
+ stats.jitter_buffer_minimum_delay.seconds<double>();
+ info.min_playout_delay_ms = stats.min_playout_delay_ms;
+ info.render_delay_ms = stats.render_delay_ms;
+ info.frames_received =
+ stats.frame_counts.key_frames + stats.frame_counts.delta_frames;
+ info.frames_dropped = stats.frames_dropped;
+ info.frames_decoded = stats.frames_decoded;
+ info.key_frames_decoded = stats.frame_counts.key_frames;
+ info.frames_rendered = stats.frames_rendered;
+ info.qp_sum = stats.qp_sum;
+ info.total_decode_time = stats.total_decode_time;
+ info.total_processing_delay = stats.total_processing_delay;
+ info.total_assembly_time = stats.total_assembly_time;
+ info.frames_assembled_from_multiple_packets =
+ stats.frames_assembled_from_multiple_packets;
+ info.last_packet_received_timestamp_ms =
+ stats.rtp_stats.last_packet_received_timestamp_ms;
+ info.estimated_playout_ntp_timestamp_ms =
+ stats.estimated_playout_ntp_timestamp_ms;
+ info.first_frame_received_to_decoded_ms =
+ stats.first_frame_received_to_decoded_ms;
+ info.total_inter_frame_delay = stats.total_inter_frame_delay;
+ info.total_squared_inter_frame_delay = stats.total_squared_inter_frame_delay;
+ info.interframe_delay_max_ms = stats.interframe_delay_max_ms;
+ info.freeze_count = stats.freeze_count;
+ info.pause_count = stats.pause_count;
+ info.total_freezes_duration_ms = stats.total_freezes_duration_ms;
+ info.total_pauses_duration_ms = stats.total_pauses_duration_ms;
+
+ info.content_type = stats.content_type;
+
+ info.firs_sent = stats.rtcp_packet_type_counts.fir_packets;
+ info.plis_sent = stats.rtcp_packet_type_counts.pli_packets;
+ info.nacks_sent = stats.rtcp_packet_type_counts.nack_packets;
+ // TODO(bugs.webrtc.org/10662): Add stats for LNTF.
+
+ info.timing_frame_info = stats.timing_frame_info;
+
+ if (stats.rtx_rtp_stats.has_value()) {
+ info.retransmitted_packets_received =
+ stats.rtx_rtp_stats->packet_counter.packets;
+ info.retransmitted_bytes_received =
+ stats.rtx_rtp_stats->packet_counter.payload_bytes;
+ // RTX information gets added to primary counters.
+ info.payload_bytes_received +=
+ stats.rtx_rtp_stats->packet_counter.payload_bytes;
+ info.header_and_padding_bytes_received +=
+ stats.rtx_rtp_stats->packet_counter.header_bytes +
+ stats.rtx_rtp_stats->packet_counter.padding_bytes;
+ info.packets_received += stats.rtx_rtp_stats->packet_counter.packets;
+ }
+
+ if (log_stats)
+ RTC_LOG(LS_INFO) << stats.ToString(rtc::TimeMillis());
+
+ return info;
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
+ SetRecordableEncodedFrameCallback(
+ std::function<void(const webrtc::RecordableEncodedFrame&)> callback) {
+ if (stream_) {
+ stream_->SetAndGetRecordingState(
+ webrtc::VideoReceiveStreamInterface::RecordingState(
+ std::move(callback)),
+ /*generate_key_frame=*/true);
+ } else {
+ RTC_LOG(LS_ERROR) << "Absent receive stream; ignoring setting encoded "
+ "frame sink";
+ }
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
+ ClearRecordableEncodedFrameCallback() {
+ if (stream_) {
+ stream_->SetAndGetRecordingState(
+ webrtc::VideoReceiveStreamInterface::RecordingState(),
+ /*generate_key_frame=*/false);
+ } else {
+ RTC_LOG(LS_ERROR) << "Absent receive stream; ignoring clearing encoded "
+ "frame sink";
+ }
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::GenerateKeyFrame() {
+ if (stream_) {
+ stream_->GenerateKeyFrame();
+ } else {
+ RTC_LOG(LS_ERROR)
+ << "Absent receive stream; ignoring key frame generation request.";
+ }
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::
+ SetDepacketizerToDecoderFrameTransformer(
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface>
+ frame_transformer) {
+ config_.frame_transformer = frame_transformer;
+ if (stream_)
+ stream_->SetDepacketizerToDecoderFrameTransformer(frame_transformer);
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::SetLocalSsrc(
+ uint32_t ssrc) {
+ config_.rtp.local_ssrc = ssrc;
+ call_->OnLocalSsrcUpdated(stream(), ssrc);
+ if (flexfec_stream_)
+ call_->OnLocalSsrcUpdated(*flexfec_stream_, ssrc);
+}
+
+void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::UpdateRtxSsrc(
+ uint32_t ssrc) {
+ stream_->UpdateRtxSsrc(ssrc);
+}
+
+VideoCodecSettings::VideoCodecSettings()
+ : flexfec_payload_type(-1), rtx_payload_type(-1) {}
+
+bool VideoCodecSettings::operator==(const VideoCodecSettings& other) const {
+ return codec == other.codec && ulpfec == other.ulpfec &&
+ flexfec_payload_type == other.flexfec_payload_type &&
+ rtx_payload_type == other.rtx_payload_type &&
+ rtx_time == other.rtx_time;
+}
+
+bool VideoCodecSettings::EqualsDisregardingFlexfec(
+ const VideoCodecSettings& a,
+ const VideoCodecSettings& b) {
+ return a.codec == b.codec && a.ulpfec == b.ulpfec &&
+ a.rtx_payload_type == b.rtx_payload_type && a.rtx_time == b.rtx_time;
+}
+
+bool VideoCodecSettings::operator!=(const VideoCodecSettings& other) const {
+ return !(*this == other);
+}
+
+WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream*
+WebRtcVideoReceiveChannel::FindReceiveStream(uint32_t ssrc) {
if (ssrc == 0) {
absl::optional<uint32_t> default_ssrc = GetUnsignaledSsrc();
if (!default_ssrc) {
@@ -3710,7 +3710,7 @@
}
// RTC_RUN_ON(worker_thread_)
-void WebRtcVideoChannel::ProcessReceivedPacket(
+void WebRtcVideoReceiveChannel::ProcessReceivedPacket(
webrtc::RtpPacketReceived packet) {
// TODO(bugs.webrtc.org/11993): This code is very similar to what
// WebRtcVoiceMediaChannel::OnPacketReceived does. For maintainability and
@@ -3731,11 +3731,11 @@
call_->Receiver()->DeliverRtpPacket(
webrtc::MediaType::VIDEO, std::move(packet),
- absl::bind_front(&WebRtcVideoChannel::MaybeCreateDefaultReceiveStream,
- this));
+ absl::bind_front(
+ &WebRtcVideoReceiveChannel::MaybeCreateDefaultReceiveStream, this));
}
-void WebRtcVideoChannel::SetRecordableEncodedFrameCallback(
+void WebRtcVideoReceiveChannel::SetRecordableEncodedFrameCallback(
uint32_t ssrc,
std::function<void(const webrtc::RecordableEncodedFrame&)> callback) {
RTC_DCHECK_RUN_ON(&thread_checker_);
@@ -3749,7 +3749,8 @@
}
}
-void WebRtcVideoChannel::ClearRecordableEncodedFrameCallback(uint32_t ssrc) {
+void WebRtcVideoReceiveChannel::ClearRecordableEncodedFrameCallback(
+ uint32_t ssrc) {
RTC_DCHECK_RUN_ON(&thread_checker_);
WebRtcVideoReceiveStream* stream = FindReceiveStream(ssrc);
if (stream) {
@@ -3761,7 +3762,7 @@
}
}
-void WebRtcVideoChannel::RequestRecvKeyFrame(uint32_t ssrc) {
+void WebRtcVideoReceiveChannel::RequestRecvKeyFrame(uint32_t ssrc) {
RTC_DCHECK_RUN_ON(&thread_checker_);
WebRtcVideoReceiveStream* stream = FindReceiveStream(ssrc);
if (stream) {
@@ -3773,7 +3774,7 @@
}
}
-void WebRtcVideoChannel::GenerateSendKeyFrame(
+void WebRtcVideoSendChannel::GenerateSendKeyFrame(
uint32_t ssrc,
const std::vector<std::string>& rids) {
RTC_DCHECK_RUN_ON(&thread_checker_);
@@ -3787,7 +3788,7 @@
}
}
-void WebRtcVideoChannel::SetEncoderToPacketizerFrameTransformer(
+void WebRtcVideoSendChannel::SetEncoderToPacketizerFrameTransformer(
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
RTC_DCHECK_RUN_ON(&thread_checker_);
@@ -3798,7 +3799,7 @@
}
}
-void WebRtcVideoChannel::SetDepacketizerToDecoderFrameTransformer(
+void WebRtcVideoReceiveChannel::SetDepacketizerToDecoderFrameTransformer(
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
RTC_DCHECK(frame_transformer);
diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h
index 5a5dd94..3c486b4 100644
--- a/media/engine/webrtc_video_engine.h
+++ b/media/engine/webrtc_video_engine.h
@@ -79,8 +79,6 @@
namespace cricket {
-class WebRtcVideoChannel;
-
// Public for testing.
// Inputs StreamStats for all types of substreams (kMedia, kRtx, kFlexfec) and
// merges any non-kMedia substream stats object into its referenced kMedia-type
@@ -134,12 +132,32 @@
const webrtc::FieldTrialsView& trials_;
};
-class WebRtcVideoChannel : public VideoMediaChannel,
- public webrtc::Transport,
- public webrtc::EncoderSwitchRequestCallback {
+struct VideoCodecSettings {
+ VideoCodecSettings();
+
+ // Checks if all members of |*this| are equal to the corresponding members
+ // of `other`.
+ bool operator==(const VideoCodecSettings& other) const;
+ bool operator!=(const VideoCodecSettings& other) const;
+
+ // Checks if all members of `a`, except `flexfec_payload_type`, are equal
+ // to the corresponding members of `b`.
+ static bool EqualsDisregardingFlexfec(const VideoCodecSettings& a,
+ const VideoCodecSettings& b);
+
+ VideoCodec codec;
+ webrtc::UlpfecConfig ulpfec;
+ int flexfec_payload_type; // -1 if absent.
+ int rtx_payload_type; // -1 if absent.
+ absl::optional<int> rtx_time;
+};
+
+class WebRtcVideoSendChannel : public MediaChannelUtil,
+ public VideoMediaSendChannelInterface,
+ public webrtc::Transport,
+ public webrtc::EncoderSwitchRequestCallback {
public:
- WebRtcVideoChannel(
- MediaChannel::Role role,
+ WebRtcVideoSendChannel(
webrtc::Call* call,
const MediaConfig& config,
const VideoOptions& options,
@@ -147,20 +165,36 @@
webrtc::VideoEncoderFactory* encoder_factory,
webrtc::VideoDecoderFactory* decoder_factory,
webrtc::VideoBitrateAllocatorFactory* bitrate_allocator_factory);
- ~WebRtcVideoChannel() override;
+ ~WebRtcVideoSendChannel() override;
- // VideoMediaChannel implementation
+ MediaType media_type() const override { return MEDIA_TYPE_VIDEO; }
+ // Type manipulations
+ VideoMediaSendChannelInterface* AsVideoSendChannel() override { return this; }
+ VoiceMediaSendChannelInterface* AsVoiceSendChannel() override {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+ // Functions imported from MediaChannelUtil
+ bool HasNetworkInterface() const override {
+ return MediaChannelUtil::HasNetworkInterface();
+ }
+ void SetExtmapAllowMixed(bool extmap_allow_mixed) override {
+ MediaChannelUtil::SetExtmapAllowMixed(extmap_allow_mixed);
+ }
+ bool ExtmapAllowMixed() const override {
+ return MediaChannelUtil::ExtmapAllowMixed();
+ }
+
+ // Common functions between sender and receiver
+ void SetInterface(MediaChannelNetworkInterface* iface) override;
+ // VideoMediaSendChannelInterface implementation
bool SetSendParameters(const VideoSendParameters& params) override;
- bool SetRecvParameters(const VideoRecvParameters& params) override;
- webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const override;
webrtc::RTCError SetRtpSendParameters(
uint32_t ssrc,
const webrtc::RtpParameters& parameters,
webrtc::SetParametersCallback callback) override;
- webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const override;
- webrtc::RtpParameters GetDefaultRtpReceiveParameters() const override;
+ webrtc::RtpParameters GetRtpSendParameters(uint32_t ssrc) const override;
bool GetSendCodec(VideoCodec* send_codec) override;
- void SetReceive(bool receive) override;
bool SetSend(bool send) override;
bool SetVideoSend(
uint32_t ssrc,
@@ -168,38 +202,14 @@
rtc::VideoSourceInterface<webrtc::VideoFrame>* source) override;
bool AddSendStream(const StreamParams& sp) override;
bool RemoveSendStream(uint32_t ssrc) override;
- bool AddRecvStream(const StreamParams& sp) override;
- bool AddDefaultRecvStreamForTesting(const StreamParams& sp) override {
- // Invokes private AddRecvStream variant function
- return AddRecvStream(sp, true);
- }
- bool RemoveRecvStream(uint32_t ssrc) override;
- void ResetUnsignaledRecvStream() override;
- absl::optional<uint32_t> GetUnsignaledSsrc() const override;
- void OnDemuxerCriteriaUpdatePending() override;
- void OnDemuxerCriteriaUpdateComplete() override;
- bool SetSink(uint32_t ssrc,
- rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override;
- void SetDefaultSink(
- rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override;
void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) override;
- bool GetSendStats(VideoMediaSendInfo* info) override;
- bool GetReceiveStats(VideoMediaReceiveInfo* info) override;
+ bool GetStats(VideoMediaSendInfo* info) override;
- void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override;
void OnPacketSent(const rtc::SentPacket& sent_packet) override;
void OnReadyToSend(bool ready) override;
void OnNetworkRouteChanged(absl::string_view transport_name,
const rtc::NetworkRoute& network_route) override;
- void SetInterface(MediaChannelNetworkInterface* iface) override;
- // E2E Encrypted Video Frame API
- // Set a frame decryptor to a particular ssrc that will intercept all
- // incoming video frames and attempt to decrypt them before forwarding the
- // result.
- void SetFrameDecryptor(uint32_t ssrc,
- rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
- frame_decryptor) override;
// Set a frame encryptor to a particular ssrc that will intercept all
// outgoing video frames and attempt to encrypt them and forward the result
// to the packetizer.
@@ -215,11 +225,6 @@
void SetVideoCodecSwitchingEnabled(bool enabled) override;
- bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
-
- absl::optional<int> GetBaseMinimumPlayoutDelayMs(
- uint32_t ssrc) const override;
-
void SetSendCodecChangedCallback(
absl::AnyInvocable<void()> callback) override {
send_codec_changed_callback_ = std::move(callback);
@@ -230,21 +235,12 @@
ssrc_list_changed_callback_ = std::move(callback);
}
- // Choose one of the available SSRCs (or default if none) as the current
- // receiver report SSRC.
- void ChooseReceiverReportSsrc(const std::set<uint32_t>& choices) override;
-
// Implemented for VideoMediaChannelTest.
bool sending() const {
RTC_DCHECK_RUN_ON(&thread_checker_);
return sending_;
}
- StreamParams unsignaled_stream_params() {
- RTC_DCHECK_RUN_ON(&thread_checker_);
- return unsignaled_stream_params_;
- }
-
// AdaptReason is used for expressing why a WebRtcVideoSendStream request
// a lower input frame size than the currently configured camera input frame
// size. There can be more than one reason OR:ed together.
@@ -256,19 +252,11 @@
static constexpr int kDefaultQpMax = 56;
- std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const override;
-
// Implements webrtc::EncoderSwitchRequestCallback.
void RequestEncoderFallback() override;
void RequestEncoderSwitch(const webrtc::SdpVideoFormat& format,
bool allow_default_fallback) override;
- void SetRecordableEncodedFrameCallback(
- uint32_t ssrc,
- std::function<void(const webrtc::RecordableEncodedFrame&)> callback)
- override;
- void ClearRecordableEncodedFrameCallback(uint32_t ssrc) override;
- void RequestRecvKeyFrame(uint32_t ssrc) override;
void GenerateSendKeyFrame(uint32_t ssrc,
const std::vector<std::string>& rids) override;
@@ -276,11 +264,6 @@
uint32_t ssrc,
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
override;
- void SetDepacketizerToDecoderFrameTransformer(
- uint32_t ssrc,
- rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
- override;
-
// Information queries to support SetReceiverFeedbackParameters
webrtc::RtcpMode SendCodecRtcpMode() const override {
RTC_DCHECK_RUN_ON(&thread_checker_);
@@ -309,34 +292,8 @@
}
return send_codec()->rtx_time;
}
- void SetReceiverFeedbackParameters(bool lntf_enabled,
- bool nack_enabled,
- webrtc::RtcpMode rtcp_mode,
- absl::optional<int> rtx_time) override;
private:
- class WebRtcVideoReceiveStream;
-
- struct VideoCodecSettings {
- VideoCodecSettings();
-
- // Checks if all members of |*this| are equal to the corresponding members
- // of `other`.
- bool operator==(const VideoCodecSettings& other) const;
- bool operator!=(const VideoCodecSettings& other) const;
-
- // Checks if all members of `a`, except `flexfec_payload_type`, are equal
- // to the corresponding members of `b`.
- static bool EqualsDisregardingFlexfec(const VideoCodecSettings& a,
- const VideoCodecSettings& b);
-
- VideoCodec codec;
- webrtc::UlpfecConfig ulpfec;
- int flexfec_payload_type; // -1 if absent.
- int rtx_payload_type; // -1 if absent.
- absl::optional<int> rtx_time;
- };
-
struct ChangedSendParameters {
// These optionals are unset if not changed.
absl::optional<VideoCodecSettings> send_codec;
@@ -349,57 +306,12 @@
absl::optional<webrtc::RtcpMode> rtcp_mode;
};
- struct ChangedRecvParameters {
- // These optionals are unset if not changed.
- absl::optional<std::vector<VideoCodecSettings>> codec_settings;
- absl::optional<std::vector<webrtc::RtpExtension>> rtp_header_extensions;
- // Keep track of the FlexFEC payload type separately from `codec_settings`.
- // This allows us to recreate the FlexfecReceiveStream separately from the
- // VideoReceiveStreamInterface when the FlexFEC payload type is changed.
- absl::optional<int> flexfec_payload_type;
- };
-
- // Finds VideoReceiveStreamInterface corresponding to ssrc. Aware of
- // unsignalled ssrc handling.
- WebRtcVideoReceiveStream* FindReceiveStream(uint32_t ssrc)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
-
- void ProcessReceivedPacket(webrtc::RtpPacketReceived packet)
- RTC_RUN_ON(thread_checker_);
-
bool GetChangedSendParameters(const VideoSendParameters& params,
ChangedSendParameters* changed_params) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
bool ApplyChangedParams(const ChangedSendParameters& changed_params);
- bool GetChangedRecvParameters(const VideoRecvParameters& params,
- ChangedRecvParameters* changed_params) const
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
-
- // Expected to be invoked once per packet that belongs to this channel that
- // can not be demuxed.
- // Returns true if a new default stream has been created.
- bool MaybeCreateDefaultReceiveStream(
- const webrtc::RtpPacketReceived& parsed_packet)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
- void ReCreateDefaultReceiveStream(uint32_t ssrc,
- absl::optional<uint32_t> rtx_ssrc);
- // Add a receive stream. Used for testing.
- bool AddRecvStream(const StreamParams& sp, bool default_stream);
-
- void ConfigureReceiverRtp(
- webrtc::VideoReceiveStreamInterface::Config* config,
- webrtc::FlexfecReceiveStream::Config* flexfec_config,
- const StreamParams& sp) const
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
bool ValidateSendSsrcAvailability(const StreamParams& sp) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
- bool ValidateReceiveSsrcAvailability(const StreamParams& sp) const
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
- void DeleteReceiveStream(WebRtcVideoReceiveStream* stream)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
-
- static std::string CodecSettingsVectorToString(
- const std::vector<VideoCodecSettings>& codecs);
// Populates `rtx_associated_payload_types`, `raw_payload_types` and
// `decoders` based on codec settings provided by `recv_codecs`.
@@ -410,10 +322,6 @@
std::set<int>& raw_payload_types,
std::vector<webrtc::VideoReceiveStreamInterface::Decoder>& decoders);
- // Called when the local ssrc changes. Sets `rtcp_receiver_report_ssrc_` and
- // updates the receive streams.
- void SetReceiverReportSsrc(uint32_t ssrc) RTC_RUN_ON(&thread_checker_);
-
// Wrapper for the sender part.
class WebRtcVideoSendStream {
public:
@@ -529,6 +437,277 @@
const bool disable_automatic_resize_;
};
+ void Construct(webrtc::Call* call, WebRtcVideoEngine* engine);
+
+ bool SendRtp(const uint8_t* data,
+ size_t len,
+ const webrtc::PacketOptions& options) override;
+ bool SendRtcp(const uint8_t* data, size_t len) override;
+
+ // Get all codecs that are compatible with the receiver.
+ std::vector<VideoCodecSettings> SelectSendVideoCodecs(
+ const std::vector<VideoCodecSettings>& remote_mapped_codecs) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+
+ void FillSenderStats(VideoMediaSendInfo* info, bool log_stats)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+ void FillBandwidthEstimationStats(const webrtc::Call::Stats& stats,
+ VideoMediaInfo* info)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+ void FillSendCodecStats(VideoMediaSendInfo* video_media_info)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+
+ // Accessor function for send_codec_. Introduced in order to ensure
+ // that a receive channel does not touch the send codec directly.
+ // Can go away once these are different classes.
+ // TODO(bugs.webrtc.org/13931): Remove this function
+ absl::optional<VideoCodecSettings>& send_codec() { return send_codec_; }
+ const absl::optional<VideoCodecSettings>& send_codec() const {
+ return send_codec_;
+ }
+ // Disabled function from interface
+ MediaChannel* ImplForTesting() override {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+ webrtc::TaskQueueBase* const worker_thread_;
+ webrtc::ScopedTaskSafety task_safety_;
+ RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker network_thread_checker_{
+ webrtc::SequenceChecker::kDetached};
+ RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker thread_checker_;
+
+ uint32_t rtcp_receiver_report_ssrc_ RTC_GUARDED_BY(thread_checker_);
+ bool sending_ RTC_GUARDED_BY(thread_checker_);
+ bool receiving_ RTC_GUARDED_BY(&thread_checker_);
+ webrtc::Call* const call_;
+
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* default_sink_
+ RTC_GUARDED_BY(thread_checker_);
+
+ // Delay for unsignaled streams, which may be set before the stream exists.
+ int default_recv_base_minimum_delay_ms_ RTC_GUARDED_BY(thread_checker_) = 0;
+
+ const MediaConfig::Video video_config_ RTC_GUARDED_BY(thread_checker_);
+
+ // Using primary-ssrc (first ssrc) as key.
+ std::map<uint32_t, WebRtcVideoSendStream*> send_streams_
+ RTC_GUARDED_BY(thread_checker_);
+ // When the channel and demuxer get reconfigured, there is a window of time
+ // where we have to be prepared for packets arriving based on the old demuxer
+ // criteria because the streams live on the worker thread and the demuxer
+ // lives on the network thread. Because packets are posted from the network
+ // thread to the worker thread, they can still be in-flight when streams are
+ // reconfgured. This can happen when `demuxer_criteria_id_` and
+ // `demuxer_criteria_completed_id_` don't match. During this time, we do not
+ // want to create unsignalled receive streams and should instead drop the
+ // packets. E.g:
+ // * If RemoveRecvStream(old_ssrc) was recently called, there may be packets
+ // in-flight for that ssrc. This happens when a receiver becomes inactive.
+ // * If we go from one to many m= sections, the demuxer may change from
+ // forwarding all packets to only forwarding the configured ssrcs, so there
+ // is a risk of receiving ssrcs for other, recently added m= sections.
+ uint32_t demuxer_criteria_id_ RTC_GUARDED_BY(thread_checker_) = 0;
+ uint32_t demuxer_criteria_completed_id_ RTC_GUARDED_BY(thread_checker_) = 0;
+ absl::optional<int64_t> last_unsignalled_ssrc_creation_time_ms_
+ RTC_GUARDED_BY(thread_checker_);
+ std::set<uint32_t> send_ssrcs_ RTC_GUARDED_BY(thread_checker_);
+ std::set<uint32_t> receive_ssrcs_ RTC_GUARDED_BY(thread_checker_);
+
+ absl::optional<VideoCodecSettings> send_codec_
+ RTC_GUARDED_BY(thread_checker_);
+ std::vector<VideoCodecSettings> negotiated_codecs_
+ RTC_GUARDED_BY(thread_checker_);
+
+ std::vector<webrtc::RtpExtension> send_rtp_extensions_
+ RTC_GUARDED_BY(thread_checker_);
+
+ webrtc::VideoEncoderFactory* const encoder_factory_
+ RTC_GUARDED_BY(thread_checker_);
+ webrtc::VideoDecoderFactory* const decoder_factory_
+ RTC_GUARDED_BY(thread_checker_);
+ webrtc::VideoBitrateAllocatorFactory* const bitrate_allocator_factory_
+ RTC_GUARDED_BY(thread_checker_);
+ std::vector<VideoCodecSettings> recv_codecs_ RTC_GUARDED_BY(thread_checker_);
+ webrtc::RtpHeaderExtensionMap recv_rtp_extension_map_
+ RTC_GUARDED_BY(thread_checker_);
+ std::vector<webrtc::RtpExtension> recv_rtp_extensions_
+ RTC_GUARDED_BY(thread_checker_);
+ // See reason for keeping track of the FlexFEC payload type separately in
+ // comment in WebRtcVideoChannel::ChangedRecvParameters.
+ int recv_flexfec_payload_type_ RTC_GUARDED_BY(thread_checker_);
+ webrtc::BitrateConstraints bitrate_config_ RTC_GUARDED_BY(thread_checker_);
+ // TODO(deadbeef): Don't duplicate information between
+ // send_params/recv_params, rtp_extensions, options, etc.
+ VideoSendParameters send_params_ RTC_GUARDED_BY(thread_checker_);
+ VideoOptions default_send_options_ RTC_GUARDED_BY(thread_checker_);
+ VideoRecvParameters recv_params_ RTC_GUARDED_BY(thread_checker_);
+ int64_t last_send_stats_log_ms_ RTC_GUARDED_BY(thread_checker_);
+ int64_t last_receive_stats_log_ms_ RTC_GUARDED_BY(thread_checker_);
+ const bool discard_unknown_ssrc_packets_ RTC_GUARDED_BY(thread_checker_);
+ // This is a stream param that comes from the remote description, but wasn't
+ // signaled with any a=ssrc lines. It holds information that was signaled
+ // before the unsignaled receive stream is created when the first packet is
+ // received.
+ StreamParams unsignaled_stream_params_ RTC_GUARDED_BY(thread_checker_);
+ // Per peer connection crypto options that last for the lifetime of the peer
+ // connection.
+ const webrtc::CryptoOptions crypto_options_ RTC_GUARDED_BY(thread_checker_);
+
+ // Optional frame transformer set on unsignaled streams.
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface>
+ unsignaled_frame_transformer_ RTC_GUARDED_BY(thread_checker_);
+
+ // TODO(bugs.webrtc.org/11341): Remove this and relevant PC API. Presence
+ // of multiple negotiated codecs allows generic encoder fallback on failures.
+ // Presence of EncoderSelector allows switching to specific encoders.
+ bool allow_codec_switching_ = false;
+
+ // RTP parameters that need to be set when creating a video receive stream.
+ // Only used in Receiver mode - in Both mode, it reads those things from the
+ // codec.
+ webrtc::VideoReceiveStreamInterface::Config::Rtp rtp_config_;
+
+ // Callback invoked whenever the send codec changes.
+ // TODO(bugs.webrtc.org/13931): Remove again when coupling isn't needed.
+ absl::AnyInvocable<void()> send_codec_changed_callback_;
+ // Callback invoked whenever the list of SSRCs changes.
+ absl::AnyInvocable<void(const std::set<uint32_t>&)>
+ ssrc_list_changed_callback_;
+};
+
+class WebRtcVideoReceiveChannel : public MediaChannelUtil,
+ public VideoMediaReceiveChannelInterface,
+ public webrtc::Transport {
+ public:
+ WebRtcVideoReceiveChannel(webrtc::Call* call,
+ const MediaConfig& config,
+ const VideoOptions& options,
+ const webrtc::CryptoOptions& crypto_options,
+ webrtc::VideoDecoderFactory* decoder_factory);
+ ~WebRtcVideoReceiveChannel() override;
+
+ public:
+ MediaType media_type() const override { return MEDIA_TYPE_VIDEO; }
+ VideoMediaReceiveChannelInterface* AsVideoReceiveChannel() override {
+ return this;
+ }
+ VoiceMediaReceiveChannelInterface* AsVoiceReceiveChannel() override {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+ // Functions imported from MediaChannelUtil
+ bool SendRtp(const uint8_t* data,
+ size_t len,
+ const webrtc::PacketOptions& options) override {
+ MediaChannelUtil::SendRtp(data, len, options);
+ return true;
+ }
+ bool SendRtcp(const uint8_t* data, size_t len) override {
+ MediaChannelUtil::SendRtcp(data, len);
+ return true;
+ }
+
+ // Common functions between sender and receiver
+ void SetInterface(MediaChannelNetworkInterface* iface) override;
+ // VideoMediaReceiveChannelInterface implementation
+ bool SetRecvParameters(const VideoRecvParameters& params) override;
+ webrtc::RtpParameters GetRtpReceiveParameters(uint32_t ssrc) const override;
+ webrtc::RtpParameters GetDefaultRtpReceiveParameters() const override;
+ void SetReceive(bool receive) override;
+ bool AddRecvStream(const StreamParams& sp) override;
+ bool AddDefaultRecvStreamForTesting(const StreamParams& sp) override {
+ // Invokes private AddRecvStream variant function
+ return AddRecvStream(sp, true);
+ }
+ bool RemoveRecvStream(uint32_t ssrc) override;
+ void ResetUnsignaledRecvStream() override;
+ absl::optional<uint32_t> GetUnsignaledSsrc() const override;
+ void OnDemuxerCriteriaUpdatePending() override;
+ void OnDemuxerCriteriaUpdateComplete() override;
+ bool SetSink(uint32_t ssrc,
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override;
+ void SetDefaultSink(
+ rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) override;
+ bool GetStats(VideoMediaReceiveInfo* info) override;
+ void OnPacketReceived(const webrtc::RtpPacketReceived& packet) override;
+ bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override;
+
+ absl::optional<int> GetBaseMinimumPlayoutDelayMs(
+ uint32_t ssrc) const override;
+
+ // Choose one of the available SSRCs (or default if none) as the current
+ // receiver report SSRC.
+ void ChooseReceiverReportSsrc(const std::set<uint32_t>& choices) override;
+
+ // E2E Encrypted Video Frame API
+ // Set a frame decryptor to a particular ssrc that will intercept all
+ // incoming video frames and attempt to decrypt them before forwarding the
+ // result.
+ void SetFrameDecryptor(uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
+ frame_decryptor) override;
+ void SetRecordableEncodedFrameCallback(
+ uint32_t ssrc,
+ std::function<void(const webrtc::RecordableEncodedFrame&)> callback)
+ override;
+ void ClearRecordableEncodedFrameCallback(uint32_t ssrc) override;
+ void RequestRecvKeyFrame(uint32_t ssrc) override;
+ void SetDepacketizerToDecoderFrameTransformer(
+ uint32_t ssrc,
+ rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
+ override;
+ std::vector<webrtc::RtpSource> GetSources(uint32_t ssrc) const override;
+
+ void SetReceiverFeedbackParameters(bool lntf_enabled,
+ bool nack_enabled,
+ webrtc::RtcpMode rtcp_mode,
+ absl::optional<int> rtx_time) override;
+
+ private:
+ class WebRtcVideoReceiveStream;
+ struct ChangedRecvParameters {
+ // These optionals are unset if not changed.
+ absl::optional<std::vector<VideoCodecSettings>> codec_settings;
+ absl::optional<std::vector<webrtc::RtpExtension>> rtp_header_extensions;
+ // Keep track of the FlexFEC payload type separately from `codec_settings`.
+ // This allows us to recreate the FlexfecReceiveStream separately from the
+ // VideoReceiveStreamInterface when the FlexFEC payload type is changed.
+ absl::optional<int> flexfec_payload_type;
+ };
+
+ // Finds VideoReceiveStreamInterface corresponding to ssrc. Aware of
+ // unsignalled ssrc handling.
+ WebRtcVideoReceiveStream* FindReceiveStream(uint32_t ssrc)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+
+ void ProcessReceivedPacket(webrtc::RtpPacketReceived packet)
+ RTC_RUN_ON(thread_checker_);
+
+ // Expected to be invoked once per packet that belongs to this channel that
+ // can not be demuxed.
+ // Returns true if a new default stream has been created.
+ bool MaybeCreateDefaultReceiveStream(
+ const webrtc::RtpPacketReceived& parsed_packet)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+ void ReCreateDefaultReceiveStream(uint32_t ssrc,
+ absl::optional<uint32_t> rtx_ssrc);
+ // Add a receive stream. Used for testing.
+ bool AddRecvStream(const StreamParams& sp, bool default_stream);
+
+ void ConfigureReceiverRtp(
+ webrtc::VideoReceiveStreamInterface::Config* config,
+ webrtc::FlexfecReceiveStream::Config* flexfec_config,
+ const StreamParams& sp) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+ bool ValidateReceiveSsrcAvailability(const StreamParams& sp) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+ void DeleteReceiveStream(WebRtcVideoReceiveStream* stream)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+
+ // Called when the local ssrc changes. Sets `rtcp_receiver_report_ssrc_` and
+ // updates the receive streams.
+ void SetReceiverReportSsrc(uint32_t ssrc) RTC_RUN_ON(&thread_checker_);
+
// Wrapper for the receiver part, contains configs etc. that are needed to
// reconstruct the underlying VideoReceiveStreamInterface.
class WebRtcVideoReceiveStream
@@ -627,56 +806,28 @@
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker thread_checker_;
bool receiving_ RTC_GUARDED_BY(&thread_checker_);
};
-
- void Construct(webrtc::Call* call, WebRtcVideoEngine* engine);
-
- bool SendRtp(const uint8_t* data,
- size_t len,
- const webrtc::PacketOptions& options) override;
- bool SendRtcp(const uint8_t* data, size_t len) override;
-
- // Generate the list of codec parameters to pass down based on the negotiated
- // "codecs". Note that VideoCodecSettings correspond to concrete codecs like
- // VP8, VP9, H264 while VideoCodecs correspond also to "virtual" codecs like
- // RTX, ULPFEC, FLEXFEC.
- static std::vector<VideoCodecSettings> MapCodecs(
- const std::vector<VideoCodec>& codecs);
- // Get all codecs that are compatible with the receiver.
- std::vector<VideoCodecSettings> SelectSendVideoCodecs(
- const std::vector<VideoCodecSettings>& remote_mapped_codecs) const
+ bool GetChangedRecvParameters(const VideoRecvParameters& params,
+ ChangedRecvParameters* changed_params) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
- static bool NonFlexfecReceiveCodecsHaveChanged(
- std::vector<VideoCodecSettings> before,
- std::vector<VideoCodecSettings> after);
-
- void FillSenderStats(VideoMediaSendInfo* info, bool log_stats)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
+ std::map<uint32_t, WebRtcVideoReceiveStream*> receive_streams_
+ RTC_GUARDED_BY(thread_checker_);
void FillReceiverStats(VideoMediaReceiveInfo* info, bool log_stats)
RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
- void FillBandwidthEstimationStats(const webrtc::Call::Stats& stats,
- VideoMediaInfo* info)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
- void FillSendCodecStats(VideoMediaSendInfo* video_media_info)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
void FillReceiveCodecStats(VideoMediaReceiveInfo* video_media_info)
RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_);
- // Accessor function for send_codec_. Introduced in order to ensure
- // that a receive channel does not touch the send codec directly.
- // Can go away once these are different classes.
- // TODO(bugs.webrtc.org/13931): Remove this function
- absl::optional<VideoCodecSettings>& send_codec() {
- RTC_DCHECK(role() == MediaChannel::Role::kSend ||
- role() == MediaChannel::Role::kBoth);
- return send_codec_;
- }
- const absl::optional<VideoCodecSettings>& send_codec() const {
- RTC_DCHECK(role() == MediaChannel::Role::kSend ||
- role() == MediaChannel::Role::kBoth);
- return send_codec_;
+ StreamParams unsignaled_stream_params() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+ return unsignaled_stream_params_;
}
+ // Disabled function from API
+ MediaChannel* ImplForTesting() override {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+ }
+ // Variables.
webrtc::TaskQueueBase* const worker_thread_;
webrtc::ScopedTaskSafety task_safety_;
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker network_thread_checker_{
@@ -684,7 +835,6 @@
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker thread_checker_;
uint32_t rtcp_receiver_report_ssrc_ RTC_GUARDED_BY(thread_checker_);
- bool sending_ RTC_GUARDED_BY(thread_checker_);
bool receiving_ RTC_GUARDED_BY(&thread_checker_);
webrtc::Call* const call_;
@@ -696,11 +846,6 @@
const MediaConfig::Video video_config_ RTC_GUARDED_BY(thread_checker_);
- // Using primary-ssrc (first ssrc) as key.
- std::map<uint32_t, WebRtcVideoSendStream*> send_streams_
- RTC_GUARDED_BY(thread_checker_);
- std::map<uint32_t, WebRtcVideoReceiveStream*> receive_streams_
- RTC_GUARDED_BY(thread_checker_);
// When the channel and demuxer get reconfigured, there is a window of time
// where we have to be prepared for packets arriving based on the old demuxer
// criteria because the streams live on the worker thread and the demuxer
@@ -730,12 +875,8 @@
std::vector<webrtc::RtpExtension> send_rtp_extensions_
RTC_GUARDED_BY(thread_checker_);
- webrtc::VideoEncoderFactory* const encoder_factory_
- RTC_GUARDED_BY(thread_checker_);
webrtc::VideoDecoderFactory* const decoder_factory_
RTC_GUARDED_BY(thread_checker_);
- webrtc::VideoBitrateAllocatorFactory* const bitrate_allocator_factory_
- RTC_GUARDED_BY(thread_checker_);
std::vector<VideoCodecSettings> recv_codecs_ RTC_GUARDED_BY(thread_checker_);
webrtc::RtpHeaderExtensionMap recv_rtp_extension_map_
RTC_GUARDED_BY(thread_checker_);
@@ -750,7 +891,6 @@
VideoSendParameters send_params_ RTC_GUARDED_BY(thread_checker_);
VideoOptions default_send_options_ RTC_GUARDED_BY(thread_checker_);
VideoRecvParameters recv_params_ RTC_GUARDED_BY(thread_checker_);
- int64_t last_send_stats_log_ms_ RTC_GUARDED_BY(thread_checker_);
int64_t last_receive_stats_log_ms_ RTC_GUARDED_BY(thread_checker_);
const bool discard_unknown_ssrc_packets_ RTC_GUARDED_BY(thread_checker_);
// This is a stream param that comes from the remote description, but wasn't
@@ -766,11 +906,6 @@
rtc::scoped_refptr<webrtc::FrameTransformerInterface>
unsignaled_frame_transformer_ RTC_GUARDED_BY(thread_checker_);
- // TODO(bugs.webrtc.org/11341): Remove this and relevant PC API. Presence
- // of multiple negotiated codecs allows generic encoder fallback on failures.
- // Presence of EncoderSelector allows switching to specific encoders.
- bool allow_codec_switching_ = false;
-
// RTP parameters that need to be set when creating a video receive stream.
// Only used in Receiver mode - in Both mode, it reads those things from the
// codec.
@@ -784,6 +919,17 @@
ssrc_list_changed_callback_;
};
+// Keeping the old name "WebRtcVideoChannel" around because some external
+// customers are using cricket::WebRtcVideoChannel::AdaptReason
+// TODO(bugs.webrtc.org/15216): Move this enum to an interface class and
+// delete this workaround.
+class WebRtcVideoChannel : public WebRtcVideoSendChannel {
+ public:
+ // Make all the values of AdaptReason available as
+ // WebRtcVideoChannel::ADAPT_xxx.
+ using WebRtcVideoSendChannel::AdaptReason;
+};
+
} // namespace cricket
#endif // MEDIA_ENGINE_WEBRTC_VIDEO_ENGINE_H_
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index 66535ca..3be1dc5 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -1820,10 +1820,10 @@
}
// Returns pointer to implementation of the send channel.
- WebRtcVideoChannel* SendImpl() {
+ WebRtcVideoSendChannel* SendImpl() {
// Note that this function requires intimate knowledge of how the channel
// was created.
- return static_cast<cricket::WebRtcVideoChannel*>(
+ return static_cast<cricket::WebRtcVideoSendChannel*>(
static_cast<VideoMediaShimChannel*>(channel_.get())
->SendImplForTesting());
}
@@ -2685,10 +2685,10 @@
}
// Returns pointer to implementation of the send channel.
- WebRtcVideoChannel* SendImpl() {
+ WebRtcVideoSendChannel* SendImpl() {
// Note that this function requires intimate knowledge of how the channel
// was created.
- return static_cast<cricket::WebRtcVideoChannel*>(
+ return static_cast<cricket::WebRtcVideoSendChannel*>(
static_cast<VideoMediaShimChannel*>(channel_.get())
->SendImplForTesting());
}
@@ -2696,7 +2696,7 @@
// Casts a shim channel to a webrtc::Transport. Used once.
webrtc::Transport* ChannelImplAsTransport(VideoMediaChannel* channel) {
return static_cast<webrtc::Transport*>(
- static_cast<cricket::WebRtcVideoChannel*>(
+ static_cast<cricket::WebRtcVideoSendChannel*>(
static_cast<VideoMediaShimChannel*>(channel)
->SendImplForTesting()));
}
diff --git a/test/scenario/video_stream.cc b/test/scenario/video_stream.cc
index 937ad4f..ec13976 100644
--- a/test/scenario/video_stream.cc
+++ b/test/scenario/video_stream.cc
@@ -40,7 +40,7 @@
kVideoRotationRtpExtensionId,
};
-constexpr int kDefaultMaxQp = cricket::WebRtcVideoChannel::kDefaultQpMax;
+constexpr int kDefaultMaxQp = cricket::WebRtcVideoSendChannel::kDefaultQpMax;
uint8_t CodecTypeToPayloadType(VideoCodecType codec_type) {
switch (codec_type) {
case VideoCodecType::kVideoCodecGeneric:
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index 2dd80ea..010e2ed 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -76,7 +76,7 @@
constexpr uint32_t kThumbnailSendSsrcStart = 0xE0000;
constexpr uint32_t kThumbnailRtxSsrcStart = 0xF0000;
-constexpr int kDefaultMaxQp = cricket::WebRtcVideoChannel::kDefaultQpMax;
+constexpr int kDefaultMaxQp = cricket::WebRtcVideoSendChannel::kDefaultQpMax;
const VideoEncoder::Capabilities kCapabilities(false);