Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/libwebrtc/pc/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 58 kB image not shown  

Quelle  rtp_transceiver.cc

  Sprache: C
 

/*
 *  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 <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <utility>
#include <vector>

#include "absl/algorithm/container.h"
#include "absl/base/nullability.h"
#include "absl/functional/any_invocable.h"
#include "absl/functional/bind_front.h"
#include "absl/strings/string_view.h"
#include "api/audio_options.h"
#include "api/crypto/crypto_options.h"
#include "api/environment/environment.h"
#include "api/jsep.h"
#include "api/make_ref_counted.h"
#include "api/media_stream_interface.h"
#include "api/media_types.h"
#include "api/rtc_error.h"
#include "api/rtp_parameters.h"
#include "api/rtp_receiver_interface.h"
#include "api/rtp_sender_interface.h"
#include "api/rtp_transceiver_direction.h"
#include "api/scoped_refptr.h"
#include "api/sequence_checker.h"
#include "api/task_queue/pending_task_safety_flag.h"
#include "api/task_queue/task_queue_base.h"
#include "api/video/video_bitrate_allocator_factory.h"
#include "api/video_codecs/scalability_mode.h"
#include "call/call.h"
#include "media/base/codec.h"
#include "media/base/codec_comparators.h"
#include "media/base/media_channel.h"
#include "media/base/media_config.h"
#include "media/base/media_engine.h"
#include "media/base/stream_params.h"
#include "pc/audio_rtp_receiver.h"
#include "pc/channel.h"
#include "pc/channel_interface.h"
#include "pc/codec_vendor.h"
#include "pc/connection_context.h"
#include "pc/dtls_transport.h"
#include "pc/legacy_stats_collector_interface.h"
#include "pc/rtp_media_utils.h"
#include "pc/rtp_receiver.h"
#include "pc/rtp_receiver_proxy.h"
#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 "pc/simulcast_description.h"
#include "pc/video_rtp_receiver.h"
#include "rtc_base/checks.h"
#include "rtc_base/crypto_random.h"
#include "rtc_base/logging.h"
#include "rtc_base/network_route.h"
#include "rtc_base/system/plan_b_only.h"
#include "rtc_base/thread.h"

namespace webrtc {
namespace {

bool HasAnyMediaCodec(const std::vector<RtpCodecCapability>& codecs) {
  return absl::c_any_of(codecs, [](const RtpCodecCapability& codec) {
    return codec.IsMediaCodec();
  });
}

RTCError VerifyCodecPreferences(const std::vector<RtpCodecCapability>& codecs,
                                const std::vector<Codec>& send_codecs,
                                const std::vector<Codec>& recv_codecs) {
  // `codec_capabilities` is the union of `send_codecs` and `recv_codecs`.
  std::vector<Codec> codec_capabilities;
  codec_capabilities.reserve(send_codecs.size() + recv_codecs.size());
  codec_capabilities.insert(codec_capabilities.end(), send_codecs.begin(),
                            send_codecs.end());
  codec_capabilities.insert(codec_capabilities.end(), recv_codecs.begin(),
                            recv_codecs.end());
  // If a media codec is not recognized from `codec_capabilities`, throw
  // InvalidModificationError.
  if (!absl::c_all_of(codecs, [&codec_capabilities](
                                  const RtpCodecCapability& codec) {
        return !codec.IsMediaCodec() ||
               absl::c_any_of(codec_capabilities,
                              [&codec](const Codec& codec_capability) {
                                return IsSameRtpCodec(codec_capability, codec);
                              });
      })) {
    return LOG_ERROR(RTCError::InvalidModification()
                     << "Invalid codec preferences: Missing codec from codec "
                        "capabilities.");
  }
  // If `codecs` only contains entries for RTX, RED, FEC or Comfort Noise, throw
  // InvalidModificationError.
  if (!HasAnyMediaCodec(codecs)) {
    return LOG_ERROR(RTCError::InvalidModification()
                     << "Invalid codec preferences: codec list must have a non "
                        "RTX, RED, FEC or Comfort Noise entry.");
  }
  return RTCError::OK();
}

// Set default header extensions depending on whether simulcast/SVC is used.
void ConfigureExtraVideoHeaderExtensions(
    const std::vector<RtpEncodingParameters>& encodings,
    std::vector<RtpHeaderExtensionCapability>& extensions) {
  bool uses_simulcast = encodings.size() > 1;
  bool uses_svc = !encodings.empty() &&
                  encodings[0].scalability_mode.has_value() &&
                  encodings[0].scalability_mode !=
                      ScalabilityModeToString(ScalabilityMode::kL1T1);
  if (!uses_simulcast && !uses_svc)
    return;

  // Enable DD and VLA extensions, can be deactivated by the API. Skip this if
  // the GFD extension was enabled via field trial for backward compatibility
  // reasons.
  bool uses_frame_descriptor =
      absl::c_any_of(extensions, [](const RtpHeaderExtensionCapability& ext) {
        return ext.uri == RtpExtension::kGenericFrameDescriptorUri00 &&
               ext.direction != RtpTransceiverDirection::kStopped;
      });
  if (!uses_frame_descriptor) {
    for (RtpHeaderExtensionCapability& ext : extensions) {
      if (ext.uri == RtpExtension::kVideoLayersAllocationUri ||
          ext.uri == RtpExtension::kDependencyDescriptorUri) {
        ext.direction = RtpTransceiverDirection::kSendRecv;
      }
    }
  }
}

void ConfigureSendCodecs(CodecVendor& codec_vendor,
                         MediaType media_type,
                         RtpSenderInternal* sender) {
  sender->SetSendCodecs(media_type == MediaType::VIDEO
                            ? codec_vendor.video_send_codecs().codecs()
                            : codec_vendor.audio_send_codecs().codecs());
}

scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> CreateSender(
    MediaType media_type,
    const Environment& env,
    ConnectionContext* context,
    LegacyStatsCollectorInterface* legacy_stats,
    RtpSenderBase::SetStreamsObserver* set_streams_observer,
    absl::string_view sender_id,
    absl::AnyInvocable<RTCError()> enable_sframe_at_owner,
    MediaSendChannelInterface* media_send_channel,
    const std::vector<RtpEncodingParameters>& init_send_encodings,
    bool simulcast_rejected,
    const std::vector<SimulcastLayer>& initial_simulcast_layers) {
  if (media_type == MediaType::AUDIO) {
    return RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
        context->signaling_thread(),
        AudioRtpSender::Create(
            env, context->signaling_thread(), context->worker_thread(),
            sender_id, legacy_stats, set_streams_observer,
            std::move(enable_sframe_at_owner),
            static_cast<VoiceMediaSendChannelInterface*>(media_send_channel)));
  }
  RTC_DCHECK_EQ(media_type, MediaType::VIDEO);
  return RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
      context->signaling_thread(),
      VideoRtpSender::Create(
          env, context->signaling_thread(), context->worker_thread(), sender_id,
          set_streams_observer, std::move(enable_sframe_at_owner),
          static_cast<VideoMediaSendChannelInterface*>(media_send_channel),
          init_send_encodings, simulcast_rejected, initial_simulcast_layers));
}

void ConfigureSender(
    scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>& sender,
    MediaStreamTrackInterface* track,
    const std::vector<std::string>& stream_ids,
    const std::vector<RtpEncodingParameters>& send_encodings,
    CodecVendor& codec_vendor) {
  bool set_track_succeeded = sender->SetTrack(track);
  RTC_DCHECK(set_track_succeeded);
  auto* internal = sender->internal();
  internal->set_stream_ids(stream_ids);
  internal->set_init_send_encodings(send_encodings);
  ConfigureSendCodecs(codec_vendor, sender->media_type(), internal);
}

template <typename RtpReceiverT, typename ReceiveInterface>
scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
CreateReceiverOfType(Thread* signaling_thread,
                     Thread* worker_thread,
                     absl::string_view receiver_id,
                     MediaReceiveChannelInterface* receive_channel,
                     absl::AnyInvocable<RTCError()> enable_sframe_at_owner) {
  return RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
      signaling_thread, worker_thread,
      make_ref_counted<RtpReceiverT>(
          worker_thread, receiver_id, std::vector<std::string>(),
          std::move(enable_sframe_at_owner),
          static_cast<ReceiveInterface*>(receive_channel)));
}

scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> CreateReceiver(
    MediaType media_type,
    Thread* signaling_thread,
    Thread* worker_thread,
    absl::string_view receiver_id,
    MediaReceiveChannelInterface* receive_channel,
    absl::AnyInvocable<RTCError()> enable_sframe_at_owner) {
  if (media_type == MediaType::AUDIO) {
    return CreateReceiverOfType<AudioRtpReceiver,
                                VoiceMediaReceiveChannelInterface>(
        signaling_thread, worker_thread, receiver_id, receive_channel,
        std::move(enable_sframe_at_owner));
  }
  RTC_DCHECK_EQ(media_type, MediaType::VIDEO);
  return CreateReceiverOfType<VideoRtpReceiver,
                              VideoMediaReceiveChannelInterface>(
      signaling_thread, worker_thread, receiver_id, receive_channel,
      std::move(enable_sframe_at_owner));
}

std::pair<std::unique_ptr<MediaSendChannelInterface>,
          std::unique_ptr<MediaReceiveChannelInterface>>
CreateMediaContentChannels(
    MediaType media_type,
    const Environment& env,
    MediaEngineInterface* media_engine,
    Call* call,
    const MediaConfig& media_config,
    const AudioOptions& audio_options,
    const VideoOptions& video_options,
    const CryptoOptions& crypto_options,
    VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
    VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
        video_encoder_switch_request_callback = nullptr) {
  if (media_type == MediaType::AUDIO) {
    return {media_engine->voice().CreateSendChannel(
                env, call, media_config, audio_options, crypto_options),
            media_engine->voice().CreateReceiveChannel(
                env, call, media_config, audio_options, crypto_options)};
  }
  return {media_engine->video().CreateSendChannel(
              env, call, media_config, video_options, crypto_options,
              video_bitrate_allocator_factory,
              std::move(video_encoder_switch_request_callback)),
          media_engine->video().CreateReceiveChannel(
              env, call, media_config, video_options, crypto_options)};
}

std::vector<absl::AnyInvocable<void() &&>> DetachAndGetStopTasksForSenders(
    std::vector<scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>&
        senders) {
  std::vector<absl::AnyInvocable<void() &&>> tasks;
  for (const auto& sender : senders) {
    auto task = sender->internal()->DetachTrackAndGetStopTask();
    if (task)
      tasks.push_back(std::move(task));
  }
  return tasks;
}

}  // namespace

RtpTransceiver::RtpTransceiver(const Environment& env,
                               MediaType media_type,
                               ConnectionContext* context,
                               CodecLookupHelper* codec_lookup_helper,
                               LegacyStatsCollectorInterface* legacy_stats)
    : env_(env),
      thread_(context->signaling_thread()),
      unified_plan_(false),
      media_type_(media_type),
      signaling_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue(
          /*alive=*/false,
          context->signaling_thread())),
      network_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue(
          /*alive=*/true,
          context->network_thread())),
      context_(context),
      codec_lookup_helper_(codec_lookup_helper),
      legacy_stats_(legacy_stats) {
  RTC_DCHECK(media_type == MediaType::AUDIO || media_type == MediaType::VIDEO);
  RTC_DCHECK(context_);
  RTC_DCHECK(context_->is_configured_for_media());
  RTC_DCHECK(codec_lookup_helper_);
}

