/* * Copyright 2004 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "pc/channel.h" #include #include #include #include #include #include #include #include "absl/algorithm/container.h" #include "absl/base/nullability.h" #include "absl/cleanup/cleanup.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "api/crypto/crypto_options.h" #include "api/jsep.h" #include "api/media_types.h" #include "api/rtc_error.h" #include "api/rtp_headers.h" #include "api/rtp_parameters.h" #include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "call/rtp_demuxer.h" #include "media/base/codec.h" #include "media/base/media_channel.h" #include "media/base/rid_description.h" #include "media/base/rtp_utils.h" #include "media/base/stream_params.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "p2p/dtls/dtls_transport_internal.h" #include "pc/channel_interface.h" #include "pc/rtp_media_utils.h" #include "pc/rtp_transport_internal.h" #include "pc/session_description.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/checks.h" #include "rtc_base/containers/flat_set.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/logging.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/socket.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/thread.h" #include "rtc_base/trace_event.h" #include "rtc_base/unique_id_generator.h" namespace webrtc { namespace { // Finds a stream based on target's Primary SSRC or RIDs. // This struct is used in BaseChannel::UpdateLocalStreams_w. struct StreamFinder { explicit StreamFinder(const StreamParams* target) : target_(target) { RTC_DCHECK(target); } bool operator()(const StreamParams& sp) const { if (target_->has_ssrcs() && sp.has_ssrcs()) { return sp.has_ssrc(target_->first_ssrc()); } if (!target_->has_rids() && !sp.has_rids()) { return false; } const std::vector& target_rids = target_->rids(); const std::vector& source_rids = sp.rids(); if (source_rids.size() != target_rids.size()) { return false; } // Check that all RIDs match. return std::equal(source_rids.begin(), source_rids.end(), target_rids.begin(), [](const RidDescription& lhs, const RidDescription& rhs) { return lhs.rid == rhs.rid; }); } const StreamParams* target_; }; void MediaChannelParametersFromMediaDescription( const MediaContentDescription* desc, const RtpHeaderExtensions& extensions, bool is_stream_active, MediaChannelParameters* params) { RTC_DCHECK(desc->type() == MediaType::AUDIO || desc->type() == MediaType::VIDEO); params->is_stream_active = is_stream_active; params->codecs = desc->codecs(); params->extensions = extensions; params->rtcp.reduced_size = desc->rtcp_reduced_size(); params->rtcp.remote_estimate = desc->remote_estimate(); params->rtcp_cc_ack_type = desc->preferred_rtcp_cc_ack_type(); } void RtpSendParametersFromMediaDescription( const MediaContentDescription* desc, RtpExtension::Filter extensions_filter, SenderParameters* send_params) { RtpHeaderExtensions extensions = RtpExtension::DeduplicateHeaderExtensions( desc->rtp_header_extensions(), extensions_filter); const bool is_stream_active = RtpTransceiverDirectionHasRecv(desc->direction()); MediaChannelParametersFromMediaDescription(desc, extensions, is_stream_active, send_params); send_params->max_bandwidth_bps = desc->bandwidth(); send_params->extmap_allow_mixed = desc->extmap_allow_mixed(); } // Ensure that there is a matching packetization for each codec in // new_params. If old_params had a special packetization but the // response in new_params has no special packetization we amend // old_params by ignoring the packetization and fall back to standard // packetization instead. RTCError MaybeIgnorePacketization(const MediaChannelParameters& new_params, MediaChannelParameters& old_params) { flat_set matched_codecs; for (Codec& codec : old_params.codecs) { if (absl::c_any_of(matched_codecs, [&](const Codec* c) { return codec.Matches(*c); })) { continue; } std::vector new_codecs = FindAllMatchingCodecs(new_params.codecs, codec); if (new_codecs.empty()) { continue; } bool may_ignore_packetization = false; bool has_matching_packetization = false; for (const Codec* new_codec : new_codecs) { if (!new_codec->packetization.has_value() && codec.packetization.has_value()) { may_ignore_packetization = true; } else if (new_codec->packetization == codec.packetization) { has_matching_packetization = true; break; } } if (may_ignore_packetization) { // Note: this writes into old_params codec.packetization = std::nullopt; } else if (!has_matching_packetization) { return RTCError(RTCErrorType::INTERNAL_ERROR) << "Failed to set local answer due to incompatible codec " << "packetization for pt='" << codec.id << "' specified."; } if (has_matching_packetization) { matched_codecs.insert(&codec); } } return RTCError::OK(); } } // namespace BaseChannel::BaseChannel( TaskQueueBase* absl_nonnull worker_thread, Thread* absl_nonnull network_thread, TaskQueueBase* absl_nonnull signaling_thread, std::unique_ptr send_media_channel_impl, std::unique_ptr receive_media_channel_impl, absl::string_view mid, MediaType media_type, bool srtp_required, CryptoOptions crypto_options, UniqueRandomIdGenerator* absl_nonnull ssrc_generator, ChannelCallbacks callbacks) : media_send_channel_(std::move(send_media_channel_impl)), media_receive_channel_(std::move(receive_media_channel_impl)), worker_thread_(worker_thread), network_thread_(network_thread), signaling_thread_(signaling_thread), alive_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue(true, worker_thread)), on_first_packet_sent_(std::move(callbacks.on_first_packet_sent)), on_packet_received_n_(std::move(callbacks.on_packet_received)), srtp_required_(srtp_required), extensions_filter_( crypto_options.srtp.enable_encrypted_rtp_header_extensions ? RtpExtension::kPreferEncryptedExtension : RtpExtension::kDiscardEncryptedExtension), mid_(std::string(mid)), last_recv_params_(media_type == MediaType::VIDEO ? ReceiverParamsVariant(VideoReceiverParameters()) : ReceiverParamsVariant(AudioReceiverParameters())), last_send_params_(media_type == MediaType::VIDEO ? SenderParamsVariant(VideoSenderParameters()) : SenderParamsVariant(AudioSenderParameter())), media_type_(media_type), ssrc_generator_(ssrc_generator) { RTC_DCHECK(media_send_channel_); RTC_DCHECK(media_receive_channel_); RTC_DCHECK(ssrc_generator_); RTC_DLOG(LS_INFO) << "Created channel: " << ToString(); } BaseChannel::~BaseChannel() { TRACE_EVENT0("webrtc", "BaseChannel::~BaseChannel"); RTC_DCHECK_RUN_ON(worker_thread_); DisableMedia_w(); // Eats any outstanding messages or packets. alive_->SetNotAlive(); // The channel must be disconnected from the transport before destruction. RTC_DCHECK(rtp_transport_ == nullptr); } std::string BaseChannel::ToString() const { return (StringBuilder() << "{mid: " << mid() << ", media_type: " << media_send_channel_->media_type() << "}") .Release(); } bool BaseChannel::ConnectToRtpTransport_n(RtpTransportInternal* rtp_transport) { RTC_DCHECK(!rtp_transport_); RTC_DCHECK(rtp_transport); RTC_DCHECK(media_send_channel()); // We don't need to call OnDemuxerCriteriaUpdatePending/Complete because // there's no previous criteria to worry about. RtpDemuxerCriteria criteria = demuxer_criteria(); if (!rtp_transport->RegisterRtpDemuxerSink(criteria, this)) { return false; } rtp_transport_ = rtp_transport; rtp_transport_->SubscribeReadyToSend( this, [this](bool ready) { OnTransportReadyToSend(ready); }); rtp_transport_->SubscribeWritableState( this, [this](bool state) { OnWritableState(state); }); rtp_transport_->SubscribeSentPacket( this, [this](const SentPacketInfo& packet) { SignalSentPacket_n(packet); }); return true; } void BaseChannel::DisconnectFromRtpTransport_n(bool permanent_teardown) { RTC_DCHECK(rtp_transport_); RTC_DCHECK(media_send_channel()); rtp_transport_->UnregisterRtpDemuxerSink(this); rtp_transport_->UnsubscribeReadyToSend(this); rtp_transport_->UnsubscribeWritableState(this); rtp_transport_->UnsubscribeSentPacket(this); rtp_transport_ = nullptr; media_send_channel()->SetInterface(nullptr); if (permanent_teardown) { media_receive_channel()->ClearReceiveSinks_n(std::nullopt); } media_receive_channel()->SetInterface(nullptr); } bool BaseChannel::SetRtpTransport(RtpTransportInternal* rtp_transport) { TRACE_EVENT0("webrtc", "BaseChannel::SetRtpTransport"); RTC_DCHECK_RUN_ON(network_thread()); if (rtp_transport == rtp_transport_) { return true; } if (rtp_transport_) { DisconnectFromRtpTransport_n(rtp_transport == nullptr); } RTC_DCHECK(!rtp_transport_); if (!rtp_transport) { return true; // We're done. } if (!ConnectToRtpTransport_n(rtp_transport)) { RTC_DCHECK(!rtp_transport_); return false; } RTC_DCHECK_EQ(rtp_transport_, rtp_transport); RTC_DCHECK(!media_send_channel()->HasNetworkInterface()); media_send_channel()->SetInterface(this); media_receive_channel()->SetInterface(this); media_send_channel()->OnReadyToSend(rtp_transport_->IsReadyToSend()); UpdateWritableState_n(); // Set the cached socket options. for (const auto& pair : socket_options_) { rtp_transport_->SetRtpOption(pair.first, pair.second); } if (!rtp_transport_->rtcp_mux_enabled()) { for (const auto& pair : rtcp_socket_options_) { rtp_transport_->SetRtcpOption(pair.first, pair.second); } } return true; } void BaseChannel::Enable(bool enable) { RTC_DCHECK_RUN_ON(signaling_thread()); if (enable == enabled_s_) return; enabled_s_ = enable; worker_thread_->PostTask(SafeTask(alive_, [this, enable] { RTC_DCHECK_RUN_ON(worker_thread()); // Sanity check to make sure that enabled_ and enabled_s_ // stay in sync. RTC_DCHECK_NE(enabled_, enable); if (enable) { EnableMedia_w(); } else { DisableMedia_w(); } })); } void BaseChannel::EnableSframe() { RTC_DCHECK_RUN_ON(signaling_thread()); worker_thread_->PostTask(SafeTask(alive_, [this] { RTC_DCHECK_RUN_ON(worker_thread()); media_send_channel()->EnableSframe(); media_receive_channel()->EnableSframe(); })); } RTCError BaseChannel::SetLocalContent(const MediaContentDescription* content, SdpType type) { RTC_DCHECK_RUN_ON(worker_thread()); TRACE_EVENT0("webrtc", "BaseChannel::SetLocalContent"); return SetLocalContent_w(content, type); } RTCError BaseChannel::SetRemoteContent(const MediaContentDescription* content, SdpType type) { RTC_DCHECK_RUN_ON(worker_thread()); TRACE_EVENT0("webrtc", "BaseChannel::SetRemoteContent"); return SetRemoteContent_w(content, type); } bool BaseChannel::IsReadyToSendMedia_w() const { // Send outgoing data if we are enabled, have local and remote content, // and we have had some form of connectivity. return enabled_ && RtpTransceiverDirectionHasRecv(remote_content_direction_) && RtpTransceiverDirectionHasSend(local_content_direction_) && was_ever_writable_; } bool BaseChannel::SendPacket(CopyOnWriteBuffer* packet, const AsyncSocketPacketOptions& options) { return SendPacket(false, packet, options); } bool BaseChannel::SendRtcp(CopyOnWriteBuffer* packet, const AsyncSocketPacketOptions& options) { return SendPacket(true, packet, options); } int BaseChannel::SetOption(SocketType type, Socket::Option opt, int value) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(network_initialized()); RTC_DCHECK(rtp_transport_); switch (type) { case ST_RTP: socket_options_.push_back(std::pair(opt, value)); return rtp_transport_->SetRtpOption(opt, value); case ST_RTCP: rtcp_socket_options_.push_back( std::pair(opt, value)); return rtp_transport_->SetRtcpOption(opt, value); } return -1; } void BaseChannel::OnWritableState(bool writable) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(network_initialized()); if (writable) { ChannelWritable_n(); } else { ChannelNotWritable_n(); } } void BaseChannel::OnTransportReadyToSend(bool ready) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(network_initialized()); media_send_channel()->OnReadyToSend(ready); } bool BaseChannel::SendPacket(bool rtcp, CopyOnWriteBuffer* packet, const AsyncSocketPacketOptions& options) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(network_initialized()); TRACE_EVENT0("webrtc", "BaseChannel::SendPacket"); // Until all the code is migrated to use RtpPacketType instead of bool. RtpPacketType packet_type = rtcp ? RtpPacketType::kRtcp : RtpPacketType::kRtp; // Ensure we have a place to send this packet before doing anything. We might // get RTCP packets that we don't intend to send. If we've negotiated RTCP // mux, send RTCP over the RTP transport. if (!rtp_transport_ || !rtp_transport_->IsWritable(rtcp)) { return false; } // Protect ourselves against crazy data. if (!IsValidRtpPacketSize(packet_type, packet->size())) { RTC_LOG(LS_ERROR) << "Dropping outgoing " << ToString() << " " << RtpPacketTypeToString(packet_type) << " packet: wrong size=" << packet->size(); return false; } if (!srtp_active()) { if (srtp_required_) { // The audio/video engines may attempt to send RTCP packets as soon as the // streams are created, so don't treat this as an error for RTCP. // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=6809 // However, there shouldn't be any RTP packets sent before SRTP is set // up (and SetSend(true) is called). RTC_DCHECK(rtcp) << "Can't send outgoing RTP packet for " << ToString() << " when SRTP is inactive and crypto is required"; return false; } RTC_DLOG(LS_WARNING) << "Sending an " << (rtcp ? "RTCP" : "RTP") << " packet without encryption for " << ToString() << "."; } if (on_first_packet_sent_ && options.info_signaled_after_sent.is_media) { std::move(on_first_packet_sent_)(); on_first_packet_sent_ = nullptr; } return rtcp ? rtp_transport_->SendRtcpPacket(packet, options, PF_SRTP_BYPASS) : rtp_transport_->SendRtpPacket(packet, options, PF_SRTP_BYPASS); } void BaseChannel::OnRtpPacket(const RtpPacketReceived& parsed_packet) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(network_initialized()); if (!srtp_active() && srtp_required_) { // Our session description indicates that SRTP is required, but we got a // packet before our SRTP filter is active. This means either that // a) we got SRTP packets before we received the SDES keys, in which case // we can't decrypt it anyway, or // b) we got SRTP packets before DTLS completed on both the RTP and RTCP // transports, so we haven't yet extracted keys, even if DTLS did // complete on the transport that the packets are being sent on. It's // really good practice to wait for both RTP and RTCP to be good to go // before sending media, to prevent weird failure modes, so it's fine // for us to just eat packets here. This is all sidestepped if RTCP mux // is used anyway. RTC_LOG(LS_WARNING) << "Can't process incoming RTP packet when " "SRTP is inactive and crypto is required " << ToString(); return; } if (on_packet_received_n_) { on_packet_received_n_(parsed_packet); } media_receive_channel()->OnPacketReceived(parsed_packet); } RTCError BaseChannel::MaybeUpdateDemuxerAndRtpExtensions_w( bool update_demuxer, std::optional> payload_types, const RtpHeaderExtensions& extensions, std::optional> ssrcs) { const bool pending_update = update_demuxer || payload_types.has_value() || ssrcs.has_value(); if (pending_update) { media_receive_channel()->OnDemuxerCriteriaUpdatePending(); } absl::Cleanup cleanup = [this, pending_update] { if (pending_update) { media_receive_channel()->OnDemuxerCriteriaUpdateComplete(); } }; // TODO(bugs.webrtc.org/13536): See if we can do this asynchronously. RTCError error = network_thread()->BlockingCall([&]() -> RTCError { RTC_DCHECK_RUN_ON(network_thread()); RTCError error = rtp_transport_ ? rtp_transport_->RegisterRtpHeaderExtensionMap(mid(), extensions) : (RTCError::InvalidState() << "No transport assigned."); if (!error.ok()) { error.string_builder() << " (mid=" << mid() << ")"; return RTC_LOG_ERROR(error); } if (payload_types) { if (payload_types_ != *payload_types) { payload_types_ = std::move(*payload_types); update_demuxer = true; } } if (ssrcs) { if (ssrcs_ != *ssrcs) { ssrcs_ = std::move(*ssrcs); update_demuxer = true; } } if (!update_demuxer) return RTCError::OK(); RtpDemuxerCriteria criteria = demuxer_criteria(); if (!rtp_transport_->RegisterRtpDemuxerSink(criteria, this)) { return RTCError::InvalidParameter() << "Failed to apply demuxer criteria for '" << mid() << "': '" << criteria.ToString() << "'."; } return RTCError::OK(); }); return error; } bool BaseChannel::RegisterRtpDemuxerSink_w( const MediaContentDescription* content, std::vector removed_ssrcs) { bool clear_payload_types = false; if (!RtpTransceiverDirectionHasSend(content->direction())) { RTC_DLOG(LS_VERBOSE) << "RegisterRtpDemuxerSink_w: remote side will not send " "- disable payload type demuxing for " << ToString(); clear_payload_types = true; } flat_set ssrcs; for (const StreamParams& new_stream : content->streams()) { ssrcs.insert(new_stream.ssrcs.begin(), new_stream.ssrcs.end()); } bool ret = network_thread_->BlockingCall([&] { RTC_DCHECK_RUN_ON(network_thread()); if (!rtp_transport_) { // Transport was disconnected before attempting to update the // criteria. This can happen while setting the remote description. // See chromium:1295469 for an example. return false; } if (!removed_ssrcs.empty()) { media_receive_channel()->ClearReceiveSinks_n(std::move(removed_ssrcs)); } bool needs_re_registration = false; if (clear_payload_types && !payload_types_.empty()) { payload_types_.clear(); needs_re_registration = true; } if (ssrcs_ != ssrcs) { ssrcs_ = std::move(ssrcs); needs_re_registration = true; } media_receive_channel()->SetReceiveSsrcs_n(ssrcs_); if (!needs_re_registration) { return true; } // Note that RegisterRtpDemuxerSink first unregisters the sink if // already registered. So this will change the state of the class // whether the call succeeds or not. RtpDemuxerCriteria criteria = demuxer_criteria(); return rtp_transport_->RegisterRtpDemuxerSink(criteria, this); }); return ret; } RtpDemuxerCriteria BaseChannel::demuxer_criteria() const { RTC_DCHECK_RUN_ON(network_thread()); RtpDemuxerCriteria criteria(mid_); criteria.payload_types() = payload_types_; criteria.ssrcs() = ssrcs_; return criteria; } void BaseChannel::EnableMedia_w() { if (enabled_) return; RTC_LOG(LS_INFO) << "Channel enabled: " << ToString(); enabled_ = true; UpdateMediaSendRecvState_w(); } void BaseChannel::DisableMedia_w() { if (!enabled_) return; RTC_LOG(LS_INFO) << "Channel disabled: " << ToString(); enabled_ = false; UpdateMediaSendRecvState_w(); } void BaseChannel::UpdateMediaSendRecvState_w() { RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); bool receive = enabled() && RtpTransceiverDirectionHasRecv(local_content_direction()); media_receive_channel()->SetReceive(receive); bool send = IsReadyToSendMedia_w(); media_send_channel()->SetSend(send); RTC_LOG(LS_INFO) << "Changing state, recv=" << receive << " send=" << send << " for " << ToString(); } RTCError BaseChannel::SetLocalContent_w(const MediaContentDescription* content, SdpType type) { TRACE_EVENT0("webrtc", "BaseChannel::SetLocalContent_w"); RTC_DLOG(LS_INFO) << "Setting local description for " << ToString(); RtpHeaderExtensions header_extensions = GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions()); std::optional> payload_types; if (RtpTransceiverDirectionHasRecv(content->direction())) { payload_types.emplace(); for (const Codec& codec : content->codecs()) { payload_types->insert(codec.id); } } RTCError error = MaybeUpdateDemuxerAndRtpExtensions_w( /*update_demuxer=*/false, std::move(payload_types), header_extensions, /*ssrcs=*/std::nullopt); if (!error.ok()) { return error; } RTC_LOG_THREAD_BLOCK_COUNT(); media_send_channel()->SetExtmapAllowMixed(content->extmap_allow_mixed()); if (media_type_ == MediaType::VIDEO) { VideoReceiverParameters recv_params = std::get(last_recv_params_); MediaChannelParametersFromMediaDescription( content, header_extensions, RtpTransceiverDirectionHasRecv(content->direction()), &recv_params); recv_params.mid = mid(); VideoSenderParameters send_params; if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { send_params = std::get(last_send_params_); send_params.extensions = header_extensions; send_params.extmap_allow_mixed = content->extmap_allow_mixed(); error = MaybeIgnorePacketization(recv_params, send_params); if (!error.ok()) return error; } if (!video_media_receive_channel()->SetReceiverParameters(recv_params)) { StringBuilder sb; sb << "Failed to set local video description recv parameters for " "m-section with mid='" << mid() << "'."; error = RTCError::InvalidParameter(sb.Release()); return error; } last_recv_params_ = std::move(recv_params); if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { if (!video_media_send_channel()->SetSenderParameters(send_params)) { StringBuilder sb; sb << "Failed to set send parameters for m-section with mid='" << mid() << "'."; error = RTCError::InvalidParameter(sb.Release()); return error; } last_send_params_ = std::move(send_params); } } else { AudioReceiverParameters recv_params = std::get(last_recv_params_); MediaChannelParametersFromMediaDescription( content, header_extensions, RtpTransceiverDirectionHasRecv(content->direction()), &recv_params); recv_params.mid = mid(); AudioSenderParameter send_params; if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { send_params = std::get(last_send_params_); send_params.extensions = header_extensions; send_params.extmap_allow_mixed = content->extmap_allow_mixed(); } if (!voice_media_receive_channel()->SetReceiverParameters(recv_params)) { StringBuilder sb; sb << "Failed to set local audio description recv parameters for " "m-section with mid='" << mid() << "'."; error = RTCError::InvalidParameter(sb.Release()); return error; } last_recv_params_ = std::move(recv_params); if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { if (!voice_media_send_channel()->SetSenderParameters(send_params)) { StringBuilder sb; sb << "Failed to set send parameters for m-section with mid='" << mid() << "'."; error = RTCError::InvalidParameter(sb.Release()); return error; } last_send_params_ = std::move(send_params); } } error = UpdateLocalStreams_w(content->streams(), type); if (!error.ok()) { return error; } set_local_content_direction(content->direction()); UpdateMediaSendRecvState_w(); RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); return RTCError::OK(); } RTCError BaseChannel::SetRemoteContent_w(const MediaContentDescription* content, SdpType type) { TRACE_EVENT0("webrtc", "BaseChannel::SetRemoteContent_w"); RTC_LOG(LS_INFO) << "Setting remote description for " << ToString(); RtpHeaderExtensions header_extensions = GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions()); RTCError error = MaybeUpdateDemuxerAndRtpExtensions_w( /*update_demuxer=*/false, /*payload_types=*/std::nullopt, header_extensions, /*ssrcs=*/std::nullopt); if (!error.ok()) { return error; } RTC_LOG_THREAD_BLOCK_COUNT(); if (media_type_ == MediaType::VIDEO) { VideoSenderParameters send_params = std::get(last_send_params_); RtpSendParametersFromMediaDescription(content, extensions_filter(), &send_params); send_params.mid = mid(); send_params.conference_mode = content->conference_mode(); VideoReceiverParameters recv_params; if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { recv_params = std::get(last_recv_params_); recv_params.extensions = send_params.extensions; recv_params.rtcp.reduced_size = send_params.rtcp.reduced_size; error = MaybeIgnorePacketization(send_params, recv_params); if (!error.ok()) return error; } if (!video_media_send_channel()->SetSenderParameters(send_params)) { StringBuilder sb; sb << "Failed to set remote video description send parameters for " "m-section with mid='" << mid() << "'."; return RTCError::InvalidParameter(sb.Release()); } last_send_params_ = std::move(send_params); if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { if (!video_media_receive_channel()->SetReceiverParameters(recv_params)) { StringBuilder sb; sb << "Failed to set recv parameters for m-section with mid='" << mid() << "'."; return RTCError::InvalidParameter(sb.Release()); } last_recv_params_ = std::move(recv_params); } } else { AudioSenderParameter send_params = std::get(last_send_params_); RtpSendParametersFromMediaDescription(content, extensions_filter(), &send_params); send_params.mid = mid(); AudioReceiverParameters recv_params; if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { recv_params = std::get(last_recv_params_); recv_params.extensions = send_params.extensions; } if (!voice_media_send_channel()->SetSenderParameters(send_params)) { StringBuilder sb; sb << "Failed to set remote audio description send parameters for " "m-section with mid='" << mid() << "'."; return RTCError::InvalidParameter(sb.Release()); } last_send_params_ = std::move(send_params); if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { if (!voice_media_receive_channel()->SetReceiverParameters(recv_params)) { StringBuilder sb; sb << "Failed to set recv parameters for m-section with mid='" << mid() << "'."; return RTCError::InvalidParameter(sb.Release()); } last_recv_params_ = std::move(recv_params); } } // Non-sender RTT (RRTR/DLRR) is signaled either by the standard // a=rtcp-xr:rcvr-rtt or by the legacy non-standard a=rtcp-fb: rrtr. const bool receive_non_sender_rtt = content->receive_non_sender_rtt() || absl::c_any_of(content->codecs(), [](const Codec& codec) { return HasRrtr(codec); }); if (media_type_ == MediaType::AUDIO) { voice_media_receive_channel()->SetRtcpMode(content->rtcp_reduced_size() ? RtcpMode::kReducedSize : RtcpMode::kCompound); voice_media_receive_channel()->SetReceiveNackEnabled( voice_media_send_channel()->SenderNackEnabled()); voice_media_receive_channel()->SetReceiveNonSenderRttEnabled( receive_non_sender_rtt); } RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); if (media_type_ == MediaType::VIDEO) { video_media_receive_channel()->SetReceiveNonSenderRttEnabled( receive_non_sender_rtt); } error = UpdateRemoteStreams_w(content, type); RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); return error; } void BaseChannel::UpdateWritableState_n() { TRACE_EVENT0("webrtc", "BaseChannel::UpdateWritableState_n"); if (rtp_transport_->IsWritable(/*rtcp=*/true) && rtp_transport_->IsWritable(/*rtcp=*/false)) { ChannelWritable_n(); } else { ChannelNotWritable_n(); } } void BaseChannel::ChannelWritable_n() { TRACE_EVENT0("webrtc", "BaseChannel::ChannelWritable_n"); if (writable_) { return; } writable_ = true; RTC_LOG(LS_INFO) << "Channel writable (" << ToString() << ")" << (was_ever_writable_n_ ? "" : " for the first time"); // We only have to do this PostTask once, when first transitioning to // writable. if (!was_ever_writable_n_) { worker_thread_->PostTask(SafeTask(alive_, [this] { RTC_DCHECK_RUN_ON(worker_thread()); was_ever_writable_ = true; UpdateMediaSendRecvState_w(); })); } was_ever_writable_n_ = true; } void BaseChannel::ChannelNotWritable_n() { TRACE_EVENT0("webrtc", "BaseChannel::ChannelNotWritable_n"); if (!writable_) { return; } writable_ = false; RTC_LOG(LS_INFO) << "Channel not writable (" << ToString() << ")"; } RTCError BaseChannel::UpdateLocalStreams_w( const std::vector& streams, SdpType type) { RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); // In the case of RIDs (where SSRCs are not negotiated), this method will // generate an SSRC for each layer in StreamParams. That representation will // be stored internally in `local_streams_`. // In subsequent offers, the same stream can appear in `streams` again // (without the SSRCs), so it should be looked up using RIDs (if available) // and then by primary SSRC. // In both scenarios, it is safe to assume that the media channel will be // created with a StreamParams object with SSRCs. However, it is not safe to // assume that `local_streams_` will always have SSRCs as there are scenarios // in which niether SSRCs or RIDs are negotiated. // Check for streams that have been removed. for (const StreamParams& old_stream : local_streams_) { if (!old_stream.has_ssrcs() || GetStream(streams, StreamFinder(&old_stream))) { continue; } if (!media_send_channel()->RemoveSendStream(old_stream.first_ssrc())) { return RTCError::InvalidParameter() << "Failed to remove send stream with ssrc " << old_stream.first_ssrc() << " from m-section with mid='" << mid() << "'."; } } // Check for new streams. std::vector all_streams; for (const StreamParams& stream : streams) { StreamParams* existing = GetStream(local_streams_, StreamFinder(&stream)); if (existing) { // Parameters cannot change for an existing stream. all_streams.push_back(*existing); continue; } all_streams.push_back(stream); StreamParams& new_stream = all_streams.back(); if (!new_stream.has_ssrcs() && !new_stream.has_rids()) { continue; } RTC_DCHECK(new_stream.has_ssrcs() || new_stream.has_rids()); if (new_stream.has_ssrcs() && new_stream.has_rids()) { return RTCError::InvalidParameter() << "Failed to add send stream: " << new_stream.first_ssrc() << " into m-section with mid='" << mid() << "'. Stream has both SSRCs and RIDs."; } // At this point we use the legacy simulcast group in StreamParams to // indicate that we want multiple layers to the media channel. if (!new_stream.has_ssrcs()) { // TODO(bugs.webrtc.org/10250): Indicate if flex is desired here. new_stream.GenerateSsrcs(new_stream.rids().size(), /* rtx = */ true, /* flex_fec = */ false, ssrc_generator_); } if (media_send_channel()->AddSendStream(new_stream)) { RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.first_ssrc() << " into " << ToString(); } else { return RTCError::InvalidParameter() << "Failed to add send stream ssrc: " << new_stream.first_ssrc() << " into m-section with mid='" << mid() << "'"; } } local_streams_ = std::move(all_streams); return RTCError::OK(); } RTCError BaseChannel::UpdateRemoteStreams_w( const MediaContentDescription* content, SdpType type) { RTC_LOG_THREAD_BLOCK_COUNT(); media_receive_channel()->OnDemuxerCriteriaUpdatePending(); absl::Cleanup cleanup = [this] { media_receive_channel()->OnDemuxerCriteriaUpdateComplete(); }; const std::vector& streams = content->streams(); const bool new_has_unsignaled_ssrcs = HasStreamWithNoSsrcs(streams); const bool old_has_unsignaled_ssrcs = HasStreamWithNoSsrcs(remote_streams_); // Check for streams that have been removed. std::vector removed_ssrcs; bool reset_unsignaled_streams = false; for (const StreamParams& old_stream : remote_streams_) { // If we no longer have an unsignaled stream, we would like to remove // the unsignaled stream params that are cached. if (!old_stream.has_ssrcs() && !new_has_unsignaled_ssrcs) { if (!reset_unsignaled_streams) { reset_unsignaled_streams = true; std::vector unsignaled = media_receive_channel()->GetUnsignaledSsrcs(); removed_ssrcs.insert(removed_ssrcs.end(), unsignaled.begin(), unsignaled.end()); RTC_LOG(LS_INFO) << "Reset unsignaled remote stream for " << ToString() << "."; } } else if (old_stream.has_ssrcs() && !GetStreamBySsrc(streams, old_stream.first_ssrc())) { removed_ssrcs.push_back(old_stream.first_ssrc()); } } RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); // Re-register the sink to update after changing the demuxer criteria first. if (!RegisterRtpDemuxerSink_w(content, std::move(removed_ssrcs))) { return RTCError::InvalidParameter() << "Failed to set up audio demuxing for mid='" << mid() << "'."; } RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); if (reset_unsignaled_streams) { media_receive_channel()->ResetUnsignaledRecvStream(); } // Now remove the old streams on the worker thread. for (const StreamParams& old_stream : remote_streams_) { if (old_stream.has_ssrcs() && !GetStreamBySsrc(streams, old_stream.first_ssrc())) { if (media_receive_channel()->RemoveRecvStream(old_stream.first_ssrc())) { RTC_LOG(LS_INFO) << "Remove remote ssrc: " << old_stream.first_ssrc() << " from " << ToString() << "."; } else { return RTCError::InvalidParameter() << "Failed to remove remote stream with ssrc " << old_stream.first_ssrc() << " from m-section with mid='" << mid() << "'."; } } } // Check for new streams. for (const StreamParams& new_stream : streams) { // We allow a StreamParams with an empty list of SSRCs, in which case the // MediaChannel will cache the parameters and use them for any unsignaled // stream received later. if ((!new_stream.has_ssrcs() && !old_has_unsignaled_ssrcs) || !GetStreamBySsrc(remote_streams_, new_stream.first_ssrc())) { if (media_receive_channel()->AddRecvStream(new_stream)) { RTC_LOG(LS_INFO) << "Add remote ssrc: " << (new_stream.has_ssrcs() ? std::to_string(new_stream.first_ssrc()) : "unsignaled") << " to " << ToString(); } else { return RTCError::InvalidParameter() << "Failed to add remote stream ssrc: " << (new_stream.has_ssrcs() ? std::to_string(new_stream.first_ssrc()) : "unsignaled") << " to " << ToString(); } } } remote_streams_ = streams; set_remote_content_direction(content->direction()); UpdateMediaSendRecvState_w(); RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); return RTCError::OK(); } RtpHeaderExtensions BaseChannel::GetDeduplicatedRtpHeaderExtensions( const RtpHeaderExtensions& extensions) { return RtpExtension::DeduplicateHeaderExtensions(extensions, extensions_filter_); } void BaseChannel::SignalSentPacket_n(const SentPacketInfo& sent_packet) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(network_initialized()); media_send_channel()->OnPacketSent(sent_packet); } } // namespace webrtc