diff --git a/api/BUILD.gn b/api/BUILD.gn index 1c6c952acf..b97118b575 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -365,6 +365,7 @@ rtc_source_set("peer_connection_quality_test_fixture_api") { "transport:network_control", "transport/media:media_transport_interface", "units:time_delta", + "video:video_frame", "video_codecs:video_codecs_api", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/types:optional", diff --git a/api/test/peerconnection_quality_test_fixture.h b/api/test/peerconnection_quality_test_fixture.h index 6ef4dfa38b..d0cdeab9d5 100644 --- a/api/test/peerconnection_quality_test_fixture.h +++ b/api/test/peerconnection_quality_test_fixture.h @@ -31,6 +31,7 @@ #include "api/transport/media/media_transport_interface.h" #include "api/transport/network_control.h" #include "api/units/time_delta.h" +#include "api/video/video_source_interface.h" #include "api/video_codecs/video_decoder_factory.h" #include "api/video_codecs/video_encoder.h" #include "api/video_codecs/video_encoder_factory.h" @@ -167,10 +168,15 @@ class PeerConnectionE2EQualityTestFixture { // Have to be unique among all specified configs for all peers in the call. // Will be auto generated if omitted. absl::optional stream_label; - // Only 1 from |generator|, |input_file_name|, |screen_share_config| and - // |capturing_device_index| can be specified. If none of them are specified, - // then |generator| will be set to VideoGeneratorType::kDefault. If - // specified generator of this type will be used to produce input video. + // You can specify one of |generator|, |input_file_name|, + // |screen_share_config| and |capturing_device_index|. + // If none of them are specified: + // * If config is added to the PeerConfigurer without specifying any video + // source, then |generator| will be set to VideoGeneratorType::kDefault. + // * If config is added with own video source implementation, then that + // video source will be used. + + // If specified generator of this type will be used to produce input video. absl::optional generator; // If specified this file will be used as input. Input video will be played // in a circle. @@ -285,6 +291,11 @@ class PeerConnectionE2EQualityTestFixture { // Add new video stream to the call that will be sent from this peer. virtual PeerConfigurer* AddVideoConfig(VideoConfig config) = 0; + // Add new video stream to the call that will be sent from this peer with + // provided own implementation of video frames source. + virtual PeerConfigurer* AddVideoConfig( + VideoConfig config, + std::unique_ptr> source) = 0; // Set the audio stream for the call from this peer. If this method won't // be invoked, this peer will send no audio. virtual PeerConfigurer* SetAudioConfig(AudioConfig config) = 0; diff --git a/test/pc/e2e/BUILD.gn b/test/pc/e2e/BUILD.gn index 5a99442be4..ae6bc7404f 100644 --- a/test/pc/e2e/BUILD.gn +++ b/test/pc/e2e/BUILD.gn @@ -177,6 +177,19 @@ rtc_library("quality_analyzing_video_encoder") { ] } +rtc_library("video_source_based_video_capturer") { + visibility = [ "*" ] + testonly = true + sources = [ + "video_source_based_video_capturer.cc", + "video_source_based_video_capturer.h", + ] + deps = [ + "../..:video_test_common", + "../../../api/video:video_frame", + ] +} + if (rtc_include_tests) { rtc_library("video_quality_analyzer_injection_helper") { visibility = [ "*" ] @@ -274,6 +287,7 @@ if (rtc_include_tests) { ":stats_poller", ":test_peer", ":video_quality_analyzer_injection_helper", + ":video_source_based_video_capturer", "../..:field_trial", "../..:platform_video_capturer", "../..:video_test_common", @@ -289,6 +303,7 @@ if (rtc_include_tests) { "../../../api/task_queue:default_task_queue_factory", "../../../api/units:time_delta", "../../../api/units:timestamp", + "../../../api/video:video_frame", "../../../pc:pc_test_utils", "../../../pc:peerconnection", "../../../rtc_base", diff --git a/test/pc/e2e/peer_connection_quality_test.cc b/test/pc/e2e/peer_connection_quality_test.cc index d94d979601..9dba0f549b 100644 --- a/test/pc/e2e/peer_connection_quality_test.cc +++ b/test/pc/e2e/peer_connection_quality_test.cc @@ -23,6 +23,7 @@ #include "api/task_queue/default_task_queue_factory.h" #include "api/test/video_quality_analyzer_interface.h" #include "api/units/time_delta.h" +#include "api/video/video_source_interface.h" #include "pc/sdp_utils.h" #include "pc/test/mock_peer_connection_observers.h" #include "rtc_base/bind.h" @@ -34,6 +35,7 @@ #include "test/pc/e2e/analyzer/audio/default_audio_quality_analyzer.h" #include "test/pc/e2e/analyzer/video/default_video_quality_analyzer.h" #include "test/pc/e2e/stats_poller.h" +#include "test/pc/e2e/video_source_based_video_capturer.h" #include "test/platform_video_capturer.h" #include "test/testsupport/file_utils.h" @@ -233,13 +235,19 @@ void PeerConnectionE2EQualityTest::Run(RunParams run_params) { peer_configurations_[0]->ReleaseParams(); std::unique_ptr alice_components = peer_configurations_[0]->ReleaseComponents(); + std::vector>> + alice_video_sources = peer_configurations_[0]->ReleaseVideoSources(); std::unique_ptr bob_params = peer_configurations_[1]->ReleaseParams(); std::unique_ptr bob_components = peer_configurations_[1]->ReleaseComponents(); + std::vector>> + bob_video_sources = peer_configurations_[1]->ReleaseVideoSources(); peer_configurations_.clear(); - SetDefaultValuesForMissingParams({alice_params.get(), bob_params.get()}); - ValidateParams(run_params, {alice_params.get(), bob_params.get()}); + SetDefaultValuesForMissingParams({alice_params.get(), bob_params.get()}, + {&alice_video_sources, &bob_video_sources}); + ValidateParams(run_params, {alice_params.get(), bob_params.get()}, + {&alice_video_sources, &bob_video_sources}); SetupRequiredFieldTrials(run_params); // Print test summary @@ -272,6 +280,7 @@ void PeerConnectionE2EQualityTest::Run(RunParams run_params) { alice_ = TestPeer::CreateTestPeer( std::move(alice_components), std::move(alice_params), + std::move(alice_video_sources), std::make_unique( [this, bob_video_configs]( rtc::scoped_refptr transceiver) { @@ -283,6 +292,7 @@ void PeerConnectionE2EQualityTest::Run(RunParams run_params) { run_params.echo_emulation_config, task_queue_.get()); bob_ = TestPeer::CreateTestPeer( std::move(bob_components), std::move(bob_params), + std::move(bob_video_sources), std::make_unique( [this, alice_video_configs]( rtc::scoped_refptr transceiver) { @@ -436,16 +446,23 @@ void PeerConnectionE2EQualityTest::Run(RunParams run_params) { } void PeerConnectionE2EQualityTest::SetDefaultValuesForMissingParams( - std::vector params) { + std::vector params, + std::vector< + std::vector>>*> + video_sources) { int video_counter = 0; int audio_counter = 0; std::set video_labels; std::set audio_labels; - for (auto* p : params) { - for (auto& video_config : p->video_configs) { + for (size_t i = 0; i < params.size(); ++i) { + auto* p = params[i]; + for (size_t j = 0; j < p->video_configs.size(); ++j) { + VideoConfig& video_config = p->video_configs[j]; + std::unique_ptr>& video_source = + (*video_sources[i])[j]; if (!video_config.generator && !video_config.input_file_name && !video_config.screen_share_config && - !video_config.capturing_device_index) { + !video_config.capturing_device_index && !video_source) { video_config.generator = VideoGeneratorType::kDefault; } if (!video_config.stream_label) { @@ -470,8 +487,12 @@ void PeerConnectionE2EQualityTest::SetDefaultValuesForMissingParams( } } -void PeerConnectionE2EQualityTest::ValidateParams(const RunParams& run_params, - std::vector params) { +void PeerConnectionE2EQualityTest::ValidateParams( + const RunParams& run_params, + std::vector params, + std::vector< + std::vector>>*> + video_sources) { RTC_CHECK_GT(run_params.video_encoder_bitrate_multiplier, 0.0); std::set video_labels; @@ -488,7 +509,8 @@ void PeerConnectionE2EQualityTest::ValidateParams(const RunParams& run_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) { + for (size_t j = 0; j < p->video_configs.size(); ++j) { + VideoConfig& video_config = p->video_configs[j]; RTC_CHECK(video_config.stream_label); bool inserted = video_labels.insert(video_config.stream_label.value()).second; @@ -503,6 +525,9 @@ void PeerConnectionE2EQualityTest::ValidateParams(const RunParams& run_params, ++input_sources_count; if (video_config.capturing_device_index) ++input_sources_count; + if ((*video_sources[i])[j]) + ++input_sources_count; + RTC_CHECK_EQ(input_sources_count, 1) << VideoConfigSourcePresenceToString(video_config); @@ -701,12 +726,13 @@ PeerConnectionE2EQualityTest::MaybeAddVideo(TestPeer* peer) { // Params here valid because of pre-run validation. Params* params = peer->params(); std::vector> out; - for (auto video_config : params->video_configs) { + for (size_t i = 0; i < params->video_configs.size(); ++i) { + auto video_config = params->video_configs[i]; // Setup input video source into peer connection. test::VideoFrameWriter* writer = MaybeCreateVideoWriter(video_config.input_dump_file_name, video_config); std::unique_ptr capturer = CreateVideoCapturer( - video_config, + video_config, peer->ReleaseVideoSource(i), video_quality_analyzer_injection_helper_->CreateFramePreprocessor( video_config, writer)); rtc::scoped_refptr source = @@ -743,6 +769,7 @@ PeerConnectionE2EQualityTest::MaybeAddVideo(TestPeer* peer) { std::unique_ptr PeerConnectionE2EQualityTest::CreateVideoCapturer( const VideoConfig& video_config, + std::unique_ptr> source, std::unique_ptr frame_preprocessor) { if (video_config.capturing_device_index) { @@ -750,10 +777,17 @@ PeerConnectionE2EQualityTest::CreateVideoCapturer( test::CreateVideoCapturer(video_config.width, video_config.height, video_config.fps, *video_config.capturing_device_index); - capturer->SetFramePreprocessor(std::move(frame_preprocessor)); RTC_CHECK(capturer) << "Failed to obtain input stream from capturing device #" << *video_config.capturing_device_index; + capturer->SetFramePreprocessor(std::move(frame_preprocessor)); + return capturer; + } + + if (source != nullptr) { + std::unique_ptr capturer = + std::make_unique(std::move(source)); + capturer->SetFramePreprocessor(std::move(frame_preprocessor)); return capturer; } diff --git a/test/pc/e2e/peer_connection_quality_test.h b/test/pc/e2e/peer_connection_quality_test.h index dea571394c..669cdb311a 100644 --- a/test/pc/e2e/peer_connection_quality_test.h +++ b/test/pc/e2e/peer_connection_quality_test.h @@ -120,6 +120,14 @@ class PeerConfigurerImpl final PeerConfigurer* AddVideoConfig( PeerConnectionE2EQualityTestFixture::VideoConfig config) override { params_->video_configs.push_back(std::move(config)); + video_sources_.push_back(nullptr); + return this; + } + PeerConfigurer* AddVideoConfig( + PeerConnectionE2EQualityTestFixture::VideoConfig config, + std::unique_ptr> source) override { + params_->video_configs.push_back(std::move(config)); + video_sources_.push_back(std::move(source)); return this; } PeerConfigurer* SetAudioConfig( @@ -153,10 +161,16 @@ class PeerConfigurerImpl final return std::move(components_); } std::unique_ptr ReleaseParams() { return std::move(params_); } + std::vector>> + ReleaseVideoSources() { + return std::move(video_sources_); + } private: std::unique_ptr components_; std::unique_ptr params_; + std::vector>> + video_sources_; }; class TestVideoCapturerVideoTrackSource : public VideoTrackSource { @@ -245,10 +259,19 @@ class PeerConnectionE2EQualityTest // * Generate video stream labels if some of them missed // * Generate audio stream labels if some of them missed // * Set video source generation mode if it is not specified - void SetDefaultValuesForMissingParams(std::vector params); + void SetDefaultValuesForMissingParams( + std::vector params, + std::vector< + std::vector>>*> + video_sources); // Validate peer's parameters, also ensure uniqueness of all video stream // labels. - void ValidateParams(const RunParams& run_params, std::vector params); + void ValidateParams( + const RunParams& run_params, + std::vector params, + std::vector< + std::vector>>*> + video_sources); // For some functionality some field trials have to be enabled, so we will // enable them here. void SetupRequiredFieldTrials(const RunParams& run_params); @@ -263,6 +286,7 @@ class PeerConnectionE2EQualityTest MaybeAddVideo(TestPeer* peer); std::unique_ptr CreateVideoCapturer( const VideoConfig& video_config, + std::unique_ptr> source, std::unique_ptr frame_preprocessor); std::unique_ptr CreateScreenShareFrameGenerator( diff --git a/test/pc/e2e/test_peer.cc b/test/pc/e2e/test_peer.cc index ad7b58b574..4db591146b 100644 --- a/test/pc/e2e/test_peer.cc +++ b/test/pc/e2e/test_peer.cc @@ -328,6 +328,8 @@ absl::optional TestPeer::CreateRemoteAudioConfig( std::unique_ptr TestPeer::CreateTestPeer( std::unique_ptr components, std::unique_ptr params, + std::vector>> + video_sources, std::unique_ptr observer, VideoQualityAnalyzerInjectionHelper* video_analyzer_helper, rtc::Thread* signaling_thread, @@ -337,6 +339,7 @@ std::unique_ptr TestPeer::CreateTestPeer( rtc::TaskQueue* task_queue) { RTC_DCHECK(components); RTC_DCHECK(params); + RTC_DCHECK_EQ(params->video_configs.size(), video_sources.size()); SetMandatoryEntities(components.get()); params->rtc_configuration.sdp_semantics = SdpSemantics::kUnifiedPlan; @@ -347,7 +350,7 @@ std::unique_ptr TestPeer::CreateTestPeer( return absl::WrapUnique(new TestPeer( tpc.peer_connection_factory(), tpc.peer_connection(), std::move(observer), - std::move(params), tpc.audio_processing())); + std::move(params), std::move(video_sources), tpc.audio_processing())); } bool TestPeer::AddIceCandidates( @@ -373,11 +376,14 @@ TestPeer::TestPeer( rtc::scoped_refptr pc, std::unique_ptr observer, std::unique_ptr params, + std::vector>> + video_sources, rtc::scoped_refptr audio_processing) : PeerConnectionWrapper::PeerConnectionWrapper(std::move(pc_factory), std::move(pc), std::move(observer)), params_(std::move(params)), + video_sources_(std::move(video_sources)), audio_processing_(audio_processing) {} } // namespace webrtc_pc_e2e diff --git a/test/pc/e2e/test_peer.h b/test/pc/e2e/test_peer.h index efacde5d17..b9a7801dc7 100644 --- a/test/pc/e2e/test_peer.h +++ b/test/pc/e2e/test_peer.h @@ -62,6 +62,8 @@ class TestPeer final : public PeerConnectionWrapper { static std::unique_ptr CreateTestPeer( std::unique_ptr components, std::unique_ptr params, + std::vector>> + video_sources, std::unique_ptr observer, VideoQualityAnalyzerInjectionHelper* video_analyzer_helper, rtc::Thread* signaling_thread, @@ -71,6 +73,11 @@ class TestPeer final : public PeerConnectionWrapper { rtc::TaskQueue* task_queue); Params* params() const { return params_.get(); } + std::unique_ptr> ReleaseVideoSource( + size_t i) { + return std::move(video_sources_[i]); + } + void DetachAecDump() { audio_processing_->DetachAecDump(); } // Adds provided |candidates| to the owned peer connection. @@ -82,9 +89,13 @@ class TestPeer final : public PeerConnectionWrapper { rtc::scoped_refptr pc, std::unique_ptr observer, std::unique_ptr params, + std::vector>> + video_sources, rtc::scoped_refptr audio_processing); std::unique_ptr params_; + std::vector>> + video_sources_; rtc::scoped_refptr audio_processing_; std::vector> remote_ice_candidates_; diff --git a/test/pc/e2e/video_source_based_video_capturer.cc b/test/pc/e2e/video_source_based_video_capturer.cc new file mode 100644 index 0000000000..9a86604b4a --- /dev/null +++ b/test/pc/e2e/video_source_based_video_capturer.cc @@ -0,0 +1,32 @@ +/* + * 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/video_source_based_video_capturer.h" + +#include + +namespace webrtc { +namespace webrtc_pc_e2e { + +VideoSourceBasedVideoCapturer::VideoSourceBasedVideoCapturer( + std::unique_ptr> source) + : source_(std::move(source)) { + source_->AddOrUpdateSink(this, rtc::VideoSinkWants()); +} +VideoSourceBasedVideoCapturer::~VideoSourceBasedVideoCapturer() { + source_->RemoveSink(this); +} + +void VideoSourceBasedVideoCapturer::OnFrame(const VideoFrame& frame) { + TestVideoCapturer::OnFrame(frame); +} + +} // namespace webrtc_pc_e2e +} // namespace webrtc diff --git a/test/pc/e2e/video_source_based_video_capturer.h b/test/pc/e2e/video_source_based_video_capturer.h new file mode 100644 index 0000000000..6369fa5040 --- /dev/null +++ b/test/pc/e2e/video_source_based_video_capturer.h @@ -0,0 +1,43 @@ +/* + * 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_VIDEO_SOURCE_BASED_VIDEO_CAPTURER_H_ +#define TEST_PC_E2E_VIDEO_SOURCE_BASED_VIDEO_CAPTURER_H_ + +#include + +#include "api/video/video_sink_interface.h" +#include "api/video/video_source_interface.h" +#include "test/test_video_capturer.h" + +namespace webrtc { +namespace webrtc_pc_e2e { + +// Used to forward VideoFrame's provided by custom video source into video +// quality analyzer and VideoAdapter inside TestVideoCapturer and then properly +// broadcast them. +class VideoSourceBasedVideoCapturer + : public webrtc::test::TestVideoCapturer, + public rtc::VideoSinkInterface { + public: + VideoSourceBasedVideoCapturer( + std::unique_ptr> source); + ~VideoSourceBasedVideoCapturer() override; + + void OnFrame(const VideoFrame& frame) override; + + private: + std::unique_ptr> source_; +}; + +} // namespace webrtc_pc_e2e +} // namespace webrtc + +#endif // TEST_PC_E2E_VIDEO_SOURCE_BASED_VIDEO_CAPTURER_H_