RtpTransceiver::RtpTransceiver(
    const Environment& env,
    scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
    scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> receiver,
    ConnectionContext* context,
    CodecLookupHelper* codec_lookup_helper,
    std::vector<RtpHeaderExtensionCapability> header_extensions_to_negotiate,
    absl::AnyInvocable<void()> on_negotiation_needed)
    : env_(env),
      thread_(context->signaling_thread()),
      unified_plan_(true),
      media_type_(sender->media_type()),
      signaling_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue(
          /*alive=*/false,
          context->signaling_thread())),
      network_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue(
          /*alive=*/true,
          context->network_thread())),
      context_(context),
      codec_lookup_helper_(codec_lookup_helper),
      legacy_stats_(nullptr),
      header_extensions_to_negotiate_(
          std::move(header_extensions_to_negotiate)),
      on_negotiation_needed_(std::move(on_negotiation_needed)) {
  RTC_DCHECK(context_);
  RTC_DCHECK(context_->is_configured_for_media());
  RTC_DCHECK(media_type_ == MediaType::AUDIO ||
             media_type_ == MediaType::VIDEO);
  RTC_DCHECK(codec_lookup_helper_);
  RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
  RTC_DCHECK_EQ(media_type_, sender->media_type());
  RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
  auto* sender_internal = sender->internal();
  senders_.push_back(std::move(sender));
  receivers_.push_back(std::move(receiver));
  if (media_type_ == MediaType::VIDEO) {
    ConfigureExtraVideoHeaderExtensions(
        sender_internal
            ->GetParametersInternal(/*may_use_cache*/ true,
                                    /*with_all_layers=*/false)
            .encodings,
        header_extensions_to_negotiate_);
  }
  ConfigureSendCodecs(codec_vendor(), media_type_, sender_internal);
}

