Übersicht der Quellen

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  rtp_transceiver_unittest.cc

  Sprache: C
 

/*
 *  Copyright 2018 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.
 */


// This file contains tests for `RtpTransceiver`.

#include "pc/rtp_transceiver.h"

#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "absl/functional/any_invocable.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_types.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_error.h"
#include "api/rtp_parameters.h"
#include "api/rtp_transceiver_direction.h"
#include "api/scoped_refptr.h"
#include "api/test/rtc_error_matchers.h"
#include "api/video_codecs/scalability_mode.h"
#include "api/video_codecs/sdp_video_format.h"
#include "call/call.h"
#include "media/base/codec.h"
#include "media/base/codec_comparators.h"
#include "media/base/fake_media_engine.h"
#include "media/base/media_channel.h"
#include "media/base/media_config.h"
#include "media/engine/fake_webrtc_call.h"
#include "p2p/dtls/fake_dtls_transport.h"
#include "pc/codec_vendor.h"
#include "pc/connection_context.h"
#include "pc/dtls_transport.h"
#include "pc/rtp_parameters_conversion.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.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/test/enable_fake_media.h"
#include "pc/test/fake_codec_lookup_helper.h"
#include "pc/test/mock_channel_interface.h"
#include "pc/test/mock_rtp_receiver_internal.h"
#include "pc/test/mock_rtp_sender_internal.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/network_route.h"
#include "rtc_base/thread.h"
#include "test/create_test_environment.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/run_loop.h"

using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::IsNull;
using ::testing::NiceMock;
using ::testing::Optional;
using ::testing::Property;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SizeIs;

namespace webrtc {

namespace {

scoped_refptr<RtpTransceiver> CreateAudioTransceiverWithChannel(
    const Environment& env,
    Call* call,
    ConnectionContext* context,
    CodecLookupHelper* codec_lookup_helper,
    const AudioOptions& audio_options,
    absl::AnyInvocable<RtpTransportInternal*() &&> transport_lookup = []() {
      return nullptr;
    }) {
  ScopedOperationsBatcher worker_tasks(context->worker_thread());
  auto transceiver = make_ref_counted<RtpTransceiver>(
      env, call, MediaConfig(), "sender""receiver", MediaType::AUDIO, nullptr,
      std::vector<std::string>(), std::vector<RtpEncodingParameters>(), context,
      codec_lookup_helper, nullptr, nullptr, audio_options, VideoOptions(),
      CryptoOptions(), nullptr, std::vector<RtpHeaderExtensionCapability>(),
      false, std::vector<SimulcastLayer>(), worker_tasks, [] {});

  RTC_CHECK(worker_tasks.Run().ok());

  ScopedOperationsBatcher network_tasks(context->network_thread());
  transceiver->CreateChannel("0", call, MediaConfig(), false, CryptoOptions(),
                             audio_options, VideoOptions(), nullptr,
                             std::move(transport_lookup), worker_tasks,
                             network_tasks);

  RTC_CHECK(worker_tasks.Run().ok());
  RTC_CHECK(network_tasks.Run().ok());

  return transceiver;
}

scoped_refptr<RtpTransceiver> CreateVideoTransceiverWithChannel(
    const Environment& env,
    Call* call,
    ConnectionContext* context,
    CodecLookupHelper* codec_lookup_helper,
    const VideoOptions& video_options,
    absl::AnyInvocable<RtpTransportInternal*() &&> transport_lookup = []() {
      return nullptr;
    }) {
  ScopedOperationsBatcher worker_tasks(context->worker_thread());
  auto transceiver = make_ref_counted<RtpTransceiver>(
      env, call, MediaConfig(), "sender""receiver", MediaType::VIDEO, nullptr,
      std::vector<std::string>(), std::vector<RtpEncodingParameters>(), context,
      codec_lookup_helper, nullptr, nullptr, AudioOptions(), video_options,
      CryptoOptions(), nullptr, std::vector<RtpHeaderExtensionCapability>(),
      false, std::vector<SimulcastLayer>(), worker_tasks, [] {});

  RTC_CHECK(worker_tasks.Run().ok());

  ScopedOperationsBatcher network_tasks(context->network_thread());
  transceiver->CreateChannel("0", call, MediaConfig(), false, CryptoOptions(),
                             AudioOptions(), video_options, nullptr,
                             std::move(transport_lookup), worker_tasks,
                             network_tasks);

  RTC_CHECK(worker_tasks.Run().ok());
  RTC_CHECK(network_tasks.Run().ok());

  return transceiver;
}

class RtpTransceiverTest : public testing::Test {
 public:
  RtpTransceiverTest()
      : env_(CreateTestEnvironment()),
        dependencies_(MakeDependencies()),
        context_(ConnectionContext::Create(env_, &dependencies_)),
        media_engine_ref_(context_),
        codec_lookup_helper_(context_.get(), env_.field_trials()) {}

 protected:
  const Environment& env() const { return env_; }
  FakeMediaEngine* media_engine() {
    // We know this cast is safe because we supplied the fake implementation
    // in MakeDependencies().
    return static_cast<FakeMediaEngine*>(media_engine_ref_.media_engine());
  }
  ConnectionContext* context() { return context_.get(); }
  CodecLookupHelper* codec_lookup_helper() { return &codec_lookup_helper_; }
  FakeCodecLookupHelper* fake_codec_lookup_helper() {
    return &codec_lookup_helper_;
  }

 protected:
  test::RunLoop main_thread_;

 private:
  static PeerConnectionFactoryDependencies MakeDependencies() {
    PeerConnectionFactoryDependencies d;
    d.network_thread = Thread::Current();
    d.worker_thread = Thread::Current();
    d.signaling_thread = Thread::Current();
    EnableFakeMedia(d, std::make_unique<FakeMediaEngine>());
    return d;
  }

  Environment env_;
  PeerConnectionFactoryDependencies dependencies_;
  scoped_refptr<ConnectionContext> context_;
  ConnectionContext::MediaEngineReference media_engine_ref_;
  FakeCodecLookupHelper codec_lookup_helper_;
};

class RtpTransceiverDoubleThreadTest : public testing::Test {
 public:
  RtpTransceiverDoubleThreadTest()
      : env_(CreateTestEnvironment()),
        network_thread_(Thread::Create()),
        worker_thread_(Thread::Create()),
        dependencies_(
            MakeDependencies(network_thread_.get(), worker_thread_.get())),
        context_(ConnectionContext::Create(env_, &dependencies_)),
        codec_lookup_helper_(context_.get(), env_.field_trials()) {
    network_thread_->Start();
    worker_thread_->Start();
    context_->worker_thread()->BlockingCall([&]() {
      media_engine_ref_ =
          std::make_unique<ConnectionContext::MediaEngineReference>(context_);
    });
  }

