blob: 8ccff691124529fd01f63d95b8ec997f8cdbbb6d [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_);
230 stats_polling_task_ = RepeatingTaskHandle::Start([this, &stats_poller]() {
231 RTC_DCHECK_RUN_ON(&task_queue_);
232 stats_poller.PollStatsAndNotifyObservers();
233 return kStatsUpdateInterval;
234 });
235 });
236
Mirko Bonadei71318802019-02-18 18:52:43 +0100237 rtc::Event done;
Mirko Bonadei12ae4f42019-02-26 15:19:07 +0100238 done.Wait(run_params.run_duration.ms());
239
240 rtc::Event stats_polling_stopped;
241 task_queue_.PostTask([&stats_polling_stopped, this]() {
242 RTC_DCHECK_RUN_ON(&task_queue_);
243 stats_polling_task_.Stop();
244 stats_polling_stopped.Set();
245 });
246 bool no_timeout = stats_polling_stopped.Wait(kStatsPollingStopTimeout.ms());
247 RTC_CHECK(no_timeout) << "Failed to stop Stats polling after "
248 << kStatsPollingStopTimeout.seconds() << " seconds.";
Mirko Bonadei71318802019-02-18 18:52:43 +0100249
250 signaling_thread->Invoke<void>(
251 RTC_FROM_HERE,
252 rtc::Bind(&PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread,
253 this));
254
Artem Titov9f97c9a2019-02-08 00:35:13 +0100255 video_quality_analyzer_injection_helper_->Stop();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100256
257 // Ensuring that TestPeers have been destroyed in order to correctly close
258 // Audio dumps.
259 RTC_CHECK(!alice_);
260 RTC_CHECK(!bob_);
261 // Ensuring that FrameGeneratorCapturerVideoTrackSource and VideoFrameWriter
262 // are destroyed on the right thread.
Artem Titovbf9e01a2019-02-14 10:51:27 +0100263 RTC_CHECK(alice_video_sources_.empty());
264 RTC_CHECK(bob_video_sources_.empty());
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100265 RTC_CHECK(video_writers_.empty());
Artem Titova6a273d2019-02-07 16:43:51 +0100266}
267
Artem Titov9a7e7212019-02-28 16:34:17 +0100268void PeerConnectionE2EQualityTest::SetDefaultValuesForMissingParams(
Artem Titova6a273d2019-02-07 16:43:51 +0100269 std::vector<Params*> params) {
Artem Titov3481db22019-02-28 13:13:15 +0100270 int video_counter = 0;
271 int audio_counter = 0;
Artem Titova6a273d2019-02-07 16:43:51 +0100272 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100273 std::set<std::string> audio_labels;
Artem Titova6a273d2019-02-07 16:43:51 +0100274 for (auto* p : params) {
275 for (auto& video_config : p->video_configs) {
Artem Titov9a7e7212019-02-28 16:34:17 +0100276 if (!video_config.generator && !video_config.input_file_name &&
277 !video_config.screen_share_config) {
278 video_config.generator = VideoGeneratorType::kDefault;
279 }
Artem Titova6a273d2019-02-07 16:43:51 +0100280 if (!video_config.stream_label) {
281 std::string label;
282 do {
Artem Titov3481db22019-02-28 13:13:15 +0100283 label = "_auto_video_stream_label_" + std::to_string(video_counter);
284 ++video_counter;
Artem Titova6a273d2019-02-07 16:43:51 +0100285 } while (!video_labels.insert(label).second);
286 video_config.stream_label = label;
287 }
288 }
Artem Titov3481db22019-02-28 13:13:15 +0100289 if (p->audio_config) {
290 if (!p->audio_config->stream_label) {
291 std::string label;
292 do {
293 label = "_auto_audio_stream_label_" + std::to_string(audio_counter);
294 ++audio_counter;
295 } while (!audio_labels.insert(label).second);
296 p->audio_config->stream_label = label;
297 }
298 }
Artem Titova6a273d2019-02-07 16:43:51 +0100299 }
300}
301
302void PeerConnectionE2EQualityTest::ValidateParams(std::vector<Params*> params) {
303 std::set<std::string> video_labels;
Artem Titov3481db22019-02-28 13:13:15 +0100304 std::set<std::string> audio_labels;
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100305 int media_streams_count = 0;
306
Artem Titova6a273d2019-02-07 16:43:51 +0100307 for (Params* p : params) {
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100308 if (p->audio_config) {
309 media_streams_count++;
310 }
311 media_streams_count += p->video_configs.size();
312
Artem Titova6a273d2019-02-07 16:43:51 +0100313 // Validate that each video config has exactly one of |generator|,
314 // |input_file_name| or |screen_share_config| set. Also validate that all
315 // video stream labels are unique.
316 for (auto& video_config : p->video_configs) {
317 RTC_CHECK(video_config.stream_label);
318 bool inserted =
319 video_labels.insert(video_config.stream_label.value()).second;
320 RTC_CHECK(inserted) << "Duplicate video_config.stream_label="
321 << video_config.stream_label.value();
322 RTC_CHECK(video_config.generator || video_config.input_file_name ||
323 video_config.screen_share_config)
324 << VideoConfigSourcePresenceToString(video_config);
325 RTC_CHECK(!(video_config.input_file_name && video_config.generator))
326 << VideoConfigSourcePresenceToString(video_config);
327 RTC_CHECK(
328 !(video_config.input_file_name && video_config.screen_share_config))
329 << VideoConfigSourcePresenceToString(video_config);
330 RTC_CHECK(!(video_config.screen_share_config && video_config.generator))
331 << VideoConfigSourcePresenceToString(video_config);
332 }
333 if (p->audio_config) {
Artem Titov3481db22019-02-28 13:13:15 +0100334 bool inserted =
335 audio_labels.insert(p->audio_config->stream_label.value()).second;
336 RTC_CHECK(inserted) << "Duplicate audio_config.stream_label="
337 << p->audio_config->stream_label.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100338 // Check that if mode input file name specified only if mode is kFile.
339 if (p->audio_config.value().mode == AudioConfig::Mode::kGenerated) {
340 RTC_CHECK(!p->audio_config.value().input_file_name);
341 }
342 if (p->audio_config.value().mode == AudioConfig::Mode::kFile) {
343 RTC_CHECK(p->audio_config.value().input_file_name);
344 RTC_CHECK(FileExists(p->audio_config.value().input_file_name.value()));
345 }
346 }
347 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100348
349 RTC_CHECK_GT(media_streams_count, 0) << "No media in the call.";
Artem Titova6a273d2019-02-07 16:43:51 +0100350}
351
Artem Titovbf9e01a2019-02-14 10:51:27 +0100352void PeerConnectionE2EQualityTest::SetupVideoSink(
353 rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
354 std::vector<VideoConfig> remote_video_configs) {
355 const rtc::scoped_refptr<MediaStreamTrackInterface>& track =
356 transceiver->receiver()->track();
357 if (track->kind() != MediaStreamTrackInterface::kVideoKind) {
358 return;
359 }
360
Artem Titov7c554152019-02-28 10:25:52 +0100361 RTC_CHECK_EQ(transceiver->receiver()->stream_ids().size(), 1);
362 std::string stream_label = transceiver->receiver()->stream_ids().front();
Artem Titovbf9e01a2019-02-14 10:51:27 +0100363 VideoConfig* video_config = nullptr;
364 for (auto& config : remote_video_configs) {
Artem Titov7c554152019-02-28 10:25:52 +0100365 if (config.stream_label == stream_label) {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100366 video_config = &config;
367 break;
368 }
369 }
370 RTC_CHECK(video_config);
Artem Titovbf9e01a2019-02-14 10:51:27 +0100371 VideoFrameWriter* writer = MaybeCreateVideoWriter(
372 video_config->output_dump_file_name, *video_config);
373 // It is safe to cast here, because it is checked above that
374 // track->kind() is kVideoKind.
375 auto* video_track = static_cast<VideoTrackInterface*>(track.get());
376 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> video_sink =
377 video_quality_analyzer_injection_helper_->CreateVideoSink(writer);
378 video_track->AddOrUpdateSink(video_sink.get(), rtc::VideoSinkWants());
379 output_video_sinks_.push_back(std::move(video_sink));
380}
381
Mirko Bonadei71318802019-02-18 18:52:43 +0100382void PeerConnectionE2EQualityTest::SetupCallOnSignalingThread() {
Artem Titov7c554152019-02-28 10:25:52 +0100383 // We need receive-only transceivers for Bob's media stream, so there will
384 // be media section in SDP for that streams in Alice's offer, because it is
385 // forbidden to add new media sections in answer in Unified Plan.
386 RtpTransceiverInit receive_only_transceiver_init;
387 receive_only_transceiver_init.direction = RtpTransceiverDirection::kRecvOnly;
388 if (bob_->params()->audio_config) {
389 // Setup receive audio transceiver if Bob has audio to send. If we'll need
390 // multiple audio streams, then we need transceiver for each Bob's audio
391 // stream.
392 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_AUDIO,
393 receive_only_transceiver_init);
394 }
395 for (size_t i = 0; i < bob_->params()->video_configs.size(); ++i) {
396 alice_->AddTransceiver(cricket::MediaType::MEDIA_TYPE_VIDEO,
397 receive_only_transceiver_init);
398 }
399 // Then add media for Alice and Bob
400 alice_video_sources_ = MaybeAddMedia(alice_.get());
401 bob_video_sources_ = MaybeAddMedia(bob_.get());
Artem Titova6a273d2019-02-07 16:43:51 +0100402
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100403 SetupCall();
Mirko Bonadei71318802019-02-18 18:52:43 +0100404}
Artem Titova6a273d2019-02-07 16:43:51 +0100405
Mirko Bonadei71318802019-02-18 18:52:43 +0100406void PeerConnectionE2EQualityTest::TearDownCallOnSignalingThread() {
Artem Titova6a273d2019-02-07 16:43:51 +0100407 TearDownCall();
Artem Titova6a273d2019-02-07 16:43:51 +0100408}
409
Artem Titovbf9e01a2019-02-14 10:51:27 +0100410std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100411PeerConnectionE2EQualityTest::MaybeAddMedia(TestPeer* peer) {
412 MaybeAddAudio(peer);
413 return MaybeAddVideo(peer);
Artem Titova6a273d2019-02-07 16:43:51 +0100414}
415
Artem Titovbf9e01a2019-02-14 10:51:27 +0100416std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
Artem Titov7c554152019-02-28 10:25:52 +0100417PeerConnectionE2EQualityTest::MaybeAddVideo(TestPeer* peer) {
Artem Titova6a273d2019-02-07 16:43:51 +0100418 // Params here valid because of pre-run validation.
419 Params* params = peer->params();
Artem Titovbf9e01a2019-02-14 10:51:27 +0100420 std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>> out;
Artem Titova6a273d2019-02-07 16:43:51 +0100421 for (auto video_config : params->video_configs) {
422 // Create video generator.
423 std::unique_ptr<FrameGenerator> frame_generator =
424 CreateFrameGenerator(video_config);
425
426 // Wrap it to inject video quality analyzer and enable dump of input video
427 // if required.
428 VideoFrameWriter* writer =
429 MaybeCreateVideoWriter(video_config.input_dump_file_name, video_config);
430 frame_generator =
431 video_quality_analyzer_injection_helper_->WrapFrameGenerator(
432 video_config.stream_label.value(), std::move(frame_generator),
433 writer);
434
435 // Setup FrameGenerator into peer connection.
436 std::unique_ptr<FrameGeneratorCapturer> capturer =
437 absl::WrapUnique(FrameGeneratorCapturer::Create(
438 std::move(frame_generator), video_config.fps, clock_));
439 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource> source =
440 new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
441 move(capturer));
Artem Titovbf9e01a2019-02-14 10:51:27 +0100442 out.push_back(source);
Artem Titova6a273d2019-02-07 16:43:51 +0100443 RTC_LOG(INFO) << "Adding video with video_config.stream_label="
444 << video_config.stream_label.value();
445 rtc::scoped_refptr<VideoTrackInterface> track =
446 peer->pc_factory()->CreateVideoTrack(video_config.stream_label.value(),
447 source);
Artem Titov7c554152019-02-28 10:25:52 +0100448 peer->AddTrack(track, {video_config.stream_label.value()});
Artem Titova6a273d2019-02-07 16:43:51 +0100449 }
Artem Titovbf9e01a2019-02-14 10:51:27 +0100450 return out;
Artem Titova6a273d2019-02-07 16:43:51 +0100451}
452
453std::unique_ptr<FrameGenerator>
454PeerConnectionE2EQualityTest::CreateFrameGenerator(
455 const VideoConfig& video_config) {
456 if (video_config.generator) {
457 absl::optional<FrameGenerator::OutputType> frame_generator_type =
458 absl::nullopt;
459 if (video_config.generator == VideoGeneratorType::kDefault) {
460 frame_generator_type = FrameGenerator::OutputType::I420;
461 } else if (video_config.generator == VideoGeneratorType::kI420A) {
462 frame_generator_type = FrameGenerator::OutputType::I420A;
463 } else if (video_config.generator == VideoGeneratorType::kI010) {
464 frame_generator_type = FrameGenerator::OutputType::I010;
465 }
466 return FrameGenerator::CreateSquareGenerator(
467 static_cast<int>(video_config.width),
468 static_cast<int>(video_config.height), frame_generator_type,
469 absl::nullopt);
470 }
471 if (video_config.input_file_name) {
472 return FrameGenerator::CreateFromYuvFile(
473 std::vector<std::string>(/*count=*/1,
474 video_config.input_file_name.value()),
475 video_config.width, video_config.height, /*frame_repeat_count=*/1);
476 }
477 if (video_config.screen_share_config) {
478 // TODO(titovartem) implement screen share support
479 // (http://bugs.webrtc.org/10138)
480 RTC_NOTREACHED() << "Screen share is not implemented";
481 return nullptr;
482 }
483 RTC_NOTREACHED() << "Unsupported video_config input source";
484 return nullptr;
485}
486
Artem Titov7c554152019-02-28 10:25:52 +0100487void PeerConnectionE2EQualityTest::MaybeAddAudio(TestPeer* peer) {
488 if (!peer->params()->audio_config) {
489 return;
490 }
Artem Titov3481db22019-02-28 13:13:15 +0100491 const AudioConfig& audio_config = peer->params()->audio_config.value();
Artem Titova6a273d2019-02-07 16:43:51 +0100492 rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
Artem Titov3481db22019-02-28 13:13:15 +0100493 peer->pc_factory()->CreateAudioSource(audio_config.audio_options);
Artem Titova6a273d2019-02-07 16:43:51 +0100494 rtc::scoped_refptr<AudioTrackInterface> track =
Artem Titov3481db22019-02-28 13:13:15 +0100495 peer->pc_factory()->CreateAudioTrack(*audio_config.stream_label, source);
496 peer->AddTrack(track, {*audio_config.stream_label});
Artem Titova6a273d2019-02-07 16:43:51 +0100497}
498
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100499void PeerConnectionE2EQualityTest::SetupCall() {
Artem Titova6a273d2019-02-07 16:43:51 +0100500 // Connect peers.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100501 ASSERT_TRUE(alice_->ExchangeOfferAnswerWith(bob_.get()));
Artem Titova6a273d2019-02-07 16:43:51 +0100502 // Do the SDP negotiation, and also exchange ice candidates.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100503 ASSERT_EQ_WAIT(alice_->signaling_state(), PeerConnectionInterface::kStable,
Artem Titova6a273d2019-02-07 16:43:51 +0100504 kDefaultTimeoutMs);
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100505 ASSERT_TRUE_WAIT(alice_->IsIceGatheringDone(), kDefaultTimeoutMs);
506 ASSERT_TRUE_WAIT(bob_->IsIceGatheringDone(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100507
508 // Connect an ICE candidate pairs.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100509 ASSERT_TRUE(bob_->AddIceCandidates(alice_->observer()->GetAllCandidates()));
510 ASSERT_TRUE(alice_->AddIceCandidates(bob_->observer()->GetAllCandidates()));
Artem Titova6a273d2019-02-07 16:43:51 +0100511 // This means that ICE and DTLS are connected.
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100512 ASSERT_TRUE_WAIT(bob_->IsIceConnected(), kDefaultTimeoutMs);
513 ASSERT_TRUE_WAIT(alice_->IsIceConnected(), kDefaultTimeoutMs);
Artem Titova6a273d2019-02-07 16:43:51 +0100514}
515
Artem Titovbf9e01a2019-02-14 10:51:27 +0100516void PeerConnectionE2EQualityTest::StartVideo(
517 const std::vector<
518 rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>& sources) {
519 for (auto& source : sources) {
520 if (source->state() != MediaSourceInterface::SourceState::kLive) {
521 source->Start();
Artem Titova6a273d2019-02-07 16:43:51 +0100522 }
Artem Titova6a273d2019-02-07 16:43:51 +0100523 }
524}
525
526void PeerConnectionE2EQualityTest::TearDownCall() {
Artem Titovbf9e01a2019-02-14 10:51:27 +0100527 for (const auto& video_source : alice_video_sources_) {
528 video_source->Stop();
529 }
530 for (const auto& video_source : bob_video_sources_) {
Artem Titova6a273d2019-02-07 16:43:51 +0100531 video_source->Stop();
532 }
533
534 alice_->pc()->Close();
535 bob_->pc()->Close();
536
537 for (const auto& video_writer : video_writers_) {
538 video_writer->Close();
539 }
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100540
Artem Titovbf9e01a2019-02-14 10:51:27 +0100541 alice_video_sources_.clear();
542 bob_video_sources_.clear();
Mirko Bonadei2bd54a12019-02-13 09:07:55 +0100543 video_writers_.clear();
544 alice_.reset();
545 bob_.reset();
Artem Titova6a273d2019-02-07 16:43:51 +0100546}
547
548VideoFrameWriter* PeerConnectionE2EQualityTest::MaybeCreateVideoWriter(
549 absl::optional<std::string> file_name,
550 const VideoConfig& config) {
551 if (!file_name) {
552 return nullptr;
553 }
554 auto video_writer = absl::make_unique<VideoFrameWriter>(
555 file_name.value(), config.width, config.height, config.fps);
556 VideoFrameWriter* out = video_writer.get();
557 video_writers_.push_back(std::move(video_writer));
558 return out;
559}
560
561} // namespace test
562} // namespace webrtc