xref: /aosp_15_r20/external/webrtc/pc/jsep_transport_collection.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright 2021 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "pc/jsep_transport_collection.h"
12 
13 #include <algorithm>
14 #include <map>
15 #include <set>
16 #include <type_traits>
17 #include <utility>
18 
19 #include "p2p/base/p2p_constants.h"
20 #include "rtc_base/logging.h"
21 
22 namespace webrtc {
23 
Update(const cricket::SessionDescription * description,SdpType type)24 void BundleManager::Update(const cricket::SessionDescription* description,
25                            SdpType type) {
26   RTC_DCHECK_RUN_ON(&sequence_checker_);
27   // Rollbacks should call Rollback, not Update.
28   RTC_DCHECK(type != SdpType::kRollback);
29   bool bundle_groups_changed = false;
30   // TODO(bugs.webrtc.org/3349): Do this for kPrAnswer as well. To make this
31   // work, we also need to make sure PRANSWERs don't call
32   // MaybeDestroyJsepTransport, because the final answer may need the destroyed
33   // transport if it changes the BUNDLE group.
34   if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle ||
35       type == SdpType::kAnswer) {
36     // If our policy is "max-bundle" or this is an answer, update all bundle
37     // groups.
38     bundle_groups_changed = true;
39     bundle_groups_.clear();
40     for (const cricket::ContentGroup* new_bundle_group :
41          description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) {
42       bundle_groups_.push_back(
43           std::make_unique<cricket::ContentGroup>(*new_bundle_group));
44       RTC_DLOG(LS_VERBOSE) << "Establishing bundle group "
45                            << new_bundle_group->ToString();
46     }
47   } else if (type == SdpType::kOffer) {
48     // If this is an offer, update existing bundle groups.
49     // We do this because as per RFC 8843, section 7.3.2, the answerer cannot
50     // remove an m= section from an existing BUNDLE group without rejecting it.
51     // Thus any m= sections added to a BUNDLE group in this offer can
52     // preemptively start using the bundled transport, as there is no possible
53     // non-bundled fallback.
54     for (const cricket::ContentGroup* new_bundle_group :
55          description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) {
56       // Attempt to find a matching existing group.
57       for (const std::string& mid : new_bundle_group->content_names()) {
58         auto it = established_bundle_groups_by_mid_.find(mid);
59         if (it != established_bundle_groups_by_mid_.end()) {
60           *it->second = *new_bundle_group;
61           bundle_groups_changed = true;
62           RTC_DLOG(LS_VERBOSE)
63               << "Establishing bundle group " << new_bundle_group->ToString();
64           break;
65         }
66       }
67     }
68   }
69   if (bundle_groups_changed) {
70     RefreshEstablishedBundleGroupsByMid();
71   }
72 }
73 
LookupGroupByMid(const std::string & mid) const74 const cricket::ContentGroup* BundleManager::LookupGroupByMid(
75     const std::string& mid) const {
76   auto it = established_bundle_groups_by_mid_.find(mid);
77   return it != established_bundle_groups_by_mid_.end() ? it->second : nullptr;
78 }
IsFirstMidInGroup(const std::string & mid) const79 bool BundleManager::IsFirstMidInGroup(const std::string& mid) const {
80   auto group = LookupGroupByMid(mid);
81   if (!group) {
82     return true;  // Unbundled MIDs are considered group leaders
83   }
84   return mid == *(group->FirstContentName());
85 }
86 
LookupGroupByMid(const std::string & mid)87 cricket::ContentGroup* BundleManager::LookupGroupByMid(const std::string& mid) {
88   auto it = established_bundle_groups_by_mid_.find(mid);
89   return it != established_bundle_groups_by_mid_.end() ? it->second : nullptr;
90 }
91 
DeleteMid(const cricket::ContentGroup * bundle_group,const std::string & mid)92 void BundleManager::DeleteMid(const cricket::ContentGroup* bundle_group,
93                               const std::string& mid) {
94   RTC_DCHECK_RUN_ON(&sequence_checker_);
95   RTC_LOG(LS_VERBOSE) << "Deleting mid " << mid << " from bundle group "
96                       << bundle_group->ToString();
97   // Remove the rejected content from the `bundle_group`.
98   // The const pointer arg is used to identify the group, we verify
99   // it before we use it to make a modification.
100   auto bundle_group_it = std::find_if(
101       bundle_groups_.begin(), bundle_groups_.end(),
102       [bundle_group](std::unique_ptr<cricket::ContentGroup>& group) {
103         return bundle_group == group.get();
104       });
105   RTC_DCHECK(bundle_group_it != bundle_groups_.end());
106   (*bundle_group_it)->RemoveContentName(mid);
107   established_bundle_groups_by_mid_.erase(
108       established_bundle_groups_by_mid_.find(mid));
109 }
110 
DeleteGroup(const cricket::ContentGroup * bundle_group)111 void BundleManager::DeleteGroup(const cricket::ContentGroup* bundle_group) {
112   RTC_DCHECK_RUN_ON(&sequence_checker_);
113   RTC_DLOG(LS_VERBOSE) << "Deleting bundle group " << bundle_group->ToString();
114 
115   auto bundle_group_it = std::find_if(
116       bundle_groups_.begin(), bundle_groups_.end(),
117       [bundle_group](std::unique_ptr<cricket::ContentGroup>& group) {
118         return bundle_group == group.get();
119       });
120   RTC_DCHECK(bundle_group_it != bundle_groups_.end());
121   auto mid_list = (*bundle_group_it)->content_names();
122   for (const auto& content_name : mid_list) {
123     DeleteMid(bundle_group, content_name);
124   }
125   bundle_groups_.erase(bundle_group_it);
126 }
127 
Rollback()128 void BundleManager::Rollback() {
129   RTC_DCHECK_RUN_ON(&sequence_checker_);
130   bundle_groups_.clear();
131   for (const auto& bundle_group : stable_bundle_groups_) {
132     bundle_groups_.push_back(
133         std::make_unique<cricket::ContentGroup>(*bundle_group));
134   }
135   RefreshEstablishedBundleGroupsByMid();
136 }
137 
Commit()138 void BundleManager::Commit() {
139   RTC_DCHECK_RUN_ON(&sequence_checker_);
140   stable_bundle_groups_.clear();
141   for (const auto& bundle_group : bundle_groups_) {
142     stable_bundle_groups_.push_back(
143         std::make_unique<cricket::ContentGroup>(*bundle_group));
144   }
145 }
146 
RefreshEstablishedBundleGroupsByMid()147 void BundleManager::RefreshEstablishedBundleGroupsByMid() {
148   established_bundle_groups_by_mid_.clear();
149   for (const auto& bundle_group : bundle_groups_) {
150     for (const std::string& content_name : bundle_group->content_names()) {
151       established_bundle_groups_by_mid_[content_name] = bundle_group.get();
152     }
153   }
154 }
155 
RegisterTransport(const std::string & mid,std::unique_ptr<cricket::JsepTransport> transport)156 void JsepTransportCollection::RegisterTransport(
157     const std::string& mid,
158     std::unique_ptr<cricket::JsepTransport> transport) {
159   RTC_DCHECK_RUN_ON(&sequence_checker_);
160   SetTransportForMid(mid, transport.get());
161   jsep_transports_by_name_[mid] = std::move(transport);
162   RTC_DCHECK(IsConsistent());
163 }
164 
Transports()165 std::vector<cricket::JsepTransport*> JsepTransportCollection::Transports() {
166   RTC_DCHECK_RUN_ON(&sequence_checker_);
167   std::vector<cricket::JsepTransport*> result;
168   for (auto& kv : jsep_transports_by_name_) {
169     result.push_back(kv.second.get());
170   }
171   return result;
172 }
173 
174 std::vector<cricket::JsepTransport*>
ActiveTransports()175 JsepTransportCollection::ActiveTransports() {
176   RTC_DCHECK_RUN_ON(&sequence_checker_);
177   std::set<cricket::JsepTransport*> transports;
178   for (const auto& kv : mid_to_transport_) {
179     transports.insert(kv.second);
180   }
181   return std::vector<cricket::JsepTransport*>(transports.begin(),
182                                               transports.end());
183 }
184 
DestroyAllTransports()185 void JsepTransportCollection::DestroyAllTransports() {
186   RTC_DCHECK_RUN_ON(&sequence_checker_);
187   for (const auto& jsep_transport : jsep_transports_by_name_) {
188     map_change_callback_(jsep_transport.first, nullptr);
189   }
190   jsep_transports_by_name_.clear();
191   RTC_DCHECK(IsConsistent());
192 }
193 
GetTransportByName(const std::string & transport_name) const194 const cricket::JsepTransport* JsepTransportCollection::GetTransportByName(
195     const std::string& transport_name) const {
196   RTC_DCHECK_RUN_ON(&sequence_checker_);
197   auto it = jsep_transports_by_name_.find(transport_name);
198   return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
199 }
200 
GetTransportByName(const std::string & transport_name)201 cricket::JsepTransport* JsepTransportCollection::GetTransportByName(
202     const std::string& transport_name) {
203   RTC_DCHECK_RUN_ON(&sequence_checker_);
204   auto it = jsep_transports_by_name_.find(transport_name);
205   return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
206 }
207 
GetTransportForMid(const std::string & mid)208 cricket::JsepTransport* JsepTransportCollection::GetTransportForMid(
209     const std::string& mid) {
210   RTC_DCHECK_RUN_ON(&sequence_checker_);
211   auto it = mid_to_transport_.find(mid);
212   return it == mid_to_transport_.end() ? nullptr : it->second;
213 }
214 
GetTransportForMid(const std::string & mid) const215 const cricket::JsepTransport* JsepTransportCollection::GetTransportForMid(
216     const std::string& mid) const {
217   RTC_DCHECK_RUN_ON(&sequence_checker_);
218   auto it = mid_to_transport_.find(mid);
219   return it == mid_to_transport_.end() ? nullptr : it->second;
220 }
221 
GetTransportForMid(absl::string_view mid)222 cricket::JsepTransport* JsepTransportCollection::GetTransportForMid(
223     absl::string_view mid) {
224   RTC_DCHECK_RUN_ON(&sequence_checker_);
225   // TODO(hta): should be a better way.
226   auto it = mid_to_transport_.find(std::string(mid));
227   return it == mid_to_transport_.end() ? nullptr : it->second;
228 }
229 
GetTransportForMid(absl::string_view mid) const230 const cricket::JsepTransport* JsepTransportCollection::GetTransportForMid(
231     absl::string_view mid) const {
232   RTC_DCHECK_RUN_ON(&sequence_checker_);
233   // TODO(hta): Should be a better way
234   auto it = mid_to_transport_.find(std::string(mid));
235   return it == mid_to_transport_.end() ? nullptr : it->second;
236 }
237 
SetTransportForMid(const std::string & mid,cricket::JsepTransport * jsep_transport)238 bool JsepTransportCollection::SetTransportForMid(
239     const std::string& mid,
240     cricket::JsepTransport* jsep_transport) {
241   RTC_DCHECK_RUN_ON(&sequence_checker_);
242   RTC_DCHECK(jsep_transport);
243 
244   auto it = mid_to_transport_.find(mid);
245   if (it != mid_to_transport_.end() && it->second == jsep_transport)
246     return true;
247 
248   // The map_change_callback must be called before destroying the
249   // transport, because it removes references to the transport
250   // in the RTP demuxer.
251   bool result = map_change_callback_(mid, jsep_transport);
252 
253   if (it == mid_to_transport_.end()) {
254     mid_to_transport_.insert(std::make_pair(mid, jsep_transport));
255   } else {
256     auto old_transport = it->second;
257     it->second = jsep_transport;
258     MaybeDestroyJsepTransport(old_transport);
259   }
260   RTC_DCHECK(IsConsistent());
261   return result;
262 }
263 
RemoveTransportForMid(const std::string & mid)264 void JsepTransportCollection::RemoveTransportForMid(const std::string& mid) {
265   RTC_DCHECK_RUN_ON(&sequence_checker_);
266   RTC_DCHECK(IsConsistent());
267   bool ret = map_change_callback_(mid, nullptr);
268   // Calling OnTransportChanged with nullptr should always succeed, since it is
269   // only expected to fail when adding media to a transport (not removing).
270   RTC_DCHECK(ret);
271 
272   auto old_transport = GetTransportForMid(mid);
273   if (old_transport) {
274     mid_to_transport_.erase(mid);
275     MaybeDestroyJsepTransport(old_transport);
276   }
277   RTC_DCHECK(IsConsistent());
278 }
279 
RollbackTransports()280 bool JsepTransportCollection::RollbackTransports() {
281   RTC_DCHECK_RUN_ON(&sequence_checker_);
282   bool ret = true;
283   // First, remove any new mid->transport mappings.
284   for (const auto& kv : mid_to_transport_) {
285     if (stable_mid_to_transport_.count(kv.first) == 0) {
286       ret = ret && map_change_callback_(kv.first, nullptr);
287     }
288   }
289   // Next, restore old mappings.
290   for (const auto& kv : stable_mid_to_transport_) {
291     auto it = mid_to_transport_.find(kv.first);
292     if (it == mid_to_transport_.end() || it->second != kv.second) {
293       ret = ret && map_change_callback_(kv.first, kv.second);
294     }
295   }
296   mid_to_transport_ = stable_mid_to_transport_;
297   // Moving a transport back to mid_to_transport_ means it's now included in
298   // the aggregate state if it wasn't previously.
299   state_change_callback_();
300   DestroyUnusedTransports();
301   RTC_DCHECK(IsConsistent());
302   return ret;
303 }
304 
CommitTransports()305 void JsepTransportCollection::CommitTransports() {
306   RTC_DCHECK_RUN_ON(&sequence_checker_);
307   stable_mid_to_transport_ = mid_to_transport_;
308   DestroyUnusedTransports();
309   RTC_DCHECK(IsConsistent());
310 }
311 
TransportInUse(cricket::JsepTransport * jsep_transport) const312 bool JsepTransportCollection::TransportInUse(
313     cricket::JsepTransport* jsep_transport) const {
314   RTC_DCHECK_RUN_ON(&sequence_checker_);
315   for (const auto& kv : mid_to_transport_) {
316     if (kv.second == jsep_transport) {
317       return true;
318     }
319   }
320   return false;
321 }
322 
TransportNeededForRollback(cricket::JsepTransport * jsep_transport) const323 bool JsepTransportCollection::TransportNeededForRollback(
324     cricket::JsepTransport* jsep_transport) const {
325   RTC_DCHECK_RUN_ON(&sequence_checker_);
326   for (const auto& kv : stable_mid_to_transport_) {
327     if (kv.second == jsep_transport) {
328       return true;
329     }
330   }
331   return false;
332 }
333 
MaybeDestroyJsepTransport(cricket::JsepTransport * transport)334 void JsepTransportCollection::MaybeDestroyJsepTransport(
335     cricket::JsepTransport* transport) {
336   RTC_DCHECK_RUN_ON(&sequence_checker_);
337   // Don't destroy the JsepTransport if there are still media sections referring
338   // to it, or if it will be needed in case of rollback.
339   if (TransportInUse(transport)) {
340     return;
341   }
342   // If this transport is needed for rollback, don't destroy it yet, but make
343   // sure the aggregate state is updated since this transport is no longer
344   // included in it.
345   if (TransportNeededForRollback(transport)) {
346     state_change_callback_();
347     return;
348   }
349   for (const auto& it : jsep_transports_by_name_) {
350     if (it.second.get() == transport) {
351       jsep_transports_by_name_.erase(it.first);
352       state_change_callback_();
353       break;
354     }
355   }
356   RTC_DCHECK(IsConsistent());
357 }
358 
DestroyUnusedTransports()359 void JsepTransportCollection::DestroyUnusedTransports() {
360   RTC_DCHECK_RUN_ON(&sequence_checker_);
361   bool need_state_change_callback = false;
362   auto it = jsep_transports_by_name_.begin();
363   while (it != jsep_transports_by_name_.end()) {
364     if (TransportInUse(it->second.get()) ||
365         TransportNeededForRollback(it->second.get())) {
366       ++it;
367     } else {
368       it = jsep_transports_by_name_.erase(it);
369       need_state_change_callback = true;
370     }
371   }
372   if (need_state_change_callback) {
373     state_change_callback_();
374   }
375 }
376 
IsConsistent()377 bool JsepTransportCollection::IsConsistent() {
378   RTC_DCHECK_RUN_ON(&sequence_checker_);
379   for (const auto& it : jsep_transports_by_name_) {
380     if (!TransportInUse(it.second.get()) &&
381         !TransportNeededForRollback(it.second.get())) {
382       RTC_LOG(LS_ERROR) << "Transport registered with mid " << it.first
383                         << " is not in use, transport " << it.second.get();
384       return false;
385     }
386   }
387   return true;
388 }
389 
390 }  // namespace webrtc
391