  ~RtpTransceiverDoubleThreadTest() override {
    context_->worker_thread()->BlockingCall(
        [&]() { media_engine_ref_.reset(); });
  }

 protected:
  const Environment& env() const { return env_; }
  FakeMediaEngine* media_engine() {
    return static_cast<FakeMediaEngine*>(media_engine_ref_->media_engine());
  }
  ConnectionContext* context() { return context_.get(); }
  CodecLookupHelper* codec_lookup_helper() { return &codec_lookup_helper_; }

 private:
  static PeerConnectionFactoryDependencies MakeDependencies(
      Thread* network_thread,
      Thread* worker_thread) {
    PeerConnectionFactoryDependencies d;
    d.network_thread = network_thread;
    d.worker_thread = worker_thread;
    d.signaling_thread = Thread::Current();
    RTC_LOG(LS_INFO) << "MakeDependencies signaling_thread="
                     << d.signaling_thread;
    EnableFakeMedia(d, std::make_unique<FakeMediaEngine>());
    return d;
  }

  test::RunLoop main_thread_;
  Environment env_;
  std::unique_ptr<Thread> network_thread_;
  std::unique_ptr<Thread> worker_thread_;
  PeerConnectionFactoryDependencies dependencies_;
  scoped_refptr<ConnectionContext> context_;
  std::unique_ptr<ConnectionContext::MediaEngineReference> media_engine_ref_;
  FakeCodecLookupHelper codec_lookup_helper_;
};

class RtpTransceiverTestWithFakeCall : public RtpTransceiverTest {
 public:
  RtpTransceiverTestWithFakeCall()
      : call_(std::make_unique<FakeCall>(env(),
                                         Thread::Current(),
                                         Thread::Current())) {}

 protected:
  std::unique_ptr<FakeCall> call_;
};

// Checks that a channel cannot be set on a stopped `RtpTransceiver`.
TEST_F(RtpTransceiverTest, CannotSetChannelOnStoppedTransceiver) {
  const std::string content_name("my_mid");
  auto transceiver = make_ref_counted<RtpTransceiver>(
      env(), MediaType::AUDIO, context(), codec_lookup_helper(), nullptr);
  transceiver->set_mid(content_name);
  auto channel1 = std::make_unique<NiceMock<MockChannelInterface>>();
  EXPECT_CALL(*channel1, media_type()).WillRepeatedly(Return(MediaType::AUDIO));
  EXPECT_CALL(*channel1, mid()).WillRepeatedly(ReturnRef(content_name));
  EXPECT_CALL(*channel1, SetRtpTransport(_)).WillRepeatedly(Return(true));

  transceiver->SetChannelForTest(std::move(channel1));
  EXPECT_TRUE(transceiver->HasChannel());

  // Stop the transceiver.
  transceiver->StopInternal();
  EXPECT_TRUE(transceiver->HasChannel());

  auto channel2 = std::make_unique<NiceMock<MockChannelInterface>>();
  EXPECT_CALL(*channel2, media_type()).WillRepeatedly(Return(MediaType::AUDIO));

  // Clear the current channel - required to allow SetChannelForTest()
  transceiver->ClearChannel();

  ASSERT_FALSE(transceiver->HasChannel());

  // Channel can no longer be set, so this call should be a no-op.
  transceiver->SetChannelForTest(std::move(channel2));
  EXPECT_FALSE(transceiver->HasChannel());
}

// Checks that a channel can be unset on a stopped `RtpTransceiver`
TEST_F(RtpTransceiverTest, CanUnsetChannelOnStoppedTransceiver) {
  const std::string content_name("my_mid");
  auto transceiver = make_ref_counted<RtpTransceiver>(
      env(), MediaType::VIDEO, context(), codec_lookup_helper(), nullptr);
  transceiver->set_mid(content_name);
  auto channel = std::make_unique<NiceMock<MockChannelInterface>>();
  EXPECT_CALL(*channel, media_type()).WillRepeatedly(Return(MediaType::VIDEO));
  EXPECT_CALL(*channel, mid()).WillRepeatedly(ReturnRef(content_name));
  EXPECT_CALL(*channel, SetRtpTransport(_)).WillRepeatedly(Return(true));

  transceiver->SetChannelForTest(std::move(channel));
  EXPECT_TRUE(transceiver->HasChannel());

  // Stop the transceiver.
  transceiver->StopInternal();
  EXPECT_TRUE(transceiver->HasChannel());

  // Set the channel to `nullptr`.
  transceiver->ClearChannel();
  EXPECT_FALSE(transceiver->HasChannel());
}

TEST_F(RtpTransceiverTestWithFakeCall, TransportNameIsUpdated) {
  const std::string content_name("my_mid");

  auto fake_dtls = std::make_unique<FakeDtlsTransport>("test_transport"false);
  auto rtp_transport = std::make_unique<RtpTransport>(/*rtcp_mux_enabled=*/true,
                                                      env().field_trials());
  rtp_transport->SetRtpPacketTransport(fake_dtls.get());

  auto transceiver = CreateAudioTransceiverWithChannel(
      env(), call_.get(), context(), codec_lookup_helper(), AudioOptions(),
      [&]() -> RtpTransportInternal* { return rtp_transport.get(); });

  EXPECT_TRUE(transceiver->HasChannel());
  EXPECT_EQ(transceiver->transport_name(), "test_transport");

  auto dtls_transport = make_ref_counted<DtlsTransport>(fake_dtls.get());
  transceiver->SetTransport(dtls_transport, "updated_transport");
  EXPECT_EQ(transceiver->transport_name(), "updated_transport");

  // Setting null transport should clear the name.
  transceiver->SetTransport(nullptr, std::nullopt);
  EXPECT_EQ(transceiver->transport_name(), std::nullopt);
  EXPECT_TRUE(transceiver->HasChannel());

  // Clearing the channel should reset the transport name to nullopt.
  transceiver->SetTransport(dtls_transport, "yadt");
  EXPECT_EQ(transceiver->transport_name(), "yadt");
  transceiver->ClearChannel();
  EXPECT_FALSE(transceiver->transport_name().has_value());
}

class RtpTransceiverUnifiedPlanTest : public RtpTransceiverTest {
 public:
  static scoped_refptr<MockRtpReceiverInternal> MockReceiver(
      MediaType media_type) {
    auto receiver = make_ref_counted<NiceMock<MockRtpReceiverInternal>>();
    EXPECT_CALL(*receiver.get(), media_type())
        .WillRepeatedly(Return(media_type));
    return receiver;
  }

