From 7581ff73754e34d7b0f5c42e075a2a12087f5bfb Mon Sep 17 00:00:00 2001 From: Artem Titov Date: Wed, 15 May 2019 15:45:33 +0200 Subject: [PATCH] Add screen share support to PC level test framework Bug: webrtc:10138 Change-Id: I1a8ac683e91f8061387f407610d7db2a6d0d4fe9 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/136805 Commit-Queue: Artem Titov Reviewed-by: Karl Wiberg Reviewed-by: Mirko Bonadei Reviewed-by: Ilya Nikolaevskiy Reviewed-by: Bjorn Mellem Cr-Commit-Position: refs/heads/master@{#27950} --- .../peerconnection_quality_test_fixture.h | 66 ++++++++++++++-- test/pc/e2e/peer_connection_e2e_smoke_test.cc | 54 ++++++++----- test/pc/e2e/peer_connection_quality_test.cc | 79 ++++++++++++++++++- test/pc/e2e/peer_connection_quality_test.h | 2 + 4 files changed, 172 insertions(+), 29 deletions(-) diff --git a/api/test/peerconnection_quality_test_fixture.h b/api/test/peerconnection_quality_test_fixture.h index 76de3d0d6a..3955da5040 100644 --- a/api/test/peerconnection_quality_test_fixture.h +++ b/api/test/peerconnection_quality_test_fixture.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "absl/memory/memory.h" @@ -40,19 +41,72 @@ namespace webrtc { namespace webrtc_pc_e2e { +constexpr size_t kDefaultSlidesWidth = 1850; +constexpr size_t kDefaultSlidesHeight = 1110; + // API is in development. Can be changed/removed without notice. class PeerConnectionE2EQualityTestFixture { public: + // Contains parameters for screen share scrolling. + // + // If scrolling is enabled, then it will be done by putting sliding window + // on source video and moving this window from top left corner to the + // bottom right corner of the picture. + // + // In such case source dimensions must be greater or equal to the sliding + // window dimensions. So |source_width| and |source_height| are the dimensions + // of the source frame, while |VideoConfig::width| and |VideoConfig::height| + // are the dimensions of the sliding window. + // + // Because |source_width| and |source_height| are dimensions of the source + // frame, they have to be width and height of videos from + // |ScreenShareConfig::slides_yuv_file_names|. + // + // Because scrolling have to be done on single slide it also requires, that + // |duration| must be less or equal to + // |ScreenShareConfig::slide_change_interval|. + struct ScrollingParams { + ScrollingParams(TimeDelta duration, + size_t source_width, + size_t source_height) + : duration(duration), + source_width(source_width), + source_height(source_height) { + RTC_CHECK_GT(duration.ms(), 0); + } + + // Duration of scrolling. + TimeDelta duration; + // Width of source slides video. + size_t source_width; + // Height of source slides video. + size_t source_height; + }; + // Contains screen share video stream properties. struct ScreenShareConfig { - // If true, slides will be generated programmatically. - bool generate_slides; + explicit ScreenShareConfig(TimeDelta slide_change_interval) + : slide_change_interval(slide_change_interval) { + RTC_CHECK_GT(slide_change_interval.ms(), 0); + } + // Shows how long one slide should be presented on the screen during // slide generation. TimeDelta slide_change_interval; - // If equal to 0, no scrolling will be applied. - TimeDelta scroll_duration; - // If empty, default set of slides will be used. + // If true, slides will be generated programmatically. No scrolling params + // will be applied in such case. + bool generate_slides = false; + // If present scrolling will be applied. Please read extra requirement on + // |slides_yuv_file_names| for scrolling. + absl::optional scrolling_params; + // Contains list of yuv files with slides. + // + // If empty, default set of slides will be used. In such case + // |VideoConfig::width| must be equal to |kDefaultSlidesWidth| and + // |VideoConfig::height| must be equal to |kDefaultSlidesHeight| or if + // |scrolling_params| are specified, then |ScrollingParams::source_width| + // must be equal to |kDefaultSlidesWidth| and + // |ScrollingParams::source_height| must be equal to |kDefaultSlidesHeight|. std::vector slides_yuv_file_names; }; @@ -63,7 +117,9 @@ class PeerConnectionE2EQualityTestFixture { VideoConfig(size_t width, size_t height, int32_t fps) : width(width), height(height), fps(fps) {} + // Video stream width. const size_t width; + // Video stream height. const size_t height; const int32_t fps; // Have to be unique among all specified configs for all peers in the call. diff --git a/test/pc/e2e/peer_connection_e2e_smoke_test.cc b/test/pc/e2e/peer_connection_e2e_smoke_test.cc index f29746f68b..fab4a90f63 100644 --- a/test/pc/e2e/peer_connection_e2e_smoke_test.cc +++ b/test/pc/e2e/peer_connection_e2e_smoke_test.cc @@ -37,6 +37,9 @@ TEST(PeerConnectionE2EQualityTestSmokeTest, MAYBE_RunWithEmulatedNetwork) { using RunParams = PeerConnectionE2EQualityTestFixture::RunParams; using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig; using AudioConfig = PeerConnectionE2EQualityTestFixture::AudioConfig; + using ScreenShareConfig = + PeerConnectionE2EQualityTestFixture::ScreenShareConfig; + using ScrollingParams = PeerConnectionE2EQualityTestFixture::ScrollingParams; // Setup emulated network std::unique_ptr network_emulation_manager = @@ -87,32 +90,43 @@ TEST(PeerConnectionE2EQualityTestSmokeTest, MAYBE_RunWithEmulatedNetwork) { {alice_endpoint}); fixture->AddPeer(alice_network->network_thread(), alice_network->network_manager(), [](PeerConfigurer* alice) { - VideoConfig video_config(640, 360, 30); - video_config.stream_label = "alice-video"; - alice->AddVideoConfig(std::move(video_config)); - AudioConfig audio_config; - audio_config.stream_label = "alice-audio"; - audio_config.mode = AudioConfig::Mode::kFile; - audio_config.input_file_name = test::ResourcePath( + VideoConfig video(640, 360, 30); + video.stream_label = "alice-video"; + alice->AddVideoConfig(std::move(video)); + + AudioConfig audio; + audio.stream_label = "alice-audio"; + audio.mode = AudioConfig::Mode::kFile; + audio.input_file_name = test::ResourcePath( "pc_quality_smoke_test_alice_source", "wav"); - alice->SetAudioConfig(std::move(audio_config)); + alice->SetAudioConfig(std::move(audio)); }); EmulatedNetworkManagerInterface* bob_network = network_emulation_manager->CreateEmulatedNetworkManagerInterface( {bob_endpoint}); - fixture->AddPeer(bob_network->network_thread(), - bob_network->network_manager(), [](PeerConfigurer* bob) { - VideoConfig video_config(640, 360, 30); - video_config.stream_label = "bob-video"; - bob->AddVideoConfig(std::move(video_config)); - AudioConfig audio_config; - audio_config.stream_label = "bob-audio"; - audio_config.mode = AudioConfig::Mode::kFile; - audio_config.input_file_name = test::ResourcePath( - "pc_quality_smoke_test_bob_source", "wav"); - bob->SetAudioConfig(std::move(audio_config)); - }); + fixture->AddPeer( + bob_network->network_thread(), bob_network->network_manager(), + [](PeerConfigurer* bob) { + VideoConfig video(640, 360, 30); + video.stream_label = "bob-video"; + bob->AddVideoConfig(std::move(video)); + + VideoConfig screenshare(640, 360, 30); + screenshare.stream_label = "bob-screenshare"; + screenshare.screen_share_config = + ScreenShareConfig(TimeDelta::seconds(2)); + screenshare.screen_share_config->scrolling_params = ScrollingParams( + TimeDelta::ms(1800), kDefaultSlidesWidth, kDefaultSlidesHeight); + bob->AddVideoConfig(screenshare); + + AudioConfig audio; + audio.stream_label = "bob-audio"; + audio.mode = AudioConfig::Mode::kFile; + audio.input_file_name = + test::ResourcePath("pc_quality_smoke_test_bob_source", "wav"); + bob->SetAudioConfig(std::move(audio)); + }); fixture->AddQualityMetricsReporter( absl::make_unique(alice_network, diff --git a/test/pc/e2e/peer_connection_quality_test.cc b/test/pc/e2e/peer_connection_quality_test.cc index 325ac3944e..ecc909b5a6 100644 --- a/test/pc/e2e/peer_connection_quality_test.cc +++ b/test/pc/e2e/peer_connection_quality_test.cc @@ -461,6 +461,37 @@ void PeerConnectionE2EQualityTest::ValidateParams(const RunParams& run_params, << VideoConfigSourcePresenceToString(video_config); RTC_CHECK(!(video_config.screen_share_config && video_config.generator)) << VideoConfigSourcePresenceToString(video_config); + + if (video_config.screen_share_config) { + if (video_config.screen_share_config->slides_yuv_file_names.empty()) { + if (video_config.screen_share_config->scrolling_params) { + // If we have scrolling params, then its |source_width| and + // |source_heigh| will be used as width and height of video input, + // so we have to validate it against width and height of default + // input. + RTC_CHECK_EQ(video_config.screen_share_config->scrolling_params + ->source_width, + kDefaultSlidesWidth); + RTC_CHECK_EQ(video_config.screen_share_config->scrolling_params + ->source_height, + kDefaultSlidesHeight); + } else { + RTC_CHECK_EQ(video_config.width, kDefaultSlidesWidth); + RTC_CHECK_EQ(video_config.height, kDefaultSlidesHeight); + } + } + if (video_config.screen_share_config->scrolling_params) { + RTC_CHECK_LE( + video_config.screen_share_config->scrolling_params->duration, + video_config.screen_share_config->slide_change_interval); + RTC_CHECK_GE( + video_config.screen_share_config->scrolling_params->source_width, + video_config.width); + RTC_CHECK_GE( + video_config.screen_share_config->scrolling_params->source_height, + video_config.height); + } + } } if (p->audio_config) { bool inserted = @@ -616,15 +647,55 @@ PeerConnectionE2EQualityTest::CreateFrameGenerator( 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; + return CreateScreenShareFrameGenerator(video_config); } RTC_NOTREACHED() << "Unsupported video_config input source"; return nullptr; } +std::unique_ptr +PeerConnectionE2EQualityTest::CreateScreenShareFrameGenerator( + const VideoConfig& video_config) { + RTC_CHECK(video_config.screen_share_config); + if (video_config.screen_share_config->generate_slides) { + return test::FrameGenerator::CreateSlideGenerator( + video_config.width, video_config.height, + video_config.screen_share_config->slide_change_interval.seconds() * + video_config.fps); + } + std::vector slides = + video_config.screen_share_config->slides_yuv_file_names; + if (slides.empty()) { + // If slides is empty we need to add default slides as source. In such case + // video width and height is validated to be equal to kDefaultSlidesWidth + // and kDefaultSlidesHeight. + slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv")); + slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv")); + slides.push_back(test::ResourcePath("photo_1850_1110", "yuv")); + slides.push_back(test::ResourcePath("difficult_photo_1850_1110", "yuv")); + } + if (!video_config.screen_share_config->scrolling_params) { + // Cycle image every slide_change_interval seconds. + return test::FrameGenerator::CreateFromYuvFile( + slides, video_config.width, video_config.height, + video_config.screen_share_config->slide_change_interval.seconds() * + video_config.fps); + } + + // |pause_duration| is nonnegative. It is validated in ValidateParams(...). + TimeDelta pause_duration = + video_config.screen_share_config->slide_change_interval - + video_config.screen_share_config->scrolling_params->duration; + + return test::FrameGenerator::CreateScrollingInputFromYuvFiles( + clock_, slides, + video_config.screen_share_config->scrolling_params->source_width, + video_config.screen_share_config->scrolling_params->source_height, + video_config.width, video_config.height, + video_config.screen_share_config->scrolling_params->duration.ms(), + pause_duration.ms()); +} + void PeerConnectionE2EQualityTest::MaybeAddAudio(TestPeer* peer) { if (!peer->params()->audio_config) { return; diff --git a/test/pc/e2e/peer_connection_quality_test.h b/test/pc/e2e/peer_connection_quality_test.h index 3b39916198..5db8f036d4 100644 --- a/test/pc/e2e/peer_connection_quality_test.h +++ b/test/pc/e2e/peer_connection_quality_test.h @@ -219,6 +219,8 @@ class PeerConnectionE2EQualityTest MaybeAddVideo(TestPeer* peer); std::unique_ptr CreateFrameGenerator( const VideoConfig& video_config); + std::unique_ptr CreateScreenShareFrameGenerator( + const VideoConfig& video_config); void MaybeAddAudio(TestPeer* peer); void SetPeerCodecPreferences(TestPeer* peer, const RunParams& run_params); void SetupCall();