| /* |
| * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "video/frame_decode_timing.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <optional> |
| |
| #include "api/field_trials_view.h" |
| #include "api/units/time_delta.h" |
| #include "api/units/timestamp.h" |
| #include "modules/video_coding/timing/timing.h" |
| #include "rtc_base/checks.h" |
| #include "rtc_base/experiments/field_trial_parser.h" |
| #include "rtc_base/logging.h" |
| #include "system_wrappers/include/clock.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| // Default pacing that is used for the low-latency renderer path. |
| constexpr TimeDelta kZeroPlayoutDelayDefaultMinPacing = TimeDelta::Millis(8); |
| |
| } // namespace |
| |
| FrameDecodeTiming::FrameDecodeTiming(Clock* clock, |
| VCMTiming const* timing, |
| const FieldTrialsView& field_trials) |
| : clock_(clock), |
| timing_(timing), |
| zero_playout_delay_min_pacing_("min_pacing", |
| kZeroPlayoutDelayDefaultMinPacing) { |
| RTC_DCHECK(clock_); |
| RTC_DCHECK(timing_); |
| ParseFieldTrial({&zero_playout_delay_min_pacing_}, |
| field_trials.Lookup("WebRTC-ZeroPlayoutDelay")); |
| } |
| |
| std::optional<FrameDecodeTiming::FrameSchedule> |
| 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_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 = MaxWaitingTime(render_time, now, too_many_frames_queued); |
| |
| // If the delay is not too far in the past, or this is the last decodable |
| // frame then it is the best frame to be decoded. Otherwise, fast-forward |
| // to the next frame in the buffer. |
| 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 << " with delay " |
| << max_wait; |
| return std::nullopt; |
| } |
| |
| max_wait = std::clamp(max_wait, 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}; |
| } |
| |
| void FrameDecodeTiming::SetLastDecodeScheduledTimestamp( |
| Timestamp last_decode_scheduled) { |
| last_decode_scheduled_ = last_decode_scheduled; |
| } |
| |
| TimeDelta FrameDecodeTiming::MaxWaitingTime(Timestamp render_time, |
| Timestamp now, |
| bool too_many_frames_queued) const { |
| const VCMTiming::VideoDelayTimings timings = timing_->GetTimings(); |
| if (render_time.IsZero() && zero_playout_delay_min_pacing_->us() > 0 && |
| timings.min_playout_delay.IsZero() && |
| timings.max_playout_delay > TimeDelta::Zero()) { |
| // `render_time` == 0 indicates that the frame should be decoded and |
| // rendered as soon as possible. However, the decoder can be choked if too |
| // many frames are sent at once. Therefore, limit the interframe delay to |
| // `zero_playout_delay_min_pacing_` unless too many frames are queued in |
| // which case the frames are sent to the decoder at once. |
| if (too_many_frames_queued) { |
| return TimeDelta::Zero(); |
| } |
| Timestamp earliest_next_decode_start_time = |
| last_decode_scheduled_ + zero_playout_delay_min_pacing_; |
| TimeDelta max_wait_time = now >= earliest_next_decode_start_time |
| ? TimeDelta::Zero() |
| : earliest_next_decode_start_time - now; |
| return max_wait_time; |
| } |
| return render_time - now - timings.estimated_max_decode_time - |
| timings.render_delay; |
| } |
| |
| } // namespace webrtc |