  static scoped_refptr<MockRtpSenderInternal> MockSender(MediaType media_type) {
    auto sender = make_ref_counted<NiceMock<MockRtpSenderInternal>>();
    EXPECT_CALL(*sender.get(), media_type()).WillRepeatedly(Return(media_type));
    return sender;
  }

  scoped_refptr<RtpTransceiver> CreateTransceiver(
      scoped_refptr<RtpSenderInternal> sender,
      scoped_refptr<RtpReceiverInternal> receiver) {
    return make_ref_counted<RtpTransceiver>(
        env(),
        RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
            Thread::Current(), std::move(sender)),
        RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
            Thread::Current(), Thread::Current(), std::move(receiver)),
        context(), codec_lookup_helper(),
        media_engine()->voice().GetRtpHeaderExtensions(&env().field_trials()),
        /* on_negotiation_needed= */ [] {});
  }
};

// Basic tests for Stop()
TEST_F(RtpTransceiverUnifiedPlanTest, StopSetsDirection) {
  scoped_refptr<MockRtpReceiverInternal> receiver =
      MockReceiver(MediaType::AUDIO);
  scoped_refptr<MockRtpSenderInternal> sender = MockSender(MediaType::AUDIO);
  scoped_refptr<RtpTransceiver> transceiver =
      CreateTransceiver(sender, receiver);

  EXPECT_EQ(RtpTransceiverDirection::kInactive, transceiver->direction());
  EXPECT_FALSE(transceiver->current_direction());
  transceiver->StopStandard();
  EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver->direction());
  EXPECT_FALSE(transceiver->current_direction());
  auto stop_task = transceiver->GetStopTransceiverProcedure();
  if (stop_task) {
    std::move(stop_task)();
  }
  EXPECT_TRUE(transceiver->current_direction());
  EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver->direction());
  EXPECT_EQ(RtpTransceiverDirection::kStopped,
            *transceiver->current_direction());
}

class RtpTransceiverFilteredCodecPreferencesTest
    : public RtpTransceiverUnifiedPlanTest {
 public:
  RtpTransceiverFilteredCodecPreferencesTest()
      : transceiver_(CreateTransceiver(MockSender(MediaType::VIDEO),
                                       MockReceiver(MediaType::VIDEO))) {}

  struct H264CodecCapabilities {
    Codec cricket_sendrecv_codec;
    RtpCodecCapability sendrecv_codec;
    Codec cricket_sendonly_codec;
    RtpCodecCapability sendonly_codec;
    Codec cricket_recvonly_codec;
    RtpCodecCapability recvonly_codec;
    Codec cricket_rtx_codec;
    RtpCodecCapability rtx_codec;
  };

  // This function must be called after modifying the media factory's
  // capabilities, since the transceiver picks up codecs from the factory
  // at transceiver create time.
  void RecreateTransceiver() {
    fake_codec_lookup_helper()->Reset();
    transceiver_ = CreateTransceiver(MockSender(MediaType::VIDEO),
                                     MockReceiver(MediaType::VIDEO));
  }

  // For H264, the profile and level IDs are entangled. This function uses
  // profile-level-id values that are not equal even when levels are ignored.
  H264CodecCapabilities ConfigureH264CodecCapabilities() {
    Codec cricket_sendrecv_codec =
        CreateVideoCodec(SdpVideoFormat("H264",
                                        {{"level-asymmetry-allowed""1"},
                                         {"packetization-mode""1"},
                                         {"profile-level-id""42f00b"}},
                                        {ScalabilityMode::kL1T1}));
    Codec cricket_sendonly_codec =
        CreateVideoCodec(SdpVideoFormat("H264",
                                        {{"level-asymmetry-allowed""1"},
                                         {"packetization-mode""1"},
                                         {"profile-level-id""640034"}},
                                        {ScalabilityMode::kL1T1}));
    Codec cricket_recvonly_codec =
        CreateVideoCodec(SdpVideoFormat("H264",
                                        {{"level-asymmetry-allowed""1"},
                                         {"packetization-mode""1"},
                                         {"profile-level-id""f4001f"}},
                                        {ScalabilityMode::kL1T1}));
    Codec cricket_rtx_codec =
        CreateVideoRtxCodec(Codec::kIdNotSet, Codec::kIdNotSet);
    media_engine()->SetVideoSendCodecs(
        {cricket_sendrecv_codec, cricket_sendonly_codec, cricket_rtx_codec});
    media_engine()->SetVideoRecvCodecs(
        {cricket_sendrecv_codec, cricket_recvonly_codec, cricket_rtx_codec});
    H264CodecCapabilities capabilities = {
        .cricket_sendrecv_codec = cricket_sendrecv_codec,
        .sendrecv_codec = ToRtpCodecCapability(cricket_sendrecv_codec),
        .cricket_sendonly_codec = cricket_sendonly_codec,
        .sendonly_codec = ToRtpCodecCapability(cricket_sendonly_codec),
        .cricket_recvonly_codec = cricket_recvonly_codec,
        .recvonly_codec = ToRtpCodecCapability(cricket_recvonly_codec),
        .cricket_rtx_codec = cricket_rtx_codec,
        .rtx_codec = ToRtpCodecCapability(cricket_rtx_codec),
    };
    EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(
        capabilities.cricket_sendrecv_codec, capabilities.sendonly_codec));
    EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(
        capabilities.cricket_sendrecv_codec, capabilities.recvonly_codec));
    EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(
        capabilities.cricket_sendonly_codec, capabilities.recvonly_codec));
    // Because RtpTransceiver buffers codec information in a CodecVendor,
    // we must recreate it after changing the supported codecs.
    RecreateTransceiver();
    return capabilities;
  }

