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}
diff --git a/test/time_controller/simulated_time_controller.cc b/test/time_controller/simulated_time_controller.cc
index 1ed2b30..dbb36fd 100644
--- a/test/time_controller/simulated_time_controller.cc
+++ b/test/time_controller/simulated_time_controller.cc
@@ -211,6 +211,18 @@
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);
diff --git a/test/time_controller/simulated_time_controller.h b/test/time_controller/simulated_time_controller.h
index 98b816c..f3f0da9 100644
--- a/test/time_controller/simulated_time_controller.h
+++ b/test/time_controller/simulated_time_controller.h
@@ -138,6 +138,11 @@
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
diff --git a/test/time_controller/simulated_time_controller_unittest.cc b/test/time_controller/simulated_time_controller_unittest.cc
index 1ee592c..f223ffe 100644
--- a/test/time_controller/simulated_time_controller_unittest.cc
+++ b/test/time_controller/simulated_time_controller_unittest.cc
@@ -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 @@
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
diff --git a/video/frame_cadence_adapter.cc b/video/frame_cadence_adapter.cc
index 9390c05..250a82b 100644
--- a/video/frame_cadence_adapter.cc
+++ b/video/frame_cadence_adapter.cc
@@ -10,6 +10,7 @@
#include "video/frame_cadence_adapter.h"
+#include <algorithm>
#include <atomic>
#include <deque>
#include <memory>
@@ -377,13 +378,14 @@
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() {
diff --git a/video/frame_cadence_adapter_unittest.cc b/video/frame_cadence_adapter_unittest.cc
index f9745c8..b826672 100644
--- a/video/frame_cadence_adapter_unittest.cc
+++ b/video/frame_cadence_adapter_unittest.cc
@@ -38,6 +38,7 @@
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, 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