blob: 03fa31fdf4a42f9e31e0607d76c6bb0087b99790 [file] [log] [blame]
Artem Titova6a273d2019-02-07 16:43:51 +01001/*
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
Mirko Bonadei71318802019-02-18 18:52:43 +010012#include <algorithm>
Artem Titova6a273d2019-02-07 16:43:51 +010013#include <set>
14#include <utility>
15
16#include "absl/memory/memory.h"
Artem Titovef3fd9c2019-06-13 16:36:52 +020017#include "api/jsep.h"
Artem Titovbf9e01a2019-02-14 10:51:27 +010018#include "api/media_stream_interface.h"
Artem Titova6a273d2019-02-07 16:43:51 +010019#include "api/peer_connection_interface.h"
Danil Chapovalov83bbe912019-08-07 12:24:53 +020020#include "api/rtc_event_log/rtc_event_log.h"
Niels Möllerd8b9ed72019-05-08 13:53:51 +020021#include "api/rtc_event_log_output_file.h"
Artem Titova6a273d2019-02-07 16:43:51 +010022#include "api/scoped_refptr.h"
Danil Chapovalovd0e0ed82019-04-18 14:48:24 +020023#include "api/task_queue/default_task_queue_factory.h"
Artem Titovd57628f2019-03-22 12:34:25 +010024#include "api/test/video_quality_analyzer_interface.h"
Artem Titova6a273d2019-02-07 16:43:51 +010025#include "api/units/time_delta.h"
Artem Titovf65a89b2019-05-07 11:56:44 +020026#include "pc/sdp_utils.h"
Artem Titovbf9e01a2019-02-14 10:51:27 +010027#include "pc/test/mock_peer_connection_observers.h"
Artem Titova6a273d2019-02-07 16:43:51 +010028#include "rtc_base/bind.h"
29#include "rtc_base/gunit.h"
Mirko Bonadei71318802019-02-18 18:52:43 +010030#include "rtc_base/numerics/safe_conversions.h"
Artem Titov9f97c9a2019-02-08 00:35:13 +010031#include "system_wrappers/include/cpu_info.h"
Artem Titov23702422019-05-27 15:28:30 +020032#include "system_wrappers/include/field_trial.h"
Mirko Bonadei3830d9b2019-02-28 14:07:56 +010033#include "test/pc/e2e/analyzer/audio/default_audio_quality_analyzer.h"
Artem Titov859abef2019-03-01 11:11:09 +010034#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer.h"
Mirko Bonadei12ae4f42019-02-26 15:19:07 +010035#include "test/pc/e2e/stats_poller.h"
Artem Titova6a273d2019-02-07 16:43:51 +010036#include "test/testsupport/file_utils.h"
37
38namespace webrtc {
Artem Titov0b443142019-03-20 11:11:08 +010039namespace webrtc_pc_e2e {
Artem Titova6a273d2019-02-07 16:43:51 +010040namespace {
41
42using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
43
44constexpr int kDefaultTimeoutMs = 10000;
45constexpr char kSignalThreadName[] = "signaling_thread";
Artem Titov9f97c9a2019-02-08 00:35:13 +010046// 1 signaling, 2 network, 2 worker and 2 extra for codecs etc.
47constexpr int kPeerConnectionUsedThreads = 7;
48// Framework has extra thread for network layer and extra thread for peer
49// connection stats polling.
50constexpr int kFrameworkUsedThreads = 2;
51constexpr int kMaxVideoAnalyzerThreads = 8;
Artem Titova6a273d2019-02-07 16:43:51 +010052
Mirko Bonadei12ae4f42019-02-26 15:19:07 +010053constexpr TimeDelta kStatsUpdateInterval = TimeDelta::Seconds<1>();
54constexpr TimeDelta kStatsPollingStopTimeout = TimeDelta::Seconds<1>();
55
Artem Titov4d29ef02019-05-20 10:43:16 +020056constexpr TimeDelta kAliveMessageLogInterval = TimeDelta::Seconds<30>();
57
Artem Titov16850592019-07-10 14:34:02 +020058constexpr int kQuickTestModeRunDurationMs = 100;
59
Artem Titov23702422019-05-27 15:28:30 +020060// Field trials to enable Flex FEC advertising and receiving.
61constexpr char kFlexFecEnabledFieldTrials[] =
62 "WebRTC-FlexFEC-03-Advertised/Enabled/WebRTC-FlexFEC-03/Enabled/";
63
Artem Titova6a273d2019-02-07 16:43:51 +010064std::string VideoConfigSourcePresenceToString(const VideoConfig& video_config) {
65 char buf[1024];
66 rtc::SimpleStringBuilder builder(buf);
67 builder << "video_config.generator=" << video_config.generator.has_value()
68 << "; video_config.input_file_name="
69 << video_config.input_file_name.has_value()
70 << "; video_config.screen_share_config="
71 << video_config.screen_share_config.has_value() << ";";
72 return builder.str();
73}
74
Artem Titovbf9e01a2019-02-14 10:51:27 +010075class FixturePeerConnectionObserver : public MockPeerConnectionObserver {
76 public:
77 // |on_track_callback| will be called when any new track will be added to peer
78 // connection.
79 // |on_connected_callback| will be called when peer connection will come to
80 // either connected or completed state. Client should notice that in the case
81 // of reconnect this callback can be called again, so it should be tolerant
82 // to such behavior.
83 FixturePeerConnectionObserver(
84 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
85 on_track_callback,
86 std::function<void()> on_connected_callback)
87 : on_track_callback_(std::move(on_track_callback)),
88 on_connected_callback_(std::move(on_connected_callback)) {}
89
90 void OnTrack(
91 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) override {
92 MockPeerConnectionObserver::OnTrack(transceiver);
93 on_track_callback_(transceiver);
94 }
95
96 void OnIceConnectionChange(
97 PeerConnectionInterface::IceConnectionState new_state) override {
98 MockPeerConnectionObserver::OnIceConnectionChange(new_state);
99 if (ice_connected_) {
100 on_connected_callback_();
101 }
102 }
103
104 private:
105 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
106 on_track_callback_;
107 std::function<void()> on_connected_callback_;
108};
109
Artem Titova6a273d2019-02-07 16:43:51 +0100110} // namespace
111
112PeerConnectionE2EQualityTest::PeerConnectionE2EQualityTest(
Artem Titov59835852019-02-27 17:44:13 +0100113 std::string test_case_name,
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100114 std::unique_ptr<AudioQualityAnalyzerInterface> audio_quality_analyzer,
115 std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer)
Artem Titov59835852019-02-27 17:44:13 +0100116 : clock_(Clock::GetRealTimeClock()),
Danil Chapovalovd0e0ed82019-04-18 14:48:24 +0200117 task_queue_factory_(CreateDefaultTaskQueueFactory()),
Artem Titovba82e002019-03-15 15:57:53 +0100118 test_case_name_(std::move(test_case_name)) {
Artem Titova6a273d2019-02-07 16:43:51 +0100119 // Create default video quality analyzer. We will always create an analyzer,
120 // even if there are no video streams, because it will be installed into video
121 // encoder/decoder factories.
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100122 if (video_quality_analyzer == nullptr) {
Artem Titov859abef2019-03-01 11:11:09 +0100123 video_quality_analyzer = absl::make_unique<DefaultVideoQualityAnalyzer>();
Artem Titova6a273d2019-02-07 16:43:51 +0100124 }
125 encoded_image_id_controller_ =
Artem Titov32232e92019-02-20 21:13:14 +0100126 absl::make_unique<SingleProcessEncodedImageDataInjector>();
Artem Titova6a273d2019-02-07 16:43:51 +0100127 video_quality_analyzer_injection_helper_ =
128 absl::make_unique<VideoQualityAnalyzerInjectionHelper>(
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100129 std::move(video_quality_analyzer), encoded_image_id_controller_.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100130 encoded_image_id_controller_.get());
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100131
132 if (audio_quality_analyzer == nullptr) {
133 audio_quality_analyzer = absl::make_unique<DefaultAudioQualityAnalyzer>();
134 }
135 audio_quality_analyzer_.swap(audio_quality_analyzer);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100136}
137
Artem Titovba82e002019-03-15 15:57:53 +0100138void PeerConnectionE2EQualityTest::ExecuteAt(
139 TimeDelta target_time_since_start,
140 std::function<void(TimeDelta)> func) {
141 ExecuteTask(target_time_since_start, absl::nullopt, func);
142}
143
144void PeerConnectionE2EQualityTest::ExecuteEvery(
145 TimeDelta initial_delay_since_start,
146 TimeDelta interval,
147 std::function<void(TimeDelta)> func) {
148 ExecuteTask(initial_delay_since_start, interval, func);
149}
150
151void PeerConnectionE2EQualityTest::ExecuteTask(
152 TimeDelta initial_delay_since_start,
153 absl::optional<TimeDelta> interval,
154 std::function<void(TimeDelta)> func) {
155 RTC_CHECK(initial_delay_since_start.IsFinite() &&
156 initial_delay_since_start >= TimeDelta::Zero());
157 RTC_CHECK(!interval ||
158 (interval->IsFinite() && *interval > TimeDelta::Zero()));
159 rtc::CritScope crit(&lock_);
160 ScheduledActivity activity(initial_delay_since_start, interval, func);
161 if (start_time_.IsInfinite()) {
162 scheduled_activities_.push(std::move(activity));
163 } else {
164 PostTask(std::move(activity));
165 }
166}
167
168void PeerConnectionE2EQualityTest::PostTask(ScheduledActivity activity) {
169 // Because start_time_ will never change at this point copy it to local
170 // variable to capture in in lambda without requirement to hold a lock.
171 Timestamp start_time = start_time_;
172
173 TimeDelta remaining_delay =
174 activity.initial_delay_since_start == TimeDelta::Zero()
175 ? TimeDelta::Zero()
176 : activity.initial_delay_since_start - (Now() - start_time_);
177 if (remaining_delay < TimeDelta::Zero()) {
178 RTC_LOG(WARNING) << "Executing late task immediately, late by="
179 << ToString(remaining_delay.Abs());
180 remaining_delay = TimeDelta::Zero();
181 }
182
183 if (activity.interval) {
184 if (remaining_delay == TimeDelta::Zero()) {
185 repeating_task_handles_.push_back(RepeatingTaskHandle::Start(
186 task_queue_->Get(), [activity, start_time, this]() {
187 activity.func(Now() - start_time);
188 return *activity.interval;
189 }));
190 return;
191 }
192 repeating_task_handles_.push_back(RepeatingTaskHandle::DelayedStart(
193 task_queue_->Get(), remaining_delay, [activity, start_time, this]() {
194 activity.func(Now() - start_time);
195 return *activity.interval;
196 }));
197 return;
198 }
199
200 if (remaining_delay == TimeDelta::Zero()) {
201 task_queue_->PostTask(
202 [activity, start_time, this]() { activity.func(Now() - start_time); });
203 return;
204 }
205
206 task_queue_->PostDelayedTask(
207 [activity, start_time, this]() { activity.func(Now() - start_time); },
208 remaining_delay.ms());
209}
210
Artem Titov18459222019-04-24 11:09:35 +0200211void PeerConnectionE2EQualityTest::AddQualityMetricsReporter(
212 std::unique_ptr<QualityMetricsReporter> quality_metrics_reporter) {
213 quality_metrics_reporters_.push_back(std::move(quality_metrics_reporter));
214}
215
Artem Titovd09bc552019-03-20 11:18:58 +0100216void PeerConnectionE2EQualityTest::AddPeer(
217 rtc::Thread* network_thread,
218 rtc::NetworkManager* network_manager,
219 rtc::FunctionView<void(PeerConfigurer*)> configurer) {
220 peer_configurations_.push_back(
221 absl::make_unique<PeerConfigurerImpl>(network_thread, network_manager));
222 configurer(peer_configurations_.back().get());
223}
224
Jonas Olssona4d87372019-07-05 19:08:33 +0200225void PeerConnectionE2EQualityTest::Run(RunParams run_params) {
Artem Titovd09bc552019-03-20 11:18:58 +0100226 RTC_CHECK_EQ(peer_configurations_.size(), 2)
227 << "Only peer to peer calls are allowed, please add 2 peers";
228
229 std::unique_ptr<Params> alice_params =
230 peer_configurations_[0]->ReleaseParams();
231 std::unique_ptr<InjectableComponents> alice_components =
232 peer_configurations_[0]->ReleaseComponents();
233 std::unique_ptr<Params> bob_params = peer_configurations_[1]->ReleaseParams();
234 std::unique_ptr<InjectableComponents> bob_components =
235 peer_configurations_[1]->ReleaseComponents();
236 peer_configurations_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100237
Artem Titov9a7e7212019-02-28 16:34:17 +0100238 SetDefaultValuesForMissingParams({alice_params.get(), bob_params.get()});
Artem Titovade945d2019-04-02 18:31:48 +0200239 ValidateParams(run_params, {alice_params.get(), bob_params.get()});
Artem Titov23702422019-05-27 15:28:30 +0200240 SetupRequiredFieldTrials(run_params);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100241
242 // Print test summary
243 RTC_LOG(INFO)
244 << "Media quality test: Alice will make a call to Bob with media video="
245 << !alice_params->video_configs.empty()
246 << "; audio=" << alice_params->audio_config.has_value()
247 << ". Bob will respond with media video="
248 << !bob_params->video_configs.empty()
249 << "; audio=" << bob_params->audio_config.has_value();
250
251 const std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
252 signaling_thread->SetName(kSignalThreadName, nullptr);
253 signaling_thread->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100254
Artem Titov70f80e52019-04-12 13:13:39 +0200255 // Create a |task_queue_|.
256 task_queue_ = absl::make_unique<TaskQueueForTest>("pc_e2e_quality_test");
257
Artem Titova6a273d2019-02-07 16:43:51 +0100258 // Create call participants: Alice and Bob.
259 // Audio streams are intercepted in AudioDeviceModule, so if it is required to
260 // catch output of Alice's stream, Alice's output_dump_file_name should be
261 // passed to Bob's TestPeer setup as audio output file name.
Artem Titovbc558ce2019-07-08 19:13:21 +0200262 absl::optional<TestPeer::RemotePeerAudioConfig> alice_remote_audio_config =
263 TestPeer::CreateRemoteAudioConfig(bob_params->audio_config);
264 absl::optional<TestPeer::RemotePeerAudioConfig> bob_remote_audio_config =
265 TestPeer::CreateRemoteAudioConfig(alice_params->audio_config);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100266 // Copy Alice and Bob video configs to correctly pass them into lambdas.
267 std::vector<VideoConfig> alice_video_configs = alice_params->video_configs;
268 std::vector<VideoConfig> bob_video_configs = bob_params->video_configs;
269
Artem Titova6a273d2019-02-07 16:43:51 +0100270 alice_ = TestPeer::CreateTestPeer(
271 std::move(alice_components), std::move(alice_params),
Artem Titovbf9e01a2019-02-14 10:51:27 +0100272 absl::make_unique<FixturePeerConnectionObserver>(
273 [this, bob_video_configs](
274 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200275 OnTrackCallback(transceiver, bob_video_configs);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100276 },
277 [this]() { StartVideo(alice_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100278 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titovbc558ce2019-07-08 19:13:21 +0200279 alice_remote_audio_config, run_params.video_encoder_bitrate_multiplier,
280 task_queue_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100281 bob_ = TestPeer::CreateTestPeer(
282 std::move(bob_components), std::move(bob_params),
Artem Titovbf9e01a2019-02-14 10:51:27 +0100283 absl::make_unique<FixturePeerConnectionObserver>(
284 [this, alice_video_configs](
285 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200286 OnTrackCallback(transceiver, alice_video_configs);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100287 },
288 [this]() { StartVideo(bob_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100289 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titovbc558ce2019-07-08 19:13:21 +0200290 bob_remote_audio_config, run_params.video_encoder_bitrate_multiplier,
291 task_queue_.get());
Artem Titov9f97c9a2019-02-08 00:35:13 +0100292
293 int num_cores = CpuInfo::DetectNumberOfCores();
294 RTC_DCHECK_GE(num_cores, 1);
295
296 int video_analyzer_threads =
297 num_cores - kPeerConnectionUsedThreads - kFrameworkUsedThreads;
298 if (video_analyzer_threads <= 0) {
299 video_analyzer_threads = 1;
300 }
301 video_analyzer_threads =
302 std::min(video_analyzer_threads, kMaxVideoAnalyzerThreads);
303 RTC_LOG(INFO) << "video_analyzer_threads=" << video_analyzer_threads;
304
Artem Titov59835852019-02-27 17:44:13 +0100305 video_quality_analyzer_injection_helper_->Start(test_case_name_,
306 video_analyzer_threads);
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200307 audio_quality_analyzer_->Start(test_case_name_, &analyzer_helper_);
Artem Titov18459222019-04-24 11:09:35 +0200308 for (auto& reporter : quality_metrics_reporters_) {
309 reporter->Start(test_case_name_);
310 }
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +0100311
312 // Start RTCEventLog recording if requested.
313 if (alice_->params()->rtc_event_log_path) {
314 auto alice_rtc_event_log = absl::make_unique<webrtc::RtcEventLogOutputFile>(
315 alice_->params()->rtc_event_log_path.value());
316 alice_->pc()->StartRtcEventLog(std::move(alice_rtc_event_log),
317 webrtc::RtcEventLog::kImmediateOutput);
318 }
319 if (bob_->params()->rtc_event_log_path) {
320 auto bob_rtc_event_log = absl::make_unique<webrtc::RtcEventLogOutputFile>(
321 bob_->params()->rtc_event_log_path.value());
322 bob_->pc()->StartRtcEventLog(std::move(bob_rtc_event_log),
323 webrtc::RtcEventLog::kImmediateOutput);
324 }
325
Artem Titov4d29ef02019-05-20 10:43:16 +0200326 // Setup alive logging. It is done to prevent test infra to think that test is
327 // dead.
328 RepeatingTaskHandle::DelayedStart(task_queue_->Get(),
329 kAliveMessageLogInterval, []() {
330 std::printf("Test is still running...\n");
331 return kAliveMessageLogInterval;
332 });
333
Artem Titovba82e002019-03-15 15:57:53 +0100334 // Setup call.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100335 signaling_thread->Invoke<void>(
Artem Titova6a273d2019-02-07 16:43:51 +0100336 RTC_FROM_HERE,
Artem Titovf65a89b2019-05-07 11:56:44 +0200337 rtc::Bind(&PeerConnectionE2EQualityTest::SetupCallOnSignalingThread, this,
338 run_params));
Artem Titovba82e002019-03-15 15:57:53 +0100339 {
340 rtc::CritScope crit(&lock_);
341 start_time_ = Now();
342 while (!scheduled_activities_.empty()) {
343 PostTask(std::move(scheduled_activities_.front()));
344 scheduled_activities_.pop();
345 }
346 }
Mirko Bonadei71318802019-02-18 18:52:43 +0100347
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100348 StatsPoller stats_poller({audio_quality_analyzer_.get(),
349 video_quality_analyzer_injection_helper_.get()},
350 {{"alice", alice_.get()}, {"bob", bob_.get()}});
351
Artem Titovba82e002019-03-15 15:57:53 +0100352 task_queue_->PostTask([&stats_poller, this]() {
353 RTC_DCHECK_RUN_ON(task_queue_.get());
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100354 stats_polling_task_ =
Artem Titovba82e002019-03-15 15:57:53 +0100355 RepeatingTaskHandle::Start(task_queue_->Get(), [this, &stats_poller]() {
356 RTC_DCHECK_RUN_ON(task_queue_.get());
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100357 stats_poller.PollStatsAndNotifyObservers();
358 return kStatsUpdateInterval;
359 });
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100360 });
361
Mirko Bonadei71318802019-02-18 18:52:43 +0100362 rtc::Event done;
Artem Titov16850592019-07-10 14:34:02 +0200363 bool is_quick_test_enabled = field_trial::IsEnabled("WebRTC-QuickPerfTest");
364 if (is_quick_test_enabled) {
365 done.Wait(kQuickTestModeRunDurationMs);
366 } else {
367 done.Wait(run_params.run_duration.ms());
368 }
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100369
370 rtc::Event stats_polling_stopped;
Artem Titovba82e002019-03-15 15:57:53 +0100371 task_queue_->PostTask([&stats_polling_stopped, this]() {
372 RTC_DCHECK_RUN_ON(task_queue_.get());
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100373 stats_polling_task_.Stop();
374 stats_polling_stopped.Set();
375 });
376 bool no_timeout = stats_polling_stopped.Wait(kStatsPollingStopTimeout.ms());
377 RTC_CHECK(no_timeout) << "Failed to stop Stats polling after "
378 << kStatsPollingStopTimeout.seconds() << " seconds.";
Mirko Bonadei71318802019-02-18 18:52:43 +0100379
Artem Titov70f80e52019-04-12 13:13:39 +0200380 // We need to detach AEC dumping from peers, because dump uses |task_queue_|
381 // inside.
382 alice_->DetachAecDump();
383 bob_->DetachAecDump();
Artem Titov4d29ef02019-05-20 10:43:16 +0200384 // Stop all client started tasks on task queue to prevent their access to any
385 // call related objects after these objects will be destroyed during call tear
386 // down.
387 task_queue_->SendTask([this]() {
388 rtc::CritScope crit(&lock_);
389 for (auto& handle : repeating_task_handles_) {
390 handle.Stop();
391 }
392 });
Artem Titovba82e002019-03-15 15:57:53 +0100393 // Tear down the call.
Mirko Bonadei71318802019-02-18 18:52:43 +0100394 signaling_thread->Invoke<void>(
395 RTC_FROM_HERE,
396 rtc::Bind(&PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread,
397 this));
Artem Titovb93c4e62019-05-02 10:52:07 +0200398 Timestamp end_time = Now();
399 {
400 rtc::CritScope crit(&lock_);
401 real_test_duration_ = end_time - start_time_;
402 }
Mirko Bonadei71318802019-02-18 18:52:43 +0100403
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200404 audio_quality_analyzer_->Stop();
Artem Titov9f97c9a2019-02-08 00:35:13 +0100405 video_quality_analyzer_injection_helper_->Stop();
Artem Titov18459222019-04-24 11:09:35 +0200406 for (auto& reporter : quality_metrics_reporters_) {
407 reporter->StopAndReportResults();
408 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100409
Artem Titov4d29ef02019-05-20 10:43:16 +0200410 // Reset |task_queue_| after test to cleanup.
411 task_queue_.reset();
412
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100413 // Ensuring that TestPeers have been destroyed in order to correctly close
414 // Audio dumps.
415 RTC_CHECK(!alice_);
416 RTC_CHECK(!bob_);
417 // Ensuring that FrameGeneratorCapturerVideoTrackSource and VideoFrameWriter
418 // are destroyed on the right thread.
Artem Titovbf9e01a2019-02-14 10:51:27 +0100419 RTC_CHECK(alice_video_sources_.empty());
420 RTC_CHECK(bob_video_sources_.empty());
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100421 RTC_CHECK(video_writers_.empty());
Artem Titova6a273d2019-02-07 16:43:51 +0100422}
423
Artem Titov9a7e7212019-02-28 16:34:17 +0100424void PeerConnectionE2EQualityTest::SetDefaultValuesForMissingParams(
Artem Titova6a273d2019-02-07 16:43:51 +0100425 std::vector<Params*> params) {
Artem Titov3481db22019-02-28 13:13:15 +0100426 int video_counter = 0;
427 int audio_counter = 0;
Artem Titova6a273d2019-02-07 16:43:51 +0100428 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100429 std::set<std::string> audio_labels;
Artem Titova6a273d2019-02-07 16:43:51 +0100430 for (auto* p : params) {
431 for (auto& video_config : p->video_configs) {
Artem Titov9a7e7212019-02-28 16:34:17 +0100432 if (!video_config.generator && !video_config.input_file_name &&
433 !video_config.screen_share_config) {
434 video_config.generator = VideoGeneratorType::kDefault;
435 }
Artem Titova6a273d2019-02-07 16:43:51 +0100436 if (!video_config.stream_label) {
437 std::string label;
438 do {
Artem Titov3481db22019-02-28 13:13:15 +0100439 label = "_auto_video_stream_label_" + std::to_string(video_counter);
440 ++video_counter;
Artem Titova6a273d2019-02-07 16:43:51 +0100441 } while (!video_labels.insert(label).second);
442 video_config.stream_label = label;
443 }
444 }
Artem Titov3481db22019-02-28 13:13:15 +0100445 if (p->audio_config) {
446 if (!p->audio_config->stream_label) {
447 std::string label;
448 do {
449 label = "_auto_audio_stream_label_" + std::to_string(audio_counter);
450 ++audio_counter;
451 } while (!audio_labels.insert(label).second);
452 p->audio_config->stream_label = label;
453 }
454 }
Artem Titova6a273d2019-02-07 16:43:51 +0100455 }
456}
457
Artem Titovade945d2019-04-02 18:31:48 +0200458void PeerConnectionE2EQualityTest::ValidateParams(const RunParams& run_params,
459 std::vector<Params*> params) {
460 RTC_CHECK_GT(run_params.video_encoder_bitrate_multiplier, 0.0);
461
Artem Titova6a273d2019-02-07 16:43:51 +0100462 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100463 std::set<std::string> audio_labels;
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100464 int media_streams_count = 0;
465
Artem Titovef3fd9c2019-06-13 16:36:52 +0200466 for (size_t i = 0; i < params.size(); ++i) {
467 Params* p = params[i];
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100468 if (p->audio_config) {
469 media_streams_count++;
470 }
471 media_streams_count += p->video_configs.size();
472
Artem Titova6a273d2019-02-07 16:43:51 +0100473 // Validate that each video config has exactly one of |generator|,
474 // |input_file_name| or |screen_share_config| set. Also validate that all
475 // video stream labels are unique.
476 for (auto& video_config : p->video_configs) {
477 RTC_CHECK(video_config.stream_label);
478 bool inserted =
479 video_labels.insert(video_config.stream_label.value()).second;
480 RTC_CHECK(inserted) << "Duplicate video_config.stream_label="
481 << video_config.stream_label.value();
482 RTC_CHECK(video_config.generator || video_config.input_file_name ||
483 video_config.screen_share_config)
484 << VideoConfigSourcePresenceToString(video_config);
485 RTC_CHECK(!(video_config.input_file_name && video_config.generator))
486 << VideoConfigSourcePresenceToString(video_config);
487 RTC_CHECK(
488 !(video_config.input_file_name && video_config.screen_share_config))
489 << VideoConfigSourcePresenceToString(video_config);
490 RTC_CHECK(!(video_config.screen_share_config && video_config.generator))
491 << VideoConfigSourcePresenceToString(video_config);
Artem Titov7581ff72019-05-15 15:45:33 +0200492
493 if (video_config.screen_share_config) {
494 if (video_config.screen_share_config->slides_yuv_file_names.empty()) {
495 if (video_config.screen_share_config->scrolling_params) {
496 // If we have scrolling params, then its |source_width| and
497 // |source_heigh| will be used as width and height of video input,
498 // so we have to validate it against width and height of default
499 // input.
500 RTC_CHECK_EQ(video_config.screen_share_config->scrolling_params
501 ->source_width,
502 kDefaultSlidesWidth);
503 RTC_CHECK_EQ(video_config.screen_share_config->scrolling_params
504 ->source_height,
505 kDefaultSlidesHeight);
506 } else {
507 RTC_CHECK_EQ(video_config.width, kDefaultSlidesWidth);
508 RTC_CHECK_EQ(video_config.height, kDefaultSlidesHeight);
509 }
510 }
511 if (video_config.screen_share_config->scrolling_params) {
512 RTC_CHECK_LE(
513 video_config.screen_share_config->scrolling_params->duration,
514 video_config.screen_share_config->slide_change_interval);
515 RTC_CHECK_GE(
516 video_config.screen_share_config->scrolling_params->source_width,
517 video_config.width);
518 RTC_CHECK_GE(
519 video_config.screen_share_config->scrolling_params->source_height,
520 video_config.height);
521 }
522 }
Artem Titovef3fd9c2019-06-13 16:36:52 +0200523 if (video_config.simulcast_config) {
Artem Titovd70d80d2019-07-19 11:00:40 +0200524 // We support simulcast only from caller.
Artem Titovef3fd9c2019-06-13 16:36:52 +0200525 RTC_CHECK_EQ(i, 0)
526 << "Only simulcast stream from first peer is supported";
527 }
Artem Titova6a273d2019-02-07 16:43:51 +0100528 }
529 if (p->audio_config) {
Artem Titov3481db22019-02-28 13:13:15 +0100530 bool inserted =
531 audio_labels.insert(p->audio_config->stream_label.value()).second;
532 RTC_CHECK(inserted) << "Duplicate audio_config.stream_label="
533 << p->audio_config->stream_label.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100534 // Check that if mode input file name specified only if mode is kFile.
535 if (p->audio_config.value().mode == AudioConfig::Mode::kGenerated) {
536 RTC_CHECK(!p->audio_config.value().input_file_name);
537 }
538 if (p->audio_config.value().mode == AudioConfig::Mode::kFile) {
539 RTC_CHECK(p->audio_config.value().input_file_name);
Artem Titov0b443142019-03-20 11:11:08 +0100540 RTC_CHECK(
Artem Titov70f80e52019-04-12 13:13:39 +0200541 test::FileExists(p->audio_config.value().input_file_name.value()))
542 << p->audio_config.value().input_file_name.value()
543 << " doesn't exist";
Artem Titova6a273d2019-02-07 16:43:51 +0100544 }
545 }
546 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100547
548 RTC_CHECK_GT(media_streams_count, 0) << "No media in the call.";
Artem Titova6a273d2019-02-07 16:43:51 +0100549}
550
Artem Titov23702422019-05-27 15:28:30 +0200551void PeerConnectionE2EQualityTest::SetupRequiredFieldTrials(
552 const RunParams& run_params) {
553 std::string field_trials = "";
554 if (run_params.use_flex_fec) {
555 field_trials += kFlexFecEnabledFieldTrials;
556 }
557 if (!field_trials.empty()) {
558 override_field_trials_ = absl::make_unique<test::ScopedFieldTrials>(
559 field_trial::GetFieldTrialString() + field_trials);
560 }
561}
562
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200563void PeerConnectionE2EQualityTest::OnTrackCallback(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100564 rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
565 std::vector<VideoConfig> remote_video_configs) {
566 const rtc::scoped_refptr<MediaStreamTrackInterface>& track =
567 transceiver->receiver()->track();
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200568 RTC_CHECK_EQ(transceiver->receiver()->stream_ids().size(), 1);
569 std::string stream_label = transceiver->receiver()->stream_ids().front();
570 analyzer_helper_.AddTrackToStreamMapping(track->id(), stream_label);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100571 if (track->kind() != MediaStreamTrackInterface::kVideoKind) {
572 return;
573 }
574
575 VideoConfig* video_config = nullptr;
576 for (auto& config : remote_video_configs) {
Artem Titov7c554152019-02-28 10:25:52 +0100577 if (config.stream_label == stream_label) {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100578 video_config = &config;
579 break;
580 }
581 }
582 RTC_CHECK(video_config);
Artem Titov0b443142019-03-20 11:11:08 +0100583 test::VideoFrameWriter* writer = MaybeCreateVideoWriter(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100584 video_config->output_dump_file_name, *video_config);
585 // It is safe to cast here, because it is checked above that
586 // track->kind() is kVideoKind.
587 auto* video_track = static_cast<VideoTrackInterface*>(track.get());
588 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> video_sink =
589 video_quality_analyzer_injection_helper_->CreateVideoSink(writer);
590 video_track->AddOrUpdateSink(video_sink.get(), rtc::VideoSinkWants());
591 output_video_sinks_.push_back(std::move(video_sink));
592}
593
Artem Titovf65a89b2019-05-07 11:56:44 +0200594void PeerConnectionE2EQualityTest::SetupCallOnSignalingThread(
595 const RunParams& run_params) {
Artem Titov7c554152019-02-28 10:25:52 +0100596 // We need receive-only transceivers for Bob's media stream, so there will
597 // be media section in SDP for that streams in Alice's offer, because it is
598 // forbidden to add new media sections in answer in Unified Plan.
599 RtpTransceiverInit receive_only_transceiver_init;
600 receive_only_transceiver_init.direction = RtpTransceiverDirection::kRecvOnly;
Artem Titovef3fd9c2019-06-13 16:36:52 +0200601 int alice_transceivers_counter = 0;
Artem Titov7c554152019-02-28 10:25:52 +0100602 if (bob_->params()->audio_config) {
603 // Setup receive audio transceiver if Bob has audio to send. If we'll need
604 // multiple audio streams, then we need transceiver for each Bob's audio
605 // stream.
Artem Titov7f2a67f2019-06-13 19:45:55 +0200606 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
607 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_AUDIO,
608 receive_only_transceiver_init);
609 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200610 alice_transceivers_counter++;
611 }
612
613 for (auto& video_config : alice_->params()->video_configs) {
614 if (video_config.simulcast_config) {
615 RtpTransceiverInit transceiver_params;
616 transceiver_params.direction = RtpTransceiverDirection::kSendOnly;
Artem Titovd70d80d2019-07-19 11:00:40 +0200617 if (run_params.video_codec_name == cricket::kVp8CodecName) {
618 // For Vp8 simulcast we need to add as many RtpEncodingParameters to the
619 // track as many simulcast streams requested.
620 for (int i = 0;
621 i < video_config.simulcast_config->simulcast_streams_count; ++i) {
622 RtpEncodingParameters enc_params;
623 // We need to be sure, that all rids will be unique with all mids.
624 enc_params.rid = std::to_string(alice_transceivers_counter) + "000" +
625 std::to_string(i);
626 transceiver_params.send_encodings.push_back(enc_params);
627 }
Artem Titovef3fd9c2019-06-13 16:36:52 +0200628 }
Artem Titov7f2a67f2019-06-13 19:45:55 +0200629 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
630 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
631 transceiver_params);
632 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200633 alice_transceivers_counter++;
634 }
Artem Titov7c554152019-02-28 10:25:52 +0100635 }
636 for (size_t i = 0; i < bob_->params()->video_configs.size(); ++i) {
Artem Titov7f2a67f2019-06-13 19:45:55 +0200637 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
638 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
639 receive_only_transceiver_init);
640 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200641 alice_transceivers_counter++;
Artem Titov7c554152019-02-28 10:25:52 +0100642 }
643 // Then add media for Alice and Bob
644 alice_video_sources_ = MaybeAddMedia(alice_.get());
645 bob_video_sources_ = MaybeAddMedia(bob_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100646
Artem Titovf65a89b2019-05-07 11:56:44 +0200647 SetPeerCodecPreferences(alice_.get(), run_params);
648 SetPeerCodecPreferences(bob_.get(), run_params);
649
Artem Titovd70d80d2019-07-19 11:00:40 +0200650 SetupCall(run_params);
Mirko Bonadei71318802019-02-18 18:52:43 +0100651}
Artem Titova6a273d2019-02-07 16:43:51 +0100652
Mirko Bonadei71318802019-02-18 18:52:43 +0100653void PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread() {
Artem Titova6a273d2019-02-07 16:43:51 +0100654 TearDownCall();
Artem Titova6a273d2019-02-07 16:43:51 +0100655}
656
Artem Titovbf9e01a2019-02-14 10:51:27 +0100657std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100658PeerConnectionE2EQualityTest::MaybeAddMedia(TestPeer* peer) {
659 MaybeAddAudio(peer);
660 return MaybeAddVideo(peer);
Artem Titova6a273d2019-02-07 16:43:51 +0100661}
662
Artem Titovbf9e01a2019-02-14 10:51:27 +0100663std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100664PeerConnectionE2EQualityTest::MaybeAddVideo(TestPeer* peer) {
Artem Titova6a273d2019-02-07 16:43:51 +0100665 // Params here valid because of pre-run validation.
666 Params* params = peer->params();
Artem Titovbf9e01a2019-02-14 10:51:27 +0100667 std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>> out;
Artem Titova6a273d2019-02-07 16:43:51 +0100668 for (auto video_config : params->video_configs) {
669 // Create video generator.
Artem Titov0b443142019-03-20 11:11:08 +0100670 std::unique_ptr<test::FrameGenerator> frame_generator =
Artem Titova6a273d2019-02-07 16:43:51 +0100671 CreateFrameGenerator(video_config);
672
673 // Wrap it to inject video quality analyzer and enable dump of input video
674 // if required.
Artem Titov0b443142019-03-20 11:11:08 +0100675 test::VideoFrameWriter* writer =
Artem Titova6a273d2019-02-07 16:43:51 +0100676 MaybeCreateVideoWriter(video_config.input_dump_file_name, video_config);
677 frame_generator =
678 video_quality_analyzer_injection_helper_->WrapFrameGenerator(
679 video_config.stream_label.value(), std::move(frame_generator),
680 writer);
681
682 // Setup FrameGenerator into peer connection.
Danil Chapovalovd0e0ed82019-04-18 14:48:24 +0200683 auto capturer = absl::make_unique<test::FrameGeneratorCapturer>(
684 clock_, std::move(frame_generator), video_config.fps,
685 *task_queue_factory_);
686 capturer->Init();
Artem Titova6a273d2019-02-07 16:43:51 +0100687 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource> source =
688 new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
Artem Titov232b6a12019-05-29 11:05:01 +0200689 std::move(capturer),
690 /*is_screencast=*/video_config.screen_share_config.has_value());
Artem Titovbf9e01a2019-02-14 10:51:27 +0100691 out.push_back(source);
Artem Titova6a273d2019-02-07 16:43:51 +0100692 RTC_LOG(INFO) << "Adding video with video_config.stream_label="
693 << video_config.stream_label.value();
694 rtc::scoped_refptr<VideoTrackInterface> track =
695 peer->pc_factory()->CreateVideoTrack(video_config.stream_label.value(),
696 source);
Artem Titov232b6a12019-05-29 11:05:01 +0200697 if (video_config.screen_share_config) {
698 track->set_content_hint(VideoTrackInterface::ContentHint::kText);
699 }
Artem Titov1e49ab22019-07-30 13:17:25 +0200700 RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> sender =
701 peer->AddTrack(track, {video_config.stream_label.value()});
702 RTC_CHECK(sender.ok());
703 if (video_config.temporal_layers_count) {
704 RtpParameters rtp_parameters = sender.value()->GetParameters();
705 for (auto& encoding_parameters : rtp_parameters.encodings) {
706 encoding_parameters.num_temporal_layers =
707 video_config.temporal_layers_count;
708 }
709 RTCError res = sender.value()->SetParameters(rtp_parameters);
710 RTC_CHECK(res.ok()) << "Failed to set RTP parameters";
711 }
Artem Titova6a273d2019-02-07 16:43:51 +0100712 }
Artem Titovbf9e01a2019-02-14 10:51:27 +0100713 return out;
Artem Titova6a273d2019-02-07 16:43:51 +0100714}
715
Artem Titov0b443142019-03-20 11:11:08 +0100716std::unique_ptr<test::FrameGenerator>
Artem Titova6a273d2019-02-07 16:43:51 +0100717PeerConnectionE2EQualityTest::CreateFrameGenerator(
718 const VideoConfig& video_config) {
719 if (video_config.generator) {
Artem Titov0b443142019-03-20 11:11:08 +0100720 absl::optional<test::FrameGenerator::OutputType> frame_generator_type =
Artem Titova6a273d2019-02-07 16:43:51 +0100721 absl::nullopt;
722 if (video_config.generator == VideoGeneratorType::kDefault) {
Sebastian Janssoned0febf2019-07-26 15:58:11 +0200723 frame_generator_type = test::FrameGenerator::OutputType::kI420;
Artem Titova6a273d2019-02-07 16:43:51 +0100724 } else if (video_config.generator == VideoGeneratorType::kI420A) {
Sebastian Janssoned0febf2019-07-26 15:58:11 +0200725 frame_generator_type = test::FrameGenerator::OutputType::kI420A;
Artem Titova6a273d2019-02-07 16:43:51 +0100726 } else if (video_config.generator == VideoGeneratorType::kI010) {
Sebastian Janssoned0febf2019-07-26 15:58:11 +0200727 frame_generator_type = test::FrameGenerator::OutputType::kI010;
Artem Titova6a273d2019-02-07 16:43:51 +0100728 }
Artem Titov0b443142019-03-20 11:11:08 +0100729 return test::FrameGenerator::CreateSquareGenerator(
Artem Titova6a273d2019-02-07 16:43:51 +0100730 static_cast<int>(video_config.width),
731 static_cast<int>(video_config.height), frame_generator_type,
732 absl::nullopt);
733 }
734 if (video_config.input_file_name) {
Artem Titov0b443142019-03-20 11:11:08 +0100735 return test::FrameGenerator::CreateFromYuvFile(
Artem Titova6a273d2019-02-07 16:43:51 +0100736 std::vector<std::string>(/*count=*/1,
737 video_config.input_file_name.value()),
738 video_config.width, video_config.height, /*frame_repeat_count=*/1);
739 }
740 if (video_config.screen_share_config) {
Artem Titov7581ff72019-05-15 15:45:33 +0200741 return CreateScreenShareFrameGenerator(video_config);
Artem Titova6a273d2019-02-07 16:43:51 +0100742 }
743 RTC_NOTREACHED() << "Unsupported video_config input source";
744 return nullptr;
745}
746
Artem Titov7581ff72019-05-15 15:45:33 +0200747std::unique_ptr<test::FrameGenerator>
748PeerConnectionE2EQualityTest::CreateScreenShareFrameGenerator(
749 const VideoConfig& video_config) {
750 RTC_CHECK(video_config.screen_share_config);
751 if (video_config.screen_share_config->generate_slides) {
752 return test::FrameGenerator::CreateSlideGenerator(
753 video_config.width, video_config.height,
754 video_config.screen_share_config->slide_change_interval.seconds() *
755 video_config.fps);
756 }
757 std::vector<std::string> slides =
758 video_config.screen_share_config->slides_yuv_file_names;
759 if (slides.empty()) {
760 // If slides is empty we need to add default slides as source. In such case
761 // video width and height is validated to be equal to kDefaultSlidesWidth
762 // and kDefaultSlidesHeight.
763 slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv"));
764 slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv"));
765 slides.push_back(test::ResourcePath("photo_1850_1110", "yuv"));
766 slides.push_back(test::ResourcePath("difficult_photo_1850_1110", "yuv"));
767 }
768 if (!video_config.screen_share_config->scrolling_params) {
769 // Cycle image every slide_change_interval seconds.
770 return test::FrameGenerator::CreateFromYuvFile(
771 slides, video_config.width, video_config.height,
772 video_config.screen_share_config->slide_change_interval.seconds() *
773 video_config.fps);
774 }
775
776 // |pause_duration| is nonnegative. It is validated in ValidateParams(...).
777 TimeDelta pause_duration =
778 video_config.screen_share_config->slide_change_interval -
779 video_config.screen_share_config->scrolling_params->duration;
780
781 return test::FrameGenerator::CreateScrollingInputFromYuvFiles(
782 clock_, slides,
783 video_config.screen_share_config->scrolling_params->source_width,
784 video_config.screen_share_config->scrolling_params->source_height,
785 video_config.width, video_config.height,
786 video_config.screen_share_config->scrolling_params->duration.ms(),
787 pause_duration.ms());
788}
789
Artem Titov7c554152019-02-28 10:25:52 +0100790void PeerConnectionE2EQualityTest::MaybeAddAudio(TestPeer* peer) {
791 if (!peer->params()->audio_config) {
792 return;
793 }
Artem Titov3481db22019-02-28 13:13:15 +0100794 const AudioConfig& audio_config = peer->params()->audio_config.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100795 rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
Artem Titov3481db22019-02-28 13:13:15 +0100796 peer->pc_factory()->CreateAudioSource(audio_config.audio_options);
Artem Titova6a273d2019-02-07 16:43:51 +0100797 rtc::scoped_refptr<AudioTrackInterface> track =
Artem Titov3481db22019-02-28 13:13:15 +0100798 peer->pc_factory()->CreateAudioTrack(*audio_config.stream_label, source);
799 peer->AddTrack(track, {*audio_config.stream_label});
Artem Titova6a273d2019-02-07 16:43:51 +0100800}
801
Artem Titovf65a89b2019-05-07 11:56:44 +0200802void PeerConnectionE2EQualityTest::SetPeerCodecPreferences(
803 TestPeer* peer,
804 const RunParams& run_params) {
Artem Titovef3fd9c2019-06-13 16:36:52 +0200805 std::vector<RtpCodecCapability> with_rtx_video_capabilities =
Artem Titov7f2a67f2019-06-13 19:45:55 +0200806 FilterVideoCodecCapabilities(
Artem Titovef3fd9c2019-06-13 16:36:52 +0200807 run_params.video_codec_name, run_params.video_codec_required_params,
808 true, run_params.use_ulp_fec, run_params.use_flex_fec,
809 peer->pc_factory()
810 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
811 .codecs);
812 std::vector<RtpCodecCapability> without_rtx_video_capabilities =
Artem Titov7f2a67f2019-06-13 19:45:55 +0200813 FilterVideoCodecCapabilities(
Artem Titovef3fd9c2019-06-13 16:36:52 +0200814 run_params.video_codec_name, run_params.video_codec_required_params,
815 false, run_params.use_ulp_fec, run_params.use_flex_fec,
816 peer->pc_factory()
817 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
818 .codecs);
Artem Titovf65a89b2019-05-07 11:56:44 +0200819
820 // Set codecs for transceivers
821 for (auto transceiver : peer->pc()->GetTransceivers()) {
822 if (transceiver->media_type() == cricket::MediaType::MEDIA_TYPE_VIDEO) {
Artem Titovef3fd9c2019-06-13 16:36:52 +0200823 if (transceiver->sender()->init_send_encodings().size() > 1) {
824 // If transceiver's sender has more then 1 send encodings, it means it
825 // has multiple simulcast streams, so we need disable RTX on it.
Artem Titov7f2a67f2019-06-13 19:45:55 +0200826 RTCError result =
827 transceiver->SetCodecPreferences(without_rtx_video_capabilities);
828 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200829 } else {
Artem Titov7f2a67f2019-06-13 19:45:55 +0200830 RTCError result =
831 transceiver->SetCodecPreferences(with_rtx_video_capabilities);
832 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200833 }
Artem Titovf65a89b2019-05-07 11:56:44 +0200834 }
835 }
836}
837
Artem Titovd70d80d2019-07-19 11:00:40 +0200838void PeerConnectionE2EQualityTest::SetupCall(const RunParams& run_params) {
Artem Titov39483c62019-07-19 17:03:52 +0200839 std::map<std::string, int> stream_label_to_simulcast_streams_count;
Artem Titov594597c2019-07-18 13:39:41 +0200840 // We add only Alice here, because simulcast/svc is supported only from the
841 // first peer.
842 for (auto& video_config : alice_->params()->video_configs) {
843 if (video_config.simulcast_config) {
Artem Titov39483c62019-07-19 17:03:52 +0200844 stream_label_to_simulcast_streams_count.insert(
845 {*video_config.stream_label,
846 video_config.simulcast_config->simulcast_streams_count});
Artem Titov594597c2019-07-18 13:39:41 +0200847 }
848 }
Artem Titovd70d80d2019-07-19 11:00:40 +0200849 PatchingParams patching_params(run_params.video_codec_name,
Artem Titov39483c62019-07-19 17:03:52 +0200850 run_params.use_conference_mode,
851 stream_label_to_simulcast_streams_count);
Artem Titov594597c2019-07-18 13:39:41 +0200852 SignalingInterceptor signaling_interceptor(patching_params);
Artem Titova6a273d2019-02-07 16:43:51 +0100853 // Connect peers.
Artem Titovef3fd9c2019-06-13 16:36:52 +0200854 ExchangeOfferAnswer(&signaling_interceptor);
Artem Titova6a273d2019-02-07 16:43:51 +0100855 // Do the SDP negotiation, and also exchange ice candidates.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100856 ASSERT_EQ_WAIT(alice_->signaling_state(), PeerConnectionInterface::kStable,
Artem Titova6a273d2019-02-07 16:43:51 +0100857 kDefaultTimeoutMs);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100858 ASSERT_TRUE_WAIT(alice_->IsIceGatheringDone(), kDefaultTimeoutMs);
859 ASSERT_TRUE_WAIT(bob_->IsIceGatheringDone(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100860
Artem Titovef3fd9c2019-06-13 16:36:52 +0200861 ExchangeIceCandidates(&signaling_interceptor);
Artem Titova6a273d2019-02-07 16:43:51 +0100862 // This means that ICE and DTLS are connected.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100863 ASSERT_TRUE_WAIT(bob_->IsIceConnected(), kDefaultTimeoutMs);
864 ASSERT_TRUE_WAIT(alice_->IsIceConnected(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100865}
866
Artem Titovef3fd9c2019-06-13 16:36:52 +0200867void PeerConnectionE2EQualityTest::ExchangeOfferAnswer(
868 SignalingInterceptor* signaling_interceptor) {
869 std::string log_output;
870
871 auto offer = alice_->CreateOffer();
872 RTC_CHECK(offer);
873 offer->ToString(&log_output);
874 RTC_LOG(INFO) << "Original offer: " << log_output;
875 LocalAndRemoteSdp patch_result =
876 signaling_interceptor->PatchOffer(std::move(offer));
877 patch_result.local_sdp->ToString(&log_output);
878 RTC_LOG(INFO) << "Offer to set as local description: " << log_output;
879 patch_result.remote_sdp->ToString(&log_output);
880 RTC_LOG(INFO) << "Offer to set as remote description: " << log_output;
881
882 bool set_local_offer =
883 alice_->SetLocalDescription(std::move(patch_result.local_sdp));
884 RTC_CHECK(set_local_offer);
885 bool set_remote_offer =
886 bob_->SetRemoteDescription(std::move(patch_result.remote_sdp));
887 RTC_CHECK(set_remote_offer);
888 auto answer = bob_->CreateAnswer();
889 RTC_CHECK(answer);
890 answer->ToString(&log_output);
891 RTC_LOG(INFO) << "Original answer: " << log_output;
892 patch_result = signaling_interceptor->PatchAnswer(std::move(answer));
893 patch_result.local_sdp->ToString(&log_output);
894 RTC_LOG(INFO) << "Answer to set as local description: " << log_output;
895 patch_result.remote_sdp->ToString(&log_output);
896 RTC_LOG(INFO) << "Answer to set as remote description: " << log_output;
897
898 bool set_local_answer =
899 bob_->SetLocalDescription(std::move(patch_result.local_sdp));
900 RTC_CHECK(set_local_answer);
901 bool set_remote_answer =
902 alice_->SetRemoteDescription(std::move(patch_result.remote_sdp));
903 RTC_CHECK(set_remote_answer);
904}
905
906void PeerConnectionE2EQualityTest::ExchangeIceCandidates(
907 SignalingInterceptor* signaling_interceptor) {
908 // Connect an ICE candidate pairs.
909 std::vector<std::unique_ptr<IceCandidateInterface>> alice_candidates =
910 signaling_interceptor->PatchOffererIceCandidates(
911 alice_->observer()->GetAllCandidates());
912 for (auto& candidate : alice_candidates) {
913 std::string candidate_str;
914 RTC_CHECK(candidate->ToString(&candidate_str));
915 RTC_LOG(INFO) << "Alice ICE candidate(mid= " << candidate->sdp_mid()
916 << "): " << candidate_str;
917 }
918 ASSERT_TRUE(bob_->AddIceCandidates(std::move(alice_candidates)));
919 std::vector<std::unique_ptr<IceCandidateInterface>> bob_candidates =
920 signaling_interceptor->PatchAnswererIceCandidates(
921 bob_->observer()->GetAllCandidates());
922 for (auto& candidate : bob_candidates) {
923 std::string candidate_str;
924 RTC_CHECK(candidate->ToString(&candidate_str));
925 RTC_LOG(INFO) << "Bob ICE candidate(mid= " << candidate->sdp_mid()
926 << "): " << candidate_str;
927 }
928 ASSERT_TRUE(alice_->AddIceCandidates(std::move(bob_candidates)));
929}
930
Artem Titovbf9e01a2019-02-14 10:51:27 +0100931void PeerConnectionE2EQualityTest::StartVideo(
932 const std::vector<
933 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>& sources) {
934 for (auto& source : sources) {
935 if (source->state() != MediaSourceInterface::SourceState::kLive) {
936 source->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100937 }
Artem Titova6a273d2019-02-07 16:43:51 +0100938 }
939}
940
941void PeerConnectionE2EQualityTest::TearDownCall() {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100942 for (const auto& video_source : alice_video_sources_) {
943 video_source->Stop();
944 }
945 for (const auto& video_source : bob_video_sources_) {
Artem Titova6a273d2019-02-07 16:43:51 +0100946 video_source->Stop();
947 }
948
949 alice_->pc()->Close();
950 bob_->pc()->Close();
951
952 for (const auto& video_writer : video_writers_) {
953 video_writer->Close();
954 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100955
Artem Titovbf9e01a2019-02-14 10:51:27 +0100956 alice_video_sources_.clear();
957 bob_video_sources_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100958 video_writers_.clear();
959 alice_.reset();
960 bob_.reset();
Artem Titova6a273d2019-02-07 16:43:51 +0100961}
962
Artem Titov0b443142019-03-20 11:11:08 +0100963test::VideoFrameWriter* PeerConnectionE2EQualityTest::MaybeCreateVideoWriter(
Artem Titova6a273d2019-02-07 16:43:51 +0100964 absl::optional<std::string> file_name,
965 const VideoConfig& config) {
966 if (!file_name) {
967 return nullptr;
968 }
Artem Titovef3fd9c2019-06-13 16:36:52 +0200969 // TODO(titovartem) create only one file writer for simulcast video track.
Artem Titov8dcaed92019-07-31 14:19:00 +0200970 auto video_writer = absl::make_unique<test::Y4mVideoFrameWriterImpl>(
Artem Titova6a273d2019-02-07 16:43:51 +0100971 file_name.value(), config.width, config.height, config.fps);
Artem Titov0b443142019-03-20 11:11:08 +0100972 test::VideoFrameWriter* out = video_writer.get();
Artem Titova6a273d2019-02-07 16:43:51 +0100973 video_writers_.push_back(std::move(video_writer));
974 return out;
975}
976
Artem Titovba82e002019-03-15 15:57:53 +0100977Timestamp PeerConnectionE2EQualityTest::Now() const {
Sebastian Janssonb64ad0e2019-06-19 09:39:34 +0200978 return clock_->CurrentTime();
Artem Titovba82e002019-03-15 15:57:53 +0100979}
980
981PeerConnectionE2EQualityTest::ScheduledActivity::ScheduledActivity(
982 TimeDelta initial_delay_since_start,
983 absl::optional<TimeDelta> interval,
984 std::function<void(TimeDelta)> func)
985 : initial_delay_since_start(initial_delay_since_start),
986 interval(std::move(interval)),
987 func(std::move(func)) {}
988
Artem Titov0b443142019-03-20 11:11:08 +0100989} // namespace webrtc_pc_e2e
Artem Titova6a273d2019-02-07 16:43:51 +0100990} // namespace webrtc