#ifdef RTC_ENABLE_H265
  struct H265CodecCapabilities {
    // The level-id from sender getCapabilities() or receiver getCapabilities().
    static constexpr const char* kSendOnlyLevel = "180";
    static constexpr const char* kRecvOnlyLevel = "156";
    // A valid H265 level-id, but one not present in either getCapabilities().
    static constexpr const char* kLevelNotInCapabilities = "135";

    Codec cricket_sendonly_codec;
    RtpCodecCapability sendonly_codec;
    Codec cricket_recvonly_codec;
    RtpCodecCapability recvonly_codec;
  };

  // For H265, the profile and level IDs are separate and are ignored by
  // IsSameRtpCodecIgnoringLevel().
  H265CodecCapabilities ConfigureH265CodecCapabilities() {
    Codec cricket_sendonly_codec = CreateVideoCodec(
        SdpVideoFormat("H265",
                       {{"profile-id""1"},
                        {"tier-flag""0"},
                        {"level-id", H265CodecCapabilities::kSendOnlyLevel},
                        {"tx-mode""SRST"}},
                       {ScalabilityMode::kL1T1}));
    Codec cricket_recvonly_codec = CreateVideoCodec(
        SdpVideoFormat("H265",
                       {{"profile-id""1"},
                        {"tier-flag""0"},
                        {"level-id", H265CodecCapabilities::kRecvOnlyLevel},
                        {"tx-mode""SRST"}},
                       {ScalabilityMode::kL1T1}));
    media_engine()->SetVideoSendCodecs({cricket_sendonly_codec});
    media_engine()->SetVideoRecvCodecs({cricket_recvonly_codec});
    // Because RtpTransceiver buffers codec information in a CodecVendor,
    // we must recreate it after changing the supported codecs.
    RecreateTransceiver();
    return {
        .cricket_sendonly_codec = cricket_sendonly_codec,
        .sendonly_codec = ToRtpCodecCapability(cricket_sendonly_codec),
        .cricket_recvonly_codec = cricket_recvonly_codec,
        .recvonly_codec = ToRtpCodecCapability(cricket_recvonly_codec),
    };
  }
#endif  // RTC_ENABLE_H265

 protected:
  scoped_refptr<RtpTransceiver> transceiver_;
};

TEST_F(RtpTransceiverFilteredCodecPreferencesTest, EmptyByDefault) {
  ConfigureH264CodecCapabilities();

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendRecv),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kInactive),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));
}

TEST_F(RtpTransceiverFilteredCodecPreferencesTest, OrderIsMaintained) {
  const auto codecs = ConfigureH264CodecCapabilities();
  std::vector<RtpCodecCapability> codec_capabilities = {codecs.sendrecv_codec,
                                                        codecs.rtx_codec};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codec_capabilities[0], codec_capabilities[1]));
  // Reverse order.
  codec_capabilities = {codecs.rtx_codec, codecs.sendrecv_codec};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codec_capabilities[0], codec_capabilities[1]));
}

TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
       FiltersCodecsBasedOnDirection) {
  const auto codecs = ConfigureH264CodecCapabilities();
  std::vector<RtpCodecCapability> codec_capabilities = {
      codecs.sendonly_codec, codecs.sendrecv_codec, codecs.recvonly_codec};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendRecv),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codecs.sendrecv_codec));

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codecs.sendonly_codec, codecs.sendrecv_codec));

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codecs.sendrecv_codec, codecs.recvonly_codec));

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kInactive),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codecs.sendrecv_codec));
}

TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
       RtxIsIncludedAfterFiltering) {
  const auto codecs = ConfigureH264CodecCapabilities();
  std::vector<RtpCodecCapability> codec_capabilities = {codecs.recvonly_codec,
                                                        codecs.rtx_codec};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codecs.recvonly_codec, codecs.rtx_codec));
}

TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
       NoMediaIsTheSameAsNoPreference) {
  const auto codecs = ConfigureH264CodecCapabilities();
  std::vector<RtpCodecCapability> codec_capabilities = {codecs.recvonly_codec,
                                                        codecs.rtx_codec};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());

  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendOnly),
      IsRtcOk());
  // After filtering the only codec that remains is RTX which is not a media
  // codec, this is the same as not having any preferences.
  EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));

  // But the preferences are remembered in case the direction changes such that
  // we do have a media codec.
  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codecs.recvonly_codec, codecs.rtx_codec));
}

TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
       H264LevelIdsIgnoredByFilter) {
  // Baseline 3.1 and 5.2 are compatible when ignoring level IDs.
  Codec baseline_3_1 =
      CreateVideoCodec(SdpVideoFormat("H264",
                                      {{"level-asymmetry-allowed""1"},
                                       {"packetization-mode""1"},
                                       {"profile-level-id""42001f"}},
                                      {ScalabilityMode::kL1T1}));
  Codec baseline_5_2 =
      CreateVideoCodec(SdpVideoFormat("H264",
                                      {{"level-asymmetry-allowed""1"},
                                       {"packetization-mode""1"},
                                       {"profile-level-id""420034"}},
                                      {ScalabilityMode::kL1T1}));
  // High is NOT compatible with baseline.
  Codec high_3_1 =
      CreateVideoCodec(SdpVideoFormat("H264",
                                      {{"level-asymmetry-allowed""1"},
                                       {"packetization-mode""1"},
                                       {"profile-level-id""64001f"}},
                                      {ScalabilityMode::kL1T1}));
  // Configure being able to both send and receive Baseline but using different
  // level IDs in either direction, while the High profile is "truly" recvonly.
  media_engine()->SetVideoSendCodecs({baseline_3_1});
  media_engine()->SetVideoRecvCodecs({baseline_5_2, high_3_1});
  // Because RtpTransceiver buffers codec information in a CodecVendor,
  // we must recreate it after changing the supported codecs.
  RecreateTransceiver();

  // Prefer to "sendrecv" Baseline 5.2. Even though we can only send 3.1 this
  // codec is not filtered out due to 5.2 and 3.1 being compatible when ignoring
  // level IDs.
  std::vector<RtpCodecCapability> codec_capabilities = {
      ToRtpCodecCapability(baseline_5_2)};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendRecv),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codec_capabilities[0]));
  // Prefer to "sendrecv" High 3.1. This gets filtered out because we cannot
  // send it (Baseline 3.1 is not compatible with it).
  codec_capabilities = {ToRtpCodecCapability(high_3_1)};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(), SizeIs(0));
  // Change direction to "recvonly" to avoid High 3.1 being filtered out.
  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codec_capabilities[0]));
}

#ifdef RTC_ENABLE_H265
TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
       H265LevelIdIsIgnoredByFilter) {
  const auto codecs = ConfigureH265CodecCapabilities();
  std::vector<RtpCodecCapability> codec_capabilities = {codecs.sendonly_codec,
                                                        codecs.recvonly_codec};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities), IsRtcOk());
  // Regardless of direction, both codecs are preferred due to ignoring levels.
  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codec_capabilities[0], codec_capabilities[1]));
  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codec_capabilities[0], codec_capabilities[1]));
  EXPECT_THAT(
      transceiver_->SetDirectionWithError(RtpTransceiverDirection::kSendRecv),
      IsRtcOk());
  EXPECT_THAT(transceiver_->filtered_codec_preferences(),
              ElementsAre(codec_capabilities[0], codec_capabilities[1]));
}

