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:
parent
adc41f3c82
commit
a8ba195db5
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user