Cap max decode delay for FrameBuffer3

When a large queue of frames builds up due to a lost frame, the decode
delay can sometimes become quite large. In this case the stream may
signal as timed out when in fact it is not. Instead, the delay should
be capped at the timeout limit.

Bug: webrtc:14168
Change-Id: I5b4e8851b2c6d7d27a698627dc1633931d7fc00e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/265404
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37199}
This commit is contained in:
Evan Shrubsole 2022-06-13 14:19:10 +00:00 committed by WebRTC LUCI CQ
parent e45cfb45b1
commit 3fa9a66f22
6 changed files with 50 additions and 15 deletions

View File

@ -468,7 +468,8 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
while (decodable_tu_info) { while (decodable_tu_info) {
schedule = decode_timing_.OnFrameBufferUpdated( schedule = decode_timing_.OnFrameBufferUpdated(
decodable_tu_info->next_rtp_timestamp, decodable_tu_info->next_rtp_timestamp,
decodable_tu_info->last_rtp_timestamp, IsTooManyFramesQueued()); decodable_tu_info->last_rtp_timestamp, MaxWait(),
IsTooManyFramesQueued());
if (schedule) { if (schedule) {
// Don't schedule if already waiting for the same frame. // Don't schedule if already waiting for the same frame.
if (frame_decode_scheduler_->ScheduledRtpTimestamp() != if (frame_decode_scheduler_->ScheduledRtpTimestamp() !=

View File

@ -28,7 +28,9 @@ FrameDecodeTiming::FrameDecodeTiming(Clock* clock,
absl::optional<FrameDecodeTiming::FrameSchedule> absl::optional<FrameDecodeTiming::FrameSchedule>
FrameDecodeTiming::OnFrameBufferUpdated(uint32_t next_temporal_unit_rtp, FrameDecodeTiming::OnFrameBufferUpdated(uint32_t next_temporal_unit_rtp,
uint32_t last_temporal_unit_rtp, uint32_t last_temporal_unit_rtp,
TimeDelta max_wait_for_frame,
bool too_many_frames_queued) { bool too_many_frames_queued) {
RTC_DCHECK_GT(max_wait_for_frame, TimeDelta::Zero());
const Timestamp now = clock_->CurrentTime(); const Timestamp now = clock_->CurrentTime();
Timestamp render_time = timing_->RenderTime(next_temporal_unit_rtp, now); Timestamp render_time = timing_->RenderTime(next_temporal_unit_rtp, now);
TimeDelta max_wait = TimeDelta max_wait =
@ -48,7 +50,8 @@ FrameDecodeTiming::OnFrameBufferUpdated(uint32_t next_temporal_unit_rtp,
<< " render time " << render_time.ms() << " render time " << render_time.ms()
<< " with a max wait of " << max_wait.ms() << "ms"; << " with a max wait of " << max_wait.ms() << "ms";
Timestamp latest_decode_time = now + std::max(max_wait, TimeDelta::Zero()); max_wait.Clamp(TimeDelta::Zero(), max_wait_for_frame);
Timestamp latest_decode_time = now + max_wait;
return FrameSchedule{.latest_decode_time = latest_decode_time, return FrameSchedule{.latest_decode_time = latest_decode_time,
.render_time = render_time}; .render_time = render_time};
} }

View File

@ -41,6 +41,7 @@ class FrameDecodeTiming {
absl::optional<FrameSchedule> OnFrameBufferUpdated( absl::optional<FrameSchedule> OnFrameBufferUpdated(
uint32_t next_temporal_unit_rtp, uint32_t next_temporal_unit_rtp,
uint32_t last_temporal_unit_rtp, uint32_t last_temporal_unit_rtp,
TimeDelta max_wait_for_frame,
bool too_many_frames_queued); bool too_many_frames_queued);
private: private:

View File

@ -19,6 +19,7 @@
#include "test/gmock.h" #include "test/gmock.h"
#include "test/gtest.h" #include "test/gtest.h"
#include "test/scoped_key_value_config.h" #include "test/scoped_key_value_config.h"
#include "video/video_receive_stream2.h"
namespace webrtc { namespace webrtc {
@ -80,13 +81,13 @@ TEST_F(FrameDecodeTimingTest, ReturnsWaitTimesWhenValid) {
const Timestamp render_time = clock_.CurrentTime() + TimeDelta::Millis(60); const Timestamp render_time = clock_.CurrentTime() + TimeDelta::Millis(60);
timing_.SetTimes(90000, render_time, decode_delay); timing_.SetTimes(90000, render_time, decode_delay);
EXPECT_THAT( EXPECT_THAT(frame_decode_scheduler_.OnFrameBufferUpdated(
frame_decode_scheduler_.OnFrameBufferUpdated(90000, 180000, false), 90000, 180000, kMaxWaitForFrame, false),
Optional( Optional(AllOf(
AllOf(Field(&FrameDecodeTiming::FrameSchedule::latest_decode_time, Field(&FrameDecodeTiming::FrameSchedule::latest_decode_time,
Eq(clock_.CurrentTime() + decode_delay)), Eq(clock_.CurrentTime() + decode_delay)),
Field(&FrameDecodeTiming::FrameSchedule::render_time, Field(&FrameDecodeTiming::FrameSchedule::render_time,
Eq(render_time))))); Eq(render_time)))));
} }
TEST_F(FrameDecodeTimingTest, FastForwardsFrameTooFarInThePast) { TEST_F(FrameDecodeTimingTest, FastForwardsFrameTooFarInThePast) {
@ -95,9 +96,9 @@ TEST_F(FrameDecodeTimingTest, FastForwardsFrameTooFarInThePast) {
const Timestamp render_time = clock_.CurrentTime(); const Timestamp render_time = clock_.CurrentTime();
timing_.SetTimes(90000, render_time, decode_delay); timing_.SetTimes(90000, render_time, decode_delay);
EXPECT_THAT( EXPECT_THAT(frame_decode_scheduler_.OnFrameBufferUpdated(
frame_decode_scheduler_.OnFrameBufferUpdated(90000, 180000, false), 90000, 180000, kMaxWaitForFrame, false),
Eq(absl::nullopt)); Eq(absl::nullopt));
} }
TEST_F(FrameDecodeTimingTest, NoFastForwardIfOnlyFrameToDecode) { TEST_F(FrameDecodeTimingTest, NoFastForwardIfOnlyFrameToDecode) {
@ -107,7 +108,8 @@ TEST_F(FrameDecodeTimingTest, NoFastForwardIfOnlyFrameToDecode) {
timing_.SetTimes(90000, render_time, decode_delay); timing_.SetTimes(90000, render_time, decode_delay);
// Negative `decode_delay` means that `latest_decode_time` is now. // Negative `decode_delay` means that `latest_decode_time` is now.
EXPECT_THAT(frame_decode_scheduler_.OnFrameBufferUpdated(90000, 90000, false), EXPECT_THAT(frame_decode_scheduler_.OnFrameBufferUpdated(
90000, 90000, kMaxWaitForFrame, false),
Optional(AllOf( Optional(AllOf(
Field(&FrameDecodeTiming::FrameSchedule::latest_decode_time, Field(&FrameDecodeTiming::FrameSchedule::latest_decode_time,
Eq(clock_.CurrentTime())), Eq(clock_.CurrentTime())),
@ -115,4 +117,31 @@ TEST_F(FrameDecodeTimingTest, NoFastForwardIfOnlyFrameToDecode) {
Eq(render_time))))); Eq(render_time)))));
} }
TEST_F(FrameDecodeTimingTest, MaxWaitCapped) {
TimeDelta frame_delay = TimeDelta::Millis(30);
const TimeDelta decode_delay = TimeDelta::Seconds(3);
const Timestamp render_time = clock_.CurrentTime() + TimeDelta::Seconds(3);
timing_.SetTimes(90000, render_time, decode_delay);
timing_.SetTimes(180000, render_time + frame_delay,
decode_delay + frame_delay);
EXPECT_THAT(frame_decode_scheduler_.OnFrameBufferUpdated(
90000, 270000, kMaxWaitForFrame, false),
Optional(AllOf(
Field(&FrameDecodeTiming::FrameSchedule::latest_decode_time,
Eq(clock_.CurrentTime() + kMaxWaitForFrame)),
Field(&FrameDecodeTiming::FrameSchedule::render_time,
Eq(render_time)))));
// Test cap keyframe.
clock_.AdvanceTime(frame_delay);
EXPECT_THAT(frame_decode_scheduler_.OnFrameBufferUpdated(
180000, 270000, kMaxWaitForKeyFrame, false),
Optional(AllOf(
Field(&FrameDecodeTiming::FrameSchedule::latest_decode_time,
Eq(clock_.CurrentTime() + kMaxWaitForKeyFrame)),
Field(&FrameDecodeTiming::FrameSchedule::render_time,
Eq(render_time + frame_delay)))));
}
} // namespace webrtc } // namespace webrtc

View File

@ -74,10 +74,8 @@ namespace internal {
namespace { namespace {
// The default delay before re-requesting a key frame to be sent. // The default delay before re-requesting a key frame to be sent.
constexpr TimeDelta kMaxWaitForKeyFrame = TimeDelta::Millis(200);
constexpr TimeDelta kMinBaseMinimumDelay = TimeDelta::Zero(); constexpr TimeDelta kMinBaseMinimumDelay = TimeDelta::Zero();
constexpr TimeDelta kMaxBaseMinimumDelay = TimeDelta::Seconds(10); constexpr TimeDelta kMaxBaseMinimumDelay = TimeDelta::Seconds(10);
constexpr TimeDelta kMaxWaitForFrame = TimeDelta::Seconds(3);
// Create a decoder for the preferred codec before the stream starts and any // Create a decoder for the preferred codec before the stream starts and any
// other decoder lazily on demand. // other decoder lazily on demand.

View File

@ -47,6 +47,9 @@ class RtpStreamReceiverControllerInterface;
class RtxReceiveStream; class RtxReceiveStream;
class VCMTiming; class VCMTiming;
constexpr TimeDelta kMaxWaitForKeyFrame = TimeDelta::Millis(200);
constexpr TimeDelta kMaxWaitForFrame = TimeDelta::Seconds(3);
namespace internal { namespace internal {
class CallStats; class CallStats;