TEST_F(RtpTransceiverFilteredCodecPreferencesTest,
       H265LevelIdHasToBeFromSenderOrReceiverCapabilities) {
  ConfigureH265CodecCapabilities();
  Codec cricket_codec = CreateVideoCodec(SdpVideoFormat(
      "H265",
      {{"profile-id""1"},
       {"tier-flag""0"},
       {"level-id", H265CodecCapabilities::kLevelNotInCapabilities},
       {"tx-mode""SRST"}},
      {ScalabilityMode::kL1T1}));

  std::vector<RtpCodecCapability> codec_capabilities = {
      ToRtpCodecCapability(cricket_codec)};
  EXPECT_THAT(transceiver_->SetCodecPreferences(codec_capabilities),
              IsRtcErrorWithTypeAndMessage(
                  RTCErrorType::INVALID_MODIFICATION,
                  "Invalid codec preferences: Missing codec from codec "
                  "capabilities."));
}
#endif  // RTC_ENABLE_H265

class RtpTransceiverTestForHeaderExtensions
    : public RtpTransceiverUnifiedPlanTest {
 public:
  RtpTransceiverTestForHeaderExtensions()
      : extensions_(
            {RtpHeaderExtensionCapability("uri1",
                                          1,
                                          RtpTransceiverDirection::kSendOnly),
             RtpHeaderExtensionCapability("uri2",
                                          2,
                                          RtpTransceiverDirection::kRecvOnly),
             RtpHeaderExtensionCapability(RtpExtension::kMidUri,
                                          3,
                                          RtpTransceiverDirection::kSendRecv),
             RtpHeaderExtensionCapability(RtpExtension::kVideoRotationUri,
                                          4,
                                          RtpTransceiverDirection::kSendRecv)}),
        transceiver_(make_ref_counted<RtpTransceiver>(
            env(),
            RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
                Thread::Current(),
                sender_),
            RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
                Thread::Current(),
                Thread::Current(),
                receiver_),
            context(),
            codec_lookup_helper(),
            extensions_,
            /* on_negotiation_needed= */ [] {})) {}

  void ClearChannel() {
    EXPECT_CALL(*sender_.get(), SetMediaChannel(IsNull()))
        .WillRepeatedly(Return());
    transceiver_->ClearChannel();
  }

 protected:
  scoped_refptr<MockRtpReceiverInternal> receiver_ =
      MockReceiver(MediaType::AUDIO);
  scoped_refptr<MockRtpSenderInternal> sender_ = MockSender(MediaType::AUDIO);

  std::vector<RtpHeaderExtensionCapability> extensions_;
  scoped_refptr<RtpTransceiver> transceiver_;
};

TEST_F(RtpTransceiverTestForHeaderExtensions, OffersChannelManagerList) {
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
}

TEST_F(RtpTransceiverTestForHeaderExtensions, ModifiesDirection) {
  auto modified_extensions = extensions_;
  modified_extensions[0].direction = RtpTransceiverDirection::kSendOnly;
  EXPECT_TRUE(
      transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok());
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(),
            modified_extensions);
  modified_extensions[0].direction = RtpTransceiverDirection::kRecvOnly;
  EXPECT_TRUE(
      transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok());
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(),
            modified_extensions);
  modified_extensions[0].direction = RtpTransceiverDirection::kSendRecv;
  EXPECT_TRUE(
      transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok());
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(),
            modified_extensions);
  modified_extensions[0].direction = RtpTransceiverDirection::kInactive;
  EXPECT_TRUE(
      transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok());
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(),
            modified_extensions);
}

TEST_F(RtpTransceiverTestForHeaderExtensions, AcceptsStoppedExtension) {
  auto modified_extensions = extensions_;
  modified_extensions[0].direction = RtpTransceiverDirection::kStopped;
  EXPECT_TRUE(
      transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions).ok());
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(),
            modified_extensions);
}

TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsDifferentSize) {
  auto modified_extensions = extensions_;
  modified_extensions.pop_back();

  EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions),
              Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION));
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
}

TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsChangedUri) {
  auto modified_extensions = extensions_;
  ASSERT_TRUE(!modified_extensions.empty());
  modified_extensions[0].uri = "http://webrtc.org";

  EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions),
              Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION));
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
}

TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsReorder) {
  auto modified_extensions = extensions_;
  ASSERT_GE(modified_extensions.size(), 2u);
  std::swap(modified_extensions[0], modified_extensions[1]);

  EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions),
              Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION));
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
}

TEST_F(RtpTransceiverTestForHeaderExtensions,
       RejectsStoppedMandatoryExtensions) {
  std::vector<RtpHeaderExtensionCapability> modified_extensions = extensions_;
  // Attempting to stop the mandatory MID extension.
  modified_extensions[2].direction = RtpTransceiverDirection::kStopped;
  EXPECT_THAT(transceiver_->SetHeaderExtensionsToNegotiate(modified_extensions),
              Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION));
  EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
}

TEST_F(RtpTransceiverTestForHeaderExtensions,
       NoNegotiatedHdrExtsWithoutChannel) {
  EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(),
              ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped)));
}

TEST_F(RtpTransceiverTestForHeaderExtensions,
       NoNegotiatedHdrExtsWithChannelWithoutNegotiation) {
  const std::string content_name("my_mid");
  transceiver_->set_mid(content_name);
  auto mock_channel = std::make_unique<NiceMock<MockChannelInterface>>();
  // Raw ptr for updating expectations later since `mock_channel` will be moved
  // to `SetChannel`.
  EXPECT_CALL(*mock_channel, media_type())
      .WillRepeatedly(Return(MediaType::AUDIO));
  EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name));
  EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true));

  transceiver_->SetChannelForTest(std::move(mock_channel));
  EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(),
              ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped)));

  ClearChannel();
}

TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) {
  const std::string content_name("my_mid");
  transceiver_->set_mid(content_name);
  auto mock_channel = std::make_unique<NiceMock<MockChannelInterface>>();
  EXPECT_CALL(*mock_channel, media_type())
      .WillRepeatedly(Return(MediaType::AUDIO));
  EXPECT_CALL(*mock_channel, voice_media_send_channel())
      .WillRepeatedly(Return(nullptr));
  EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name));
  EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true));

  RtpHeaderExtensions extensions = {RtpExtension("uri1"1),
                                    RtpExtension("uri2"2)};
  AudioContentDescription description;
  description.set_rtp_header_extensions(extensions);
  transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description);

  transceiver_->SetChannelForTest(std::move(mock_channel));

  EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(),
              ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped)));

  ClearChannel();
}

