• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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