Introduce Sync-Decoding based on Metronome
Adds new class DecodeSynchronizer that will coalesce the decoding
of received streams on the metronome. This feature is experimental and
is backed by a field trial WebRTC-FrameBuffer3.
This experiment now has 3 arms to it,
"WebRTC-FrameBuffer3/arm:FrameBuffer2/": Default, uses old frame buffer.
"WebRTC-FrameBuffer3/arm:FrameBuffer3/": Uses new frame buffer.
"WebRTC-FrameBuffer3/arm:SyncDecoding/": Uses new frame buffer with
frame scheduled on the metronome.
The SyncDecoding arm will not work until it is wired up in the follow-up
CL.
This change also makes the following modifications,
* Adds FakeMetronome utilities for tests using a metronome.
* Makes FrameDecodeScheduler an interface. The default implementation is
TaskQueueFrameDecodeScheduler.
* FrameDecodeScheduler now has a Stop() method, which must be called
before destruction.
TBR=philipel@webrtc.org
Change-Id: I58a306bb883604b0be3eb2a04b3d07dbdf185c71
Bug: webrtc:13658
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/250665
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Stefan Holmer <holmer@google.com>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35988}
diff --git a/video/frame_buffer_proxy_unittest.cc b/video/frame_buffer_proxy_unittest.cc
index 408fac9..fc266d2 100644
--- a/video/frame_buffer_proxy_unittest.cc
+++ b/video/frame_buffer_proxy_unittest.cc
@@ -20,6 +20,7 @@
#include "absl/types/optional.h"
#include "absl/types/variant.h"
+#include "api/metronome/test/fake_metronome.h"
#include "api/units/frequency.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
@@ -32,6 +33,7 @@
#include "test/gtest.h"
#include "test/run_loop.h"
#include "test/time_controller/simulated_time_controller.h"
+#include "video/decode_synchronizer.h"
using ::testing::_;
using ::testing::AllOf;
@@ -209,6 +211,9 @@
decode_queue_(time_controller_.GetTaskQueueFactory()->CreateTaskQueue(
"decode_queue",
TaskQueueFactory::Priority::NORMAL)),
+ fake_metronome_(time_controller_.GetTaskQueueFactory(),
+ TimeDelta::Millis(16)),
+ decode_sync_(clock_, &fake_metronome_, run_loop_.task_queue()),
timing_(clock_),
proxy_(FrameBufferProxy::CreateFromFieldTrial(clock_,
run_loop_.task_queue(),
@@ -217,7 +222,8 @@
&decode_queue_,
this,
kMaxWaitForKeyframe,
- kMaxWaitForFrame)) {
+ kMaxWaitForFrame,
+ &decode_sync_)) {
// Avoid starting with negative render times.
timing_.set_min_playout_delay(10);
@@ -230,6 +236,8 @@
if (proxy_) {
proxy_->StopOnWorker();
}
+ fake_metronome_.Stop();
+ time_controller_.AdvanceTime(TimeDelta::Zero());
}
void OnEncodedFrame(std::unique_ptr<EncodedFrame> frame) override {
@@ -291,7 +299,10 @@
Clock* const clock_;
test::RunLoop run_loop_;
rtc::TaskQueue decode_queue_;
+ test::FakeMetronome fake_metronome_;
+ DecodeSynchronizer decode_sync_;
VCMTiming timing_;
+
::testing::NiceMock<VCMReceiveStatisticsCallbackMock> stats_callback_;
std::unique_ptr<FrameBufferProxy> proxy_;
@@ -544,7 +555,7 @@
proxy_->InsertFrame(Builder().Id(0).Time(0).AsLast().Build());
EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(WithId(0)));
- time_controller_.AdvanceTime(kFps30Delay);
+ time_controller_.AdvanceTime(kFps30Delay / 2);
proxy_->InsertFrame(
Builder().Id(1).Time(kFps30Rtp).Refs({0}).AsLast().Build());
StartNextDecode();
@@ -598,6 +609,8 @@
StartNextDecode();
EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(WithId(33)));
+ StartNextDecode();
+ EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), TimedOut());
EXPECT_EQ(dropped_frames(), 1);
}
@@ -617,6 +630,10 @@
time_controller_.AdvanceTime(TimeDelta::Zero());
}
+// Note: This test takes a long time to run if the fake metronome is active.
+// Since the test needs to wait for the timestamp to rollover, it has a fake
+// delay of around 6.5 hours. Even though time is simulated, this will be
+// around 1,500,000 metronome tick invocations.
TEST_P(FrameBufferProxyTest, NextFrameWithOldTimestamp) {
// Test inserting 31 frames and pause the stream for a long time before
// frame 32.
@@ -668,16 +685,19 @@
.AsLast()
.Build());
// FrameBuffer2 drops the frame, while FrameBuffer3 will continue the stream.
- if (field_trial::IsEnabled("WebRTC-FrameBuffer3")) {
+ if (field_trial::FindFullName("WebRTC-FrameBuffer3")
+ .find("arm:FrameBuffer2") == std::string::npos) {
EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(WithId(2)));
} else {
EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), TimedOut());
}
}
-INSTANTIATE_TEST_SUITE_P(FrameBufferProxy,
- FrameBufferProxyTest,
- ::testing::Values("WebRTC-FrameBuffer3/Disabled/",
- "WebRTC-FrameBuffer3/Enabled/"));
+INSTANTIATE_TEST_SUITE_P(
+ FrameBufferProxy,
+ FrameBufferProxyTest,
+ ::testing::Values("WebRTC-FrameBuffer3/arm:FrameBuffer2/",
+ "WebRTC-FrameBuffer3/arm:FrameBuffer3/",
+ "WebRTC-FrameBuffer3/arm:SyncDecoding/"));
} // namespace webrtc