TEST_F(RtpTransceiverTestForHeaderExtensions,
       ReturnsNegotiatedHdrExtsOnPrAnswer) {
  const std::string content_name("my_mid");
  transceiver_->set_mid(content_name);
  auto mock_channel = std::make_unique<NiceMock<MockChannelInterface>>();
  EXPECT_CALL(*mock_channel, media_type())
      .WillRepeatedly(Return(MediaType::AUDIO));
  EXPECT_CALL(*mock_channel, voice_media_send_channel())
      .WillRepeatedly(Return(nullptr));
  EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name));
  EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true));

  RtpHeaderExtensions extensions = {RtpExtension("uri1"1),
                                    RtpExtension("uri2"2)};
  AudioContentDescription description;
  description.set_rtp_header_extensions(extensions);
  transceiver_->OnNegotiationUpdate(SdpType::kPrAnswer, &description);

  transceiver_->SetChannelForTest(std::move(mock_channel));

  EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(),
              ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped)));

  ClearChannel();
}

TEST_F(RtpTransceiverTestForHeaderExtensions,
       AnswerCanUseOtherHdrExtensionsThanPrAnswer) {
  const std::string content_name("my_mid");
  transceiver_->set_mid(content_name);
  auto mock_channel = std::make_unique<NiceMock<MockChannelInterface>>();

  EXPECT_CALL(*mock_channel, media_type())
      .WillRepeatedly(Return(MediaType::AUDIO));
  EXPECT_CALL(*mock_channel, voice_media_send_channel())
      .WillRepeatedly(Return(nullptr));
  EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name));
  EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true));

  transceiver_->SetChannelForTest(std::move(mock_channel));

  AudioContentDescription description_pr_answer;
  description_pr_answer.set_rtp_header_extensions({RtpExtension("uri1"1)});
  transceiver_->OnNegotiationUpdate(SdpType::kPrAnswer, &description_pr_answer);

  EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(),
              ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped)));

  AudioContentDescription description_answer;
  description_answer.set_rtp_header_extensions(
      {RtpExtension("uri1"1), RtpExtension("uri2"2)});
  transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description_answer);

  EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(),
              ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped)));

  ClearChannel();
}

TEST_F(RtpTransceiverTestForHeaderExtensions,
       ReturnsNegotiatedHdrExtsSecondTime) {
  RtpHeaderExtensions extensions = {RtpExtension("uri1"1),
                                    RtpExtension("uri2"2)};
  AudioContentDescription description;
  description.set_rtp_header_extensions(extensions);
  transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description);

  EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(),
              ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kSendRecv),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped)));
  extensions = {RtpExtension("uri3"4), RtpExtension("uri5"6)};
  description.set_rtp_header_extensions(extensions);
  transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description);

  EXPECT_THAT(transceiver_->GetNegotiatedHeaderExtensions(),
              ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped),
                          Field(&RtpHeaderExtensionCapability::direction,
                                RtpTransceiverDirection::kStopped)));
}

TEST_F(RtpTransceiverTestForHeaderExtensions,
       SimulcastOrSvcEnablesExtensionsByDefault) {
  std::vector<RtpHeaderExtensionCapability> extensions = {
      {RtpExtension::kDependencyDescriptorUri, 1,
       RtpTransceiverDirection::kStopped},
      {RtpExtension::kVideoLayersAllocationUri, 2,
       RtpTransceiverDirection::kStopped},
  };

  // Default is stopped.
  auto sender = MockSender(MediaType::VIDEO);
  auto receiver = MockReceiver(MediaType::VIDEO);
  auto transceiver = make_ref_counted<RtpTransceiver>(
      env(),
      RtpSenderProxyWithInternal<RtpSenderInternal>::Create(Thread::Current(),
                                                            sender),
      RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
          Thread::Current(), Thread::Current(), receiver),
      context(), codec_lookup_helper(), extensions,
      /* on_negotiation_needed= */ [] {});
  ASSERT_EQ(sender->media_type(), MediaType::VIDEO);
  std::vector<RtpHeaderExtensionCapability> header_extensions =
      transceiver->GetHeaderExtensionsToNegotiate();
  ASSERT_EQ(header_extensions.size(), 2u);
  EXPECT_EQ(header_extensions[0].uri, RtpExtension::kDependencyDescriptorUri);
  EXPECT_EQ(header_extensions[0].direction, RtpTransceiverDirection::kStopped);
  EXPECT_EQ(header_extensions[1].uri, RtpExtension::kVideoLayersAllocationUri);
  EXPECT_EQ(header_extensions[1].direction, RtpTransceiverDirection::kStopped);

  // Simulcast, i.e. more than one encoding.
  RtpParameters simulcast_parameters;
  simulcast_parameters.encodings.resize(2);
  auto simulcast_sender = MockSender(MediaType::VIDEO);
  EXPECT_CALL(*simulcast_sender, GetParametersInternal(_, _))
      .WillRepeatedly(Return(simulcast_parameters));
  auto simulcast_transceiver = make_ref_counted<RtpTransceiver>(
      env(),
      RtpSenderProxyWithInternal<RtpSenderInternal>::Create(Thread::Current(),
                                                            simulcast_sender),
      RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
          Thread::Current(), Thread::Current(), receiver),
      context(), codec_lookup_helper(), extensions,
      /* on_negotiation_needed= */ [] {});
  auto simulcast_extensions =
      simulcast_transceiver->GetHeaderExtensionsToNegotiate();
  ASSERT_EQ(simulcast_extensions.size(), 2u);
  EXPECT_EQ(simulcast_extensions[0].uri,
            RtpExtension::kDependencyDescriptorUri);
  EXPECT_EQ(simulcast_extensions[0].direction,
            RtpTransceiverDirection::kSendRecv);
  EXPECT_EQ(simulcast_extensions[1].uri,
            RtpExtension::kVideoLayersAllocationUri);
  EXPECT_EQ(simulcast_extensions[1].direction,
            RtpTransceiverDirection::kSendRecv);

  // SVC, a single encoding with a scalabilityMode other than L1T1.
  RtpParameters svc_parameters;
  svc_parameters.encodings.resize(1);
  svc_parameters.encodings[0].scalability_mode = "L3T3";

  auto svc_sender = MockSender(MediaType::VIDEO);
  EXPECT_CALL(*svc_sender, GetParametersInternal(_, _))
      .WillRepeatedly(Return(svc_parameters));
  auto svc_transceiver = make_ref_counted<RtpTransceiver>(
      env(),
      RtpSenderProxyWithInternal<RtpSenderInternal>::Create(Thread::Current(),
                                                            svc_sender),
      RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
          Thread::Current(), Thread::Current(), receiver),
      context(), codec_lookup_helper(), extensions,
      /* on_negotiation_needed= */ [] {});
  std::vector<RtpHeaderExtensionCapability> svc_extensions =
      svc_transceiver->GetHeaderExtensionsToNegotiate();
  ASSERT_EQ(svc_extensions.size(), 2u);
  EXPECT_EQ(svc_extensions[0].uri, RtpExtension::kDependencyDescriptorUri);
  EXPECT_EQ(svc_extensions[0].direction, RtpTransceiverDirection::kSendRecv);
  EXPECT_EQ(svc_extensions[1].uri, RtpExtension::kVideoLayersAllocationUri);
  EXPECT_EQ(svc_extensions[1].direction, RtpTransceiverDirection::kSendRecv);
}

