Batch DtlsTransport lookups in SdpOfferAnswerHandler

This reduces the number of blocking calls to the network thread during
SDP negotiation by fetching all DtlsTransports for affected transceivers
in a single blocking call instead of one per transceiver.

In some situations this means a significant reduction of hops to the
network thread. Example from the
RenegotiateManyVideoTransceiversAndWatchAudioDelay test:

Before: Blocking ApplyLocalDescription: total=34
After:  Blocking ApplyLocalDescription: total=19

Bug: webrtc:42222804
Change-Id: I09d205959e18ef9c4c28db937fdd58fe44274d1b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/439360
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#46551}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 00862f1..8245cd3 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -1118,6 +1118,7 @@
     ":codec_vendor",
     ":connection_context",
     ":data_channel_controller",
+    ":dtls_transport",
     ":jsep_transport_collection",
     ":jsep_transport_controller",
     ":legacy_stats_collector",
diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc
index f517378..642e352 100644
--- a/pc/sdp_offer_answer.cc
+++ b/pc/sdp_offer_answer.cc
@@ -71,6 +71,7 @@
 #include "pc/channel_interface.h"
 #include "pc/codec_vendor.h"
 #include "pc/connection_context.h"
+#include "pc/dtls_transport.h"
 #include "pc/jsep_transport_controller.h"
 #include "pc/legacy_stats_collector.h"
 #include "pc/media_options.h"
@@ -177,6 +178,36 @@
   return bundle_groups_by_mid;
 }
 
+// Helper function to look up DTLS transports for all transceivers in a single
+// blocking call to the network thread.
+flat_map<std::string, scoped_refptr<DtlsTransport>> GetDtlsTransports(
+    const TransceiverList& transceivers,
+    Thread* network_thread,
+    JsepTransportController* transport_controller) {
+  const auto& transceiver_list = transceivers.ListRef();
+  std::vector<std::string> mids_to_lookup;
+  mids_to_lookup.reserve(transceiver_list.size());
+  for (const auto& transceiver : transceiver_list) {
+    if (const auto mid = transceiver->internal()->mid()) {
+      mids_to_lookup.push_back(*mid);
+    }
+  }
+  if (mids_to_lookup.empty()) {
+    return flat_map<std::string, scoped_refptr<DtlsTransport>>();
+  }
+  return network_thread->BlockingCall([&] {
+    RTC_DCHECK_RUN_ON(network_thread);
+    std::vector<std::pair<std::string, scoped_refptr<DtlsTransport>>> entries;
+    entries.reserve(mids_to_lookup.size());
+    for (const auto& mid : mids_to_lookup) {
+      entries.emplace_back(
+          mid, transport_controller->LookupDtlsTransportByMid_n(mid));
+    }
+    return flat_map<std::string, scoped_refptr<DtlsTransport>>(
+        std::move(entries));
+  });
+}
+
 // Returns true if `new_desc` requests an ICE restart (i.e., new ufrag/pwd).
 bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc,
                               const SessionDescriptionInterface* new_desc,
@@ -1867,6 +1898,11 @@
     if (ConfiguredForMedia()) {
       std::vector<scoped_refptr<RtpTransceiverInterface>> remove_list;
       std::vector<scoped_refptr<MediaStreamInterface>> removed_streams;
+      flat_map<std::string, scoped_refptr<DtlsTransport>>
+          dtls_transports_by_mid =
+              GetDtlsTransports(*transceivers(), context_->network_thread(),
+                                transport_controller_s());
+
       for (const auto& transceiver_ext : transceivers()->List()) {
         auto transceiver = transceiver_ext->internal();
         if (transceiver->stopped()) {
@@ -1877,11 +1913,10 @@
         // Note that code paths that don't set MID won't be able to use
         // information about DTLS transports.
         if (transceiver->mid()) {
-          auto dtls_transport =
-              transport_controller_s()->LookupDtlsTransportByMid(
-                  *transceiver->mid());
-          transceiver->sender_internal()->set_transport(dtls_transport);
-          transceiver->receiver_internal()->set_transport(dtls_transport);
+          auto it = dtls_transports_by_mid.find(*transceiver->mid());
+          RTC_DCHECK(it != dtls_transports_by_mid.end());
+          transceiver->sender_internal()->set_transport(it->second);
+          transceiver->receiver_internal()->set_transport(it->second);
         }
 
         const ContentInfo* content =
@@ -2269,6 +2304,10 @@
   std::vector<scoped_refptr<RtpTransceiverInterface>> remove_list;
   std::vector<scoped_refptr<MediaStreamInterface>> added_streams;
   std::vector<scoped_refptr<MediaStreamInterface>> removed_streams;
+  flat_map<std::string, scoped_refptr<DtlsTransport>> dtls_transports_by_mid =
+      GetDtlsTransports(*transceivers(), context_->network_thread(),
+                        transport_controller_s());
+
   for (const auto& transceiver_ext : transceivers()->List()) {
     const auto transceiver = transceiver_ext->internal();
     const ContentInfo* content =
@@ -2344,11 +2383,10 @@
       transceiver->set_current_direction(local_direction);
       // 2.2.8.1.11.[3-6]: Set the transport internal slots.
       if (transceiver->mid()) {
-        auto dtls_transport =
-            transport_controller_s()->LookupDtlsTransportByMid(
-                *transceiver->mid());
-        transceiver->sender_internal()->set_transport(dtls_transport);
-        transceiver->receiver_internal()->set_transport(dtls_transport);
+        auto it = dtls_transports_by_mid.find(*transceiver->mid());
+        RTC_DCHECK(it != dtls_transports_by_mid.end());
+        transceiver->sender_internal()->set_transport(it->second);
+        transceiver->receiver_internal()->set_transport(it->second);
       }
     }
     // 2.2.8.1.12: If the media description is rejected, and transceiver is
diff --git a/pc/transceiver_list.cc b/pc/transceiver_list.cc
index 3c824ce..8b1a59e 100644
--- a/pc/transceiver_list.cc
+++ b/pc/transceiver_list.cc
@@ -55,6 +55,7 @@
 std::vector<RtpTransceiver*> TransceiverList::ListInternal() const {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   std::vector<RtpTransceiver*> internals;
+  internals.reserve(transceivers_.size());
   for (auto transceiver : transceivers_) {
     internals.push_back(transceiver->internal());
   }
diff --git a/pc/transceiver_list.h b/pc/transceiver_list.h
index a29d384..d7583ae 100644
--- a/pc/transceiver_list.h
+++ b/pc/transceiver_list.h
@@ -99,6 +99,12 @@
     return transceivers_;
   }
 
+  // Returns a const reference to the list without generating a copy.
+  const std::vector<RtpTransceiverProxyRefPtr>& ListRef() const {
+    RTC_DCHECK_RUN_ON(&sequence_checker_);
+    return transceivers_;
+  }
+
   // Returns a list of the internal() pointers of the currently active list
   // of transceivers. These raw pointers are not thread-safe, so need to
   // be consumed on the same thread.