blob: 91df37671f3c1701e0bea1893e6097179a5f0696 [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 Titovbf9e01a2019-02-14 10:51:27 +010017#include "api/media_stream_interface.h"
Artem Titova6a273d2019-02-07 16:43:51 +010018#include "api/peer_connection_interface.h"
19#include "api/scoped_refptr.h"
20#include "api/units/time_delta.h"
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +010021#include "logging/rtc_event_log/output/rtc_event_log_output_file.h"
22#include "logging/rtc_event_log/rtc_event_log.h"
Artem Titovbf9e01a2019-02-14 10:51:27 +010023#include "pc/test/mock_peer_connection_observers.h"
Artem Titova6a273d2019-02-07 16:43:51 +010024#include "rtc_base/bind.h"
25#include "rtc_base/gunit.h"
Mirko Bonadei71318802019-02-18 18:52:43 +010026#include "rtc_base/numerics/safe_conversions.h"
Artem Titov9f97c9a2019-02-08 00:35:13 +010027#include "system_wrappers/include/cpu_info.h"
Mirko Bonadei3830d9b2019-02-28 14:07:56 +010028#include "test/pc/e2e/analyzer/audio/default_audio_quality_analyzer.h"
Artem Titov859abef2019-03-01 11:11:09 +010029#include "test/pc/e2e/analyzer/video/default_video_quality_analyzer.h"
Artem Titova6a273d2019-02-07 16:43:51 +010030#include "test/pc/e2e/api/video_quality_analyzer_interface.h"
Mirko Bonadei12ae4f42019-02-26 15:19:07 +010031#include "test/pc/e2e/stats_poller.h"
Artem Titova6a273d2019-02-07 16:43:51 +010032#include "test/testsupport/file_utils.h"
33
34namespace webrtc {
Artem Titov0b443142019-03-20 11:11:08 +010035namespace webrtc_pc_e2e {
Artem Titova6a273d2019-02-07 16:43:51 +010036namespace {
37
38using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
39
40constexpr int kDefaultTimeoutMs = 10000;
41constexpr char kSignalThreadName[] = "signaling_thread";
Artem Titov9f97c9a2019-02-08 00:35:13 +010042// 1 signaling, 2 network, 2 worker and 2 extra for codecs etc.
43constexpr int kPeerConnectionUsedThreads = 7;
44// Framework has extra thread for network layer and extra thread for peer
45// connection stats polling.
46constexpr int kFrameworkUsedThreads = 2;
47constexpr int kMaxVideoAnalyzerThreads = 8;
Artem Titova6a273d2019-02-07 16:43:51 +010048
Mirko Bonadei12ae4f42019-02-26 15:19:07 +010049constexpr TimeDelta kStatsUpdateInterval = TimeDelta::Seconds<1>();
50constexpr TimeDelta kStatsPollingStopTimeout = TimeDelta::Seconds<1>();
51
Artem Titova6a273d2019-02-07 16:43:51 +010052std::string VideoConfigSourcePresenceToString(const VideoConfig& video_config) {
53 char buf[1024];
54 rtc::SimpleStringBuilder builder(buf);
55 builder << "video_config.generator=" << video_config.generator.has_value()
56 << "; video_config.input_file_name="
57 << video_config.input_file_name.has_value()
58 << "; video_config.screen_share_config="
59 << video_config.screen_share_config.has_value() << ";";
60 return builder.str();
61}
62
Artem Titovbf9e01a2019-02-14 10:51:27 +010063class FixturePeerConnectionObserver : public MockPeerConnectionObserver {
64 public:
65 // |on_track_callback| will be called when any new track will be added to peer
66 // connection.
67 // |on_connected_callback| will be called when peer connection will come to
68 // either connected or completed state. Client should notice that in the case
69 // of reconnect this callback can be called again, so it should be tolerant
70 // to such behavior.
71 FixturePeerConnectionObserver(
72 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
73 on_track_callback,
74 std::function<void()> on_connected_callback)
75 : on_track_callback_(std::move(on_track_callback)),
76 on_connected_callback_(std::move(on_connected_callback)) {}
77
78 void OnTrack(
79 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) override {
80 MockPeerConnectionObserver::OnTrack(transceiver);
81 on_track_callback_(transceiver);
82 }
83
84 void OnIceConnectionChange(
85 PeerConnectionInterface::IceConnectionState new_state) override {
86 MockPeerConnectionObserver::OnIceConnectionChange(new_state);
87 if (ice_connected_) {
88 on_connected_callback_();
89 }
90 }
91
92 private:
93 std::function<void(rtc::scoped_refptr<RtpTransceiverInterface>)>
94 on_track_callback_;
95 std::function<void()> on_connected_callback_;
96};
97
Artem Titova6a273d2019-02-07 16:43:51 +010098} // namespace
99
100PeerConnectionE2EQualityTest::PeerConnectionE2EQualityTest(
Artem Titov59835852019-02-27 17:44:13 +0100101 std::string test_case_name,
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100102 std::unique_ptr<AudioQualityAnalyzerInterface> audio_quality_analyzer,
103 std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer)
Artem Titov59835852019-02-27 17:44:13 +0100104 : clock_(Clock::GetRealTimeClock()),
Artem Titovba82e002019-03-15 15:57:53 +0100105 test_case_name_(std::move(test_case_name)) {
Artem Titova6a273d2019-02-07 16:43:51 +0100106 // Create default video quality analyzer. We will always create an analyzer,
107 // even if there are no video streams, because it will be installed into video
108 // encoder/decoder factories.
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100109 if (video_quality_analyzer == nullptr) {
Artem Titov859abef2019-03-01 11:11:09 +0100110 video_quality_analyzer = absl::make_unique<DefaultVideoQualityAnalyzer>();
Artem Titova6a273d2019-02-07 16:43:51 +0100111 }
112 encoded_image_id_controller_ =
Artem Titov32232e92019-02-20 21:13:14 +0100113 absl::make_unique<SingleProcessEncodedImageDataInjector>();
Artem Titova6a273d2019-02-07 16:43:51 +0100114 video_quality_analyzer_injection_helper_ =
115 absl::make_unique<VideoQualityAnalyzerInjectionHelper>(
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100116 std::move(video_quality_analyzer), encoded_image_id_controller_.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100117 encoded_image_id_controller_.get());
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100118
119 if (audio_quality_analyzer == nullptr) {
120 audio_quality_analyzer = absl::make_unique<DefaultAudioQualityAnalyzer>();
121 }
122 audio_quality_analyzer_.swap(audio_quality_analyzer);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100123}
124
Artem Titovba82e002019-03-15 15:57:53 +0100125void PeerConnectionE2EQualityTest::ExecuteAt(
126 TimeDelta target_time_since_start,
127 std::function<void(TimeDelta)> func) {
128 ExecuteTask(target_time_since_start, absl::nullopt, func);
129}
130
131void PeerConnectionE2EQualityTest::ExecuteEvery(
132 TimeDelta initial_delay_since_start,
133 TimeDelta interval,
134 std::function<void(TimeDelta)> func) {
135 ExecuteTask(initial_delay_since_start, interval, func);
136}
137
138void PeerConnectionE2EQualityTest::ExecuteTask(
139 TimeDelta initial_delay_since_start,
140 absl::optional<TimeDelta> interval,
141 std::function<void(TimeDelta)> func) {
142 RTC_CHECK(initial_delay_since_start.IsFinite() &&
143 initial_delay_since_start >= TimeDelta::Zero());
144 RTC_CHECK(!interval ||
145 (interval->IsFinite() && *interval > TimeDelta::Zero()));
146 rtc::CritScope crit(&lock_);
147 ScheduledActivity activity(initial_delay_since_start, interval, func);
148 if (start_time_.IsInfinite()) {
149 scheduled_activities_.push(std::move(activity));
150 } else {
151 PostTask(std::move(activity));
152 }
153}
154
155void PeerConnectionE2EQualityTest::PostTask(ScheduledActivity activity) {
156 // Because start_time_ will never change at this point copy it to local
157 // variable to capture in in lambda without requirement to hold a lock.
158 Timestamp start_time = start_time_;
159
160 TimeDelta remaining_delay =
161 activity.initial_delay_since_start == TimeDelta::Zero()
162 ? TimeDelta::Zero()
163 : activity.initial_delay_since_start - (Now() - start_time_);
164 if (remaining_delay < TimeDelta::Zero()) {
165 RTC_LOG(WARNING) << "Executing late task immediately, late by="
166 << ToString(remaining_delay.Abs());
167 remaining_delay = TimeDelta::Zero();
168 }
169
170 if (activity.interval) {
171 if (remaining_delay == TimeDelta::Zero()) {
172 repeating_task_handles_.push_back(RepeatingTaskHandle::Start(
173 task_queue_->Get(), [activity, start_time, this]() {
174 activity.func(Now() - start_time);
175 return *activity.interval;
176 }));
177 return;
178 }
179 repeating_task_handles_.push_back(RepeatingTaskHandle::DelayedStart(
180 task_queue_->Get(), remaining_delay, [activity, start_time, this]() {
181 activity.func(Now() - start_time);
182 return *activity.interval;
183 }));
184 return;
185 }
186
187 if (remaining_delay == TimeDelta::Zero()) {
188 task_queue_->PostTask(
189 [activity, start_time, this]() { activity.func(Now() - start_time); });
190 return;
191 }
192
193 task_queue_->PostDelayedTask(
194 [activity, start_time, this]() { activity.func(Now() - start_time); },
195 remaining_delay.ms());
196}
197
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100198void PeerConnectionE2EQualityTest::Run(
199 std::unique_ptr<InjectableComponents> alice_components,
200 std::unique_ptr<Params> alice_params,
201 std::unique_ptr<InjectableComponents> bob_components,
202 std::unique_ptr<Params> bob_params,
203 RunParams run_params) {
204 RTC_CHECK(alice_components);
205 RTC_CHECK(alice_params);
206 RTC_CHECK(bob_components);
207 RTC_CHECK(bob_params);
208
Artem Titov9a7e7212019-02-28 16:34:17 +0100209 SetDefaultValuesForMissingParams({alice_params.get(), bob_params.get()});
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100210 ValidateParams({alice_params.get(), bob_params.get()});
211
212 // Print test summary
213 RTC_LOG(INFO)
214 << "Media quality test: Alice will make a call to Bob with media video="
215 << !alice_params->video_configs.empty()
216 << "; audio=" << alice_params->audio_config.has_value()
217 << ". Bob will respond with media video="
218 << !bob_params->video_configs.empty()
219 << "; audio=" << bob_params->audio_config.has_value();
220
221 const std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
222 signaling_thread->SetName(kSignalThreadName, nullptr);
223 signaling_thread->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100224
225 // Create call participants: Alice and Bob.
226 // Audio streams are intercepted in AudioDeviceModule, so if it is required to
227 // catch output of Alice's stream, Alice's output_dump_file_name should be
228 // passed to Bob's TestPeer setup as audio output file name.
229 absl::optional<std::string> alice_audio_output_dump_file_name =
230 bob_params->audio_config ? bob_params->audio_config->output_dump_file_name
231 : absl::nullopt;
232 absl::optional<std::string> bob_audio_output_dump_file_name =
233 alice_params->audio_config
234 ? alice_params->audio_config->output_dump_file_name
235 : absl::nullopt;
Artem Titovbf9e01a2019-02-14 10:51:27 +0100236 // Copy Alice and Bob video configs to correctly pass them into lambdas.
237 std::vector<VideoConfig> alice_video_configs = alice_params->video_configs;
238 std::vector<VideoConfig> bob_video_configs = bob_params->video_configs;
239
Artem Titova6a273d2019-02-07 16:43:51 +0100240 alice_ = TestPeer::CreateTestPeer(
241 std::move(alice_components), std::move(alice_params),
Artem Titovbf9e01a2019-02-14 10:51:27 +0100242 absl::make_unique<FixturePeerConnectionObserver>(
243 [this, bob_video_configs](
244 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
245 SetupVideoSink(transceiver, bob_video_configs);
246 },
247 [this]() { StartVideo(alice_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100248 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100249 alice_audio_output_dump_file_name);
250 bob_ = TestPeer::CreateTestPeer(
251 std::move(bob_components), std::move(bob_params),
Artem Titovbf9e01a2019-02-14 10:51:27 +0100252 absl::make_unique<FixturePeerConnectionObserver>(
253 [this, alice_video_configs](
254 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
255 SetupVideoSink(transceiver, alice_video_configs);
256 },
257 [this]() { StartVideo(bob_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100258 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100259 bob_audio_output_dump_file_name);
Artem Titov9f97c9a2019-02-08 00:35:13 +0100260
261 int num_cores = CpuInfo::DetectNumberOfCores();
262 RTC_DCHECK_GE(num_cores, 1);
263
264 int video_analyzer_threads =
265 num_cores - kPeerConnectionUsedThreads - kFrameworkUsedThreads;
266 if (video_analyzer_threads <= 0) {
267 video_analyzer_threads = 1;
268 }
269 video_analyzer_threads =
270 std::min(video_analyzer_threads, kMaxVideoAnalyzerThreads);
271 RTC_LOG(INFO) << "video_analyzer_threads=" << video_analyzer_threads;
272
Artem Titov59835852019-02-27 17:44:13 +0100273 video_quality_analyzer_injection_helper_->Start(test_case_name_,
274 video_analyzer_threads);
275 audio_quality_analyzer_->Start(test_case_name_);
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +0100276
277 // Start RTCEventLog recording if requested.
278 if (alice_->params()->rtc_event_log_path) {
279 auto alice_rtc_event_log = absl::make_unique<webrtc::RtcEventLogOutputFile>(
280 alice_->params()->rtc_event_log_path.value());
281 alice_->pc()->StartRtcEventLog(std::move(alice_rtc_event_log),
282 webrtc::RtcEventLog::kImmediateOutput);
283 }
284 if (bob_->params()->rtc_event_log_path) {
285 auto bob_rtc_event_log = absl::make_unique<webrtc::RtcEventLogOutputFile>(
286 bob_->params()->rtc_event_log_path.value());
287 bob_->pc()->StartRtcEventLog(std::move(bob_rtc_event_log),
288 webrtc::RtcEventLog::kImmediateOutput);
289 }
290
Artem Titovba82e002019-03-15 15:57:53 +0100291 // Create a |task_queue_|.
292 task_queue_ = absl::make_unique<rtc::TaskQueue>("pc_e2e_quality_test");
293 // Setup call.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100294 signaling_thread->Invoke<void>(
Artem Titova6a273d2019-02-07 16:43:51 +0100295 RTC_FROM_HERE,
Mirko Bonadei71318802019-02-18 18:52:43 +0100296 rtc::Bind(&PeerConnectionE2EQualityTest::SetupCallOnSignalingThread,
297 this));
Artem Titovba82e002019-03-15 15:57:53 +0100298 {
299 rtc::CritScope crit(&lock_);
300 start_time_ = Now();
301 while (!scheduled_activities_.empty()) {
302 PostTask(std::move(scheduled_activities_.front()));
303 scheduled_activities_.pop();
304 }
305 }
Mirko Bonadei71318802019-02-18 18:52:43 +0100306
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100307 StatsPoller stats_poller({audio_quality_analyzer_.get(),
308 video_quality_analyzer_injection_helper_.get()},
309 {{"alice", alice_.get()}, {"bob", bob_.get()}});
310
Artem Titovba82e002019-03-15 15:57:53 +0100311 task_queue_->PostTask([&stats_poller, this]() {
312 RTC_DCHECK_RUN_ON(task_queue_.get());
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100313 stats_polling_task_ =
Artem Titovba82e002019-03-15 15:57:53 +0100314 RepeatingTaskHandle::Start(task_queue_->Get(), [this, &stats_poller]() {
315 RTC_DCHECK_RUN_ON(task_queue_.get());
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100316 stats_poller.PollStatsAndNotifyObservers();
317 return kStatsUpdateInterval;
318 });
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100319 });
320
Mirko Bonadei71318802019-02-18 18:52:43 +0100321 rtc::Event done;
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100322 done.Wait(run_params.run_duration.ms());
323
324 rtc::Event stats_polling_stopped;
Artem Titovba82e002019-03-15 15:57:53 +0100325 task_queue_->PostTask([&stats_polling_stopped, this]() {
326 RTC_DCHECK_RUN_ON(task_queue_.get());
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100327 stats_polling_task_.Stop();
328 stats_polling_stopped.Set();
329 });
330 bool no_timeout = stats_polling_stopped.Wait(kStatsPollingStopTimeout.ms());
331 RTC_CHECK(no_timeout) << "Failed to stop Stats polling after "
332 << kStatsPollingStopTimeout.seconds() << " seconds.";
Mirko Bonadei71318802019-02-18 18:52:43 +0100333
Artem Titovba82e002019-03-15 15:57:53 +0100334 // Destroy |task_queue_|. It is done to stop all running tasks and prevent
335 // their access to any call related objects after these objects will be
336 // destroyed during call tear down.
337 task_queue_.reset();
338 // Tear down the call.
Mirko Bonadei71318802019-02-18 18:52:43 +0100339 signaling_thread->Invoke<void>(
340 RTC_FROM_HERE,
341 rtc::Bind(&PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread,
342 this));
343
Artem Titov9f97c9a2019-02-08 00:35:13 +0100344 video_quality_analyzer_injection_helper_->Stop();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100345
346 // Ensuring that TestPeers have been destroyed in order to correctly close
347 // Audio dumps.
348 RTC_CHECK(!alice_);
349 RTC_CHECK(!bob_);
350 // Ensuring that FrameGeneratorCapturerVideoTrackSource and VideoFrameWriter
351 // are destroyed on the right thread.
Artem Titovbf9e01a2019-02-14 10:51:27 +0100352 RTC_CHECK(alice_video_sources_.empty());
353 RTC_CHECK(bob_video_sources_.empty());
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100354 RTC_CHECK(video_writers_.empty());
Artem Titova6a273d2019-02-07 16:43:51 +0100355}
356
Artem Titov9a7e7212019-02-28 16:34:17 +0100357void PeerConnectionE2EQualityTest::SetDefaultValuesForMissingParams(
Artem Titova6a273d2019-02-07 16:43:51 +0100358 std::vector<Params*> params) {
Artem Titov3481db22019-02-28 13:13:15 +0100359 int video_counter = 0;
360 int audio_counter = 0;
Artem Titova6a273d2019-02-07 16:43:51 +0100361 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100362 std::set<std::string> audio_labels;
Artem Titova6a273d2019-02-07 16:43:51 +0100363 for (auto* p : params) {
364 for (auto& video_config : p->video_configs) {
Artem Titov9a7e7212019-02-28 16:34:17 +0100365 if (!video_config.generator && !video_config.input_file_name &&
366 !video_config.screen_share_config) {
367 video_config.generator = VideoGeneratorType::kDefault;
368 }
Artem Titova6a273d2019-02-07 16:43:51 +0100369 if (!video_config.stream_label) {
370 std::string label;
371 do {
Artem Titov3481db22019-02-28 13:13:15 +0100372 label = "_auto_video_stream_label_" + std::to_string(video_counter);
373 ++video_counter;
Artem Titova6a273d2019-02-07 16:43:51 +0100374 } while (!video_labels.insert(label).second);
375 video_config.stream_label = label;
376 }
377 }
Artem Titov3481db22019-02-28 13:13:15 +0100378 if (p->audio_config) {
379 if (!p->audio_config->stream_label) {
380 std::string label;
381 do {
382 label = "_auto_audio_stream_label_" + std::to_string(audio_counter);
383 ++audio_counter;
384 } while (!audio_labels.insert(label).second);
385 p->audio_config->stream_label = label;
386 }
387 }
Artem Titova6a273d2019-02-07 16:43:51 +0100388 }
389}
390
391void PeerConnectionE2EQualityTest::ValidateParams(std::vector<Params*> params) {
392 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100393 std::set<std::string> audio_labels;
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100394 int media_streams_count = 0;
395
Artem Titova6a273d2019-02-07 16:43:51 +0100396 for (Params* p : params) {
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100397 if (p->audio_config) {
398 media_streams_count++;
399 }
400 media_streams_count += p->video_configs.size();
401
Artem Titova6a273d2019-02-07 16:43:51 +0100402 // Validate that each video config has exactly one of |generator|,
403 // |input_file_name| or |screen_share_config| set. Also validate that all
404 // video stream labels are unique.
405 for (auto& video_config : p->video_configs) {
406 RTC_CHECK(video_config.stream_label);
407 bool inserted =
408 video_labels.insert(video_config.stream_label.value()).second;
409 RTC_CHECK(inserted) << "Duplicate video_config.stream_label="
410 << video_config.stream_label.value();
411 RTC_CHECK(video_config.generator || video_config.input_file_name ||
412 video_config.screen_share_config)
413 << VideoConfigSourcePresenceToString(video_config);
414 RTC_CHECK(!(video_config.input_file_name && video_config.generator))
415 << VideoConfigSourcePresenceToString(video_config);
416 RTC_CHECK(
417 !(video_config.input_file_name && video_config.screen_share_config))
418 << VideoConfigSourcePresenceToString(video_config);
419 RTC_CHECK(!(video_config.screen_share_config && video_config.generator))
420 << VideoConfigSourcePresenceToString(video_config);
421 }
422 if (p->audio_config) {
Artem Titov3481db22019-02-28 13:13:15 +0100423 bool inserted =
424 audio_labels.insert(p->audio_config->stream_label.value()).second;
425 RTC_CHECK(inserted) << "Duplicate audio_config.stream_label="
426 << p->audio_config->stream_label.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100427 // Check that if mode input file name specified only if mode is kFile.
428 if (p->audio_config.value().mode == AudioConfig::Mode::kGenerated) {
429 RTC_CHECK(!p->audio_config.value().input_file_name);
430 }
431 if (p->audio_config.value().mode == AudioConfig::Mode::kFile) {
432 RTC_CHECK(p->audio_config.value().input_file_name);
Artem Titov0b443142019-03-20 11:11:08 +0100433 RTC_CHECK(
434 test::FileExists(p->audio_config.value().input_file_name.value()));
Artem Titova6a273d2019-02-07 16:43:51 +0100435 }
436 }
437 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100438
439 RTC_CHECK_GT(media_streams_count, 0) << "No media in the call.";
Artem Titova6a273d2019-02-07 16:43:51 +0100440}
441
Artem Titovbf9e01a2019-02-14 10:51:27 +0100442void PeerConnectionE2EQualityTest::SetupVideoSink(
443 rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
444 std::vector<VideoConfig> remote_video_configs) {
445 const rtc::scoped_refptr<MediaStreamTrackInterface>& track =
446 transceiver->receiver()->track();
447 if (track->kind() != MediaStreamTrackInterface::kVideoKind) {
448 return;
449 }
450
Artem Titov7c554152019-02-28 10:25:52 +0100451 RTC_CHECK_EQ(transceiver->receiver()->stream_ids().size(), 1);
452 std::string stream_label = transceiver->receiver()->stream_ids().front();
Artem Titovbf9e01a2019-02-14 10:51:27 +0100453 VideoConfig* video_config = nullptr;
454 for (auto& config : remote_video_configs) {
Artem Titov7c554152019-02-28 10:25:52 +0100455 if (config.stream_label == stream_label) {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100456 video_config = &config;
457 break;
458 }
459 }
460 RTC_CHECK(video_config);
Artem Titov0b443142019-03-20 11:11:08 +0100461 test::VideoFrameWriter* writer = MaybeCreateVideoWriter(
Artem Titovbf9e01a2019-02-14 10:51:27 +0100462 video_config->output_dump_file_name, *video_config);
463 // It is safe to cast here, because it is checked above that
464 // track->kind() is kVideoKind.
465 auto* video_track = static_cast<VideoTrackInterface*>(track.get());
466 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> video_sink =
467 video_quality_analyzer_injection_helper_->CreateVideoSink(writer);
468 video_track->AddOrUpdateSink(video_sink.get(), rtc::VideoSinkWants());
469 output_video_sinks_.push_back(std::move(video_sink));
470}
471
Mirko Bonadei71318802019-02-18 18:52:43 +0100472void PeerConnectionE2EQualityTest::SetupCallOnSignalingThread() {
Artem Titov7c554152019-02-28 10:25:52 +0100473 // We need receive-only transceivers for Bob's media stream, so there will
474 // be media section in SDP for that streams in Alice's offer, because it is
475 // forbidden to add new media sections in answer in Unified Plan.
476 RtpTransceiverInit receive_only_transceiver_init;
477 receive_only_transceiver_init.direction = RtpTransceiverDirection::kRecvOnly;
478 if (bob_->params()->audio_config) {
479 // Setup receive audio transceiver if Bob has audio to send. If we'll need
480 // multiple audio streams, then we need transceiver for each Bob's audio
481 // stream.
482 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_AUDIO,
483 receive_only_transceiver_init);
484 }
485 for (size_t i = 0; i < bob_->params()->video_configs.size(); ++i) {
486 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
487 receive_only_transceiver_init);
488 }
489 // Then add media for Alice and Bob
490 alice_video_sources_ = MaybeAddMedia(alice_.get());
491 bob_video_sources_ = MaybeAddMedia(bob_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100492
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100493 SetupCall();
Mirko Bonadei71318802019-02-18 18:52:43 +0100494}
Artem Titova6a273d2019-02-07 16:43:51 +0100495
Mirko Bonadei71318802019-02-18 18:52:43 +0100496void PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread() {
Artem Titova6a273d2019-02-07 16:43:51 +0100497 TearDownCall();
Artem Titova6a273d2019-02-07 16:43:51 +0100498}
499
Artem Titovbf9e01a2019-02-14 10:51:27 +0100500std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100501PeerConnectionE2EQualityTest::MaybeAddMedia(TestPeer* peer) {
502 MaybeAddAudio(peer);
503 return MaybeAddVideo(peer);
Artem Titova6a273d2019-02-07 16:43:51 +0100504}
505
Artem Titovbf9e01a2019-02-14 10:51:27 +0100506std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100507PeerConnectionE2EQualityTest::MaybeAddVideo(TestPeer* peer) {
Artem Titova6a273d2019-02-07 16:43:51 +0100508 // Params here valid because of pre-run validation.
509 Params* params = peer->params();
Artem Titovbf9e01a2019-02-14 10:51:27 +0100510 std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>> out;
Artem Titova6a273d2019-02-07 16:43:51 +0100511 for (auto video_config : params->video_configs) {
512 // Create video generator.
Artem Titov0b443142019-03-20 11:11:08 +0100513 std::unique_ptr<test::FrameGenerator> frame_generator =
Artem Titova6a273d2019-02-07 16:43:51 +0100514 CreateFrameGenerator(video_config);
515
516 // Wrap it to inject video quality analyzer and enable dump of input video
517 // if required.
Artem Titov0b443142019-03-20 11:11:08 +0100518 test::VideoFrameWriter* writer =
Artem Titova6a273d2019-02-07 16:43:51 +0100519 MaybeCreateVideoWriter(video_config.input_dump_file_name, video_config);
520 frame_generator =
521 video_quality_analyzer_injection_helper_->WrapFrameGenerator(
522 video_config.stream_label.value(), std::move(frame_generator),
523 writer);
524
525 // Setup FrameGenerator into peer connection.
Artem Titov0b443142019-03-20 11:11:08 +0100526 std::unique_ptr<test::FrameGeneratorCapturer> capturer =
527 absl::WrapUnique(test::FrameGeneratorCapturer::Create(
Artem Titova6a273d2019-02-07 16:43:51 +0100528 std::move(frame_generator), video_config.fps, clock_));
529 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource> source =
530 new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
531 move(capturer));
Artem Titovbf9e01a2019-02-14 10:51:27 +0100532 out.push_back(source);
Artem Titova6a273d2019-02-07 16:43:51 +0100533 RTC_LOG(INFO) << "Adding video with video_config.stream_label="
534 << video_config.stream_label.value();
535 rtc::scoped_refptr<VideoTrackInterface> track =
536 peer->pc_factory()->CreateVideoTrack(video_config.stream_label.value(),
537 source);
Artem Titov7c554152019-02-28 10:25:52 +0100538 peer->AddTrack(track, {video_config.stream_label.value()});
Artem Titova6a273d2019-02-07 16:43:51 +0100539 }
Artem Titovbf9e01a2019-02-14 10:51:27 +0100540 return out;
Artem Titova6a273d2019-02-07 16:43:51 +0100541}
542
Artem Titov0b443142019-03-20 11:11:08 +0100543std::unique_ptr<test::FrameGenerator>
Artem Titova6a273d2019-02-07 16:43:51 +0100544PeerConnectionE2EQualityTest::CreateFrameGenerator(
545 const VideoConfig& video_config) {
546 if (video_config.generator) {
Artem Titov0b443142019-03-20 11:11:08 +0100547 absl::optional<test::FrameGenerator::OutputType> frame_generator_type =
Artem Titova6a273d2019-02-07 16:43:51 +0100548 absl::nullopt;
549 if (video_config.generator == VideoGeneratorType::kDefault) {
Artem Titov0b443142019-03-20 11:11:08 +0100550 frame_generator_type = test::FrameGenerator::OutputType::I420;
Artem Titova6a273d2019-02-07 16:43:51 +0100551 } else if (video_config.generator == VideoGeneratorType::kI420A) {
Artem Titov0b443142019-03-20 11:11:08 +0100552 frame_generator_type = test::FrameGenerator::OutputType::I420A;
Artem Titova6a273d2019-02-07 16:43:51 +0100553 } else if (video_config.generator == VideoGeneratorType::kI010) {
Artem Titov0b443142019-03-20 11:11:08 +0100554 frame_generator_type = test::FrameGenerator::OutputType::I010;
Artem Titova6a273d2019-02-07 16:43:51 +0100555 }
Artem Titov0b443142019-03-20 11:11:08 +0100556 return test::FrameGenerator::CreateSquareGenerator(
Artem Titova6a273d2019-02-07 16:43:51 +0100557 static_cast<int>(video_config.width),
558 static_cast<int>(video_config.height), frame_generator_type,
559 absl::nullopt);
560 }
561 if (video_config.input_file_name) {
Artem Titov0b443142019-03-20 11:11:08 +0100562 return test::FrameGenerator::CreateFromYuvFile(
Artem Titova6a273d2019-02-07 16:43:51 +0100563 std::vector<std::string>(/*count=*/1,
564 video_config.input_file_name.value()),
565 video_config.width, video_config.height, /*frame_repeat_count=*/1);
566 }
567 if (video_config.screen_share_config) {
568 // TODO(titovartem) implement screen share support
569 // (http://bugs.webrtc.org/10138)
570 RTC_NOTREACHED() << "Screen share is not implemented";
571 return nullptr;
572 }
573 RTC_NOTREACHED() << "Unsupported video_config input source";
574 return nullptr;
575}
576
Artem Titov7c554152019-02-28 10:25:52 +0100577void PeerConnectionE2EQualityTest::MaybeAddAudio(TestPeer* peer) {
578 if (!peer->params()->audio_config) {
579 return;
580 }
Artem Titov3481db22019-02-28 13:13:15 +0100581 const AudioConfig& audio_config = peer->params()->audio_config.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100582 rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
Artem Titov3481db22019-02-28 13:13:15 +0100583 peer->pc_factory()->CreateAudioSource(audio_config.audio_options);
Artem Titova6a273d2019-02-07 16:43:51 +0100584 rtc::scoped_refptr<AudioTrackInterface> track =
Artem Titov3481db22019-02-28 13:13:15 +0100585 peer->pc_factory()->CreateAudioTrack(*audio_config.stream_label, source);
586 peer->AddTrack(track, {*audio_config.stream_label});
Artem Titova6a273d2019-02-07 16:43:51 +0100587}
588
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100589void PeerConnectionE2EQualityTest::SetupCall() {
Artem Titova6a273d2019-02-07 16:43:51 +0100590 // Connect peers.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100591 ASSERT_TRUE(alice_->ExchangeOfferAnswerWith(bob_.get()));
Artem Titova6a273d2019-02-07 16:43:51 +0100592 // Do the SDP negotiation, and also exchange ice candidates.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100593 ASSERT_EQ_WAIT(alice_->signaling_state(), PeerConnectionInterface::kStable,
Artem Titova6a273d2019-02-07 16:43:51 +0100594 kDefaultTimeoutMs);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100595 ASSERT_TRUE_WAIT(alice_->IsIceGatheringDone(), kDefaultTimeoutMs);
596 ASSERT_TRUE_WAIT(bob_->IsIceGatheringDone(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100597
598 // Connect an ICE candidate pairs.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100599 ASSERT_TRUE(bob_->AddIceCandidates(alice_->observer()->GetAllCandidates()));
600 ASSERT_TRUE(alice_->AddIceCandidates(bob_->observer()->GetAllCandidates()));
Artem Titova6a273d2019-02-07 16:43:51 +0100601 // This means that ICE and DTLS are connected.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100602 ASSERT_TRUE_WAIT(bob_->IsIceConnected(), kDefaultTimeoutMs);
603 ASSERT_TRUE_WAIT(alice_->IsIceConnected(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100604}
605
Artem Titovbf9e01a2019-02-14 10:51:27 +0100606void PeerConnectionE2EQualityTest::StartVideo(
607 const std::vector<
608 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>& sources) {
609 for (auto& source : sources) {
610 if (source->state() != MediaSourceInterface::SourceState::kLive) {
611 source->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100612 }
Artem Titova6a273d2019-02-07 16:43:51 +0100613 }
614}
615
616void PeerConnectionE2EQualityTest::TearDownCall() {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100617 for (const auto& video_source : alice_video_sources_) {
618 video_source->Stop();
619 }
620 for (const auto& video_source : bob_video_sources_) {
Artem Titova6a273d2019-02-07 16:43:51 +0100621 video_source->Stop();
622 }
623
624 alice_->pc()->Close();
625 bob_->pc()->Close();
626
627 for (const auto& video_writer : video_writers_) {
628 video_writer->Close();
629 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100630
Artem Titovbf9e01a2019-02-14 10:51:27 +0100631 alice_video_sources_.clear();
632 bob_video_sources_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100633 video_writers_.clear();
634 alice_.reset();
635 bob_.reset();
Artem Titova6a273d2019-02-07 16:43:51 +0100636}
637
Artem Titov0b443142019-03-20 11:11:08 +0100638test::VideoFrameWriter* PeerConnectionE2EQualityTest::MaybeCreateVideoWriter(
Artem Titova6a273d2019-02-07 16:43:51 +0100639 absl::optional<std::string> file_name,
640 const VideoConfig& config) {
641 if (!file_name) {
642 return nullptr;
643 }
Artem Titov0b443142019-03-20 11:11:08 +0100644 auto video_writer = absl::make_unique<test::VideoFrameWriter>(
Artem Titova6a273d2019-02-07 16:43:51 +0100645 file_name.value(), config.width, config.height, config.fps);
Artem Titov0b443142019-03-20 11:11:08 +0100646 test::VideoFrameWriter* out = video_writer.get();
Artem Titova6a273d2019-02-07 16:43:51 +0100647 video_writers_.push_back(std::move(video_writer));
648 return out;
649}
650
Artem Titovba82e002019-03-15 15:57:53 +0100651Timestamp PeerConnectionE2EQualityTest::Now() const {
652 return Timestamp::us(clock_->TimeInMicroseconds());
653}
654
655PeerConnectionE2EQualityTest::ScheduledActivity::ScheduledActivity(
656 TimeDelta initial_delay_since_start,
657 absl::optional<TimeDelta> interval,
658 std::function<void(TimeDelta)> func)
659 : initial_delay_since_start(initial_delay_since_start),
660 interval(std::move(interval)),
661 func(std::move(func)) {}
662
Artem Titov0b443142019-03-20 11:11:08 +0100663} // namespace webrtc_pc_e2e
Artem Titova6a273d2019-02-07 16:43:51 +0100664} // namespace webrtc