RtpTransceiver::RtpTransceiver(
    const Environment& env,
    Call* call,
    const MediaConfig& media_config,
    absl::string_view sender_id,
    absl::string_view receiver_id,
    MediaType media_type,
    scoped_refptr<MediaStreamTrackInterface> track,
    const std::vector<std::string>& stream_ids,
    const std::vector<RtpEncodingParameters>& init_send_encodings,
    ConnectionContext* context,
    CodecLookupHelper* codec_lookup_helper,
    LegacyStatsCollectorInterface* legacy_stats,
    RtpSenderBase::SetStreamsObserver* set_streams_observer,
    const AudioOptions& audio_options,
    const VideoOptions& video_options,
    const CryptoOptions& crypto_options,
    VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
    std::vector<RtpHeaderExtensionCapability> header_extensions_to_negotiate,
    bool simulcast_rejected,
    const std::vector<SimulcastLayer>& initial_simulcast_layers,
    ScopedOperationsBatcher& worker_tasks,
    absl::AnyInvocable<void()> on_negotiation_needed)
    : env_(env),
      thread_(context->signaling_thread()),
      unified_plan_(true),
      media_type_(media_type),
      signaling_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue(
          /*alive=*/false,
          context->signaling_thread())),
      network_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue(
          /*alive=*/true,
          context->network_thread())),
      media_engine_ref_(nullptr),
      context_(context),
      codec_lookup_helper_(codec_lookup_helper),
      legacy_stats_(legacy_stats),
      set_streams_observer_(set_streams_observer),
      header_extensions_to_negotiate_(
          std::move(header_extensions_to_negotiate)),
      on_negotiation_needed_(std::move(on_negotiation_needed)) {
  RTC_DCHECK(context_);
  RTC_DCHECK(context_->is_configured_for_media());
  RTC_DCHECK(media_type_ == MediaType::AUDIO ||
             media_type_ == MediaType::VIDEO);
  RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
  if (media_type_ == MediaType::VIDEO) {
    ConfigureExtraVideoHeaderExtensions(init_send_encodings,
                                        header_extensions_to_negotiate_);
  }

  auto encoder_switch_callback = GetEncoderSwitchRequestCallback();

  worker_tasks.AddWithFinalizer(
      [this, call, media_config, audio_options, video_options, crypto_options,
       video_bitrate_allocator_factory,
       encoder_switch_callback = std::move(encoder_switch_callback),
       sender_id = std::string(sender_id), init_send_encodings,
       simulcast_rejected, initial_simulcast_layers, track, stream_ids,
       receiver_id = std::string(
           receiver_id)]() mutable -> ScopedOperationsBatcher::FinalizerTask {
        RTC_DCHECK_RUN_ON(this->context()->worker_thread());
        auto channels = CreateMediaContentChannels(
            media_type_, env_, media_engine(), call, media_config,
            audio_options, video_options, crypto_options,
            video_bitrate_allocator_factory,
            std::move(encoder_switch_callback));
        auto sender = CreateSender(
            media_type_, env_, context_, legacy_stats_, set_streams_observer_,
            sender_id,
            absl::bind_front(&RtpTransceiver::TryToEnableSframe, this),
            channels.first.get(), init_send_encodings, simulcast_rejected,
            initial_simulcast_layers);
        return ScopedOperationsBatcher::FinalizerTask(
            [this, channels = std::move(channels), sender = std::move(sender),
             track, stream_ids, init_send_encodings, receiver_id]() mutable {
              RTC_DCHECK_RUN_ON(thread_);
              owned_send_channel_ = std::move(channels.first);
              owned_receive_channel_ = std::move(channels.second);
              senders_.push_back(std::move(sender));

              ConfigureSender(senders_.back(), track.get(), stream_ids,
                              init_send_encodings, codec_vendor());

              receivers_.push_back(CreateReceiver(
                  media_type_, context_->signaling_thread(),
                  context_->worker_thread(),
                  receiver_id.empty() ? CreateRandomUuid() : receiver_id,
                  owned_receive_channel_.get(),
                  absl::bind_front(&RtpTransceiver::TryToEnableSframe, this)));
            });
      });
}

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?";
  RTC_DCHECK(!media_engine_ref_);
  RTC_DCHECK(!owned_send_channel_);
  RTC_DCHECK(!owned_receive_channel_);
}

void RtpTransceiver::CreateChannel(
    absl::string_view mid,
    Call* call_ptr,
    const MediaConfig& media_config,
    bool srtp_required,
    CryptoOptions crypto_options,
    const AudioOptions& audio_options,
    const VideoOptions& video_options,
    VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
    absl::AnyInvocable<RtpTransportInternal*() &&> transport_lookup,
    ScopedOperationsBatcher& worker_tasks,
    ScopedOperationsBatcher& network_tasks) {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(!channel_);
  RTC_DCHECK(!mid_ || mid_.value() == mid);
  RTC_DCHECK(!stopped_);

  mid_ = mid;

  if (!signaling_thread_safety_) {
    // This code path is hit during rollback.
    signaling_thread_safety_ = PendingTaskSafetyFlag::Create();
  } else {
    // Newly constructed.
    RTC_DCHECK(!signaling_thread_safety_->alive());
    signaling_thread_safety_->SetAlive();
  }

  ChannelCallbacks callbacks;
  callbacks.on_first_packet_received =
      [thread = thread_, flag = signaling_thread_safety_,
       this](const RtpPacketReceived& packet) mutable {
        thread->PostTask(SafeTask(
            std::move(flag),
            [this, ssrc = packet.Ssrc()]() { OnFirstPacketReceived(ssrc); }));
      };
  callbacks.on_first_packet_sent =
      [thread = thread_, flag = signaling_thread_safety_, this]() mutable {
        thread->PostTask(
            SafeTask(std::move(flag), [this]() { OnFirstPacketSent(); }));
      };
  callbacks.on_packet_received =
      [this, flag = signaling_thread_safety_](const RtpPacketReceived& packet) {
        RTC_DCHECK_RUN_ON(context()->network_thread());
        OnPacketReceived(packet.Ssrc(), flag);
      };

  // 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).
  worker_tasks.AddWithFinalizer(
      [this, mid_str = std::string(mid), call_ptr, media_config, srtp_required,
       crypto_options, audio_options, video_options,
       video_bitrate_allocator_factory,
       callbacks = std::move(callbacks)]() mutable
          -> RTCErrorOr<ScopedOperationsBatcher::FinalizerTask> {
        RTC_DCHECK_RUN_ON(context()->worker_thread());

        std::unique_ptr<MediaSendChannelInterface> media_send_channel;
        std::unique_ptr<MediaReceiveChannelInterface> media_receive_channel;

        if (owned_send_channel_) {
          RTC_DCHECK(owned_receive_channel_);
          media_send_channel = std::move(owned_send_channel_);
          media_receive_channel = std::move(owned_receive_channel_);
          // Apply options to the voice channels for audio and send channel for
          // video. Note that the video options are primarily for sending.
          if (media_type() == MediaType::AUDIO) {
            media_send_channel->AsVoiceSendChannel()->SetOptions(audio_options);
            media_receive_channel->AsVoiceReceiveChannel()->SetOptions(
                audio_options);
          } else if (media_type() == MediaType::VIDEO) {
            media_send_channel->AsVideoSendChannel()->SetOptions(video_options);
          }
        } else {
          auto channels = CreateMediaContentChannels(
              media_type(), env_, media_engine(), call_ptr, media_config,
              audio_options, video_options, crypto_options,
              video_bitrate_allocator_factory,
              GetEncoderSwitchRequestCallback());
          media_send_channel = std::move(channels.first);
          media_receive_channel = std::move(channels.second);
          SetMediaChannels(media_send_channel.get(),
                           media_receive_channel.get());
        }

        std::unique_ptr<ChannelInterface> new_channel =
            std::make_unique<BaseChannel>(
                context()->worker_thread(), context()->network_thread(),
                context()->signaling_thread(), std::move(media_send_channel),
                std::move(media_receive_channel), mid_str, media_type(),
                srtp_required, crypto_options, context()->ssrc_generator(),
                std::move(callbacks));

        return ScopedOperationsBatcher::FinalizerTask(
            [this, new_channel = std::move(new_channel)]() mutable {
              RTC_DCHECK_RUN_ON(thread_);
              channel_ = std::move(new_channel);
              transport_name_ = std::nullopt;
            });
      });

  network_tasks.AddWithFinalizer(
      [this, transport_lookup = std::move(transport_lookup)]() mutable
          -> RTCErrorOr<ScopedOperationsBatcher::FinalizerTask> {
        RTC_DCHECK_RUN_ON(context()->network_thread());
        auto result = InitializeOnNetworkThread(std::move(transport_lookup));
        if (!result.ok()) {
          return result.MoveError();
        }
        std::optional<std::string> transport_name = std::move(result.value());
        return ScopedOperationsBatcher::FinalizerTask(
            [this, transport_name = std::move(transport_name)]() mutable {
              RTC_DCHECK_RUN_ON(thread_);
              transport_name_ = std::move(transport_name);
            });
      });
}

