1 /*
2 * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 // This file contains tests that verify that field trials do what they're
12 // supposed to do.
13
14 #include <set>
15
16 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
17 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
18 #include "api/create_peerconnection_factory.h"
19 #include "api/peer_connection_interface.h"
20 #include "api/stats/rtcstats_objects.h"
21 #include "api/task_queue/default_task_queue_factory.h"
22 #include "api/video_codecs/builtin_video_decoder_factory.h"
23 #include "api/video_codecs/builtin_video_encoder_factory.h"
24 #include "media/engine/webrtc_media_engine.h"
25 #include "media/engine/webrtc_media_engine_defaults.h"
26 #include "pc/peer_connection_wrapper.h"
27 #include "pc/session_description.h"
28 #include "pc/test/fake_audio_capture_module.h"
29 #include "pc/test/frame_generator_capturer_video_track_source.h"
30 #include "pc/test/peer_connection_test_wrapper.h"
31 #include "rtc_base/gunit.h"
32 #include "rtc_base/internal/default_socket_server.h"
33 #include "rtc_base/physical_socket_server.h"
34 #include "rtc_base/thread.h"
35 #include "test/gtest.h"
36 #include "test/scoped_key_value_config.h"
37
38 #ifdef WEBRTC_ANDROID
39 #include "pc/test/android_test_initializer.h"
40 #endif
41
42 namespace webrtc {
43
44 namespace {
45 static const int kDefaultTimeoutMs = 5000;
46
AddIceCandidates(PeerConnectionWrapper * peer,std::vector<const IceCandidateInterface * > candidates)47 bool AddIceCandidates(PeerConnectionWrapper* peer,
48 std::vector<const IceCandidateInterface*> candidates) {
49 for (const auto candidate : candidates) {
50 if (!peer->pc()->AddIceCandidate(candidate)) {
51 return false;
52 }
53 }
54 return true;
55 }
56 } // namespace
57
58 using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
59
60 class PeerConnectionFieldTrialTest : public ::testing::Test {
61 protected:
62 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
63
PeerConnectionFieldTrialTest()64 PeerConnectionFieldTrialTest()
65 : clock_(Clock::GetRealTimeClock()),
66 socket_server_(rtc::CreateDefaultSocketServer()),
67 main_thread_(socket_server_.get()) {
68 #ifdef WEBRTC_ANDROID
69 InitializeAndroidObjects();
70 #endif
71 webrtc::PeerConnectionInterface::IceServer ice_server;
72 ice_server.uri = "stun:stun.l.google.com:19302";
73 config_.servers.push_back(ice_server);
74 config_.sdp_semantics = SdpSemantics::kUnifiedPlan;
75 }
76
TearDown()77 void TearDown() override { pc_factory_ = nullptr; }
78
CreatePCFactory(std::unique_ptr<FieldTrialsView> field_trials)79 void CreatePCFactory(std::unique_ptr<FieldTrialsView> field_trials) {
80 PeerConnectionFactoryDependencies pcf_deps;
81 pcf_deps.signaling_thread = rtc::Thread::Current();
82 pcf_deps.trials = std::move(field_trials);
83 pcf_deps.task_queue_factory = CreateDefaultTaskQueueFactory();
84 pcf_deps.call_factory = webrtc::CreateCallFactory();
85 cricket::MediaEngineDependencies media_deps;
86 media_deps.task_queue_factory = pcf_deps.task_queue_factory.get();
87 media_deps.adm = FakeAudioCaptureModule::Create();
88 media_deps.trials = pcf_deps.trials.get();
89 webrtc::SetMediaEngineDefaults(&media_deps);
90 pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
91 pc_factory_ = CreateModularPeerConnectionFactory(std::move(pcf_deps));
92
93 // Allow ADAPTER_TYPE_LOOPBACK to create PeerConnections with loopback in
94 // this test.
95 RTC_DCHECK(pc_factory_);
96 PeerConnectionFactoryInterface::Options options;
97 options.network_ignore_mask = 0;
98 pc_factory_->SetOptions(options);
99 }
100
CreatePeerConnection()101 WrapperPtr CreatePeerConnection() {
102 auto observer = std::make_unique<MockPeerConnectionObserver>();
103 auto result = pc_factory_->CreatePeerConnectionOrError(
104 config_, PeerConnectionDependencies(observer.get()));
105 RTC_CHECK(result.ok());
106
107 observer->SetPeerConnectionInterface(result.value().get());
108 return std::make_unique<PeerConnectionWrapper>(
109 pc_factory_, result.MoveValue(), std::move(observer));
110 }
111
112 Clock* const clock_;
113 std::unique_ptr<rtc::SocketServer> socket_server_;
114 rtc::AutoSocketServerThread main_thread_;
115 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_ = nullptr;
116 webrtc::PeerConnectionInterface::RTCConfiguration config_;
117 };
118
119 // Tests for the dependency descriptor field trial. The dependency descriptor
120 // field trial is implemented in media/engine/webrtc_video_engine.cc.
TEST_F(PeerConnectionFieldTrialTest,EnableDependencyDescriptorAdvertised)121 TEST_F(PeerConnectionFieldTrialTest, EnableDependencyDescriptorAdvertised) {
122 std::unique_ptr<test::ScopedKeyValueConfig> field_trials =
123 std::make_unique<test::ScopedKeyValueConfig>(
124 "WebRTC-DependencyDescriptorAdvertised/Enabled/");
125 CreatePCFactory(std::move(field_trials));
126
127 WrapperPtr caller = CreatePeerConnection();
128 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
129
130 auto offer = caller->CreateOffer();
131 auto contents1 = offer->description()->contents();
132 ASSERT_EQ(1u, contents1.size());
133
134 const cricket::MediaContentDescription* media_description1 =
135 contents1[0].media_description();
136 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description1->type());
137 const cricket::RtpHeaderExtensions& rtp_header_extensions1 =
138 media_description1->rtp_header_extensions();
139
140 bool found = absl::c_find_if(rtp_header_extensions1,
141 [](const webrtc::RtpExtension& rtp_extension) {
142 return rtp_extension.uri ==
143 RtpExtension::kDependencyDescriptorUri;
144 }) != rtp_header_extensions1.end();
145 EXPECT_TRUE(found);
146 }
147
148 // Tests that dependency descriptor RTP header extensions can be exchanged
149 // via SDP munging, even if dependency descriptor field trial is disabled.
TEST_F(PeerConnectionFieldTrialTest,InjectDependencyDescriptor)150 TEST_F(PeerConnectionFieldTrialTest, InjectDependencyDescriptor) {
151 std::unique_ptr<test::ScopedKeyValueConfig> field_trials =
152 std::make_unique<test::ScopedKeyValueConfig>(
153 "WebRTC-DependencyDescriptorAdvertised/Disabled/");
154 CreatePCFactory(std::move(field_trials));
155
156 WrapperPtr caller = CreatePeerConnection();
157 WrapperPtr callee = CreatePeerConnection();
158 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
159
160 auto offer = caller->CreateOffer();
161 cricket::ContentInfos& contents1 = offer->description()->contents();
162 ASSERT_EQ(1u, contents1.size());
163
164 cricket::MediaContentDescription* media_description1 =
165 contents1[0].media_description();
166 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description1->type());
167 cricket::RtpHeaderExtensions rtp_header_extensions1 =
168 media_description1->rtp_header_extensions();
169
170 bool found1 = absl::c_find_if(rtp_header_extensions1,
171 [](const webrtc::RtpExtension& rtp_extension) {
172 return rtp_extension.uri ==
173 RtpExtension::kDependencyDescriptorUri;
174 }) != rtp_header_extensions1.end();
175 EXPECT_FALSE(found1);
176
177 std::set<int> existing_ids;
178 for (const webrtc::RtpExtension& rtp_extension : rtp_header_extensions1) {
179 existing_ids.insert(rtp_extension.id);
180 }
181
182 // Find the currently unused RTP header extension ID.
183 int insert_id = 1;
184 std::set<int>::const_iterator iter = existing_ids.begin();
185 while (true) {
186 if (iter == existing_ids.end()) {
187 break;
188 }
189 if (*iter != insert_id) {
190 break;
191 }
192 insert_id++;
193 iter++;
194 }
195
196 rtp_header_extensions1.emplace_back(RtpExtension::kDependencyDescriptorUri,
197 insert_id);
198 media_description1->set_rtp_header_extensions(rtp_header_extensions1);
199
200 caller->SetLocalDescription(offer->Clone());
201
202 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
203 auto answer = callee->CreateAnswer();
204
205 cricket::ContentInfos& contents2 = answer->description()->contents();
206 ASSERT_EQ(1u, contents2.size());
207
208 cricket::MediaContentDescription* media_description2 =
209 contents2[0].media_description();
210 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description2->type());
211 cricket::RtpHeaderExtensions rtp_header_extensions2 =
212 media_description2->rtp_header_extensions();
213
214 bool found2 = absl::c_find_if(rtp_header_extensions2,
215 [](const webrtc::RtpExtension& rtp_extension) {
216 return rtp_extension.uri ==
217 RtpExtension::kDependencyDescriptorUri;
218 }) != rtp_header_extensions2.end();
219 EXPECT_TRUE(found2);
220 }
221
222 // Test that the ability to emulate degraded networks works without crashing.
TEST_F(PeerConnectionFieldTrialTest,ApplyFakeNetworkConfig)223 TEST_F(PeerConnectionFieldTrialTest, ApplyFakeNetworkConfig) {
224 std::unique_ptr<test::ScopedKeyValueConfig> field_trials =
225 std::make_unique<test::ScopedKeyValueConfig>(
226 "WebRTC-FakeNetworkSendConfig/link_capacity_kbps:500/"
227 "WebRTC-FakeNetworkReceiveConfig/loss_percent:1/");
228
229 CreatePCFactory(std::move(field_trials));
230
231 WrapperPtr caller = CreatePeerConnection();
232 BitrateSettings bitrate_settings;
233 bitrate_settings.start_bitrate_bps = 1'000'000;
234 bitrate_settings.max_bitrate_bps = 1'000'000;
235 caller->pc()->SetBitrate(bitrate_settings);
236 FrameGeneratorCapturerVideoTrackSource::Config config;
237 auto video_track_source =
238 rtc::make_ref_counted<FrameGeneratorCapturerVideoTrackSource>(
239 config, clock_, /*is_screencast=*/false);
240 caller->AddTrack(
241 pc_factory_->CreateVideoTrack("v", video_track_source.get()));
242 WrapperPtr callee = CreatePeerConnection();
243
244 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
245 ASSERT_TRUE(
246 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
247
248 // Do the SDP negotiation, and also exchange ice candidates.
249 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
250 ASSERT_TRUE_WAIT(
251 caller->signaling_state() == PeerConnectionInterface::kStable,
252 kDefaultTimeoutMs);
253 ASSERT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeoutMs);
254 ASSERT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeoutMs);
255
256 // Connect an ICE candidate pairs.
257 ASSERT_TRUE(
258 AddIceCandidates(callee.get(), caller->observer()->GetAllCandidates()));
259 ASSERT_TRUE(
260 AddIceCandidates(caller.get(), callee->observer()->GetAllCandidates()));
261
262 // This means that ICE and DTLS are connected.
263 ASSERT_TRUE_WAIT(callee->IsIceConnected(), kDefaultTimeoutMs);
264 ASSERT_TRUE_WAIT(caller->IsIceConnected(), kDefaultTimeoutMs);
265
266 // Send packets for kDefaultTimeoutMs
267 WAIT(false, kDefaultTimeoutMs);
268
269 std::vector<const RTCOutboundRTPStreamStats*> outbound_rtp_stats =
270 caller->GetStats()->GetStatsOfType<RTCOutboundRTPStreamStats>();
271 ASSERT_GE(outbound_rtp_stats.size(), 1u);
272 ASSERT_TRUE(outbound_rtp_stats[0]->target_bitrate.is_defined());
273 // Link capacity is limited to 500k, so BWE is expected to be close to 500k.
274 ASSERT_LE(*outbound_rtp_stats[0]->target_bitrate, 500'000 * 1.1);
275 }
276
277 } // namespace webrtc
278