webrtc_m130/pc/rtp_transceiver_unittest.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

882 lines
39 KiB
C++
Raw Normal View History

/*
* 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.
*/
Reland "Use backticks not vertical bars to denote variables in comments for /pc" Original change's description: > Revert "Use backticks not vertical bars to denote variables in comments for /pc" > > This reverts commit 37ee0f5e594dd772ec6d620b5e5ea8a751b684f0. > > Reason for revert: Revert in order to be able to revert https://webrtc-review.googlesource.com/c/src/+/225642 > > Original change's description: > > Use backticks not vertical bars to denote variables in comments for /pc > > > > Bug: webrtc:12338 > > Change-Id: I88cf10afa5fc810b95d2a585ab2e895dcc163b63 > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/226953 > > Reviewed-by: Harald Alvestrand <hta@webrtc.org> > > Commit-Queue: Artem Titov <titovartem@webrtc.org> > > Cr-Commit-Position: refs/heads/master@{#34575} > > TBR=hta@webrtc.org,titovartem@webrtc.org,webrtc-scoped@luci-project-accounts.iam.gserviceaccount.com > > Change-Id: I5eddd3a14e1f664bf831e5c294fbc4de5f6a88af > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: webrtc:12338 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227082 > Reviewed-by: Björn Terelius <terelius@webrtc.org> > Commit-Queue: Björn Terelius <terelius@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#34577} Bug: webrtc:12338 Change-Id: I96bd229b73613c162d11d75fa4f5934e1b4295c7 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227087 Commit-Queue: Artem Titov <titovartem@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34611}
2021-07-30 22:30:23 +02:00
// This file contains tests for `RtpTransceiver`.
#include "pc/rtp_transceiver.h"
#include <memory>
#include <optional>
#include <utility>
#include "absl/strings/string_view.h"
#include "api/environment/environment_factory.h"
#include "api/peer_connection_interface.h"
#include "api/rtp_parameters.h"
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
#include "api/test/rtc_error_matchers.h"
#include "media/base/codec_comparators.h"
#include "media/base/fake_media_engine.h"
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
#include "pc/rtp_parameters_conversion.h"
#include "pc/test/enable_fake_media.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/thread.h"
#include "test/gmock.h"
#include "test/gtest.h"
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::NiceMock;
using ::testing::Optional;
using ::testing::Property;
using ::testing::Return;
using ::testing::ReturnRef;
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
using ::testing::SizeIs;
namespace webrtc {
namespace {
class RtpTransceiverTest : public testing::Test {
public:
RtpTransceiverTest()
: dependencies_(MakeDependencies()),
context_(
ConnectionContext::Create(CreateEnvironment(), &dependencies_)) {}
protected:
cricket::FakeMediaEngine* media_engine() {
// We know this cast is safe because we supplied the fake implementation
// in MakeDependencies().
return static_cast<cricket::FakeMediaEngine*>(context_->media_engine());
}
ConnectionContext* context() { return context_.get(); }
private:
rtc::AutoThread main_thread_;
static PeerConnectionFactoryDependencies MakeDependencies() {
PeerConnectionFactoryDependencies d;
d.network_thread = rtc::Thread::Current();
d.worker_thread = rtc::Thread::Current();
d.signaling_thread = rtc::Thread::Current();
EnableFakeMedia(d, std::make_unique<cricket::FakeMediaEngine>());
return d;
}
PeerConnectionFactoryDependencies dependencies_;
rtc::scoped_refptr<ConnectionContext> context_;
};
Reland "Use backticks not vertical bars to denote variables in comments for /pc" Original change's description: > Revert "Use backticks not vertical bars to denote variables in comments for /pc" > > This reverts commit 37ee0f5e594dd772ec6d620b5e5ea8a751b684f0. > > Reason for revert: Revert in order to be able to revert https://webrtc-review.googlesource.com/c/src/+/225642 > > Original change's description: > > Use backticks not vertical bars to denote variables in comments for /pc > > > > Bug: webrtc:12338 > > Change-Id: I88cf10afa5fc810b95d2a585ab2e895dcc163b63 > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/226953 > > Reviewed-by: Harald Alvestrand <hta@webrtc.org> > > Commit-Queue: Artem Titov <titovartem@webrtc.org> > > Cr-Commit-Position: refs/heads/master@{#34575} > > TBR=hta@webrtc.org,titovartem@webrtc.org,webrtc-scoped@luci-project-accounts.iam.gserviceaccount.com > > Change-Id: I5eddd3a14e1f664bf831e5c294fbc4de5f6a88af > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: webrtc:12338 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227082 > Reviewed-by: Björn Terelius <terelius@webrtc.org> > Commit-Queue: Björn Terelius <terelius@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#34577} Bug: webrtc:12338 Change-Id: I96bd229b73613c162d11d75fa4f5934e1b4295c7 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227087 Commit-Queue: Artem Titov <titovartem@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34611}
2021-07-30 22:30:23 +02:00
// Checks that a channel cannot be set on a stopped `RtpTransceiver`.
TEST_F(RtpTransceiverTest, CannotSetChannelOnStoppedTransceiver) {
const std::string content_name("my_mid");
auto transceiver = rtc::make_ref_counted<RtpTransceiver>(
cricket::MediaType::MEDIA_TYPE_AUDIO, context());
auto channel1 = std::make_unique<NiceMock<cricket::MockChannelInterface>>();
EXPECT_CALL(*channel1, media_type())
.WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO));
EXPECT_CALL(*channel1, mid()).WillRepeatedly(ReturnRef(content_name));
EXPECT_CALL(*channel1, SetFirstPacketReceivedCallback(_));
EXPECT_CALL(*channel1, SetRtpTransport(_)).WillRepeatedly(Return(true));
auto channel1_ptr = channel1.get();
transceiver->SetChannel(std::move(channel1), [&](const std::string& mid) {
EXPECT_EQ(mid, content_name);
return nullptr;
});
EXPECT_EQ(channel1_ptr, transceiver->channel());
// Stop the transceiver.
transceiver->StopInternal();
EXPECT_EQ(channel1_ptr, transceiver->channel());
auto channel2 = std::make_unique<NiceMock<cricket::MockChannelInterface>>();
EXPECT_CALL(*channel2, media_type())
.WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO));
// Clear the current channel - required to allow SetChannel()
EXPECT_CALL(*channel1_ptr, SetFirstPacketReceivedCallback(_));
transceiver->ClearChannel();
ASSERT_EQ(nullptr, transceiver->channel());
// Channel can no longer be set, so this call should be a no-op.
transceiver->SetChannel(std::move(channel2),
[](const std::string&) { return nullptr; });
EXPECT_EQ(nullptr, transceiver->channel());
}
Reland "Use backticks not vertical bars to denote variables in comments for /pc" Original change's description: > Revert "Use backticks not vertical bars to denote variables in comments for /pc" > > This reverts commit 37ee0f5e594dd772ec6d620b5e5ea8a751b684f0. > > Reason for revert: Revert in order to be able to revert https://webrtc-review.googlesource.com/c/src/+/225642 > > Original change's description: > > Use backticks not vertical bars to denote variables in comments for /pc > > > > Bug: webrtc:12338 > > Change-Id: I88cf10afa5fc810b95d2a585ab2e895dcc163b63 > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/226953 > > Reviewed-by: Harald Alvestrand <hta@webrtc.org> > > Commit-Queue: Artem Titov <titovartem@webrtc.org> > > Cr-Commit-Position: refs/heads/master@{#34575} > > TBR=hta@webrtc.org,titovartem@webrtc.org,webrtc-scoped@luci-project-accounts.iam.gserviceaccount.com > > Change-Id: I5eddd3a14e1f664bf831e5c294fbc4de5f6a88af > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: webrtc:12338 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227082 > Reviewed-by: Björn Terelius <terelius@webrtc.org> > Commit-Queue: Björn Terelius <terelius@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#34577} Bug: webrtc:12338 Change-Id: I96bd229b73613c162d11d75fa4f5934e1b4295c7 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227087 Commit-Queue: Artem Titov <titovartem@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34611}
2021-07-30 22:30:23 +02:00
// Checks that a channel can be unset on a stopped `RtpTransceiver`
TEST_F(RtpTransceiverTest, CanUnsetChannelOnStoppedTransceiver) {
const std::string content_name("my_mid");
auto transceiver = rtc::make_ref_counted<RtpTransceiver>(
cricket::MediaType::MEDIA_TYPE_VIDEO, context());
auto channel = std::make_unique<NiceMock<cricket::MockChannelInterface>>();
EXPECT_CALL(*channel, media_type())
.WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_VIDEO));
EXPECT_CALL(*channel, mid()).WillRepeatedly(ReturnRef(content_name));
EXPECT_CALL(*channel, SetFirstPacketReceivedCallback(_))
.WillRepeatedly(testing::Return());
EXPECT_CALL(*channel, SetRtpTransport(_)).WillRepeatedly(Return(true));
auto channel_ptr = channel.get();
transceiver->SetChannel(std::move(channel), [&](const std::string& mid) {
EXPECT_EQ(mid, content_name);
return nullptr;
});
EXPECT_EQ(channel_ptr, transceiver->channel());
// Stop the transceiver.
transceiver->StopInternal();
EXPECT_EQ(channel_ptr, transceiver->channel());
Reland "Use backticks not vertical bars to denote variables in comments for /pc" Original change's description: > Revert "Use backticks not vertical bars to denote variables in comments for /pc" > > This reverts commit 37ee0f5e594dd772ec6d620b5e5ea8a751b684f0. > > Reason for revert: Revert in order to be able to revert https://webrtc-review.googlesource.com/c/src/+/225642 > > Original change's description: > > Use backticks not vertical bars to denote variables in comments for /pc > > > > Bug: webrtc:12338 > > Change-Id: I88cf10afa5fc810b95d2a585ab2e895dcc163b63 > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/226953 > > Reviewed-by: Harald Alvestrand <hta@webrtc.org> > > Commit-Queue: Artem Titov <titovartem@webrtc.org> > > Cr-Commit-Position: refs/heads/master@{#34575} > > TBR=hta@webrtc.org,titovartem@webrtc.org,webrtc-scoped@luci-project-accounts.iam.gserviceaccount.com > > Change-Id: I5eddd3a14e1f664bf831e5c294fbc4de5f6a88af > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: webrtc:12338 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227082 > Reviewed-by: Björn Terelius <terelius@webrtc.org> > Commit-Queue: Björn Terelius <terelius@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#34577} Bug: webrtc:12338 Change-Id: I96bd229b73613c162d11d75fa4f5934e1b4295c7 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227087 Commit-Queue: Artem Titov <titovartem@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34611}
2021-07-30 22:30:23 +02:00
// Set the channel to `nullptr`.
transceiver->ClearChannel();
EXPECT_EQ(nullptr, transceiver->channel());
}
class RtpTransceiverUnifiedPlanTest : public RtpTransceiverTest {
public:
static rtc::scoped_refptr<MockRtpReceiverInternal> MockReceiver(
cricket::MediaType media_type) {
auto receiver = rtc::make_ref_counted<NiceMock<MockRtpReceiverInternal>>();
EXPECT_CALL(*receiver.get(), media_type())
.WillRepeatedly(Return(media_type));
return receiver;
}
static rtc::scoped_refptr<MockRtpSenderInternal> MockSender(
cricket::MediaType media_type) {
auto sender = rtc::make_ref_counted<NiceMock<MockRtpSenderInternal>>();
EXPECT_CALL(*sender.get(), media_type()).WillRepeatedly(Return(media_type));
return sender;
}
rtc::scoped_refptr<RtpTransceiver> CreateTransceiver(
rtc::scoped_refptr<RtpSenderInternal> sender,
rtc::scoped_refptr<RtpReceiverInternal> receiver) {
return rtc::make_ref_counted<RtpTransceiver>(
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
rtc::Thread::Current(), std::move(sender)),
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
rtc::Thread::Current(), rtc::Thread::Current(),
std::move(receiver)),
context(), media_engine()->voice().GetRtpHeaderExtensions(),
/* on_negotiation_needed= */ [] {});
}
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
protected:
rtc::AutoThread main_thread_;
};
// Basic tests for Stop()
TEST_F(RtpTransceiverUnifiedPlanTest, StopSetsDirection) {
rtc::scoped_refptr<MockRtpReceiverInternal> receiver =
MockReceiver(cricket::MediaType::MEDIA_TYPE_AUDIO);
rtc::scoped_refptr<MockRtpSenderInternal> sender =
MockSender(cricket::MediaType::MEDIA_TYPE_AUDIO);
rtc::scoped_refptr<RtpTransceiver> transceiver =
CreateTransceiver(sender, receiver);
EXPECT_CALL(*receiver.get(), Stop());
EXPECT_CALL(*receiver.get(), SetMediaChannel(_));
EXPECT_CALL(*sender.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender.get(), Stop());
EXPECT_EQ(RtpTransceiverDirection::kInactive, transceiver->direction());
EXPECT_FALSE(transceiver->current_direction());
transceiver->StopStandard();
EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver->direction());
EXPECT_FALSE(transceiver->current_direction());
transceiver->StopTransceiverProcedure();
EXPECT_TRUE(transceiver->current_direction());
EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver->direction());
EXPECT_EQ(RtpTransceiverDirection::kStopped,
*transceiver->current_direction());
}
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
class RtpTransceiverFilteredCodecPreferencesTest
: public RtpTransceiverUnifiedPlanTest {
public:
RtpTransceiverFilteredCodecPreferencesTest()
: transceiver_(CreateTransceiver(
MockSender(cricket::MediaType::MEDIA_TYPE_VIDEO),
MockReceiver(cricket::MediaType::MEDIA_TYPE_VIDEO))) {}
struct H264CodecCapabilities {
cricket::Codec cricket_sendrecv_codec;
RtpCodecCapability sendrecv_codec;
cricket::Codec cricket_sendonly_codec;
RtpCodecCapability sendonly_codec;
cricket::Codec cricket_recvonly_codec;
RtpCodecCapability recvonly_codec;
cricket::Codec cricket_rtx_codec;
RtpCodecCapability rtx_codec;
};
// 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.
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
H264CodecCapabilities ConfigureH264CodecCapabilities() {
cricket::Codec cricket_sendrecv_codec = cricket::CreateVideoCodec(
SdpVideoFormat("H264",
{{"level-asymmetry-allowed", "1"},
{"packetization-mode", "1"},
{"profile-level-id", "42f00b"}},
{ScalabilityMode::kL1T1}));
cricket::Codec cricket_sendonly_codec = cricket::CreateVideoCodec(
SdpVideoFormat("H264",
{{"level-asymmetry-allowed", "1"},
{"packetization-mode", "1"},
{"profile-level-id", "640034"}},
{ScalabilityMode::kL1T1}));
cricket::Codec cricket_recvonly_codec = cricket::CreateVideoCodec(
SdpVideoFormat("H264",
{{"level-asymmetry-allowed", "1"},
{"packetization-mode", "1"},
{"profile-level-id", "f4001f"}},
{ScalabilityMode::kL1T1}));
cricket::Codec cricket_rtx_codec = cricket::CreateVideoRtxCodec(
cricket::Codec::kIdNotSet, cricket::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 = {
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
.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));
return capabilities;
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
}
#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";
cricket::Codec cricket_sendonly_codec;
RtpCodecCapability sendonly_codec;
cricket::Codec cricket_recvonly_codec;
RtpCodecCapability recvonly_codec;
};
// For H265, the profile and level IDs are separate and are ignored by
// IsSameRtpCodecIgnoringLevel().
H265CodecCapabilities ConfigureH265CodecCapabilities() {
cricket::Codec cricket_sendonly_codec = cricket::CreateVideoCodec(
SdpVideoFormat("H265",
{{"profile-id", "1"},
{"tier-flag", "0"},
{"level-id", H265CodecCapabilities::kSendOnlyLevel},
{"tx-mode", "SRST"}},
{ScalabilityMode::kL1T1}));
cricket::Codec cricket_recvonly_codec = cricket::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});
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:
rtc::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.
cricket::Codec baseline_3_1 = cricket::CreateVideoCodec(
SdpVideoFormat("H264",
{{"level-asymmetry-allowed", "1"},
{"packetization-mode", "1"},
{"profile-level-id", "42001f"}},
{ScalabilityMode::kL1T1}));
cricket::Codec baseline_5_2 = cricket::CreateVideoCodec(
SdpVideoFormat("H264",
{{"level-asymmetry-allowed", "1"},
{"packetization-mode", "1"},
{"profile-level-id", "420034"}},
{ScalabilityMode::kL1T1}));
// High is NOT compatible with baseline.
cricket::Codec high_3_1 = cricket::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});
// 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]));
}
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
#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) {
Add unidirectional codec support ("offer to send" use case). This CL implements allowing sendonly codecs in setCodecPreferences(), i.e. this spec PR: https://github.com/w3c/webrtc-pc/pull/3018. It also makes the setCodecPreferences() ignore level IDs in the filtering algorithm (but not in the sCP method call) as per this spec PR: https://github.com/w3c/webrtc-pc/pull/3023. In short, before this CL, setCodecPreferences() threw an exception if a codec was preferred that is not present in receiver codec capabilities. After this CL, setCodecPreferences() allows you to prefer codecs that are *either* in the sender capabilities *or* the receiver capabilities. - This allows you to "offer to send", i.e. prefer sendonly codecs on a sendonly transceiver. - The filtering on direction is handled by RtpTransceiver::filtered_codec_preferences() which is called during SDP offer/answer (sdp_offer_answer.cc). Also as per spec changes, if this filtering results in not having any codecs to offer or answer then this results in not having any codec preferences as opposed to throwing an exception (old behavior). - Two old peer_connection_media_unittest.cc tests are updated to reflect the API failing less. This CL adds both unit tests (rtp_transceiver_unittest.cc) and full stack integration tests (peer_connection_encodings_integrationtest.cc). It also makes us pass the following Web Platform Tests in Chrome: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/webrtc/protocol/h265-level-id.https.html Bug: chromium:381407888 Change-Id: I98a5ad1acccb56db0538e4d47975b8a725102c33 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/374520 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43788}
2025-01-22 15:13:37 +01:00
ConfigureH265CodecCapabilities();
cricket::Codec cricket_codec = cricket::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_(rtc::make_ref_counted<RtpTransceiver>(
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
rtc::Thread::Current(),
sender_),
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
rtc::Thread::Current(),
rtc::Thread::Current(),
receiver_),
context(),
extensions_,
/* on_negotiation_needed= */ [] {})) {}
void ClearChannel() {
EXPECT_CALL(*sender_.get(), SetMediaChannel(_));
transceiver_->ClearChannel();
}
rtc::scoped_refptr<MockRtpReceiverInternal> receiver_ =
MockReceiver(cricket::MediaType::MEDIA_TYPE_AUDIO);
rtc::scoped_refptr<MockRtpSenderInternal> sender_ =
MockSender(cricket::MediaType::MEDIA_TYPE_AUDIO);
std::vector<RtpHeaderExtensionCapability> extensions_;
rtc::scoped_refptr<RtpTransceiver> transceiver_;
};
TEST_F(RtpTransceiverTestForHeaderExtensions, OffersChannelManagerList) {
EXPECT_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
EXPECT_EQ(transceiver_->GetHeaderExtensionsToNegotiate(), extensions_);
}
TEST_F(RtpTransceiverTestForHeaderExtensions, ModifiesDirection) {
EXPECT_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
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) {
EXPECT_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
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) {
EXPECT_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
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) {
EXPECT_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
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) {
EXPECT_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
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) {
EXPECT_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
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_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
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");
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)).WillRepeatedly(Return());
EXPECT_CALL(*receiver_.get(), Stop()).WillRepeatedly(Return());
EXPECT_CALL(*sender_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
auto mock_channel =
std::make_unique<NiceMock<cricket::MockChannelInterface>>();
auto mock_channel_ptr = mock_channel.get();
EXPECT_CALL(*mock_channel, SetFirstPacketReceivedCallback(_));
EXPECT_CALL(*mock_channel, media_type())
.WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_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_->SetChannel(std::move(mock_channel),
[](const std::string&) { return nullptr; });
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)));
EXPECT_CALL(*mock_channel_ptr, SetFirstPacketReceivedCallback(_));
ClearChannel();
}
TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) {
const std::string content_name("my_mid");
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)).WillRepeatedly(Return());
EXPECT_CALL(*receiver_.get(), Stop()).WillRepeatedly(Return());
EXPECT_CALL(*sender_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
auto mock_channel =
std::make_unique<NiceMock<cricket::MockChannelInterface>>();
auto mock_channel_ptr = mock_channel.get();
EXPECT_CALL(*mock_channel, SetFirstPacketReceivedCallback(_));
EXPECT_CALL(*mock_channel, media_type())
.WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_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));
cricket::RtpHeaderExtensions extensions = {RtpExtension("uri1", 1),
RtpExtension("uri2", 2)};
cricket::AudioContentDescription description;
description.set_rtp_header_extensions(extensions);
transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description);
transceiver_->SetChannel(std::move(mock_channel),
[](const std::string&) { return nullptr; });
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)));
EXPECT_CALL(*mock_channel_ptr, SetFirstPacketReceivedCallback(_));
ClearChannel();
}
TEST_F(RtpTransceiverTestForHeaderExtensions,
ReturnsNegotiatedHdrExtsSecondTime) {
EXPECT_CALL(*receiver_.get(), Stop());
EXPECT_CALL(*receiver_.get(), SetMediaChannel(_));
EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped());
EXPECT_CALL(*sender_.get(), Stop());
cricket::RtpHeaderExtensions extensions = {RtpExtension("uri1", 1),
RtpExtension("uri2", 2)};
cricket::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 = rtc::make_ref_counted<NiceMock<MockRtpSenderInternal>>();
auto transceiver = rtc::make_ref_counted<RtpTransceiver>(
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
rtc::Thread::Current(), sender),
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
rtc::Thread::Current(), rtc::Thread::Current(), receiver_),
context(), extensions,
/* on_negotiation_needed= */ [] {});
std::vector<webrtc::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 =
rtc::make_ref_counted<NiceMock<MockRtpSenderInternal>>();
EXPECT_CALL(*simulcast_sender, GetParametersInternal())
.WillRepeatedly(Return(simulcast_parameters));
auto simulcast_transceiver = rtc::make_ref_counted<RtpTransceiver>(
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
rtc::Thread::Current(), simulcast_sender),
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
rtc::Thread::Current(), rtc::Thread::Current(), receiver_),
context(), 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.
webrtc::RtpParameters svc_parameters;
svc_parameters.encodings.resize(1);
svc_parameters.encodings[0].scalability_mode = "L3T3";
auto svc_sender = rtc::make_ref_counted<NiceMock<MockRtpSenderInternal>>();
EXPECT_CALL(*svc_sender, GetParametersInternal())
.WillRepeatedly(Return(svc_parameters));
auto svc_transceiver = rtc::make_ref_counted<RtpTransceiver>(
RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
rtc::Thread::Current(), svc_sender),
RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
rtc::Thread::Current(), rtc::Thread::Current(), receiver_),
context(), extensions,
/* on_negotiation_needed= */ [] {});
std::vector<webrtc::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);
}
} // namespace
} // namespace webrtc