From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../libwebrtc/pc/jsep_transport_collection.cc | 390 +++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 third_party/libwebrtc/pc/jsep_transport_collection.cc (limited to 'third_party/libwebrtc/pc/jsep_transport_collection.cc') diff --git a/third_party/libwebrtc/pc/jsep_transport_collection.cc b/third_party/libwebrtc/pc/jsep_transport_collection.cc new file mode 100644 index 0000000000..b50d303d77 --- /dev/null +++ b/third_party/libwebrtc/pc/jsep_transport_collection.cc @@ -0,0 +1,390 @@ +/* + * Copyright 2021 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/jsep_transport_collection.h" + +#include +#include +#include +#include +#include + +#include "p2p/base/p2p_constants.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +void BundleManager::Update(const cricket::SessionDescription* description, + SdpType type) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + // Rollbacks should call Rollback, not Update. + RTC_DCHECK(type != SdpType::kRollback); + bool bundle_groups_changed = false; + // TODO(bugs.webrtc.org/3349): Do this for kPrAnswer as well. To make this + // work, we also need to make sure PRANSWERs don't call + // MaybeDestroyJsepTransport, because the final answer may need the destroyed + // transport if it changes the BUNDLE group. + if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle || + type == SdpType::kAnswer) { + // If our policy is "max-bundle" or this is an answer, update all bundle + // groups. + bundle_groups_changed = true; + bundle_groups_.clear(); + for (const cricket::ContentGroup* new_bundle_group : + description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) { + bundle_groups_.push_back( + std::make_unique(*new_bundle_group)); + RTC_DLOG(LS_VERBOSE) << "Establishing bundle group " + << new_bundle_group->ToString(); + } + } else if (type == SdpType::kOffer) { + // If this is an offer, update existing bundle groups. + // We do this because as per RFC 8843, section 7.3.2, the answerer cannot + // remove an m= section from an existing BUNDLE group without rejecting it. + // Thus any m= sections added to a BUNDLE group in this offer can + // preemptively start using the bundled transport, as there is no possible + // non-bundled fallback. + for (const cricket::ContentGroup* new_bundle_group : + description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) { + // Attempt to find a matching existing group. + for (const std::string& mid : new_bundle_group->content_names()) { + auto it = established_bundle_groups_by_mid_.find(mid); + if (it != established_bundle_groups_by_mid_.end()) { + *it->second = *new_bundle_group; + bundle_groups_changed = true; + RTC_DLOG(LS_VERBOSE) + << "Establishing bundle group " << new_bundle_group->ToString(); + break; + } + } + } + } + if (bundle_groups_changed) { + RefreshEstablishedBundleGroupsByMid(); + } +} + +const cricket::ContentGroup* BundleManager::LookupGroupByMid( + const std::string& mid) const { + auto it = established_bundle_groups_by_mid_.find(mid); + return it != established_bundle_groups_by_mid_.end() ? it->second : nullptr; +} +bool BundleManager::IsFirstMidInGroup(const std::string& mid) const { + auto group = LookupGroupByMid(mid); + if (!group) { + return true; // Unbundled MIDs are considered group leaders + } + return mid == *(group->FirstContentName()); +} + +cricket::ContentGroup* BundleManager::LookupGroupByMid(const std::string& mid) { + auto it = established_bundle_groups_by_mid_.find(mid); + return it != established_bundle_groups_by_mid_.end() ? it->second : nullptr; +} + +void BundleManager::DeleteMid(const cricket::ContentGroup* bundle_group, + const std::string& mid) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_LOG(LS_VERBOSE) << "Deleting mid " << mid << " from bundle group " + << bundle_group->ToString(); + // Remove the rejected content from the `bundle_group`. + // The const pointer arg is used to identify the group, we verify + // it before we use it to make a modification. + auto bundle_group_it = std::find_if( + bundle_groups_.begin(), bundle_groups_.end(), + [bundle_group](std::unique_ptr& group) { + return bundle_group == group.get(); + }); + RTC_DCHECK(bundle_group_it != bundle_groups_.end()); + (*bundle_group_it)->RemoveContentName(mid); + established_bundle_groups_by_mid_.erase( + established_bundle_groups_by_mid_.find(mid)); +} + +void BundleManager::DeleteGroup(const cricket::ContentGroup* bundle_group) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DLOG(LS_VERBOSE) << "Deleting bundle group " << bundle_group->ToString(); + + auto bundle_group_it = std::find_if( + bundle_groups_.begin(), bundle_groups_.end(), + [bundle_group](std::unique_ptr& group) { + return bundle_group == group.get(); + }); + RTC_DCHECK(bundle_group_it != bundle_groups_.end()); + auto mid_list = (*bundle_group_it)->content_names(); + for (const auto& content_name : mid_list) { + DeleteMid(bundle_group, content_name); + } + bundle_groups_.erase(bundle_group_it); +} + +void BundleManager::Rollback() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + bundle_groups_.clear(); + for (const auto& bundle_group : stable_bundle_groups_) { + bundle_groups_.push_back( + std::make_unique(*bundle_group)); + } + RefreshEstablishedBundleGroupsByMid(); +} + +void BundleManager::Commit() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + stable_bundle_groups_.clear(); + for (const auto& bundle_group : bundle_groups_) { + stable_bundle_groups_.push_back( + std::make_unique(*bundle_group)); + } +} + +void BundleManager::RefreshEstablishedBundleGroupsByMid() { + established_bundle_groups_by_mid_.clear(); + for (const auto& bundle_group : bundle_groups_) { + for (const std::string& content_name : bundle_group->content_names()) { + established_bundle_groups_by_mid_[content_name] = bundle_group.get(); + } + } +} + +void JsepTransportCollection::RegisterTransport( + const std::string& mid, + std::unique_ptr transport) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + SetTransportForMid(mid, transport.get()); + jsep_transports_by_name_[mid] = std::move(transport); + RTC_DCHECK(IsConsistent()); +} + +std::vector JsepTransportCollection::Transports() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + std::vector result; + for (auto& kv : jsep_transports_by_name_) { + result.push_back(kv.second.get()); + } + return result; +} + +std::vector +JsepTransportCollection::ActiveTransports() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + std::set transports; + for (const auto& kv : mid_to_transport_) { + transports.insert(kv.second); + } + return std::vector(transports.begin(), + transports.end()); +} + +void JsepTransportCollection::DestroyAllTransports() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + for (const auto& jsep_transport : jsep_transports_by_name_) { + map_change_callback_(jsep_transport.first, nullptr); + } + jsep_transports_by_name_.clear(); + RTC_DCHECK(IsConsistent()); +} + +const cricket::JsepTransport* JsepTransportCollection::GetTransportByName( + const std::string& transport_name) const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + auto it = jsep_transports_by_name_.find(transport_name); + return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get(); +} + +cricket::JsepTransport* JsepTransportCollection::GetTransportByName( + const std::string& transport_name) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + auto it = jsep_transports_by_name_.find(transport_name); + return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get(); +} + +cricket::JsepTransport* JsepTransportCollection::GetTransportForMid( + const std::string& mid) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + auto it = mid_to_transport_.find(mid); + return it == mid_to_transport_.end() ? nullptr : it->second; +} + +const cricket::JsepTransport* JsepTransportCollection::GetTransportForMid( + const std::string& mid) const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + auto it = mid_to_transport_.find(mid); + return it == mid_to_transport_.end() ? nullptr : it->second; +} + +cricket::JsepTransport* JsepTransportCollection::GetTransportForMid( + absl::string_view mid) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + // TODO(hta): should be a better way. + auto it = mid_to_transport_.find(std::string(mid)); + return it == mid_to_transport_.end() ? nullptr : it->second; +} + +const cricket::JsepTransport* JsepTransportCollection::GetTransportForMid( + absl::string_view mid) const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + // TODO(hta): Should be a better way + auto it = mid_to_transport_.find(std::string(mid)); + return it == mid_to_transport_.end() ? nullptr : it->second; +} + +bool JsepTransportCollection::SetTransportForMid( + const std::string& mid, + cricket::JsepTransport* jsep_transport) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DCHECK(jsep_transport); + + auto it = mid_to_transport_.find(mid); + if (it != mid_to_transport_.end() && it->second == jsep_transport) + return true; + + // The map_change_callback must be called before destroying the + // transport, because it removes references to the transport + // in the RTP demuxer. + bool result = map_change_callback_(mid, jsep_transport); + + if (it == mid_to_transport_.end()) { + mid_to_transport_.insert(std::make_pair(mid, jsep_transport)); + } else { + auto old_transport = it->second; + it->second = jsep_transport; + MaybeDestroyJsepTransport(old_transport); + } + RTC_DCHECK(IsConsistent()); + return result; +} + +void JsepTransportCollection::RemoveTransportForMid(const std::string& mid) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + RTC_DCHECK(IsConsistent()); + bool ret = map_change_callback_(mid, nullptr); + // Calling OnTransportChanged with nullptr should always succeed, since it is + // only expected to fail when adding media to a transport (not removing). + RTC_DCHECK(ret); + + auto old_transport = GetTransportForMid(mid); + if (old_transport) { + mid_to_transport_.erase(mid); + MaybeDestroyJsepTransport(old_transport); + } + RTC_DCHECK(IsConsistent()); +} + +bool JsepTransportCollection::RollbackTransports() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + bool ret = true; + // First, remove any new mid->transport mappings. + for (const auto& kv : mid_to_transport_) { + if (stable_mid_to_transport_.count(kv.first) == 0) { + ret = ret && map_change_callback_(kv.first, nullptr); + } + } + // Next, restore old mappings. + for (const auto& kv : stable_mid_to_transport_) { + auto it = mid_to_transport_.find(kv.first); + if (it == mid_to_transport_.end() || it->second != kv.second) { + ret = ret && map_change_callback_(kv.first, kv.second); + } + } + mid_to_transport_ = stable_mid_to_transport_; + // Moving a transport back to mid_to_transport_ means it's now included in + // the aggregate state if it wasn't previously. + state_change_callback_(); + DestroyUnusedTransports(); + RTC_DCHECK(IsConsistent()); + return ret; +} + +void JsepTransportCollection::CommitTransports() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + stable_mid_to_transport_ = mid_to_transport_; + DestroyUnusedTransports(); + RTC_DCHECK(IsConsistent()); +} + +bool JsepTransportCollection::TransportInUse( + cricket::JsepTransport* jsep_transport) const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + for (const auto& kv : mid_to_transport_) { + if (kv.second == jsep_transport) { + return true; + } + } + return false; +} + +bool JsepTransportCollection::TransportNeededForRollback( + cricket::JsepTransport* jsep_transport) const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + for (const auto& kv : stable_mid_to_transport_) { + if (kv.second == jsep_transport) { + return true; + } + } + return false; +} + +void JsepTransportCollection::MaybeDestroyJsepTransport( + cricket::JsepTransport* transport) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + // Don't destroy the JsepTransport if there are still media sections referring + // to it, or if it will be needed in case of rollback. + if (TransportInUse(transport)) { + return; + } + // If this transport is needed for rollback, don't destroy it yet, but make + // sure the aggregate state is updated since this transport is no longer + // included in it. + if (TransportNeededForRollback(transport)) { + state_change_callback_(); + return; + } + for (const auto& it : jsep_transports_by_name_) { + if (it.second.get() == transport) { + jsep_transports_by_name_.erase(it.first); + state_change_callback_(); + break; + } + } + RTC_DCHECK(IsConsistent()); +} + +void JsepTransportCollection::DestroyUnusedTransports() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + bool need_state_change_callback = false; + auto it = jsep_transports_by_name_.begin(); + while (it != jsep_transports_by_name_.end()) { + if (TransportInUse(it->second.get()) || + TransportNeededForRollback(it->second.get())) { + ++it; + } else { + it = jsep_transports_by_name_.erase(it); + need_state_change_callback = true; + } + } + if (need_state_change_callback) { + state_change_callback_(); + } +} + +bool JsepTransportCollection::IsConsistent() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + for (const auto& it : jsep_transports_by_name_) { + if (!TransportInUse(it.second.get()) && + !TransportNeededForRollback(it.second.get())) { + RTC_LOG(LS_ERROR) << "Transport registered with mid " << it.first + << " is not in use, transport " << it.second.get(); + return false; + } + } + return true; +} + +} // namespace webrtc -- cgit v1.2.3