RTCError RtpTransceiver::SetChannelForTest(
    std::unique_ptr<ChannelInterface> channel,
    absl::AnyInvocable<RtpTransportInternal*() &&> 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 RTCError::InvalidState();
  }

  RTC_LOG_THREAD_BLOCK_COUNT();

  RTC_DCHECK_EQ(media_type(), channel->media_type());
  RTC_DCHECK(mid_ || channel->mid().empty());
  channel_ = std::move(channel);
  transport_name_ = std::nullopt;

  // 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.
  std::optional<std::string> transport_name;
  RTCError err = context()->network_thread()->BlockingCall(
      [&, flag = signaling_thread_safety_, channel = channel_.get()]() {
        RTC_DCHECK_RUN_ON(context()->network_thread());
        auto result = InitializeOnNetworkThread(std::move(transport_lookup));
        if (result.ok()) {
          transport_name = std::move(result.value());
        }
        return result.MoveError();
      });

  if (err.ok()) {
    transport_name_ = std::move(transport_name);
  }

  RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2);

  return err;
}

absl::AnyInvocable<void() &&> RtpTransceiver::GetClearChannelNetworkTask() {
  RTC_DCHECK_RUN_ON(thread_);
  // GetClearChannelNetworkTask must be called before GetDeleteChannelWorkerTask
  // since that's where we clear the `channel_` pointer. Perhaps we should
  // combine these into one function to avoid an ordering mistake?

  if (!channel_) {
    RTC_DCHECK(!signaling_thread_safety_ || !signaling_thread_safety_->alive());
    return nullptr;
  }

  signaling_thread_safety_->SetNotAlive();
  signaling_thread_safety_ = nullptr;

  ChannelInterface* channel = channel_.get();
  return [this, channel, flag = network_thread_safety_] {
    RTC_DCHECK_RUN_ON(context()->network_thread());
    flag->SetNotAlive();
    channel->SetRtpTransport(nullptr);
    ClearRtpTransportState();
  };
}

absl::AnyInvocable<void() &&> RtpTransceiver::GetDeleteChannelWorkerTask(
    bool stop_senders) {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(!signaling_thread_safety_ || !signaling_thread_safety_->alive())
      << "GetClearChannelNetworkTask() must be called first";

  if (!channel_) {
    return nullptr;
  }

  std::vector<absl::AnyInvocable<void() &&>> stop_sender_actions;
  if (stop_senders) {
    stop_sender_actions = DetachAndGetStopTasksForSenders(senders_);
  }

  transport_name_ = std::nullopt;

  // Ensure that channel_ is not reachable via the transceiver, but is deleted
  // only after clearing the references in senders_ and receivers_.
  return [this, channel = std::move(channel_), senders = senders_,
          receivers = receivers_,
          stop_sender_actions = std::move(stop_sender_actions)]() mutable {
    RTC_DCHECK_RUN_ON(context()->worker_thread());
    for (auto& task : stop_sender_actions) {
      std::move(task)();
    }
    ClearMediaChannelReferences();
    channel.reset();
  };
}

void RtpTransceiver::ClearChannel() {
  RTC_DCHECK_RUN_ON(thread_);
  if (!channel_) {
    return;
  }

  absl::AnyInvocable<void() &&> network_task = GetClearChannelNetworkTask();
  if (network_task) {
    context()->network_thread()->BlockingCall(
        [&] { std::move(network_task)(); });
  }

  absl::AnyInvocable<void() &&> worker_task =
      GetDeleteChannelWorkerTask(/*stop_senders=*/false);
  if (worker_task) {
    context()->worker_thread()->BlockingCall([&] { std::move(worker_task)(); });
  }
}

// RTC_RUN_ON(context()->worker_thread());
void RtpTransceiver::SetMediaChannels(MediaSendChannelInterface* send,
                                      MediaReceiveChannelInterface* receive) {
  for (const auto& sender : senders_) {
    sender->internal()->SetMediaChannel(send);
  }
  for (const auto& receiver : receivers_) {
    receiver->internal()->SetMediaChannel(receive);
  }
}
VideoMediaSendChannelInterface::EncoderSwitchRequestCallback
RtpTransceiver::GetEncoderSwitchRequestCallback() {
  if (media_type() != MediaType::VIDEO) {
    return nullptr;
  }
  RTC_DCHECK(signaling_thread_safety_);
  // Return a task that first clears the sender parameter cache on the signaling
  // thread and then posts a task to apply the codec switch changes to the
  // parameters on the worker.
  return
      [this, signaling_thread = context_->signaling_thread(),
       worker_thread = context_->worker_thread(),
       signaling_safety = signaling_thread_safety_](
          VideoMediaSendChannelInterface::EncoderSwitchRequestAction action) {
        // Called on the encoder task queue.
        signaling_thread->PostTask(SafeTask(
            signaling_safety,
            [this, worker_thread, action = std::move(action)]() mutable {
              for (const auto& sender : senders_) {
                sender->internal()->SetCachedParameters(std::nullopt);
              }
              worker_thread->PostTask([action = std::move(action)]() mutable {
                std::move(action)();
              });
            }));
      };
}

