Add a new frame generator that cycles through randomly generated slides.
Like YuvFileGenerator, this also updates the display with a new slide on every Nth frame, but it generates the slides itself instead of reading them from files. BUG=webrtc:8138 Review-Url: https://codereview.webrtc.org/3003193002 Cr-Commit-Position: refs/heads/master@{#19585}
This commit is contained in:
parent
c63f9345e7
commit
579de6faef
@ -181,6 +181,85 @@ class YuvFileGenerator : public FrameGenerator {
|
||||
std::unique_ptr<VideoFrame> temp_frame_;
|
||||
};
|
||||
|
||||
// SlideGenerator works similarly to YuvFileGenerator but it fills the frames
|
||||
// with randomly sized and colored squares instead of reading their content
|
||||
// from files.
|
||||
class SlideGenerator : public FrameGenerator {
|
||||
public:
|
||||
SlideGenerator(int width, int height, int frame_repeat_count)
|
||||
: width_(width),
|
||||
height_(height),
|
||||
frame_display_count_(frame_repeat_count),
|
||||
current_display_count_(0),
|
||||
random_generator_(1234) {
|
||||
RTC_DCHECK_GT(width, 0);
|
||||
RTC_DCHECK_GT(height, 0);
|
||||
RTC_DCHECK_GT(frame_repeat_count, 0);
|
||||
}
|
||||
|
||||
VideoFrame* NextFrame() override {
|
||||
if (current_display_count_ == 0)
|
||||
GenerateNewFrame();
|
||||
if (++current_display_count_ >= frame_display_count_)
|
||||
current_display_count_ = 0;
|
||||
|
||||
frame_.reset(
|
||||
new VideoFrame(buffer_, 0, 0, webrtc::kVideoRotation_0));
|
||||
return frame_.get();
|
||||
}
|
||||
|
||||
// Generates some randomly sized and colored squares scattered
|
||||
// over the frame.
|
||||
void GenerateNewFrame() {
|
||||
// The squares should have a varying order of magnitude in order
|
||||
// to simulate variation in the slides' complexity.
|
||||
const int kSquareNum = 1 << (4 + (random_generator_.Rand(0, 3) * 4));
|
||||
|
||||
buffer_ = I420Buffer::Create(width_, height_);
|
||||
memset(buffer_->MutableDataY(), 127, height_ * buffer_->StrideY());
|
||||
memset(buffer_->MutableDataU(), 127,
|
||||
buffer_->ChromaHeight() * buffer_->StrideU());
|
||||
memset(buffer_->MutableDataV(), 127,
|
||||
buffer_->ChromaHeight() * buffer_->StrideV());
|
||||
|
||||
for (int i = 0; i < kSquareNum; ++i) {
|
||||
int length = random_generator_.Rand(1, width_ > 4 ? width_ / 4 : 1);
|
||||
// Limit the length of later squares so that they don't overwrite the
|
||||
// previous ones too much.
|
||||
length = (length * (kSquareNum - i)) / kSquareNum;
|
||||
|
||||
int x = random_generator_.Rand(0, width_ - length);
|
||||
int y = random_generator_.Rand(0, height_ - length);
|
||||
uint8_t yuv_y = random_generator_.Rand(0, 255);
|
||||
uint8_t yuv_u = random_generator_.Rand(0, 255);
|
||||
uint8_t yuv_v = random_generator_.Rand(0, 255);
|
||||
|
||||
for (int yy = y; yy < y + length; ++yy) {
|
||||
uint8_t* pos_y =
|
||||
(buffer_->MutableDataY() + x + yy * buffer_->StrideY());
|
||||
memset(pos_y, yuv_y, length);
|
||||
}
|
||||
for (int yy = y; yy < y + length; yy += 2) {
|
||||
uint8_t* pos_u =
|
||||
(buffer_->MutableDataU() + x / 2 + yy / 2 * buffer_->StrideU());
|
||||
memset(pos_u, yuv_u, length / 2);
|
||||
uint8_t* pos_v =
|
||||
(buffer_->MutableDataV() + x / 2 + yy / 2 * buffer_->StrideV());
|
||||
memset(pos_v, yuv_v, length / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const int width_;
|
||||
const int height_;
|
||||
const int frame_display_count_;
|
||||
int current_display_count_;
|
||||
Random random_generator_;
|
||||
rtc::scoped_refptr<I420Buffer> buffer_;
|
||||
std::unique_ptr<VideoFrame> frame_;
|
||||
};
|
||||
|
||||
class ScrollingImageFrameGenerator : public FrameGenerator {
|
||||
public:
|
||||
ScrollingImageFrameGenerator(Clock* clock,
|
||||
@ -321,6 +400,12 @@ std::unique_ptr<FrameGenerator> FrameGenerator::CreateSquareGenerator(
|
||||
return std::unique_ptr<FrameGenerator>(new SquareGenerator(width, height));
|
||||
}
|
||||
|
||||
std::unique_ptr<FrameGenerator> FrameGenerator::CreateSlideGenerator(
|
||||
int width, int height, int frame_repeat_count) {
|
||||
return std::unique_ptr<FrameGenerator>(new SlideGenerator(
|
||||
width, height, frame_repeat_count));
|
||||
}
|
||||
|
||||
std::unique_ptr<FrameGenerator> FrameGenerator::CreateFromYuvFile(
|
||||
std::vector<std::string> filenames,
|
||||
size_t width,
|
||||
|
||||
@ -89,6 +89,11 @@ class FrameGenerator {
|
||||
size_t target_height,
|
||||
int64_t scroll_time_ms,
|
||||
int64_t pause_time_ms);
|
||||
|
||||
// Creates a frame generator that produces randomly generated slides.
|
||||
// frame_repeat_count determines how many times each slide is shown.
|
||||
static std::unique_ptr<FrameGenerator> CreateSlideGenerator(
|
||||
int width, int height, int frame_repeat_count);
|
||||
};
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
@ -114,6 +114,22 @@ FrameGeneratorCapturer* FrameGeneratorCapturer::CreateFromYuvFile(
|
||||
return capturer.release();
|
||||
}
|
||||
|
||||
FrameGeneratorCapturer* FrameGeneratorCapturer::CreateSlideGenerator(
|
||||
int width,
|
||||
int height,
|
||||
int frame_repeat_count,
|
||||
int target_fps,
|
||||
Clock* clock) {
|
||||
std::unique_ptr<FrameGeneratorCapturer> capturer(new FrameGeneratorCapturer(
|
||||
clock, FrameGenerator::CreateSlideGenerator(width, height,
|
||||
frame_repeat_count),
|
||||
target_fps));
|
||||
if (!capturer->Init())
|
||||
return nullptr;
|
||||
|
||||
return capturer.release();
|
||||
}
|
||||
|
||||
FrameGeneratorCapturer::FrameGeneratorCapturer(
|
||||
Clock* clock,
|
||||
std::unique_ptr<FrameGenerator> frame_generator,
|
||||
|
||||
@ -50,6 +50,12 @@ class FrameGeneratorCapturer : public VideoCapturer {
|
||||
size_t height,
|
||||
int target_fps,
|
||||
Clock* clock);
|
||||
|
||||
static FrameGeneratorCapturer* CreateSlideGenerator(int width,
|
||||
int height,
|
||||
int frame_repeat_count,
|
||||
int target_fps,
|
||||
Clock* clock);
|
||||
virtual ~FrameGeneratorCapturer();
|
||||
|
||||
void Start() override;
|
||||
|
||||
@ -81,6 +81,26 @@ class FrameGeneratorTest : public ::testing::Test {
|
||||
frame->set_timestamp(13);
|
||||
}
|
||||
|
||||
uint64_t Hash(VideoFrame* frame) {
|
||||
// Generate a 64-bit hash from the frame's buffer.
|
||||
uint64_t hash = 19;
|
||||
rtc::scoped_refptr<I420BufferInterface> i420_buffer =
|
||||
frame->video_frame_buffer()->ToI420();
|
||||
const uint8_t* buffer = i420_buffer->DataY();
|
||||
for (int i = 0; i < y_size; ++i) {
|
||||
hash = (37 * hash) + buffer[i];
|
||||
}
|
||||
buffer = i420_buffer->DataU();
|
||||
for (int i = 0; i < uv_size; ++i) {
|
||||
hash = (37 * hash) + buffer[i];
|
||||
}
|
||||
buffer = i420_buffer->DataV();
|
||||
for (int i = 0; i < uv_size; ++i) {
|
||||
hash = (37 * hash) + buffer[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::string two_frame_filename_;
|
||||
std::string one_frame_filename_;
|
||||
const int y_size = kFrameWidth * kFrameHeight;
|
||||
@ -145,5 +165,25 @@ TEST_F(FrameGeneratorTest, MultipleFrameFilesWithRepeat) {
|
||||
CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
|
||||
}
|
||||
|
||||
TEST_F(FrameGeneratorTest, SlideGenerator) {
|
||||
const int kGenCount = 9;
|
||||
const int kRepeatCount = 3;
|
||||
std::unique_ptr<FrameGenerator> generator(
|
||||
FrameGenerator::CreateSlideGenerator(
|
||||
kFrameWidth, kFrameHeight, kRepeatCount));
|
||||
uint64_t hashes[kGenCount];
|
||||
for (int i = 0; i < kGenCount; ++i) {
|
||||
hashes[i] = Hash(generator->NextFrame());
|
||||
}
|
||||
// Check that the buffer changes only every |kRepeatCount| frames.
|
||||
for (int i = 1; i < kGenCount; ++i) {
|
||||
if (i % kRepeatCount == 0) {
|
||||
EXPECT_NE(hashes[i-1], hashes[i]);
|
||||
} else {
|
||||
EXPECT_EQ(hashes[i-1], hashes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
@ -342,7 +342,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
RunTest(screenshare);
|
||||
@ -352,7 +352,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_3TL_Simulcast) {
|
||||
test::ScopedFieldTrials field_trial(kScreenshareSimulcastExperiment);
|
||||
VideoQualityTest::Params screenshare;
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.video = {true, 1850, 1110, 5, 800000,
|
||||
2500000, 2500000, false, "VP8", 3,
|
||||
2, 400000, false, false, ""};
|
||||
@ -379,7 +379,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_Scroll) {
|
||||
config.call.send_side_bwe = true;
|
||||
config.video = {true, 1850, 1110 / 2, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
config.screenshare = {true, 10, 2};
|
||||
config.screenshare = {true, false, 10, 2};
|
||||
config.analyzer = {"screenshare_slides_scrolling", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
RunTest(config);
|
||||
@ -390,7 +390,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_LossyNet) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides_lossy_net", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
screenshare.pipe.loss_percent = 5;
|
||||
@ -404,7 +404,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_VeryLossyNet) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides_very_lossy", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
screenshare.pipe.loss_percent = 10;
|
||||
@ -418,7 +418,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_LossyNetRestrictedQueue) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides_lossy_limited", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
screenshare.pipe.loss_percent = 5;
|
||||
@ -433,7 +433,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_ModeratelyRestricted) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides_moderately_restricted", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
screenshare.pipe.loss_percent = 1;
|
||||
@ -450,7 +450,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_LossyNetRestrictedQueue_ALR) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides_lossy_limited_ALR", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
screenshare.pipe.loss_percent = 5;
|
||||
@ -466,7 +466,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_ALR) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides_ALR", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
RunTest(screenshare);
|
||||
@ -478,7 +478,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_ModeratelyRestricted_ALR) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP8", 2, 1, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides_moderately_restricted_ALR", 0.0,
|
||||
0.0, kFullStackTestDurationSecs};
|
||||
screenshare.pipe.loss_percent = 1;
|
||||
@ -493,7 +493,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_3TL_Simulcast_ALR) {
|
||||
kAlrProbingExperiment);
|
||||
VideoQualityTest::Params screenshare;
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.video = {true, 1850, 1110, 5, 800000,
|
||||
2500000, 2500000, false, "VP8", 3,
|
||||
2, 400000, false, false, ""};
|
||||
@ -541,7 +541,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP9_2SL) {
|
||||
screenshare.call.send_side_bwe = true;
|
||||
screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false,
|
||||
"VP9", 1, 0, 400000, false, false, ""};
|
||||
screenshare.screenshare = {true, 10};
|
||||
screenshare.screenshare = {true, false, 10};
|
||||
screenshare.analyzer = {"screenshare_slides_vp9_2sl", 0.0, 0.0,
|
||||
kFullStackTestDurationSecs};
|
||||
screenshare.ss = {std::vector<VideoStream>(), 0, 2, 1,
|
||||
|
||||
@ -218,6 +218,13 @@ int MinTransmitBitrateKbps() {
|
||||
return FLAG_min_transmit_bitrate;
|
||||
}
|
||||
|
||||
DEFINE_bool(generate_slides,
|
||||
false,
|
||||
"Whether to use randomly generated slides or read them from files.");
|
||||
bool GenerateSlides() {
|
||||
return static_cast<int>(FLAG_generate_slides);
|
||||
}
|
||||
|
||||
DEFINE_int(slide_change_interval,
|
||||
10,
|
||||
"Interval (in seconds) between simulated slide changes.");
|
||||
@ -278,7 +285,8 @@ void Loopback() {
|
||||
false, // ULPFEC disabled.
|
||||
false, // FlexFEC disabled.
|
||||
""};
|
||||
params.screenshare = {true, flags::SlideChangeInterval(),
|
||||
params.screenshare = {true, flags::GenerateSlides(),
|
||||
flags::SlideChangeInterval(),
|
||||
flags::ScrollDuration(), flags::Slides()};
|
||||
params.analyzer = {"screenshare", 0.0, 0.0, flags::DurationSecs(),
|
||||
flags::OutputFilename(), flags::GraphTitle()};
|
||||
|
||||
@ -1174,7 +1174,7 @@ VideoQualityTest::Params::Params()
|
||||
video({false, 640, 480, 30, 50, 800, 800, false, "VP8", 1, -1, 0, false,
|
||||
false, ""}),
|
||||
audio({false, false, false}),
|
||||
screenshare({false, 10, 0}),
|
||||
screenshare({false, false, 10, 0}),
|
||||
analyzer({"", 0.0, 0.0, 0, "", ""}),
|
||||
pipe(),
|
||||
ss({std::vector<VideoStream>(), 0, 0, -1, std::vector<SpatialLayer>()}),
|
||||
@ -1689,32 +1689,41 @@ void VideoQualityTest::SetupScreenshareOrSVC() {
|
||||
// Setup frame generator.
|
||||
const size_t kWidth = 1850;
|
||||
const size_t kHeight = 1110;
|
||||
std::vector<std::string> slides = params_.screenshare.slides;
|
||||
if (slides.size() == 0) {
|
||||
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 (params_.screenshare.scroll_duration == 0) {
|
||||
// Cycle image every slide_change_interval seconds.
|
||||
frame_generator_ = test::FrameGenerator::CreateFromYuvFile(
|
||||
slides, kWidth, kHeight,
|
||||
if (params_.screenshare.generate_slides) {
|
||||
frame_generator_ = test::FrameGenerator::CreateSlideGenerator(
|
||||
kWidth, kHeight,
|
||||
params_.screenshare.slide_change_interval * params_.video.fps);
|
||||
} else {
|
||||
RTC_CHECK_LE(params_.video.width, kWidth);
|
||||
RTC_CHECK_LE(params_.video.height, kHeight);
|
||||
RTC_CHECK_GT(params_.screenshare.slide_change_interval, 0);
|
||||
const int kPauseDurationMs = (params_.screenshare.slide_change_interval -
|
||||
params_.screenshare.scroll_duration) *
|
||||
1000;
|
||||
RTC_CHECK_LE(params_.screenshare.scroll_duration,
|
||||
params_.screenshare.slide_change_interval);
|
||||
std::vector<std::string> slides = params_.screenshare.slides;
|
||||
if (slides.size() == 0) {
|
||||
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 (params_.screenshare.scroll_duration == 0) {
|
||||
// Cycle image every slide_change_interval seconds.
|
||||
frame_generator_ = test::FrameGenerator::CreateFromYuvFile(
|
||||
slides, kWidth, kHeight,
|
||||
params_.screenshare.slide_change_interval * params_.video.fps);
|
||||
} else {
|
||||
RTC_CHECK_LE(params_.video.width, kWidth);
|
||||
RTC_CHECK_LE(params_.video.height, kHeight);
|
||||
RTC_CHECK_GT(params_.screenshare.slide_change_interval, 0);
|
||||
const int kPauseDurationMs =
|
||||
(params_.screenshare.slide_change_interval -
|
||||
params_.screenshare.scroll_duration) *
|
||||
1000;
|
||||
RTC_CHECK_LE(params_.screenshare.scroll_duration,
|
||||
params_.screenshare.slide_change_interval);
|
||||
|
||||
frame_generator_ = test::FrameGenerator::CreateScrollingInputFromYuvFiles(
|
||||
clock_, slides, kWidth, kHeight, params_.video.width,
|
||||
params_.video.height, params_.screenshare.scroll_duration * 1000,
|
||||
kPauseDurationMs);
|
||||
frame_generator_ =
|
||||
test::FrameGenerator::CreateScrollingInputFromYuvFiles(
|
||||
clock_, slides, kWidth, kHeight, params_.video.width,
|
||||
params_.video.height,
|
||||
params_.screenshare.scroll_duration * 1000, kPauseDurationMs);
|
||||
}
|
||||
}
|
||||
} else if (params_.ss.num_spatial_layers > 1) { // For non-screenshare case.
|
||||
RTC_CHECK(params_.video.codec == "VP9");
|
||||
|
||||
@ -61,6 +61,7 @@ class VideoQualityTest : public test::CallTest {
|
||||
} audio;
|
||||
struct Screenshare {
|
||||
bool enabled;
|
||||
bool generate_slides;
|
||||
int32_t slide_change_interval;
|
||||
int32_t scroll_duration;
|
||||
std::vector<std::string> slides;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user