TEST_F(RtpTransceiverTestWithFakeCall,
       SetChannelAppliesAudioOptionsToSendChannel) {
  AudioOptions audio_options;
  audio_options.audio_network_adaptor = true;

  auto transceiver = CreateAudioTransceiverWithChannel(
      env(), call_.get(), context(), codec_lookup_helper(), audio_options);

  ASSERT_TRUE(transceiver->HasChannel());
  auto* voice_channel = transceiver->voice_media_send_channel();
  ASSERT_TRUE(voice_channel);
  auto* fake_channel = static_cast<FakeVoiceMediaSendChannel*>(voice_channel);
  EXPECT_TRUE(fake_channel->options().audio_network_adaptor);

  ScopedOperationsBatcher worker_tasks(context()->worker_thread());
  ScopedOperationsBatcher network_tasks(context()->network_thread());
  network_tasks.Add(transceiver->GetClearChannelNetworkTask());
  worker_tasks.Add(
      transceiver->GetDeleteChannelWorkerTask(/*stop_senders=*/true));
}

TEST_F(RtpTransceiverTestWithFakeCall, OnNetworkRouteChangedForwardsToChannel) {
  const std::string content_name("my_mid");

  auto fake_dtls = std::make_unique<FakeDtlsTransport>("test_transport"false);
  auto rtp_transport = std::make_unique<RtpTransport>(/*rtcp_mux_enabled=*/true,
                                                      env().field_trials());
  rtp_transport->SetRtpPacketTransport(fake_dtls.get());

  auto transceiver = CreateAudioTransceiverWithChannel(
      env(), call_.get(), context(), codec_lookup_helper(), AudioOptions(),
      [&]() -> RtpTransportInternal* { return rtp_transport.get(); });

  ASSERT_TRUE(transceiver->HasChannel());
  auto* voice_channel = transceiver->voice_media_send_channel();
  ASSERT_TRUE(voice_channel);
  auto* fake_channel = static_cast<FakeVoiceMediaSendChannel*>(voice_channel);

  NetworkRoute network_route;
  network_route.connected = true;
  network_route.local = RouteEndpoint::CreateWithNetworkId(1);
  network_route.remote = RouteEndpoint::CreateWithNetworkId(2);
  network_route.last_sent_packet_id = 100;
  network_route.packet_overhead = 28;

  // Trigger network route change on the transport on the network thread.
  context()->network_thread()->BlockingCall([&]() {
    fake_dtls->ice_transport()->NotifyNetworkRouteChanged(
        std::optional<NetworkRoute>(network_route));
  });

  // Verify that the channel received it.
  EXPECT_EQ(1, fake_channel->num_network_route_changes());
  EXPECT_TRUE(fake_channel->last_network_route().connected);
  EXPECT_EQ(1, fake_channel->last_network_route().local.network_id());
  EXPECT_EQ(2, fake_channel->last_network_route().remote.network_id());
  EXPECT_EQ(100, fake_channel->last_network_route().last_sent_packet_id);
  EXPECT_EQ(28, fake_channel->transport_overhead_per_packet());

  ScopedOperationsBatcher worker_tasks(context()->worker_thread());
  ScopedOperationsBatcher network_tasks(context()->network_thread());
  network_tasks.Add(transceiver->GetClearChannelNetworkTask());
  worker_tasks.Add(
      transceiver->GetDeleteChannelWorkerTask(/*stop_senders=*/true));
}

TEST_F(RtpTransceiverTestWithFakeCall,
       OnNetworkRouteChangedForwardsToVideoChannel) {
  const std::string content_name("my_mid");

  auto fake_dtls = std::make_unique<FakeDtlsTransport>("test_transport"false);
  auto rtp_transport = std::make_unique<RtpTransport>(/*rtcp_mux_enabled=*/true,
                                                      env().field_trials());
  rtp_transport->SetRtpPacketTransport(fake_dtls.get());

  auto transceiver = CreateVideoTransceiverWithChannel(
      env(), call_.get(), context(), codec_lookup_helper(), VideoOptions(),
      [&]() -> RtpTransportInternal* { return rtp_transport.get(); });

  ASSERT_TRUE(transceiver->HasChannel());
  auto* video_channel = transceiver->video_media_send_channel();
  ASSERT_TRUE(video_channel);
  auto* fake_channel = static_cast<FakeVideoMediaSendChannel*>(video_channel);

  NetworkRoute network_route;
  network_route.connected = true;
  network_route.local = RouteEndpoint::CreateWithNetworkId(1);
  network_route.remote = RouteEndpoint::CreateWithNetworkId(2);
  network_route.last_sent_packet_id = 100;
  network_route.packet_overhead = 28;

  // Trigger network route change on the transport on the network thread.
  context()->network_thread()->BlockingCall([&]() {
    fake_dtls->ice_transport()->NotifyNetworkRouteChanged(
        std::optional<NetworkRoute>(network_route));
  });

  // Verify that the channel received it.
  EXPECT_EQ(1, fake_channel->num_network_route_changes());
  EXPECT_TRUE(fake_channel->last_network_route().connected);
  EXPECT_EQ(1, fake_channel->last_network_route().local.network_id());
  EXPECT_EQ(2, fake_channel->last_network_route().remote.network_id());
  EXPECT_EQ(100, fake_channel->last_network_route().last_sent_packet_id);
  EXPECT_EQ(28, fake_channel->transport_overhead_per_packet());

  ScopedOperationsBatcher worker_tasks(context()->worker_thread());
  ScopedOperationsBatcher network_tasks(context()->network_thread());
  network_tasks.Add(transceiver->GetClearChannelNetworkTask());
  worker_tasks.Add(
      transceiver->GetDeleteChannelWorkerTask(/*stop_senders=*/true));
}

