diff --git a/webrtc/test/frame_generator.cc b/webrtc/test/frame_generator.cc index 52ae14fe64..1956cdb3d7 100644 --- a/webrtc/test/frame_generator.cc +++ b/webrtc/test/frame_generator.cc @@ -181,6 +181,85 @@ class YuvFileGenerator : public FrameGenerator { std::unique_ptr 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 buffer_; + std::unique_ptr frame_; +}; + class ScrollingImageFrameGenerator : public FrameGenerator { public: ScrollingImageFrameGenerator(Clock* clock, @@ -321,6 +400,12 @@ std::unique_ptr FrameGenerator::CreateSquareGenerator( return std::unique_ptr(new SquareGenerator(width, height)); } +std::unique_ptr FrameGenerator::CreateSlideGenerator( + int width, int height, int frame_repeat_count) { + return std::unique_ptr(new SlideGenerator( + width, height, frame_repeat_count)); +} + std::unique_ptr FrameGenerator::CreateFromYuvFile( std::vector filenames, size_t width, diff --git a/webrtc/test/frame_generator.h b/webrtc/test/frame_generator.h index 6b0137df24..dfe35b832e 100644 --- a/webrtc/test/frame_generator.h +++ b/webrtc/test/frame_generator.h @@ -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 CreateSlideGenerator( + int width, int height, int frame_repeat_count); }; } // namespace test } // namespace webrtc diff --git a/webrtc/test/frame_generator_capturer.cc b/webrtc/test/frame_generator_capturer.cc index 4f1937b40a..095a2043e5 100644 --- a/webrtc/test/frame_generator_capturer.cc +++ b/webrtc/test/frame_generator_capturer.cc @@ -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 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 frame_generator, diff --git a/webrtc/test/frame_generator_capturer.h b/webrtc/test/frame_generator_capturer.h index f62afbac2d..44251218af 100644 --- a/webrtc/test/frame_generator_capturer.h +++ b/webrtc/test/frame_generator_capturer.h @@ -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; diff --git a/webrtc/test/frame_generator_unittest.cc b/webrtc/test/frame_generator_unittest.cc index 1ead698062..79e7b63abf 100644 --- a/webrtc/test/frame_generator_unittest.cc +++ b/webrtc/test/frame_generator_unittest.cc @@ -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 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 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 diff --git a/webrtc/video/full_stack_tests.cc b/webrtc/video/full_stack_tests.cc index 8190bf6cb5..bd694bc128 100644 --- a/webrtc/video/full_stack_tests.cc +++ b/webrtc/video/full_stack_tests.cc @@ -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(), 0, 2, 1, diff --git a/webrtc/video/screenshare_loopback.cc b/webrtc/video/screenshare_loopback.cc index d9de841084..17fd8deec9 100644 --- a/webrtc/video/screenshare_loopback.cc +++ b/webrtc/video/screenshare_loopback.cc @@ -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(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()}; diff --git a/webrtc/video/video_quality_test.cc b/webrtc/video/video_quality_test.cc index 1e7ce0fc4f..2aa6295ccf 100644 --- a/webrtc/video/video_quality_test.cc +++ b/webrtc/video/video_quality_test.cc @@ -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(), 0, 0, -1, std::vector()}), @@ -1689,32 +1689,41 @@ void VideoQualityTest::SetupScreenshareOrSVC() { // Setup frame generator. const size_t kWidth = 1850; const size_t kHeight = 1110; - std::vector 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 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"); diff --git a/webrtc/video/video_quality_test.h b/webrtc/video/video_quality_test.h index 3bb5cd282e..4238447b80 100644 --- a/webrtc/video/video_quality_test.h +++ b/webrtc/video/video_quality_test.h @@ -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 slides;