Introduce PeerConnectionE2EQualityTestFixture implementation.
Introduce PeerConnectionE2EQualityTestFixture implementation with
example test.
Bug: webrtc:10138
Change-Id: Iec1d135f1b43863a3fa6f0723b579d2b7ff44807
Reviewed-on: https://webrtc-review.googlesource.com/c/120810
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Peter Slatala <psla@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Yves Gerey <yvesg@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26589}
diff --git a/test/DEPS b/test/DEPS
index c7ae24f..712dc6b 100644
--- a/test/DEPS
+++ b/test/DEPS
@@ -54,4 +54,7 @@
"+pc/test/mock_peer_connection_observers.h",
"+p2p/client/basic_port_allocator.h",
],
+ ".*peer_connection_quality_test\.h": [
+ "+pc",
+ ]
}
diff --git a/test/pc/e2e/BUILD.gn b/test/pc/e2e/BUILD.gn
index 994d61f..11293b8 100644
--- a/test/pc/e2e/BUILD.gn
+++ b/test/pc/e2e/BUILD.gn
@@ -22,6 +22,7 @@
]
if (rtc_include_tests) {
deps += [
+ ":peerconnection_quality_test",
":test_peer",
":video_quality_analyzer_injection_helper",
]
@@ -34,6 +35,7 @@
deps = [
":default_encoded_image_id_injector_unittest",
+ ":peer_connection_e2e_smoke_test",
":single_process_encoded_image_id_injector_unittest",
]
}
@@ -195,6 +197,38 @@
}
}
+ rtc_source_set("peerconnection_quality_test") {
+ visibility = [ "*" ]
+ testonly = true
+ sources = [
+ "peer_connection_quality_test.cc",
+ "peer_connection_quality_test.h",
+ ]
+ deps = [
+ ":example_video_quality_analyzer",
+ ":single_process_encoded_image_id_injector",
+ ":test_peer",
+ ":video_quality_analyzer_injection_helper",
+ "../../../api:libjingle_peerconnection_api",
+ "../../../api:scoped_refptr",
+ "../../../api/units:time_delta",
+ "../../../pc:pc_test_utils",
+ "../../../rtc_base:gunit_helpers",
+ "../../../rtc_base:rtc_base",
+ "../../../rtc_base:rtc_base_approved",
+ "../../../system_wrappers:system_wrappers",
+ "../../../test:fileutils",
+ "../../../test:video_test_support",
+ "api:peer_connection_quality_test_fixture_api",
+ "api:video_quality_analyzer_api",
+ "//third_party/abseil-cpp/absl/memory:memory",
+ ]
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+ }
+
rtc_source_set("single_process_encoded_image_id_injector_unittest") {
testonly = true
sources = [
@@ -220,6 +254,47 @@
"../../../test:test_support",
]
}
+
+ rtc_source_set("peer_connection_e2e_smoke_test") {
+ testonly = true
+ sources = [
+ "peer_connection_e2e_smoke_test.cc",
+ ]
+ deps = [
+ ":example_video_quality_analyzer",
+ "../../../api:callfactory_api",
+ "../../../api:libjingle_peerconnection_api",
+ "../../../api:scoped_refptr",
+ "../../../api:simulated_network_api",
+ "../../../api/audio_codecs:builtin_audio_decoder_factory",
+ "../../../api/audio_codecs:builtin_audio_encoder_factory",
+ "../../../api/video_codecs:builtin_video_decoder_factory",
+ "../../../api/video_codecs:builtin_video_encoder_factory",
+ "../../../call:simulated_network",
+ "../../../logging:rtc_event_log_impl_base",
+ "../../../media:rtc_audio_video",
+ "../../../modules/audio_device:audio_device_impl",
+ "../../../p2p:rtc_p2p",
+ "../../../pc:pc_test_utils",
+ "../../../pc:peerconnection_wrapper",
+ "../../../rtc_base:gunit_helpers",
+ "../../../rtc_base:logging",
+ "../../../rtc_base:rtc_base",
+ "../../../rtc_base:rtc_base_tests_utils",
+ "../../../rtc_base:rtc_event",
+ "../../../test:fileutils",
+ "../../../test:test_support",
+ "../../../test/scenario/network:emulated_network",
+ "api:create_peerconnection_quality_test_fixture",
+ "api:peer_connection_quality_test_fixture_api",
+ "//third_party/abseil-cpp/absl/memory:memory",
+ ]
+
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+ }
}
rtc_source_set("example_video_quality_analyzer") {
diff --git a/test/pc/e2e/api/BUILD.gn b/test/pc/e2e/api/BUILD.gn
index 6bb1aa3..8d8c822 100644
--- a/test/pc/e2e/api/BUILD.gn
+++ b/test/pc/e2e/api/BUILD.gn
@@ -49,6 +49,28 @@
"../../../../api/video_codecs:video_codecs_api",
"../../../../logging:rtc_event_log_api",
"../../../../rtc_base:rtc_base",
+ "//third_party/abseil-cpp/absl/memory:memory",
"//third_party/abseil-cpp/absl/types:optional",
]
}
+
+if (rtc_include_tests) {
+ rtc_source_set("create_peerconnection_quality_test_fixture") {
+ visibility = [ "*" ]
+ testonly = true
+ sources = [
+ "create_peerconnection_quality_test_fixture.cc",
+ "create_peerconnection_quality_test_fixture.h",
+ ]
+
+ deps = [
+ ":peer_connection_quality_test_fixture_api",
+ "../:peerconnection_quality_test",
+ "//third_party/abseil-cpp/absl/memory:memory",
+ ]
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+ }
+}
diff --git a/test/pc/e2e/api/create_peerconnection_quality_test_fixture.cc b/test/pc/e2e/api/create_peerconnection_quality_test_fixture.cc
new file mode 100644
index 0000000..0ef36e9
--- /dev/null
+++ b/test/pc/e2e/api/create_peerconnection_quality_test_fixture.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "test/pc/e2e/api/create_peerconnection_quality_test_fixture.h"
+
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "test/pc/e2e/peer_connection_quality_test.h"
+
+namespace webrtc {
+
+std::unique_ptr<PeerConnectionE2EQualityTestFixture>
+CreatePeerConnectionE2EQualityTestFixture(
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::InjectableComponents>
+ alice_components,
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::Params> alice_params,
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::InjectableComponents>
+ bob_components,
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::Params> bob_params,
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::Analyzers> analyzers) {
+ return absl::make_unique<webrtc::test::PeerConnectionE2EQualityTest>(
+ std::move(alice_components), std::move(alice_params),
+ std::move(bob_components), std::move(bob_params), std::move(analyzers));
+}
+
+} // namespace webrtc
diff --git a/test/pc/e2e/api/create_peerconnection_quality_test_fixture.h b/test/pc/e2e/api/create_peerconnection_quality_test_fixture.h
new file mode 100644
index 0000000..a9e768f
--- /dev/null
+++ b/test/pc/e2e/api/create_peerconnection_quality_test_fixture.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef TEST_PC_E2E_API_CREATE_PEERCONNECTION_QUALITY_TEST_FIXTURE_H_
+#define TEST_PC_E2E_API_CREATE_PEERCONNECTION_QUALITY_TEST_FIXTURE_H_
+
+#include <memory>
+
+#include "test/pc/e2e/api/peerconnection_quality_test_fixture.h"
+
+namespace webrtc {
+
+// API is in development. Can be changed/removed without notice.
+// Create test fixture to establish test call between Alice and Bob.
+// During the test Alice will be caller and Bob will answer the call.
+std::unique_ptr<PeerConnectionE2EQualityTestFixture>
+CreatePeerConnectionE2EQualityTestFixture(
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::InjectableComponents>
+ alice_components,
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::Params> alice_params,
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::InjectableComponents>
+ bob_components,
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::Params> bob_params,
+ std::unique_ptr<PeerConnectionE2EQualityTestFixture::Analyzers> analyzers);
+
+} // namespace webrtc
+
+#endif // TEST_PC_E2E_API_CREATE_PEERCONNECTION_QUALITY_TEST_FIXTURE_H_
diff --git a/test/pc/e2e/api/peerconnection_quality_test_fixture.h b/test/pc/e2e/api/peerconnection_quality_test_fixture.h
index 0d85423..adffd0b 100644
--- a/test/pc/e2e/api/peerconnection_quality_test_fixture.h
+++ b/test/pc/e2e/api/peerconnection_quality_test_fixture.h
@@ -14,6 +14,7 @@
#include <string>
#include <vector>
+#include "absl/memory/memory.h"
#include "api/async_resolver_factory.h"
#include "api/call/call_factory_interface.h"
#include "api/fec_controller.h"
@@ -81,7 +82,10 @@
// has a network thread, that will be used to communicate with another peers.
struct InjectableComponents {
explicit InjectableComponents(rtc::Thread* network_thread)
- : network_thread(network_thread) {
+ : network_thread(network_thread),
+ pcf_dependencies(
+ absl::make_unique<PeerConnectionFactoryComponents>()),
+ pc_dependencies(absl::make_unique<PeerConnectionComponents>()) {
RTC_CHECK(network_thread);
}
@@ -104,6 +108,8 @@
std::vector<std::string> slides_yuv_file_names;
};
+ enum VideoGeneratorType { kDefault, kI420A, kI010 };
+
// Contains properties of single video stream.
struct VideoConfig {
size_t width;
@@ -113,17 +119,22 @@
absl::optional<std::string> stream_label;
// Only single from 3 next fields can be specified.
// If specified generator with this name will be used as input.
- absl::optional<std::string> generator_name;
- // If specified this file will be used as input.
+ absl::optional<VideoGeneratorType> generator;
+ // If specified this file will be used as input. Input video will be played
+ // in a circle.
absl::optional<std::string> input_file_name;
// If specified screen share video stream will be created as input.
absl::optional<ScreenShareConfig> screen_share_config;
// If specified the input stream will be also copied to specified file.
+ // It is actually one of the test's output file, which contains copy of what
+ // was captured during the test for this video stream on sender side.
+ // It is useful when generator is used as input.
absl::optional<std::string> input_dump_file_name;
// If specified this file will be used as output on the receiver side for
// this stream. If multiple streams will be produced by input stream,
- // output files will be appended with indexes.
- absl::optional<std::string> output_file_name;
+ // output files will be appended with indexes. The produced files contains
+ // what was rendered for this video stream on receiver side.
+ absl::optional<std::string> output_dump_file_name;
};
// Contains properties for audio in the call.
@@ -138,7 +149,7 @@
// If specified the input stream will be also copied to specified file.
absl::optional<std::string> input_dump_file_name;
// If specified the output stream will be copied to specified file.
- absl::optional<std::string> output_file_name;
+ absl::optional<std::string> output_dump_file_name;
// Audio options to use.
cricket::AudioOptions audio_options;
};
@@ -162,7 +173,16 @@
std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer;
};
- virtual void Run() = 0;
+ // Contains parameters, that describe how long framework should run quality
+ // test.
+ struct RunParams {
+ // Specifies how long the test should be run. This time shows how long
+ // the media should flow after connection was established and before
+ // it will be shut downed.
+ TimeDelta run_duration;
+ };
+
+ virtual void Run(RunParams run_params) = 0;
virtual ~PeerConnectionE2EQualityTestFixture() = default;
};
diff --git a/test/pc/e2e/peer_connection_e2e_smoke_test.cc b/test/pc/e2e/peer_connection_e2e_smoke_test.cc
new file mode 100644
index 0000000..b922d73
--- /dev/null
+++ b/test/pc/e2e/peer_connection_e2e_smoke_test.cc
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <cstdint>
+#include <memory>
+
+#include "absl/memory/memory.h"
+#include "call/simulated_network.h"
+#include "rtc_base/async_invoker.h"
+#include "rtc_base/fake_network.h"
+#include "test/gtest.h"
+#include "test/pc/e2e/analyzer/video/example_video_quality_analyzer.h"
+#include "test/pc/e2e/api/create_peerconnection_quality_test_fixture.h"
+#include "test/pc/e2e/api/peerconnection_quality_test_fixture.h"
+#include "test/scenario/network/network_emulation.h"
+#include "test/scenario/network/network_emulation_manager.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+std::unique_ptr<rtc::NetworkManager> CreateFakeNetworkManager(
+ std::vector<EndpointNode*> endpoints) {
+ auto network_manager = absl::make_unique<rtc::FakeNetworkManager>();
+ for (auto* endpoint : endpoints) {
+ network_manager->AddInterface(
+ rtc::SocketAddress(endpoint->GetPeerLocalAddress(), /*port=*/0));
+ }
+ return network_manager;
+}
+
+} // namespace
+
+TEST(PeerConnectionE2EQualityTestSmokeTest, RunWithEmulatedNetwork) {
+ using Params = PeerConnectionE2EQualityTestFixture::Params;
+ using RunParams = PeerConnectionE2EQualityTestFixture::RunParams;
+ using VideoGeneratorType =
+ PeerConnectionE2EQualityTestFixture::VideoGeneratorType;
+ using Analyzers = PeerConnectionE2EQualityTestFixture::Analyzers;
+ using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
+ using AudioConfig = PeerConnectionE2EQualityTestFixture::AudioConfig;
+ using InjectableComponents =
+ PeerConnectionE2EQualityTestFixture::InjectableComponents;
+
+ auto alice_params = absl::make_unique<Params>();
+ VideoConfig alice_video_config;
+ alice_video_config.width = 1280;
+ alice_video_config.height = 720;
+ alice_video_config.fps = 30;
+ alice_video_config.stream_label = "alice-video";
+ alice_video_config.generator = VideoGeneratorType::kDefault;
+
+ alice_params->video_configs.push_back(alice_video_config);
+ alice_params->audio_config = AudioConfig{
+ AudioConfig::Mode::kGenerated,
+ /*input_file_name=*/absl::nullopt,
+ /*input_dump_file_name=*/absl::nullopt,
+ /*output_dump_file_name=*/absl::nullopt, cricket::AudioOptions()};
+
+ // Setup emulated network
+ NetworkEmulationManager network_emulation_manager(Clock::GetRealTimeClock());
+
+ EmulatedNetworkNode* alice_node =
+ network_emulation_manager.CreateEmulatedNode(
+ absl::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedNetworkNode* bob_node = network_emulation_manager.CreateEmulatedNode(
+ absl::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EndpointNode* alice_endpoint =
+ network_emulation_manager.CreateEndpoint(rtc::IPAddress(1));
+ EndpointNode* bob_endpoint =
+ network_emulation_manager.CreateEndpoint(rtc::IPAddress(2));
+ network_emulation_manager.CreateRoute(alice_endpoint, {alice_node},
+ bob_endpoint);
+ network_emulation_manager.CreateRoute(bob_endpoint, {bob_node},
+ alice_endpoint);
+
+ rtc::Thread* alice_network_thread =
+ network_emulation_manager.CreateNetworkThread({alice_endpoint});
+ rtc::Thread* bob_network_thread =
+ network_emulation_manager.CreateNetworkThread({bob_endpoint});
+
+ // Setup components. We need to provide rtc::NetworkManager compatible with
+ // emulated network layer.
+ auto alice_components =
+ absl::make_unique<InjectableComponents>(alice_network_thread);
+ alice_components->pc_dependencies->network_manager =
+ CreateFakeNetworkManager({alice_endpoint});
+ auto bob_components =
+ absl::make_unique<InjectableComponents>(bob_network_thread);
+ bob_components->pc_dependencies->network_manager =
+ CreateFakeNetworkManager({bob_endpoint});
+
+ // Create analyzers.
+ auto analyzers = absl::make_unique<Analyzers>();
+ analyzers->video_quality_analyzer =
+ absl::make_unique<ExampleVideoQualityAnalyzer>();
+ auto* video_analyzer = static_cast<ExampleVideoQualityAnalyzer*>(
+ analyzers->video_quality_analyzer.get());
+
+ network_emulation_manager.Start();
+
+ auto fixture = CreatePeerConnectionE2EQualityTestFixture(
+ std::move(alice_components), std::move(alice_params),
+ std::move(bob_components), absl::make_unique<Params>(),
+ std::move(analyzers));
+ fixture->Run(RunParams{TimeDelta::seconds(5)});
+
+ network_emulation_manager.Stop();
+
+ RTC_LOG(INFO) << "Captured: " << video_analyzer->frames_captured();
+ RTC_LOG(INFO) << "Sent : " << video_analyzer->frames_sent();
+ RTC_LOG(INFO) << "Received: " << video_analyzer->frames_received();
+ RTC_LOG(INFO) << "Rendered: " << video_analyzer->frames_rendered();
+ RTC_LOG(INFO) << "Dropped : " << video_analyzer->frames_dropped();
+
+ // 150 = 30fps * 5s
+ EXPECT_NEAR(video_analyzer->frames_captured(), 150, 15);
+ EXPECT_NEAR(video_analyzer->frames_sent(), 150, 15);
+ EXPECT_NEAR(video_analyzer->frames_received(), 150, 15);
+ EXPECT_NEAR(video_analyzer->frames_rendered(), 150, 15);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/test/pc/e2e/peer_connection_quality_test.cc b/test/pc/e2e/peer_connection_quality_test.cc
new file mode 100644
index 0000000..e356e59
--- /dev/null
+++ b/test/pc/e2e/peer_connection_quality_test.cc
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "test/pc/e2e/peer_connection_quality_test.h"
+
+#include <set>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "api/peer_connection_interface.h"
+#include "api/scoped_refptr.h"
+#include "api/units/time_delta.h"
+#include "rtc_base/bind.h"
+#include "rtc_base/gunit.h"
+#include "test/pc/e2e/analyzer/video/example_video_quality_analyzer.h"
+#include "test/pc/e2e/api/video_quality_analyzer_interface.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
+
+constexpr int kDefaultTimeoutMs = 10000;
+constexpr char kSignalThreadName[] = "signaling_thread";
+
+std::string VideoConfigSourcePresenceToString(const VideoConfig& video_config) {
+ char buf[1024];
+ rtc::SimpleStringBuilder builder(buf);
+ builder << "video_config.generator=" << video_config.generator.has_value()
+ << "; video_config.input_file_name="
+ << video_config.input_file_name.has_value()
+ << "; video_config.screen_share_config="
+ << video_config.screen_share_config.has_value() << ";";
+ return builder.str();
+}
+
+} // namespace
+
+PeerConnectionE2EQualityTest::PeerConnectionE2EQualityTest(
+ std::unique_ptr<InjectableComponents> alice_components,
+ std::unique_ptr<Params> alice_params,
+ std::unique_ptr<InjectableComponents> bob_components,
+ std::unique_ptr<Params> bob_params,
+ std::unique_ptr<Analyzers> analyzers)
+ : clock_(Clock::GetRealTimeClock()),
+ signaling_thread_(rtc::Thread::Create()) {
+ RTC_CHECK(alice_components);
+ RTC_CHECK(alice_params);
+ RTC_CHECK(bob_components);
+ RTC_CHECK(bob_params);
+ RTC_CHECK(analyzers);
+
+ // Print test summary
+ RTC_LOG(INFO)
+ << "Media quality test: Alice will make a call to Bob with media video="
+ << !alice_params->video_configs.empty()
+ << "; audio=" << alice_params->audio_config.has_value()
+ << ". Bob will respond with media video="
+ << !bob_params->video_configs.empty()
+ << "; audio=" << bob_params->audio_config.has_value();
+
+ // Check that at least Alice or Bob has at least one media stream.
+ RTC_CHECK(!alice_params->video_configs.empty() ||
+ alice_params->audio_config || !bob_params->video_configs.empty() ||
+ bob_params->audio_config)
+ << "No media in the call";
+
+ signaling_thread_->SetName(kSignalThreadName, nullptr);
+ signaling_thread_->Start();
+
+ // Create default video quality analyzer. We will always create an analyzer,
+ // even if there are no video streams, because it will be installed into video
+ // encoder/decoder factories.
+ if (analyzers->video_quality_analyzer == nullptr) {
+ analyzers->video_quality_analyzer =
+ absl::make_unique<ExampleVideoQualityAnalyzer>();
+ }
+ encoded_image_id_controller_ =
+ absl::make_unique<SingleProcessEncodedImageIdInjector>();
+ video_quality_analyzer_injection_helper_ =
+ absl::make_unique<VideoQualityAnalyzerInjectionHelper>(
+ std::move(analyzers->video_quality_analyzer),
+ encoded_image_id_controller_.get(),
+ encoded_image_id_controller_.get());
+
+ // Create call participants: Alice and Bob.
+ // Audio streams are intercepted in AudioDeviceModule, so if it is required to
+ // catch output of Alice's stream, Alice's output_dump_file_name should be
+ // passed to Bob's TestPeer setup as audio output file name.
+ absl::optional<std::string> alice_audio_output_dump_file_name =
+ bob_params->audio_config ? bob_params->audio_config->output_dump_file_name
+ : absl::nullopt;
+ absl::optional<std::string> bob_audio_output_dump_file_name =
+ alice_params->audio_config
+ ? alice_params->audio_config->output_dump_file_name
+ : absl::nullopt;
+ alice_ = TestPeer::CreateTestPeer(
+ std::move(alice_components), std::move(alice_params),
+ video_quality_analyzer_injection_helper_.get(), signaling_thread_.get(),
+ alice_audio_output_dump_file_name);
+ bob_ = TestPeer::CreateTestPeer(
+ std::move(bob_components), std::move(bob_params),
+ video_quality_analyzer_injection_helper_.get(), signaling_thread_.get(),
+ bob_audio_output_dump_file_name);
+}
+
+void PeerConnectionE2EQualityTest::Run(RunParams run_params) {
+ SetMissedVideoStreamLabels({alice_->params(), bob_->params()});
+ ValidateParams({alice_->params(), bob_->params()});
+ signaling_thread_->Invoke<void>(
+ RTC_FROM_HERE,
+ rtc::Bind(&PeerConnectionE2EQualityTest::RunOnSignalingThread, this,
+ run_params));
+}
+
+void PeerConnectionE2EQualityTest::SetMissedVideoStreamLabels(
+ std::vector<Params*> params) {
+ int counter = 0;
+ std::set<std::string> video_labels;
+ for (auto* p : params) {
+ for (auto& video_config : p->video_configs) {
+ if (!video_config.stream_label) {
+ std::string label;
+ do {
+ label = "_auto_video_stream_label_" + std::to_string(counter);
+ ++counter;
+ } while (!video_labels.insert(label).second);
+ video_config.stream_label = label;
+ }
+ }
+ }
+}
+
+void PeerConnectionE2EQualityTest::ValidateParams(std::vector<Params*> params) {
+ std::set<std::string> video_labels;
+ for (Params* p : params) {
+ // Validate that each video config has exactly one of |generator|,
+ // |input_file_name| or |screen_share_config| set. Also validate that all
+ // video stream labels are unique.
+ for (auto& video_config : p->video_configs) {
+ RTC_CHECK(video_config.stream_label);
+ bool inserted =
+ video_labels.insert(video_config.stream_label.value()).second;
+ RTC_CHECK(inserted) << "Duplicate video_config.stream_label="
+ << video_config.stream_label.value();
+ RTC_CHECK(video_config.generator || video_config.input_file_name ||
+ video_config.screen_share_config)
+ << VideoConfigSourcePresenceToString(video_config);
+ RTC_CHECK(!(video_config.input_file_name && video_config.generator))
+ << VideoConfigSourcePresenceToString(video_config);
+ RTC_CHECK(
+ !(video_config.input_file_name && video_config.screen_share_config))
+ << VideoConfigSourcePresenceToString(video_config);
+ RTC_CHECK(!(video_config.screen_share_config && video_config.generator))
+ << VideoConfigSourcePresenceToString(video_config);
+ }
+ if (p->audio_config) {
+ // Check that if mode input file name specified only if mode is kFile.
+ if (p->audio_config.value().mode == AudioConfig::Mode::kGenerated) {
+ RTC_CHECK(!p->audio_config.value().input_file_name);
+ }
+ if (p->audio_config.value().mode == AudioConfig::Mode::kFile) {
+ RTC_CHECK(p->audio_config.value().input_file_name);
+ RTC_CHECK(FileExists(p->audio_config.value().input_file_name.value()));
+ }
+ }
+ }
+}
+
+void PeerConnectionE2EQualityTest::RunOnSignalingThread(RunParams run_params) {
+ AddMedia(alice_.get());
+ AddMedia(bob_.get());
+
+ SetupCall(alice_.get(), bob_.get());
+
+ WaitForTransceiversSetup(alice_->params(), bob_.get());
+ WaitForTransceiversSetup(bob_->params(), alice_.get());
+ SetupVideoSink(alice_->params(), bob_.get());
+ SetupVideoSink(bob_->params(), alice_.get());
+
+ StartVideo();
+
+ rtc::Event done;
+ done.Wait(static_cast<int>(run_params.run_duration.ms()));
+
+ TearDownCall();
+ video_quality_analyzer_injection_helper_->Stop();
+}
+
+void PeerConnectionE2EQualityTest::AddMedia(TestPeer* peer) {
+ AddVideo(peer);
+ if (peer->params()->audio_config) {
+ AddAudio(peer);
+ }
+}
+
+void PeerConnectionE2EQualityTest::AddVideo(TestPeer* peer) {
+ // Params here valid because of pre-run validation.
+ Params* params = peer->params();
+ for (auto video_config : params->video_configs) {
+ // Create video generator.
+ std::unique_ptr<FrameGenerator> frame_generator =
+ CreateFrameGenerator(video_config);
+
+ // Wrap it to inject video quality analyzer and enable dump of input video
+ // if required.
+ VideoFrameWriter* writer =
+ MaybeCreateVideoWriter(video_config.input_dump_file_name, video_config);
+ frame_generator =
+ video_quality_analyzer_injection_helper_->WrapFrameGenerator(
+ video_config.stream_label.value(), std::move(frame_generator),
+ writer);
+
+ // Setup FrameGenerator into peer connection.
+ std::unique_ptr<FrameGeneratorCapturer> capturer =
+ absl::WrapUnique(FrameGeneratorCapturer::Create(
+ std::move(frame_generator), video_config.fps, clock_));
+ rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource> source =
+ new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
+ move(capturer));
+ video_sources_.push_back(source);
+ RTC_LOG(INFO) << "Adding video with video_config.stream_label="
+ << video_config.stream_label.value();
+ rtc::scoped_refptr<VideoTrackInterface> track =
+ peer->pc_factory()->CreateVideoTrack(video_config.stream_label.value(),
+ source);
+ peer->AddTransceiver(track);
+ }
+}
+
+std::unique_ptr<FrameGenerator>
+PeerConnectionE2EQualityTest::CreateFrameGenerator(
+ const VideoConfig& video_config) {
+ if (video_config.generator) {
+ absl::optional<FrameGenerator::OutputType> frame_generator_type =
+ absl::nullopt;
+ if (video_config.generator == VideoGeneratorType::kDefault) {
+ frame_generator_type = FrameGenerator::OutputType::I420;
+ } else if (video_config.generator == VideoGeneratorType::kI420A) {
+ frame_generator_type = FrameGenerator::OutputType::I420A;
+ } else if (video_config.generator == VideoGeneratorType::kI010) {
+ frame_generator_type = FrameGenerator::OutputType::I010;
+ }
+ return FrameGenerator::CreateSquareGenerator(
+ static_cast<int>(video_config.width),
+ static_cast<int>(video_config.height), frame_generator_type,
+ absl::nullopt);
+ }
+ if (video_config.input_file_name) {
+ return FrameGenerator::CreateFromYuvFile(
+ std::vector<std::string>(/*count=*/1,
+ video_config.input_file_name.value()),
+ video_config.width, video_config.height, /*frame_repeat_count=*/1);
+ }
+ if (video_config.screen_share_config) {
+ // TODO(titovartem) implement screen share support
+ // (http://bugs.webrtc.org/10138)
+ RTC_NOTREACHED() << "Screen share is not implemented";
+ return nullptr;
+ }
+ RTC_NOTREACHED() << "Unsupported video_config input source";
+ return nullptr;
+}
+
+void PeerConnectionE2EQualityTest::AddAudio(TestPeer* peer) {
+ RTC_CHECK(peer->params()->audio_config);
+ rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
+ peer->pc_factory()->CreateAudioSource(
+ peer->params()->audio_config->audio_options);
+ rtc::scoped_refptr<AudioTrackInterface> track =
+ peer->pc_factory()->CreateAudioTrack("audio", source);
+ peer->AddTransceiver(track);
+}
+
+void PeerConnectionE2EQualityTest::SetupCall(TestPeer* alice, TestPeer* bob) {
+ // Connect peers.
+ ASSERT_TRUE(alice->ExchangeOfferAnswerWith(bob));
+ // Do the SDP negotiation, and also exchange ice candidates.
+ ASSERT_EQ_WAIT(alice->signaling_state(), PeerConnectionInterface::kStable,
+ kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(alice->IsIceGatheringDone(), kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(bob->IsIceGatheringDone(), kDefaultTimeoutMs);
+
+ // Connect an ICE candidate pairs.
+ ASSERT_TRUE(bob->AddIceCandidates(alice->observer()->GetAllCandidates()));
+ ASSERT_TRUE(alice->AddIceCandidates(bob->observer()->GetAllCandidates()));
+ // This means that ICE and DTLS are connected.
+ ASSERT_TRUE_WAIT(bob->IsIceConnected(), kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(alice->IsIceConnected(), kDefaultTimeoutMs);
+}
+
+void PeerConnectionE2EQualityTest::WaitForTransceiversSetup(
+ Params* params,
+ TestPeer* remote_peer) {
+ uint64_t expected_remote_transceivers =
+ params->video_configs.size() + (params->audio_config ? 1 : 0);
+ ASSERT_EQ_WAIT(remote_peer->observer()->on_track_transceivers_.size(),
+ expected_remote_transceivers, kDefaultTimeoutMs);
+}
+
+void PeerConnectionE2EQualityTest::SetupVideoSink(Params* params,
+ TestPeer* remote_peer) {
+ if (params->video_configs.empty()) {
+ return;
+ }
+ std::map<std::string, VideoConfig*> video_configs_by_label;
+ for (auto& video_config : params->video_configs) {
+ video_configs_by_label.insert(std::pair<std::string, VideoConfig*>(
+ video_config.stream_label.value(), &video_config));
+ }
+
+ for (const auto& transceiver :
+ remote_peer->observer()->on_track_transceivers_) {
+ const rtc::scoped_refptr<MediaStreamTrackInterface>& track =
+ transceiver->receiver()->track();
+ if (track->kind() != MediaStreamTrackInterface::kVideoKind) {
+ continue;
+ }
+
+ auto it = video_configs_by_label.find(track->id());
+ RTC_CHECK(it != video_configs_by_label.end());
+ VideoConfig* video_config = it->second;
+
+ VideoFrameWriter* writer = MaybeCreateVideoWriter(
+ video_config->output_dump_file_name, *video_config);
+ // It is safe to cast here, because it is checked above that track->kind()
+ // is kVideoKind.
+ auto* video_track = static_cast<VideoTrackInterface*>(track.get());
+ std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> video_sink =
+ video_quality_analyzer_injection_helper_->CreateVideoSink(writer);
+ video_track->AddOrUpdateSink(video_sink.get(), rtc::VideoSinkWants());
+ output_video_sinks_.push_back(std::move(video_sink));
+ }
+}
+
+void PeerConnectionE2EQualityTest::StartVideo() {
+ for (const auto& video_source : video_sources_) {
+ video_source->Start();
+ }
+}
+
+void PeerConnectionE2EQualityTest::TearDownCall() {
+ for (const auto& video_source : video_sources_) {
+ video_source->Stop();
+ }
+
+ alice_->pc()->Close();
+ bob_->pc()->Close();
+
+ for (const auto& video_writer : video_writers_) {
+ video_writer->Close();
+ }
+}
+
+VideoFrameWriter* PeerConnectionE2EQualityTest::MaybeCreateVideoWriter(
+ absl::optional<std::string> file_name,
+ const VideoConfig& config) {
+ if (!file_name) {
+ return nullptr;
+ }
+ auto video_writer = absl::make_unique<VideoFrameWriter>(
+ file_name.value(), config.width, config.height, config.fps);
+ VideoFrameWriter* out = video_writer.get();
+ video_writers_.push_back(std::move(video_writer));
+ return out;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/test/pc/e2e/peer_connection_quality_test.h b/test/pc/e2e/peer_connection_quality_test.h
new file mode 100644
index 0000000..a1908af
--- /dev/null
+++ b/test/pc/e2e/peer_connection_quality_test.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef TEST_PC_E2E_PEER_CONNECTION_QUALITY_TEST_H_
+#define TEST_PC_E2E_PEER_CONNECTION_QUALITY_TEST_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "pc/test/frame_generator_capturer_video_track_source.h"
+#include "rtc_base/thread.h"
+#include "system_wrappers/include/clock.h"
+#include "test/pc/e2e/analyzer/video/single_process_encoded_image_id_injector.h"
+#include "test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h"
+#include "test/pc/e2e/api/peerconnection_quality_test_fixture.h"
+#include "test/pc/e2e/test_peer.h"
+#include "test/testsupport/video_frame_writer.h"
+
+namespace webrtc {
+namespace test {
+
+class PeerConnectionE2EQualityTest
+ : public PeerConnectionE2EQualityTestFixture {
+ public:
+ using Params = PeerConnectionE2EQualityTestFixture::Params;
+ using Analyzers = PeerConnectionE2EQualityTestFixture::Analyzers;
+ using InjectableComponents =
+ PeerConnectionE2EQualityTestFixture::InjectableComponents;
+ using VideoGeneratorType =
+ PeerConnectionE2EQualityTestFixture::VideoGeneratorType;
+ using RunParams = PeerConnectionE2EQualityTestFixture::RunParams;
+ using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
+
+ PeerConnectionE2EQualityTest(
+ std::unique_ptr<InjectableComponents> alice_components,
+ std::unique_ptr<Params> alice_params,
+ std::unique_ptr<InjectableComponents> bob_components,
+ std::unique_ptr<Params> bob_params,
+ std::unique_ptr<Analyzers> analyzers);
+
+ ~PeerConnectionE2EQualityTest() override = default;
+
+ void Run(RunParams run_params) override;
+
+ private:
+ // Sets video stream labels that are not specified in VideoConfigs to unique
+ // generated values.
+ void SetMissedVideoStreamLabels(std::vector<Params*> params);
+ // Validate peer's parameters, also ensure uniqueness of all video stream
+ // labels.
+ void ValidateParams(std::vector<Params*> params);
+ // Have to be run on the signaling thread.
+ void RunOnSignalingThread(RunParams run_params);
+ void AddMedia(TestPeer* peer);
+ void AddVideo(TestPeer* peer);
+ std::unique_ptr<FrameGenerator> CreateFrameGenerator(
+ const VideoConfig& video_config);
+ void AddAudio(TestPeer* peer);
+ void SetupCall(TestPeer* alice, TestPeer* bob);
+ void WaitForTransceiversSetup(Params* params, TestPeer* remote_peer);
+ void SetupVideoSink(Params* params, TestPeer* remote_peer);
+ void StartVideo();
+ void TearDownCall();
+ VideoFrameWriter* MaybeCreateVideoWriter(
+ absl::optional<std::string> file_name,
+ const VideoConfig& config);
+
+ Clock* const clock_;
+ std::unique_ptr<VideoQualityAnalyzerInjectionHelper>
+ video_quality_analyzer_injection_helper_;
+ std::unique_ptr<SingleProcessEncodedImageIdInjector>
+ encoded_image_id_controller_;
+ const std::unique_ptr<rtc::Thread> signaling_thread_;
+
+ std::unique_ptr<TestPeer> alice_;
+ std::unique_ptr<TestPeer> bob_;
+
+ std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
+ video_sources_;
+ std::vector<std::unique_ptr<VideoFrameWriter>> video_writers_;
+ std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>
+ output_video_sinks_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_PC_E2E_PEER_CONNECTION_QUALITY_TEST_H_
diff --git a/test/pc/e2e/test_peer.cc b/test/pc/e2e/test_peer.cc
index 62bbc1d..8c46206 100644
--- a/test/pc/e2e/test_peer.cc
+++ b/test/pc/e2e/test_peer.cc
@@ -77,14 +77,14 @@
if (audio_config.mode == AudioConfig::Mode::kGenerated) {
return TestAudioDeviceModule::CreatePulsedNoiseCapturer(
kGeneratedAudioMaxAmplitude, kSamplingFrequencyInHz);
- } else if (audio_config.mode == AudioConfig::Mode::kFile) {
+ }
+ if (audio_config.mode == AudioConfig::Mode::kFile) {
RTC_DCHECK(audio_config.input_file_name);
return TestAudioDeviceModule::CreateWavFileReader(
audio_config.input_file_name.value());
- } else {
- RTC_NOTREACHED() << "Unknown audio_config->mode";
- return nullptr;
}
+ RTC_NOTREACHED() << "Unknown audio_config->mode";
+ return nullptr;
}
rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModule(
@@ -176,12 +176,10 @@
VideoQualityAnalyzerInjectionHelper* video_analyzer_helper,
rtc::Thread* network_thread,
rtc::Thread* signaling_thread,
- rtc::Thread* worker_thread,
absl::optional<std::string> audio_output_file_name) {
PeerConnectionFactoryDependencies pcf_deps;
pcf_deps.network_thread = network_thread;
pcf_deps.signaling_thread = signaling_thread;
- pcf_deps.worker_thread = worker_thread;
pcf_deps.media_engine = CreateMediaEngine(
pcf_dependencies.get(), std::move(audio_config), video_analyzer_helper,
std::move(audio_output_file_name));
@@ -217,8 +215,7 @@
// until the end of the test.
if (pc_dependencies->network_manager == nullptr) {
pc_dependencies->network_manager =
- // TODO(titovartem) have network manager integrated with emulated
- // network layer.
+ // Use real network (on the loopback interface)
absl::make_unique<rtc::BasicNetworkManager>();
}
auto port_allocator = absl::make_unique<cricket::BasicPortAllocator>(
@@ -250,7 +247,6 @@
std::unique_ptr<Params> params,
VideoQualityAnalyzerInjectionHelper* video_analyzer_helper,
rtc::Thread* signaling_thread,
- rtc::Thread* worker_thread,
absl::optional<std::string> audio_output_file_name) {
RTC_DCHECK(components);
RTC_DCHECK(params);
@@ -264,7 +260,7 @@
PeerConnectionFactoryDependencies pcf_deps = CreatePCFDependencies(
std::move(components->pcf_dependencies), params->audio_config,
video_analyzer_helper, components->network_thread, signaling_thread,
- worker_thread, std::move(audio_output_file_name));
+ std::move(audio_output_file_name));
rtc::scoped_refptr<PeerConnectionFactoryInterface> pcf =
CreateModularPeerConnectionFactory(std::move(pcf_deps));
@@ -284,6 +280,11 @@
bool success = true;
for (const auto* candidate : candidates) {
if (!pc()->AddIceCandidate(candidate)) {
+ std::string candidate_str;
+ bool res = candidate->ToString(&candidate_str);
+ RTC_CHECK(res);
+ RTC_LOG(LS_ERROR) << "Failed to add ICE candidate, candidate_str="
+ << candidate_str;
success = false;
}
}
@@ -296,8 +297,8 @@
std::unique_ptr<MockPeerConnectionObserver> observer,
std::unique_ptr<Params> params,
std::unique_ptr<rtc::NetworkManager> network_manager)
- : PeerConnectionWrapper::PeerConnectionWrapper(pc_factory,
- pc,
+ : PeerConnectionWrapper::PeerConnectionWrapper(std::move(pc_factory),
+ std::move(pc),
std::move(observer)),
params_(std::move(params)),
network_manager_(std::move(network_manager)) {}
diff --git a/test/pc/e2e/test_peer.h b/test/pc/e2e/test_peer.h
index afd6f0b..f6e878d 100644
--- a/test/pc/e2e/test_peer.h
+++ b/test/pc/e2e/test_peer.h
@@ -44,8 +44,6 @@
// also will setup dependencies, that are required for media analyzers
// injection.
//
- // We require |worker_thread| here, because TestPeer can't own worker thread,
- // because in such case it will be destroyed before peer connection.
// |signaling_thread| will be provided by test fixture implementation.
// |params| - describes current peer paramters, like current peer video
// streams and audio streams
@@ -57,7 +55,6 @@
std::unique_ptr<Params> params,
VideoQualityAnalyzerInjectionHelper* video_analyzer_helper,
rtc::Thread* signaling_thread,
- rtc::Thread* worker_thread,
absl::optional<std::string> audio_output_file_name);
Params* params() const { return params_.get(); }