TEST_F(RtpTransceiverDoubleThreadTest,
       OnNetworkRouteChangedForwardsToChannel_DoubleThread) {
  const std::string content_name("my_mid");
  RTC_LOG(LS_INFO) << "TestBody current_thread=" << Thread::Current();
  RTC_LOG(LS_INFO) << "TestBody signaling_thread="
                   << context()->signaling_thread();
  RTC_LOG(LS_INFO) << "TestBody network_thread=" << context()->network_thread();

  std::unique_ptr<FakeDtlsTransport> fake_dtls;
  auto rtp_transport = std::make_unique<RtpTransport>(/*rtcp_mux_enabled=*/true,
                                                      env().field_trials());
  context()->network_thread()->BlockingCall([&]() {
    fake_dtls = std::make_unique<FakeDtlsTransport>(
        "test_transport"0, context()->network_thread());
    rtp_transport->SetRtpPacketTransport(fake_dtls.get());
  });

  auto call = std::make_unique<FakeCall>(env(), context()->worker_thread(),
                                         context()->network_thread());

  auto transceiver = CreateAudioTransceiverWithChannel(
      env(), call.get(), context(), codec_lookup_helper(), AudioOptions(),
      [&]() -> RtpTransportInternal* { return rtp_transport.get(); });

  ASSERT_TRUE(transceiver->HasChannel());
  auto* voice_channel = transceiver->voice_media_send_channel();
  ASSERT_TRUE(voice_channel);
  auto* fake_channel = static_cast<FakeVoiceMediaSendChannel*>(voice_channel);

  NetworkRoute network_route;
  network_route.connected = true;
  network_route.local = RouteEndpoint::CreateWithNetworkId(1);
  network_route.remote = RouteEndpoint::CreateWithNetworkId(2);
  network_route.last_sent_packet_id = 100;
  network_route.packet_overhead = 28;

  // Trigger network route change on the transport on the network thread.
  context()->network_thread()->BlockingCall([&]() {
    fake_dtls->ice_transport()->NotifyNetworkRouteChanged(
        std::optional<NetworkRoute>(network_route));
  });

  // Verify that the channel received it.
  int changes = context()->network_thread()->BlockingCall(
      [&]() { return fake_channel->num_network_route_changes(); });
  EXPECT_EQ(1, changes);

  NetworkRoute last_route = context()->network_thread()->BlockingCall(
      [&]() { return fake_channel->last_network_route(); });
  EXPECT_TRUE(last_route.connected);
  EXPECT_EQ(1, last_route.local.network_id());
  EXPECT_EQ(2, last_route.remote.network_id());
  EXPECT_EQ(100, last_route.last_sent_packet_id);

  int overhead = context()->network_thread()->BlockingCall(
      [&]() { return fake_channel->transport_overhead_per_packet(); });
  EXPECT_EQ(28, overhead);

  ScopedOperationsBatcher worker_tasks(context()->worker_thread());
  ScopedOperationsBatcher network_tasks(context()->network_thread());
  network_tasks.Add(transceiver->GetClearChannelNetworkTask());
  worker_tasks.Add(
      transceiver->GetDeleteChannelWorkerTask(/*stop_senders=*/true));

  RTC_CHECK(network_tasks.Run().ok());
  RTC_CHECK(worker_tasks.Run().ok());

  context()->network_thread()->BlockingCall([&]() { fake_dtls.reset(); });
}

// Sframe tests

TEST_F(RtpTransceiverUnifiedPlanTest, SframeEnabledIsNulloptByDefault) {
  scoped_refptr<RtpTransceiver> transceiver = CreateTransceiver(
      MockSender(MediaType::AUDIO), MockReceiver(MediaType::AUDIO));

  EXPECT_EQ(transceiver->SframeEnabled(), std::nullopt);
}

TEST_F(RtpTransceiverUnifiedPlanTest, TryToEnableSframeSetsValueToTrue) {
  scoped_refptr<RtpTransceiver> transceiver = CreateTransceiver(
      MockSender(MediaType::AUDIO), MockReceiver(MediaType::AUDIO));

  EXPECT_TRUE(transceiver->TryToEnableSframe().ok());
  EXPECT_THAT(transceiver->SframeEnabled(), Optional(true));
}

TEST_F(RtpTransceiverUnifiedPlanTest,
       TryToEnableSframeCanBeCalledMultipleTimes) {
  scoped_refptr<RtpTransceiver> transceiver = CreateTransceiver(
      MockSender(MediaType::AUDIO), MockReceiver(MediaType::AUDIO));

  EXPECT_TRUE(transceiver->TryToEnableSframe().ok());
  EXPECT_TRUE(transceiver->TryToEnableSframe().ok());
  EXPECT_THAT(transceiver->SframeEnabled(), Optional(true));
}

TEST_F(RtpTransceiverUnifiedPlanTest,
       TryToEnableSframeFailsAfterExplicitlySetToFalse) {
  scoped_refptr<RtpTransceiver> transceiver = CreateTransceiver(
      MockSender(MediaType::AUDIO), MockReceiver(MediaType::AUDIO));

  // Simulate the transceiver having been explicitly set to false (e.g. via
  // ApplySframeEnabled during SDP application).
  transceiver->ApplySframeEnabled(false);
  EXPECT_THAT(transceiver->SframeEnabled(), Optional(false));

  RTCError error = transceiver->TryToEnableSframe();
  EXPECT_FALSE(error.ok());
  EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
  EXPECT_THAT(transceiver->SframeEnabled(), Optional(false));
}

TEST_F(RtpTransceiverUnifiedPlanTest, ApplySframeEnabledTrueSetsState) {
  scoped_refptr<RtpTransceiver> transceiver = CreateTransceiver(
      MockSender(MediaType::AUDIO), MockReceiver(MediaType::AUDIO));

  transceiver->ApplySframeEnabled(true);
  EXPECT_THAT(transceiver->SframeEnabled(), Optional(true));
}

TEST_F(RtpTransceiverUnifiedPlanTest, ApplySframeEnabledFalseSetsState) {
  scoped_refptr<RtpTransceiver> transceiver = CreateTransceiver(
      MockSender(MediaType::AUDIO), MockReceiver(MediaType::AUDIO));

  transceiver->ApplySframeEnabled(false);
  EXPECT_THAT(transceiver->SframeEnabled(), Optional(false));
}

}  // namespace

}  // namespace webrtc

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

¤ Dauer der Verarbeitung: 0.7 Sekunden  (vorverarbeitet am  2026-08-23) ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=655579