| /* | |
| * 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 <algorithm> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #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<RidDescription>& target_rids = target_->rids(); | |
| const std::vector<RidDescription>& 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<const Codec*> 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<const Codec*> 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<MediaSendChannelInterface> send_media_channel_impl, | |
| std::unique_ptr<MediaReceiveChannelInterface> 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<Socket::Option, int>(opt, value)); | |
| return rtp_transport_->SetRtpOption(opt, value); | |
| case ST_RTCP: | |
| rtcp_socket_options_.push_back( | |
| std::pair<Socket::Option, int>(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<flat_set<uint8_t>> payload_types, | |
| const RtpHeaderExtensions& extensions, | |
| std::optional<flat_set<uint32_t>> 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<uint32_t> 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<uint32_t> 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<flat_set<uint8_t>> 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<VideoReceiverParameters>(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<VideoSenderParameters>(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 { | |
| // Set the MID before UpdateLocalStreams_w() adds send streams, so that | |
| // they're created with it. The send parameters, which also carry the MID, | |
| // are only applied here for answers. | |
| voice_media_send_channel()->SetMid(mid()); | |
| AudioReceiverParameters recv_params = | |
| std::get<AudioReceiverParameters>(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<AudioSenderParameter>(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<VideoSenderParameters>(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<VideoReceiverParameters>(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<AudioSenderParameter>(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<AudioReceiverParameters>(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:<pt> 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<StreamParams>& 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<StreamParams> 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<StreamParams>& 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<uint32_t> 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<uint32_t> 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 |