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:
parent
d0b67c2a70
commit
53571c75c6
@ -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",
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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_);
|
||||
|
||||
|
||||
45
test/frame_generator_capturer_unittest.cc
Normal file
45
test/frame_generator_capturer_unittest.cc
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user