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}
This commit is contained in:
@ -13,6 +13,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
@ -40,19 +41,72 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace webrtc_pc_e2e {
|
namespace webrtc_pc_e2e {
|
||||||
|
|
||||||
|
constexpr size_t kDefaultSlidesWidth = 1850;
|
||||||
|
constexpr size_t kDefaultSlidesHeight = 1110;
|
||||||
|
|
||||||
// API is in development. Can be changed/removed without notice.
|
// API is in development. Can be changed/removed without notice.
|
||||||
class PeerConnectionE2EQualityTestFixture {
|
class PeerConnectionE2EQualityTestFixture {
|
||||||
public:
|
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.
|
// Contains screen share video stream properties.
|
||||||
struct ScreenShareConfig {
|
struct ScreenShareConfig {
|
||||||
// If true, slides will be generated programmatically.
|
explicit ScreenShareConfig(TimeDelta slide_change_interval)
|
||||||
bool generate_slides;
|
: 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
|
// Shows how long one slide should be presented on the screen during
|
||||||
// slide generation.
|
// slide generation.
|
||||||
TimeDelta slide_change_interval;
|
TimeDelta slide_change_interval;
|
||||||
// If equal to 0, no scrolling will be applied.
|
// If true, slides will be generated programmatically. No scrolling params
|
||||||
TimeDelta scroll_duration;
|
// will be applied in such case.
|
||||||
// If empty, default set of slides will be used.
|
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;
|
std::vector<std::string> slides_yuv_file_names;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,7 +117,9 @@ class PeerConnectionE2EQualityTestFixture {
|
|||||||
VideoConfig(size_t width, size_t height, int32_t fps)
|
VideoConfig(size_t width, size_t height, int32_t fps)
|
||||||
: width(width), height(height), fps(fps) {}
|
: width(width), height(height), fps(fps) {}
|
||||||
|
|
||||||
|
// Video stream width.
|
||||||
const size_t width;
|
const size_t width;
|
||||||
|
// Video stream height.
|
||||||
const size_t height;
|
const size_t height;
|
||||||
const int32_t fps;
|
const int32_t fps;
|
||||||
// Have to be unique among all specified configs for all peers in the call.
|
// Have to be unique among all specified configs for all peers in the call.
|
||||||
|
@ -37,6 +37,9 @@ TEST(PeerConnectionE2EQualityTestSmokeTest, MAYBE_RunWithEmulatedNetwork) {
|
|||||||
using RunParams = PeerConnectionE2EQualityTestFixture::RunParams;
|
using RunParams = PeerConnectionE2EQualityTestFixture::RunParams;
|
||||||
using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
|
using VideoConfig = PeerConnectionE2EQualityTestFixture::VideoConfig;
|
||||||
using AudioConfig = PeerConnectionE2EQualityTestFixture::AudioConfig;
|
using AudioConfig = PeerConnectionE2EQualityTestFixture::AudioConfig;
|
||||||
|
using ScreenShareConfig =
|
||||||
|
PeerConnectionE2EQualityTestFixture::ScreenShareConfig;
|
||||||
|
using ScrollingParams = PeerConnectionE2EQualityTestFixture::ScrollingParams;
|
||||||
|
|
||||||
// Setup emulated network
|
// Setup emulated network
|
||||||
std::unique_ptr<NetworkEmulationManager> network_emulation_manager =
|
std::unique_ptr<NetworkEmulationManager> network_emulation_manager =
|
||||||
@ -87,32 +90,43 @@ TEST(PeerConnectionE2EQualityTestSmokeTest, MAYBE_RunWithEmulatedNetwork) {
|
|||||||
{alice_endpoint});
|
{alice_endpoint});
|
||||||
fixture->AddPeer(alice_network->network_thread(),
|
fixture->AddPeer(alice_network->network_thread(),
|
||||||
alice_network->network_manager(), [](PeerConfigurer* alice) {
|
alice_network->network_manager(), [](PeerConfigurer* alice) {
|
||||||
VideoConfig video_config(640, 360, 30);
|
VideoConfig video(640, 360, 30);
|
||||||
video_config.stream_label = "alice-video";
|
video.stream_label = "alice-video";
|
||||||
alice->AddVideoConfig(std::move(video_config));
|
alice->AddVideoConfig(std::move(video));
|
||||||
AudioConfig audio_config;
|
|
||||||
audio_config.stream_label = "alice-audio";
|
AudioConfig audio;
|
||||||
audio_config.mode = AudioConfig::Mode::kFile;
|
audio.stream_label = "alice-audio";
|
||||||
audio_config.input_file_name = test::ResourcePath(
|
audio.mode = AudioConfig::Mode::kFile;
|
||||||
|
audio.input_file_name = test::ResourcePath(
|
||||||
"pc_quality_smoke_test_alice_source", "wav");
|
"pc_quality_smoke_test_alice_source", "wav");
|
||||||
alice->SetAudioConfig(std::move(audio_config));
|
alice->SetAudioConfig(std::move(audio));
|
||||||
});
|
});
|
||||||
|
|
||||||
EmulatedNetworkManagerInterface* bob_network =
|
EmulatedNetworkManagerInterface* bob_network =
|
||||||
network_emulation_manager->CreateEmulatedNetworkManagerInterface(
|
network_emulation_manager->CreateEmulatedNetworkManagerInterface(
|
||||||
{bob_endpoint});
|
{bob_endpoint});
|
||||||
fixture->AddPeer(bob_network->network_thread(),
|
fixture->AddPeer(
|
||||||
bob_network->network_manager(), [](PeerConfigurer* bob) {
|
bob_network->network_thread(), bob_network->network_manager(),
|
||||||
VideoConfig video_config(640, 360, 30);
|
[](PeerConfigurer* bob) {
|
||||||
video_config.stream_label = "bob-video";
|
VideoConfig video(640, 360, 30);
|
||||||
bob->AddVideoConfig(std::move(video_config));
|
video.stream_label = "bob-video";
|
||||||
AudioConfig audio_config;
|
bob->AddVideoConfig(std::move(video));
|
||||||
audio_config.stream_label = "bob-audio";
|
|
||||||
audio_config.mode = AudioConfig::Mode::kFile;
|
VideoConfig screenshare(640, 360, 30);
|
||||||
audio_config.input_file_name = test::ResourcePath(
|
screenshare.stream_label = "bob-screenshare";
|
||||||
"pc_quality_smoke_test_bob_source", "wav");
|
screenshare.screen_share_config =
|
||||||
bob->SetAudioConfig(std::move(audio_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(
|
fixture->AddQualityMetricsReporter(
|
||||||
absl::make_unique<NetworkQualityMetricsReporter>(alice_network,
|
absl::make_unique<NetworkQualityMetricsReporter>(alice_network,
|
||||||
|
@ -461,6 +461,37 @@ void PeerConnectionE2EQualityTest::ValidateParams(const RunParams& run_params,
|
|||||||
<< VideoConfigSourcePresenceToString(video_config);
|
<< VideoConfigSourcePresenceToString(video_config);
|
||||||
RTC_CHECK(!(video_config.screen_share_config && video_config.generator))
|
RTC_CHECK(!(video_config.screen_share_config && video_config.generator))
|
||||||
<< VideoConfigSourcePresenceToString(video_config);
|
<< 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) {
|
if (p->audio_config) {
|
||||||
bool inserted =
|
bool inserted =
|
||||||
@ -616,15 +647,55 @@ PeerConnectionE2EQualityTest::CreateFrameGenerator(
|
|||||||
video_config.width, video_config.height, /*frame_repeat_count=*/1);
|
video_config.width, video_config.height, /*frame_repeat_count=*/1);
|
||||||
}
|
}
|
||||||
if (video_config.screen_share_config) {
|
if (video_config.screen_share_config) {
|
||||||
// TODO(titovartem) implement screen share support
|
return CreateScreenShareFrameGenerator(video_config);
|
||||||
// (http://bugs.webrtc.org/10138)
|
|
||||||
RTC_NOTREACHED() << "Screen share is not implemented";
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
RTC_NOTREACHED() << "Unsupported video_config input source";
|
RTC_NOTREACHED() << "Unsupported video_config input source";
|
||||||
return nullptr;
|
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) {
|
void PeerConnectionE2EQualityTest::MaybeAddAudio(TestPeer* peer) {
|
||||||
if (!peer->params()->audio_config) {
|
if (!peer->params()->audio_config) {
|
||||||
return;
|
return;
|
||||||
|
@ -219,6 +219,8 @@ class PeerConnectionE2EQualityTest
|
|||||||
MaybeAddVideo(TestPeer* peer);
|
MaybeAddVideo(TestPeer* peer);
|
||||||
std::unique_ptr<test::FrameGenerator> CreateFrameGenerator(
|
std::unique_ptr<test::FrameGenerator> CreateFrameGenerator(
|
||||||
const VideoConfig& video_config);
|
const VideoConfig& video_config);
|
||||||
|
std::unique_ptr<test::FrameGenerator> CreateScreenShareFrameGenerator(
|
||||||
|
const VideoConfig& video_config);
|
||||||
void MaybeAddAudio(TestPeer* peer);
|
void MaybeAddAudio(TestPeer* peer);
|
||||||
void SetPeerCodecPreferences(TestPeer* peer, const RunParams& run_params);
|
void SetPeerCodecPreferences(TestPeer* peer, const RunParams& run_params);
|
||||||
void SetupCall();
|
void SetupCall();
|
||||||
|
Reference in New Issue
Block a user