// RTC_RUN_ON(context()->worker_thread());
void RtpTransceiver::ClearMediaChannelReferences() {
  SetMediaChannels(nullptr, nullptr);
  owned_send_channel_ = nullptr;
  owned_receive_channel_ = nullptr;
  media_engine_ref_ = nullptr;
}

PLAN_B_ONLY void RtpTransceiver::AddSenderPlanB(
    scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> 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));
  ConfigureSendCodecs(codec_vendor(), media_type(), sender->internal());
  senders_.push_back(sender);
}

PLAN_B_ONLY scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>
RtpTransceiver::AddSenderPlanB(
    scoped_refptr<MediaStreamTrackInterface> track,
    absl::string_view sender_id,
    const std::vector<std::string>& stream_ids,
    const std::vector<RtpEncodingParameters>& send_encodings) {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(!stopped_);
  RTC_DCHECK(!unified_plan_);
  RTC_DCHECK(media_type_ == MediaType::AUDIO ||
             media_type_ == MediaType::VIDEO);
  context_->worker_thread()->BlockingCall([&]() mutable {
    RTC_DCHECK_RUN_ON(context()->worker_thread());
    senders_.push_back(CreateSender(
        media_type_, env_, context_, legacy_stats_, set_streams_observer_,
        sender_id, /*enable_sframe_at_owner=*/nullptr,
        channel_ ? channel_->media_send_channel() : nullptr, send_encodings,
        false, {}));
  });
  ConfigureSender(senders_.back(), track.get(), stream_ids, send_encodings,
                  codec_vendor());
  return senders_.back();
}

PLAN_B_ONLY bool RtpTransceiver::RemoveSenderPlanB(RtpSenderInterface* sender) {
  RTC_DCHECK(!unified_plan_);
  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;
}

PLAN_B_ONLY void RtpTransceiver::AddReceiverPlanB(
    scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> 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);
}

PLAN_B_ONLY bool RtpTransceiver::RemoveReceiverPlanB(
    RtpReceiverInterface* receiver) {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(!unified_plan_);
  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([&]() {
    (*it)->internal()->SetMediaChannel(nullptr);
  });

  receivers_.erase(it);
  return true;
}

scoped_refptr<RtpSenderInternal> RtpTransceiver::sender_internal() const {
  RTC_DCHECK(unified_plan_);
  RTC_CHECK_EQ(1u, senders_.size());
  return scoped_refptr<RtpSenderInternal>(senders_[0]->internal());
}

scoped_refptr<RtpReceiverInternal> RtpTransceiver::receiver_internal() const {
  RTC_DCHECK(unified_plan_);
  RTC_CHECK_EQ(1u, receivers_.size());
  return scoped_refptr<RtpReceiverInternal>(receivers_[0]->internal());
}

MediaType RtpTransceiver::media_type() const {
  return media_type_;
}

std::optional<std::string> RtpTransceiver::mid() const {
  return mid_;
}

// RTC_RUN_ON(context()->worker_thread())
MediaEngineInterface* RtpTransceiver::media_engine() {
  if (!media_engine_ref_) {
    media_engine_ref_ =
        std::make_unique<ConnectionContext::MediaEngineReference>(
            scoped_refptr<ConnectionContext>(context_));
  }
  return media_engine_ref_->media_engine();
}

void RtpTransceiver::OnFirstPacketReceived(uint32_t ssrc) {
  for (const auto& receiver : receivers_) {
    receiver->internal()->NotifyFirstPacketReceived(ssrc);
  }
}

// RTC_RUN_ON(context()->network_thread())
void RtpTransceiver::OnPacketReceived(
    uint32_t ssrc,
    scoped_refptr<PendingTaskSafetyFlag> safety) {
  if (!receptive_n_) {
    return;
  }
  if (packet_notified_after_receptive_) {
    return;
  }
  packet_notified_after_receptive_ = true;
  thread_->PostTask(SafeTask(safety, [this, ssrc]() {
    RTC_DCHECK_RUN_ON(thread_);
    if (stopping() || stopped() || !receptive_) {
      return;
    }
    for (const auto& receiver : receivers_) {
      receiver->internal()->NotifyFirstPacketReceivedAfterReceptiveChange(ssrc);
    }
  }));
}

void RtpTransceiver::OnFirstPacketSent() {
  for (const auto& sender : senders_) {
    sender->internal()->NotifyFirstPacketSent();
  }
}

scoped_refptr<RtpSenderInterface> RtpTransceiver::sender() const {
  RTC_DCHECK(unified_plan_);
  RTC_CHECK_EQ(1u, senders_.size());
  return senders_[0];
}

scoped_refptr<RtpReceiverInterface> RtpTransceiver::receiver() const {
  RTC_DCHECK(unified_plan_);
  RTC_CHECK_EQ(1u, receivers_.size());
  return receivers_[0];
}

void RtpTransceiver::set_current_direction(RtpTransceiverDirection direction) {
  if (current_direction_ == direction)
    return;
  RTC_LOG(LS_INFO) << "Changing transceiver (MID=" << mid_.value_or("<not set>")
                   << ") current direction from "
                   << (current_direction_ ? RtpTransceiverDirectionToString(
                                                *current_direction_)
                                          : "<not set>")
                   << " to " << RtpTransceiverDirectionToString(direction);
  current_direction_ = direction;
  if (RtpTransceiverDirectionHasSend(*current_direction_)) {
    has_ever_been_used_to_send_ = true;
  }
}

void RtpTransceiver::set_fired_direction(
    std::optional<RtpTransceiverDirection> 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 RtpTransceiverDirection::kStopped;

  return direction_;
}

RTCError RtpTransceiver::SetDirectionWithError(
    RtpTransceiverDirection new_direction) {
  if (unified_plan_ && stopping()) {
    return LOG_ERROR(RTCError::InvalidState()
                     << "Cannot set direction on a stopping transceiver.");
  }
  if (new_direction == direction_)
    return RTCError::OK();

  if (new_direction == RtpTransceiverDirection::kStopped) {
    return LOG_ERROR(RTCError::InvalidParameter()
                     << "The set direction 'stopped' is invalid.");
  }

  direction_ = new_direction;
  on_negotiation_needed_();

  return RTCError::OK();
}

std::optional<RtpTransceiverDirection> RtpTransceiver::current_direction()
    const {
  if (unified_plan_ && stopped())
    return RtpTransceiverDirection::kStopped;

  return current_direction_;
}

std::optional<RtpTransceiverDirection> RtpTransceiver::fired_direction() const {
  return fired_direction_;
}

