/*
 *  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 RTC_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 RTC_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;
      }
    }
  }
}

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,
    std::vector<std::string> stream_ids,
    std::vector<Codec> send_codecs) {
  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),
            std::move(stream_ids), init_send_encodings,
            std::move(send_codecs)));
  }
  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,
          std::move(stream_ids), std::move(send_codecs)));
}

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,
    VoiceChannelFactoryInterface* voice_factory,
    VideoChannelFactoryInterface* video_factory,
    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,
    absl::AnyInvocable<void()> parameters_changed_callback = nullptr) {
  if (media_type == MediaType::AUDIO) {
    RTC_DCHECK(voice_factory);
    return {voice_factory->CreateSendChannel(
                env, call, media_config, audio_options, crypto_options,
                std::move(parameters_changed_callback)),
            voice_factory->CreateReceiveChannel(env, call, media_config,
                                                audio_options, crypto_options)};
  }
  RTC_DCHECK(video_factory);
  return {video_factory->CreateSendChannel(
              env, call, media_config, video_options, crypto_options,
              video_bitrate_allocator_factory,
              std::move(video_encoder_switch_request_callback),
              std::move(parameters_changed_callback)),
          video_factory->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

std::vector<Codec> RtpTransceiver::GetSendCodecs() {
  RTC_DCHECK_RUN_ON(thread_);
  return media_type_ == MediaType::VIDEO
             ? codec_vendor().video_send_codecs().codecs()
             : codec_vendor().audio_send_codecs().codecs();
}

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_);
  }
  sender_internal->SetSendCodecs(GetSendCodecs());
}

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,
    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())),
      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();
  std::vector<Codec> send_codecs = GetSendCodecs();

  auto channels = CreateMediaContentChannels(
      media_type_, env_, voice_channel_factory(), video_channel_factory(), call,
      media_config, audio_options, video_options, crypto_options,
      video_bitrate_allocator_factory, std::move(encoder_switch_callback),
      GetParametersChangedCallback());

  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, stream_ids, std::move(send_codecs));

  owned_send_channel_ = std::move(channels.first);
  owned_receive_channel_ = std::move(channels.second);
  senders_.push_back(std::move(sender));

  bool set_track_succeeded = senders_.back()->SetTrack(track.get());
  RTC_DCHECK(set_track_succeeded);

  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(!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);
      };

  auto encoder_switch_callback = GetEncoderSwitchRequestCallback();
  auto parameters_changed_callback = GetParametersChangedCallback();

  // 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,
       encoder_switch_callback = std::move(encoder_switch_callback),
       parameters_changed_callback = std::move(parameters_changed_callback),
       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_, voice_channel_factory(),
              video_channel_factory(), call_ptr, media_config, audio_options,
              video_options, crypto_options, video_bitrate_allocator_factory,
              std::move(encoder_switch_callback),
              std::move(parameters_changed_callback));
          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)();
              });
            }));
      };
}

absl::AnyInvocable<void()> RtpTransceiver::GetParametersChangedCallback() {
  RTC_DCHECK(signaling_thread_safety_);
  return [this, signaling_safety = signaling_thread_safety_]() {
    thread_->PostTask(SafeTask(signaling_safety, [this]() {
      RTC_DCHECK_RUN_ON(thread_);
      for (const auto& sender : senders_) {
        sender->internal()->OnParametersChanged();
      }
    }));
  };
}

// RTC_RUN_ON(context()->worker_thread());
void RtpTransceiver::ClearMediaChannelReferences() {
  SetMediaChannels(nullptr, nullptr);
  owned_send_channel_ = nullptr;
  owned_receive_channel_ = 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));
  sender->internal()->SetSendCodecs(GetSendCodecs());
  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);
  std::vector<Codec> send_codecs = GetSendCodecs();
  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, {}, stream_ids, std::move(send_codecs)));
  });
  bool set_track_succeeded = senders_.back()->SetTrack(track.get());
  RTC_DCHECK(set_track_succeeded);
  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_;
}

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 RTC_LOG_ERROR(RTCError::InvalidState()
                         << "Cannot set direction on a stopping transceiver.");
  }
  if (new_direction == direction_)
    return RTCError::OK();

  if (new_direction == RtpTransceiverDirection::kStopped) {
    return RTC_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 RTC_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());

  if (!channel_) {
    return true;
  }

  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
