Add killswitch for receive-only setCodecPreferences change
Adds a killswitch
WebRTC-SetCodecPreferences-ReceiveOnlyFilterInsteadOfThrow
to accompany the spec-change to throw when codec capabilities
are taken from the RtpSender instead of the RtpReceiver.
With the killswitch triggered, such codecs will be filtered.
BUG=webrtc:15396
Change-Id: I7d27111c72085eb7a7b2a1e66d0a08d12883ce17
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/341460
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@microsoft.com>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41845}
diff --git a/experiments/field_trials.py b/experiments/field_trials.py
index 6c935ea..c748cd6 100755
--- a/experiments/field_trials.py
+++ b/experiments/field_trials.py
@@ -131,6 +131,9 @@
FieldTrial('WebRTC-SendPacketsOnWorkerThread',
'webrtc:14502',
date(2024, 4, 1)),
+ FieldTrial('WebRTC-SetCodecPreferences-ReceiveOnlyFilterInsteadOfThrow',
+ 'webrtc:15396',
+ date(2024, 12, 1)),
FieldTrial('WebRTC-SrtpRemoveReceiveStream',
'webrtc:15604',
date(2024, 10, 1)),
diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc
index acefd9e..e114e5d 100644
--- a/pc/peer_connection_media_unittest.cc
+++ b/pc/peer_connection_media_unittest.cc
@@ -176,19 +176,19 @@
EnableFakeMedia(factory_dependencies, std::move(media_engine));
factory_dependencies.event_log_factory =
std::make_unique<RtcEventLogFactory>();
-
+ factory_dependencies.trials = std::move(field_trials_);
auto pc_factory =
CreateModularPeerConnectionFactory(std::move(factory_dependencies));
auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
rtc::Thread::Current(),
- std::make_unique<rtc::BasicPacketSocketFactory>(vss_.get()),
- &field_trials_);
+ std::make_unique<rtc::BasicPacketSocketFactory>(vss_.get()), nullptr);
auto observer = std::make_unique<MockPeerConnectionObserver>();
auto modified_config = config;
modified_config.sdp_semantics = sdp_semantics_;
PeerConnectionDependencies pc_dependencies(observer.get());
pc_dependencies.allocator = std::move(fake_port_allocator);
+
auto result = pc_factory->CreatePeerConnectionOrError(
modified_config, std::move(pc_dependencies));
if (!result.ok()) {
@@ -253,7 +253,7 @@
return sdp_semantics_ == SdpSemantics::kUnifiedPlan;
}
- test::ScopedKeyValueConfig field_trials_;
+ std::unique_ptr<test::ScopedKeyValueConfig> field_trials_;
std::unique_ptr<rtc::VirtualSocketServer> vss_;
rtc::AutoSocketServerThread main_;
const SdpSemantics sdp_semantics_;
@@ -1641,6 +1641,25 @@
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
+ SetCodecPreferencesAudioSendOnlyKillswitch) {
+ field_trials_ = std::make_unique<test::ScopedKeyValueConfig>(
+ "WebRTC-SetCodecPreferences-ReceiveOnlyFilterInsteadOfThrow/Disabled/");
+ auto fake_engine = std::make_unique<FakeMediaEngine>();
+ auto send_codecs = fake_engine->voice().send_codecs();
+ send_codecs.push_back(cricket::CreateAudioCodec(send_codecs.back().id + 1,
+ "send_only_codec", 0, 1));
+ fake_engine->SetAudioSendCodecs(send_codecs);
+
+ auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
+
+ auto transceiver = caller->pc()->GetTransceivers().front();
+ auto send_capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
+ cricket::MediaType::MEDIA_TYPE_AUDIO);
+
+ EXPECT_TRUE(transceiver->SetCodecPreferences(send_capabilities.codecs).ok());
+}
+
+TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesVideoRejectsAudioCodec) {
auto caller = CreatePeerConnectionWithVideo();
@@ -1735,6 +1754,25 @@
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
+ SetCodecPreferencesVideoSendOnlyKillswitch) {
+ field_trials_ = std::make_unique<test::ScopedKeyValueConfig>(
+ "WebRTC-SetCodecPreferences-ReceiveOnlyFilterInsteadOfThrow/Disabled/");
+ auto fake_engine = std::make_unique<FakeMediaEngine>();
+ auto send_codecs = fake_engine->voice().send_codecs();
+ send_codecs.push_back(
+ cricket::CreateVideoCodec(send_codecs.back().id + 1, "send_only_codec"));
+ fake_engine->SetAudioSendCodecs(send_codecs);
+
+ auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
+
+ auto transceiver = caller->pc()->GetTransceivers().front();
+ auto send_capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
+ cricket::MediaType::MEDIA_TYPE_AUDIO);
+
+ EXPECT_TRUE(transceiver->SetCodecPreferences(send_capabilities.codecs).ok());
+}
+
+TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesVideoCodecDuplicatesRemoved) {
auto caller = CreatePeerConnectionWithVideo();
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index 55a0f7d..e585667 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -40,16 +40,17 @@
namespace {
RTCError VerifyCodecPreferences(
- const std::vector<RtpCodecCapability>& codecs,
- const std::vector<cricket::Codec>& recv_codecs) {
+ const std::vector<RtpCodecCapability>& unfiltered_codecs,
+ const std::vector<cricket::Codec>& recv_codecs,
+ const FieldTrialsView& field_trials) {
// If the intersection between codecs and
- // RTCRtpSender.getCapabilities(kind).codecs or the intersection between
- // codecs and RTCRtpReceiver.getCapabilities(kind).codecs contains only
- // contains RTX, RED, FEC or CN codecs or is an empty set, throw
+ // RTCRtpReceiver.getCapabilities(kind).codecs only contains RTX, RED, FEC
+ // codecs or Comfort Noise codecs or is an empty set, throw
// InvalidModificationError.
// This ensures that we always have something to offer, regardless of
// transceiver.direction.
-
+ // TODO(fippo): clean up the filtering killswitch
+ std::vector<RtpCodecCapability> codecs = unfiltered_codecs;
if (!absl::c_any_of(codecs, [&recv_codecs](const RtpCodecCapability& codec) {
return codec.IsMediaCodec() &&
absl::c_any_of(recv_codecs,
@@ -71,10 +72,26 @@
return codec.MatchesRtpCodec(codec_preference);
});
if (!is_recv_codec) {
- LOG_AND_RETURN_ERROR(
- RTCErrorType::INVALID_MODIFICATION,
- std::string("Invalid codec preferences: invalid codec with name \"") +
- codec_preference.name + "\".");
+ if (!field_trials.IsDisabled(
+ "WebRTC-SetCodecPreferences-ReceiveOnlyFilterInsteadOfThrow")) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_MODIFICATION,
+ std::string(
+ "Invalid codec preferences: invalid codec with name \"") +
+ codec_preference.name + "\".");
+ } else {
+ // Killswitch behavior: filter out any codec not in receive codecs.
+ codecs.erase(std::remove_if(
+ codecs.begin(), codecs.end(),
+ [&recv_codecs](const RtpCodecCapability& codec) {
+ return codec.IsMediaCodec() &&
+ !absl::c_any_of(
+ recv_codecs,
+ [&codec](const cricket::Codec& recv_codec) {
+ return recv_codec.MatchesRtpCodec(codec);
+ });
+ }));
+ }
}
}
@@ -670,7 +687,8 @@
} else if (media_type_ == cricket::MEDIA_TYPE_VIDEO) {
recv_codecs = media_engine()->video().recv_codecs(context()->use_rtx());
}
- result = VerifyCodecPreferences(codecs, recv_codecs);
+ result = VerifyCodecPreferences(codecs, recv_codecs,
+ context()->env().field_trials());
if (result.ok()) {
codec_preferences_ = codecs;