Adds config based creation of frame generator capturer.

This simplifies creations of frame generator capturers in a reusable
way. It's modelled on the scenario VideoSendStreamConfig,

Bug: webrtc:10839
Change-Id: Ibe0709cd94521f78c6267eece533b048607d0994
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/147272
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28722}
This commit is contained in:
Sebastian Jansson 2019-07-31 17:30:03 +02:00 committed by Commit Bot
parent d0b67c2a70
commit 53571c75c6
4 changed files with 237 additions and 5 deletions

View File

@ -52,6 +52,7 @@ rtc_source_set("video_test_common") {
]
deps = [
":fileutils",
"../api:libjingle_peerconnection_api",
"../api:scoped_refptr",
"../api/task_queue",
@ -390,6 +391,7 @@ if (rtc_include_tests) {
"../test:single_threaded_task_queue",
"pc/e2e:e2e_unittests",
"scenario:scenario_unittests",
"time_controller",
"time_controller:time_controller_unittests",
"//testing/gmock",
"//testing/gtest",
@ -401,6 +403,7 @@ if (rtc_include_tests) {
"call_config_utils_unittest.cc",
"direct_transport_unittest.cc",
"fake_vp8_encoder_unittest.cc",
"frame_generator_capturer_unittest.cc",
"frame_generator_unittest.cc",
"rtp_file_reader_unittest.cc",
"rtp_file_writer_unittest.cc",

View File

@ -16,16 +16,31 @@
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "rtc_base/checks.h"
#include "rtc_base/critical_section.h"
#include "rtc_base/logging.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/task_utils/repeating_task.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/clock.h"
#include "test/testsupport/file_utils.h"
namespace webrtc {
namespace test {
namespace {
std::string TransformFilePath(std::string path) {
static const std::string resource_prefix = "res://";
int ext_pos = path.rfind(".");
if (ext_pos < 0) {
return test::ResourcePath(path, "yuv");
} else if (path.find(resource_prefix) == 0) {
std::string name = path.substr(resource_prefix.length(), ext_pos);
std::string ext = path.substr(ext_pos, path.size());
return test::ResourcePath(name, ext);
}
return path;
}
} // namespace
FrameGeneratorCapturer::FrameGeneratorCapturer(
Clock* clock,
@ -50,6 +65,88 @@ FrameGeneratorCapturer::~FrameGeneratorCapturer() {
Stop();
}
std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
FrameGeneratorCapturerConfig::SquaresVideo config) {
return absl::make_unique<FrameGeneratorCapturer>(
clock,
FrameGenerator::CreateSquareGenerator(
config.width, config.height, config.pixel_format, config.num_squares),
config.framerate, task_queue_factory);
}
std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
FrameGeneratorCapturerConfig::SquareSlides config) {
return absl::make_unique<FrameGeneratorCapturer>(
clock,
FrameGenerator::CreateSlideGenerator(
config.width, config.height,
/*frame_repeat_count*/ config.change_interval.seconds<double>() *
config.framerate),
config.framerate, task_queue_factory);
}
std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
FrameGeneratorCapturerConfig::VideoFile config) {
RTC_CHECK(config.width && config.height);
return absl::make_unique<FrameGeneratorCapturer>(
clock,
FrameGenerator::CreateFromYuvFile({TransformFilePath(config.name)},
config.width, config.height,
/*frame_repeat_count*/ 1),
config.framerate, task_queue_factory);
}
std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
FrameGeneratorCapturerConfig::ImageSlides config) {
std::unique_ptr<FrameGenerator> slides_generator;
std::vector<std::string> paths = config.paths;
for (std::string& path : paths)
path = TransformFilePath(path);
if (config.crop.width || config.crop.height) {
TimeDelta pause_duration =
config.change_interval - config.crop.scroll_duration;
RTC_CHECK_GE(pause_duration, TimeDelta::Zero());
int crop_width = config.crop.width.value_or(config.width);
int crop_height = config.crop.height.value_or(config.height);
RTC_CHECK_LE(crop_width, config.width);
RTC_CHECK_LE(crop_height, config.height);
slides_generator = FrameGenerator::CreateScrollingInputFromYuvFiles(
clock, paths, config.width, config.height, crop_width, crop_height,
config.crop.scroll_duration.ms(), pause_duration.ms());
} else {
slides_generator = FrameGenerator::CreateFromYuvFile(
paths, config.width, config.height,
/*frame_repeat_count*/ config.change_interval.seconds<double>() *
config.framerate);
}
return absl::make_unique<FrameGeneratorCapturer>(
clock, std::move(slides_generator), config.framerate, task_queue_factory);
}
std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
const FrameGeneratorCapturerConfig& config) {
if (config.video_file) {
return Create(clock, task_queue_factory, *config.video_file);
} else if (config.image_slides) {
return Create(clock, task_queue_factory, *config.image_slides);
} else if (config.squares_slides) {
return Create(clock, task_queue_factory, *config.squares_slides);
} else {
return Create(clock, task_queue_factory,
config.squares_video.value_or(
FrameGeneratorCapturerConfig::SquaresVideo()));
}
}
void FrameGeneratorCapturer::SetFakeRotation(VideoRotation rotation) {
rtc::CritScope cs(&lock_);
fake_rotation_ = rotation;
@ -67,7 +164,7 @@ bool FrameGeneratorCapturer::Init() {
if (frame_generator_.get() == nullptr)
return false;
RepeatingTaskHandle::DelayedStart(
frame_task_ = RepeatingTaskHandle::DelayedStart(
task_queue_.Get(),
TimeDelta::seconds(1) / GetCurrentConfiguredFramerate(), [this] {
InsertFrame();
@ -101,8 +198,16 @@ void FrameGeneratorCapturer::InsertFrame() {
}
void FrameGeneratorCapturer::Start() {
rtc::CritScope cs(&lock_);
sending_ = true;
{
rtc::CritScope cs(&lock_);
sending_ = true;
}
if (!frame_task_.Running()) {
frame_task_ = RepeatingTaskHandle::Start(task_queue_.Get(), [this] {
InsertFrame();
return TimeDelta::seconds(1) / GetCurrentConfiguredFramerate();
});
}
}
void FrameGeneratorCapturer::Stop() {

View File

@ -17,14 +17,71 @@
#include "api/video/video_frame.h"
#include "rtc_base/critical_section.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/task_utils/repeating_task.h"
#include "test/frame_generator.h"
#include "test/test_video_capturer.h"
namespace webrtc {
namespace test {
namespace frame_gen_cap_impl {
template <typename T>
class AutoOpt : public absl::optional<T> {
public:
T* operator->() {
if (!absl::optional<T>::has_value())
this->emplace(T());
return absl::optional<T>::operator->();
}
};
} // namespace frame_gen_cap_impl
struct FrameGeneratorCapturerConfig {
struct SquaresVideo {
int framerate = 30;
FrameGenerator::OutputType pixel_format = FrameGenerator::OutputType::I420;
int width = 320;
int height = 180;
int num_squares = 10;
};
class FrameGenerator;
struct SquareSlides {
int framerate = 30;
TimeDelta change_interval = TimeDelta::seconds(10);
int width = 1600;
int height = 1200;
};
struct VideoFile {
int framerate = 30;
std::string name;
// Must be set to width and height of the source video file.
int width = 0;
int height = 0;
};
struct ImageSlides {
int framerate = 30;
TimeDelta change_interval = TimeDelta::seconds(10);
struct Crop {
TimeDelta scroll_duration = TimeDelta::seconds(0);
absl::optional<int> width;
absl::optional<int> height;
} crop;
int width = 1850;
int height = 1110;
std::vector<std::string> paths = {
"web_screenshot_1850_1110",
"presentation_1850_1110",
"photo_1850_1110",
"difficult_photo_1850_1110",
};
};
frame_gen_cap_impl::AutoOpt<SquaresVideo> squares_video;
frame_gen_cap_impl::AutoOpt<SquareSlides> squares_slides;
frame_gen_cap_impl::AutoOpt<VideoFile> video_file;
frame_gen_cap_impl::AutoOpt<ImageSlides> image_slides;
};
class FrameGeneratorCapturer : public TestVideoCapturer {
public:
@ -45,6 +102,27 @@ class FrameGeneratorCapturer : public TestVideoCapturer {
TaskQueueFactory& task_queue_factory);
virtual ~FrameGeneratorCapturer();
static std::unique_ptr<FrameGeneratorCapturer> Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
FrameGeneratorCapturerConfig::SquaresVideo config);
static std::unique_ptr<FrameGeneratorCapturer> Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
FrameGeneratorCapturerConfig::SquareSlides config);
static std::unique_ptr<FrameGeneratorCapturer> Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
FrameGeneratorCapturerConfig::VideoFile config);
static std::unique_ptr<FrameGeneratorCapturer> Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
FrameGeneratorCapturerConfig::ImageSlides config);
static std::unique_ptr<FrameGeneratorCapturer> Create(
Clock* clock,
TaskQueueFactory& task_queue_factory,
const FrameGeneratorCapturerConfig& config);
void Start();
void Stop();
void ChangeResolution(size_t width, size_t height);
@ -71,6 +149,7 @@ class FrameGeneratorCapturer : public TestVideoCapturer {
void UpdateFps(int max_fps) RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_);
Clock* const clock_;
RepeatingTaskHandle frame_task_;
bool sending_;
SinkWantsObserver* sink_wants_observer_ RTC_GUARDED_BY(&lock_);

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "test/frame_generator_capturer.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/time_controller/simulated_time_controller.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::Eq;
using ::testing::Property;
class MockVideoSinkInterfaceVideoFrame
: public rtc::VideoSinkInterface<VideoFrame> {
public:
MOCK_METHOD1(OnFrame, void(const VideoFrame& frame));
MOCK_METHOD0(OnDiscardedFrame, void());
};
} // namespace
TEST(FrameGeneratorCapturerTest, CreateFromConfig) {
GlobalSimulatedTimeController time(Timestamp::seconds(1000));
FrameGeneratorCapturerConfig config;
config.squares_video->width = 300;
config.squares_video->height = 200;
config.squares_video->framerate = 20;
auto capturer = FrameGeneratorCapturer::Create(
time.GetClock(), *time.GetTaskQueueFactory(), config);
testing::StrictMock<MockVideoSinkInterfaceVideoFrame> mock_sink;
capturer->AddOrUpdateSink(&mock_sink, rtc::VideoSinkWants());
capturer->Start();
EXPECT_CALL(mock_sink, OnFrame(Property(&VideoFrame::width, Eq(300))))
.Times(20);
time.Sleep(TimeDelta::seconds(1));
}
} // namespace test
} // namespace webrtc