From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- third_party/libwebrtc/pc/rtp_transceiver.cc | 791 ++++++++++++++++++++++++++++ 1 file changed, 791 insertions(+) create mode 100644 third_party/libwebrtc/pc/rtp_transceiver.cc (limited to 'third_party/libwebrtc/pc/rtp_transceiver.cc') diff --git a/third_party/libwebrtc/pc/rtp_transceiver.cc b/third_party/libwebrtc/pc/rtp_transceiver.cc new file mode 100644 index 0000000000..815ec9dece --- /dev/null +++ b/third_party/libwebrtc/pc/rtp_transceiver.cc @@ -0,0 +1,791 @@ +/* + * Copyright 2017 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/rtp_transceiver.h" + +#include + +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/memory/memory.h" +#include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/field_trials_view.h" +#include "api/rtp_parameters.h" +#include "api/sequence_checker.h" +#include "media/base/codec.h" +#include "media/base/media_channel.h" +#include "media/base/media_channel_impl.h" +#include "media/base/media_constants.h" +#include "media/base/media_engine.h" +#include "pc/channel.h" +#include "pc/rtp_media_utils.h" +#include "pc/session_description.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" + +namespace webrtc { +namespace { + +RTCError VerifyCodecPreferences( + const std::vector& codecs, + const std::vector& send_codecs, + const std::vector& recv_codecs) { + // If the intersection between codecs and + // RTCRtpSender.getCapabilities(kind).codecs or the intersection between + // codecs and RTCRtpReceiver.getCapabilities(kind).codecs only contains RTX, + // RED or FEC codecs or is an empty set, throw InvalidModificationError. + // This ensures that we always have something to offer, regardless of + // transceiver.direction. + + if (!absl::c_any_of(codecs, [&recv_codecs](const RtpCodecCapability& codec) { + return codec.name != cricket::kRtxCodecName && + codec.name != cricket::kRedCodecName && + codec.name != cricket::kFlexfecCodecName && + absl::c_any_of(recv_codecs, + [&codec](const cricket::Codec& recv_codec) { + return recv_codec.MatchesRtpCodec(codec); + }); + })) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Invalid codec preferences: Missing codec from recv " + "codec capabilities."); + } + + if (!absl::c_any_of(codecs, [&send_codecs](const RtpCodecCapability& codec) { + return codec.name != cricket::kRtxCodecName && + codec.name != cricket::kRedCodecName && + codec.name != cricket::kFlexfecCodecName && + absl::c_any_of(send_codecs, + [&codec](const cricket::Codec& send_codec) { + return send_codec.MatchesRtpCodec(codec); + }); + })) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Invalid codec preferences: Missing codec from send " + "codec capabilities."); + } + + // Let codecCapabilities be the union of + // RTCRtpSender.getCapabilities(kind).codecs and + // RTCRtpReceiver.getCapabilities(kind).codecs. For each codec in codecs, If + // codec is not in codecCapabilities, throw InvalidModificationError. + for (const auto& codec_preference : codecs) { + bool is_recv_codec = absl::c_any_of( + recv_codecs, [&codec_preference](const cricket::Codec& codec) { + return codec.MatchesRtpCodec(codec_preference); + }); + + bool is_send_codec = absl::c_any_of( + send_codecs, [&codec_preference](const cricket::Codec& codec) { + return codec.MatchesRtpCodec(codec_preference); + }); + + if (!is_recv_codec && !is_send_codec) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + std::string("Invalid codec preferences: invalid codec with name \"") + + codec_preference.name + "\"."); + } + } + + // Check we have a real codec (not just rtx, red or fec) + if (absl::c_all_of(codecs, [](const RtpCodecCapability& codec) { + return codec.name == cricket::kRtxCodecName || + codec.name == cricket::kRedCodecName || + codec.name == cricket::kUlpfecCodecName; + })) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INVALID_MODIFICATION, + "Invalid codec preferences: codec list must have a non " + "RTX, RED or FEC entry."); + } + + return RTCError::OK(); +} + +// Matches the list of codecs as capabilities (potentially without SVC related +// information) to the list of send codecs and returns the list of codecs with +// all the SVC related information. +std::vector MatchCodecPreferences( + const std::vector& codecs, + const std::vector& send_codecs) { + std::vector result; + + for (const auto& codec_preference : codecs) { + for (const cricket::VideoCodec& send_codec : send_codecs) { + if (send_codec.MatchesRtpCodec(codec_preference)) { + result.push_back(send_codec); + } + } + } + + return result; +} + +TaskQueueBase* GetCurrentTaskQueueOrThread() { + TaskQueueBase* current = TaskQueueBase::Current(); + if (!current) + current = rtc::ThreadManager::Instance()->CurrentThread(); + return current; +} + +} // namespace + +RtpTransceiver::RtpTransceiver(cricket::MediaType media_type, + ConnectionContext* context) + : thread_(GetCurrentTaskQueueOrThread()), + unified_plan_(false), + media_type_(media_type), + context_(context) { + RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO); +} + +RtpTransceiver::RtpTransceiver( + rtc::scoped_refptr> sender, + rtc::scoped_refptr> + receiver, + ConnectionContext* context, + std::vector header_extensions_to_negotiate, + std::function on_negotiation_needed) + : thread_(GetCurrentTaskQueueOrThread()), + unified_plan_(true), + media_type_(sender->media_type()), + context_(context), + header_extensions_to_negotiate_( + std::move(header_extensions_to_negotiate)), + on_negotiation_needed_(std::move(on_negotiation_needed)) { + RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO || + media_type_ == cricket::MEDIA_TYPE_VIDEO); + RTC_DCHECK_EQ(sender->media_type(), receiver->media_type()); + sender->internal()->SetCodecPreferences( + sender->media_type() == cricket::MEDIA_TYPE_VIDEO + ? media_engine()->video().send_codecs(false) + : media_engine()->voice().send_codecs()); + senders_.push_back(sender); + receivers_.push_back(receiver); +} + +RtpTransceiver::~RtpTransceiver() { + // TODO(tommi): On Android, when running PeerConnectionClientTest (e.g. + // PeerConnectionClientTest#testCameraSwitch), the instance doesn't get + // deleted on `thread_`. See if we can fix that. + if (!stopped_) { + RTC_DCHECK_RUN_ON(thread_); + StopInternal(); + } + + RTC_CHECK(!channel_) << "Missing call to ClearChannel?"; +} + +RTCError RtpTransceiver::CreateChannel( + absl::string_view mid, + Call* call_ptr, + const cricket::MediaConfig& media_config, + bool srtp_required, + CryptoOptions crypto_options, + const cricket::AudioOptions& audio_options, + const cricket::VideoOptions& video_options, + VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, + std::function transport_lookup) { + RTC_DCHECK_RUN_ON(thread_); + if (!media_engine()) { + // TODO(hta): Must be a better way + return RTCError(RTCErrorType::INTERNAL_ERROR, + "No media engine for mid=" + std::string(mid)); + } + std::unique_ptr new_channel; + if (media_type() == cricket::MEDIA_TYPE_AUDIO) { + // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to + // the worker thread. We shouldn't be using the `call_ptr_` hack here but + // simply be on the worker thread and use `call_` (update upstream code). + RTC_DCHECK(call_ptr); + RTC_DCHECK(media_engine()); + // TODO(bugs.webrtc.org/11992): Remove this workaround after updates in + // PeerConnection and add the expectation that we're already on the right + // thread. + context()->worker_thread()->BlockingCall([&] { + RTC_DCHECK_RUN_ON(context()->worker_thread()); + + AudioCodecPairId codec_pair_id = AudioCodecPairId::Create(); + + std::unique_ptr + media_send_channel = media_engine()->voice().CreateSendChannel( + call_ptr, media_config, audio_options, crypto_options, + codec_pair_id); + if (!media_send_channel) { + // TODO(bugs.webrtc.org/14912): Consider CHECK or reporting failure + return; + } + std::unique_ptr + media_receive_channel = media_engine()->voice().CreateReceiveChannel( + call_ptr, media_config, audio_options, crypto_options, + codec_pair_id); + if (!media_receive_channel) { + return; + } + // Note that this is safe because both sending and + // receiving channels will be deleted at the same time. + media_send_channel->SetSsrcListChangedCallback( + [receive_channel = + media_receive_channel.get()](const std::set& choices) { + receive_channel->ChooseReceiverReportSsrc(choices); + }); + + new_channel = std::make_unique( + context()->worker_thread(), context()->network_thread(), + context()->signaling_thread(), std::move(media_send_channel), + std::move(media_receive_channel), mid, srtp_required, crypto_options, + context()->ssrc_generator()); + }); + } else { + RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, media_type()); + + // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to + // the worker thread. We shouldn't be using the `call_ptr_` hack here but + // simply be on the worker thread and use `call_` (update upstream code). + context()->worker_thread()->BlockingCall([&] { + RTC_DCHECK_RUN_ON(context()->worker_thread()); + + std::unique_ptr + media_send_channel = media_engine()->video().CreateSendChannel( + call_ptr, media_config, video_options, crypto_options, + video_bitrate_allocator_factory); + if (!media_send_channel) { + return; + } + + std::unique_ptr + media_receive_channel = media_engine()->video().CreateReceiveChannel( + call_ptr, media_config, video_options, crypto_options); + if (!media_receive_channel) { + return; + } + // Note that this is safe because both sending and + // receiving channels will be deleted at the same time. + media_send_channel->SetSsrcListChangedCallback( + [receive_channel = + media_receive_channel.get()](const std::set& choices) { + receive_channel->ChooseReceiverReportSsrc(choices); + }); + + new_channel = std::make_unique( + context()->worker_thread(), context()->network_thread(), + context()->signaling_thread(), std::move(media_send_channel), + std::move(media_receive_channel), mid, srtp_required, crypto_options, + context()->ssrc_generator()); + }); + } + if (!new_channel) { + // TODO(hta): Must be a better way + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to create channel for mid=" + std::string(mid)); + } + SetChannel(std::move(new_channel), transport_lookup); + return RTCError::OK(); +} + +void RtpTransceiver::SetChannel( + std::unique_ptr channel, + std::function transport_lookup) { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(channel); + RTC_DCHECK(transport_lookup); + RTC_DCHECK(!channel_); + // Cannot set a channel on a stopped transceiver. + if (stopped_) { + return; + } + + RTC_LOG_THREAD_BLOCK_COUNT(); + + RTC_DCHECK_EQ(media_type(), channel->media_type()); + signaling_thread_safety_ = PendingTaskSafetyFlag::Create(); + + std::unique_ptr channel_to_delete; + + // An alternative to this, could be to require SetChannel to be called + // on the network thread. The channel object operates for the most part + // on the network thread, as part of its initialization being on the network + // thread is required, so setting a channel object as part of the construction + // (without thread hopping) might be the more efficient thing to do than + // how SetChannel works today. + // Similarly, if the channel() accessor is limited to the network thread, that + // helps with keeping the channel implementation requirements being met and + // avoids synchronization for accessing the pointer or network related state. + context()->network_thread()->BlockingCall([&]() { + if (channel_) { + channel_->SetFirstPacketReceivedCallback(nullptr); + channel_->SetRtpTransport(nullptr); + channel_to_delete = std::move(channel_); + } + + channel_ = std::move(channel); + + channel_->SetRtpTransport(transport_lookup(channel_->mid())); + channel_->SetFirstPacketReceivedCallback( + [thread = thread_, flag = signaling_thread_safety_, this]() mutable { + thread->PostTask( + SafeTask(std::move(flag), [this]() { OnFirstPacketReceived(); })); + }); + }); + PushNewMediaChannelAndDeleteChannel(nullptr); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2); +} + +void RtpTransceiver::ClearChannel() { + RTC_DCHECK_RUN_ON(thread_); + + if (!channel_) { + return; + } + + RTC_LOG_THREAD_BLOCK_COUNT(); + + if (channel_) { + signaling_thread_safety_->SetNotAlive(); + signaling_thread_safety_ = nullptr; + } + std::unique_ptr channel_to_delete; + + context()->network_thread()->BlockingCall([&]() { + if (channel_) { + channel_->SetFirstPacketReceivedCallback(nullptr); + channel_->SetRtpTransport(nullptr); + channel_to_delete = std::move(channel_); + } + }); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); + PushNewMediaChannelAndDeleteChannel(std::move(channel_to_delete)); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2); +} + +void RtpTransceiver::PushNewMediaChannelAndDeleteChannel( + std::unique_ptr channel_to_delete) { + // The clumsy combination of pushing down media channel and deleting + // the channel is due to the desire to do both things in one Invoke(). + if (!channel_to_delete && senders_.empty() && receivers_.empty()) { + return; + } + context()->worker_thread()->BlockingCall([&]() { + // Push down the new media_channel, if any, otherwise clear it. + auto* media_send_channel = + channel_ ? channel_->media_send_channel() : nullptr; + for (const auto& sender : senders_) { + sender->internal()->SetMediaChannel(media_send_channel); + } + + auto* media_receive_channel = + channel_ ? channel_->media_receive_channel() : nullptr; + for (const auto& receiver : receivers_) { + receiver->internal()->SetMediaChannel(media_receive_channel); + } + + // Destroy the channel, if we had one, now _after_ updating the receivers + // who might have had references to the previous channel. + if (channel_to_delete) { + channel_to_delete.reset(nullptr); + } + }); +} + +void RtpTransceiver::AddSender( + rtc::scoped_refptr> sender) { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(!stopped_); + RTC_DCHECK(!unified_plan_); + RTC_DCHECK(sender); + RTC_DCHECK_EQ(media_type(), sender->media_type()); + RTC_DCHECK(!absl::c_linear_search(senders_, sender)); + + std::vector send_codecs = + media_type() == cricket::MEDIA_TYPE_VIDEO + ? media_engine()->video().send_codecs(false) + : media_engine()->voice().send_codecs(); + sender->internal()->SetCodecPreferences( + codec_preferences_.empty() + ? send_codecs + : MatchCodecPreferences(codec_preferences_, send_codecs)); + senders_.push_back(sender); +} + +bool RtpTransceiver::RemoveSender(RtpSenderInterface* sender) { + RTC_DCHECK(!unified_plan_); + if (sender) { + RTC_DCHECK_EQ(media_type(), sender->media_type()); + } + auto it = absl::c_find(senders_, sender); + if (it == senders_.end()) { + return false; + } + (*it)->internal()->Stop(); + senders_.erase(it); + return true; +} + +void RtpTransceiver::AddReceiver( + rtc::scoped_refptr> + receiver) { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(!stopped_); + RTC_DCHECK(!unified_plan_); + RTC_DCHECK(receiver); + RTC_DCHECK_EQ(media_type(), receiver->media_type()); + RTC_DCHECK(!absl::c_linear_search(receivers_, receiver)); + receivers_.push_back(receiver); +} + +bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(!unified_plan_); + if (receiver) { + RTC_DCHECK_EQ(media_type(), receiver->media_type()); + } + auto it = absl::c_find(receivers_, receiver); + if (it == receivers_.end()) { + return false; + } + + (*it)->internal()->Stop(); + context()->worker_thread()->BlockingCall([&]() { + // `Stop()` will clear the receiver's pointer to the media channel. + (*it)->internal()->SetMediaChannel(nullptr); + }); + + receivers_.erase(it); + return true; +} + +rtc::scoped_refptr RtpTransceiver::sender_internal() const { + RTC_DCHECK(unified_plan_); + RTC_CHECK_EQ(1u, senders_.size()); + return rtc::scoped_refptr(senders_[0]->internal()); +} + +rtc::scoped_refptr RtpTransceiver::receiver_internal() + const { + RTC_DCHECK(unified_plan_); + RTC_CHECK_EQ(1u, receivers_.size()); + return rtc::scoped_refptr(receivers_[0]->internal()); +} + +cricket::MediaType RtpTransceiver::media_type() const { + return media_type_; +} + +absl::optional RtpTransceiver::mid() const { + return mid_; +} + +void RtpTransceiver::OnFirstPacketReceived() { + for (const auto& receiver : receivers_) { + receiver->internal()->NotifyFirstPacketReceived(); + } +} + +rtc::scoped_refptr RtpTransceiver::sender() const { + RTC_DCHECK(unified_plan_); + RTC_CHECK_EQ(1u, senders_.size()); + return senders_[0]; +} + +rtc::scoped_refptr RtpTransceiver::receiver() const { + RTC_DCHECK(unified_plan_); + RTC_CHECK_EQ(1u, receivers_.size()); + return receivers_[0]; +} + +void RtpTransceiver::set_current_direction(RtpTransceiverDirection direction) { + RTC_LOG(LS_INFO) << "Changing transceiver (MID=" << mid_.value_or("") + << ") current direction from " + << (current_direction_ ? RtpTransceiverDirectionToString( + *current_direction_) + : "") + << " to " << RtpTransceiverDirectionToString(direction) + << "."; + current_direction_ = direction; + if (RtpTransceiverDirectionHasSend(*current_direction_)) { + has_ever_been_used_to_send_ = true; + } +} + +void RtpTransceiver::set_fired_direction( + absl::optional direction) { + fired_direction_ = direction; +} + +bool RtpTransceiver::stopped() const { + RTC_DCHECK_RUN_ON(thread_); + return stopped_; +} + +bool RtpTransceiver::stopping() const { + RTC_DCHECK_RUN_ON(thread_); + return stopping_; +} + +RtpTransceiverDirection RtpTransceiver::direction() const { + if (unified_plan_ && stopping()) + return webrtc::RtpTransceiverDirection::kStopped; + + return direction_; +} + +RTCError RtpTransceiver::SetDirectionWithError( + RtpTransceiverDirection new_direction) { + if (unified_plan_ && stopping()) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "Cannot set direction on a stopping transceiver."); + } + if (new_direction == direction_) + return RTCError::OK(); + + if (new_direction == RtpTransceiverDirection::kStopped) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "The set direction 'stopped' is invalid."); + } + + direction_ = new_direction; + on_negotiation_needed_(); + + return RTCError::OK(); +} + +absl::optional RtpTransceiver::current_direction() + const { + if (unified_plan_ && stopped()) + return webrtc::RtpTransceiverDirection::kStopped; + + return current_direction_; +} + +absl::optional RtpTransceiver::fired_direction() + const { + return fired_direction_; +} + +void RtpTransceiver::StopSendingAndReceiving() { + // 1. Let sender be transceiver.[[Sender]]. + // 2. Let receiver be transceiver.[[Receiver]]. + // + // 3. Stop sending media with sender. + // + RTC_DCHECK_RUN_ON(thread_); + + // 4. Send an RTCP BYE for each RTP stream that was being sent by sender, as + // specified in [RFC3550]. + for (const auto& sender : senders_) + sender->internal()->Stop(); + + // Signal to receiver sources that we're stopping. + for (const auto& receiver : receivers_) + receiver->internal()->Stop(); + + context()->worker_thread()->BlockingCall([&]() { + // 5 Stop receiving media with receiver. + for (const auto& receiver : receivers_) + receiver->internal()->SetMediaChannel(nullptr); + }); + + stopping_ = true; + direction_ = webrtc::RtpTransceiverDirection::kInactive; +} + +RTCError RtpTransceiver::StopStandard() { + RTC_DCHECK_RUN_ON(thread_); + // If we're on Plan B, do what Stop() used to do there. + if (!unified_plan_) { + StopInternal(); + return RTCError::OK(); + } + // 1. Let transceiver be the RTCRtpTransceiver object on which the method is + // invoked. + // + // 2. Let connection be the RTCPeerConnection object associated with + // transceiver. + // + // 3. If connection.[[IsClosed]] is true, throw an InvalidStateError. + if (is_pc_closed_) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, + "PeerConnection is closed."); + } + + // 4. If transceiver.[[Stopping]] is true, abort these steps. + if (stopping_) + return RTCError::OK(); + + // 5. Stop sending and receiving given transceiver, and update the + // negotiation-needed flag for connection. + StopSendingAndReceiving(); + on_negotiation_needed_(); + + return RTCError::OK(); +} + +void RtpTransceiver::StopInternal() { + RTC_DCHECK_RUN_ON(thread_); + StopTransceiverProcedure(); +} + +void RtpTransceiver::StopTransceiverProcedure() { + RTC_DCHECK_RUN_ON(thread_); + // As specified in the "Stop the RTCRtpTransceiver" procedure + // 1. If transceiver.[[Stopping]] is false, stop sending and receiving given + // transceiver. + if (!stopping_) + StopSendingAndReceiving(); + + // 2. Set transceiver.[[Stopped]] to true. + stopped_ = true; + + // Signal the updated change to the senders. + for (const auto& sender : senders_) + sender->internal()->SetTransceiverAsStopped(); + + // 3. Set transceiver.[[Receptive]] to false. + // 4. Set transceiver.[[CurrentDirection]] to null. + current_direction_ = absl::nullopt; +} + +RTCError RtpTransceiver::SetCodecPreferences( + rtc::ArrayView codec_capabilities) { + RTC_DCHECK(unified_plan_); + // 3. If codecs is an empty list, set transceiver's [[PreferredCodecs]] slot + // to codecs and abort these steps. + if (codec_capabilities.empty()) { + codec_preferences_.clear(); + senders_.front()->internal()->SetCodecPreferences( + media_type() == cricket::MEDIA_TYPE_VIDEO + ? media_engine()->video().send_codecs(false) + : media_engine()->voice().send_codecs()); + return RTCError::OK(); + } + + // 4. Remove any duplicate values in codecs. + std::vector codecs; + absl::c_remove_copy_if(codec_capabilities, std::back_inserter(codecs), + [&codecs](const RtpCodecCapability& codec) { + return absl::c_linear_search(codecs, codec); + }); + + // 6. to 8. + RTCError result; + std::vector recv_codecs, send_codecs; + if (media_type_ == cricket::MEDIA_TYPE_AUDIO) { + send_codecs = media_engine()->voice().send_codecs(); + recv_codecs = media_engine()->voice().recv_codecs(); + } else if (media_type_ == cricket::MEDIA_TYPE_VIDEO) { + send_codecs = media_engine()->video().send_codecs(context()->use_rtx()); + recv_codecs = media_engine()->video().recv_codecs(context()->use_rtx()); + } + result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs); + + if (result.ok()) { + senders_.front()->internal()->SetCodecPreferences( + MatchCodecPreferences(codecs, send_codecs)); + codec_preferences_ = codecs; + } + + return result; +} + +std::vector +RtpTransceiver::GetHeaderExtensionsToNegotiate() const { + return header_extensions_to_negotiate_; +} + +std::vector +RtpTransceiver::GetNegotiatedHeaderExtensions() const { + RTC_DCHECK_RUN_ON(thread_); + std::vector result; + result.reserve(header_extensions_to_negotiate_.size()); + for (const auto& ext : header_extensions_to_negotiate_) { + auto negotiated = absl::c_find_if(negotiated_header_extensions_, + [&ext](const RtpExtension& negotiated) { + return negotiated.uri == ext.uri; + }); + RtpHeaderExtensionCapability capability(ext.uri); + // TODO(bugs.webrtc.org/7477): extend when header extensions support + // direction. + capability.direction = negotiated != negotiated_header_extensions_.end() + ? RtpTransceiverDirection::kSendRecv + : RtpTransceiverDirection::kStopped; + result.push_back(capability); + } + return result; +} + +// Helper function to determine mandatory-to-negotiate extensions. +// See https://www.rfc-editor.org/rfc/rfc8834#name-header-extensions +// and https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface +// Since BUNDLE is offered by default, MID is mandatory and can not be turned +// off via this API. +bool IsMandatoryHeaderExtension(const std::string& uri) { + return uri == RtpExtension::kMidUri; +} + +RTCError RtpTransceiver::SetHeaderExtensionsToNegotiate( + rtc::ArrayView header_extensions) { + // https://w3c.github.io/webrtc-extensions/#dom-rtcrtptransceiver-setheaderextensionstonegotiate + if (header_extensions.size() != header_extensions_to_negotiate_.size()) { + return RTCError(RTCErrorType::INVALID_MODIFICATION, + "Size of extensions to negotiate does not match."); + } + // For each index i of extensions, run the following steps: ... + for (size_t i = 0; i < header_extensions.size(); i++) { + const auto& extension = header_extensions[i]; + if (extension.uri != header_extensions_to_negotiate_[i].uri) { + return RTCError(RTCErrorType::INVALID_MODIFICATION, + "Reordering extensions is not allowed."); + } + if (IsMandatoryHeaderExtension(extension.uri) && + extension.direction != RtpTransceiverDirection::kSendRecv) { + return RTCError(RTCErrorType::INVALID_MODIFICATION, + "Attempted to stop a mandatory extension."); + } + + // TODO(bugs.webrtc.org/7477): Currently there are no recvonly extensions so + // this can not be checked: "When there exists header extension capabilities + // that have directions other than kSendRecv, restrict extension.direction + // as to not exceed that capability." + } + + // Apply mutation after error checking. + for (size_t i = 0; i < header_extensions.size(); i++) { + header_extensions_to_negotiate_[i].direction = + header_extensions[i].direction; + } + + return RTCError::OK(); +} + +void RtpTransceiver::OnNegotiationUpdate( + SdpType sdp_type, + const cricket::MediaContentDescription* content) { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(content); + if (sdp_type == SdpType::kAnswer) + negotiated_header_extensions_ = content->rtp_header_extensions(); +} + +void RtpTransceiver::SetPeerConnectionClosed() { + is_pc_closed_ = true; +} + +} // namespace webrtc -- cgit v1.2.3