Zero-hertz encoding mode: avoid encoder bitrate overshooting.

The encoders wrapped in VideoStreamEncoder grossly over-estimates
available bitrate when capture FPS falls close to zero, and frames
re-commence highly frequent delivery. Avoid this by moving the input
RateStatistics inside VSE into the frame cadence adapter, and changing
the reported framerate under zero-hertz encoding mode to always return
the configured max FPS.

Bug: chromium:1255737
Change-Id: Iaa71ef51c0755b12e24e435d86d9562122ed494e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/239126
Commit-Queue: Markus Handell <handellm@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35431}
This commit is contained in:
Markus Handell 2021-11-29 12:35:12 +01:00 committed by WebRTC LUCI CQ
parent b55c299f4d
commit ee22543829
7 changed files with 344 additions and 31 deletions

View File

@ -273,6 +273,7 @@ rtc_library("frame_cadence_adapter") {
"../rtc_base:macromagic",
"../rtc_base:rtc_base_approved",
"../rtc_base/synchronization:mutex",
"../rtc_base/system:no_unique_address",
"../rtc_base/task_utils:pending_task_safety_flag",
"../rtc_base/task_utils:to_queued_task",
"../system_wrappers",

View File

@ -18,7 +18,9 @@
#include "api/task_queue/task_queue_base.h"
#include "rtc_base/logging.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/rate_statistics.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/task_utils/pending_task_safety_flag.h"
#include "rtc_base/task_utils/to_queued_task.h"
#include "system_wrappers/include/clock.h"
@ -28,6 +30,80 @@
namespace webrtc {
namespace {
// Abstracts concrete modes of the cadence adapter.
class AdapterMode {
public:
virtual ~AdapterMode() = default;
// Called on the worker thread for every frame that enters.
virtual void OnFrame(Timestamp post_time,
int frames_scheduled_for_processing,
const VideoFrame& frame) = 0;
// Returns the currently estimated input framerate.
virtual absl::optional<uint32_t> GetInputFrameRateFps() = 0;
// Updates the frame rate.
virtual void UpdateFrameRate() = 0;
};
// Implements a pass-through adapter. Single-threaded.
class PassthroughAdapterMode : public AdapterMode {
public:
PassthroughAdapterMode(Clock* clock,
FrameCadenceAdapterInterface::Callback* callback)
: clock_(clock), callback_(callback) {
sequence_checker_.Detach();
}
// Adapter overrides.
void OnFrame(Timestamp post_time,
int frames_scheduled_for_processing,
const VideoFrame& frame) override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
callback_->OnFrame(post_time, frames_scheduled_for_processing, frame);
}
absl::optional<uint32_t> GetInputFrameRateFps() override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return input_framerate_.Rate(clock_->TimeInMilliseconds());
}
void UpdateFrameRate() override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
input_framerate_.Update(1, clock_->TimeInMilliseconds());
}
private:
Clock* const clock_;
FrameCadenceAdapterInterface::Callback* const callback_;
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
// Input frame rate statistics for use when not in zero-hertz mode.
RateStatistics input_framerate_ RTC_GUARDED_BY(sequence_checker_){
FrameCadenceAdapterInterface::kFrameRateAveragingWindowSizeMs, 1000};
};
// Implements a frame cadence adapter supporting zero-hertz input.
class ZeroHertzAdapterMode : public AdapterMode {
public:
ZeroHertzAdapterMode(FrameCadenceAdapterInterface::Callback* callback,
double max_fps);
// Adapter overrides.
void OnFrame(Timestamp post_time,
int frames_scheduled_for_processing,
const VideoFrame& frame) override;
absl::optional<uint32_t> GetInputFrameRateFps() override;
void UpdateFrameRate() override {}
private:
FrameCadenceAdapterInterface::Callback* const callback_;
// The configured max_fps.
// TODO(crbug.com/1255737): support max_fps updates.
const double max_fps_;
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
};
class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
public:
FrameCadenceAdapterImpl(Clock* clock, TaskQueueBase* queue);
@ -35,6 +111,8 @@ class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
// FrameCadenceAdapterInterface overrides.
void Initialize(Callback* callback) override;
void SetZeroHertzModeEnabled(bool enabled) override;
absl::optional<uint32_t> GetInputFrameRateFps() override;
void UpdateFrameRate() override;
// VideoFrameSink overrides.
void OnFrame(const VideoFrame& frame) override;
@ -48,8 +126,18 @@ class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
int frames_scheduled_for_processing,
const VideoFrame& frame) RTC_RUN_ON(queue_);
// Returns true under all of the following conditions:
// - constraints min fps set to 0
// - constraints max fps set and greater than 0,
// - field trial enabled
// - zero-hertz mode enabled
bool IsZeroHertzScreenshareEnabled() const RTC_RUN_ON(queue_);
// Handles adapter creation on configuration changes.
void MaybeReconfigureAdapters(bool was_zero_hertz_enabled) RTC_RUN_ON(queue_);
// Called to report on constraint UMAs.
void MaybeReportFrameRateConstraintUmas() RTC_RUN_ON(&queue_);
void MaybeReportFrameRateConstraintUmas() RTC_RUN_ON(queue_);
Clock* const clock_;
TaskQueueBase* const queue_;
@ -58,6 +146,12 @@ class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
// 0 Hz.
const bool zero_hertz_screenshare_enabled_;
// The two possible modes we're under.
absl::optional<PassthroughAdapterMode> passthrough_adapter_;
absl::optional<ZeroHertzAdapterMode> zero_hertz_adapter_;
// Cache for the current adapter mode.
AdapterMode* current_adapter_mode_ = nullptr;
// Set up during Initialize.
Callback* callback_ = nullptr;
@ -80,6 +174,26 @@ class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
ScopedTaskSafetyDetached safety_;
};
ZeroHertzAdapterMode::ZeroHertzAdapterMode(
FrameCadenceAdapterInterface::Callback* callback,
double max_fps)
: callback_(callback), max_fps_(max_fps) {
sequence_checker_.Detach();
}
void ZeroHertzAdapterMode::OnFrame(Timestamp post_time,
int frames_scheduled_for_processing,
const VideoFrame& frame) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
// TODO(crbug.com/1255737): fill with meaningful implementation.
callback_->OnFrame(post_time, frames_scheduled_for_processing, frame);
}
absl::optional<uint32_t> ZeroHertzAdapterMode::GetInputFrameRateFps() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return max_fps_;
}
FrameCadenceAdapterImpl::FrameCadenceAdapterImpl(Clock* clock,
TaskQueueBase* queue)
: clock_(clock),
@ -89,13 +203,30 @@ FrameCadenceAdapterImpl::FrameCadenceAdapterImpl(Clock* clock,
void FrameCadenceAdapterImpl::Initialize(Callback* callback) {
callback_ = callback;
passthrough_adapter_.emplace(clock_, callback);
current_adapter_mode_ = &passthrough_adapter_.value();
}
void FrameCadenceAdapterImpl::SetZeroHertzModeEnabled(bool enabled) {
RTC_DCHECK_RUN_ON(queue_);
bool was_zero_hertz_enabled = zero_hertz_and_uma_reporting_enabled_;
if (enabled && !zero_hertz_and_uma_reporting_enabled_)
has_reported_screenshare_frame_rate_umas_ = false;
zero_hertz_and_uma_reporting_enabled_ = enabled;
MaybeReconfigureAdapters(was_zero_hertz_enabled);
}
absl::optional<uint32_t> FrameCadenceAdapterImpl::GetInputFrameRateFps() {
RTC_DCHECK_RUN_ON(queue_);
return current_adapter_mode_->GetInputFrameRateFps();
}
void FrameCadenceAdapterImpl::UpdateFrameRate() {
RTC_DCHECK_RUN_ON(queue_);
// The frame rate need not be updated for the zero-hertz adapter. The
// passthrough adapter however uses it. Always pass frames into the
// passthrough to keep the estimation alive should there be an adapter switch.
passthrough_adapter_->UpdateFrameRate();
}
void FrameCadenceAdapterImpl::OnFrame(const VideoFrame& frame) {
@ -124,7 +255,9 @@ void FrameCadenceAdapterImpl::OnConstraintsChanged(
<< constraints.max_fps.value_or(-1);
queue_->PostTask(ToQueuedTask(safety_.flag(), [this, constraints] {
RTC_DCHECK_RUN_ON(queue_);
bool was_zero_hertz_enabled = IsZeroHertzScreenshareEnabled();
source_constraints_ = constraints;
MaybeReconfigureAdapters(was_zero_hertz_enabled);
}));
}
@ -133,7 +266,33 @@ void FrameCadenceAdapterImpl::OnFrameOnMainQueue(
Timestamp post_time,
int frames_scheduled_for_processing,
const VideoFrame& frame) {
callback_->OnFrame(post_time, frames_scheduled_for_processing, frame);
current_adapter_mode_->OnFrame(post_time, frames_scheduled_for_processing,
frame);
}
// RTC_RUN_ON(queue_)
bool FrameCadenceAdapterImpl::IsZeroHertzScreenshareEnabled() const {
return zero_hertz_screenshare_enabled_ && source_constraints_.has_value() &&
source_constraints_->max_fps.value_or(-1) > 0 &&
source_constraints_->min_fps.value_or(-1) == 0 &&
zero_hertz_and_uma_reporting_enabled_;
}
// RTC_RUN_ON(queue_)
void FrameCadenceAdapterImpl::MaybeReconfigureAdapters(
bool was_zero_hertz_enabled) {
bool is_zero_hertz_enabled = IsZeroHertzScreenshareEnabled();
if (is_zero_hertz_enabled) {
if (!was_zero_hertz_enabled) {
zero_hertz_adapter_.emplace(callback_,
source_constraints_->max_fps.value());
}
current_adapter_mode_ = &zero_hertz_adapter_.value();
} else {
if (was_zero_hertz_enabled)
zero_hertz_adapter_ = absl::nullopt;
current_adapter_mode_ = &passthrough_adapter_.value();
}
}
// RTC_RUN_ON(queue_)

View File

@ -29,6 +29,10 @@ namespace webrtc {
class FrameCadenceAdapterInterface
: public rtc::VideoSinkInterface<VideoFrame> {
public:
// Averaging window spanning 90 frames at default 30fps, matching old media
// optimization module defaults.
static constexpr int64_t kFrameRateAveragingWindowSizeMs = (1000 / 30) * 90;
// Callback interface used to inform instance owners.
class Callback {
public:
@ -66,6 +70,14 @@ class FrameCadenceAdapterInterface
// Pass true in |enabled| as a prerequisite to enable zero-hertz operation.
virtual void SetZeroHertzModeEnabled(bool enabled) = 0;
// Returns the input framerate. This is measured by RateStatistics when
// zero-hertz mode is off, and returns the max framerate in zero-hertz mode.
virtual absl::optional<uint32_t> GetInputFrameRateFps() = 0;
// Updates frame rate. This is done unconditionally irrespective of adapter
// mode.
virtual void UpdateFrameRate() = 0;
};
} // namespace webrtc

View File

@ -16,6 +16,7 @@
#include "api/task_queue/task_queue_base.h"
#include "api/video/nv12_buffer.h"
#include "api/video/video_frame.h"
#include "rtc_base/rate_statistics.h"
#include "rtc_base/ref_counted_object.h"
#include "system_wrappers/include/metrics.h"
#include "test/field_trial.h"
@ -56,6 +57,12 @@ class ZeroHertzFieldTrialDisabler : public test::ScopedFieldTrials {
: test::ScopedFieldTrials("WebRTC-ZeroHertzScreenshare/Disabled/") {}
};
class ZeroHertzFieldTrialEnabler : public test::ScopedFieldTrials {
public:
ZeroHertzFieldTrialEnabler()
: test::ScopedFieldTrials("WebRTC-ZeroHertzScreenshare/Enabled/") {}
};
TEST(FrameCadenceAdapterTest,
ForwardsFramesOnConstructionAndUnderDisabledFieldTrial) {
GlobalSimulatedTimeController time_controller(Timestamp::Millis(1));
@ -93,6 +100,92 @@ TEST(FrameCadenceAdapterTest, CountsOutstandingFramesToProcess) {
time_controller.AdvanceTime(TimeDelta::Zero());
}
TEST(FrameCadenceAdapterTest, FrameRateFollowsRateStatisticsByDefault) {
GlobalSimulatedTimeController time_controller(Timestamp::Millis(0));
auto adapter = CreateAdapter(time_controller.GetClock());
adapter->Initialize(nullptr);
// Create an "oracle" rate statistics which should be followed on a sequence
// of frames.
RateStatistics rate(
FrameCadenceAdapterInterface::kFrameRateAveragingWindowSizeMs, 1000);
for (int frame = 0; frame != 10; ++frame) {
time_controller.AdvanceTime(TimeDelta::Millis(10));
rate.Update(1, time_controller.GetClock()->TimeInMilliseconds());
adapter->UpdateFrameRate();
EXPECT_EQ(rate.Rate(time_controller.GetClock()->TimeInMilliseconds()),
adapter->GetInputFrameRateFps())
<< " failed for frame " << frame;
}
}
TEST(FrameCadenceAdapterTest,
FrameRateFollowsRateStatisticsWhenFeatureDisabled) {
ZeroHertzFieldTrialDisabler feature_disabler;
GlobalSimulatedTimeController time_controller(Timestamp::Millis(0));
auto adapter = CreateAdapter(time_controller.GetClock());
adapter->Initialize(nullptr);
// Create an "oracle" rate statistics which should be followed on a sequence
// of frames.
RateStatistics rate(
FrameCadenceAdapterInterface::kFrameRateAveragingWindowSizeMs, 1000);
for (int frame = 0; frame != 10; ++frame) {
time_controller.AdvanceTime(TimeDelta::Millis(10));
rate.Update(1, time_controller.GetClock()->TimeInMilliseconds());
adapter->UpdateFrameRate();
EXPECT_EQ(rate.Rate(time_controller.GetClock()->TimeInMilliseconds()),
adapter->GetInputFrameRateFps())
<< " failed for frame " << frame;
}
}
TEST(FrameCadenceAdapterTest, FrameRateFollowsMaxFpsWhenZeroHertzActivated) {
ZeroHertzFieldTrialEnabler enabler;
MockCallback callback;
GlobalSimulatedTimeController time_controller(Timestamp::Millis(0));
auto adapter = CreateAdapter(time_controller.GetClock());
adapter->Initialize(nullptr);
adapter->SetZeroHertzModeEnabled(true);
adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
for (int frame = 0; frame != 10; ++frame) {
time_controller.AdvanceTime(TimeDelta::Millis(10));
adapter->UpdateFrameRate();
EXPECT_EQ(adapter->GetInputFrameRateFps(), 1u);
}
}
TEST(FrameCadenceAdapterTest,
FrameRateFollowsRateStatisticsAfterZeroHertzDeactivated) {
ZeroHertzFieldTrialEnabler enabler;
MockCallback callback;
GlobalSimulatedTimeController time_controller(Timestamp::Millis(0));
auto adapter = CreateAdapter(time_controller.GetClock());
adapter->Initialize(nullptr);
adapter->SetZeroHertzModeEnabled(true);
adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
RateStatistics rate(
FrameCadenceAdapterInterface::kFrameRateAveragingWindowSizeMs, 1000);
constexpr int MAX = 10;
for (int frame = 0; frame != MAX; ++frame) {
time_controller.AdvanceTime(TimeDelta::Millis(10));
rate.Update(1, time_controller.GetClock()->TimeInMilliseconds());
adapter->UpdateFrameRate();
}
// Turn off zero hertz on the next-last frame; after the last frame we
// should see a value that tracks the rate oracle.
adapter->SetZeroHertzModeEnabled(false);
// Last frame.
time_controller.AdvanceTime(TimeDelta::Millis(10));
rate.Update(1, time_controller.GetClock()->TimeInMilliseconds());
adapter->UpdateFrameRate();
EXPECT_EQ(rate.Rate(time_controller.GetClock()->TimeInMilliseconds()),
adapter->GetInputFrameRateFps());
}
class FrameCadenceAdapterMetricsTest : public ::testing::Test {
public:
FrameCadenceAdapterMetricsTest() : time_controller_(Timestamp::Millis(1)) {

View File

@ -64,10 +64,6 @@ const int64_t kPendingFrameTimeoutMs = 1000;
constexpr char kFrameDropperFieldTrial[] = "WebRTC-FrameDropper";
// Averaging window spanning 90 frames at default 30fps, matching old media
// optimization module defaults.
const int64_t kFrameRateAvergingWindowSizeMs = (1000 / 30) * 90;
const size_t kDefaultPayloadSize = 1440;
const int64_t kParameterUpdateIntervalMs = 1000;
@ -633,7 +629,6 @@ VideoStreamEncoder::VideoStreamEncoder(
expect_resize_state_(ExpectResizeState::kNoResize),
fec_controller_override_(nullptr),
force_disable_frame_dropper_(false),
input_framerate_(kFrameRateAvergingWindowSizeMs, 1000),
pending_frame_drops_(0),
cwnd_frame_counter_(0),
next_frame_types_(1, VideoFrameType::kVideoFrameDelta),
@ -1422,8 +1417,13 @@ VideoStreamEncoder::UpdateBitrateAllocation(
uint32_t VideoStreamEncoder::GetInputFramerateFps() {
const uint32_t default_fps = max_framerate_ != -1 ? max_framerate_ : 30;
// This method may be called after we cleared out the frame_cadence_adapter_
// reference in Stop(). In such a situation it's probably not important with a
// decent estimate.
absl::optional<uint32_t> input_fps =
input_framerate_.Rate(clock_->TimeInMilliseconds());
frame_cadence_adapter_ ? frame_cadence_adapter_->GetInputFrameRateFps()
: absl::nullopt;
if (!input_fps || *input_fps == 0) {
return default_fps;
}
@ -1525,7 +1525,7 @@ void VideoStreamEncoder::MaybeEncodeVideoFrame(const VideoFrame& video_frame,
// Poll the rate before updating, otherwise we risk the rate being estimated
// a little too high at the start of the call when then window is small.
uint32_t framerate_fps = GetInputFramerateFps();
input_framerate_.Update(1u, clock_->TimeInMilliseconds());
frame_cadence_adapter_->UpdateFrameRate();
int64_t now_ms = clock_->TimeInMilliseconds();
if (pending_encoder_reconfiguration_) {

View File

@ -339,7 +339,6 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
// trusted rate controller. This is determined on a per-frame basis, as the
// encoder behavior might dynamically change.
bool force_disable_frame_dropper_ RTC_GUARDED_BY(&encoder_queue_);
RateStatistics input_framerate_ RTC_GUARDED_BY(&encoder_queue_);
// Incremented on worker thread whenever `frame_dropper_` determines that a
// frame should be dropped. Decremented on whichever thread runs
// OnEncodedImage(), which is only called by one thread but not necessarily

View File

@ -76,6 +76,7 @@ using ::testing::Eq;
using ::testing::Field;
using ::testing::Ge;
using ::testing::Gt;
using ::testing::Invoke;
using ::testing::Le;
using ::testing::Lt;
using ::testing::Matcher;
@ -640,30 +641,26 @@ class SimpleVideoStreamEncoderFactory {
~AdaptedVideoStreamEncoder() { Stop(); }
};
SimpleVideoStreamEncoderFactory()
: time_controller_(Timestamp::Millis(0)),
task_queue_factory_(time_controller_.CreateTaskQueueFactory()),
stats_proxy_(std::make_unique<MockableSendStatisticsProxy>(
time_controller_.GetClock(),
VideoSendStream::Config(nullptr),
webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo)),
encoder_settings_(
VideoEncoder::Capabilities(/*loss_notification=*/false)),
fake_encoder_(time_controller_.GetClock()),
encoder_factory_(&fake_encoder_) {
SimpleVideoStreamEncoderFactory() {
encoder_settings_.encoder_factory = &encoder_factory_;
encoder_settings_.bitrate_allocator_factory =
bitrate_allocator_factory_.get();
}
std::unique_ptr<AdaptedVideoStreamEncoder> Create(
std::unique_ptr<FrameCadenceAdapterInterface> zero_hertz_adapter) {
std::unique_ptr<FrameCadenceAdapterInterface> zero_hertz_adapter,
TaskQueueBase** encoder_queue_ptr = nullptr) {
auto encoder_queue =
time_controller_.GetTaskQueueFactory()->CreateTaskQueue(
"EncoderQueue", TaskQueueFactory::Priority::NORMAL);
if (encoder_queue_ptr)
*encoder_queue_ptr = encoder_queue.get();
auto result = std::make_unique<AdaptedVideoStreamEncoder>(
time_controller_.GetClock(),
/*number_of_cores=*/1,
/*stats_proxy=*/stats_proxy_.get(), encoder_settings_,
std::make_unique<CpuOveruseDetectorProxy>(/*stats_proxy=*/nullptr),
std::move(zero_hertz_adapter),
time_controller_.GetTaskQueueFactory()->CreateTaskQueue(
"EncoderQueue", TaskQueueFactory::Priority::NORMAL),
std::move(zero_hertz_adapter), std::move(encoder_queue),
VideoStreamEncoder::BitrateAllocationCallbackType::
kVideoBitrateAllocation);
result->SetSink(&sink_, /*rotation_applied=*/false);
@ -692,12 +689,20 @@ class SimpleVideoStreamEncoderFactory {
}
};
GlobalSimulatedTimeController time_controller_;
std::unique_ptr<TaskQueueFactory> task_queue_factory_;
std::unique_ptr<MockableSendStatisticsProxy> stats_proxy_;
VideoStreamEncoderSettings encoder_settings_;
test::FakeEncoder fake_encoder_;
test::VideoEncoderProxyFactory encoder_factory_;
GlobalSimulatedTimeController time_controller_{Timestamp::Millis(0)};
std::unique_ptr<TaskQueueFactory> task_queue_factory_{
time_controller_.CreateTaskQueueFactory()};
std::unique_ptr<MockableSendStatisticsProxy> stats_proxy_ =
std::make_unique<MockableSendStatisticsProxy>(
time_controller_.GetClock(),
VideoSendStream::Config(nullptr),
webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo);
std::unique_ptr<VideoBitrateAllocatorFactory> bitrate_allocator_factory_ =
CreateBuiltinVideoBitrateAllocatorFactory();
VideoStreamEncoderSettings encoder_settings_{
VideoEncoder::Capabilities(/*loss_notification=*/false)};
test::FakeEncoder fake_encoder_{time_controller_.GetClock()};
test::VideoEncoderProxyFactory encoder_factory_{&fake_encoder_};
NullEncoderSink sink_;
};
@ -706,6 +711,8 @@ class MockFrameCadenceAdapter : public FrameCadenceAdapterInterface {
MOCK_METHOD(void, Initialize, (Callback * callback), (override));
MOCK_METHOD(void, SetZeroHertzModeEnabled, (bool), (override));
MOCK_METHOD(void, OnFrame, (const VideoFrame&), (override));
MOCK_METHOD(absl::optional<uint32_t>, GetInputFrameRateFps, (), (override));
MOCK_METHOD(void, UpdateFrameRate, (), (override));
};
class MockEncoderSelector
@ -8746,4 +8753,46 @@ TEST(VideoStreamEncoderFrameCadenceTest,
.build());
}
TEST(VideoStreamEncoderFrameCadenceTest, UsesFrameCadenceAdapterForFrameRate) {
auto adapter = std::make_unique<MockFrameCadenceAdapter>();
auto* adapter_ptr = adapter.get();
test::FrameForwarder video_source;
SimpleVideoStreamEncoderFactory factory;
FrameCadenceAdapterInterface::Callback* video_stream_encoder_callback =
nullptr;
EXPECT_CALL(*adapter_ptr, Initialize)
.WillOnce(Invoke([&video_stream_encoder_callback](
FrameCadenceAdapterInterface::Callback* callback) {
video_stream_encoder_callback = callback;
}));
TaskQueueBase* encoder_queue = nullptr;
auto video_stream_encoder =
factory.Create(std::move(adapter), &encoder_queue);
// This is just to make the VSE operational. We'll feed a frame directly by
// the callback interface.
video_stream_encoder->SetSource(
&video_source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
VideoEncoderConfig video_encoder_config;
test::FillEncoderConfiguration(kVideoCodecGeneric, 1, &video_encoder_config);
video_stream_encoder->ConfigureEncoder(std::move(video_encoder_config),
/*max_data_payload_length=*/1000);
EXPECT_CALL(*adapter_ptr, GetInputFrameRateFps);
EXPECT_CALL(*adapter_ptr, UpdateFrameRate);
encoder_queue->PostTask(ToQueuedTask([video_stream_encoder_callback] {
video_stream_encoder_callback->OnFrame(
Timestamp::Millis(1), 1,
VideoFrame::Builder()
.set_video_frame_buffer(
rtc::make_ref_counted<NV12Buffer>(/*width=*/16, /*height=*/16))
.set_ntp_time_ms(0)
.set_timestamp_ms(0)
.set_rotation(kVideoRotation_0)
.build());
}));
factory.DepleteTaskQueues();
}
} // namespace webrtc