Replace test::FrameGenerator::ChromaGenerator with new FrameGenerator::SquareGenerator The problem with the ChromaGenerator is that the VP8 encoder produce a too low bitrate for each frame. The squaregenerator make the VP8 encoder produce about 600kbit/s at VGA.
SquareGenerator is a FrameGenerator that draws 10 randomly sized and colored
squares. Between each new generated frame, the squares are moved slightly
towards the lower right corner.
BUG=webrtc:7192
Review-Url: https://codereview.webrtc.org/2705973002
Cr-Commit-Position: refs/heads/master@{#16870}
diff --git a/webrtc/modules/video_coding/video_sender_unittest.cc b/webrtc/modules/video_coding/video_sender_unittest.cc
index 50e17bf..81eca3b 100644
--- a/webrtc/modules/video_coding/video_sender_unittest.cc
+++ b/webrtc/modules/video_coding/video_sender_unittest.cc
@@ -404,9 +404,9 @@
const char* input_video = "foreman_cif";
const int width = 352;
const int height = 288;
- generator_.reset(FrameGenerator::CreateFromYuvFile(
+ generator_ = FrameGenerator::CreateFromYuvFile(
std::vector<std::string>(1, test::ResourcePath(input_video, "yuv")),
- width, height, 1));
+ width, height, 1);
codec_ = MakeVp8VideoCodec(width, height, 3);
codec_.minBitrate = 10;
diff --git a/webrtc/test/frame_generator.cc b/webrtc/test/frame_generator.cc
index 1375ba3..cd0bcc6 100644
--- a/webrtc/test/frame_generator.cc
+++ b/webrtc/test/frame_generator.cc
@@ -18,6 +18,7 @@
#include "webrtc/api/video/i420_buffer.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/keep_ref_until_done.h"
+#include "webrtc/base/random.h"
#include "webrtc/common_video/include/video_frame_buffer.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/system_wrappers/include/clock.h"
@@ -27,16 +28,22 @@
namespace test {
namespace {
-class ChromaGenerator : public FrameGenerator {
+// SquareGenerator is a FrameGenerator that draws 10 randomly sized and colored
+// squares. Between each new generated frame, the squares are moved slightly
+// towards the lower right corner.
+class SquareGenerator : public FrameGenerator {
public:
- ChromaGenerator(size_t width, size_t height) : angle_(0.0) {
+ SquareGenerator(int width, int height) {
ChangeResolution(width, height);
+ for (int i = 0; i < 10; ++i) {
+ squares_.emplace_back(new Square(width, height, i + 1));
+ }
}
void ChangeResolution(size_t width, size_t height) override {
rtc::CritScope lock(&crit_);
- width_ = width;
- height_ = height;
+ width_ = static_cast<int>(width);
+ height_ = static_cast<int>(height);
RTC_CHECK(width_ > 0);
RTC_CHECK(height_ > 0);
half_width_ = (width_ + 1) / 2;
@@ -46,32 +53,67 @@
VideoFrame* NextFrame() override {
rtc::CritScope lock(&crit_);
- angle_ += 30.0;
- uint8_t u = fabs(sin(angle_)) * 0xFF;
- uint8_t v = fabs(cos(angle_)) * 0xFF;
// Ensure stride == width.
- rtc::scoped_refptr<I420Buffer> buffer(I420Buffer::Create(
- static_cast<int>(width_), static_cast<int>(height_),
- static_cast<int>(width_), static_cast<int>(half_width_),
- static_cast<int>(half_width_)));
+ rtc::scoped_refptr<I420Buffer> buffer(
+ I420Buffer::Create(width_, height_, width_, half_width_, half_width_));
+ memset(buffer->MutableDataY(), 127, y_size_);
+ memset(buffer->MutableDataU(), 127, uv_size_);
+ memset(buffer->MutableDataV(), 127, uv_size_);
- memset(buffer->MutableDataY(), 0x80, y_size_);
- memset(buffer->MutableDataU(), u, uv_size_);
- memset(buffer->MutableDataV(), v, uv_size_);
+ for (const auto& square : squares_)
+ square->Draw(buffer);
frame_.reset(new VideoFrame(buffer, 0, 0, webrtc::kVideoRotation_0));
return frame_.get();
}
private:
+ class Square {
+ public:
+ Square(int width, int height, int seed)
+ : random_generator_(seed),
+ x_(random_generator_.Rand(0, width)),
+ y_(random_generator_.Rand(0, height)),
+ length_(random_generator_.Rand(1, width > 4 ? width / 4 : 1)),
+ yuv_y_(random_generator_.Rand(0, 255)),
+ yuv_u_(random_generator_.Rand(0, 255)),
+ yuv_v_(random_generator_.Rand(0, 255)) {}
+
+ void Draw(const rtc::scoped_refptr<I420Buffer>& buffer) {
+ x_ = (x_ + random_generator_.Rand(0, 4)) % (buffer->width() - length_);
+ y_ = (y_ + random_generator_.Rand(0, 4)) % (buffer->height() - length_);
+ for (int x = x_; x < x_ + length_; ++x) {
+ for (int y = y_; y < y_ + length_; ++y) {
+ uint8_t* pos_y = (buffer->MutableDataY() + x + y * buffer->StrideY());
+ *pos_y = yuv_y_;
+ uint8_t* pos_u =
+ (buffer->MutableDataU() + x / 2 + y / 2 * buffer->StrideU());
+ *pos_u = yuv_u_;
+ uint8_t* pos_v =
+ (buffer->MutableDataV() + x / 2 + y / 2 * buffer->StrideV());
+ *pos_v = yuv_v_;
+ }
+ }
+ }
+
+ private:
+ Random random_generator_;
+ int x_;
+ int y_;
+ const int length_;
+ const uint8_t yuv_y_;
+ const uint8_t yuv_u_;
+ const uint8_t yuv_v_;
+ };
+
rtc::CriticalSection crit_;
- double angle_ GUARDED_BY(&crit_);
- size_t width_ GUARDED_BY(&crit_);
- size_t height_ GUARDED_BY(&crit_);
- size_t half_width_ GUARDED_BY(&crit_);
+ int width_ GUARDED_BY(&crit_);
+ int height_ GUARDED_BY(&crit_);
+ int half_width_ GUARDED_BY(&crit_);
size_t y_size_ GUARDED_BY(&crit_);
size_t uv_size_ GUARDED_BY(&crit_);
+ std::vector<std::unique_ptr<Square>> squares_ GUARDED_BY(&crit_);
std::unique_ptr<VideoFrame> frame_ GUARDED_BY(&crit_);
};
@@ -280,12 +322,13 @@
return sink_ != nullptr;
}
-FrameGenerator* FrameGenerator::CreateChromaGenerator(size_t width,
- size_t height) {
- return new ChromaGenerator(width, height);
+std::unique_ptr<FrameGenerator> FrameGenerator::CreateSquareGenerator(
+ int width,
+ int height) {
+ return std::unique_ptr<FrameGenerator>(new SquareGenerator(width, height));
}
-FrameGenerator* FrameGenerator::CreateFromYuvFile(
+std::unique_ptr<FrameGenerator> FrameGenerator::CreateFromYuvFile(
std::vector<std::string> filenames,
size_t width,
size_t height,
@@ -298,10 +341,12 @@
files.push_back(file);
}
- return new YuvFileGenerator(files, width, height, frame_repeat_count);
+ return std::unique_ptr<FrameGenerator>(
+ new YuvFileGenerator(files, width, height, frame_repeat_count));
}
-FrameGenerator* FrameGenerator::CreateScrollingInputFromYuvFiles(
+std::unique_ptr<FrameGenerator>
+FrameGenerator::CreateScrollingInputFromYuvFiles(
Clock* clock,
std::vector<std::string> filenames,
size_t source_width,
@@ -318,9 +363,9 @@
files.push_back(file);
}
- return new ScrollingImageFrameGenerator(
+ return std::unique_ptr<FrameGenerator>(new ScrollingImageFrameGenerator(
clock, files, source_width, source_height, target_width, target_height,
- scroll_time_ms, pause_time_ms);
+ scroll_time_ms, pause_time_ms));
}
} // namespace test
diff --git a/webrtc/test/frame_generator.h b/webrtc/test/frame_generator.h
index e2519c1..95b0798 100644
--- a/webrtc/test/frame_generator.h
+++ b/webrtc/test/frame_generator.h
@@ -10,6 +10,7 @@
#ifndef WEBRTC_TEST_FRAME_GENERATOR_H_
#define WEBRTC_TEST_FRAME_GENERATOR_H_
+#include <memory>
#include <string>
#include <vector>
@@ -47,8 +48,7 @@
class FrameGenerator {
public:
- FrameGenerator() {}
- virtual ~FrameGenerator() {}
+ virtual ~FrameGenerator() = default;
// Returns video frame that remains valid until next call.
virtual VideoFrame* NextFrame() = 0;
@@ -58,17 +58,19 @@
RTC_NOTREACHED();
}
- // Creates a test frame generator that creates fully saturated frames with
- // varying U, V values over time.
- static FrameGenerator* CreateChromaGenerator(size_t width, size_t height);
+ // Creates a frame generator that produces frames with small squares that
+ // move randomly towards the lower right corner.
+ static std::unique_ptr<FrameGenerator> CreateSquareGenerator(int width,
+ int height);
// Creates a frame generator that repeatedly plays a set of yuv files.
// The frame_repeat_count determines how many times each frame is shown,
// with 1 = show each frame once, etc.
- static FrameGenerator* CreateFromYuvFile(std::vector<std::string> files,
- size_t width,
- size_t height,
- int frame_repeat_count);
+ static std::unique_ptr<FrameGenerator> CreateFromYuvFile(
+ std::vector<std::string> files,
+ size_t width,
+ size_t height,
+ int frame_repeat_count);
// Creates a frame generator which takes a set of yuv files (wrapping a
// frame generator created by CreateFromYuvFile() above), but outputs frames
@@ -78,7 +80,7 @@
// be scrolled top to bottom/left to right for scroll_tim_ms milliseconds.
// After that the image will stay in place for pause_time_ms milliseconds,
// and then this will be repeated with the next file from the input set.
- static FrameGenerator* CreateScrollingInputFromYuvFiles(
+ static std::unique_ptr<FrameGenerator> CreateScrollingInputFromYuvFiles(
Clock* clock,
std::vector<std::string> filenames,
size_t source_width,
diff --git a/webrtc/test/frame_generator_capturer.cc b/webrtc/test/frame_generator_capturer.cc
index 65be956..00efbc8 100644
--- a/webrtc/test/frame_generator_capturer.cc
+++ b/webrtc/test/frame_generator_capturer.cc
@@ -21,12 +21,12 @@
namespace webrtc {
namespace test {
-FrameGeneratorCapturer* FrameGeneratorCapturer::Create(size_t width,
- size_t height,
+FrameGeneratorCapturer* FrameGeneratorCapturer::Create(int width,
+ int height,
int target_fps,
Clock* clock) {
FrameGeneratorCapturer* capturer = new FrameGeneratorCapturer(
- clock, FrameGenerator::CreateChromaGenerator(width, height), target_fps);
+ clock, FrameGenerator::CreateSquareGenerator(width, height), target_fps);
if (!capturer->Init()) {
delete capturer;
return NULL;
@@ -53,19 +53,20 @@
return capturer;
}
-FrameGeneratorCapturer::FrameGeneratorCapturer(Clock* clock,
- FrameGenerator* frame_generator,
- int target_fps)
+FrameGeneratorCapturer::FrameGeneratorCapturer(
+ Clock* clock,
+ std::unique_ptr<FrameGenerator> frame_generator,
+ int target_fps)
: clock_(clock),
sending_(false),
sink_(nullptr),
sink_wants_observer_(nullptr),
tick_(EventTimerWrapper::Create()),
thread_(FrameGeneratorCapturer::Run, this, "FrameGeneratorCapturer"),
- frame_generator_(frame_generator),
+ frame_generator_(std::move(frame_generator)),
target_fps_(target_fps),
first_frame_capture_time_(-1) {
- RTC_DCHECK(frame_generator);
+ RTC_DCHECK(frame_generator_);
RTC_DCHECK_GT(target_fps, 0);
}
diff --git a/webrtc/test/frame_generator_capturer.h b/webrtc/test/frame_generator_capturer.h
index 3a1b6c9..dfd1271 100644
--- a/webrtc/test/frame_generator_capturer.h
+++ b/webrtc/test/frame_generator_capturer.h
@@ -41,8 +41,8 @@
virtual ~SinkWantsObserver() {}
};
- static FrameGeneratorCapturer* Create(size_t width,
- size_t height,
+ static FrameGeneratorCapturer* Create(int width,
+ int height,
int target_fps,
Clock* clock);
@@ -69,7 +69,7 @@
int64_t first_frame_capture_time() const { return first_frame_capture_time_; }
FrameGeneratorCapturer(Clock* clock,
- FrameGenerator* frame_generator,
+ std::unique_ptr<FrameGenerator> frame_generator,
int target_fps);
bool Init();
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index f427471..cb7edd4 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -229,7 +229,7 @@
// Create frames that are smaller than the send width/height, this is done to
// check that the callbacks are done after processing video.
std::unique_ptr<test::FrameGenerator> frame_generator(
- test::FrameGenerator::CreateChromaGenerator(kWidth, kHeight));
+ test::FrameGenerator::CreateSquareGenerator(kWidth, kHeight));
test::FrameForwarder frame_forwarder;
video_send_stream_->SetSource(
&frame_forwarder, VideoSendStream::DegradationPreference::kBalanced);
@@ -273,7 +273,7 @@
Start();
std::unique_ptr<test::FrameGenerator> frame_generator(
- test::FrameGenerator::CreateChromaGenerator(kDefaultWidth,
+ test::FrameGenerator::CreateSquareGenerator(kDefaultWidth,
kDefaultHeight));
test::FrameForwarder frame_forwarder;
video_send_stream_->SetSource(
@@ -1669,11 +1669,7 @@
drop_packet = true;
}
- size_t payload_length =
- length - (header.headerLength + header.paddingLength);
- if (payload_length == 0) {
- padding_observed_ = true;
- } else if (header.payloadType == kSendRtxPayloadType) {
+ if (header.payloadType == kSendRtxPayloadType) {
uint16_t original_sequence_number =
ByteReader<uint16_t>::ReadBigEndian(&data[header.headerLength]);
uint32_t original_ssrc =
@@ -1704,7 +1700,7 @@
bool IsDone() {
bool observed_types_ok =
streams_observed_.size() == MultiStreamTest::kNumStreams &&
- padding_observed_ && retransmit_observed_ && rtx_padding_observed_;
+ retransmit_observed_ && rtx_padding_observed_;
if (!observed_types_ok)
return false;
// We should not have any gaps in the sequence number range.
@@ -1759,9 +1755,11 @@
send_config->rtp.extensions.push_back(RtpExtension(
RtpExtension::kTransportSequenceNumberUri, kExtensionId));
- // Force some padding to be sent.
+ // Force some padding to be sent. Note that since we do send media
+ // packets we can not guarantee that a padding only packet is sent.
+ // Instead, padding will most likely be send as an RTX packet.
const int kPaddingBitrateBps = 50000;
- encoder_config->max_bitrate_bps = 1000000;
+ encoder_config->max_bitrate_bps = 200000;
encoder_config->min_transmit_bitrate_bps =
encoder_config->max_bitrate_bps + kPaddingBitrateBps;
@@ -1953,7 +1951,7 @@
Start();
std::unique_ptr<test::FrameGenerator> frame_generator(
- test::FrameGenerator::CreateChromaGenerator(kDefaultWidth,
+ test::FrameGenerator::CreateSquareGenerator(kDefaultWidth,
kDefaultHeight));
test::FrameForwarder forwarder;
video_send_stream_->SetSource(
diff --git a/webrtc/video/video_quality_test.cc b/webrtc/video/video_quality_test.cc
index c227075..8389cfc 100644
--- a/webrtc/video/video_quality_test.cc
+++ b/webrtc/video/video_quality_test.cc
@@ -1343,9 +1343,9 @@
if (params_.screenshare.scroll_duration == 0) {
// Cycle image every slide_change_interval seconds.
- frame_generator_.reset(test::FrameGenerator::CreateFromYuvFile(
+ frame_generator_ = test::FrameGenerator::CreateFromYuvFile(
slides, kWidth, kHeight,
- params_.screenshare.slide_change_interval * params_.video.fps));
+ params_.screenshare.slide_change_interval * params_.video.fps);
} else {
RTC_CHECK_LE(params_.video.width, kWidth);
RTC_CHECK_LE(params_.video.height, kHeight);
@@ -1356,11 +1356,10 @@
RTC_CHECK_LE(params_.screenshare.scroll_duration,
params_.screenshare.slide_change_interval);
- frame_generator_.reset(
- 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");
@@ -1377,7 +1376,7 @@
void VideoQualityTest::CreateCapturer() {
if (params_.screenshare.enabled) {
test::FrameGeneratorCapturer* frame_generator_capturer =
- new test::FrameGeneratorCapturer(clock_, frame_generator_.release(),
+ new test::FrameGeneratorCapturer(clock_, std::move(frame_generator_),
params_.video.fps);
EXPECT_TRUE(frame_generator_capturer->Init());
video_capturer_.reset(frame_generator_capturer);
@@ -1388,8 +1387,8 @@
if (!video_capturer_) {
// Failed to get actual camera, use chroma generator as backup.
video_capturer_.reset(test::FrameGeneratorCapturer::Create(
- params_.video.width, params_.video.height, params_.video.fps,
- clock_));
+ static_cast<int>(params_.video.width),
+ static_cast<int>(params_.video.height), params_.video.fps, clock_));
}
} else {
video_capturer_.reset(test::FrameGeneratorCapturer::CreateFromYuvFile(