blob: 9eda4df2601c05600db38ad5b29fccb9329a67e4 [file] [log] [blame]
pbos@webrtc.org26d12102013-05-29 13:41:03 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "test/frame_generator_capturer.h"
pbos@webrtc.org26d12102013-05-29 13:41:03 +000012
Yves Gerey3e707812018-11-28 16:47:49 +010013#include <algorithm>
14#include <cmath>
15#include <limits>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020016#include <memory>
ilnikbaded152017-03-17 05:55:25 -070017#include <utility>
18#include <vector>
19
Yves Gerey3e707812018-11-28 16:47:49 +010020#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 09:11:00 -080021#include "rtc_base/critical_section.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "rtc_base/logging.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/task_queue.h"
Steve Anton10542f22019-01-11 09:11:00 -080024#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "system_wrappers/include/clock.h"
Sebastian Jansson53571c72019-07-31 17:30:03 +020026#include "test/testsupport/file_utils.h"
pbos@webrtc.org26d12102013-05-29 13:41:03 +000027
28namespace webrtc {
29namespace test {
Sebastian Jansson53571c72019-07-31 17:30:03 +020030namespace {
31std::string TransformFilePath(std::string path) {
32 static const std::string resource_prefix = "res://";
33 int ext_pos = path.rfind(".");
34 if (ext_pos < 0) {
35 return test::ResourcePath(path, "yuv");
36 } else if (path.find(resource_prefix) == 0) {
37 std::string name = path.substr(resource_prefix.length(), ext_pos);
38 std::string ext = path.substr(ext_pos, path.size());
39 return test::ResourcePath(name, ext);
40 }
41 return path;
42}
43} // namespace
pbos@webrtc.org26d12102013-05-29 13:41:03 +000044
Sebastian Janssonfb14c5d2019-02-28 13:30:04 +010045FrameGeneratorCapturer::FrameGeneratorCapturer(
46 Clock* clock,
47 std::unique_ptr<FrameGenerator> frame_generator,
48 int target_fps,
49 TaskQueueFactory& task_queue_factory)
perkja49cbd32016-09-16 07:53:41 -070050 : clock_(clock),
Niels Möller8eeccbe2018-12-14 13:35:32 +010051 sending_(true),
perkj803d97f2016-11-01 11:45:46 -070052 sink_wants_observer_(nullptr),
perkja8ba1952017-02-27 06:52:10 -080053 frame_generator_(std::move(frame_generator)),
Sebastian Janssonba3decf2018-08-30 11:19:23 +020054 source_fps_(target_fps),
55 target_capture_fps_(target_fps),
ilnikbaded152017-03-17 05:55:25 -070056 first_frame_capture_time_(-1),
Sebastian Janssonfb14c5d2019-02-28 13:30:04 +010057 task_queue_(task_queue_factory.CreateTaskQueue(
58 "FrameGenCapQ",
59 TaskQueueFactory::Priority::HIGH)) {
perkja8ba1952017-02-27 06:52:10 -080060 RTC_DCHECK(frame_generator_);
perkja49cbd32016-09-16 07:53:41 -070061 RTC_DCHECK_GT(target_fps, 0);
pbos@webrtc.org26d12102013-05-29 13:41:03 +000062}
63
64FrameGeneratorCapturer::~FrameGeneratorCapturer() {
65 Stop();
pbos@webrtc.org26d12102013-05-29 13:41:03 +000066}
67
Sebastian Jansson53571c72019-07-31 17:30:03 +020068std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
69 Clock* clock,
70 TaskQueueFactory& task_queue_factory,
71 FrameGeneratorCapturerConfig::SquaresVideo config) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020072 return std::make_unique<FrameGeneratorCapturer>(
Sebastian Jansson53571c72019-07-31 17:30:03 +020073 clock,
74 FrameGenerator::CreateSquareGenerator(
75 config.width, config.height, config.pixel_format, config.num_squares),
76 config.framerate, task_queue_factory);
77}
78std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
79 Clock* clock,
80 TaskQueueFactory& task_queue_factory,
81 FrameGeneratorCapturerConfig::SquareSlides config) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020082 return std::make_unique<FrameGeneratorCapturer>(
Sebastian Jansson53571c72019-07-31 17:30:03 +020083 clock,
84 FrameGenerator::CreateSlideGenerator(
85 config.width, config.height,
86 /*frame_repeat_count*/ config.change_interval.seconds<double>() *
87 config.framerate),
88 config.framerate, task_queue_factory);
89}
90std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
91 Clock* clock,
92 TaskQueueFactory& task_queue_factory,
93 FrameGeneratorCapturerConfig::VideoFile config) {
94 RTC_CHECK(config.width && config.height);
Mirko Bonadei317a1f02019-09-17 17:06:18 +020095 return std::make_unique<FrameGeneratorCapturer>(
Sebastian Jansson53571c72019-07-31 17:30:03 +020096 clock,
97 FrameGenerator::CreateFromYuvFile({TransformFilePath(config.name)},
98 config.width, config.height,
99 /*frame_repeat_count*/ 1),
100 config.framerate, task_queue_factory);
101}
102
103std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
104 Clock* clock,
105 TaskQueueFactory& task_queue_factory,
106 FrameGeneratorCapturerConfig::ImageSlides config) {
107 std::unique_ptr<FrameGenerator> slides_generator;
108 std::vector<std::string> paths = config.paths;
109 for (std::string& path : paths)
110 path = TransformFilePath(path);
111
112 if (config.crop.width || config.crop.height) {
113 TimeDelta pause_duration =
114 config.change_interval - config.crop.scroll_duration;
115 RTC_CHECK_GE(pause_duration, TimeDelta::Zero());
116 int crop_width = config.crop.width.value_or(config.width);
117 int crop_height = config.crop.height.value_or(config.height);
118 RTC_CHECK_LE(crop_width, config.width);
119 RTC_CHECK_LE(crop_height, config.height);
120 slides_generator = FrameGenerator::CreateScrollingInputFromYuvFiles(
121 clock, paths, config.width, config.height, crop_width, crop_height,
122 config.crop.scroll_duration.ms(), pause_duration.ms());
123 } else {
124 slides_generator = FrameGenerator::CreateFromYuvFile(
125 paths, config.width, config.height,
126 /*frame_repeat_count*/ config.change_interval.seconds<double>() *
127 config.framerate);
128 }
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200129 return std::make_unique<FrameGeneratorCapturer>(
Sebastian Jansson53571c72019-07-31 17:30:03 +0200130 clock, std::move(slides_generator), config.framerate, task_queue_factory);
131}
132
133std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
134 Clock* clock,
135 TaskQueueFactory& task_queue_factory,
136 const FrameGeneratorCapturerConfig& config) {
137 if (config.video_file) {
138 return Create(clock, task_queue_factory, *config.video_file);
139 } else if (config.image_slides) {
140 return Create(clock, task_queue_factory, *config.image_slides);
141 } else if (config.squares_slides) {
142 return Create(clock, task_queue_factory, *config.squares_slides);
143 } else {
144 return Create(clock, task_queue_factory,
145 config.squares_video.value_or(
146 FrameGeneratorCapturerConfig::SquaresVideo()));
147 }
148}
149
Perba7dc722016-04-19 15:01:23 +0200150void FrameGeneratorCapturer::SetFakeRotation(VideoRotation rotation) {
151 rtc::CritScope cs(&lock_);
152 fake_rotation_ = rotation;
153}
154
Johannes Kronf7f13e02018-12-12 11:17:43 +0100155void FrameGeneratorCapturer::SetFakeColorSpace(
156 absl::optional<ColorSpace> color_space) {
157 rtc::CritScope cs(&lock_);
158 fake_color_space_ = color_space;
159}
160
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000161bool FrameGeneratorCapturer::Init() {
pbos@webrtc.org94015242013-10-16 11:05:37 +0000162 // This check is added because frame_generator_ might be file based and should
163 // not crash because a file moved.
sprangc5d62e22017-04-02 23:53:04 -0700164 if (frame_generator_.get() == nullptr)
pbos@webrtc.org94015242013-10-16 11:05:37 +0000165 return false;
166
Sebastian Jansson53571c72019-07-31 17:30:03 +0200167 frame_task_ = RepeatingTaskHandle::DelayedStart(
Danil Chapovalov4423c362019-03-06 18:41:39 +0100168 task_queue_.Get(),
169 TimeDelta::seconds(1) / GetCurrentConfiguredFramerate(), [this] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100170 InsertFrame();
171 return TimeDelta::seconds(1) / GetCurrentConfiguredFramerate();
172 });
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000173 return true;
174}
175
176void FrameGeneratorCapturer::InsertFrame() {
sprangc5d62e22017-04-02 23:53:04 -0700177 rtc::CritScope cs(&lock_);
178 if (sending_) {
179 VideoFrame* frame = frame_generator_->NextFrame();
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200180 // TODO(srte): Use more advanced frame rate control to allow arbritrary
181 // fractions.
182 int decimation =
183 std::round(static_cast<double>(source_fps_) / target_capture_fps_);
184 for (int i = 1; i < decimation; ++i)
185 frame = frame_generator_->NextFrame();
ilnik04f4d122017-06-19 07:18:55 -0700186 frame->set_timestamp_us(clock_->TimeInMicroseconds());
sprangc5d62e22017-04-02 23:53:04 -0700187 frame->set_ntp_time_ms(clock_->CurrentNtpInMilliseconds());
188 frame->set_rotation(fake_rotation_);
Johannes Kronf7f13e02018-12-12 11:17:43 +0100189 if (fake_color_space_) {
Danil Chapovalovb7698942019-02-05 11:32:19 +0100190 frame->set_color_space(fake_color_space_);
Johannes Kronf7f13e02018-12-12 11:17:43 +0100191 }
sprangc5d62e22017-04-02 23:53:04 -0700192 if (first_frame_capture_time_ == -1) {
193 first_frame_capture_time_ = frame->ntp_time_ms();
194 }
195
Niels Möller3793bb42018-12-20 13:46:06 +0100196 TestVideoCapturer::OnFrame(*frame);
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000197 }
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000198}
199
200void FrameGeneratorCapturer::Start() {
Sebastian Jansson53571c72019-07-31 17:30:03 +0200201 {
202 rtc::CritScope cs(&lock_);
203 sending_ = true;
204 }
205 if (!frame_task_.Running()) {
206 frame_task_ = RepeatingTaskHandle::Start(task_queue_.Get(), [this] {
207 InsertFrame();
208 return TimeDelta::seconds(1) / GetCurrentConfiguredFramerate();
209 });
210 }
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000211}
212
213void FrameGeneratorCapturer::Stop() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200214 rtc::CritScope cs(&lock_);
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000215 sending_ = false;
216}
sprang867fb522015-08-03 04:38:41 -0700217
perkjfa10b552016-10-02 23:45:26 -0700218void FrameGeneratorCapturer::ChangeResolution(size_t width, size_t height) {
219 rtc::CritScope cs(&lock_);
220 frame_generator_->ChangeResolution(width, height);
221}
222
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200223void FrameGeneratorCapturer::ChangeFramerate(int target_framerate) {
224 rtc::CritScope cs(&lock_);
225 RTC_CHECK(target_capture_fps_ > 0);
226 if (target_framerate > source_fps_)
227 RTC_LOG(LS_WARNING) << "Target framerate clamped from " << target_framerate
228 << " to " << source_fps_;
229 if (source_fps_ % target_capture_fps_ != 0) {
230 int decimation =
231 std::round(static_cast<double>(source_fps_) / target_capture_fps_);
232 int effective_rate = target_capture_fps_ / decimation;
233 RTC_LOG(LS_WARNING) << "Target framerate, " << target_framerate
234 << ", is an uneven fraction of the source rate, "
235 << source_fps_
236 << ". The framerate will be :" << effective_rate;
237 }
238 target_capture_fps_ = std::min(source_fps_, target_framerate);
239}
240
perkj803d97f2016-11-01 11:45:46 -0700241void FrameGeneratorCapturer::SetSinkWantsObserver(SinkWantsObserver* observer) {
242 rtc::CritScope cs(&lock_);
243 RTC_DCHECK(!sink_wants_observer_);
244 sink_wants_observer_ = observer;
245}
246
perkja49cbd32016-09-16 07:53:41 -0700247void FrameGeneratorCapturer::AddOrUpdateSink(
248 rtc::VideoSinkInterface<VideoFrame>* sink,
249 const rtc::VideoSinkWants& wants) {
Niels Möller3793bb42018-12-20 13:46:06 +0100250 TestVideoCapturer::AddOrUpdateSink(sink, wants);
perkja49cbd32016-09-16 07:53:41 -0700251 rtc::CritScope cs(&lock_);
Niels Möller3793bb42018-12-20 13:46:06 +0100252 if (sink_wants_observer_) {
253 // Tests need to observe unmodified sink wants.
perkj803d97f2016-11-01 11:45:46 -0700254 sink_wants_observer_->OnSinkWantsChanged(sink, wants);
sprangc5d62e22017-04-02 23:53:04 -0700255 }
Niels Möller3793bb42018-12-20 13:46:06 +0100256 UpdateFps(GetSinkWants().max_framerate_fps);
perkja49cbd32016-09-16 07:53:41 -0700257}
258
259void FrameGeneratorCapturer::RemoveSink(
260 rtc::VideoSinkInterface<VideoFrame>* sink) {
Niels Möller3793bb42018-12-20 13:46:06 +0100261 TestVideoCapturer::RemoveSink(sink);
262
perkja49cbd32016-09-16 07:53:41 -0700263 rtc::CritScope cs(&lock_);
Niels Möller3793bb42018-12-20 13:46:06 +0100264 UpdateFps(GetSinkWants().max_framerate_fps);
265}
266
267void FrameGeneratorCapturer::UpdateFps(int max_fps) {
268 if (max_fps < target_capture_fps_) {
269 wanted_fps_.emplace(max_fps);
270 } else {
271 wanted_fps_.reset();
272 }
perkja49cbd32016-09-16 07:53:41 -0700273}
274
sprang867fb522015-08-03 04:38:41 -0700275void FrameGeneratorCapturer::ForceFrame() {
ilnikbaded152017-03-17 05:55:25 -0700276 // One-time non-repeating task,
Danil Chapovalovabd42732018-09-10 14:07:45 +0200277 task_queue_.PostTask([this] { InsertFrame(); });
sprang867fb522015-08-03 04:38:41 -0700278}
ilnikbaded152017-03-17 05:55:25 -0700279
sprangc5d62e22017-04-02 23:53:04 -0700280int FrameGeneratorCapturer::GetCurrentConfiguredFramerate() {
281 rtc::CritScope cs(&lock_);
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200282 if (wanted_fps_ && *wanted_fps_ < target_capture_fps_)
sprangc5d62e22017-04-02 23:53:04 -0700283 return *wanted_fps_;
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200284 return target_capture_fps_;
sprangc5d62e22017-04-02 23:53:04 -0700285}
286
ilnikbaded152017-03-17 05:55:25 -0700287} // namespace test
288} // namespace webrtc