• 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/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