FrameCadenceAdapter: account for encode sequence contention.

The synthetic delay added in ZeroHzAdapterMode::OnFrame does not
account for delay with respect to the initial frame post from
FrameCadenceAdapter::OnFrame. Fix this to account for time spent
in contention on the encode sequence.

Bug: webrtc:15456
Change-Id: I63446e8dfe8f62b09d972434a705e912f8a73d69
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/318420
Commit-Queue: Markus Handell <handellm@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40675}
This commit is contained in:
Markus Handell 2023-08-31 11:59:06 +02:00 committed by WebRTC LUCI CQ
parent 58240ed964
commit 8fa8619d7e
5 changed files with 80 additions and 1 deletions

View File

@ -211,6 +211,18 @@ void GlobalSimulatedTimeController::AdvanceTime(TimeDelta duration) {
impl_.RunReadyRunners();
}
void GlobalSimulatedTimeController::SkipForwardBy(TimeDelta duration) {
rtc::ScopedYieldPolicy yield_policy(&impl_);
Timestamp current_time = impl_.CurrentTime();
Timestamp target_time = current_time + duration;
impl_.AdvanceTime(target_time);
sim_clock_.AdvanceTimeMicroseconds(duration.us());
global_clock_.AdvanceTime(duration);
// Run tasks that were pending during the skip.
impl_.RunReadyRunners();
}
void GlobalSimulatedTimeController::Register(
sim_time_impl::SimulatedSequenceRunner* runner) {
impl_.Register(runner);

View File

@ -138,6 +138,11 @@ class GlobalSimulatedTimeController : public TimeController {
void AdvanceTime(TimeDelta duration) override;
// Advances time by `duration`and do not run delayed tasks in the meantime.
// Runs any pending tasks at the end.
// Useful for simulating contention on destination queues.
void SkipForwardBy(TimeDelta duration);
// Makes the simulated time controller aware of a custom
// SimulatedSequenceRunner.
// TODO(bugs.webrtc.org/11581): remove method once the ModuleRtpRtcpImpl2 unit

View File

@ -13,6 +13,7 @@
#include <atomic>
#include <memory>
#include "api/units/time_delta.h"
#include "rtc_base/event.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/task_queue_for_test.h"
@ -146,4 +147,18 @@ TEST(SimulatedTimeControllerTest, ThreadYeildsOnSynchronousCall) {
ASSERT_TRUE(task_has_run);
}
TEST(SimulatedTimeControllerTest, SkipsDelayedTaskForward) {
GlobalSimulatedTimeController sim(kStartTime);
auto main_thread = sim.GetMainThread();
constexpr auto duration_during_which_nothing_runs = TimeDelta::Seconds(2);
constexpr auto shorter_duration = TimeDelta::Seconds(1);
MockFunction<void()> fun;
EXPECT_CALL(fun, Call).WillOnce(Invoke([&] {
ASSERT_EQ(sim.GetClock()->CurrentTime(),
kStartTime + duration_during_which_nothing_runs);
}));
main_thread->PostDelayedTask(fun.AsStdFunction(), shorter_duration);
sim.SkipForwardBy(duration_during_which_nothing_runs);
}
} // namespace webrtc

View File

@ -10,6 +10,7 @@
#include "video/frame_cadence_adapter.h"
#include <algorithm>
#include <atomic>
#include <deque>
#include <memory>
@ -377,13 +378,14 @@ void ZeroHertzAdapterMode::OnFrame(Timestamp post_time,
queued_frames_.push_back(frame);
current_frame_id_++;
scheduled_repeat_ = absl::nullopt;
TimeDelta time_spent_since_post = clock_->CurrentTime() - post_time;
queue_->PostDelayedHighPrecisionTask(
SafeTask(safety_.flag(),
[this] {
RTC_DCHECK_RUN_ON(&sequence_checker_);
ProcessOnDelayedCadence();
}),
frame_delay_);
std::max(frame_delay_ - time_spent_since_post, TimeDelta::Zero()));
}
void ZeroHertzAdapterMode::OnDiscardedFrame() {

View File

@ -38,6 +38,7 @@ namespace {
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::Mock;
using ::testing::Pair;
using ::testing::Values;
@ -241,6 +242,50 @@ TEST(FrameCadenceAdapterTest, ForwardsFramesDelayed) {
}
}
TEST(FrameCadenceAdapterTest, DelayedProcessingUnderSlightContention) {
ZeroHertzFieldTrialEnabler enabler;
GlobalSimulatedTimeController time_controller(Timestamp::Zero());
auto adapter = CreateAdapter(enabler, time_controller.GetClock());
MockCallback callback;
adapter->Initialize(&callback);
adapter->SetZeroHertzModeEnabled(
FrameCadenceAdapterInterface::ZeroHertzModeParams{});
adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
// Expect frame delivery at 1 sec despite target sequence not running
// callbacks for the time skipped.
constexpr TimeDelta time_skipped = TimeDelta::Millis(999);
EXPECT_CALL(callback, OnFrame).WillOnce(InvokeWithoutArgs([&] {
EXPECT_EQ(time_controller.GetClock()->CurrentTime(),
Timestamp::Zero() + TimeDelta::Seconds(1));
}));
adapter->OnFrame(CreateFrame());
time_controller.SkipForwardBy(time_skipped);
time_controller.AdvanceTime(TimeDelta::Seconds(1) - time_skipped);
}
TEST(FrameCadenceAdapterTest, DelayedProcessingUnderHeavyContention) {
ZeroHertzFieldTrialEnabler enabler;
GlobalSimulatedTimeController time_controller(Timestamp::Zero());
auto adapter = CreateAdapter(enabler, time_controller.GetClock());
MockCallback callback;
adapter->Initialize(&callback);
adapter->SetZeroHertzModeEnabled(
FrameCadenceAdapterInterface::ZeroHertzModeParams{});
adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
// Expect frame delivery at origin + `time_skipped` when the target sequence
// is not running callbacks for the initial 1+ sec.
constexpr TimeDelta time_skipped =
TimeDelta::Seconds(1) + TimeDelta::Micros(1);
EXPECT_CALL(callback, OnFrame).WillOnce(InvokeWithoutArgs([&] {
EXPECT_EQ(time_controller.GetClock()->CurrentTime(),
Timestamp::Zero() + time_skipped);
}));
adapter->OnFrame(CreateFrame());
time_controller.SkipForwardBy(time_skipped);
}
TEST(FrameCadenceAdapterTest, RepeatsFramesDelayed) {
// Logic in the frame cadence adapter avoids modifying frame NTP and render
// timestamps if these timestamps looks unset, which is the case when the