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}
This commit is contained in:
Artem Titov
2019-02-07 16:43:51 +01:00
committed by Commit Bot
parent c363a53587
commit a6a273db11
11 changed files with 812 additions and 22 deletions

View File

@ -54,4 +54,7 @@ specific_include_rules = {
"+pc/test/mock_peer_connection_observers.h",
"+p2p/client/basic_port_allocator.h",
],
".*peer_connection_quality_test\.h": [
"+pc",
]
}

View File

@ -22,6 +22,7 @@ group("e2e") {
]
if (rtc_include_tests) {
deps += [
":peerconnection_quality_test",
":test_peer",
":video_quality_analyzer_injection_helper",
]
@ -34,6 +35,7 @@ if (rtc_include_tests) {
deps = [
":default_encoded_image_id_injector_unittest",
":peer_connection_e2e_smoke_test",
":single_process_encoded_image_id_injector_unittest",
]
}
@ -195,6 +197,38 @@ if (rtc_include_tests) {
}
}
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 @@ if (rtc_include_tests) {
"../../../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") {

View File

@ -49,6 +49,28 @@ rtc_source_set("peer_connection_quality_test_fixture_api") {
"../../../../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" ]
}
}
}

View File

@ -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

View File

@ -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_

View File

@ -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 @@ class PeerConnectionE2EQualityTestFixture {
// 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 @@ class PeerConnectionE2EQualityTestFixture {
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 @@ class PeerConnectionE2EQualityTestFixture {
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 @@ class PeerConnectionE2EQualityTestFixture {
// 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 @@ class PeerConnectionE2EQualityTestFixture {
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;
};

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -77,15 +77,15 @@ std::unique_ptr<TestAudioDeviceModule::Capturer> CreateAudioCapturer(
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::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModule(
absl::optional<AudioConfig> audio_config,
@ -176,12 +176,10 @@ PeerConnectionFactoryDependencies CreatePCFDependencies(
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 @@ PeerConnectionDependencies CreatePCDependencies(
// 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<TestPeer> TestPeer::CreateTestPeer(
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 @@ std::unique_ptr<TestPeer> TestPeer::CreateTestPeer(
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 TestPeer::AddIceCandidates(
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 @@ TestPeer::TestPeer(
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)) {}

View File

@ -44,8 +44,6 @@ class TestPeer final : public PeerConnectionWrapper {
// 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 @@ class TestPeer final : public PeerConnectionWrapper {
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(); }