RTCError RtpTransceiver::TryToEnableSframe() {
  RTC_DCHECK_RUN_ON(thread_);

  if (sframe_enabled_.has_value() && sframe_enabled_.value() == false) {
    return LOG_ERROR(RTCError::InvalidModification()
                     << "Cannot enable Sframe after it has been "
                        "disabled by a completed negotiation.");
  }

  sframe_enabled_ = true;

  on_negotiation_needed_();

  return RTCError::OK();
}

void RtpTransceiver::ApplySframeEnabled(bool sframe_enabled) {
  RTC_DCHECK_RUN_ON(thread_);
  // Cannot re-enable Sframe after it has been negotiated to disabled.
  RTC_DCHECK(!(sframe_enabled_ == false && sframe_enabled == true));

  sframe_enabled_ = sframe_enabled;

  if (sframe_enabled && channel_) {
    // TODO(bugs.webrtc.org/479862368): Enable Sframe on the media send and
    // receive channels when the encryption pipeline is implemented.
  }
}

std::optional<bool> RtpTransceiver::SframeEnabled() const {
  RTC_DCHECK_RUN_ON(thread_);
  return sframe_enabled_;
}

bool RtpTransceiver::receptive() const {
  RTC_DCHECK_RUN_ON(thread_);
  return receptive_;
}

void RtpTransceiver::set_receptive(bool receptive) {
  RTC_DCHECK_RUN_ON(thread_);
  if (receptive != receptive_) {
    receptive_ = receptive;
    context()->network_thread()->PostTask(
        SafeTask(network_thread_safety_, [this, receptive = receptive]() {
          RTC_DCHECK_RUN_ON(context()->network_thread());
          receptive_n_ = receptive;
          packet_notified_after_receptive_ = false;
        }));
  }
}

absl_nonnull absl::AnyInvocable<void() &&>
RtpTransceiver::GetStopSendingAndReceiving() {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(!stopped_);
  RTC_DCHECK(!stopping_);
  // 1. Let sender be transceiver.[[Sender]].
  // 2. Let receiver be transceiver.[[Receiver]].

  RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();

  // Signal to receiver sources that we're stopping.
  for (const auto& receiver : receivers_) {
    receiver->internal()->Stop();
  }

  // 4. Stop sending media with sender.
  // We do this *after* the media channel has been set to nullptr on the
  // worker thread to avoid each sender doing that within `Stop()`.
  // Senders will have already cleared send when the media channel was set to
  // nullptr.
  std::vector<absl::AnyInvocable<void() &&>> stop_sender_actions =
      DetachAndGetStopTasksForSenders(senders_);

  stopping_ = true;
  direction_ = RtpTransceiverDirection::kInactive;

  // 3. Send an RTCP BYE for each RTP stream that was being sent by sender, as
  // specified in [RFC3550].

  return
      [this, stop_sender_actions = std::move(stop_sender_actions)]() mutable {
        RTC_DCHECK_RUN_ON(context()->worker_thread());
        for (auto& task : stop_sender_actions) {
          std::move(task)();
        }
        ClearMediaChannelReferences();
      };
}

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.
  //    (Note: Checking for IsClosed() is implemented by the user agent).
  //
  // 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.
  auto stop_task = GetStopSendingAndReceiving();
  context_->worker_thread()->BlockingCall(
      [&]() mutable { std::move(stop_task)(); });
  on_negotiation_needed_();

  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();
  if (stop_task) {
    context_->worker_thread()->BlockingCall(
        [stop_task = std::move(stop_task)]() mutable {
          std::move(stop_task)();
        });
  }
}

absl_nullable absl::AnyInvocable<void() &&>
RtpTransceiver::GetStopTransceiverProcedure() {
  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.
  absl::AnyInvocable<void() &&> stop_task;
  if (!stopping_)
    stop_task = GetStopSendingAndReceiving();

  // 2. Set transceiver.[[Stopped]] to true.
  stopped_ = true;

  // 3. Set transceiver.[[Receptive]] to false.
  receptive_ = false;

  // 4. Set transceiver.[[CurrentDirection]] to null.
  current_direction_ = std::nullopt;

  return stop_task;
}

RTCError RtpTransceiver::SetCodecPreferences(
    std::span<RtpCodecCapability> 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();
    sendrecv_codec_preferences_.clear();
    sendonly_codec_preferences_.clear();
    recvonly_codec_preferences_.clear();
    return RTCError::OK();
  }
  // 4. Remove any duplicate values in codecs.
  std::vector<RtpCodecCapability> codecs;
  absl::c_remove_copy_if(codec_capabilities, std::back_inserter(codecs),
                         [&codecs](const RtpCodecCapability& codec) {
                           return absl::c_linear_search(codecs, codec);
                         });
  // TODO(https://crbug.com/webrtc/391530822): Move logic in
  // MediaSessionDescriptionFactory to this level.
  return UpdateCodecPreferencesCaches(codecs);
}

RTCError RtpTransceiver::UpdateCodecPreferencesCaches(
    const std::vector<RtpCodecCapability>& codecs) {
  // Get codec capabilities from media engine.
  std::vector<Codec> send_codecs, recv_codecs;
  if (media_type_ == MediaType::AUDIO) {
    send_codecs = codec_vendor().audio_send_codecs().codecs();
    recv_codecs = codec_vendor().audio_recv_codecs().codecs();
  } else if (media_type_ == MediaType::VIDEO) {
    send_codecs = codec_vendor().video_send_codecs().codecs();
    recv_codecs = codec_vendor().video_recv_codecs().codecs();
  }
  RTCError error = VerifyCodecPreferences(codecs, send_codecs, recv_codecs);
  if (!error.ok()) {
    return error;
  }
  codec_preferences_ = codecs;
  // Update the filtered views of `codec_preferences_` so that we don't have
  // to query codec capabilities when calling filtered_codec_preferences() or
  // every time the direction changes.
  sendrecv_codec_preferences_.clear();
  sendonly_codec_preferences_.clear();
  recvonly_codec_preferences_.clear();
  for (const RtpCodecCapability& codec : codec_preferences_) {
    if (!codec.IsMediaCodec()) {
      // Non-media codecs don't need to be filtered at this level.
      sendrecv_codec_preferences_.push_back(codec);
      sendonly_codec_preferences_.push_back(codec);
      recvonly_codec_preferences_.push_back(codec);
      continue;
    }
    // Is this a send codec, receive codec or both?
    bool is_send_codec =
        absl::c_any_of(send_codecs, [&codec](const Codec& send_codec) {
          return IsSameRtpCodecIgnoringLevel(send_codec, codec);
        });
    bool is_recv_codec =
        absl::c_any_of(recv_codecs, [&codec](const Codec& recv_codec) {
          return IsSameRtpCodecIgnoringLevel(recv_codec, codec);
        });
    // The codec being neither for sending or receving is not possible because
    // of prior validation by VerifyCodecPreferences().
    RTC_CHECK(is_send_codec || is_recv_codec);
    if (is_send_codec && is_recv_codec) {
      sendrecv_codec_preferences_.push_back(codec);
    }
    if (is_send_codec) {
      sendonly_codec_preferences_.push_back(codec);
    }
    if (is_recv_codec) {
      recvonly_codec_preferences_.push_back(codec);
    }
  }
  // If filtering results in an empty list this is the same as not having any
  // preferences.
  if (!HasAnyMediaCodec(sendrecv_codec_preferences_)) {
    sendrecv_codec_preferences_.clear();
  }
  if (!HasAnyMediaCodec(sendonly_codec_preferences_)) {
    sendonly_codec_preferences_.clear();
  }
  if (!HasAnyMediaCodec(recvonly_codec_preferences_)) {
    recvonly_codec_preferences_.clear();
  }
  return RTCError::OK();
}

