1 /*
2 * Copyright (c) 2019 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 #include "test/pc/e2e/peer_connection_quality_test.h"
11
12 #include <algorithm>
13 #include <memory>
14 #include <set>
15 #include <utility>
16
17 #include "absl/strings/string_view.h"
18 #include "api/jsep.h"
19 #include "api/media_stream_interface.h"
20 #include "api/peer_connection_interface.h"
21 #include "api/rtc_event_log/rtc_event_log.h"
22 #include "api/rtc_event_log_output_file.h"
23 #include "api/scoped_refptr.h"
24 #include "api/task_queue/default_task_queue_factory.h"
25 #include "api/test/time_controller.h"
26 #include "api/test/video_quality_analyzer_interface.h"
27 #include "pc/sdp_utils.h"
28 #include "pc/test/mock_peer_connection_observers.h"
29 #include "rtc_base/bind.h"
30 #include "rtc_base/gunit.h"
31 #include "rtc_base/numerics/safe_conversions.h"
32 #include "system_wrappers/include/cpu_info.h"
33 #include "system_wrappers/include/field_trial.h"
34 #include "test/pc/e2e/analyzer/audio/default_audio_quality_analyzer.h"
35 #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer.h"
36 #include "test/pc/e2e/analyzer/video/video_quality_metrics_reporter.h"
37 #include "test/pc/e2e/cross_media_metrics_reporter.h"
38 #include "test/pc/e2e/stats_poller.h"
39 #include "test/pc/e2e/test_peer_factory.h"
40 #include "test/testsupport/file_utils.h"
41 #include "test/testsupport/perf_test.h"
42
43 namespace webrtc {
44 namespace webrtc_pc_e2e {
45 namespace {
46
47 using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
48 using VideoCodecConfig = PeerConnectionE2EQualityTestFixture::VideoCodecConfig;
49
50 constexpr TimeDelta kDefaultTimeout = TimeDelta::Seconds(10);
51 constexpr char kSignalThreadName[] = "signaling_thread";
52 // 1 signaling, 2 network, 2 worker and 2 extra for codecs etc.
53 constexpr int kPeerConnectionUsedThreads = 7;
54 // Framework has extra thread for network layer and extra thread for peer
55 // connection stats polling.
56 constexpr int kFrameworkUsedThreads = 2;
57 constexpr int kMaxVideoAnalyzerThreads = 8;
58
59 constexpr TimeDelta kStatsUpdateInterval = TimeDelta::Seconds(1);
60
61 constexpr TimeDelta kAliveMessageLogInterval = TimeDelta::Seconds(30);
62
63 constexpr TimeDelta kQuickTestModeRunDuration = TimeDelta::Millis(100);
64
65 // Field trials to enable Flex FEC advertising and receiving.
66 constexpr char kFlexFecEnabledFieldTrials[] =
67 "WebRTC-FlexFEC-03-Advertised/Enabled/WebRTC-FlexFEC-03/Enabled/";
68
69 class FixturePeerConnectionObserver : public MockPeerConnectionObserver {
70 public:
71 // |on_track_callback| will be called when any new track will be added to peer
72 // connection.
73 // |on_connected_callback| will be called when peer connection will come to
74 // either connected or completed state. Client should notice that in the case
75 // of reconnect this callback can be called again, so it should be tolerant
76 // to such behavior.
FixturePeerConnectionObserver(std::function<void (rtc::scoped_refptr<RtpTransceiverInterface>)> on_track_callback,std::function<void ()> on_connected_callback)77 FixturePeerConnectionObserver(
78 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
79 on_track_callback,
80 std::function<void()> on_connected_callback)
81 : on_track_callback_(std::move(on_track_callback)),
82 on_connected_callback_(std::move(on_connected_callback)) {}
83
OnTrack(rtc::scoped_refptr<RtpTransceiverInterface> transceiver)84 void OnTrack(
85 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) override {
86 MockPeerConnectionObserver::OnTrack(transceiver);
87 on_track_callback_(transceiver);
88 }
89
OnIceConnectionChange(PeerConnectionInterface::IceConnectionState new_state)90 void OnIceConnectionChange(
91 PeerConnectionInterface::IceConnectionState new_state) override {
92 MockPeerConnectionObserver::OnIceConnectionChange(new_state);
93 if (ice_connected_) {
94 on_connected_callback_();
95 }
96 }
97
98 private:
99 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
100 on_track_callback_;
101 std::function<void()> on_connected_callback_;
102 };
103
104 } // namespace
105
PeerConnectionE2EQualityTest(std::string test_case_name,TimeController & time_controller,std::unique_ptr<AudioQualityAnalyzerInterface> audio_quality_analyzer,std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer)106 PeerConnectionE2EQualityTest::PeerConnectionE2EQualityTest(
107 std::string test_case_name,
108 TimeController& time_controller,
109 std::unique_ptr<AudioQualityAnalyzerInterface> audio_quality_analyzer,
110 std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer)
111 : time_controller_(time_controller),
112 task_queue_factory_(CreateDefaultTaskQueueFactory()),
113 test_case_name_(std::move(test_case_name)),
114 executor_(std::make_unique<TestActivitiesExecutor>(
115 time_controller_.GetClock())) {
116 // Create default video quality analyzer. We will always create an analyzer,
117 // even if there are no video streams, because it will be installed into video
118 // encoder/decoder factories.
119 if (video_quality_analyzer == nullptr) {
120 video_quality_analyzer = std::make_unique<DefaultVideoQualityAnalyzer>(
121 time_controller_.GetClock());
122 }
123 encoded_image_id_controller_ =
124 std::make_unique<SingleProcessEncodedImageDataInjector>();
125 video_quality_analyzer_injection_helper_ =
126 std::make_unique<VideoQualityAnalyzerInjectionHelper>(
127 std::move(video_quality_analyzer), encoded_image_id_controller_.get(),
128 encoded_image_id_controller_.get());
129
130 if (audio_quality_analyzer == nullptr) {
131 audio_quality_analyzer = std::make_unique<DefaultAudioQualityAnalyzer>();
132 }
133 audio_quality_analyzer_.swap(audio_quality_analyzer);
134 }
135
ExecuteAt(TimeDelta target_time_since_start,std::function<void (TimeDelta)> func)136 void PeerConnectionE2EQualityTest::ExecuteAt(
137 TimeDelta target_time_since_start,
138 std::function<void(TimeDelta)> func) {
139 executor_->ScheduleActivity(target_time_since_start, absl::nullopt, func);
140 }
141
ExecuteEvery(TimeDelta initial_delay_since_start,TimeDelta interval,std::function<void (TimeDelta)> func)142 void PeerConnectionE2EQualityTest::ExecuteEvery(
143 TimeDelta initial_delay_since_start,
144 TimeDelta interval,
145 std::function<void(TimeDelta)> func) {
146 executor_->ScheduleActivity(initial_delay_since_start, interval, func);
147 }
148
AddQualityMetricsReporter(std::unique_ptr<QualityMetricsReporter> quality_metrics_reporter)149 void PeerConnectionE2EQualityTest::AddQualityMetricsReporter(
150 std::unique_ptr<QualityMetricsReporter> quality_metrics_reporter) {
151 quality_metrics_reporters_.push_back(std::move(quality_metrics_reporter));
152 }
153
AddPeer(rtc::Thread * network_thread,rtc::NetworkManager * network_manager,rtc::FunctionView<void (PeerConfigurer *)> configurer)154 void PeerConnectionE2EQualityTest::AddPeer(
155 rtc::Thread* network_thread,
156 rtc::NetworkManager* network_manager,
157 rtc::FunctionView<void(PeerConfigurer*)> configurer) {
158 peer_configurations_.push_back(
159 std::make_unique<PeerConfigurerImpl>(network_thread, network_manager));
160 configurer(peer_configurations_.back().get());
161 }
162
Run(RunParams run_params)163 void PeerConnectionE2EQualityTest::Run(RunParams run_params) {
164 SetDefaultValuesForMissingParams(&run_params, &peer_configurations_);
165 ValidateParams(run_params, peer_configurations_);
166 RTC_CHECK_EQ(peer_configurations_.size(), 2)
167 << "Only peer to peer calls are allowed, please add 2 peers";
168
169 std::unique_ptr<PeerConfigurerImpl> alice_configurer =
170 std::move(peer_configurations_[0]);
171 std::unique_ptr<PeerConfigurerImpl> bob_configurer =
172 std::move(peer_configurations_[1]);
173 peer_configurations_.clear();
174
175 for (size_t i = 0; i < bob_configurer->params()->video_configs.size(); ++i) {
176 // We support simulcast only from caller.
177 RTC_CHECK(!bob_configurer->params()->video_configs[i].simulcast_config)
178 << "Only simulcast stream from first peer is supported";
179 }
180
181 SetupRequiredFieldTrials(run_params);
182
183 // Print test summary
184 RTC_LOG(INFO) << "Media quality test: " << *alice_configurer->params()->name
185 << " will make a call to " << *bob_configurer->params()->name
186 << " with media video="
187 << !alice_configurer->params()->video_configs.empty()
188 << "; audio="
189 << alice_configurer->params()->audio_config.has_value() << ". "
190 << *bob_configurer->params()->name
191 << " will respond with media video="
192 << !bob_configurer->params()->video_configs.empty()
193 << "; audio="
194 << bob_configurer->params()->audio_config.has_value();
195
196 const std::unique_ptr<rtc::Thread> signaling_thread =
197 time_controller_.CreateThread(kSignalThreadName);
198 media_helper_ = std::make_unique<MediaHelper>(
199 video_quality_analyzer_injection_helper_.get(),
200 task_queue_factory_.get());
201
202 // Create a |task_queue_|.
203 task_queue_ = std::make_unique<webrtc::TaskQueueForTest>(
204 time_controller_.GetTaskQueueFactory()->CreateTaskQueue(
205 "pc_e2e_quality_test", webrtc::TaskQueueFactory::Priority::NORMAL));
206
207 // Create call participants: Alice and Bob.
208 // Audio streams are intercepted in AudioDeviceModule, so if it is required to
209 // catch output of Alice's stream, Alice's output_dump_file_name should be
210 // passed to Bob's TestPeer setup as audio output file name.
211 absl::optional<RemotePeerAudioConfig> alice_remote_audio_config =
212 RemotePeerAudioConfig::Create(bob_configurer->params()->audio_config);
213 absl::optional<RemotePeerAudioConfig> bob_remote_audio_config =
214 RemotePeerAudioConfig::Create(alice_configurer->params()->audio_config);
215 // Copy Alice and Bob video configs and names to correctly pass them into
216 // lambdas.
217 std::vector<VideoConfig> alice_video_configs =
218 alice_configurer->params()->video_configs;
219 std::string alice_name = alice_configurer->params()->name.value();
220 std::vector<VideoConfig> bob_video_configs =
221 bob_configurer->params()->video_configs;
222 std::string bob_name = bob_configurer->params()->name.value();
223
224 TestPeerFactory test_peer_factory(
225 signaling_thread.get(), time_controller_,
226 video_quality_analyzer_injection_helper_.get(), task_queue_.get());
227 alice_ = test_peer_factory.CreateTestPeer(
228 std::move(alice_configurer),
229 std::make_unique<FixturePeerConnectionObserver>(
230 [this, bob_video_configs, alice_name](
231 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
232 OnTrackCallback(alice_name, transceiver, bob_video_configs);
233 },
234 [this]() { StartVideo(alice_video_sources_); }),
235 alice_remote_audio_config, run_params.video_encoder_bitrate_multiplier,
236 run_params.echo_emulation_config);
237 bob_ = test_peer_factory.CreateTestPeer(
238 std::move(bob_configurer),
239 std::make_unique<FixturePeerConnectionObserver>(
240 [this, alice_video_configs,
241 bob_name](rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
242 OnTrackCallback(bob_name, transceiver, alice_video_configs);
243 },
244 [this]() { StartVideo(bob_video_sources_); }),
245 bob_remote_audio_config, run_params.video_encoder_bitrate_multiplier,
246 run_params.echo_emulation_config);
247
248 int num_cores = CpuInfo::DetectNumberOfCores();
249 RTC_DCHECK_GE(num_cores, 1);
250
251 int video_analyzer_threads =
252 num_cores - kPeerConnectionUsedThreads - kFrameworkUsedThreads;
253 if (video_analyzer_threads <= 0) {
254 video_analyzer_threads = 1;
255 }
256 video_analyzer_threads =
257 std::min(video_analyzer_threads, kMaxVideoAnalyzerThreads);
258 RTC_LOG(INFO) << "video_analyzer_threads=" << video_analyzer_threads;
259 quality_metrics_reporters_.push_back(
260 std::make_unique<VideoQualityMetricsReporter>(
261 time_controller_.GetClock()));
262 quality_metrics_reporters_.push_back(
263 std::make_unique<CrossMediaMetricsReporter>());
264
265 video_quality_analyzer_injection_helper_->Start(
266 test_case_name_,
267 std::vector<std::string>{alice_->params()->name.value(),
268 bob_->params()->name.value()},
269 video_analyzer_threads);
270 audio_quality_analyzer_->Start(test_case_name_, &analyzer_helper_);
271 for (auto& reporter : quality_metrics_reporters_) {
272 reporter->Start(test_case_name_, &analyzer_helper_);
273 }
274
275 // Start RTCEventLog recording if requested.
276 if (alice_->params()->rtc_event_log_path) {
277 auto alice_rtc_event_log = std::make_unique<webrtc::RtcEventLogOutputFile>(
278 alice_->params()->rtc_event_log_path.value());
279 alice_->pc()->StartRtcEventLog(std::move(alice_rtc_event_log),
280 webrtc::RtcEventLog::kImmediateOutput);
281 }
282 if (bob_->params()->rtc_event_log_path) {
283 auto bob_rtc_event_log = std::make_unique<webrtc::RtcEventLogOutputFile>(
284 bob_->params()->rtc_event_log_path.value());
285 bob_->pc()->StartRtcEventLog(std::move(bob_rtc_event_log),
286 webrtc::RtcEventLog::kImmediateOutput);
287 }
288
289 // Setup alive logging. It is done to prevent test infra to think that test is
290 // dead.
291 RepeatingTaskHandle::DelayedStart(task_queue_->Get(),
292 kAliveMessageLogInterval, []() {
293 std::printf("Test is still running...\n");
294 return kAliveMessageLogInterval;
295 });
296
297 RTC_LOG(INFO) << "Configuration is done. Now " << *alice_->params()->name
298 << " is calling to " << *bob_->params()->name << "...";
299
300 // Setup stats poller.
301 std::vector<StatsObserverInterface*> observers = {
302 audio_quality_analyzer_.get(),
303 video_quality_analyzer_injection_helper_.get()};
304 for (auto& reporter : quality_metrics_reporters_) {
305 observers.push_back(reporter.get());
306 }
307 StatsPoller stats_poller(observers, {{*alice_->params()->name, alice_.get()},
308 {*bob_->params()->name, bob_.get()}});
309 executor_->ScheduleActivity(TimeDelta::Zero(), kStatsUpdateInterval,
310 [&stats_poller](TimeDelta) {
311 stats_poller.PollStatsAndNotifyObservers();
312 });
313
314 // Setup call.
315 signaling_thread->Invoke<void>(
316 RTC_FROM_HERE,
317 rtc::Bind(&PeerConnectionE2EQualityTest::SetupCallOnSignalingThread, this,
318 run_params));
319 std::unique_ptr<SignalingInterceptor> signaling_interceptor =
320 CreateSignalingInterceptor(run_params);
321 // Connect peers.
322 signaling_thread->Invoke<void>(
323 RTC_FROM_HERE,
324 rtc::Bind(&PeerConnectionE2EQualityTest::ExchangeOfferAnswer, this,
325 signaling_interceptor.get()));
326 WaitUntilIceCandidatesGathered(signaling_thread.get());
327
328 signaling_thread->Invoke<void>(
329 RTC_FROM_HERE,
330 rtc::Bind(&PeerConnectionE2EQualityTest::ExchangeIceCandidates, this,
331 signaling_interceptor.get()));
332 WaitUntilPeersAreConnected(signaling_thread.get());
333
334 executor_->Start(task_queue_.get());
335 Timestamp start_time = Now();
336
337 bool is_quick_test_enabled = field_trial::IsEnabled("WebRTC-QuickPerfTest");
338 if (is_quick_test_enabled) {
339 time_controller_.AdvanceTime(kQuickTestModeRunDuration);
340 } else {
341 time_controller_.AdvanceTime(run_params.run_duration);
342 }
343
344 RTC_LOG(INFO) << "Test is done, initiating disconnect sequence.";
345
346 // Stop all client started tasks to prevent their access to any call related
347 // objects after these objects will be destroyed during call tear down.
348 executor_->Stop();
349 // There is no guarantee, that last stats collection will happen at the end
350 // of the call, so we force it after executor, which is among others is doing
351 // stats collection, was stopped.
352 task_queue_->SendTask(
353 [&stats_poller]() {
354 // Get final end-of-call stats.
355 stats_poller.PollStatsAndNotifyObservers();
356 },
357 RTC_FROM_HERE);
358 // We need to detach AEC dumping from peers, because dump uses |task_queue_|
359 // inside.
360 alice_->DetachAecDump();
361 bob_->DetachAecDump();
362 // Tear down the call.
363 signaling_thread->Invoke<void>(
364 RTC_FROM_HERE,
365 rtc::Bind(&PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread,
366 this));
367 Timestamp end_time = Now();
368 RTC_LOG(INFO) << "All peers are disconnected.";
369 {
370 MutexLock lock(&lock_);
371 real_test_duration_ = end_time - start_time;
372 }
373
374 ReportGeneralTestResults();
375 audio_quality_analyzer_->Stop();
376 video_quality_analyzer_injection_helper_->Stop();
377 for (auto& reporter : quality_metrics_reporters_) {
378 reporter->StopAndReportResults();
379 }
380
381 // Reset |task_queue_| after test to cleanup.
382 task_queue_.reset();
383
384 alice_ = nullptr;
385 bob_ = nullptr;
386 // Ensuring that TestVideoCapturerVideoTrackSource are destroyed on the right
387 // thread.
388 RTC_CHECK(alice_video_sources_.empty());
389 RTC_CHECK(bob_video_sources_.empty());
390 }
391
SetupRequiredFieldTrials(const RunParams & run_params)392 void PeerConnectionE2EQualityTest::SetupRequiredFieldTrials(
393 const RunParams& run_params) {
394 std::string field_trials = "";
395 if (run_params.use_flex_fec) {
396 field_trials += kFlexFecEnabledFieldTrials;
397 }
398 if (!field_trials.empty()) {
399 override_field_trials_ = std::make_unique<test::ScopedFieldTrials>(
400 field_trial::GetFieldTrialString() + field_trials);
401 }
402 }
403
OnTrackCallback(absl::string_view peer_name,rtc::scoped_refptr<RtpTransceiverInterface> transceiver,std::vector<VideoConfig> remote_video_configs)404 void PeerConnectionE2EQualityTest::OnTrackCallback(
405 absl::string_view peer_name,
406 rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
407 std::vector<VideoConfig> remote_video_configs) {
408 const rtc::scoped_refptr<MediaStreamTrackInterface>& track =
409 transceiver->receiver()->track();
410 RTC_CHECK_EQ(transceiver->receiver()->stream_ids().size(), 2)
411 << "Expected 2 stream ids: 1st - sync group, 2nd - unique stream label";
412 std::string sync_group = transceiver->receiver()->stream_ids()[0];
413 std::string stream_label = transceiver->receiver()->stream_ids()[1];
414 analyzer_helper_.AddTrackToStreamMapping(track->id(), stream_label,
415 sync_group);
416 if (track->kind() != MediaStreamTrackInterface::kVideoKind) {
417 return;
418 }
419
420 // It is safe to cast here, because it is checked above that
421 // track->kind() is kVideoKind.
422 auto* video_track = static_cast<VideoTrackInterface*>(track.get());
423 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> video_sink =
424 video_quality_analyzer_injection_helper_->CreateVideoSink(peer_name);
425 video_track->AddOrUpdateSink(video_sink.get(), rtc::VideoSinkWants());
426 output_video_sinks_.push_back(std::move(video_sink));
427 }
428
SetupCallOnSignalingThread(const RunParams & run_params)429 void PeerConnectionE2EQualityTest::SetupCallOnSignalingThread(
430 const RunParams& run_params) {
431 // We need receive-only transceivers for Bob's media stream, so there will
432 // be media section in SDP for that streams in Alice's offer, because it is
433 // forbidden to add new media sections in answer in Unified Plan.
434 RtpTransceiverInit receive_only_transceiver_init;
435 receive_only_transceiver_init.direction = RtpTransceiverDirection::kRecvOnly;
436 int alice_transceivers_counter = 0;
437 if (bob_->params()->audio_config) {
438 // Setup receive audio transceiver if Bob has audio to send. If we'll need
439 // multiple audio streams, then we need transceiver for each Bob's audio
440 // stream.
441 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
442 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_AUDIO,
443 receive_only_transceiver_init);
444 RTC_CHECK(result.ok());
445 alice_transceivers_counter++;
446 }
447
448 size_t alice_video_transceivers_non_simulcast_counter = 0;
449 for (auto& video_config : alice_->params()->video_configs) {
450 RtpTransceiverInit transceiver_params;
451 if (video_config.simulcast_config) {
452 transceiver_params.direction = RtpTransceiverDirection::kSendOnly;
453 // Because simulcast enabled |run_params.video_codecs| has only 1 element.
454 if (run_params.video_codecs[0].name == cricket::kVp8CodecName) {
455 // For Vp8 simulcast we need to add as many RtpEncodingParameters to the
456 // track as many simulcast streams requested. If they specified in
457 // |video_config.simulcast_config| it should be copied from there.
458 for (int i = 0;
459 i < video_config.simulcast_config->simulcast_streams_count; ++i) {
460 RtpEncodingParameters enc_params;
461 if (video_config.simulcast_config->encoding_params.size() > 0) {
462 enc_params = video_config.simulcast_config->encoding_params[i];
463 }
464 // We need to be sure, that all rids will be unique with all mids.
465 enc_params.rid = std::to_string(alice_transceivers_counter) + "000" +
466 std::to_string(i);
467 transceiver_params.send_encodings.push_back(enc_params);
468 }
469 }
470 } else {
471 transceiver_params.direction = RtpTransceiverDirection::kSendRecv;
472 RtpEncodingParameters enc_params;
473 enc_params.max_bitrate_bps = video_config.max_encode_bitrate_bps;
474 enc_params.min_bitrate_bps = video_config.min_encode_bitrate_bps;
475 transceiver_params.send_encodings.push_back(enc_params);
476
477 alice_video_transceivers_non_simulcast_counter++;
478 }
479 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
480 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
481 transceiver_params);
482 RTC_CHECK(result.ok());
483
484 alice_transceivers_counter++;
485 }
486
487 // Add receive only transceivers in case Bob has more video_configs than
488 // Alice.
489 for (size_t i = alice_video_transceivers_non_simulcast_counter;
490 i < bob_->params()->video_configs.size(); ++i) {
491 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
492 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
493 receive_only_transceiver_init);
494 RTC_CHECK(result.ok());
495 alice_transceivers_counter++;
496 }
497
498 // Then add media for Alice and Bob
499 media_helper_->MaybeAddAudio(alice_.get());
500 alice_video_sources_ = media_helper_->MaybeAddVideo(alice_.get());
501 media_helper_->MaybeAddAudio(bob_.get());
502 bob_video_sources_ = media_helper_->MaybeAddVideo(bob_.get());
503
504 SetPeerCodecPreferences(alice_.get(), run_params);
505 SetPeerCodecPreferences(bob_.get(), run_params);
506 }
507
TearDownCallOnSignalingThread()508 void PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread() {
509 TearDownCall();
510 }
511
SetPeerCodecPreferences(TestPeer * peer,const RunParams & run_params)512 void PeerConnectionE2EQualityTest::SetPeerCodecPreferences(
513 TestPeer* peer,
514 const RunParams& run_params) {
515 std::vector<RtpCodecCapability> with_rtx_video_capabilities =
516 FilterVideoCodecCapabilities(
517 run_params.video_codecs, true, run_params.use_ulp_fec,
518 run_params.use_flex_fec,
519 peer->pc_factory()
520 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
521 .codecs);
522 std::vector<RtpCodecCapability> without_rtx_video_capabilities =
523 FilterVideoCodecCapabilities(
524 run_params.video_codecs, false, run_params.use_ulp_fec,
525 run_params.use_flex_fec,
526 peer->pc_factory()
527 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
528 .codecs);
529
530 // Set codecs for transceivers
531 for (auto transceiver : peer->pc()->GetTransceivers()) {
532 if (transceiver->media_type() == cricket::MediaType::MEDIA_TYPE_VIDEO) {
533 if (transceiver->sender()->init_send_encodings().size() > 1) {
534 // If transceiver's sender has more then 1 send encodings, it means it
535 // has multiple simulcast streams, so we need disable RTX on it.
536 RTCError result =
537 transceiver->SetCodecPreferences(without_rtx_video_capabilities);
538 RTC_CHECK(result.ok());
539 } else {
540 RTCError result =
541 transceiver->SetCodecPreferences(with_rtx_video_capabilities);
542 RTC_CHECK(result.ok());
543 }
544 }
545 }
546 }
547
548 std::unique_ptr<SignalingInterceptor>
CreateSignalingInterceptor(const RunParams & run_params)549 PeerConnectionE2EQualityTest::CreateSignalingInterceptor(
550 const RunParams& run_params) {
551 std::map<std::string, int> stream_label_to_simulcast_streams_count;
552 // We add only Alice here, because simulcast/svc is supported only from the
553 // first peer.
554 for (auto& video_config : alice_->params()->video_configs) {
555 if (video_config.simulcast_config) {
556 stream_label_to_simulcast_streams_count.insert(
557 {*video_config.stream_label,
558 video_config.simulcast_config->simulcast_streams_count});
559 }
560 }
561 PatchingParams patching_params(run_params.video_codecs,
562 run_params.use_conference_mode,
563 stream_label_to_simulcast_streams_count);
564 return std::make_unique<SignalingInterceptor>(patching_params);
565 }
566
WaitUntilIceCandidatesGathered(rtc::Thread * signaling_thread)567 void PeerConnectionE2EQualityTest::WaitUntilIceCandidatesGathered(
568 rtc::Thread* signaling_thread) {
569 ASSERT_TRUE(time_controller_.Wait(
570 [&]() {
571 return signaling_thread->Invoke<bool>(RTC_FROM_HERE, [&]() {
572 return alice_->IsIceGatheringDone() && bob_->IsIceGatheringDone();
573 });
574 },
575 2 * kDefaultTimeout));
576 }
577
WaitUntilPeersAreConnected(rtc::Thread * signaling_thread)578 void PeerConnectionE2EQualityTest::WaitUntilPeersAreConnected(
579 rtc::Thread* signaling_thread) {
580 // This means that ICE and DTLS are connected.
581 alice_connected_ = time_controller_.Wait(
582 [&]() {
583 return signaling_thread->Invoke<bool>(
584 RTC_FROM_HERE, [&]() { return alice_->IsIceConnected(); });
585 },
586 kDefaultTimeout);
587 bob_connected_ = time_controller_.Wait(
588 [&]() {
589 return signaling_thread->Invoke<bool>(
590 RTC_FROM_HERE, [&]() { return bob_->IsIceConnected(); });
591 },
592 kDefaultTimeout);
593 }
594
ExchangeOfferAnswer(SignalingInterceptor * signaling_interceptor)595 void PeerConnectionE2EQualityTest::ExchangeOfferAnswer(
596 SignalingInterceptor* signaling_interceptor) {
597 std::string log_output;
598
599 auto offer = alice_->CreateOffer();
600 RTC_CHECK(offer);
601 offer->ToString(&log_output);
602 RTC_LOG(INFO) << "Original offer: " << log_output;
603 LocalAndRemoteSdp patch_result =
604 signaling_interceptor->PatchOffer(std::move(offer));
605 patch_result.local_sdp->ToString(&log_output);
606 RTC_LOG(INFO) << "Offer to set as local description: " << log_output;
607 patch_result.remote_sdp->ToString(&log_output);
608 RTC_LOG(INFO) << "Offer to set as remote description: " << log_output;
609
610 bool set_local_offer =
611 alice_->SetLocalDescription(std::move(patch_result.local_sdp));
612 RTC_CHECK(set_local_offer);
613 bool set_remote_offer =
614 bob_->SetRemoteDescription(std::move(patch_result.remote_sdp));
615 RTC_CHECK(set_remote_offer);
616 auto answer = bob_->CreateAnswer();
617 RTC_CHECK(answer);
618 answer->ToString(&log_output);
619 RTC_LOG(INFO) << "Original answer: " << log_output;
620 patch_result = signaling_interceptor->PatchAnswer(std::move(answer));
621 patch_result.local_sdp->ToString(&log_output);
622 RTC_LOG(INFO) << "Answer to set as local description: " << log_output;
623 patch_result.remote_sdp->ToString(&log_output);
624 RTC_LOG(INFO) << "Answer to set as remote description: " << log_output;
625
626 bool set_local_answer =
627 bob_->SetLocalDescription(std::move(patch_result.local_sdp));
628 RTC_CHECK(set_local_answer);
629 bool set_remote_answer =
630 alice_->SetRemoteDescription(std::move(patch_result.remote_sdp));
631 RTC_CHECK(set_remote_answer);
632 }
633
ExchangeIceCandidates(SignalingInterceptor * signaling_interceptor)634 void PeerConnectionE2EQualityTest::ExchangeIceCandidates(
635 SignalingInterceptor* signaling_interceptor) {
636 // Connect an ICE candidate pairs.
637 std::vector<std::unique_ptr<IceCandidateInterface>> alice_candidates =
638 signaling_interceptor->PatchOffererIceCandidates(
639 alice_->observer()->GetAllCandidates());
640 for (auto& candidate : alice_candidates) {
641 std::string candidate_str;
642 RTC_CHECK(candidate->ToString(&candidate_str));
643 RTC_LOG(INFO) << *alice_->params()->name
644 << " ICE candidate(mid= " << candidate->sdp_mid()
645 << "): " << candidate_str;
646 }
647 ASSERT_TRUE(bob_->AddIceCandidates(std::move(alice_candidates)));
648 std::vector<std::unique_ptr<IceCandidateInterface>> bob_candidates =
649 signaling_interceptor->PatchAnswererIceCandidates(
650 bob_->observer()->GetAllCandidates());
651 for (auto& candidate : bob_candidates) {
652 std::string candidate_str;
653 RTC_CHECK(candidate->ToString(&candidate_str));
654 RTC_LOG(INFO) << *bob_->params()->name
655 << " ICE candidate(mid= " << candidate->sdp_mid()
656 << "): " << candidate_str;
657 }
658 ASSERT_TRUE(alice_->AddIceCandidates(std::move(bob_candidates)));
659 }
660
StartVideo(const std::vector<rtc::scoped_refptr<TestVideoCapturerVideoTrackSource>> & sources)661 void PeerConnectionE2EQualityTest::StartVideo(
662 const std::vector<rtc::scoped_refptr<TestVideoCapturerVideoTrackSource>>&
663 sources) {
664 for (auto& source : sources) {
665 if (source->state() != MediaSourceInterface::SourceState::kLive) {
666 source->Start();
667 }
668 }
669 }
670
TearDownCall()671 void PeerConnectionE2EQualityTest::TearDownCall() {
672 for (const auto& video_source : alice_video_sources_) {
673 video_source->Stop();
674 }
675 for (const auto& video_source : bob_video_sources_) {
676 video_source->Stop();
677 }
678
679 alice_->pc()->Close();
680 bob_->pc()->Close();
681
682 alice_video_sources_.clear();
683 bob_video_sources_.clear();
684
685 media_helper_ = nullptr;
686 }
687
ReportGeneralTestResults()688 void PeerConnectionE2EQualityTest::ReportGeneralTestResults() {
689 test::PrintResult(*alice_->params()->name + "_connected", "", test_case_name_,
690 alice_connected_, "unitless",
691 /*important=*/false,
692 test::ImproveDirection::kBiggerIsBetter);
693 test::PrintResult(*bob_->params()->name + "_connected", "", test_case_name_,
694 bob_connected_, "unitless",
695 /*important=*/false,
696 test::ImproveDirection::kBiggerIsBetter);
697 }
698
Now() const699 Timestamp PeerConnectionE2EQualityTest::Now() const {
700 return time_controller_.GetClock()->CurrentTime();
701 }
702
703 } // namespace webrtc_pc_e2e
704 } // namespace webrtc
705