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