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:
parent
e45cfb45b1
commit
3fa9a66f22
@ -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() !=
|
||||||
|
|||||||
@ -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};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user