Cap FrameBuffer3 max wait based on remaining timeout

This was capped to the max wait for a frame, but if the stream was
timing out in a set period of time, it would do this before the frame
was decoded. Instead, this should be done the stream timeout is
triggered.

Bug: webrtc:14168
Change-Id: Iecde082bd223c469f735afeb77a00c0387e47b3b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/266369
Commit-Queue: Evan Shrubsole <eshr@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37310}
This commit is contained in:
Evan Shrubsole 2022-06-22 14:32:36 +00:00 committed by WebRTC LUCI CQ
parent c4a35898d9
commit 4d3ba77975
6 changed files with 75 additions and 15 deletions

View File

@ -342,6 +342,7 @@ rtc_library("video_receive_stream_timeout_tracker") {
"../api/task_queue",
"../api/units:time_delta",
"../api/units:timestamp",
"../rtc_base:logging",
"../rtc_base/task_utils:repeating_task",
"../system_wrappers",
]

View File

@ -186,8 +186,6 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
std::unique_ptr<FrameDecodeScheduler> frame_decode_scheduler,
const FieldTrialsView& field_trials)
: field_trials_(field_trials),
max_wait_for_keyframe_(max_wait_for_keyframe),
max_wait_for_frame_(max_wait_for_frame),
clock_(clock),
worker_queue_(worker_queue),
decode_queue_(decode_queue),
@ -393,10 +391,6 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
OnFrameReady(std::move(frames), render_time);
}
TimeDelta MaxWait() const RTC_RUN_ON(&worker_sequence_checker_) {
return keyframe_required_ ? max_wait_for_keyframe_ : max_wait_for_frame_;
}
void UpdateDroppedFrames() RTC_RUN_ON(&worker_sequence_checker_) {
const int dropped_frames = buffer_->GetTotalNumberOfDroppedFrames() -
frames_dropped_before_last_new_frame_;
@ -463,11 +457,15 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
return;
}
TimeDelta max_wait = timeout_tracker_.TimeUntilTimeout();
// Ensures the frame is scheduled for decode before the stream times out.
// This is otherwise a race condition.
max_wait = std::max(max_wait - TimeDelta::Millis(1), TimeDelta::Zero());
absl::optional<FrameDecodeTiming::FrameSchedule> schedule;
while (decodable_tu_info) {
schedule = decode_timing_.OnFrameBufferUpdated(
decodable_tu_info->next_rtp_timestamp,
decodable_tu_info->last_rtp_timestamp, MaxWait(),
decodable_tu_info->last_rtp_timestamp, max_wait,
IsTooManyFramesQueued());
if (schedule) {
// Don't schedule if already waiting for the same frame.
@ -488,8 +486,6 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
RTC_NO_UNIQUE_ADDRESS SequenceChecker worker_sequence_checker_;
const FieldTrialsView& field_trials_;
const TimeDelta max_wait_for_keyframe_;
const TimeDelta max_wait_for_frame_;
const absl::optional<RttMultExperiment::Settings> rtt_mult_settings_ =
RttMultExperiment::GetRttMultValue();
Clock* const clock_;

View File

@ -30,7 +30,7 @@ FrameDecodeTiming::OnFrameBufferUpdated(uint32_t next_temporal_unit_rtp,
uint32_t last_temporal_unit_rtp,
TimeDelta max_wait_for_frame,
bool too_many_frames_queued) {
RTC_DCHECK_GT(max_wait_for_frame, TimeDelta::Zero());
RTC_DCHECK_GE(max_wait_for_frame, TimeDelta::Zero());
const Timestamp now = clock_->CurrentTime();
Timestamp render_time = timing_->RenderTime(next_temporal_unit_rtp, now);
TimeDelta max_wait =
@ -42,15 +42,16 @@ FrameDecodeTiming::OnFrameBufferUpdated(uint32_t next_temporal_unit_rtp,
if (max_wait <= -kMaxAllowedFrameDelay &&
next_temporal_unit_rtp != last_temporal_unit_rtp) {
RTC_DLOG(LS_VERBOSE) << "Fast-forwarded frame " << next_temporal_unit_rtp
<< " render time " << render_time.ms()
<< " with delay " << max_wait.ms() << "ms";
<< " render time " << render_time << " with delay "
<< max_wait;
return absl::nullopt;
}
RTC_DLOG(LS_VERBOSE) << "Selected frame with rtp " << next_temporal_unit_rtp
<< " render time " << render_time.ms()
<< " with a max wait of " << max_wait.ms() << "ms";
max_wait.Clamp(TimeDelta::Zero(), max_wait_for_frame);
RTC_DLOG(LS_VERBOSE) << "Selected frame with rtp " << next_temporal_unit_rtp
<< " render time " << render_time
<< " with a max wait of " << max_wait_for_frame
<< " clamped to " << max_wait;
Timestamp latest_decode_time = now + max_wait;
return FrameSchedule{.latest_decode_time = latest_decode_time,
.render_time = render_time};

View File

@ -1162,6 +1162,59 @@ TEST_P(VideoReceiveStream2Test, PoorConnectionWithFpsChangeDuringLostFrame) {
video_receive_stream_->Stop();
}
TEST_P(VideoReceiveStream2Test, StreamShouldNotTimeoutWhileWaitingForFrame) {
// Disable smoothing since this makes it hard to test frame timing.
config_.enable_prerenderer_smoothing = false;
RecreateReceiveStream();
video_receive_stream_->Start();
EXPECT_CALL(mock_transport_, SendRtcp).Times(AnyNumber());
video_receive_stream_->OnCompleteFrame(
test::FakeFrameBuilder()
.Id(0)
.PayloadType(99)
.Time(RtpTimestampForFrame(0))
.ReceivedTime(ReceiveTimeForFrame(0))
.AsLast()
.Build());
EXPECT_THAT(fake_renderer_.WaitForFrame(k30FpsDelay, /*advance_time=*/true),
RenderedFrameWith(RtpTimestamp(RtpTimestampForFrame(0))));
for (int id = 1; id < 30; ++id) {
video_receive_stream_->OnCompleteFrame(
test::FakeFrameBuilder()
.Id(id)
.PayloadType(99)
.Time(RtpTimestampForFrame(id))
.ReceivedTime(ReceiveTimeForFrame(id))
.Refs({0})
.AsLast()
.Build());
EXPECT_THAT(fake_renderer_.WaitForFrame(k30FpsDelay, /*advance_time=*/true),
RenderedFrameWith(RtpTimestamp(RtpTimestampForFrame(id))));
}
// Simulate a pause in the stream, followed by a decodable frame that is ready
// long in the future. The stream should not timeout in this case, but rather
// decode the frame just before the timeout.
time_controller_.AdvanceTime(TimeDelta::Millis(2900));
uint32_t late_decode_rtp = kFirstRtpTimestamp + 200 * k30FpsRtpTimestampDelta;
video_receive_stream_->OnCompleteFrame(
test::FakeFrameBuilder()
.Id(121)
.PayloadType(99)
.Time(late_decode_rtp)
.ReceivedTime(clock_->CurrentTime())
.AsLast()
.Build());
EXPECT_THAT(fake_renderer_.WaitForFrame(TimeDelta::Millis(100),
/*advance_time=*/true),
RenderedFrameWith(RtpTimestamp(late_decode_rtp)));
video_receive_stream_->Stop();
}
INSTANTIATE_TEST_SUITE_P(VideoReceiveStream2Test,
VideoReceiveStream2Test,
testing::Bool(),

View File

@ -10,8 +10,11 @@
#include "video/video_receive_stream_timeout_tracker.h"
#include <algorithm>
#include <utility>
#include "rtc_base/logging.h"
namespace webrtc {
VideoReceiveStreamTimeoutTracker::VideoReceiveStreamTimeoutTracker(
@ -32,6 +35,10 @@ bool VideoReceiveStreamTimeoutTracker::Running() const {
return timeout_task_.Running();
}
TimeDelta VideoReceiveStreamTimeoutTracker::TimeUntilTimeout() const {
return std::max(timeout_ - clock_->CurrentTime(), TimeDelta::Zero());
}
void VideoReceiveStreamTimeoutTracker::Start(bool waiting_for_keyframe) {
RTC_DCHECK(!timeout_task_.Running());
waiting_for_keyframe_ = waiting_for_keyframe;
@ -68,6 +75,7 @@ TimeDelta VideoReceiveStreamTimeoutTracker::HandleTimeoutTask() {
// `timeout_` is hit and we have timed out. Schedule the next timeout at
// the timeout delay.
if (now >= timeout_) {
RTC_DLOG(LS_VERBOSE) << "Stream timeout at " << now;
TimeDelta timeout_delay = TimeoutForNextFrame();
timeout_ = now + timeout_delay;
timeout_cb_(now - last_frame_);

View File

@ -44,6 +44,7 @@ class VideoReceiveStreamTimeoutTracker {
void Stop();
void SetWaitingForKeyframe();
void OnEncodedFrameReleased();
TimeDelta TimeUntilTimeout() const;
private:
TimeDelta TimeoutForNextFrame() const {