blob: d94d979601c1224e90228884bb4ad7d1f47bec3b [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>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020013#include <memory>
Artem Titova6a273d2019-02-07 16:43:51 +010014#include <set>
15#include <utility>
16
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"
Artem Titov9afdddf2019-10-10 13:29:03 +020033#include "test/frame_generator_capturer.h"
Mirko Bonadei3830d9b2019-02-28 14:07:56 +010034#include "test/pc/e2e/analyzer/audio/default_audio_quality_analyzer.h"
Artem Titov859abef2019-03-01 11:11:09 +010035#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer.h"
Mirko Bonadei12ae4f42019-02-26 15:19:07 +010036#include "test/pc/e2e/stats_poller.h"
Artem Titov9afdddf2019-10-10 13:29:03 +020037#include "test/platform_video_capturer.h"
Artem Titova6a273d2019-02-07 16:43:51 +010038#include "test/testsupport/file_utils.h"
39
40namespace webrtc {
Artem Titov0b443142019-03-20 11:11:08 +010041namespace webrtc_pc_e2e {
Artem Titova6a273d2019-02-07 16:43:51 +010042namespace {
43
44using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
45
46constexpr int kDefaultTimeoutMs = 10000;
47constexpr char kSignalThreadName[] = "signaling_thread";
Artem Titov9f97c9a2019-02-08 00:35:13 +010048// 1 signaling, 2 network, 2 worker and 2 extra for codecs etc.
49constexpr int kPeerConnectionUsedThreads = 7;
50// Framework has extra thread for network layer and extra thread for peer
51// connection stats polling.
52constexpr int kFrameworkUsedThreads = 2;
53constexpr int kMaxVideoAnalyzerThreads = 8;
Artem Titova6a273d2019-02-07 16:43:51 +010054
Mirko Bonadei12ae4f42019-02-26 15:19:07 +010055constexpr TimeDelta kStatsUpdateInterval = TimeDelta::Seconds<1>();
Mirko Bonadei12ae4f42019-02-26 15:19:07 +010056
Artem Titov4d29ef02019-05-20 10:43:16 +020057constexpr TimeDelta kAliveMessageLogInterval = TimeDelta::Seconds<30>();
58
Artem Titov16850592019-07-10 14:34:02 +020059constexpr int kQuickTestModeRunDurationMs = 100;
60
Artem Titov23702422019-05-27 15:28:30 +020061// Field trials to enable Flex FEC advertising and receiving.
62constexpr char kFlexFecEnabledFieldTrials[] =
63 "WebRTC-FlexFEC-03-Advertised/Enabled/WebRTC-FlexFEC-03/Enabled/";
64
Artem Titova6a273d2019-02-07 16:43:51 +010065std::string VideoConfigSourcePresenceToString(const VideoConfig& video_config) {
66 char buf[1024];
67 rtc::SimpleStringBuilder builder(buf);
68 builder << "video_config.generator=" << video_config.generator.has_value()
69 << "; video_config.input_file_name="
70 << video_config.input_file_name.has_value()
71 << "; video_config.screen_share_config="
Artem Titov9afdddf2019-10-10 13:29:03 +020072 << video_config.screen_share_config.has_value()
73 << "; video_config.capturing_device_index="
74 << video_config.capturing_device_index.has_value() << ";";
Artem Titova6a273d2019-02-07 16:43:51 +010075 return builder.str();
76}
77
Artem Titovbf9e01a2019-02-14 10:51:27 +010078class FixturePeerConnectionObserver : public MockPeerConnectionObserver {
79 public:
80 // |on_track_callback| will be called when any new track will be added to peer
81 // connection.
82 // |on_connected_callback| will be called when peer connection will come to
83 // either connected or completed state. Client should notice that in the case
84 // of reconnect this callback can be called again, so it should be tolerant
85 // to such behavior.
86 FixturePeerConnectionObserver(
87 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
88 on_track_callback,
89 std::function<void()> on_connected_callback)
90 : on_track_callback_(std::move(on_track_callback)),
91 on_connected_callback_(std::move(on_connected_callback)) {}
92
93 void OnTrack(
94 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) override {
95 MockPeerConnectionObserver::OnTrack(transceiver);
96 on_track_callback_(transceiver);
97 }
98
99 void OnIceConnectionChange(
100 PeerConnectionInterface::IceConnectionState new_state) override {
101 MockPeerConnectionObserver::OnIceConnectionChange(new_state);
102 if (ice_connected_) {
103 on_connected_callback_();
104 }
105 }
106
107 private:
108 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
109 on_track_callback_;
110 std::function<void()> on_connected_callback_;
111};
112
Artem Titova6a273d2019-02-07 16:43:51 +0100113} // namespace
114
115PeerConnectionE2EQualityTest::PeerConnectionE2EQualityTest(
Artem Titov59835852019-02-27 17:44:13 +0100116 std::string test_case_name,
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100117 std::unique_ptr<AudioQualityAnalyzerInterface> audio_quality_analyzer,
118 std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer)
Artem Titov59835852019-02-27 17:44:13 +0100119 : clock_(Clock::GetRealTimeClock()),
Danil Chapovalovd0e0ed82019-04-18 14:48:24 +0200120 task_queue_factory_(CreateDefaultTaskQueueFactory()),
Artem Titovba82e002019-03-15 15:57:53 +0100121 test_case_name_(std::move(test_case_name)) {
Artem Titova6a273d2019-02-07 16:43:51 +0100122 // Create default video quality analyzer. We will always create an analyzer,
123 // even if there are no video streams, because it will be installed into video
124 // encoder/decoder factories.
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100125 if (video_quality_analyzer == nullptr) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200126 video_quality_analyzer = std::make_unique<DefaultVideoQualityAnalyzer>();
Artem Titova6a273d2019-02-07 16:43:51 +0100127 }
128 encoded_image_id_controller_ =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200129 std::make_unique<SingleProcessEncodedImageDataInjector>();
Artem Titova6a273d2019-02-07 16:43:51 +0100130 video_quality_analyzer_injection_helper_ =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200131 std::make_unique<VideoQualityAnalyzerInjectionHelper>(
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100132 std::move(video_quality_analyzer), encoded_image_id_controller_.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100133 encoded_image_id_controller_.get());
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100134
135 if (audio_quality_analyzer == nullptr) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200136 audio_quality_analyzer = std::make_unique<DefaultAudioQualityAnalyzer>();
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100137 }
138 audio_quality_analyzer_.swap(audio_quality_analyzer);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100139}
140
Artem Titovba82e002019-03-15 15:57:53 +0100141void PeerConnectionE2EQualityTest::ExecuteAt(
142 TimeDelta target_time_since_start,
143 std::function<void(TimeDelta)> func) {
144 ExecuteTask(target_time_since_start, absl::nullopt, func);
145}
146
147void PeerConnectionE2EQualityTest::ExecuteEvery(
148 TimeDelta initial_delay_since_start,
149 TimeDelta interval,
150 std::function<void(TimeDelta)> func) {
151 ExecuteTask(initial_delay_since_start, interval, func);
152}
153
154void PeerConnectionE2EQualityTest::ExecuteTask(
155 TimeDelta initial_delay_since_start,
156 absl::optional<TimeDelta> interval,
157 std::function<void(TimeDelta)> func) {
158 RTC_CHECK(initial_delay_since_start.IsFinite() &&
159 initial_delay_since_start >= TimeDelta::Zero());
160 RTC_CHECK(!interval ||
161 (interval->IsFinite() && *interval > TimeDelta::Zero()));
162 rtc::CritScope crit(&lock_);
163 ScheduledActivity activity(initial_delay_since_start, interval, func);
164 if (start_time_.IsInfinite()) {
165 scheduled_activities_.push(std::move(activity));
166 } else {
167 PostTask(std::move(activity));
168 }
169}
170
171void PeerConnectionE2EQualityTest::PostTask(ScheduledActivity activity) {
172 // Because start_time_ will never change at this point copy it to local
173 // variable to capture in in lambda without requirement to hold a lock.
174 Timestamp start_time = start_time_;
175
176 TimeDelta remaining_delay =
177 activity.initial_delay_since_start == TimeDelta::Zero()
178 ? TimeDelta::Zero()
179 : activity.initial_delay_since_start - (Now() - start_time_);
180 if (remaining_delay < TimeDelta::Zero()) {
181 RTC_LOG(WARNING) << "Executing late task immediately, late by="
182 << ToString(remaining_delay.Abs());
183 remaining_delay = TimeDelta::Zero();
184 }
185
186 if (activity.interval) {
187 if (remaining_delay == TimeDelta::Zero()) {
188 repeating_task_handles_.push_back(RepeatingTaskHandle::Start(
189 task_queue_->Get(), [activity, start_time, this]() {
190 activity.func(Now() - start_time);
191 return *activity.interval;
192 }));
193 return;
194 }
195 repeating_task_handles_.push_back(RepeatingTaskHandle::DelayedStart(
196 task_queue_->Get(), remaining_delay, [activity, start_time, this]() {
197 activity.func(Now() - start_time);
198 return *activity.interval;
199 }));
200 return;
201 }
202
203 if (remaining_delay == TimeDelta::Zero()) {
204 task_queue_->PostTask(
205 [activity, start_time, this]() { activity.func(Now() - start_time); });
206 return;
207 }
208
209 task_queue_->PostDelayedTask(
210 [activity, start_time, this]() { activity.func(Now() - start_time); },
211 remaining_delay.ms());
212}
213
Artem Titov18459222019-04-24 11:09:35 +0200214void PeerConnectionE2EQualityTest::AddQualityMetricsReporter(
215 std::unique_ptr<QualityMetricsReporter> quality_metrics_reporter) {
216 quality_metrics_reporters_.push_back(std::move(quality_metrics_reporter));
217}
218
Artem Titovd09bc552019-03-20 11:18:58 +0100219void PeerConnectionE2EQualityTest::AddPeer(
220 rtc::Thread* network_thread,
221 rtc::NetworkManager* network_manager,
222 rtc::FunctionView<void(PeerConfigurer*)> configurer) {
223 peer_configurations_.push_back(
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200224 std::make_unique<PeerConfigurerImpl>(network_thread, network_manager));
Artem Titovd09bc552019-03-20 11:18:58 +0100225 configurer(peer_configurations_.back().get());
226}
227
Jonas Olssona4d87372019-07-05 19:08:33 +0200228void PeerConnectionE2EQualityTest::Run(RunParams run_params) {
Artem Titovd09bc552019-03-20 11:18:58 +0100229 RTC_CHECK_EQ(peer_configurations_.size(), 2)
230 << "Only peer to peer calls are allowed, please add 2 peers";
231
232 std::unique_ptr<Params> alice_params =
233 peer_configurations_[0]->ReleaseParams();
234 std::unique_ptr<InjectableComponents> alice_components =
235 peer_configurations_[0]->ReleaseComponents();
236 std::unique_ptr<Params> bob_params = peer_configurations_[1]->ReleaseParams();
237 std::unique_ptr<InjectableComponents> bob_components =
238 peer_configurations_[1]->ReleaseComponents();
239 peer_configurations_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100240
Artem Titov9a7e7212019-02-28 16:34:17 +0100241 SetDefaultValuesForMissingParams({alice_params.get(), bob_params.get()});
Artem Titovade945d2019-04-02 18:31:48 +0200242 ValidateParams(run_params, {alice_params.get(), bob_params.get()});
Artem Titov23702422019-05-27 15:28:30 +0200243 SetupRequiredFieldTrials(run_params);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100244
245 // Print test summary
246 RTC_LOG(INFO)
247 << "Media quality test: Alice will make a call to Bob with media video="
248 << !alice_params->video_configs.empty()
249 << "; audio=" << alice_params->audio_config.has_value()
250 << ". Bob will respond with media video="
251 << !bob_params->video_configs.empty()
252 << "; audio=" << bob_params->audio_config.has_value();
253
254 const std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
255 signaling_thread->SetName(kSignalThreadName, nullptr);
256 signaling_thread->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100257
Artem Titov70f80e52019-04-12 13:13:39 +0200258 // Create a |task_queue_|.
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200259 task_queue_ = std::make_unique<TaskQueueForTest>("pc_e2e_quality_test");
Artem Titov70f80e52019-04-12 13:13:39 +0200260
Artem Titova6a273d2019-02-07 16:43:51 +0100261 // Create call participants: Alice and Bob.
262 // Audio streams are intercepted in AudioDeviceModule, so if it is required to
263 // catch output of Alice's stream, Alice's output_dump_file_name should be
264 // passed to Bob's TestPeer setup as audio output file name.
Artem Titovbc558ce2019-07-08 19:13:21 +0200265 absl::optional<TestPeer::RemotePeerAudioConfig> alice_remote_audio_config =
266 TestPeer::CreateRemoteAudioConfig(bob_params->audio_config);
267 absl::optional<TestPeer::RemotePeerAudioConfig> bob_remote_audio_config =
268 TestPeer::CreateRemoteAudioConfig(alice_params->audio_config);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100269 // Copy Alice and Bob video configs to correctly pass them into lambdas.
270 std::vector<VideoConfig> alice_video_configs = alice_params->video_configs;
271 std::vector<VideoConfig> bob_video_configs = bob_params->video_configs;
272
Artem Titova6a273d2019-02-07 16:43:51 +0100273 alice_ = TestPeer::CreateTestPeer(
274 std::move(alice_components), std::move(alice_params),
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200275 std::make_unique<FixturePeerConnectionObserver>(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100276 [this, bob_video_configs](
277 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200278 OnTrackCallback(transceiver, bob_video_configs);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100279 },
280 [this]() { StartVideo(alice_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100281 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titovbc558ce2019-07-08 19:13:21 +0200282 alice_remote_audio_config, run_params.video_encoder_bitrate_multiplier,
Artem Titov728a0ee2019-08-20 13:36:35 +0200283 run_params.echo_emulation_config, task_queue_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100284 bob_ = TestPeer::CreateTestPeer(
285 std::move(bob_components), std::move(bob_params),
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200286 std::make_unique<FixturePeerConnectionObserver>(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100287 [this, alice_video_configs](
288 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200289 OnTrackCallback(transceiver, alice_video_configs);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100290 },
291 [this]() { StartVideo(bob_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100292 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titovbc558ce2019-07-08 19:13:21 +0200293 bob_remote_audio_config, run_params.video_encoder_bitrate_multiplier,
Artem Titov728a0ee2019-08-20 13:36:35 +0200294 run_params.echo_emulation_config, task_queue_.get());
Artem Titov9f97c9a2019-02-08 00:35:13 +0100295
296 int num_cores = CpuInfo::DetectNumberOfCores();
297 RTC_DCHECK_GE(num_cores, 1);
298
299 int video_analyzer_threads =
300 num_cores - kPeerConnectionUsedThreads - kFrameworkUsedThreads;
301 if (video_analyzer_threads <= 0) {
302 video_analyzer_threads = 1;
303 }
304 video_analyzer_threads =
305 std::min(video_analyzer_threads, kMaxVideoAnalyzerThreads);
306 RTC_LOG(INFO) << "video_analyzer_threads=" << video_analyzer_threads;
307
Artem Titov59835852019-02-27 17:44:13 +0100308 video_quality_analyzer_injection_helper_->Start(test_case_name_,
309 video_analyzer_threads);
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200310 audio_quality_analyzer_->Start(test_case_name_, &analyzer_helper_);
Artem Titov18459222019-04-24 11:09:35 +0200311 for (auto& reporter : quality_metrics_reporters_) {
312 reporter->Start(test_case_name_);
313 }
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +0100314
315 // Start RTCEventLog recording if requested.
316 if (alice_->params()->rtc_event_log_path) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200317 auto alice_rtc_event_log = std::make_unique<webrtc::RtcEventLogOutputFile>(
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +0100318 alice_->params()->rtc_event_log_path.value());
319 alice_->pc()->StartRtcEventLog(std::move(alice_rtc_event_log),
320 webrtc::RtcEventLog::kImmediateOutput);
321 }
322 if (bob_->params()->rtc_event_log_path) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200323 auto bob_rtc_event_log = std::make_unique<webrtc::RtcEventLogOutputFile>(
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +0100324 bob_->params()->rtc_event_log_path.value());
325 bob_->pc()->StartRtcEventLog(std::move(bob_rtc_event_log),
326 webrtc::RtcEventLog::kImmediateOutput);
327 }
328
Artem Titov4d29ef02019-05-20 10:43:16 +0200329 // Setup alive logging. It is done to prevent test infra to think that test is
330 // dead.
331 RepeatingTaskHandle::DelayedStart(task_queue_->Get(),
332 kAliveMessageLogInterval, []() {
333 std::printf("Test is still running...\n");
334 return kAliveMessageLogInterval;
335 });
336
Artem Titov3ab8eb52019-08-16 16:10:34 +0200337 RTC_LOG(INFO) << "Configuration is done. Now Alice is calling to Bob...";
338
Artem Titovba82e002019-03-15 15:57:53 +0100339 // Setup call.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100340 signaling_thread->Invoke<void>(
Artem Titova6a273d2019-02-07 16:43:51 +0100341 RTC_FROM_HERE,
Artem Titovf65a89b2019-05-07 11:56:44 +0200342 rtc::Bind(&PeerConnectionE2EQualityTest::SetupCallOnSignalingThread, this,
343 run_params));
Artem Titovba82e002019-03-15 15:57:53 +0100344 {
345 rtc::CritScope crit(&lock_);
346 start_time_ = Now();
347 while (!scheduled_activities_.empty()) {
348 PostTask(std::move(scheduled_activities_.front()));
349 scheduled_activities_.pop();
350 }
351 }
Mirko Bonadei71318802019-02-18 18:52:43 +0100352
Artem Titova8549212019-08-19 14:38:06 +0200353 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,
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100360 {{"alice", alice_.get()}, {"bob", bob_.get()}});
361
Artem Titovba82e002019-03-15 15:57:53 +0100362 task_queue_->PostTask([&stats_poller, this]() {
363 RTC_DCHECK_RUN_ON(task_queue_.get());
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100364 stats_polling_task_ =
Artem Titovba82e002019-03-15 15:57:53 +0100365 RepeatingTaskHandle::Start(task_queue_->Get(), [this, &stats_poller]() {
366 RTC_DCHECK_RUN_ON(task_queue_.get());
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100367 stats_poller.PollStatsAndNotifyObservers();
368 return kStatsUpdateInterval;
369 });
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100370 });
371
Mirko Bonadei71318802019-02-18 18:52:43 +0100372 rtc::Event done;
Artem Titov16850592019-07-10 14:34:02 +0200373 bool is_quick_test_enabled = field_trial::IsEnabled("WebRTC-QuickPerfTest");
374 if (is_quick_test_enabled) {
375 done.Wait(kQuickTestModeRunDurationMs);
376 } else {
377 done.Wait(run_params.run_duration.ms());
378 }
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100379
Artem Titov3ab8eb52019-08-16 16:10:34 +0200380 RTC_LOG(INFO) << "Test is done, initiating disconnect sequence.";
381
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200382 task_queue_->SendTask(
383 [&stats_poller, this]() {
384 RTC_DCHECK_RUN_ON(task_queue_.get());
385 stats_polling_task_.Stop();
386 // Get final end-of-call stats.
387 stats_poller.PollStatsAndNotifyObservers();
388 },
389 RTC_FROM_HERE);
Mirko Bonadei71318802019-02-18 18:52:43 +0100390
Artem Titov70f80e52019-04-12 13:13:39 +0200391 // We need to detach AEC dumping from peers, because dump uses |task_queue_|
392 // inside.
393 alice_->DetachAecDump();
394 bob_->DetachAecDump();
Artem Titov4d29ef02019-05-20 10:43:16 +0200395 // Stop all client started tasks on task queue to prevent their access to any
396 // call related objects after these objects will be destroyed during call tear
397 // down.
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200398 task_queue_->SendTask(
399 [this]() {
400 rtc::CritScope crit(&lock_);
401 for (auto& handle : repeating_task_handles_) {
402 handle.Stop();
403 }
404 },
405 RTC_FROM_HERE);
Artem Titovba82e002019-03-15 15:57:53 +0100406 // Tear down the call.
Mirko Bonadei71318802019-02-18 18:52:43 +0100407 signaling_thread->Invoke<void>(
408 RTC_FROM_HERE,
409 rtc::Bind(&PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread,
410 this));
Artem Titovb93c4e62019-05-02 10:52:07 +0200411 Timestamp end_time = Now();
Artem Titov3ab8eb52019-08-16 16:10:34 +0200412 RTC_LOG(INFO) << "All peers are disconnected.";
Artem Titovb93c4e62019-05-02 10:52:07 +0200413 {
414 rtc::CritScope crit(&lock_);
415 real_test_duration_ = end_time - start_time_;
416 }
Mirko Bonadei71318802019-02-18 18:52:43 +0100417
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200418 audio_quality_analyzer_->Stop();
Artem Titov9f97c9a2019-02-08 00:35:13 +0100419 video_quality_analyzer_injection_helper_->Stop();
Artem Titov18459222019-04-24 11:09:35 +0200420 for (auto& reporter : quality_metrics_reporters_) {
421 reporter->StopAndReportResults();
422 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100423
Artem Titov4d29ef02019-05-20 10:43:16 +0200424 // Reset |task_queue_| after test to cleanup.
425 task_queue_.reset();
426
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100427 // Ensuring that TestPeers have been destroyed in order to correctly close
428 // Audio dumps.
429 RTC_CHECK(!alice_);
430 RTC_CHECK(!bob_);
Artem Titov9afdddf2019-10-10 13:29:03 +0200431 // Ensuring that TestVideoCapturerVideoTrackSource and VideoFrameWriter
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100432 // are destroyed on the right thread.
Artem Titovbf9e01a2019-02-14 10:51:27 +0100433 RTC_CHECK(alice_video_sources_.empty());
434 RTC_CHECK(bob_video_sources_.empty());
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100435 RTC_CHECK(video_writers_.empty());
Artem Titova6a273d2019-02-07 16:43:51 +0100436}
437
Artem Titov9a7e7212019-02-28 16:34:17 +0100438void PeerConnectionE2EQualityTest::SetDefaultValuesForMissingParams(
Artem Titova6a273d2019-02-07 16:43:51 +0100439 std::vector<Params*> params) {
Artem Titov3481db22019-02-28 13:13:15 +0100440 int video_counter = 0;
441 int audio_counter = 0;
Artem Titova6a273d2019-02-07 16:43:51 +0100442 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100443 std::set<std::string> audio_labels;
Artem Titova6a273d2019-02-07 16:43:51 +0100444 for (auto* p : params) {
445 for (auto& video_config : p->video_configs) {
Artem Titov9a7e7212019-02-28 16:34:17 +0100446 if (!video_config.generator && !video_config.input_file_name &&
Artem Titov9afdddf2019-10-10 13:29:03 +0200447 !video_config.screen_share_config &&
448 !video_config.capturing_device_index) {
Artem Titov9a7e7212019-02-28 16:34:17 +0100449 video_config.generator = VideoGeneratorType::kDefault;
450 }
Artem Titova6a273d2019-02-07 16:43:51 +0100451 if (!video_config.stream_label) {
452 std::string label;
453 do {
Artem Titov3481db22019-02-28 13:13:15 +0100454 label = "_auto_video_stream_label_" + std::to_string(video_counter);
455 ++video_counter;
Artem Titova6a273d2019-02-07 16:43:51 +0100456 } while (!video_labels.insert(label).second);
457 video_config.stream_label = label;
458 }
459 }
Artem Titov3481db22019-02-28 13:13:15 +0100460 if (p->audio_config) {
461 if (!p->audio_config->stream_label) {
462 std::string label;
463 do {
464 label = "_auto_audio_stream_label_" + std::to_string(audio_counter);
465 ++audio_counter;
466 } while (!audio_labels.insert(label).second);
467 p->audio_config->stream_label = label;
468 }
469 }
Artem Titova6a273d2019-02-07 16:43:51 +0100470 }
471}
472
Artem Titovade945d2019-04-02 18:31:48 +0200473void PeerConnectionE2EQualityTest::ValidateParams(const RunParams& run_params,
474 std::vector<Params*> params) {
475 RTC_CHECK_GT(run_params.video_encoder_bitrate_multiplier, 0.0);
476
Artem Titova6a273d2019-02-07 16:43:51 +0100477 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100478 std::set<std::string> audio_labels;
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100479 int media_streams_count = 0;
480
Artem Titovef3fd9c2019-06-13 16:36:52 +0200481 for (size_t i = 0; i < params.size(); ++i) {
482 Params* p = params[i];
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100483 if (p->audio_config) {
484 media_streams_count++;
485 }
486 media_streams_count += p->video_configs.size();
487
Artem Titova6a273d2019-02-07 16:43:51 +0100488 // Validate that each video config has exactly one of |generator|,
489 // |input_file_name| or |screen_share_config| set. Also validate that all
490 // video stream labels are unique.
491 for (auto& video_config : p->video_configs) {
492 RTC_CHECK(video_config.stream_label);
493 bool inserted =
494 video_labels.insert(video_config.stream_label.value()).second;
495 RTC_CHECK(inserted) << "Duplicate video_config.stream_label="
496 << video_config.stream_label.value();
Artem Titov9afdddf2019-10-10 13:29:03 +0200497 int input_sources_count = 0;
498 if (video_config.generator)
499 ++input_sources_count;
500 if (video_config.input_file_name)
501 ++input_sources_count;
502 if (video_config.screen_share_config)
503 ++input_sources_count;
504 if (video_config.capturing_device_index)
505 ++input_sources_count;
506 RTC_CHECK_EQ(input_sources_count, 1)
Artem Titova6a273d2019-02-07 16:43:51 +0100507 << VideoConfigSourcePresenceToString(video_config);
Artem Titov7581ff72019-05-15 15:45:33 +0200508
509 if (video_config.screen_share_config) {
510 if (video_config.screen_share_config->slides_yuv_file_names.empty()) {
511 if (video_config.screen_share_config->scrolling_params) {
512 // If we have scrolling params, then its |source_width| and
513 // |source_heigh| will be used as width and height of video input,
514 // so we have to validate it against width and height of default
515 // input.
516 RTC_CHECK_EQ(video_config.screen_share_config->scrolling_params
517 ->source_width,
518 kDefaultSlidesWidth);
519 RTC_CHECK_EQ(video_config.screen_share_config->scrolling_params
520 ->source_height,
521 kDefaultSlidesHeight);
522 } else {
523 RTC_CHECK_EQ(video_config.width, kDefaultSlidesWidth);
524 RTC_CHECK_EQ(video_config.height, kDefaultSlidesHeight);
525 }
526 }
527 if (video_config.screen_share_config->scrolling_params) {
528 RTC_CHECK_LE(
529 video_config.screen_share_config->scrolling_params->duration,
530 video_config.screen_share_config->slide_change_interval);
531 RTC_CHECK_GE(
532 video_config.screen_share_config->scrolling_params->source_width,
533 video_config.width);
534 RTC_CHECK_GE(
535 video_config.screen_share_config->scrolling_params->source_height,
536 video_config.height);
537 }
538 }
Artem Titovef3fd9c2019-06-13 16:36:52 +0200539 if (video_config.simulcast_config) {
Artem Titovd70d80d2019-07-19 11:00:40 +0200540 // We support simulcast only from caller.
Artem Titovef3fd9c2019-06-13 16:36:52 +0200541 RTC_CHECK_EQ(i, 0)
542 << "Only simulcast stream from first peer is supported";
Johannes Kronac315b22019-09-19 14:55:58 +0200543 RTC_CHECK(!video_config.max_encode_bitrate_bps)
544 << "Setting max encode bitrate is not implemented for simulcast.";
545 RTC_CHECK(!video_config.min_encode_bitrate_bps)
546 << "Setting min encode bitrate is not implemented for simulcast.";
Artem Titovef3fd9c2019-06-13 16:36:52 +0200547 }
Artem Titova6a273d2019-02-07 16:43:51 +0100548 }
549 if (p->audio_config) {
Artem Titov3481db22019-02-28 13:13:15 +0100550 bool inserted =
551 audio_labels.insert(p->audio_config->stream_label.value()).second;
552 RTC_CHECK(inserted) << "Duplicate audio_config.stream_label="
553 << p->audio_config->stream_label.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100554 // Check that if mode input file name specified only if mode is kFile.
555 if (p->audio_config.value().mode == AudioConfig::Mode::kGenerated) {
556 RTC_CHECK(!p->audio_config.value().input_file_name);
557 }
558 if (p->audio_config.value().mode == AudioConfig::Mode::kFile) {
559 RTC_CHECK(p->audio_config.value().input_file_name);
Artem Titov0b443142019-03-20 11:11:08 +0100560 RTC_CHECK(
Artem Titov70f80e52019-04-12 13:13:39 +0200561 test::FileExists(p->audio_config.value().input_file_name.value()))
562 << p->audio_config.value().input_file_name.value()
563 << " doesn't exist";
Artem Titova6a273d2019-02-07 16:43:51 +0100564 }
565 }
566 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100567
568 RTC_CHECK_GT(media_streams_count, 0) << "No media in the call.";
Artem Titova6a273d2019-02-07 16:43:51 +0100569}
570
Artem Titov23702422019-05-27 15:28:30 +0200571void PeerConnectionE2EQualityTest::SetupRequiredFieldTrials(
572 const RunParams& run_params) {
573 std::string field_trials = "";
574 if (run_params.use_flex_fec) {
575 field_trials += kFlexFecEnabledFieldTrials;
576 }
577 if (!field_trials.empty()) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200578 override_field_trials_ = std::make_unique<test::ScopedFieldTrials>(
Artem Titov23702422019-05-27 15:28:30 +0200579 field_trial::GetFieldTrialString() + field_trials);
580 }
581}
582
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200583void PeerConnectionE2EQualityTest::OnTrackCallback(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100584 rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
585 std::vector<VideoConfig> remote_video_configs) {
586 const rtc::scoped_refptr<MediaStreamTrackInterface>& track =
587 transceiver->receiver()->track();
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200588 RTC_CHECK_EQ(transceiver->receiver()->stream_ids().size(), 1);
589 std::string stream_label = transceiver->receiver()->stream_ids().front();
590 analyzer_helper_.AddTrackToStreamMapping(track->id(), stream_label);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100591 if (track->kind() != MediaStreamTrackInterface::kVideoKind) {
592 return;
593 }
594
595 VideoConfig* video_config = nullptr;
596 for (auto& config : remote_video_configs) {
Artem Titov7c554152019-02-28 10:25:52 +0100597 if (config.stream_label == stream_label) {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100598 video_config = &config;
599 break;
600 }
601 }
602 RTC_CHECK(video_config);
Artem Titov0b443142019-03-20 11:11:08 +0100603 test::VideoFrameWriter* writer = MaybeCreateVideoWriter(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100604 video_config->output_dump_file_name, *video_config);
605 // It is safe to cast here, because it is checked above that
606 // track->kind() is kVideoKind.
607 auto* video_track = static_cast<VideoTrackInterface*>(track.get());
608 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> video_sink =
Artem Titovddef8d12019-09-06 14:31:50 +0200609 video_quality_analyzer_injection_helper_->CreateVideoSink(*video_config,
610 writer);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100611 video_track->AddOrUpdateSink(video_sink.get(), rtc::VideoSinkWants());
612 output_video_sinks_.push_back(std::move(video_sink));
613}
614
Artem Titovf65a89b2019-05-07 11:56:44 +0200615void PeerConnectionE2EQualityTest::SetupCallOnSignalingThread(
616 const RunParams& run_params) {
Artem Titov7c554152019-02-28 10:25:52 +0100617 // We need receive-only transceivers for Bob's media stream, so there will
618 // be media section in SDP for that streams in Alice's offer, because it is
619 // forbidden to add new media sections in answer in Unified Plan.
620 RtpTransceiverInit receive_only_transceiver_init;
621 receive_only_transceiver_init.direction = RtpTransceiverDirection::kRecvOnly;
Artem Titovef3fd9c2019-06-13 16:36:52 +0200622 int alice_transceivers_counter = 0;
Artem Titov7c554152019-02-28 10:25:52 +0100623 if (bob_->params()->audio_config) {
624 // Setup receive audio transceiver if Bob has audio to send. If we'll need
625 // multiple audio streams, then we need transceiver for each Bob's audio
626 // stream.
Artem Titov7f2a67f2019-06-13 19:45:55 +0200627 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
628 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_AUDIO,
629 receive_only_transceiver_init);
630 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200631 alice_transceivers_counter++;
632 }
633
Johannes Kronac315b22019-09-19 14:55:58 +0200634 size_t alice_video_transceivers_non_simulcast_counter = 0;
Artem Titovef3fd9c2019-06-13 16:36:52 +0200635 for (auto& video_config : alice_->params()->video_configs) {
Johannes Kronac315b22019-09-19 14:55:58 +0200636 RtpTransceiverInit transceiver_params;
Artem Titovef3fd9c2019-06-13 16:36:52 +0200637 if (video_config.simulcast_config) {
Artem Titovef3fd9c2019-06-13 16:36:52 +0200638 transceiver_params.direction = RtpTransceiverDirection::kSendOnly;
Artem Titovd70d80d2019-07-19 11:00:40 +0200639 if (run_params.video_codec_name == cricket::kVp8CodecName) {
640 // For Vp8 simulcast we need to add as many RtpEncodingParameters to the
641 // track as many simulcast streams requested.
642 for (int i = 0;
643 i < video_config.simulcast_config->simulcast_streams_count; ++i) {
644 RtpEncodingParameters enc_params;
645 // We need to be sure, that all rids will be unique with all mids.
646 enc_params.rid = std::to_string(alice_transceivers_counter) + "000" +
647 std::to_string(i);
648 transceiver_params.send_encodings.push_back(enc_params);
649 }
Artem Titovef3fd9c2019-06-13 16:36:52 +0200650 }
Johannes Kronac315b22019-09-19 14:55:58 +0200651 } else {
652 transceiver_params.direction = RtpTransceiverDirection::kSendRecv;
653 RtpEncodingParameters enc_params;
654 enc_params.max_bitrate_bps = video_config.max_encode_bitrate_bps;
655 enc_params.min_bitrate_bps = video_config.min_encode_bitrate_bps;
656 transceiver_params.send_encodings.push_back(enc_params);
657
658 alice_video_transceivers_non_simulcast_counter++;
Artem Titovef3fd9c2019-06-13 16:36:52 +0200659 }
Johannes Kronac315b22019-09-19 14:55:58 +0200660 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
661 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
662 transceiver_params);
663 RTC_CHECK(result.ok());
664
665 alice_transceivers_counter++;
Artem Titov7c554152019-02-28 10:25:52 +0100666 }
Johannes Kronac315b22019-09-19 14:55:58 +0200667
668 // Add receive only transceivers in case Bob has more video_configs than
669 // Alice.
670 for (size_t i = alice_video_transceivers_non_simulcast_counter;
671 i < bob_->params()->video_configs.size(); ++i) {
Artem Titov7f2a67f2019-06-13 19:45:55 +0200672 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
673 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
674 receive_only_transceiver_init);
675 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200676 alice_transceivers_counter++;
Artem Titov7c554152019-02-28 10:25:52 +0100677 }
Johannes Kronac315b22019-09-19 14:55:58 +0200678
Artem Titov7c554152019-02-28 10:25:52 +0100679 // Then add media for Alice and Bob
680 alice_video_sources_ = MaybeAddMedia(alice_.get());
681 bob_video_sources_ = MaybeAddMedia(bob_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100682
Artem Titovf65a89b2019-05-07 11:56:44 +0200683 SetPeerCodecPreferences(alice_.get(), run_params);
684 SetPeerCodecPreferences(bob_.get(), run_params);
685
Artem Titovd70d80d2019-07-19 11:00:40 +0200686 SetupCall(run_params);
Mirko Bonadei71318802019-02-18 18:52:43 +0100687}
Artem Titova6a273d2019-02-07 16:43:51 +0100688
Mirko Bonadei71318802019-02-18 18:52:43 +0100689void PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread() {
Artem Titova6a273d2019-02-07 16:43:51 +0100690 TearDownCall();
Artem Titova6a273d2019-02-07 16:43:51 +0100691}
692
Artem Titov9afdddf2019-10-10 13:29:03 +0200693std::vector<rtc::scoped_refptr<TestVideoCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100694PeerConnectionE2EQualityTest::MaybeAddMedia(TestPeer* peer) {
695 MaybeAddAudio(peer);
696 return MaybeAddVideo(peer);
Artem Titova6a273d2019-02-07 16:43:51 +0100697}
698
Artem Titov9afdddf2019-10-10 13:29:03 +0200699std::vector<rtc::scoped_refptr<TestVideoCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100700PeerConnectionE2EQualityTest::MaybeAddVideo(TestPeer* peer) {
Artem Titova6a273d2019-02-07 16:43:51 +0100701 // Params here valid because of pre-run validation.
702 Params* params = peer->params();
Artem Titov9afdddf2019-10-10 13:29:03 +0200703 std::vector<rtc::scoped_refptr<TestVideoCapturerVideoTrackSource>> out;
Artem Titova6a273d2019-02-07 16:43:51 +0100704 for (auto video_config : params->video_configs) {
Artem Titov9afdddf2019-10-10 13:29:03 +0200705 // Setup input video source into peer connection.
Artem Titov0b443142019-03-20 11:11:08 +0100706 test::VideoFrameWriter* writer =
Artem Titova6a273d2019-02-07 16:43:51 +0100707 MaybeCreateVideoWriter(video_config.input_dump_file_name, video_config);
Artem Titov9afdddf2019-10-10 13:29:03 +0200708 std::unique_ptr<test::TestVideoCapturer> capturer = CreateVideoCapturer(
709 video_config,
710 video_quality_analyzer_injection_helper_->CreateFramePreprocessor(
711 video_config, writer));
712 rtc::scoped_refptr<TestVideoCapturerVideoTrackSource> source =
713 new rtc::RefCountedObject<TestVideoCapturerVideoTrackSource>(
Artem Titov89e7fcb2019-09-26 11:59:49 +0000714 std::move(capturer),
Artem Titovb3f14872019-09-09 13:48:21 +0200715 /*is_screencast=*/video_config.screen_share_config &&
716 video_config.screen_share_config->use_text_content_hint);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100717 out.push_back(source);
Artem Titova6a273d2019-02-07 16:43:51 +0100718 RTC_LOG(INFO) << "Adding video with video_config.stream_label="
719 << video_config.stream_label.value();
720 rtc::scoped_refptr<VideoTrackInterface> track =
721 peer->pc_factory()->CreateVideoTrack(video_config.stream_label.value(),
722 source);
Artem Titovb3f14872019-09-09 13:48:21 +0200723 if (video_config.screen_share_config &&
724 video_config.screen_share_config->use_text_content_hint) {
Artem Titov232b6a12019-05-29 11:05:01 +0200725 track->set_content_hint(VideoTrackInterface::ContentHint::kText);
726 }
Artem Titov1e49ab22019-07-30 13:17:25 +0200727 RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> sender =
728 peer->AddTrack(track, {video_config.stream_label.value()});
729 RTC_CHECK(sender.ok());
730 if (video_config.temporal_layers_count) {
731 RtpParameters rtp_parameters = sender.value()->GetParameters();
732 for (auto& encoding_parameters : rtp_parameters.encodings) {
733 encoding_parameters.num_temporal_layers =
734 video_config.temporal_layers_count;
735 }
736 RTCError res = sender.value()->SetParameters(rtp_parameters);
737 RTC_CHECK(res.ok()) << "Failed to set RTP parameters";
738 }
Artem Titova6a273d2019-02-07 16:43:51 +0100739 }
Artem Titovbf9e01a2019-02-14 10:51:27 +0100740 return out;
Artem Titova6a273d2019-02-07 16:43:51 +0100741}
742
Artem Titov9afdddf2019-10-10 13:29:03 +0200743std::unique_ptr<test::TestVideoCapturer>
744PeerConnectionE2EQualityTest::CreateVideoCapturer(
745 const VideoConfig& video_config,
746 std::unique_ptr<test::TestVideoCapturer::FramePreprocessor>
747 frame_preprocessor) {
748 if (video_config.capturing_device_index) {
749 std::unique_ptr<test::TestVideoCapturer> capturer =
750 test::CreateVideoCapturer(video_config.width, video_config.height,
751 video_config.fps,
752 *video_config.capturing_device_index);
753 capturer->SetFramePreprocessor(std::move(frame_preprocessor));
754 RTC_CHECK(capturer)
755 << "Failed to obtain input stream from capturing device #"
756 << *video_config.capturing_device_index;
757 return capturer;
758 }
759
760 std::unique_ptr<test::FrameGenerator> frame_generator = nullptr;
Artem Titova6a273d2019-02-07 16:43:51 +0100761 if (video_config.generator) {
Artem Titov0b443142019-03-20 11:11:08 +0100762 absl::optional<test::FrameGenerator::OutputType> frame_generator_type =
Artem Titova6a273d2019-02-07 16:43:51 +0100763 absl::nullopt;
764 if (video_config.generator == VideoGeneratorType::kDefault) {
Sebastian Janssoned0febf2019-07-26 15:58:11 +0200765 frame_generator_type = test::FrameGenerator::OutputType::kI420;
Artem Titova6a273d2019-02-07 16:43:51 +0100766 } else if (video_config.generator == VideoGeneratorType::kI420A) {
Sebastian Janssoned0febf2019-07-26 15:58:11 +0200767 frame_generator_type = test::FrameGenerator::OutputType::kI420A;
Artem Titova6a273d2019-02-07 16:43:51 +0100768 } else if (video_config.generator == VideoGeneratorType::kI010) {
Sebastian Janssoned0febf2019-07-26 15:58:11 +0200769 frame_generator_type = test::FrameGenerator::OutputType::kI010;
Artem Titova6a273d2019-02-07 16:43:51 +0100770 }
Artem Titov9afdddf2019-10-10 13:29:03 +0200771 frame_generator = test::FrameGenerator::CreateSquareGenerator(
Artem Titova6a273d2019-02-07 16:43:51 +0100772 static_cast<int>(video_config.width),
773 static_cast<int>(video_config.height), frame_generator_type,
774 absl::nullopt);
775 }
776 if (video_config.input_file_name) {
Artem Titov9afdddf2019-10-10 13:29:03 +0200777 frame_generator = test::FrameGenerator::CreateFromYuvFile(
Artem Titova6a273d2019-02-07 16:43:51 +0100778 std::vector<std::string>(/*count=*/1,
779 video_config.input_file_name.value()),
780 video_config.width, video_config.height, /*frame_repeat_count=*/1);
781 }
782 if (video_config.screen_share_config) {
Artem Titov9afdddf2019-10-10 13:29:03 +0200783 frame_generator = CreateScreenShareFrameGenerator(video_config);
Artem Titova6a273d2019-02-07 16:43:51 +0100784 }
Artem Titov9afdddf2019-10-10 13:29:03 +0200785 RTC_CHECK(frame_generator) << "Unsupported video_config input source";
786
787 auto capturer = std::make_unique<test::FrameGeneratorCapturer>(
788 clock_, std::move(frame_generator), video_config.fps,
789 *task_queue_factory_);
790 capturer->SetFramePreprocessor(std::move(frame_preprocessor));
791 capturer->Init();
792 return capturer;
Artem Titova6a273d2019-02-07 16:43:51 +0100793}
794
Artem Titov7581ff72019-05-15 15:45:33 +0200795std::unique_ptr<test::FrameGenerator>
796PeerConnectionE2EQualityTest::CreateScreenShareFrameGenerator(
797 const VideoConfig& video_config) {
798 RTC_CHECK(video_config.screen_share_config);
799 if (video_config.screen_share_config->generate_slides) {
800 return test::FrameGenerator::CreateSlideGenerator(
801 video_config.width, video_config.height,
802 video_config.screen_share_config->slide_change_interval.seconds() *
803 video_config.fps);
804 }
805 std::vector<std::string> slides =
806 video_config.screen_share_config->slides_yuv_file_names;
807 if (slides.empty()) {
808 // If slides is empty we need to add default slides as source. In such case
809 // video width and height is validated to be equal to kDefaultSlidesWidth
810 // and kDefaultSlidesHeight.
811 slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv"));
812 slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv"));
813 slides.push_back(test::ResourcePath("photo_1850_1110", "yuv"));
814 slides.push_back(test::ResourcePath("difficult_photo_1850_1110", "yuv"));
815 }
816 if (!video_config.screen_share_config->scrolling_params) {
817 // Cycle image every slide_change_interval seconds.
818 return test::FrameGenerator::CreateFromYuvFile(
819 slides, video_config.width, video_config.height,
820 video_config.screen_share_config->slide_change_interval.seconds() *
821 video_config.fps);
822 }
823
824 // |pause_duration| is nonnegative. It is validated in ValidateParams(...).
825 TimeDelta pause_duration =
826 video_config.screen_share_config->slide_change_interval -
827 video_config.screen_share_config->scrolling_params->duration;
828
829 return test::FrameGenerator::CreateScrollingInputFromYuvFiles(
830 clock_, slides,
831 video_config.screen_share_config->scrolling_params->source_width,
832 video_config.screen_share_config->scrolling_params->source_height,
833 video_config.width, video_config.height,
834 video_config.screen_share_config->scrolling_params->duration.ms(),
835 pause_duration.ms());
836}
837
Artem Titov7c554152019-02-28 10:25:52 +0100838void PeerConnectionE2EQualityTest::MaybeAddAudio(TestPeer* peer) {
839 if (!peer->params()->audio_config) {
840 return;
841 }
Artem Titov3481db22019-02-28 13:13:15 +0100842 const AudioConfig& audio_config = peer->params()->audio_config.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100843 rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
Artem Titov3481db22019-02-28 13:13:15 +0100844 peer->pc_factory()->CreateAudioSource(audio_config.audio_options);
Artem Titova6a273d2019-02-07 16:43:51 +0100845 rtc::scoped_refptr<AudioTrackInterface> track =
Artem Titov3481db22019-02-28 13:13:15 +0100846 peer->pc_factory()->CreateAudioTrack(*audio_config.stream_label, source);
847 peer->AddTrack(track, {*audio_config.stream_label});
Artem Titova6a273d2019-02-07 16:43:51 +0100848}
849
Artem Titovf65a89b2019-05-07 11:56:44 +0200850void PeerConnectionE2EQualityTest::SetPeerCodecPreferences(
851 TestPeer* peer,
852 const RunParams& run_params) {
Artem Titovef3fd9c2019-06-13 16:36:52 +0200853 std::vector<RtpCodecCapability> with_rtx_video_capabilities =
Artem Titov7f2a67f2019-06-13 19:45:55 +0200854 FilterVideoCodecCapabilities(
Artem Titovef3fd9c2019-06-13 16:36:52 +0200855 run_params.video_codec_name, run_params.video_codec_required_params,
856 true, run_params.use_ulp_fec, run_params.use_flex_fec,
857 peer->pc_factory()
858 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
859 .codecs);
860 std::vector<RtpCodecCapability> without_rtx_video_capabilities =
Artem Titov7f2a67f2019-06-13 19:45:55 +0200861 FilterVideoCodecCapabilities(
Artem Titovef3fd9c2019-06-13 16:36:52 +0200862 run_params.video_codec_name, run_params.video_codec_required_params,
863 false, run_params.use_ulp_fec, run_params.use_flex_fec,
864 peer->pc_factory()
865 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
866 .codecs);
Artem Titovf65a89b2019-05-07 11:56:44 +0200867
868 // Set codecs for transceivers
869 for (auto transceiver : peer->pc()->GetTransceivers()) {
870 if (transceiver->media_type() == cricket::MediaType::MEDIA_TYPE_VIDEO) {
Artem Titovef3fd9c2019-06-13 16:36:52 +0200871 if (transceiver->sender()->init_send_encodings().size() > 1) {
872 // If transceiver's sender has more then 1 send encodings, it means it
873 // has multiple simulcast streams, so we need disable RTX on it.
Artem Titov7f2a67f2019-06-13 19:45:55 +0200874 RTCError result =
875 transceiver->SetCodecPreferences(without_rtx_video_capabilities);
876 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200877 } else {
Artem Titov7f2a67f2019-06-13 19:45:55 +0200878 RTCError result =
879 transceiver->SetCodecPreferences(with_rtx_video_capabilities);
880 RTC_CHECK(result.ok());
Artem Titovef3fd9c2019-06-13 16:36:52 +0200881 }
Artem Titovf65a89b2019-05-07 11:56:44 +0200882 }
883 }
884}
885
Artem Titovd70d80d2019-07-19 11:00:40 +0200886void PeerConnectionE2EQualityTest::SetupCall(const RunParams& run_params) {
Artem Titov39483c62019-07-19 17:03:52 +0200887 std::map<std::string, int> stream_label_to_simulcast_streams_count;
Artem Titov594597c2019-07-18 13:39:41 +0200888 // We add only Alice here, because simulcast/svc is supported only from the
889 // first peer.
890 for (auto& video_config : alice_->params()->video_configs) {
891 if (video_config.simulcast_config) {
Artem Titov39483c62019-07-19 17:03:52 +0200892 stream_label_to_simulcast_streams_count.insert(
893 {*video_config.stream_label,
894 video_config.simulcast_config->simulcast_streams_count});
Artem Titov594597c2019-07-18 13:39:41 +0200895 }
896 }
Artem Titovd70d80d2019-07-19 11:00:40 +0200897 PatchingParams patching_params(run_params.video_codec_name,
Artem Titov39483c62019-07-19 17:03:52 +0200898 run_params.use_conference_mode,
899 stream_label_to_simulcast_streams_count);
Artem Titov594597c2019-07-18 13:39:41 +0200900 SignalingInterceptor signaling_interceptor(patching_params);
Artem Titova6a273d2019-02-07 16:43:51 +0100901 // Connect peers.
Artem Titovef3fd9c2019-06-13 16:36:52 +0200902 ExchangeOfferAnswer(&signaling_interceptor);
Artem Titova6a273d2019-02-07 16:43:51 +0100903 // Do the SDP negotiation, and also exchange ice candidates.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100904 ASSERT_EQ_WAIT(alice_->signaling_state(), PeerConnectionInterface::kStable,
Artem Titova6a273d2019-02-07 16:43:51 +0100905 kDefaultTimeoutMs);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100906 ASSERT_TRUE_WAIT(alice_->IsIceGatheringDone(), kDefaultTimeoutMs);
907 ASSERT_TRUE_WAIT(bob_->IsIceGatheringDone(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100908
Artem Titovef3fd9c2019-06-13 16:36:52 +0200909 ExchangeIceCandidates(&signaling_interceptor);
Artem Titova6a273d2019-02-07 16:43:51 +0100910 // This means that ICE and DTLS are connected.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100911 ASSERT_TRUE_WAIT(bob_->IsIceConnected(), kDefaultTimeoutMs);
912 ASSERT_TRUE_WAIT(alice_->IsIceConnected(), kDefaultTimeoutMs);
Artem Titov3ab8eb52019-08-16 16:10:34 +0200913 RTC_LOG(INFO) << "Call is started (all peers are connected).";
Artem Titova6a273d2019-02-07 16:43:51 +0100914}
915
Artem Titovef3fd9c2019-06-13 16:36:52 +0200916void PeerConnectionE2EQualityTest::ExchangeOfferAnswer(
917 SignalingInterceptor* signaling_interceptor) {
918 std::string log_output;
919
920 auto offer = alice_->CreateOffer();
921 RTC_CHECK(offer);
922 offer->ToString(&log_output);
923 RTC_LOG(INFO) << "Original offer: " << log_output;
924 LocalAndRemoteSdp patch_result =
925 signaling_interceptor->PatchOffer(std::move(offer));
926 patch_result.local_sdp->ToString(&log_output);
927 RTC_LOG(INFO) << "Offer to set as local description: " << log_output;
928 patch_result.remote_sdp->ToString(&log_output);
929 RTC_LOG(INFO) << "Offer to set as remote description: " << log_output;
930
931 bool set_local_offer =
932 alice_->SetLocalDescription(std::move(patch_result.local_sdp));
933 RTC_CHECK(set_local_offer);
934 bool set_remote_offer =
935 bob_->SetRemoteDescription(std::move(patch_result.remote_sdp));
936 RTC_CHECK(set_remote_offer);
937 auto answer = bob_->CreateAnswer();
938 RTC_CHECK(answer);
939 answer->ToString(&log_output);
940 RTC_LOG(INFO) << "Original answer: " << log_output;
941 patch_result = signaling_interceptor->PatchAnswer(std::move(answer));
942 patch_result.local_sdp->ToString(&log_output);
943 RTC_LOG(INFO) << "Answer to set as local description: " << log_output;
944 patch_result.remote_sdp->ToString(&log_output);
945 RTC_LOG(INFO) << "Answer to set as remote description: " << log_output;
946
947 bool set_local_answer =
948 bob_->SetLocalDescription(std::move(patch_result.local_sdp));
949 RTC_CHECK(set_local_answer);
950 bool set_remote_answer =
951 alice_->SetRemoteDescription(std::move(patch_result.remote_sdp));
952 RTC_CHECK(set_remote_answer);
953}
954
955void PeerConnectionE2EQualityTest::ExchangeIceCandidates(
956 SignalingInterceptor* signaling_interceptor) {
957 // Connect an ICE candidate pairs.
958 std::vector<std::unique_ptr<IceCandidateInterface>> alice_candidates =
959 signaling_interceptor->PatchOffererIceCandidates(
960 alice_->observer()->GetAllCandidates());
961 for (auto& candidate : alice_candidates) {
962 std::string candidate_str;
963 RTC_CHECK(candidate->ToString(&candidate_str));
964 RTC_LOG(INFO) << "Alice ICE candidate(mid= " << candidate->sdp_mid()
965 << "): " << candidate_str;
966 }
967 ASSERT_TRUE(bob_->AddIceCandidates(std::move(alice_candidates)));
968 std::vector<std::unique_ptr<IceCandidateInterface>> bob_candidates =
969 signaling_interceptor->PatchAnswererIceCandidates(
970 bob_->observer()->GetAllCandidates());
971 for (auto& candidate : bob_candidates) {
972 std::string candidate_str;
973 RTC_CHECK(candidate->ToString(&candidate_str));
974 RTC_LOG(INFO) << "Bob ICE candidate(mid= " << candidate->sdp_mid()
975 << "): " << candidate_str;
976 }
977 ASSERT_TRUE(alice_->AddIceCandidates(std::move(bob_candidates)));
978}
979
Artem Titovbf9e01a2019-02-14 10:51:27 +0100980void PeerConnectionE2EQualityTest::StartVideo(
Artem Titov9afdddf2019-10-10 13:29:03 +0200981 const std::vector<rtc::scoped_refptr<TestVideoCapturerVideoTrackSource>>&
982 sources) {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100983 for (auto& source : sources) {
984 if (source->state() != MediaSourceInterface::SourceState::kLive) {
985 source->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100986 }
Artem Titova6a273d2019-02-07 16:43:51 +0100987 }
988}
989
990void PeerConnectionE2EQualityTest::TearDownCall() {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100991 for (const auto& video_source : alice_video_sources_) {
992 video_source->Stop();
993 }
994 for (const auto& video_source : bob_video_sources_) {
Artem Titova6a273d2019-02-07 16:43:51 +0100995 video_source->Stop();
996 }
997
998 alice_->pc()->Close();
999 bob_->pc()->Close();
1000
1001 for (const auto& video_writer : video_writers_) {
1002 video_writer->Close();
1003 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +01001004
Artem Titovbf9e01a2019-02-14 10:51:27 +01001005 alice_video_sources_.clear();
1006 bob_video_sources_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +01001007 video_writers_.clear();
1008 alice_.reset();
1009 bob_.reset();
Artem Titova6a273d2019-02-07 16:43:51 +01001010}
1011
Artem Titov0b443142019-03-20 11:11:08 +01001012test::VideoFrameWriter* PeerConnectionE2EQualityTest::MaybeCreateVideoWriter(
Artem Titova6a273d2019-02-07 16:43:51 +01001013 absl::optional<std::string> file_name,
1014 const VideoConfig& config) {
1015 if (!file_name) {
1016 return nullptr;
1017 }
Artem Titovef3fd9c2019-06-13 16:36:52 +02001018 // TODO(titovartem) create only one file writer for simulcast video track.
Mirko Bonadei317a1f02019-09-17 17:06:18 +02001019 auto video_writer = std::make_unique<test::Y4mVideoFrameWriterImpl>(
Artem Titova6a273d2019-02-07 16:43:51 +01001020 file_name.value(), config.width, config.height, config.fps);
Artem Titov0b443142019-03-20 11:11:08 +01001021 test::VideoFrameWriter* out = video_writer.get();
Artem Titova6a273d2019-02-07 16:43:51 +01001022 video_writers_.push_back(std::move(video_writer));
1023 return out;
1024}
1025
Artem Titovba82e002019-03-15 15:57:53 +01001026Timestamp PeerConnectionE2EQualityTest::Now() const {
Sebastian Janssonb64ad0e2019-06-19 09:39:34 +02001027 return clock_->CurrentTime();
Artem Titovba82e002019-03-15 15:57:53 +01001028}
1029
1030PeerConnectionE2EQualityTest::ScheduledActivity::ScheduledActivity(
1031 TimeDelta initial_delay_since_start,
1032 absl::optional<TimeDelta> interval,
1033 std::function<void(TimeDelta)> func)
1034 : initial_delay_since_start(initial_delay_since_start),
Johannes Kronac315b22019-09-19 14:55:58 +02001035 interval(interval),
Artem Titovba82e002019-03-15 15:57:53 +01001036 func(std::move(func)) {}
1037
Artem Titov0b443142019-03-20 11:11:08 +01001038} // namespace webrtc_pc_e2e
Artem Titova6a273d2019-02-07 16:43:51 +01001039} // namespace webrtc