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}
This commit is contained in:
perkj 2017-02-27 06:52:10 -08:00 committed by Commit bot
parent adc41f3c82
commit a8ba195db5
7 changed files with 115 additions and 70 deletions

View File

@ -404,9 +404,9 @@ class TestVideoSenderWithVp8 : public TestVideoSender {
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;

View File

@ -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 webrtc {
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 @@ class ChromaGenerator : public FrameGenerator {
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 @@ bool FrameForwarder::has_sinks() const {
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 @@ FrameGenerator* FrameGenerator::CreateFromYuvFile(
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 @@ FrameGenerator* FrameGenerator::CreateScrollingInputFromYuvFiles(
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

View File

@ -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 FrameForwarder : public rtc::VideoSourceInterface<VideoFrame> {
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 @@ class FrameGenerator {
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 @@ class FrameGenerator {
// 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,

View File

@ -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 @@ FrameGeneratorCapturer* FrameGeneratorCapturer::CreateFromYuvFile(
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);
}

View File

@ -41,8 +41,8 @@ class FrameGeneratorCapturer : public VideoCapturer {
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 @@ class FrameGeneratorCapturer : public VideoCapturer {
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();

View File

@ -229,7 +229,7 @@ TEST_P(EndToEndTest, RendersSingleDelayedFrame) {
// 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 @@ TEST_P(EndToEndTest, TransmitsFirstFrame) {
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 @@ TEST_P(EndToEndTest, AssignsTransportSequenceNumbers) {
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 @@ TEST_P(EndToEndTest, AssignsTransportSequenceNumbers) {
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 @@ TEST_P(EndToEndTest, AssignsTransportSequenceNumbers) {
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 @@ TEST_P(EndToEndTest, ObserversEncodedFrames) {
Start();
std::unique_ptr<test::FrameGenerator> frame_generator(
test::FrameGenerator::CreateChromaGenerator(kDefaultWidth,
test::FrameGenerator::CreateSquareGenerator(kDefaultWidth,
kDefaultHeight));
test::FrameForwarder forwarder;
video_send_stream_->SetSource(

View File

@ -1343,9 +1343,9 @@ void VideoQualityTest::SetupScreenshareOrSVC() {
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 @@ void VideoQualityTest::SetupScreenshareOrSVC() {
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::SetupScreenshareOrSVC() {
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 @@ void VideoQualityTest::CreateCapturer() {
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(