• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
11 #include "test/frame_generator_capturer.h"
12 
13 #include <algorithm>
14 #include <cmath>
15 #include <limits>
16 #include <memory>
17 #include <utility>
18 #include <vector>
19 
20 #include "absl/strings/match.h"
21 #include "api/test/create_frame_generator.h"
22 #include "rtc_base/checks.h"
23 #include "rtc_base/logging.h"
24 #include "rtc_base/task_queue.h"
25 #include "rtc_base/time_utils.h"
26 #include "system_wrappers/include/clock.h"
27 #include "test/testsupport/file_utils.h"
28 
29 namespace webrtc {
30 namespace test {
31 namespace {
TransformFilePath(std::string path)32 std::string TransformFilePath(std::string path) {
33   static const std::string resource_prefix = "res://";
34   int ext_pos = path.rfind(".");
35   if (ext_pos < 0) {
36     return test::ResourcePath(path, "yuv");
37   } else if (absl::StartsWith(path, resource_prefix)) {
38     std::string name = path.substr(resource_prefix.length(), ext_pos);
39     std::string ext = path.substr(ext_pos, path.size());
40     return test::ResourcePath(name, ext);
41   }
42   return path;
43 }
44 }  // namespace
45 
FrameGeneratorCapturer(Clock * clock,std::unique_ptr<FrameGeneratorInterface> frame_generator,int target_fps,TaskQueueFactory & task_queue_factory)46 FrameGeneratorCapturer::FrameGeneratorCapturer(
47     Clock* clock,
48     std::unique_ptr<FrameGeneratorInterface> frame_generator,
49     int target_fps,
50     TaskQueueFactory& task_queue_factory)
51     : clock_(clock),
52       sending_(true),
53       sink_wants_observer_(nullptr),
54       frame_generator_(std::move(frame_generator)),
55       source_fps_(target_fps),
56       target_capture_fps_(target_fps),
57       first_frame_capture_time_(-1),
58       task_queue_(task_queue_factory.CreateTaskQueue(
59           "FrameGenCapQ",
60           TaskQueueFactory::Priority::HIGH)) {
61   RTC_DCHECK(frame_generator_);
62   RTC_DCHECK_GT(target_fps, 0);
63 }
64 
~FrameGeneratorCapturer()65 FrameGeneratorCapturer::~FrameGeneratorCapturer() {
66   Stop();
67 }
68 
Create(Clock * clock,TaskQueueFactory & task_queue_factory,FrameGeneratorCapturerConfig::SquaresVideo config)69 std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
70     Clock* clock,
71     TaskQueueFactory& task_queue_factory,
72     FrameGeneratorCapturerConfig::SquaresVideo config) {
73   return std::make_unique<FrameGeneratorCapturer>(
74       clock,
75       CreateSquareFrameGenerator(config.width, config.height,
76                                  config.pixel_format, config.num_squares),
77       config.framerate, task_queue_factory);
78 }
Create(Clock * clock,TaskQueueFactory & task_queue_factory,FrameGeneratorCapturerConfig::SquareSlides config)79 std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
80     Clock* clock,
81     TaskQueueFactory& task_queue_factory,
82     FrameGeneratorCapturerConfig::SquareSlides config) {
83   return std::make_unique<FrameGeneratorCapturer>(
84       clock,
85       CreateSlideFrameGenerator(
86           config.width, config.height,
87           /*frame_repeat_count*/ config.change_interval.seconds<double>() *
88               config.framerate),
89       config.framerate, task_queue_factory);
90 }
Create(Clock * clock,TaskQueueFactory & task_queue_factory,FrameGeneratorCapturerConfig::VideoFile config)91 std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
92     Clock* clock,
93     TaskQueueFactory& task_queue_factory,
94     FrameGeneratorCapturerConfig::VideoFile config) {
95   RTC_CHECK(config.width && config.height);
96   return std::make_unique<FrameGeneratorCapturer>(
97       clock,
98       CreateFromYuvFileFrameGenerator({TransformFilePath(config.name)},
99                                       config.width, config.height,
100                                       /*frame_repeat_count*/ 1),
101       config.framerate, task_queue_factory);
102 }
103 
Create(Clock * clock,TaskQueueFactory & task_queue_factory,FrameGeneratorCapturerConfig::ImageSlides config)104 std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
105     Clock* clock,
106     TaskQueueFactory& task_queue_factory,
107     FrameGeneratorCapturerConfig::ImageSlides config) {
108   std::unique_ptr<FrameGeneratorInterface> slides_generator;
109   std::vector<std::string> paths = config.paths;
110   for (std::string& path : paths)
111     path = TransformFilePath(path);
112 
113   if (config.crop.width || config.crop.height) {
114     TimeDelta pause_duration =
115         config.change_interval - config.crop.scroll_duration;
116     RTC_CHECK_GE(pause_duration, TimeDelta::Zero());
117     int crop_width = config.crop.width.value_or(config.width);
118     int crop_height = config.crop.height.value_or(config.height);
119     RTC_CHECK_LE(crop_width, config.width);
120     RTC_CHECK_LE(crop_height, config.height);
121     slides_generator = CreateScrollingInputFromYuvFilesFrameGenerator(
122         clock, paths, config.width, config.height, crop_width, crop_height,
123         config.crop.scroll_duration.ms(), pause_duration.ms());
124   } else {
125     slides_generator = CreateFromYuvFileFrameGenerator(
126         paths, config.width, config.height,
127         /*frame_repeat_count*/ config.change_interval.seconds<double>() *
128             config.framerate);
129   }
130   return std::make_unique<FrameGeneratorCapturer>(
131       clock, std::move(slides_generator), config.framerate, task_queue_factory);
132 }
133 
Create(Clock * clock,TaskQueueFactory & task_queue_factory,const FrameGeneratorCapturerConfig & config)134 std::unique_ptr<FrameGeneratorCapturer> FrameGeneratorCapturer::Create(
135     Clock* clock,
136     TaskQueueFactory& task_queue_factory,
137     const FrameGeneratorCapturerConfig& config) {
138   if (config.video_file) {
139     return Create(clock, task_queue_factory, *config.video_file);
140   } else if (config.image_slides) {
141     return Create(clock, task_queue_factory, *config.image_slides);
142   } else if (config.squares_slides) {
143     return Create(clock, task_queue_factory, *config.squares_slides);
144   } else {
145     return Create(clock, task_queue_factory,
146                   config.squares_video.value_or(
147                       FrameGeneratorCapturerConfig::SquaresVideo()));
148   }
149 }
150 
SetFakeRotation(VideoRotation rotation)151 void FrameGeneratorCapturer::SetFakeRotation(VideoRotation rotation) {
152   MutexLock lock(&lock_);
153   fake_rotation_ = rotation;
154 }
155 
SetFakeColorSpace(absl::optional<ColorSpace> color_space)156 void FrameGeneratorCapturer::SetFakeColorSpace(
157     absl::optional<ColorSpace> color_space) {
158   MutexLock lock(&lock_);
159   fake_color_space_ = color_space;
160 }
161 
Init()162 bool FrameGeneratorCapturer::Init() {
163   // This check is added because frame_generator_ might be file based and should
164   // not crash because a file moved.
165   if (frame_generator_.get() == nullptr)
166     return false;
167 
168   frame_task_ = RepeatingTaskHandle::DelayedStart(
169       task_queue_.Get(),
170       TimeDelta::Seconds(1) / GetCurrentConfiguredFramerate(), [this] {
171         InsertFrame();
172         return TimeDelta::Seconds(1) / GetCurrentConfiguredFramerate();
173       });
174   return true;
175 }
176 
InsertFrame()177 void FrameGeneratorCapturer::InsertFrame() {
178   MutexLock lock(&lock_);
179   if (sending_) {
180     FrameGeneratorInterface::VideoFrameData frame_data =
181         frame_generator_->NextFrame();
182     // TODO(srte): Use more advanced frame rate control to allow arbritrary
183     // fractions.
184     int decimation =
185         std::round(static_cast<double>(source_fps_) / target_capture_fps_);
186     for (int i = 1; i < decimation; ++i)
187       frame_data = frame_generator_->NextFrame();
188 
189     VideoFrame frame = VideoFrame::Builder()
190                            .set_video_frame_buffer(frame_data.buffer)
191                            .set_rotation(fake_rotation_)
192                            .set_timestamp_us(clock_->TimeInMicroseconds())
193                            .set_ntp_time_ms(clock_->CurrentNtpInMilliseconds())
194                            .set_update_rect(frame_data.update_rect)
195                            .set_color_space(fake_color_space_)
196                            .build();
197     if (first_frame_capture_time_ == -1) {
198       first_frame_capture_time_ = frame.ntp_time_ms();
199     }
200 
201     TestVideoCapturer::OnFrame(frame);
202   }
203 }
204 
Start()205 void FrameGeneratorCapturer::Start() {
206   {
207     MutexLock lock(&lock_);
208     sending_ = true;
209   }
210   if (!frame_task_.Running()) {
211     frame_task_ = RepeatingTaskHandle::Start(task_queue_.Get(), [this] {
212       InsertFrame();
213       return TimeDelta::Seconds(1) / GetCurrentConfiguredFramerate();
214     });
215   }
216 }
217 
Stop()218 void FrameGeneratorCapturer::Stop() {
219   MutexLock lock(&lock_);
220   sending_ = false;
221 }
222 
ChangeResolution(size_t width,size_t height)223 void FrameGeneratorCapturer::ChangeResolution(size_t width, size_t height) {
224   MutexLock lock(&lock_);
225   frame_generator_->ChangeResolution(width, height);
226 }
227 
ChangeFramerate(int target_framerate)228 void FrameGeneratorCapturer::ChangeFramerate(int target_framerate) {
229   MutexLock lock(&lock_);
230   RTC_CHECK(target_capture_fps_ > 0);
231   if (target_framerate > source_fps_)
232     RTC_LOG(LS_WARNING) << "Target framerate clamped from " << target_framerate
233                         << " to " << source_fps_;
234   if (source_fps_ % target_capture_fps_ != 0) {
235     int decimation =
236         std::round(static_cast<double>(source_fps_) / target_capture_fps_);
237     int effective_rate = target_capture_fps_ / decimation;
238     RTC_LOG(LS_WARNING) << "Target framerate, " << target_framerate
239                         << ", is an uneven fraction of the source rate, "
240                         << source_fps_
241                         << ". The framerate will be :" << effective_rate;
242   }
243   target_capture_fps_ = std::min(source_fps_, target_framerate);
244 }
245 
SetSinkWantsObserver(SinkWantsObserver * observer)246 void FrameGeneratorCapturer::SetSinkWantsObserver(SinkWantsObserver* observer) {
247   MutexLock lock(&lock_);
248   RTC_DCHECK(!sink_wants_observer_);
249   sink_wants_observer_ = observer;
250 }
251 
AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame> * sink,const rtc::VideoSinkWants & wants)252 void FrameGeneratorCapturer::AddOrUpdateSink(
253     rtc::VideoSinkInterface<VideoFrame>* sink,
254     const rtc::VideoSinkWants& wants) {
255   TestVideoCapturer::AddOrUpdateSink(sink, wants);
256   MutexLock lock(&lock_);
257   if (sink_wants_observer_) {
258     // Tests need to observe unmodified sink wants.
259     sink_wants_observer_->OnSinkWantsChanged(sink, wants);
260   }
261   UpdateFps(GetSinkWants().max_framerate_fps);
262 }
263 
RemoveSink(rtc::VideoSinkInterface<VideoFrame> * sink)264 void FrameGeneratorCapturer::RemoveSink(
265     rtc::VideoSinkInterface<VideoFrame>* sink) {
266   TestVideoCapturer::RemoveSink(sink);
267 
268   MutexLock lock(&lock_);
269   UpdateFps(GetSinkWants().max_framerate_fps);
270 }
271 
UpdateFps(int max_fps)272 void FrameGeneratorCapturer::UpdateFps(int max_fps) {
273   if (max_fps < target_capture_fps_) {
274     wanted_fps_.emplace(max_fps);
275   } else {
276     wanted_fps_.reset();
277   }
278 }
279 
ForceFrame()280 void FrameGeneratorCapturer::ForceFrame() {
281   // One-time non-repeating task,
282   task_queue_.PostTask([this] { InsertFrame(); });
283 }
284 
GetCurrentConfiguredFramerate()285 int FrameGeneratorCapturer::GetCurrentConfiguredFramerate() {
286   MutexLock lock(&lock_);
287   if (wanted_fps_ && *wanted_fps_ < target_capture_fps_)
288     return *wanted_fps_;
289   return target_capture_fps_;
290 }
291 
292 }  // namespace test
293 }  // namespace webrtc
294