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 <titovartem@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Bjorn Mellem <mellem@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27950}
diff --git a/api/test/peerconnection_quality_test_fixture.h b/api/test/peerconnection_quality_test_fixture.h
index 76de3d0..3955da5 100644
--- a/api/test/peerconnection_quality_test_fixture.h
+++ b/api/test/peerconnection_quality_test_fixture.h
@@ -13,6 +13,7 @@
#include <map>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#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<ScrollingParams> 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<std::string> slides_yuv_file_names;
};
@@ -63,7 +117,9 @@
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 f29746f..fab4a90 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 @@
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<NetworkEmulationManager> network_emulation_manager =
@@ -87,32 +90,43 @@
{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<NetworkQualityMetricsReporter>(alice_network,
diff --git a/test/pc/e2e/peer_connection_quality_test.cc b/test/pc/e2e/peer_connection_quality_test.cc
index 325ac39..ecc909b 100644
--- a/test/pc/e2e/peer_connection_quality_test.cc
+++ b/test/pc/e2e/peer_connection_quality_test.cc
@@ -461,6 +461,37 @@
<< 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 @@
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<test::FrameGenerator>
+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<std::string> 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 3b39916..5db8f03 100644
--- a/test/pc/e2e/peer_connection_quality_test.h
+++ b/test/pc/e2e/peer_connection_quality_test.h
@@ -219,6 +219,8 @@
MaybeAddVideo(TestPeer* peer);
std::unique_ptr<test::FrameGenerator> CreateFrameGenerator(
const VideoConfig& video_config);
+ std::unique_ptr<test::FrameGenerator> CreateScreenShareFrameGenerator(
+ const VideoConfig& video_config);
void MaybeAddAudio(TestPeer* peer);
void SetPeerCodecPreferences(TestPeer* peer, const RunParams& run_params);
void SetupCall();