blob: 127818d9bcf60274c2efdf60b653c29750b1eec1 [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"
Niels Möllerd8b9ed72019-05-08 13:53:51 +020020#include "api/rtc_event_log_output_file.h"
Artem Titova6a273d2019-02-07 16:43:51 +010021#include "api/scoped_refptr.h"
Danil Chapovalovd0e0ed82019-04-18 14:48:24 +020022#include "api/task_queue/default_task_queue_factory.h"
Artem Titovd57628f2019-03-22 12:34:25 +010023#include "api/test/video_quality_analyzer_interface.h"
Artem Titova6a273d2019-02-07 16:43:51 +010024#include "api/units/time_delta.h"
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +010025#include "logging/rtc_event_log/rtc_event_log.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 Titov23702422019-05-27 15:28:30 +020058// Field trials to enable Flex FEC advertising and receiving.
59constexpr char kFlexFecEnabledFieldTrials[] =
60 "WebRTC-FlexFEC-03-Advertised/Enabled/WebRTC-FlexFEC-03/Enabled/";
61
Artem Titova6a273d2019-02-07 16:43:51 +010062std::string VideoConfigSourcePresenceToString(const VideoConfig& video_config) {
63 char buf[1024];
64 rtc::SimpleStringBuilder builder(buf);
65 builder << "video_config.generator=" << video_config.generator.has_value()
66 << "; video_config.input_file_name="
67 << video_config.input_file_name.has_value()
68 << "; video_config.screen_share_config="
69 << video_config.screen_share_config.has_value() << ";";
70 return builder.str();
71}
72
Artem Titovbf9e01a2019-02-14 10:51:27 +010073class FixturePeerConnectionObserver : public MockPeerConnectionObserver {
74 public:
75 // |on_track_callback| will be called when any new track will be added to peer
76 // connection.
77 // |on_connected_callback| will be called when peer connection will come to
78 // either connected or completed state. Client should notice that in the case
79 // of reconnect this callback can be called again, so it should be tolerant
80 // to such behavior.
81 FixturePeerConnectionObserver(
82 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
83 on_track_callback,
84 std::function<void()> on_connected_callback)
85 : on_track_callback_(std::move(on_track_callback)),
86 on_connected_callback_(std::move(on_connected_callback)) {}
87
88 void OnTrack(
89 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) override {
90 MockPeerConnectionObserver::OnTrack(transceiver);
91 on_track_callback_(transceiver);
92 }
93
94 void OnIceConnectionChange(
95 PeerConnectionInterface::IceConnectionState new_state) override {
96 MockPeerConnectionObserver::OnIceConnectionChange(new_state);
97 if (ice_connected_) {
98 on_connected_callback_();
99 }
100 }
101
102 private:
103 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
104 on_track_callback_;
105 std::function<void()> on_connected_callback_;
106};
107
Artem Titova6a273d2019-02-07 16:43:51 +0100108} // namespace
109
110PeerConnectionE2EQualityTest::PeerConnectionE2EQualityTest(
Artem Titov59835852019-02-27 17:44:13 +0100111 std::string test_case_name,
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100112 std::unique_ptr<AudioQualityAnalyzerInterface> audio_quality_analyzer,
113 std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer)
Artem Titov59835852019-02-27 17:44:13 +0100114 : clock_(Clock::GetRealTimeClock()),
Danil Chapovalovd0e0ed82019-04-18 14:48:24 +0200115 task_queue_factory_(CreateDefaultTaskQueueFactory()),
Artem Titovba82e002019-03-15 15:57:53 +0100116 test_case_name_(std::move(test_case_name)) {
Artem Titova6a273d2019-02-07 16:43:51 +0100117 // Create default video quality analyzer. We will always create an analyzer,
118 // even if there are no video streams, because it will be installed into video
119 // encoder/decoder factories.
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100120 if (video_quality_analyzer == nullptr) {
Artem Titov859abef2019-03-01 11:11:09 +0100121 video_quality_analyzer = absl::make_unique<DefaultVideoQualityAnalyzer>();
Artem Titova6a273d2019-02-07 16:43:51 +0100122 }
123 encoded_image_id_controller_ =
Artem Titov32232e92019-02-20 21:13:14 +0100124 absl::make_unique<SingleProcessEncodedImageDataInjector>();
Artem Titova6a273d2019-02-07 16:43:51 +0100125 video_quality_analyzer_injection_helper_ =
126 absl::make_unique<VideoQualityAnalyzerInjectionHelper>(
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100127 std::move(video_quality_analyzer), encoded_image_id_controller_.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100128 encoded_image_id_controller_.get());
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100129
130 if (audio_quality_analyzer == nullptr) {
131 audio_quality_analyzer = absl::make_unique<DefaultAudioQualityAnalyzer>();
132 }
133 audio_quality_analyzer_.swap(audio_quality_analyzer);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100134}
135
Artem Titovba82e002019-03-15 15:57:53 +0100136void PeerConnectionE2EQualityTest::ExecuteAt(
137 TimeDelta target_time_since_start,
138 std::function<void(TimeDelta)> func) {
139 ExecuteTask(target_time_since_start, absl::nullopt, func);
140}
141
142void PeerConnectionE2EQualityTest::ExecuteEvery(
143 TimeDelta initial_delay_since_start,
144 TimeDelta interval,
145 std::function<void(TimeDelta)> func) {
146 ExecuteTask(initial_delay_since_start, interval, func);
147}
148
149void PeerConnectionE2EQualityTest::ExecuteTask(
150 TimeDelta initial_delay_since_start,
151 absl::optional<TimeDelta> interval,
152 std::function<void(TimeDelta)> func) {
153 RTC_CHECK(initial_delay_since_start.IsFinite() &&
154 initial_delay_since_start >= TimeDelta::Zero());
155 RTC_CHECK(!interval ||
156 (interval->IsFinite() && *interval > TimeDelta::Zero()));
157 rtc::CritScope crit(&lock_);
158 ScheduledActivity activity(initial_delay_since_start, interval, func);
159 if (start_time_.IsInfinite()) {
160 scheduled_activities_.push(std::move(activity));
161 } else {
162 PostTask(std::move(activity));
163 }
164}
165
166void PeerConnectionE2EQualityTest::PostTask(ScheduledActivity activity) {
167 // Because start_time_ will never change at this point copy it to local
168 // variable to capture in in lambda without requirement to hold a lock.
169 Timestamp start_time = start_time_;
170
171 TimeDelta remaining_delay =
172 activity.initial_delay_since_start == TimeDelta::Zero()
173 ? TimeDelta::Zero()
174 : activity.initial_delay_since_start - (Now() - start_time_);
175 if (remaining_delay < TimeDelta::Zero()) {
176 RTC_LOG(WARNING) << "Executing late task immediately, late by="
177 << ToString(remaining_delay.Abs());
178 remaining_delay = TimeDelta::Zero();
179 }
180
181 if (activity.interval) {
182 if (remaining_delay == TimeDelta::Zero()) {
183 repeating_task_handles_.push_back(RepeatingTaskHandle::Start(
184 task_queue_->Get(), [activity, start_time, this]() {
185 activity.func(Now() - start_time);
186 return *activity.interval;
187 }));
188 return;
189 }
190 repeating_task_handles_.push_back(RepeatingTaskHandle::DelayedStart(
191 task_queue_->Get(), remaining_delay, [activity, start_time, this]() {
192 activity.func(Now() - start_time);
193 return *activity.interval;
194 }));
195 return;
196 }
197
198 if (remaining_delay == TimeDelta::Zero()) {
199 task_queue_->PostTask(
200 [activity, start_time, this]() { activity.func(Now() - start_time); });
201 return;
202 }
203
204 task_queue_->PostDelayedTask(
205 [activity, start_time, this]() { activity.func(Now() - start_time); },
206 remaining_delay.ms());
207}
208
Artem Titov18459222019-04-24 11:09:35 +0200209void PeerConnectionE2EQualityTest::AddQualityMetricsReporter(
210 std::unique_ptr<QualityMetricsReporter> quality_metrics_reporter) {
211 quality_metrics_reporters_.push_back(std::move(quality_metrics_reporter));
212}
213
Artem Titovd09bc552019-03-20 11:18:58 +0100214void PeerConnectionE2EQualityTest::AddPeer(
215 rtc::Thread* network_thread,
216 rtc::NetworkManager* network_manager,
217 rtc::FunctionView<void(PeerConfigurer*)> configurer) {
218 peer_configurations_.push_back(
219 absl::make_unique<PeerConfigurerImpl>(network_thread, network_manager));
220 configurer(peer_configurations_.back().get());
221}
222
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100223void PeerConnectionE2EQualityTest::Run(
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100224 RunParams run_params) {
Artem Titovd09bc552019-03-20 11:18:58 +0100225 RTC_CHECK_EQ(peer_configurations_.size(), 2)
226 << "Only peer to peer calls are allowed, please add 2 peers";
227
228 std::unique_ptr<Params> alice_params =
229 peer_configurations_[0]->ReleaseParams();
230 std::unique_ptr<InjectableComponents> alice_components =
231 peer_configurations_[0]->ReleaseComponents();
232 std::unique_ptr<Params> bob_params = peer_configurations_[1]->ReleaseParams();
233 std::unique_ptr<InjectableComponents> bob_components =
234 peer_configurations_[1]->ReleaseComponents();
235 peer_configurations_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100236
Artem Titov9a7e7212019-02-28 16:34:17 +0100237 SetDefaultValuesForMissingParams({alice_params.get(), bob_params.get()});
Artem Titovade945d2019-04-02 18:31:48 +0200238 ValidateParams(run_params, {alice_params.get(), bob_params.get()});
Artem Titov23702422019-05-27 15:28:30 +0200239 SetupRequiredFieldTrials(run_params);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100240
241 // Print test summary
242 RTC_LOG(INFO)
243 << "Media quality test: Alice will make a call to Bob with media video="
244 << !alice_params->video_configs.empty()
245 << "; audio=" << alice_params->audio_config.has_value()
246 << ". Bob will respond with media video="
247 << !bob_params->video_configs.empty()
248 << "; audio=" << bob_params->audio_config.has_value();
249
250 const std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
251 signaling_thread->SetName(kSignalThreadName, nullptr);
252 signaling_thread->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100253
Artem Titov70f80e52019-04-12 13:13:39 +0200254 // Create a |task_queue_|.
255 task_queue_ = absl::make_unique<TaskQueueForTest>("pc_e2e_quality_test");
256
Artem Titova6a273d2019-02-07 16:43:51 +0100257 // Create call participants: Alice and Bob.
258 // Audio streams are intercepted in AudioDeviceModule, so if it is required to
259 // catch output of Alice's stream, Alice's output_dump_file_name should be
260 // passed to Bob's TestPeer setup as audio output file name.
261 absl::optional<std::string> alice_audio_output_dump_file_name =
262 bob_params->audio_config ? bob_params->audio_config->output_dump_file_name
263 : absl::nullopt;
264 absl::optional<std::string> bob_audio_output_dump_file_name =
265 alice_params->audio_config
266 ? alice_params->audio_config->output_dump_file_name
267 : absl::nullopt;
Artem Titovbf9e01a2019-02-14 10:51:27 +0100268 // Copy Alice and Bob video configs to correctly pass them into lambdas.
269 std::vector<VideoConfig> alice_video_configs = alice_params->video_configs;
270 std::vector<VideoConfig> bob_video_configs = bob_params->video_configs;
271
Artem Titova6a273d2019-02-07 16:43:51 +0100272 alice_ = TestPeer::CreateTestPeer(
273 std::move(alice_components), std::move(alice_params),
Artem Titovbf9e01a2019-02-14 10:51:27 +0100274 absl::make_unique<FixturePeerConnectionObserver>(
275 [this, bob_video_configs](
276 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200277 OnTrackCallback(transceiver, bob_video_configs);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100278 },
279 [this]() { StartVideo(alice_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100280 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titovade945d2019-04-02 18:31:48 +0200281 alice_audio_output_dump_file_name,
Artem Titov70f80e52019-04-12 13:13:39 +0200282 run_params.video_encoder_bitrate_multiplier, task_queue_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100283 bob_ = TestPeer::CreateTestPeer(
284 std::move(bob_components), std::move(bob_params),
Artem Titovbf9e01a2019-02-14 10:51:27 +0100285 absl::make_unique<FixturePeerConnectionObserver>(
286 [this, alice_video_configs](
287 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200288 OnTrackCallback(transceiver, alice_video_configs);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100289 },
290 [this]() { StartVideo(bob_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100291 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titovade945d2019-04-02 18:31:48 +0200292 bob_audio_output_dump_file_name,
Artem Titov70f80e52019-04-12 13:13:39 +0200293 run_params.video_encoder_bitrate_multiplier, task_queue_.get());
Artem Titov9f97c9a2019-02-08 00:35:13 +0100294
295 int num_cores = CpuInfo::DetectNumberOfCores();
296 RTC_DCHECK_GE(num_cores, 1);
297
298 int video_analyzer_threads =
299 num_cores - kPeerConnectionUsedThreads - kFrameworkUsedThreads;
300 if (video_analyzer_threads <= 0) {
301 video_analyzer_threads = 1;
302 }
303 video_analyzer_threads =
304 std::min(video_analyzer_threads, kMaxVideoAnalyzerThreads);
305 RTC_LOG(INFO) << "video_analyzer_threads=" << video_analyzer_threads;
306
Artem Titov59835852019-02-27 17:44:13 +0100307 video_quality_analyzer_injection_helper_->Start(test_case_name_,
308 video_analyzer_threads);
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200309 audio_quality_analyzer_->Start(test_case_name_, &analyzer_helper_);
Artem Titov18459222019-04-24 11:09:35 +0200310 for (auto& reporter : quality_metrics_reporters_) {
311 reporter->Start(test_case_name_);
312 }
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +0100313
314 // Start RTCEventLog recording if requested.
315 if (alice_->params()->rtc_event_log_path) {
316 auto alice_rtc_event_log = absl::make_unique<webrtc::RtcEventLogOutputFile>(
317 alice_->params()->rtc_event_log_path.value());
318 alice_->pc()->StartRtcEventLog(std::move(alice_rtc_event_log),
319 webrtc::RtcEventLog::kImmediateOutput);
320 }
321 if (bob_->params()->rtc_event_log_path) {
322 auto bob_rtc_event_log = absl::make_unique<webrtc::RtcEventLogOutputFile>(
323 bob_->params()->rtc_event_log_path.value());
324 bob_->pc()->StartRtcEventLog(std::move(bob_rtc_event_log),
325 webrtc::RtcEventLog::kImmediateOutput);
326 }
327
Artem Titov4d29ef02019-05-20 10:43:16 +0200328 // Setup alive logging. It is done to prevent test infra to think that test is
329 // dead.
330 RepeatingTaskHandle::DelayedStart(task_queue_->Get(),
331 kAliveMessageLogInterval, []() {
332 std::printf("Test is still running...\n");
333 return kAliveMessageLogInterval;
334 });
335
Artem Titovba82e002019-03-15 15:57:53 +0100336 // Setup call.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100337 signaling_thread->Invoke<void>(
Artem Titova6a273d2019-02-07 16:43:51 +0100338 RTC_FROM_HERE,
Artem Titovf65a89b2019-05-07 11:56:44 +0200339 rtc::Bind(&PeerConnectionE2EQualityTest::SetupCallOnSignalingThread, this,
340 run_params));
Artem Titovba82e002019-03-15 15:57:53 +0100341 {
342 rtc::CritScope crit(&lock_);
343 start_time_ = Now();
344 while (!scheduled_activities_.empty()) {
345 PostTask(std::move(scheduled_activities_.front()));
346 scheduled_activities_.pop();
347 }
348 }
Mirko Bonadei71318802019-02-18 18:52:43 +0100349
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100350 StatsPoller stats_poller({audio_quality_analyzer_.get(),
351 video_quality_analyzer_injection_helper_.get()},
352 {{"alice", alice_.get()}, {"bob", bob_.get()}});
353
Artem Titovba82e002019-03-15 15:57:53 +0100354 task_queue_->PostTask([&stats_poller, this]() {
355 RTC_DCHECK_RUN_ON(task_queue_.get());
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100356 stats_polling_task_ =
Artem Titovba82e002019-03-15 15:57:53 +0100357 RepeatingTaskHandle::Start(task_queue_->Get(), [this, &stats_poller]() {
358 RTC_DCHECK_RUN_ON(task_queue_.get());
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100359 stats_poller.PollStatsAndNotifyObservers();
360 return kStatsUpdateInterval;
361 });
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100362 });
363
Mirko Bonadei71318802019-02-18 18:52:43 +0100364 rtc::Event done;
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100365 done.Wait(run_params.run_duration.ms());
366
367 rtc::Event stats_polling_stopped;
Artem Titovba82e002019-03-15 15:57:53 +0100368 task_queue_->PostTask([&stats_polling_stopped, this]() {
369 RTC_DCHECK_RUN_ON(task_queue_.get());
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100370 stats_polling_task_.Stop();
371 stats_polling_stopped.Set();
372 });
373 bool no_timeout = stats_polling_stopped.Wait(kStatsPollingStopTimeout.ms());
374 RTC_CHECK(no_timeout) << "Failed to stop Stats polling after "
375 << kStatsPollingStopTimeout.seconds() << " seconds.";
Mirko Bonadei71318802019-02-18 18:52:43 +0100376
Artem Titov70f80e52019-04-12 13:13:39 +0200377 // We need to detach AEC dumping from peers, because dump uses |task_queue_|
378 // inside.
379 alice_->DetachAecDump();
380 bob_->DetachAecDump();
Artem Titov4d29ef02019-05-20 10:43:16 +0200381 // Stop all client started tasks on task queue to prevent their access to any
382 // call related objects after these objects will be destroyed during call tear
383 // down.
384 task_queue_->SendTask([this]() {
385 rtc::CritScope crit(&lock_);
386 for (auto& handle : repeating_task_handles_) {
387 handle.Stop();
388 }
389 });
Artem Titovba82e002019-03-15 15:57:53 +0100390 // Tear down the call.
Mirko Bonadei71318802019-02-18 18:52:43 +0100391 signaling_thread->Invoke<void>(
392 RTC_FROM_HERE,
393 rtc::Bind(&PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread,
394 this));
Artem Titovb93c4e62019-05-02 10:52:07 +0200395 Timestamp end_time = Now();
396 {
397 rtc::CritScope crit(&lock_);
398 real_test_duration_ = end_time - start_time_;
399 }
Mirko Bonadei71318802019-02-18 18:52:43 +0100400
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200401 audio_quality_analyzer_->Stop();
Artem Titov9f97c9a2019-02-08 00:35:13 +0100402 video_quality_analyzer_injection_helper_->Stop();
Artem Titov18459222019-04-24 11:09:35 +0200403 for (auto& reporter : quality_metrics_reporters_) {
404 reporter->StopAndReportResults();
405 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100406
Artem Titov4d29ef02019-05-20 10:43:16 +0200407 // Reset |task_queue_| after test to cleanup.
408 task_queue_.reset();
409
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100410 // Ensuring that TestPeers have been destroyed in order to correctly close
411 // Audio dumps.
412 RTC_CHECK(!alice_);
413 RTC_CHECK(!bob_);
414 // Ensuring that FrameGeneratorCapturerVideoTrackSource and VideoFrameWriter
415 // are destroyed on the right thread.
Artem Titovbf9e01a2019-02-14 10:51:27 +0100416 RTC_CHECK(alice_video_sources_.empty());
417 RTC_CHECK(bob_video_sources_.empty());
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100418 RTC_CHECK(video_writers_.empty());
Artem Titova6a273d2019-02-07 16:43:51 +0100419}
420
Artem Titov9a7e7212019-02-28 16:34:17 +0100421void PeerConnectionE2EQualityTest::SetDefaultValuesForMissingParams(
Artem Titova6a273d2019-02-07 16:43:51 +0100422 std::vector<Params*> params) {
Artem Titov3481db22019-02-28 13:13:15 +0100423 int video_counter = 0;
424 int audio_counter = 0;
Artem Titova6a273d2019-02-07 16:43:51 +0100425 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100426 std::set<std::string> audio_labels;
Artem Titova6a273d2019-02-07 16:43:51 +0100427 for (auto* p : params) {
428 for (auto& video_config : p->video_configs) {
Artem Titov9a7e7212019-02-28 16:34:17 +0100429 if (!video_config.generator && !video_config.input_file_name &&
430 !video_config.screen_share_config) {
431 video_config.generator = VideoGeneratorType::kDefault;
432 }
Artem Titova6a273d2019-02-07 16:43:51 +0100433 if (!video_config.stream_label) {
434 std::string label;
435 do {
Artem Titov3481db22019-02-28 13:13:15 +0100436 label = "_auto_video_stream_label_" + std::to_string(video_counter);
437 ++video_counter;
Artem Titova6a273d2019-02-07 16:43:51 +0100438 } while (!video_labels.insert(label).second);
439 video_config.stream_label = label;
440 }
441 }
Artem Titov3481db22019-02-28 13:13:15 +0100442 if (p->audio_config) {
443 if (!p->audio_config->stream_label) {
444 std::string label;
445 do {
446 label = "_auto_audio_stream_label_" + std::to_string(audio_counter);
447 ++audio_counter;
448 } while (!audio_labels.insert(label).second);
449 p->audio_config->stream_label = label;
450 }
451 }
Artem Titova6a273d2019-02-07 16:43:51 +0100452 }
453}
454
Artem Titovade945d2019-04-02 18:31:48 +0200455void PeerConnectionE2EQualityTest::ValidateParams(const RunParams& run_params,
456 std::vector<Params*> params) {
457 RTC_CHECK_GT(run_params.video_encoder_bitrate_multiplier, 0.0);
458
Artem Titova6a273d2019-02-07 16:43:51 +0100459 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100460 std::set<std::string> audio_labels;
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100461 int media_streams_count = 0;
462
Artem Titovef3fd9c2019-06-13 16:36:52 +0200463 for (size_t i = 0; i < params.size(); ++i) {
464 Params* p = params[i];
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100465 if (p->audio_config) {
466 media_streams_count++;
467 }
468 media_streams_count += p->video_configs.size();
469
Artem Titova6a273d2019-02-07 16:43:51 +0100470 // Validate that each video config has exactly one of |generator|,
471 // |input_file_name| or |screen_share_config| set. Also validate that all
472 // video stream labels are unique.
473 for (auto& video_config : p->video_configs) {
474 RTC_CHECK(video_config.stream_label);
475 bool inserted =
476 video_labels.insert(video_config.stream_label.value()).second;
477 RTC_CHECK(inserted) << "Duplicate video_config.stream_label="
478 << video_config.stream_label.value();
479 RTC_CHECK(video_config.generator || video_config.input_file_name ||
480 video_config.screen_share_config)
481 << VideoConfigSourcePresenceToString(video_config);
482 RTC_CHECK(!(video_config.input_file_name && video_config.generator))
483 << VideoConfigSourcePresenceToString(video_config);
484 RTC_CHECK(
485 !(video_config.input_file_name && video_config.screen_share_config))
486 << VideoConfigSourcePresenceToString(video_config);
487 RTC_CHECK(!(video_config.screen_share_config && video_config.generator))
488 << VideoConfigSourcePresenceToString(video_config);
Artem Titov7581ff72019-05-15 15:45:33 +0200489
490 if (video_config.screen_share_config) {
491 if (video_config.screen_share_config->slides_yuv_file_names.empty()) {
492 if (video_config.screen_share_config->scrolling_params) {
493 // If we have scrolling params, then its |source_width| and
494 // |source_heigh| will be used as width and height of video input,
495 // so we have to validate it against width and height of default
496 // input.
497 RTC_CHECK_EQ(video_config.screen_share_config->scrolling_params
498 ->source_width,
499 kDefaultSlidesWidth);
500 RTC_CHECK_EQ(video_config.screen_share_config->scrolling_params
501 ->source_height,
502 kDefaultSlidesHeight);
503 } else {
504 RTC_CHECK_EQ(video_config.width, kDefaultSlidesWidth);
505 RTC_CHECK_EQ(video_config.height, kDefaultSlidesHeight);
506 }
507 }
508 if (video_config.screen_share_config->scrolling_params) {
509 RTC_CHECK_LE(
510 video_config.screen_share_config->scrolling_params->duration,
511 video_config.screen_share_config->slide_change_interval);
512 RTC_CHECK_GE(
513 video_config.screen_share_config->scrolling_params->source_width,
514 video_config.width);
515 RTC_CHECK_GE(
516 video_config.screen_share_config->scrolling_params->source_height,
517 video_config.height);
518 }
519 }
Artem Titovef3fd9c2019-06-13 16:36:52 +0200520 if (video_config.simulcast_config) {
521 // We support simulcast only for Vp8 for now.
522 // RTC_CHECK_EQ(run_params.video_codec_name, cricket::kVp8CodecName);
523 // Also we support simulcast only from caller.
524 RTC_CHECK_EQ(i, 0)
525 << "Only simulcast stream from first peer is supported";
526 }
Artem Titova6a273d2019-02-07 16:43:51 +0100527 }
528 if (p->audio_config) {
Artem Titov3481db22019-02-28 13:13:15 +0100529 bool inserted =
530 audio_labels.insert(p->audio_config->stream_label.value()).second;
531 RTC_CHECK(inserted) << "Duplicate audio_config.stream_label="
532 << p->audio_config->stream_label.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100533 // Check that if mode input file name specified only if mode is kFile.
534 if (p->audio_config.value().mode == AudioConfig::Mode::kGenerated) {
535 RTC_CHECK(!p->audio_config.value().input_file_name);
536 }
537 if (p->audio_config.value().mode == AudioConfig::Mode::kFile) {
538 RTC_CHECK(p->audio_config.value().input_file_name);
Artem Titov0b443142019-03-20 11:11:08 +0100539 RTC_CHECK(
Artem Titov70f80e52019-04-12 13:13:39 +0200540 test::FileExists(p->audio_config.value().input_file_name.value()))
541 << p->audio_config.value().input_file_name.value()
542 << " doesn't exist";
Artem Titova6a273d2019-02-07 16:43:51 +0100543 }
544 }
545 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100546
547 RTC_CHECK_GT(media_streams_count, 0) << "No media in the call.";
Artem Titova6a273d2019-02-07 16:43:51 +0100548}
549
Artem Titov23702422019-05-27 15:28:30 +0200550void PeerConnectionE2EQualityTest::SetupRequiredFieldTrials(
551 const RunParams& run_params) {
552 std::string field_trials = "";
553 if (run_params.use_flex_fec) {
554 field_trials += kFlexFecEnabledFieldTrials;
555 }
556 if (!field_trials.empty()) {
557 override_field_trials_ = absl::make_unique<test::ScopedFieldTrials>(
558 field_trial::GetFieldTrialString() + field_trials);
559 }
560}
561
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200562void PeerConnectionE2EQualityTest::OnTrackCallback(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100563 rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
564 std::vector<VideoConfig> remote_video_configs) {
565 const rtc::scoped_refptr<MediaStreamTrackInterface>& track =
566 transceiver->receiver()->track();
Mirko Bonadeif948eb62019-04-05 15:13:23 +0200567 RTC_CHECK_EQ(transceiver->receiver()->stream_ids().size(), 1);
568 std::string stream_label = transceiver->receiver()->stream_ids().front();
569 analyzer_helper_.AddTrackToStreamMapping(track->id(), stream_label);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100570 if (track->kind() != MediaStreamTrackInterface::kVideoKind) {
571 return;
572 }
573
574 VideoConfig* video_config = nullptr;
575 for (auto& config : remote_video_configs) {
Artem Titov7c554152019-02-28 10:25:52 +0100576 if (config.stream_label == stream_label) {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100577 video_config = &config;
578 break;
579 }
580 }
581 RTC_CHECK(video_config);
Artem Titov0b443142019-03-20 11:11:08 +0100582 test::VideoFrameWriter* writer = MaybeCreateVideoWriter(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100583 video_config->output_dump_file_name, *video_config);
584 // It is safe to cast here, because it is checked above that
585 // track->kind() is kVideoKind.
586 auto* video_track = static_cast<VideoTrackInterface*>(track.get());
587 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> video_sink =
588 video_quality_analyzer_injection_helper_->CreateVideoSink(writer);
589 video_track->AddOrUpdateSink(video_sink.get(), rtc::VideoSinkWants());
590 output_video_sinks_.push_back(std::move(video_sink));
591}
592
Artem Titovf65a89b2019-05-07 11:56:44 +0200593void PeerConnectionE2EQualityTest::SetupCallOnSignalingThread(
594 const RunParams& run_params) {
Artem Titov7c554152019-02-28 10:25:52 +0100595 // We need receive-only transceivers for Bob's media stream, so there will
596 // be media section in SDP for that streams in Alice's offer, because it is
597 // forbidden to add new media sections in answer in Unified Plan.
598 RtpTransceiverInit receive_only_transceiver_init;
599 receive_only_transceiver_init.direction = RtpTransceiverDirection::kRecvOnly;
Artem Titovef3fd9c2019-06-13 16:36:52 +0200600 int alice_transceivers_counter = 0;
Artem Titov7c554152019-02-28 10:25:52 +0100601 if (bob_->params()->audio_config) {
602 // Setup receive audio transceiver if Bob has audio to send. If we'll need
603 // multiple audio streams, then we need transceiver for each Bob's audio
604 // stream.
605 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_AUDIO,
606 receive_only_transceiver_init);
Artem Titovef3fd9c2019-06-13 16:36:52 +0200607 alice_transceivers_counter++;
608 }
609
610 for (auto& video_config : alice_->params()->video_configs) {
611 if (video_config.simulcast_config) {
612 RtpTransceiverInit transceiver_params;
613 transceiver_params.direction = RtpTransceiverDirection::kSendOnly;
614 for (int i = 0;
615 i < video_config.simulcast_config->simulcast_streams_count; ++i) {
616 RtpEncodingParameters enc_params;
617 // We need to be sure, that all rids will be unique with all mids.
618 enc_params.rid = std::to_string(alice_transceivers_counter) + "000" +
619 std::to_string(i);
620 transceiver_params.send_encodings.push_back(enc_params);
621 }
622 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
623 transceiver_params);
624 alice_transceivers_counter++;
625 }
Artem Titov7c554152019-02-28 10:25:52 +0100626 }
627 for (size_t i = 0; i < bob_->params()->video_configs.size(); ++i) {
628 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
629 receive_only_transceiver_init);
Artem Titovef3fd9c2019-06-13 16:36:52 +0200630 alice_transceivers_counter++;
Artem Titov7c554152019-02-28 10:25:52 +0100631 }
632 // Then add media for Alice and Bob
633 alice_video_sources_ = MaybeAddMedia(alice_.get());
634 bob_video_sources_ = MaybeAddMedia(bob_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100635
Artem Titovf65a89b2019-05-07 11:56:44 +0200636 SetPeerCodecPreferences(alice_.get(), run_params);
637 SetPeerCodecPreferences(bob_.get(), run_params);
638
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100639 SetupCall();
Mirko Bonadei71318802019-02-18 18:52:43 +0100640}
Artem Titova6a273d2019-02-07 16:43:51 +0100641
Mirko Bonadei71318802019-02-18 18:52:43 +0100642void PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread() {
Artem Titova6a273d2019-02-07 16:43:51 +0100643 TearDownCall();
Artem Titova6a273d2019-02-07 16:43:51 +0100644}
645
Artem Titovbf9e01a2019-02-14 10:51:27 +0100646std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100647PeerConnectionE2EQualityTest::MaybeAddMedia(TestPeer* peer) {
648 MaybeAddAudio(peer);
649 return MaybeAddVideo(peer);
Artem Titova6a273d2019-02-07 16:43:51 +0100650}
651
Artem Titovbf9e01a2019-02-14 10:51:27 +0100652std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100653PeerConnectionE2EQualityTest::MaybeAddVideo(TestPeer* peer) {
Artem Titova6a273d2019-02-07 16:43:51 +0100654 // Params here valid because of pre-run validation.
655 Params* params = peer->params();
Artem Titovbf9e01a2019-02-14 10:51:27 +0100656 std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>> out;
Artem Titova6a273d2019-02-07 16:43:51 +0100657 for (auto video_config : params->video_configs) {
658 // Create video generator.
Artem Titov0b443142019-03-20 11:11:08 +0100659 std::unique_ptr<test::FrameGenerator> frame_generator =
Artem Titova6a273d2019-02-07 16:43:51 +0100660 CreateFrameGenerator(video_config);
661
662 // Wrap it to inject video quality analyzer and enable dump of input video
663 // if required.
Artem Titov0b443142019-03-20 11:11:08 +0100664 test::VideoFrameWriter* writer =
Artem Titova6a273d2019-02-07 16:43:51 +0100665 MaybeCreateVideoWriter(video_config.input_dump_file_name, video_config);
666 frame_generator =
667 video_quality_analyzer_injection_helper_->WrapFrameGenerator(
668 video_config.stream_label.value(), std::move(frame_generator),
669 writer);
670
671 // Setup FrameGenerator into peer connection.
Danil Chapovalovd0e0ed82019-04-18 14:48:24 +0200672 auto capturer = absl::make_unique<test::FrameGeneratorCapturer>(
673 clock_, std::move(frame_generator), video_config.fps,
674 *task_queue_factory_);
675 capturer->Init();
Artem Titova6a273d2019-02-07 16:43:51 +0100676 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource> source =
677 new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
Artem Titov232b6a12019-05-29 11:05:01 +0200678 std::move(capturer),
679 /*is_screencast=*/video_config.screen_share_config.has_value());
Artem Titovbf9e01a2019-02-14 10:51:27 +0100680 out.push_back(source);
Artem Titova6a273d2019-02-07 16:43:51 +0100681 RTC_LOG(INFO) << "Adding video with video_config.stream_label="
682 << video_config.stream_label.value();
683 rtc::scoped_refptr<VideoTrackInterface> track =
684 peer->pc_factory()->CreateVideoTrack(video_config.stream_label.value(),
685 source);
Artem Titov232b6a12019-05-29 11:05:01 +0200686 if (video_config.screen_share_config) {
687 track->set_content_hint(VideoTrackInterface::ContentHint::kText);
688 }
Artem Titov7c554152019-02-28 10:25:52 +0100689 peer->AddTrack(track, {video_config.stream_label.value()});
Artem Titova6a273d2019-02-07 16:43:51 +0100690 }
Artem Titovbf9e01a2019-02-14 10:51:27 +0100691 return out;
Artem Titova6a273d2019-02-07 16:43:51 +0100692}
693
Artem Titov0b443142019-03-20 11:11:08 +0100694std::unique_ptr<test::FrameGenerator>
Artem Titova6a273d2019-02-07 16:43:51 +0100695PeerConnectionE2EQualityTest::CreateFrameGenerator(
696 const VideoConfig& video_config) {
697 if (video_config.generator) {
Artem Titov0b443142019-03-20 11:11:08 +0100698 absl::optional<test::FrameGenerator::OutputType> frame_generator_type =
Artem Titova6a273d2019-02-07 16:43:51 +0100699 absl::nullopt;
700 if (video_config.generator == VideoGeneratorType::kDefault) {
Artem Titov0b443142019-03-20 11:11:08 +0100701 frame_generator_type = test::FrameGenerator::OutputType::I420;
Artem Titova6a273d2019-02-07 16:43:51 +0100702 } else if (video_config.generator == VideoGeneratorType::kI420A) {
Artem Titov0b443142019-03-20 11:11:08 +0100703 frame_generator_type = test::FrameGenerator::OutputType::I420A;
Artem Titova6a273d2019-02-07 16:43:51 +0100704 } else if (video_config.generator == VideoGeneratorType::kI010) {
Artem Titov0b443142019-03-20 11:11:08 +0100705 frame_generator_type = test::FrameGenerator::OutputType::I010;
Artem Titova6a273d2019-02-07 16:43:51 +0100706 }
Artem Titov0b443142019-03-20 11:11:08 +0100707 return test::FrameGenerator::CreateSquareGenerator(
Artem Titova6a273d2019-02-07 16:43:51 +0100708 static_cast<int>(video_config.width),
709 static_cast<int>(video_config.height), frame_generator_type,
710 absl::nullopt);
711 }
712 if (video_config.input_file_name) {
Artem Titov0b443142019-03-20 11:11:08 +0100713 return test::FrameGenerator::CreateFromYuvFile(
Artem Titova6a273d2019-02-07 16:43:51 +0100714 std::vector<std::string>(/*count=*/1,
715 video_config.input_file_name.value()),
716 video_config.width, video_config.height, /*frame_repeat_count=*/1);
717 }
718 if (video_config.screen_share_config) {
Artem Titov7581ff72019-05-15 15:45:33 +0200719 return CreateScreenShareFrameGenerator(video_config);
Artem Titova6a273d2019-02-07 16:43:51 +0100720 }
721 RTC_NOTREACHED() << "Unsupported video_config input source";
722 return nullptr;
723}
724
Artem Titov7581ff72019-05-15 15:45:33 +0200725std::unique_ptr<test::FrameGenerator>
726PeerConnectionE2EQualityTest::CreateScreenShareFrameGenerator(
727 const VideoConfig& video_config) {
728 RTC_CHECK(video_config.screen_share_config);
729 if (video_config.screen_share_config->generate_slides) {
730 return test::FrameGenerator::CreateSlideGenerator(
731 video_config.width, video_config.height,
732 video_config.screen_share_config->slide_change_interval.seconds() *
733 video_config.fps);
734 }
735 std::vector<std::string> slides =
736 video_config.screen_share_config->slides_yuv_file_names;
737 if (slides.empty()) {
738 // If slides is empty we need to add default slides as source. In such case
739 // video width and height is validated to be equal to kDefaultSlidesWidth
740 // and kDefaultSlidesHeight.
741 slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv"));
742 slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv"));
743 slides.push_back(test::ResourcePath("photo_1850_1110", "yuv"));
744 slides.push_back(test::ResourcePath("difficult_photo_1850_1110", "yuv"));
745 }
746 if (!video_config.screen_share_config->scrolling_params) {
747 // Cycle image every slide_change_interval seconds.
748 return test::FrameGenerator::CreateFromYuvFile(
749 slides, video_config.width, video_config.height,
750 video_config.screen_share_config->slide_change_interval.seconds() *
751 video_config.fps);
752 }
753
754 // |pause_duration| is nonnegative. It is validated in ValidateParams(...).
755 TimeDelta pause_duration =
756 video_config.screen_share_config->slide_change_interval -
757 video_config.screen_share_config->scrolling_params->duration;
758
759 return test::FrameGenerator::CreateScrollingInputFromYuvFiles(
760 clock_, slides,
761 video_config.screen_share_config->scrolling_params->source_width,
762 video_config.screen_share_config->scrolling_params->source_height,
763 video_config.width, video_config.height,
764 video_config.screen_share_config->scrolling_params->duration.ms(),
765 pause_duration.ms());
766}
767
Artem Titov7c554152019-02-28 10:25:52 +0100768void PeerConnectionE2EQualityTest::MaybeAddAudio(TestPeer* peer) {
769 if (!peer->params()->audio_config) {
770 return;
771 }
Artem Titov3481db22019-02-28 13:13:15 +0100772 const AudioConfig& audio_config = peer->params()->audio_config.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100773 rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
Artem Titov3481db22019-02-28 13:13:15 +0100774 peer->pc_factory()->CreateAudioSource(audio_config.audio_options);
Artem Titova6a273d2019-02-07 16:43:51 +0100775 rtc::scoped_refptr<AudioTrackInterface> track =
Artem Titov3481db22019-02-28 13:13:15 +0100776 peer->pc_factory()->CreateAudioTrack(*audio_config.stream_label, source);
777 peer->AddTrack(track, {*audio_config.stream_label});
Artem Titova6a273d2019-02-07 16:43:51 +0100778}
779
Artem Titovf65a89b2019-05-07 11:56:44 +0200780void PeerConnectionE2EQualityTest::SetPeerCodecPreferences(
781 TestPeer* peer,
782 const RunParams& run_params) {
Artem Titovef3fd9c2019-06-13 16:36:52 +0200783 std::vector<RtpCodecCapability> with_rtx_video_capabilities =
784 FilterCodecCapabilities(
785 run_params.video_codec_name, run_params.video_codec_required_params,
786 true, run_params.use_ulp_fec, run_params.use_flex_fec,
787 peer->pc_factory()
788 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
789 .codecs);
790 std::vector<RtpCodecCapability> without_rtx_video_capabilities =
791 FilterCodecCapabilities(
792 run_params.video_codec_name, run_params.video_codec_required_params,
793 false, run_params.use_ulp_fec, run_params.use_flex_fec,
794 peer->pc_factory()
795 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
796 .codecs);
Artem Titovf65a89b2019-05-07 11:56:44 +0200797
798 // Set codecs for transceivers
799 for (auto transceiver : peer->pc()->GetTransceivers()) {
800 if (transceiver->media_type() == cricket::MediaType::MEDIA_TYPE_VIDEO) {
Artem Titovef3fd9c2019-06-13 16:36:52 +0200801 if (transceiver->sender()->init_send_encodings().size() > 1) {
802 // If transceiver's sender has more then 1 send encodings, it means it
803 // has multiple simulcast streams, so we need disable RTX on it.
804 transceiver->SetCodecPreferences(without_rtx_video_capabilities);
805 } else {
806 transceiver->SetCodecPreferences(with_rtx_video_capabilities);
807 }
Artem Titovf65a89b2019-05-07 11:56:44 +0200808 }
809 }
810}
811
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100812void PeerConnectionE2EQualityTest::SetupCall() {
Artem Titovef3fd9c2019-06-13 16:36:52 +0200813 SignalingInterceptor signaling_interceptor;
Artem Titova6a273d2019-02-07 16:43:51 +0100814 // Connect peers.
Artem Titovef3fd9c2019-06-13 16:36:52 +0200815 ExchangeOfferAnswer(&signaling_interceptor);
Artem Titova6a273d2019-02-07 16:43:51 +0100816 // Do the SDP negotiation, and also exchange ice candidates.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100817 ASSERT_EQ_WAIT(alice_->signaling_state(), PeerConnectionInterface::kStable,
Artem Titova6a273d2019-02-07 16:43:51 +0100818 kDefaultTimeoutMs);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100819 ASSERT_TRUE_WAIT(alice_->IsIceGatheringDone(), kDefaultTimeoutMs);
820 ASSERT_TRUE_WAIT(bob_->IsIceGatheringDone(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100821
Artem Titovef3fd9c2019-06-13 16:36:52 +0200822 ExchangeIceCandidates(&signaling_interceptor);
Artem Titova6a273d2019-02-07 16:43:51 +0100823 // This means that ICE and DTLS are connected.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100824 ASSERT_TRUE_WAIT(bob_->IsIceConnected(), kDefaultTimeoutMs);
825 ASSERT_TRUE_WAIT(alice_->IsIceConnected(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100826}
827
Artem Titovef3fd9c2019-06-13 16:36:52 +0200828void PeerConnectionE2EQualityTest::ExchangeOfferAnswer(
829 SignalingInterceptor* signaling_interceptor) {
830 std::string log_output;
831
832 auto offer = alice_->CreateOffer();
833 RTC_CHECK(offer);
834 offer->ToString(&log_output);
835 RTC_LOG(INFO) << "Original offer: " << log_output;
836 LocalAndRemoteSdp patch_result =
837 signaling_interceptor->PatchOffer(std::move(offer));
838 patch_result.local_sdp->ToString(&log_output);
839 RTC_LOG(INFO) << "Offer to set as local description: " << log_output;
840 patch_result.remote_sdp->ToString(&log_output);
841 RTC_LOG(INFO) << "Offer to set as remote description: " << log_output;
842
843 bool set_local_offer =
844 alice_->SetLocalDescription(std::move(patch_result.local_sdp));
845 RTC_CHECK(set_local_offer);
846 bool set_remote_offer =
847 bob_->SetRemoteDescription(std::move(patch_result.remote_sdp));
848 RTC_CHECK(set_remote_offer);
849 auto answer = bob_->CreateAnswer();
850 RTC_CHECK(answer);
851 answer->ToString(&log_output);
852 RTC_LOG(INFO) << "Original answer: " << log_output;
853 patch_result = signaling_interceptor->PatchAnswer(std::move(answer));
854 patch_result.local_sdp->ToString(&log_output);
855 RTC_LOG(INFO) << "Answer to set as local description: " << log_output;
856 patch_result.remote_sdp->ToString(&log_output);
857 RTC_LOG(INFO) << "Answer to set as remote description: " << log_output;
858
859 bool set_local_answer =
860 bob_->SetLocalDescription(std::move(patch_result.local_sdp));
861 RTC_CHECK(set_local_answer);
862 bool set_remote_answer =
863 alice_->SetRemoteDescription(std::move(patch_result.remote_sdp));
864 RTC_CHECK(set_remote_answer);
865}
866
867void PeerConnectionE2EQualityTest::ExchangeIceCandidates(
868 SignalingInterceptor* signaling_interceptor) {
869 // Connect an ICE candidate pairs.
870 std::vector<std::unique_ptr<IceCandidateInterface>> alice_candidates =
871 signaling_interceptor->PatchOffererIceCandidates(
872 alice_->observer()->GetAllCandidates());
873 for (auto& candidate : alice_candidates) {
874 std::string candidate_str;
875 RTC_CHECK(candidate->ToString(&candidate_str));
876 RTC_LOG(INFO) << "Alice ICE candidate(mid= " << candidate->sdp_mid()
877 << "): " << candidate_str;
878 }
879 ASSERT_TRUE(bob_->AddIceCandidates(std::move(alice_candidates)));
880 std::vector<std::unique_ptr<IceCandidateInterface>> bob_candidates =
881 signaling_interceptor->PatchAnswererIceCandidates(
882 bob_->observer()->GetAllCandidates());
883 for (auto& candidate : bob_candidates) {
884 std::string candidate_str;
885 RTC_CHECK(candidate->ToString(&candidate_str));
886 RTC_LOG(INFO) << "Bob ICE candidate(mid= " << candidate->sdp_mid()
887 << "): " << candidate_str;
888 }
889 ASSERT_TRUE(alice_->AddIceCandidates(std::move(bob_candidates)));
890}
891
Artem Titovbf9e01a2019-02-14 10:51:27 +0100892void PeerConnectionE2EQualityTest::StartVideo(
893 const std::vector<
894 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>& sources) {
895 for (auto& source : sources) {
896 if (source->state() != MediaSourceInterface::SourceState::kLive) {
897 source->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100898 }
Artem Titova6a273d2019-02-07 16:43:51 +0100899 }
900}
901
902void PeerConnectionE2EQualityTest::TearDownCall() {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100903 for (const auto& video_source : alice_video_sources_) {
904 video_source->Stop();
905 }
906 for (const auto& video_source : bob_video_sources_) {
Artem Titova6a273d2019-02-07 16:43:51 +0100907 video_source->Stop();
908 }
909
910 alice_->pc()->Close();
911 bob_->pc()->Close();
912
913 for (const auto& video_writer : video_writers_) {
914 video_writer->Close();
915 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100916
Artem Titovbf9e01a2019-02-14 10:51:27 +0100917 alice_video_sources_.clear();
918 bob_video_sources_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100919 video_writers_.clear();
920 alice_.reset();
921 bob_.reset();
Artem Titova6a273d2019-02-07 16:43:51 +0100922}
923
Artem Titov0b443142019-03-20 11:11:08 +0100924test::VideoFrameWriter* PeerConnectionE2EQualityTest::MaybeCreateVideoWriter(
Artem Titova6a273d2019-02-07 16:43:51 +0100925 absl::optional<std::string> file_name,
926 const VideoConfig& config) {
927 if (!file_name) {
928 return nullptr;
929 }
Artem Titovef3fd9c2019-06-13 16:36:52 +0200930 // TODO(titovartem) create only one file writer for simulcast video track.
Artem Titov0b443142019-03-20 11:11:08 +0100931 auto video_writer = absl::make_unique<test::VideoFrameWriter>(
Artem Titova6a273d2019-02-07 16:43:51 +0100932 file_name.value(), config.width, config.height, config.fps);
Artem Titov0b443142019-03-20 11:11:08 +0100933 test::VideoFrameWriter* out = video_writer.get();
Artem Titova6a273d2019-02-07 16:43:51 +0100934 video_writers_.push_back(std::move(video_writer));
935 return out;
936}
937
Artem Titovba82e002019-03-15 15:57:53 +0100938Timestamp PeerConnectionE2EQualityTest::Now() const {
939 return Timestamp::us(clock_->TimeInMicroseconds());
940}
941
942PeerConnectionE2EQualityTest::ScheduledActivity::ScheduledActivity(
943 TimeDelta initial_delay_since_start,
944 absl::optional<TimeDelta> interval,
945 std::function<void(TimeDelta)> func)
946 : initial_delay_since_start(initial_delay_since_start),
947 interval(std::move(interval)),
948 func(std::move(func)) {}
949
Artem Titov0b443142019-03-20 11:11:08 +0100950} // namespace webrtc_pc_e2e
Artem Titova6a273d2019-02-07 16:43:51 +0100951} // namespace webrtc