std::vector<RtpCodecCapability> RtpTransceiver::codec_preferences() const {
  return codec_preferences_;
}

std::vector<RtpCodecCapability> RtpTransceiver::filtered_codec_preferences()
    const {
  switch (direction_) {
    case RtpTransceiverDirection::kSendRecv:
    case RtpTransceiverDirection::kInactive:
    case RtpTransceiverDirection::kStopped:
      return sendrecv_codec_preferences_;
    case RtpTransceiverDirection::kSendOnly:
      return sendonly_codec_preferences_;
    case RtpTransceiverDirection::kRecvOnly:
      return recvonly_codec_preferences_;
  }
  return codec_preferences_;
}

std::vector<RtpHeaderExtensionCapability>
RtpTransceiver::GetHeaderExtensionsToNegotiate() const {
  RTC_DCHECK_RUN_ON(thread_);
  return header_extensions_to_negotiate_;
}

std::vector<RtpHeaderExtensionCapability> ModifyCapabilitiesAccordingToHeaders(
    const std::vector<RtpHeaderExtensionCapability>& old_values,
    const std::vector<RtpExtension>& extension_list) {
  std::vector<RtpHeaderExtensionCapability> result;
  result.reserve(old_values.size());
  // Create new capability objects that start as a copy of the old values.
  for (RtpHeaderExtensionCapability capability : old_values) {
    auto negotiated = absl::c_find_if(
        extension_list, [&capability](const RtpExtension& negotiated) {
          return negotiated.uri == capability.uri;
        });
    // TODO(bugs.webrtc.org/7477): extend when header extensions support
    // direction.
    if (negotiated != extension_list.end()) {
      capability.direction = RtpTransceiverDirection::kSendRecv;
      capability.preferred_id = negotiated->id;
      capability.preferred_encrypt = negotiated->encrypt;
    } else {
      capability.direction = RtpTransceiverDirection::kStopped;
    }
    result.push_back(capability);
  }
  return result;
}
std::vector<RtpHeaderExtensionCapability>
RtpTransceiver::GetNegotiatedHeaderExtensions() const {
  RTC_DCHECK_RUN_ON(thread_);
  return ModifyCapabilitiesAccordingToHeaders(header_extensions_to_negotiate_,
                                              negotiated_header_extensions_);
}

std::vector<RtpHeaderExtensionCapability>
RtpTransceiver::GetOfferedAndImplementedHeaderExtensions(
    const MediaContentDescription* content) const {
  RTC_DCHECK_RUN_ON(thread_);
  return ModifyCapabilitiesAccordingToHeaders(header_extensions_to_negotiate_,
                                              content->rtp_header_extensions());
}

// 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(absl::string_view uri) {
  return uri == RtpExtension::kMidUri;
}

