webrtc_m130/test/frame_generator_capturer.cc
Niels Möller 3793bb447a Refactor TestVideoCapturer to support multiple sinks.
To be able to reuse VideoBroadcaster, that class needs to be
officially threadsafe. It already had the needed locks, but thread
checkers have to be deleted to allow calls to AddOrUpdateSink on
multiple threads (worker thread + encoder thread).

Bug: webrtc:6353, webrtc:10147
Change-Id: I16128ac205c566f09402b6f22587a340d9a983c1
Reviewed-on: https://webrtc-review.googlesource.com/c/115201
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Commit-Queue: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26073}
2018-12-20 14:04:16 +00:00

273 lines
8.3 KiB
C++

/*
* Copyright (c) 2013 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 <algorithm>
#include <cmath>
#include <limits>
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "rtc_base/checks.h"
#include "rtc_base/criticalsection.h"
#include "rtc_base/logging.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/timeutils.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
namespace test {
class FrameGeneratorCapturer::InsertFrameTask : public rtc::QueuedTask {
public:
explicit InsertFrameTask(FrameGeneratorCapturer* frame_generator_capturer)
: frame_generator_capturer_(frame_generator_capturer),
repeat_interval_ms_(-1),
next_run_time_ms_(-1) {}
private:
bool Run() override {
// Check if the frame interval for this
// task queue is the same same as the current configured frame rate.
int interval_ms =
1000 / frame_generator_capturer_->GetCurrentConfiguredFramerate();
if (repeat_interval_ms_ != interval_ms) {
// Restart the timer if frame rate has changed since task was started.
next_run_time_ms_ = rtc::TimeMillis();
repeat_interval_ms_ = interval_ms;
}
// Schedule the next frame capture event to happen at approximately the
// correct absolute time point.
next_run_time_ms_ += interval_ms;
frame_generator_capturer_->InsertFrame();
int64_t now_ms = rtc::TimeMillis();
if (next_run_time_ms_ < now_ms) {
RTC_LOG(LS_ERROR) << "Frame Generator Capturer can't keep up with "
"requested fps.";
rtc::TaskQueue::Current()->PostTask(absl::WrapUnique(this));
} else {
int64_t delay_ms = next_run_time_ms_ - now_ms;
RTC_DCHECK_GE(delay_ms, 0);
RTC_DCHECK_LE(delay_ms, interval_ms);
rtc::TaskQueue::Current()->PostDelayedTask(absl::WrapUnique(this),
delay_ms);
}
return false;
}
webrtc::test::FrameGeneratorCapturer* const frame_generator_capturer_;
int repeat_interval_ms_;
int64_t next_run_time_ms_;
};
FrameGeneratorCapturer* FrameGeneratorCapturer::Create(
int width,
int height,
absl::optional<FrameGenerator::OutputType> type,
absl::optional<int> num_squares,
int target_fps,
Clock* clock) {
auto capturer = absl::make_unique<FrameGeneratorCapturer>(
clock,
FrameGenerator::CreateSquareGenerator(width, height, type, num_squares),
target_fps);
if (!capturer->Init())
return nullptr;
return capturer.release();
}
FrameGeneratorCapturer* FrameGeneratorCapturer::CreateFromYuvFile(
const std::string& file_name,
size_t width,
size_t height,
int target_fps,
Clock* clock) {
auto capturer = absl::make_unique<FrameGeneratorCapturer>(
clock,
FrameGenerator::CreateFromYuvFile(std::vector<std::string>(1, file_name),
width, height, 1),
target_fps);
if (!capturer->Init())
return nullptr;
return capturer.release();
}
FrameGeneratorCapturer* FrameGeneratorCapturer::CreateSlideGenerator(
int width,
int height,
int frame_repeat_count,
int target_fps,
Clock* clock) {
auto capturer = absl::make_unique<FrameGeneratorCapturer>(
clock,
FrameGenerator::CreateSlideGenerator(width, height, frame_repeat_count),
target_fps);
if (!capturer->Init())
return nullptr;
return capturer.release();
}
FrameGeneratorCapturer::FrameGeneratorCapturer(
Clock* clock,
std::unique_ptr<FrameGenerator> frame_generator,
int target_fps)
: clock_(clock),
sending_(true),
sink_wants_observer_(nullptr),
frame_generator_(std::move(frame_generator)),
source_fps_(target_fps),
target_capture_fps_(target_fps),
first_frame_capture_time_(-1),
task_queue_("FrameGenCapQ", rtc::TaskQueue::Priority::HIGH) {
RTC_DCHECK(frame_generator_);
RTC_DCHECK_GT(target_fps, 0);
}
FrameGeneratorCapturer::~FrameGeneratorCapturer() {
Stop();
}
void FrameGeneratorCapturer::SetFakeRotation(VideoRotation rotation) {
rtc::CritScope cs(&lock_);
fake_rotation_ = rotation;
}
void FrameGeneratorCapturer::SetFakeColorSpace(
absl::optional<ColorSpace> color_space) {
rtc::CritScope cs(&lock_);
fake_color_space_ = color_space;
}
bool FrameGeneratorCapturer::Init() {
// This check is added because frame_generator_ might be file based and should
// not crash because a file moved.
if (frame_generator_.get() == nullptr)
return false;
int framerate_fps = GetCurrentConfiguredFramerate();
task_queue_.PostDelayedTask(absl::make_unique<InsertFrameTask>(this),
1000 / framerate_fps);
return true;
}
void FrameGeneratorCapturer::InsertFrame() {
rtc::CritScope cs(&lock_);
if (sending_) {
VideoFrame* frame = frame_generator_->NextFrame();
// TODO(srte): Use more advanced frame rate control to allow arbritrary
// fractions.
int decimation =
std::round(static_cast<double>(source_fps_) / target_capture_fps_);
for (int i = 1; i < decimation; ++i)
frame = frame_generator_->NextFrame();
frame->set_timestamp_us(clock_->TimeInMicroseconds());
frame->set_ntp_time_ms(clock_->CurrentNtpInMilliseconds());
frame->set_rotation(fake_rotation_);
if (fake_color_space_) {
frame->set_color_space(&fake_color_space_.value());
}
if (first_frame_capture_time_ == -1) {
first_frame_capture_time_ = frame->ntp_time_ms();
}
TestVideoCapturer::OnFrame(*frame);
}
}
void FrameGeneratorCapturer::Start() {
rtc::CritScope cs(&lock_);
sending_ = true;
}
void FrameGeneratorCapturer::Stop() {
rtc::CritScope cs(&lock_);
sending_ = false;
}
void FrameGeneratorCapturer::ChangeResolution(size_t width, size_t height) {
rtc::CritScope cs(&lock_);
frame_generator_->ChangeResolution(width, height);
}
void FrameGeneratorCapturer::ChangeFramerate(int target_framerate) {
rtc::CritScope cs(&lock_);
RTC_CHECK(target_capture_fps_ > 0);
if (target_framerate > source_fps_)
RTC_LOG(LS_WARNING) << "Target framerate clamped from " << target_framerate
<< " to " << source_fps_;
if (source_fps_ % target_capture_fps_ != 0) {
int decimation =
std::round(static_cast<double>(source_fps_) / target_capture_fps_);
int effective_rate = target_capture_fps_ / decimation;
RTC_LOG(LS_WARNING) << "Target framerate, " << target_framerate
<< ", is an uneven fraction of the source rate, "
<< source_fps_
<< ". The framerate will be :" << effective_rate;
}
target_capture_fps_ = std::min(source_fps_, target_framerate);
}
void FrameGeneratorCapturer::SetSinkWantsObserver(SinkWantsObserver* observer) {
rtc::CritScope cs(&lock_);
RTC_DCHECK(!sink_wants_observer_);
sink_wants_observer_ = observer;
}
void FrameGeneratorCapturer::AddOrUpdateSink(
rtc::VideoSinkInterface<VideoFrame>* sink,
const rtc::VideoSinkWants& wants) {
TestVideoCapturer::AddOrUpdateSink(sink, wants);
rtc::CritScope cs(&lock_);
if (sink_wants_observer_) {
// Tests need to observe unmodified sink wants.
sink_wants_observer_->OnSinkWantsChanged(sink, wants);
}
UpdateFps(GetSinkWants().max_framerate_fps);
}
void FrameGeneratorCapturer::RemoveSink(
rtc::VideoSinkInterface<VideoFrame>* sink) {
TestVideoCapturer::RemoveSink(sink);
rtc::CritScope cs(&lock_);
UpdateFps(GetSinkWants().max_framerate_fps);
}
void FrameGeneratorCapturer::UpdateFps(int max_fps) {
if (max_fps < target_capture_fps_) {
wanted_fps_.emplace(max_fps);
} else {
wanted_fps_.reset();
}
}
void FrameGeneratorCapturer::ForceFrame() {
// One-time non-repeating task,
task_queue_.PostTask([this] { InsertFrame(); });
}
int FrameGeneratorCapturer::GetCurrentConfiguredFramerate() {
rtc::CritScope cs(&lock_);
if (wanted_fps_ && *wanted_fps_ < target_capture_fps_)
return *wanted_fps_;
return target_capture_fps_;
}
} // namespace test
} // namespace webrtc