blob: 3f14746f3ba0c5cb66bb285a5e6f4d44ea7edbc4 [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 {
35namespace test {
36namespace {
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()),
105 test_case_name_(std::move(test_case_name)),
106 task_queue_("pc_e2e_quality_test") {
Artem Titova6a273d2019-02-07 16:43:51 +0100107 // Create default video quality analyzer. We will always create an analyzer,
108 // even if there are no video streams, because it will be installed into video
109 // encoder/decoder factories.
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100110 if (video_quality_analyzer == nullptr) {
Artem Titov859abef2019-03-01 11:11:09 +0100111 video_quality_analyzer = absl::make_unique<DefaultVideoQualityAnalyzer>();
Artem Titova6a273d2019-02-07 16:43:51 +0100112 }
113 encoded_image_id_controller_ =
Artem Titov32232e92019-02-20 21:13:14 +0100114 absl::make_unique<SingleProcessEncodedImageDataInjector>();
Artem Titova6a273d2019-02-07 16:43:51 +0100115 video_quality_analyzer_injection_helper_ =
116 absl::make_unique<VideoQualityAnalyzerInjectionHelper>(
Mirko Bonadeif5d88082019-02-20 16:16:54 +0100117 std::move(video_quality_analyzer), encoded_image_id_controller_.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100118 encoded_image_id_controller_.get());
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100119
120 if (audio_quality_analyzer == nullptr) {
121 audio_quality_analyzer = absl::make_unique<DefaultAudioQualityAnalyzer>();
122 }
123 audio_quality_analyzer_.swap(audio_quality_analyzer);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100124}
125
126void PeerConnectionE2EQualityTest::Run(
127 std::unique_ptr<InjectableComponents> alice_components,
128 std::unique_ptr<Params> alice_params,
129 std::unique_ptr<InjectableComponents> bob_components,
130 std::unique_ptr<Params> bob_params,
131 RunParams run_params) {
132 RTC_CHECK(alice_components);
133 RTC_CHECK(alice_params);
134 RTC_CHECK(bob_components);
135 RTC_CHECK(bob_params);
136
Artem Titov9a7e7212019-02-28 16:34:17 +0100137 SetDefaultValuesForMissingParams({alice_params.get(), bob_params.get()});
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100138 ValidateParams({alice_params.get(), bob_params.get()});
139
140 // Print test summary
141 RTC_LOG(INFO)
142 << "Media quality test: Alice will make a call to Bob with media video="
143 << !alice_params->video_configs.empty()
144 << "; audio=" << alice_params->audio_config.has_value()
145 << ". Bob will respond with media video="
146 << !bob_params->video_configs.empty()
147 << "; audio=" << bob_params->audio_config.has_value();
148
149 const std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
150 signaling_thread->SetName(kSignalThreadName, nullptr);
151 signaling_thread->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100152
153 // Create call participants: Alice and Bob.
154 // Audio streams are intercepted in AudioDeviceModule, so if it is required to
155 // catch output of Alice's stream, Alice's output_dump_file_name should be
156 // passed to Bob's TestPeer setup as audio output file name.
157 absl::optional<std::string> alice_audio_output_dump_file_name =
158 bob_params->audio_config ? bob_params->audio_config->output_dump_file_name
159 : absl::nullopt;
160 absl::optional<std::string> bob_audio_output_dump_file_name =
161 alice_params->audio_config
162 ? alice_params->audio_config->output_dump_file_name
163 : absl::nullopt;
Artem Titovbf9e01a2019-02-14 10:51:27 +0100164 // Copy Alice and Bob video configs to correctly pass them into lambdas.
165 std::vector<VideoConfig> alice_video_configs = alice_params->video_configs;
166 std::vector<VideoConfig> bob_video_configs = bob_params->video_configs;
167
Artem Titova6a273d2019-02-07 16:43:51 +0100168 alice_ = TestPeer::CreateTestPeer(
169 std::move(alice_components), std::move(alice_params),
Artem Titovbf9e01a2019-02-14 10:51:27 +0100170 absl::make_unique<FixturePeerConnectionObserver>(
171 [this, bob_video_configs](
172 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
173 SetupVideoSink(transceiver, bob_video_configs);
174 },
175 [this]() { StartVideo(alice_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100176 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100177 alice_audio_output_dump_file_name);
178 bob_ = TestPeer::CreateTestPeer(
179 std::move(bob_components), std::move(bob_params),
Artem Titovbf9e01a2019-02-14 10:51:27 +0100180 absl::make_unique<FixturePeerConnectionObserver>(
181 [this, alice_video_configs](
182 rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {
183 SetupVideoSink(transceiver, alice_video_configs);
184 },
185 [this]() { StartVideo(bob_video_sources_); }),
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100186 video_quality_analyzer_injection_helper_.get(), signaling_thread.get(),
Artem Titova6a273d2019-02-07 16:43:51 +0100187 bob_audio_output_dump_file_name);
Artem Titov9f97c9a2019-02-08 00:35:13 +0100188
189 int num_cores = CpuInfo::DetectNumberOfCores();
190 RTC_DCHECK_GE(num_cores, 1);
191
192 int video_analyzer_threads =
193 num_cores - kPeerConnectionUsedThreads - kFrameworkUsedThreads;
194 if (video_analyzer_threads <= 0) {
195 video_analyzer_threads = 1;
196 }
197 video_analyzer_threads =
198 std::min(video_analyzer_threads, kMaxVideoAnalyzerThreads);
199 RTC_LOG(INFO) << "video_analyzer_threads=" << video_analyzer_threads;
200
Artem Titov59835852019-02-27 17:44:13 +0100201 video_quality_analyzer_injection_helper_->Start(test_case_name_,
202 video_analyzer_threads);
203 audio_quality_analyzer_->Start(test_case_name_);
Mirko Bonadeice7a4fb2019-02-25 11:45:07 +0100204
205 // Start RTCEventLog recording if requested.
206 if (alice_->params()->rtc_event_log_path) {
207 auto alice_rtc_event_log = absl::make_unique<webrtc::RtcEventLogOutputFile>(
208 alice_->params()->rtc_event_log_path.value());
209 alice_->pc()->StartRtcEventLog(std::move(alice_rtc_event_log),
210 webrtc::RtcEventLog::kImmediateOutput);
211 }
212 if (bob_->params()->rtc_event_log_path) {
213 auto bob_rtc_event_log = absl::make_unique<webrtc::RtcEventLogOutputFile>(
214 bob_->params()->rtc_event_log_path.value());
215 bob_->pc()->StartRtcEventLog(std::move(bob_rtc_event_log),
216 webrtc::RtcEventLog::kImmediateOutput);
217 }
218
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100219 signaling_thread->Invoke<void>(
Artem Titova6a273d2019-02-07 16:43:51 +0100220 RTC_FROM_HERE,
Mirko Bonadei71318802019-02-18 18:52:43 +0100221 rtc::Bind(&PeerConnectionE2EQualityTest::SetupCallOnSignalingThread,
222 this));
223
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100224 StatsPoller stats_poller({audio_quality_analyzer_.get(),
225 video_quality_analyzer_injection_helper_.get()},
226 {{"alice", alice_.get()}, {"bob", bob_.get()}});
227
228 task_queue_.PostTask([&stats_poller, this]() {
229 RTC_DCHECK_RUN_ON(&task_queue_);
Sebastian Janssoncda86dd2019-03-11 17:26:36 +0100230 stats_polling_task_ =
231 RepeatingTaskHandle::Start(task_queue_.Get(), [this, &stats_poller]() {
232 RTC_DCHECK_RUN_ON(&task_queue_);
233 stats_poller.PollStatsAndNotifyObservers();
234 return kStatsUpdateInterval;
235 });
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100236 });
237
Mirko Bonadei71318802019-02-18 18:52:43 +0100238 rtc::Event done;
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100239 done.Wait(run_params.run_duration.ms());
240
241 rtc::Event stats_polling_stopped;
242 task_queue_.PostTask([&stats_polling_stopped, this]() {
243 RTC_DCHECK_RUN_ON(&task_queue_);
244 stats_polling_task_.Stop();
245 stats_polling_stopped.Set();
246 });
247 bool no_timeout = stats_polling_stopped.Wait(kStatsPollingStopTimeout.ms());
248 RTC_CHECK(no_timeout) << "Failed to stop Stats polling after "
249 << kStatsPollingStopTimeout.seconds() << " seconds.";
Mirko Bonadei71318802019-02-18 18:52:43 +0100250
251 signaling_thread->Invoke<void>(
252 RTC_FROM_HERE,
253 rtc::Bind(&PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread,
254 this));
255
Artem Titov9f97c9a2019-02-08 00:35:13 +0100256 video_quality_analyzer_injection_helper_->Stop();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100257
258 // Ensuring that TestPeers have been destroyed in order to correctly close
259 // Audio dumps.
260 RTC_CHECK(!alice_);
261 RTC_CHECK(!bob_);
262 // Ensuring that FrameGeneratorCapturerVideoTrackSource and VideoFrameWriter
263 // are destroyed on the right thread.
Artem Titovbf9e01a2019-02-14 10:51:27 +0100264 RTC_CHECK(alice_video_sources_.empty());
265 RTC_CHECK(bob_video_sources_.empty());
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100266 RTC_CHECK(video_writers_.empty());
Artem Titova6a273d2019-02-07 16:43:51 +0100267}
268
Artem Titov9a7e7212019-02-28 16:34:17 +0100269void PeerConnectionE2EQualityTest::SetDefaultValuesForMissingParams(
Artem Titova6a273d2019-02-07 16:43:51 +0100270 std::vector<Params*> params) {
Artem Titov3481db22019-02-28 13:13:15 +0100271 int video_counter = 0;
272 int audio_counter = 0;
Artem Titova6a273d2019-02-07 16:43:51 +0100273 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100274 std::set<std::string> audio_labels;
Artem Titova6a273d2019-02-07 16:43:51 +0100275 for (auto* p : params) {
276 for (auto& video_config : p->video_configs) {
Artem Titov9a7e7212019-02-28 16:34:17 +0100277 if (!video_config.generator && !video_config.input_file_name &&
278 !video_config.screen_share_config) {
279 video_config.generator = VideoGeneratorType::kDefault;
280 }
Artem Titova6a273d2019-02-07 16:43:51 +0100281 if (!video_config.stream_label) {
282 std::string label;
283 do {
Artem Titov3481db22019-02-28 13:13:15 +0100284 label = "_auto_video_stream_label_" + std::to_string(video_counter);
285 ++video_counter;
Artem Titova6a273d2019-02-07 16:43:51 +0100286 } while (!video_labels.insert(label).second);
287 video_config.stream_label = label;
288 }
289 }
Artem Titov3481db22019-02-28 13:13:15 +0100290 if (p->audio_config) {
291 if (!p->audio_config->stream_label) {
292 std::string label;
293 do {
294 label = "_auto_audio_stream_label_" + std::to_string(audio_counter);
295 ++audio_counter;
296 } while (!audio_labels.insert(label).second);
297 p->audio_config->stream_label = label;
298 }
299 }
Artem Titova6a273d2019-02-07 16:43:51 +0100300 }
301}
302
303void PeerConnectionE2EQualityTest::ValidateParams(std::vector<Params*> params) {
304 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100305 std::set<std::string> audio_labels;
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100306 int media_streams_count = 0;
307
Artem Titova6a273d2019-02-07 16:43:51 +0100308 for (Params* p : params) {
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100309 if (p->audio_config) {
310 media_streams_count++;
311 }
312 media_streams_count += p->video_configs.size();
313
Artem Titova6a273d2019-02-07 16:43:51 +0100314 // Validate that each video config has exactly one of |generator|,
315 // |input_file_name| or |screen_share_config| set. Also validate that all
316 // video stream labels are unique.
317 for (auto& video_config : p->video_configs) {
318 RTC_CHECK(video_config.stream_label);
319 bool inserted =
320 video_labels.insert(video_config.stream_label.value()).second;
321 RTC_CHECK(inserted) << "Duplicate video_config.stream_label="
322 << video_config.stream_label.value();
323 RTC_CHECK(video_config.generator || video_config.input_file_name ||
324 video_config.screen_share_config)
325 << VideoConfigSourcePresenceToString(video_config);
326 RTC_CHECK(!(video_config.input_file_name && video_config.generator))
327 << VideoConfigSourcePresenceToString(video_config);
328 RTC_CHECK(
329 !(video_config.input_file_name && video_config.screen_share_config))
330 << VideoConfigSourcePresenceToString(video_config);
331 RTC_CHECK(!(video_config.screen_share_config && video_config.generator))
332 << VideoConfigSourcePresenceToString(video_config);
333 }
334 if (p->audio_config) {
Artem Titov3481db22019-02-28 13:13:15 +0100335 bool inserted =
336 audio_labels.insert(p->audio_config->stream_label.value()).second;
337 RTC_CHECK(inserted) << "Duplicate audio_config.stream_label="
338 << p->audio_config->stream_label.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100339 // Check that if mode input file name specified only if mode is kFile.
340 if (p->audio_config.value().mode == AudioConfig::Mode::kGenerated) {
341 RTC_CHECK(!p->audio_config.value().input_file_name);
342 }
343 if (p->audio_config.value().mode == AudioConfig::Mode::kFile) {
344 RTC_CHECK(p->audio_config.value().input_file_name);
345 RTC_CHECK(FileExists(p->audio_config.value().input_file_name.value()));
346 }
347 }
348 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100349
350 RTC_CHECK_GT(media_streams_count, 0) << "No media in the call.";
Artem Titova6a273d2019-02-07 16:43:51 +0100351}
352
Artem Titovbf9e01a2019-02-14 10:51:27 +0100353void PeerConnectionE2EQualityTest::SetupVideoSink(
354 rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
355 std::vector<VideoConfig> remote_video_configs) {
356 const rtc::scoped_refptr<MediaStreamTrackInterface>& track =
357 transceiver->receiver()->track();
358 if (track->kind() != MediaStreamTrackInterface::kVideoKind) {
359 return;
360 }
361
Artem Titov7c554152019-02-28 10:25:52 +0100362 RTC_CHECK_EQ(transceiver->receiver()->stream_ids().size(), 1);
363 std::string stream_label = transceiver->receiver()->stream_ids().front();
Artem Titovbf9e01a2019-02-14 10:51:27 +0100364 VideoConfig* video_config = nullptr;
365 for (auto& config : remote_video_configs) {
Artem Titov7c554152019-02-28 10:25:52 +0100366 if (config.stream_label == stream_label) {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100367 video_config = &config;
368 break;
369 }
370 }
371 RTC_CHECK(video_config);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100372 VideoFrameWriter* writer = MaybeCreateVideoWriter(
373 video_config->output_dump_file_name, *video_config);
374 // It is safe to cast here, because it is checked above that
375 // track->kind() is kVideoKind.
376 auto* video_track = static_cast<VideoTrackInterface*>(track.get());
377 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> video_sink =
378 video_quality_analyzer_injection_helper_->CreateVideoSink(writer);
379 video_track->AddOrUpdateSink(video_sink.get(), rtc::VideoSinkWants());
380 output_video_sinks_.push_back(std::move(video_sink));
381}
382
Mirko Bonadei71318802019-02-18 18:52:43 +0100383void PeerConnectionE2EQualityTest::SetupCallOnSignalingThread() {
Artem Titov7c554152019-02-28 10:25:52 +0100384 // We need receive-only transceivers for Bob's media stream, so there will
385 // be media section in SDP for that streams in Alice's offer, because it is
386 // forbidden to add new media sections in answer in Unified Plan.
387 RtpTransceiverInit receive_only_transceiver_init;
388 receive_only_transceiver_init.direction = RtpTransceiverDirection::kRecvOnly;
389 if (bob_->params()->audio_config) {
390 // Setup receive audio transceiver if Bob has audio to send. If we'll need
391 // multiple audio streams, then we need transceiver for each Bob's audio
392 // stream.
393 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_AUDIO,
394 receive_only_transceiver_init);
395 }
396 for (size_t i = 0; i < bob_->params()->video_configs.size(); ++i) {
397 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
398 receive_only_transceiver_init);
399 }
400 // Then add media for Alice and Bob
401 alice_video_sources_ = MaybeAddMedia(alice_.get());
402 bob_video_sources_ = MaybeAddMedia(bob_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100403
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100404 SetupCall();
Mirko Bonadei71318802019-02-18 18:52:43 +0100405}
Artem Titova6a273d2019-02-07 16:43:51 +0100406
Mirko Bonadei71318802019-02-18 18:52:43 +0100407void PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread() {
Artem Titova6a273d2019-02-07 16:43:51 +0100408 TearDownCall();
Artem Titova6a273d2019-02-07 16:43:51 +0100409}
410
Artem Titovbf9e01a2019-02-14 10:51:27 +0100411std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100412PeerConnectionE2EQualityTest::MaybeAddMedia(TestPeer* peer) {
413 MaybeAddAudio(peer);
414 return MaybeAddVideo(peer);
Artem Titova6a273d2019-02-07 16:43:51 +0100415}
416
Artem Titovbf9e01a2019-02-14 10:51:27 +0100417std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100418PeerConnectionE2EQualityTest::MaybeAddVideo(TestPeer* peer) {
Artem Titova6a273d2019-02-07 16:43:51 +0100419 // Params here valid because of pre-run validation.
420 Params* params = peer->params();
Artem Titovbf9e01a2019-02-14 10:51:27 +0100421 std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>> out;
Artem Titova6a273d2019-02-07 16:43:51 +0100422 for (auto video_config : params->video_configs) {
423 // Create video generator.
424 std::unique_ptr<FrameGenerator> frame_generator =
425 CreateFrameGenerator(video_config);
426
427 // Wrap it to inject video quality analyzer and enable dump of input video
428 // if required.
429 VideoFrameWriter* writer =
430 MaybeCreateVideoWriter(video_config.input_dump_file_name, video_config);
431 frame_generator =
432 video_quality_analyzer_injection_helper_->WrapFrameGenerator(
433 video_config.stream_label.value(), std::move(frame_generator),
434 writer);
435
436 // Setup FrameGenerator into peer connection.
437 std::unique_ptr<FrameGeneratorCapturer> capturer =
438 absl::WrapUnique(FrameGeneratorCapturer::Create(
439 std::move(frame_generator), video_config.fps, clock_));
440 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource> source =
441 new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
442 move(capturer));
Artem Titovbf9e01a2019-02-14 10:51:27 +0100443 out.push_back(source);
Artem Titova6a273d2019-02-07 16:43:51 +0100444 RTC_LOG(INFO) << "Adding video with video_config.stream_label="
445 << video_config.stream_label.value();
446 rtc::scoped_refptr<VideoTrackInterface> track =
447 peer->pc_factory()->CreateVideoTrack(video_config.stream_label.value(),
448 source);
Artem Titov7c554152019-02-28 10:25:52 +0100449 peer->AddTrack(track, {video_config.stream_label.value()});
Artem Titova6a273d2019-02-07 16:43:51 +0100450 }
Artem Titovbf9e01a2019-02-14 10:51:27 +0100451 return out;
Artem Titova6a273d2019-02-07 16:43:51 +0100452}
453
454std::unique_ptr<FrameGenerator>
455PeerConnectionE2EQualityTest::CreateFrameGenerator(
456 const VideoConfig& video_config) {
457 if (video_config.generator) {
458 absl::optional<FrameGenerator::OutputType> frame_generator_type =
459 absl::nullopt;
460 if (video_config.generator == VideoGeneratorType::kDefault) {
461 frame_generator_type = FrameGenerator::OutputType::I420;
462 } else if (video_config.generator == VideoGeneratorType::kI420A) {
463 frame_generator_type = FrameGenerator::OutputType::I420A;
464 } else if (video_config.generator == VideoGeneratorType::kI010) {
465 frame_generator_type = FrameGenerator::OutputType::I010;
466 }
467 return FrameGenerator::CreateSquareGenerator(
468 static_cast<int>(video_config.width),
469 static_cast<int>(video_config.height), frame_generator_type,
470 absl::nullopt);
471 }
472 if (video_config.input_file_name) {
473 return FrameGenerator::CreateFromYuvFile(
474 std::vector<std::string>(/*count=*/1,
475 video_config.input_file_name.value()),
476 video_config.width, video_config.height, /*frame_repeat_count=*/1);
477 }
478 if (video_config.screen_share_config) {
479 // TODO(titovartem) implement screen share support
480 // (http://bugs.webrtc.org/10138)
481 RTC_NOTREACHED() << "Screen share is not implemented";
482 return nullptr;
483 }
484 RTC_NOTREACHED() << "Unsupported video_config input source";
485 return nullptr;
486}
487
Artem Titov7c554152019-02-28 10:25:52 +0100488void PeerConnectionE2EQualityTest::MaybeAddAudio(TestPeer* peer) {
489 if (!peer->params()->audio_config) {
490 return;
491 }
Artem Titov3481db22019-02-28 13:13:15 +0100492 const AudioConfig& audio_config = peer->params()->audio_config.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100493 rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
Artem Titov3481db22019-02-28 13:13:15 +0100494 peer->pc_factory()->CreateAudioSource(audio_config.audio_options);
Artem Titova6a273d2019-02-07 16:43:51 +0100495 rtc::scoped_refptr<AudioTrackInterface> track =
Artem Titov3481db22019-02-28 13:13:15 +0100496 peer->pc_factory()->CreateAudioTrack(*audio_config.stream_label, source);
497 peer->AddTrack(track, {*audio_config.stream_label});
Artem Titova6a273d2019-02-07 16:43:51 +0100498}
499
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100500void PeerConnectionE2EQualityTest::SetupCall() {
Artem Titova6a273d2019-02-07 16:43:51 +0100501 // Connect peers.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100502 ASSERT_TRUE(alice_->ExchangeOfferAnswerWith(bob_.get()));
Artem Titova6a273d2019-02-07 16:43:51 +0100503 // Do the SDP negotiation, and also exchange ice candidates.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100504 ASSERT_EQ_WAIT(alice_->signaling_state(), PeerConnectionInterface::kStable,
Artem Titova6a273d2019-02-07 16:43:51 +0100505 kDefaultTimeoutMs);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100506 ASSERT_TRUE_WAIT(alice_->IsIceGatheringDone(), kDefaultTimeoutMs);
507 ASSERT_TRUE_WAIT(bob_->IsIceGatheringDone(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100508
509 // Connect an ICE candidate pairs.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100510 ASSERT_TRUE(bob_->AddIceCandidates(alice_->observer()->GetAllCandidates()));
511 ASSERT_TRUE(alice_->AddIceCandidates(bob_->observer()->GetAllCandidates()));
Artem Titova6a273d2019-02-07 16:43:51 +0100512 // This means that ICE and DTLS are connected.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100513 ASSERT_TRUE_WAIT(bob_->IsIceConnected(), kDefaultTimeoutMs);
514 ASSERT_TRUE_WAIT(alice_->IsIceConnected(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100515}
516
Artem Titovbf9e01a2019-02-14 10:51:27 +0100517void PeerConnectionE2EQualityTest::StartVideo(
518 const std::vector<
519 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>& sources) {
520 for (auto& source : sources) {
521 if (source->state() != MediaSourceInterface::SourceState::kLive) {
522 source->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100523 }
Artem Titova6a273d2019-02-07 16:43:51 +0100524 }
525}
526
527void PeerConnectionE2EQualityTest::TearDownCall() {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100528 for (const auto& video_source : alice_video_sources_) {
529 video_source->Stop();
530 }
531 for (const auto& video_source : bob_video_sources_) {
Artem Titova6a273d2019-02-07 16:43:51 +0100532 video_source->Stop();
533 }
534
535 alice_->pc()->Close();
536 bob_->pc()->Close();
537
538 for (const auto& video_writer : video_writers_) {
539 video_writer->Close();
540 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100541
Artem Titovbf9e01a2019-02-14 10:51:27 +0100542 alice_video_sources_.clear();
543 bob_video_sources_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100544 video_writers_.clear();
545 alice_.reset();
546 bob_.reset();
Artem Titova6a273d2019-02-07 16:43:51 +0100547}
548
549VideoFrameWriter* PeerConnectionE2EQualityTest::MaybeCreateVideoWriter(
550 absl::optional<std::string> file_name,
551 const VideoConfig& config) {
552 if (!file_name) {
553 return nullptr;
554 }
555 auto video_writer = absl::make_unique<VideoFrameWriter>(
556 file_name.value(), config.width, config.height, config.fps);
557 VideoFrameWriter* out = video_writer.get();
558 video_writers_.push_back(std::move(video_writer));
559 return out;
560}
561
562} // namespace test
563} // namespace webrtc