RTCError RtpTransceiver::SetHeaderExtensionsToNegotiate(
    std::span<const RtpHeaderExtensionCapability> header_extensions) {
  RTC_DCHECK_RUN_ON(thread_);
  // https://w3c.github.io/webrtc-extensions/#dom-rtcrtptransceiver-setheaderextensionstonegotiate
  if (header_extensions.size() != header_extensions_to_negotiate_.size()) {
    return RTCError::InvalidModification()
           << "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::InvalidModification()
             << "Reordering extensions is not allowed.";
    }
    if (IsMandatoryHeaderExtension(extension.uri) &&
        extension.direction != RtpTransceiverDirection::kSendRecv) {
      return RTCError::InvalidModification()
             << "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 MediaContentDescription* content) {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(content);
  if (sdp_type == SdpType::kAnswer || sdp_type == SdpType::kPrAnswer) {
    negotiated_header_extensions_ = content->rtp_header_extensions();
    if (!env_.field_trials().IsDisabled(
            "WebRTC-HeaderExtensionNegotiateMemory")) {
      header_extensions_to_negotiate_ = GetNegotiatedHeaderExtensions();
    }
  } else if (sdp_type == SdpType::kOffer) {
    if (!env_.field_trials().IsDisabled(
            "WebRTC-HeaderExtensionNegotiateMemory")) {
      header_extensions_for_rollback_ = header_extensions_to_negotiate_;
      header_extensions_to_negotiate_ =
          GetOfferedAndImplementedHeaderExtensions(content);
    }
  } else if (sdp_type == SdpType::kRollback) {
    if (!env_.field_trials().IsDisabled(
            "WebRTC-HeaderExtensionNegotiateMemory")) {
      RTC_CHECK(!header_extensions_for_rollback_.empty());
      header_extensions_to_negotiate_ = header_extensions_for_rollback_;
    }
  }
}

bool RtpTransceiver::SetRtpTransport(RtpTransportInternal* transport) {
  RTC_DCHECK_RUN_ON(context()->network_thread());
  RTC_DCHECK(channel_);

  if (transport == rtp_transport_) {
    return true;
  }

  ClearRtpTransportState();

  if (!channel_->SetRtpTransport(transport)) {
    return false;
  }

  SetRtpTransportState(transport);
  return true;
}

void RtpTransceiver::ClearRtpTransportState() {
  RTC_DCHECK_RUN_ON(context()->network_thread());
  if (rtp_transport_) {
    rtp_transport_->UnsubscribeNetworkRouteChanged(this);
    rtp_transport_ = nullptr;
  }
}

void RtpTransceiver::SetRtpTransportState(RtpTransportInternal* transport) {
  RTC_DCHECK_RUN_ON(context()->network_thread());
  RTC_DCHECK(!rtp_transport_);
  rtp_transport_ = transport;
  if (rtp_transport_) {
    // Safe to capture raw pointer to channel because we unsubscribe on the
    // network thread (via GetClearChannelNetworkTask) before the channel is
    // destroyed on the worker thread (via GetDeleteChannelWorkerTask).
    // Capturing raw pointer avoids reading `channel_` member on the network
    // thread, which races with signaling thread moving it during teardown.
    rtp_transport_->SubscribeNetworkRouteChanged(
        this,
        [this, channel = channel_.get()](std::optional<NetworkRoute> route) {
          RTC_DCHECK_RUN_ON(context()->network_thread());
          OnNetworkRouteChanged(channel, route);
        });
  }
}

RTCErrorOr<std::optional<std::string>>
RtpTransceiver::InitializeOnNetworkThread(
    absl::AnyInvocable<RtpTransportInternal*() &&> transport_lookup) {
  RTC_DCHECK_RUN_ON(context()->network_thread());
  RTC_DCHECK(!rtp_transport_);
  RTC_DCHECK(channel_);
  RtpTransportInternal* transport = std::move(transport_lookup)();
  if (!SetRtpTransport(transport)) {
    return RTCError::InvalidParameter()
           << "Invalid transport for mid=" << channel_->mid();
  }

  std::optional<std::string> transport_name;
  if (transport) {
    transport_name = transport->transport_name();
  }
  return transport_name;
}

void RtpTransceiver::OnNetworkRouteChanged(
    ChannelInterface* channel,
    std::optional<NetworkRoute> network_route) {
  RTC_DCHECK_RUN_ON(context()->network_thread());
  if (channel && rtp_transport_) {
    RTC_LOG(LS_INFO) << "Network route changed for mid=" << channel->mid();
    channel->media_send_channel()->OnNetworkRouteChanged(
        rtp_transport_->transport_name(),
        network_route.value_or(NetworkRoute()));
  }
}

void RtpTransceiver::SetChannelLocalContent(
    const MediaContentDescription* content,
    SdpType type,
    ScopedOperationsBatcher& batcher) {
  RTC_DCHECK_RUN_ON(context()->signaling_thread());
  SetChannelContent(
      [this, content, type]() {
        return channel_->SetLocalContent(content, type);
      },
      batcher);
}

void RtpTransceiver::SetChannelRemoteContent(
    const MediaContentDescription* content,
    SdpType type,
    ScopedOperationsBatcher& batcher) {
  RTC_DCHECK_RUN_ON(context()->signaling_thread());
  SetChannelContent(
      [this, content, type]() {
        return channel_->SetRemoteContent(content, type);
      },
      batcher);
}

void RtpTransceiver::SetChannelContent(
    absl::AnyInvocable<RTCError() &&> set_content,
    ScopedOperationsBatcher& batcher) {
  RTC_DCHECK_RUN_ON(context()->signaling_thread());

  struct SenderParameters {
    const uint32_t ssrc;
    RtpSenderInternal* const sender;
    std::optional<RtpParameters> parameters;
  };

  std::vector<SenderParameters> sender_parameters;
  sender_parameters.reserve(senders_.size());
  for (const auto& sender : senders_) {
    sender_parameters.push_back(
        {.ssrc = sender->ssrc(), .sender = sender->internal()});
  }

  batcher.AddWithFinalizer(
      [this, set_content = std::move(set_content),
       sender_parameters = std::move(sender_parameters)]() mutable
          -> RTCErrorOr<ScopedOperationsBatcher::FinalizerTask> {
        RTC_DCHECK_RUN_ON(context()->worker_thread());
        if (!channel_) {
          return RTCError::InvalidState() << "No channel";
        }
        RTCError result = std::move(set_content)();
        if (!result.ok()) {
          return result;
        }
        for (auto& entry : sender_parameters) {
          if (entry.ssrc != 0) {
            entry.parameters =
                channel_->media_send_channel()->GetRtpSendParameters(
                    entry.ssrc);
          }
        }
        return ScopedOperationsBatcher::FinalizerTask(
            [sender_parameters = std::move(sender_parameters)]() mutable {
              for (auto& entry : sender_parameters) {
                if (entry.parameters) {
                  entry.sender->SetCachedParameters(
                      std::move(*entry.parameters));
                }
              }
            });
      });
}
void RtpTransceiver::EnableChannel(bool enable) {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(channel_);
  channel_->Enable(enable);
}

const std::vector<StreamParams>& RtpTransceiver::channel_local_streams() const {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(channel_);
  return channel_->local_streams();
}

const std::vector<StreamParams>& RtpTransceiver::channel_remote_streams()
    const {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(channel_);
  return channel_->remote_streams();
}

absl::string_view RtpTransceiver::channel_transport_name() const {
  RTC_DCHECK_RUN_ON(context()->network_thread());
  RTC_DCHECK(channel_);
  return channel_->transport_name();
}

MediaSendChannelInterface* RtpTransceiver::media_send_channel() {
  RTC_DCHECK_RUN_ON(thread_);
  return channel_ ? channel_->media_send_channel() : nullptr;
}

const MediaSendChannelInterface* RtpTransceiver::media_send_channel() const {
  RTC_DCHECK_RUN_ON(thread_);
  return channel_ ? channel_->media_send_channel() : nullptr;
}

MediaReceiveChannelInterface* RtpTransceiver::media_receive_channel() {
  RTC_DCHECK_RUN_ON(thread_);
  return channel_ ? channel_->media_receive_channel() : nullptr;
}

const MediaReceiveChannelInterface* RtpTransceiver::media_receive_channel()
    const {
  RTC_DCHECK_RUN_ON(thread_);
  return channel_ ? channel_->media_receive_channel() : nullptr;
}

VideoMediaSendChannelInterface* RtpTransceiver::video_media_send_channel() {
  // Accessed from multiple threads.
  // See https://issues.webrtc.org/475126742
  return channel_ ? channel_->video_media_send_channel() : nullptr;
}

VoiceMediaSendChannelInterface* RtpTransceiver::voice_media_send_channel() {
  // Accessed from multiple threads.
  // See https://issues.webrtc.org/475126742
  return channel_ ? channel_->voice_media_send_channel() : nullptr;
}

VideoMediaReceiveChannelInterface*
RtpTransceiver::video_media_receive_channel() {
  // Accessed from multiple threads.
  // See https://issues.webrtc.org/475126742
  return channel_ ? channel_->video_media_receive_channel() : nullptr;
}

VoiceMediaReceiveChannelInterface*
RtpTransceiver::voice_media_receive_channel() {
  // Accessed from multiple threads.
  // See https://issues.webrtc.org/475126742
  return channel_ ? channel_->voice_media_receive_channel() : nullptr;
}

void RtpTransceiver::SetTransport(scoped_refptr<DtlsTransport> transport,
                                  std::optional<std::string> transport_name) {
  RTC_DCHECK_RUN_ON(thread_);
  RTC_DCHECK(HasChannel() || !transport);
  RTC_DCHECK((transport && transport_name.has_value()) ||
             (!transport && !transport_name));
  RTC_DCHECK(!transport_name.has_value() || !transport_name.value().empty());
  transport_name_ = std::move(transport_name);
  for (auto& sender : senders_) {
    sender->internal()->set_transport(transport);
  }
  for (auto& receiver : receivers_) {
    receiver->internal()->set_transport(transport);
  }
}

}  // namespace webrtc

Messung V0.5 in Prozent
C=91 H=100 G=95

¤ Dauer der Verarbeitung: 0.18 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.