blob: 88e65ba1fb4a649bb53fc7bd94e0d421f637e503 [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
ilnikbaded152017-03-17 05:55:25 -070013#include <utility>
14#include <vector>
15
Danil Chapovalovabd42732018-09-10 14:07:45 +020016#include "absl/memory/memory.h"
Yves Gerey665174f2018-06-19 15:03:05 +020017#include "call/video_send_stream.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/criticalsection.h"
19#include "rtc_base/logging.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "rtc_base/task_queue.h"
21#include "rtc_base/timeutils.h"
22#include "system_wrappers/include/clock.h"
pbos@webrtc.org26d12102013-05-29 13:41:03 +000023
24namespace webrtc {
25namespace test {
26
ilnikbaded152017-03-17 05:55:25 -070027class FrameGeneratorCapturer::InsertFrameTask : public rtc::QueuedTask {
28 public:
Danil Chapovalovabd42732018-09-10 14:07:45 +020029 explicit InsertFrameTask(FrameGeneratorCapturer* frame_generator_capturer)
ilnikbaded152017-03-17 05:55:25 -070030 : frame_generator_capturer_(frame_generator_capturer),
Danil Chapovalovabd42732018-09-10 14:07:45 +020031 repeat_interval_ms_(-1),
32 next_run_time_ms_(-1) {}
ilnikbaded152017-03-17 05:55:25 -070033
34 private:
35 bool Run() override {
Danil Chapovalovabd42732018-09-10 14:07:45 +020036 // Check if the frame interval for this
37 // task queue is the same same as the current configured frame rate.
38 int interval_ms =
39 1000 / frame_generator_capturer_->GetCurrentConfiguredFramerate();
40 if (repeat_interval_ms_ != interval_ms) {
41 // Restart the timer if frame rate has changed since task was started.
42 next_run_time_ms_ = rtc::TimeMillis();
43 repeat_interval_ms_ = interval_ms;
ilnikbaded152017-03-17 05:55:25 -070044 }
Danil Chapovalovabd42732018-09-10 14:07:45 +020045 // Schedule the next frame capture event to happen at approximately the
46 // correct absolute time point.
47 next_run_time_ms_ += interval_ms;
48
ilnikbaded152017-03-17 05:55:25 -070049 frame_generator_capturer_->InsertFrame();
Danil Chapovalovabd42732018-09-10 14:07:45 +020050
51 int64_t now_ms = rtc::TimeMillis();
52 if (next_run_time_ms_ < now_ms) {
Danil Chapovalovabd42732018-09-10 14:07:45 +020053 RTC_LOG(LS_ERROR) << "Frame Generator Capturer can't keep up with "
Danil Chapovalovafc3eb12018-09-12 12:53:10 +020054 "requested fps.";
55 rtc::TaskQueue::Current()->PostTask(absl::WrapUnique(this));
56 } else {
57 int64_t delay_ms = next_run_time_ms_ - now_ms;
58 RTC_DCHECK_GE(delay_ms, 0);
59 RTC_DCHECK_LE(delay_ms, interval_ms);
60 rtc::TaskQueue::Current()->PostDelayedTask(absl::WrapUnique(this),
61 delay_ms);
Danil Chapovalovabd42732018-09-10 14:07:45 +020062 }
Danil Chapovalovabd42732018-09-10 14:07:45 +020063 return false;
ilnikbaded152017-03-17 05:55:25 -070064 }
65
66 webrtc::test::FrameGeneratorCapturer* const frame_generator_capturer_;
Danil Chapovalovabd42732018-09-10 14:07:45 +020067 int repeat_interval_ms_;
68 int64_t next_run_time_ms_;
ilnikbaded152017-03-17 05:55:25 -070069};
70
Emircan Uysaler03e6ec92018-03-09 15:03:26 -080071FrameGeneratorCapturer* FrameGeneratorCapturer::Create(
72 int width,
73 int height,
Danil Chapovalov431abd92018-06-18 12:54:17 +020074 absl::optional<FrameGenerator::OutputType> type,
75 absl::optional<int> num_squares,
Emircan Uysaler03e6ec92018-03-09 15:03:26 -080076 int target_fps,
77 Clock* clock) {
Danil Chapovalovabd42732018-09-10 14:07:45 +020078 auto capturer = absl::make_unique<FrameGeneratorCapturer>(
Emircan Uysaler03e6ec92018-03-09 15:03:26 -080079 clock,
80 FrameGenerator::CreateSquareGenerator(width, height, type, num_squares),
Danil Chapovalovabd42732018-09-10 14:07:45 +020081 target_fps);
Taylor Brandstetter081136f2018-03-08 01:54:10 +000082 if (!capturer->Init())
83 return nullptr;
84
85 return capturer.release();
86}
87
andresp@webrtc.orgab654952013-09-19 12:14:03 +000088FrameGeneratorCapturer* FrameGeneratorCapturer::CreateFromYuvFile(
sprang@webrtc.org131bea82015-02-18 12:46:06 +000089 const std::string& file_name,
andresp@webrtc.orgab654952013-09-19 12:14:03 +000090 size_t width,
91 size_t height,
92 int target_fps,
93 Clock* clock) {
Danil Chapovalovabd42732018-09-10 14:07:45 +020094 auto capturer = absl::make_unique<FrameGeneratorCapturer>(
sprangc5d62e22017-04-02 23:53:04 -070095 clock,
96 FrameGenerator::CreateFromYuvFile(std::vector<std::string>(1, file_name),
97 width, height, 1),
Danil Chapovalovabd42732018-09-10 14:07:45 +020098 target_fps);
sprangc5d62e22017-04-02 23:53:04 -070099 if (!capturer->Init())
100 return nullptr;
andresp@webrtc.orgab654952013-09-19 12:14:03 +0000101
sprangc5d62e22017-04-02 23:53:04 -0700102 return capturer.release();
andresp@webrtc.orgab654952013-09-19 12:14:03 +0000103}
104
erikvarga579de6f2017-08-29 09:12:57 -0700105FrameGeneratorCapturer* FrameGeneratorCapturer::CreateSlideGenerator(
106 int width,
107 int height,
108 int frame_repeat_count,
109 int target_fps,
110 Clock* clock) {
Danil Chapovalovabd42732018-09-10 14:07:45 +0200111 auto capturer = absl::make_unique<FrameGeneratorCapturer>(
Yves Gerey665174f2018-06-19 15:03:05 +0200112 clock,
113 FrameGenerator::CreateSlideGenerator(width, height, frame_repeat_count),
Danil Chapovalovabd42732018-09-10 14:07:45 +0200114 target_fps);
erikvarga579de6f2017-08-29 09:12:57 -0700115 if (!capturer->Init())
116 return nullptr;
117
118 return capturer.release();
119}
120
perkja8ba1952017-02-27 06:52:10 -0800121FrameGeneratorCapturer::FrameGeneratorCapturer(
122 Clock* clock,
123 std::unique_ptr<FrameGenerator> frame_generator,
124 int target_fps)
perkja49cbd32016-09-16 07:53:41 -0700125 : clock_(clock),
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000126 sending_(false),
perkja49cbd32016-09-16 07:53:41 -0700127 sink_(nullptr),
perkj803d97f2016-11-01 11:45:46 -0700128 sink_wants_observer_(nullptr),
perkja8ba1952017-02-27 06:52:10 -0800129 frame_generator_(std::move(frame_generator)),
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200130 source_fps_(target_fps),
131 target_capture_fps_(target_fps),
ilnikbaded152017-03-17 05:55:25 -0700132 first_frame_capture_time_(-1),
Yves Gerey665174f2018-06-19 15:03:05 +0200133 task_queue_("FrameGenCapQ", rtc::TaskQueue::Priority::HIGH) {
perkja8ba1952017-02-27 06:52:10 -0800134 RTC_DCHECK(frame_generator_);
perkja49cbd32016-09-16 07:53:41 -0700135 RTC_DCHECK_GT(target_fps, 0);
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000136}
137
138FrameGeneratorCapturer::~FrameGeneratorCapturer() {
139 Stop();
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000140}
141
Perba7dc722016-04-19 15:01:23 +0200142void FrameGeneratorCapturer::SetFakeRotation(VideoRotation rotation) {
143 rtc::CritScope cs(&lock_);
144 fake_rotation_ = rotation;
145}
146
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000147bool FrameGeneratorCapturer::Init() {
pbos@webrtc.org94015242013-10-16 11:05:37 +0000148 // This check is added because frame_generator_ might be file based and should
149 // not crash because a file moved.
sprangc5d62e22017-04-02 23:53:04 -0700150 if (frame_generator_.get() == nullptr)
pbos@webrtc.org94015242013-10-16 11:05:37 +0000151 return false;
152
sprangc5d62e22017-04-02 23:53:04 -0700153 int framerate_fps = GetCurrentConfiguredFramerate();
Danil Chapovalovabd42732018-09-10 14:07:45 +0200154 task_queue_.PostDelayedTask(absl::make_unique<InsertFrameTask>(this),
155 1000 / framerate_fps);
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000156
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000157 return true;
158}
159
160void FrameGeneratorCapturer::InsertFrame() {
sprangc5d62e22017-04-02 23:53:04 -0700161 rtc::CritScope cs(&lock_);
162 if (sending_) {
163 VideoFrame* frame = frame_generator_->NextFrame();
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200164 // TODO(srte): Use more advanced frame rate control to allow arbritrary
165 // fractions.
166 int decimation =
167 std::round(static_cast<double>(source_fps_) / target_capture_fps_);
168 for (int i = 1; i < decimation; ++i)
169 frame = frame_generator_->NextFrame();
ilnik04f4d122017-06-19 07:18:55 -0700170 frame->set_timestamp_us(clock_->TimeInMicroseconds());
sprangc5d62e22017-04-02 23:53:04 -0700171 frame->set_ntp_time_ms(clock_->CurrentNtpInMilliseconds());
172 frame->set_rotation(fake_rotation_);
173 if (first_frame_capture_time_ == -1) {
174 first_frame_capture_time_ = frame->ntp_time_ms();
175 }
176
177 if (sink_) {
Danil Chapovalov431abd92018-06-18 12:54:17 +0200178 absl::optional<VideoFrame> out_frame = AdaptFrame(*frame);
sprangc5d62e22017-04-02 23:53:04 -0700179 if (out_frame)
180 sink_->OnFrame(*out_frame);
andresp@webrtc.orgab654952013-09-19 12:14:03 +0000181 }
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000182 }
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000183}
184
185void FrameGeneratorCapturer::Start() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200186 rtc::CritScope cs(&lock_);
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000187 sending_ = true;
188}
189
190void FrameGeneratorCapturer::Stop() {
Peter Boströmf2f82832015-05-01 13:00:41 +0200191 rtc::CritScope cs(&lock_);
pbos@webrtc.org26d12102013-05-29 13:41:03 +0000192 sending_ = false;
193}
sprang867fb522015-08-03 04:38:41 -0700194
perkjfa10b552016-10-02 23:45:26 -0700195void FrameGeneratorCapturer::ChangeResolution(size_t width, size_t height) {
196 rtc::CritScope cs(&lock_);
197 frame_generator_->ChangeResolution(width, height);
198}
199
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200200void FrameGeneratorCapturer::ChangeFramerate(int target_framerate) {
201 rtc::CritScope cs(&lock_);
202 RTC_CHECK(target_capture_fps_ > 0);
203 if (target_framerate > source_fps_)
204 RTC_LOG(LS_WARNING) << "Target framerate clamped from " << target_framerate
205 << " to " << source_fps_;
206 if (source_fps_ % target_capture_fps_ != 0) {
207 int decimation =
208 std::round(static_cast<double>(source_fps_) / target_capture_fps_);
209 int effective_rate = target_capture_fps_ / decimation;
210 RTC_LOG(LS_WARNING) << "Target framerate, " << target_framerate
211 << ", is an uneven fraction of the source rate, "
212 << source_fps_
213 << ". The framerate will be :" << effective_rate;
214 }
215 target_capture_fps_ = std::min(source_fps_, target_framerate);
216}
217
perkj803d97f2016-11-01 11:45:46 -0700218void FrameGeneratorCapturer::SetSinkWantsObserver(SinkWantsObserver* observer) {
219 rtc::CritScope cs(&lock_);
220 RTC_DCHECK(!sink_wants_observer_);
221 sink_wants_observer_ = observer;
222}
223
perkja49cbd32016-09-16 07:53:41 -0700224void FrameGeneratorCapturer::AddOrUpdateSink(
225 rtc::VideoSinkInterface<VideoFrame>* sink,
226 const rtc::VideoSinkWants& wants) {
227 rtc::CritScope cs(&lock_);
perkj803d97f2016-11-01 11:45:46 -0700228 RTC_CHECK(!sink_ || sink_ == sink);
perkja49cbd32016-09-16 07:53:41 -0700229 sink_ = sink;
perkj803d97f2016-11-01 11:45:46 -0700230 if (sink_wants_observer_)
231 sink_wants_observer_->OnSinkWantsChanged(sink, wants);
sprangc5d62e22017-04-02 23:53:04 -0700232
233 // Handle framerate within this class, just pass on resolution for possible
234 // adaptation.
235 rtc::VideoSinkWants resolution_wants = wants;
236 resolution_wants.max_framerate_fps = std::numeric_limits<int>::max();
Sebastian Janssonf1f363f2018-08-13 14:24:58 +0200237 TestVideoCapturer::AddOrUpdateSink(sink, resolution_wants);
sprangc5d62e22017-04-02 23:53:04 -0700238
239 // Ignore any requests for framerate higher than initially configured.
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200240 if (wants.max_framerate_fps < target_capture_fps_) {
sprangc5d62e22017-04-02 23:53:04 -0700241 wanted_fps_.emplace(wants.max_framerate_fps);
242 } else {
243 wanted_fps_.reset();
244 }
perkja49cbd32016-09-16 07:53:41 -0700245}
246
247void FrameGeneratorCapturer::RemoveSink(
248 rtc::VideoSinkInterface<VideoFrame>* sink) {
249 rtc::CritScope cs(&lock_);
250 RTC_CHECK(sink_ == sink);
251 sink_ = nullptr;
252}
253
sprang867fb522015-08-03 04:38:41 -0700254void FrameGeneratorCapturer::ForceFrame() {
ilnikbaded152017-03-17 05:55:25 -0700255 // One-time non-repeating task,
Danil Chapovalovabd42732018-09-10 14:07:45 +0200256 task_queue_.PostTask([this] { InsertFrame(); });
sprang867fb522015-08-03 04:38:41 -0700257}
ilnikbaded152017-03-17 05:55:25 -0700258
sprangc5d62e22017-04-02 23:53:04 -0700259int FrameGeneratorCapturer::GetCurrentConfiguredFramerate() {
260 rtc::CritScope cs(&lock_);
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200261 if (wanted_fps_ && *wanted_fps_ < target_capture_fps_)
sprangc5d62e22017-04-02 23:53:04 -0700262 return *wanted_fps_;
Sebastian Janssonba3decf2018-08-30 11:19:23 +0200263 return target_capture_fps_;
sprangc5d62e22017-04-02 23:53:04 -0700264}
265
ilnikbaded152017-03-17 05:55:25 -0700266} // namespace test
267} // namespace webrtc