Introduce StopStandardAsync in RtpTransceiver

Returns a task compatible with ScopedOperationsBatcher to avoid
BlockingCall in UpdateTransceiversAndDataChannels.

Bug: webrtc:42222804
Change-Id: I3289c44135109d69e5fc30687be017f12de96207
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/463260
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47474}
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index b0e736e..fe47e3a 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -1089,6 +1089,28 @@
   return RTCError::OK();
 }
 
+ScopedOperationsBatcher::BatchTaskWithFinalizer
+RtpTransceiver::StopStandardAsync() {
+  RTC_DCHECK_RUN_ON(thread_);
+  RTC_DCHECK(unified_plan_);
+
+  if (stopping_) {
+    return nullptr;
+  }
+
+  auto stop_task = GetStopSendingAndReceiving();
+
+  return [this, stop_task = std::move(stop_task)]() mutable
+             -> RTCErrorOr<ScopedOperationsBatcher::FinalizerTask> {
+    RTC_DCHECK_RUN_ON(context()->worker_thread());
+    std::move(stop_task)();
+    return ScopedOperationsBatcher::FinalizerTask([this]() {
+      RTC_DCHECK_RUN_ON(thread_);
+      on_negotiation_needed_();
+    });
+  };
+}
+
 void RtpTransceiver::StopInternal() {
   RTC_DCHECK_RUN_ON(thread_);
   auto stop_task = GetStopTransceiverProcedure();
diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h
index e455e3c..e5ace43 100644
--- a/pc/rtp_transceiver.h
+++ b/pc/rtp_transceiver.h
@@ -55,6 +55,7 @@
 #include "pc/rtp_sender.h"
 #include "pc/rtp_sender_proxy.h"
 #include "pc/rtp_transport_internal.h"
+#include "pc/scoped_operations_batcher.h"
 #include "pc/session_description.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/system/plan_b_only.h"
@@ -331,6 +332,11 @@
   [[nodiscard]] absl_nullable absl::AnyInvocable<void() &&>
   GetStopTransceiverProcedure();
 
+  // Returns a task that stops the transceiver.
+  // The task must be executed on the worker thread.
+  // This is used by SdpOfferAnswerHandler to batch worker thread operations.
+  ScopedOperationsBatcher::BatchTaskWithFinalizer StopStandardAsync();
+
   // RtpTransceiverInterface implementation.
   MediaType media_type() const override;
   std::optional<std::string> mid() const override;
diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc
index 7fc773a..2505c39 100644
--- a/pc/sdp_offer_answer.cc
+++ b/pc/sdp_offer_answer.cc
@@ -4187,7 +4187,8 @@
           // transceiver stopping. But if the rejection was caused by SDP
           // munging then we need to ensure the transceiver is stopping here.
           if (!transceiver->internal()->stopping()) {
-            transceiver->internal()->StopStandard();
+            worker_tasks.AddWithFinalizer(
+                transceiver->internal()->StopStandardAsync());
           }
           RTC_DCHECK(transceiver->internal()->stopping());
         } else {