xref: /aosp_15_r20/external/webrtc/modules/video_coding/timing/timing.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "modules/video_coding/timing/timing.h"
12 
13 #include <algorithm>
14 
15 #include "api/units/time_delta.h"
16 #include "modules/video_coding/timing/timestamp_extrapolator.h"
17 #include "rtc_base/experiments/field_trial_parser.h"
18 #include "rtc_base/logging.h"
19 #include "system_wrappers/include/clock.h"
20 
21 namespace webrtc {
22 namespace {
23 
24 // Default pacing that is used for the low-latency renderer path.
25 constexpr TimeDelta kZeroPlayoutDelayDefaultMinPacing = TimeDelta::Millis(8);
26 constexpr TimeDelta kLowLatencyStreamMaxPlayoutDelayThreshold =
27     TimeDelta::Millis(500);
28 
CheckDelaysValid(TimeDelta min_delay,TimeDelta max_delay)29 void CheckDelaysValid(TimeDelta min_delay, TimeDelta max_delay) {
30   if (min_delay > max_delay) {
31     RTC_LOG(LS_ERROR)
32         << "Playout delays set incorrectly: min playout delay (" << min_delay
33         << ") > max playout delay (" << max_delay
34         << "). This is undefined behaviour. Application writers should "
35            "ensure that the min delay is always less than or equals max "
36            "delay. If trying to use the playout delay header extensions "
37            "described in "
38            "https://webrtc.googlesource.com/src/+/refs/heads/main/docs/"
39            "native-code/rtp-hdrext/playout-delay/, be careful that a playout "
40            "delay hint or A/V sync settings may have caused this conflict.";
41   }
42 }
43 
44 }  // namespace
45 
VCMTiming(Clock * clock,const FieldTrialsView & field_trials)46 VCMTiming::VCMTiming(Clock* clock, const FieldTrialsView& field_trials)
47     : clock_(clock),
48       ts_extrapolator_(
49           std::make_unique<TimestampExtrapolator>(clock_->CurrentTime())),
50       codec_timer_(std::make_unique<CodecTimer>()),
51       render_delay_(kDefaultRenderDelay),
52       min_playout_delay_(TimeDelta::Zero()),
53       max_playout_delay_(TimeDelta::Seconds(10)),
54       jitter_delay_(TimeDelta::Zero()),
55       current_delay_(TimeDelta::Zero()),
56       prev_frame_timestamp_(0),
57       num_decoded_frames_(0),
58       zero_playout_delay_min_pacing_("min_pacing",
59                                      kZeroPlayoutDelayDefaultMinPacing),
60       last_decode_scheduled_(Timestamp::Zero()) {
61   ParseFieldTrial({&zero_playout_delay_min_pacing_},
62                   field_trials.Lookup("WebRTC-ZeroPlayoutDelay"));
63 }
64 
Reset()65 void VCMTiming::Reset() {
66   MutexLock lock(&mutex_);
67   ts_extrapolator_->Reset(clock_->CurrentTime());
68   codec_timer_ = std::make_unique<CodecTimer>();
69   render_delay_ = kDefaultRenderDelay;
70   min_playout_delay_ = TimeDelta::Zero();
71   jitter_delay_ = TimeDelta::Zero();
72   current_delay_ = TimeDelta::Zero();
73   prev_frame_timestamp_ = 0;
74 }
75 
set_render_delay(TimeDelta render_delay)76 void VCMTiming::set_render_delay(TimeDelta render_delay) {
77   MutexLock lock(&mutex_);
78   render_delay_ = render_delay;
79 }
80 
min_playout_delay() const81 TimeDelta VCMTiming::min_playout_delay() const {
82   MutexLock lock(&mutex_);
83   return min_playout_delay_;
84 }
85 
set_min_playout_delay(TimeDelta min_playout_delay)86 void VCMTiming::set_min_playout_delay(TimeDelta min_playout_delay) {
87   MutexLock lock(&mutex_);
88   if (min_playout_delay_ != min_playout_delay) {
89     CheckDelaysValid(min_playout_delay, max_playout_delay_);
90     min_playout_delay_ = min_playout_delay;
91   }
92 }
93 
set_max_playout_delay(TimeDelta max_playout_delay)94 void VCMTiming::set_max_playout_delay(TimeDelta max_playout_delay) {
95   MutexLock lock(&mutex_);
96   if (max_playout_delay_ != max_playout_delay) {
97     CheckDelaysValid(min_playout_delay_, max_playout_delay);
98     max_playout_delay_ = max_playout_delay;
99   }
100 }
101 
SetJitterDelay(TimeDelta jitter_delay)102 void VCMTiming::SetJitterDelay(TimeDelta jitter_delay) {
103   MutexLock lock(&mutex_);
104   if (jitter_delay != jitter_delay_) {
105     jitter_delay_ = jitter_delay;
106     // When in initial state, set current delay to minimum delay.
107     if (current_delay_.IsZero()) {
108       current_delay_ = jitter_delay_;
109     }
110   }
111 }
112 
UpdateCurrentDelay(uint32_t frame_timestamp)113 void VCMTiming::UpdateCurrentDelay(uint32_t frame_timestamp) {
114   MutexLock lock(&mutex_);
115   TimeDelta target_delay = TargetDelayInternal();
116 
117   if (current_delay_.IsZero()) {
118     // Not initialized, set current delay to target.
119     current_delay_ = target_delay;
120   } else if (target_delay != current_delay_) {
121     TimeDelta delay_diff = target_delay - current_delay_;
122     // Never change the delay with more than 100 ms every second. If we're
123     // changing the delay in too large steps we will get noticeable freezes. By
124     // limiting the change we can increase the delay in smaller steps, which
125     // will be experienced as the video is played in slow motion. When lowering
126     // the delay the video will be played at a faster pace.
127     TimeDelta max_change = TimeDelta::Zero();
128     if (frame_timestamp < 0x0000ffff && prev_frame_timestamp_ > 0xffff0000) {
129       // wrap
130       max_change =
131           TimeDelta::Millis(kDelayMaxChangeMsPerS *
132                             (frame_timestamp + (static_cast<int64_t>(1) << 32) -
133                              prev_frame_timestamp_) /
134                             90000);
135     } else {
136       max_change =
137           TimeDelta::Millis(kDelayMaxChangeMsPerS *
138                             (frame_timestamp - prev_frame_timestamp_) / 90000);
139     }
140 
141     if (max_change <= TimeDelta::Zero()) {
142       // Any changes less than 1 ms are truncated and will be postponed.
143       // Negative change will be due to reordering and should be ignored.
144       return;
145     }
146     delay_diff = std::max(delay_diff, -max_change);
147     delay_diff = std::min(delay_diff, max_change);
148 
149     current_delay_ = current_delay_ + delay_diff;
150   }
151   prev_frame_timestamp_ = frame_timestamp;
152 }
153 
UpdateCurrentDelay(Timestamp render_time,Timestamp actual_decode_time)154 void VCMTiming::UpdateCurrentDelay(Timestamp render_time,
155                                    Timestamp actual_decode_time) {
156   MutexLock lock(&mutex_);
157   TimeDelta target_delay = TargetDelayInternal();
158   TimeDelta delayed =
159       (actual_decode_time - render_time) + RequiredDecodeTime() + render_delay_;
160 
161   // Only consider `delayed` as negative by more than a few microseconds.
162   if (delayed.ms() < 0) {
163     return;
164   }
165   if (current_delay_ + delayed <= target_delay) {
166     current_delay_ += delayed;
167   } else {
168     current_delay_ = target_delay;
169   }
170 }
171 
StopDecodeTimer(TimeDelta decode_time,Timestamp now)172 void VCMTiming::StopDecodeTimer(TimeDelta decode_time, Timestamp now) {
173   MutexLock lock(&mutex_);
174   codec_timer_->AddTiming(decode_time.ms(), now.ms());
175   RTC_DCHECK_GE(decode_time, TimeDelta::Zero());
176   ++num_decoded_frames_;
177 }
178 
IncomingTimestamp(uint32_t rtp_timestamp,Timestamp now)179 void VCMTiming::IncomingTimestamp(uint32_t rtp_timestamp, Timestamp now) {
180   MutexLock lock(&mutex_);
181   ts_extrapolator_->Update(now, rtp_timestamp);
182 }
183 
RenderTime(uint32_t frame_timestamp,Timestamp now) const184 Timestamp VCMTiming::RenderTime(uint32_t frame_timestamp, Timestamp now) const {
185   MutexLock lock(&mutex_);
186   return RenderTimeInternal(frame_timestamp, now);
187 }
188 
SetLastDecodeScheduledTimestamp(Timestamp last_decode_scheduled)189 void VCMTiming::SetLastDecodeScheduledTimestamp(
190     Timestamp last_decode_scheduled) {
191   MutexLock lock(&mutex_);
192   last_decode_scheduled_ = last_decode_scheduled;
193 }
194 
RenderTimeInternal(uint32_t frame_timestamp,Timestamp now) const195 Timestamp VCMTiming::RenderTimeInternal(uint32_t frame_timestamp,
196                                         Timestamp now) const {
197   if (UseLowLatencyRendering()) {
198     // Render as soon as possible or with low-latency renderer algorithm.
199     return Timestamp::Zero();
200   }
201   // Note that TimestampExtrapolator::ExtrapolateLocalTime is not a const
202   // method; it mutates the object's wraparound state.
203   Timestamp estimated_complete_time =
204       ts_extrapolator_->ExtrapolateLocalTime(frame_timestamp).value_or(now);
205 
206   // Make sure the actual delay stays in the range of `min_playout_delay_`
207   // and `max_playout_delay_`.
208   TimeDelta actual_delay =
209       current_delay_.Clamped(min_playout_delay_, max_playout_delay_);
210   return estimated_complete_time + actual_delay;
211 }
212 
RequiredDecodeTime() const213 TimeDelta VCMTiming::RequiredDecodeTime() const {
214   const int decode_time_ms = codec_timer_->RequiredDecodeTimeMs();
215   RTC_DCHECK_GE(decode_time_ms, 0);
216   return TimeDelta::Millis(decode_time_ms);
217 }
218 
MaxWaitingTime(Timestamp render_time,Timestamp now,bool too_many_frames_queued) const219 TimeDelta VCMTiming::MaxWaitingTime(Timestamp render_time,
220                                     Timestamp now,
221                                     bool too_many_frames_queued) const {
222   MutexLock lock(&mutex_);
223 
224   if (render_time.IsZero() && zero_playout_delay_min_pacing_->us() > 0 &&
225       min_playout_delay_.IsZero() && max_playout_delay_ > TimeDelta::Zero()) {
226     // `render_time` == 0 indicates that the frame should be decoded and
227     // rendered as soon as possible. However, the decoder can be choked if too
228     // many frames are sent at once. Therefore, limit the interframe delay to
229     // |zero_playout_delay_min_pacing_| unless too many frames are queued in
230     // which case the frames are sent to the decoder at once.
231     if (too_many_frames_queued) {
232       return TimeDelta::Zero();
233     }
234     Timestamp earliest_next_decode_start_time =
235         last_decode_scheduled_ + zero_playout_delay_min_pacing_;
236     TimeDelta max_wait_time = now >= earliest_next_decode_start_time
237                                   ? TimeDelta::Zero()
238                                   : earliest_next_decode_start_time - now;
239     return max_wait_time;
240   }
241   return render_time - now - RequiredDecodeTime() - render_delay_;
242 }
243 
TargetVideoDelay() const244 TimeDelta VCMTiming::TargetVideoDelay() const {
245   MutexLock lock(&mutex_);
246   return TargetDelayInternal();
247 }
248 
TargetDelayInternal() const249 TimeDelta VCMTiming::TargetDelayInternal() const {
250   return std::max(min_playout_delay_,
251                   jitter_delay_ + RequiredDecodeTime() + render_delay_);
252 }
253 
RenderParameters() const254 VideoFrame::RenderParameters VCMTiming::RenderParameters() const {
255   MutexLock lock(&mutex_);
256   return {.use_low_latency_rendering = UseLowLatencyRendering(),
257           .max_composition_delay_in_frames = max_composition_delay_in_frames_};
258 }
259 
UseLowLatencyRendering() const260 bool VCMTiming::UseLowLatencyRendering() const {
261   // min_playout_delay_==0,
262   // max_playout_delay_<=kLowLatencyStreamMaxPlayoutDelayThreshold indicates
263   // that the low-latency path should be used, which means that frames should be
264   // decoded and rendered as soon as possible.
265   return min_playout_delay_.IsZero() &&
266          max_playout_delay_ <= kLowLatencyStreamMaxPlayoutDelayThreshold;
267 }
268 
GetTimings() const269 VCMTiming::VideoDelayTimings VCMTiming::GetTimings() const {
270   MutexLock lock(&mutex_);
271   return VideoDelayTimings{.max_decode_duration = RequiredDecodeTime(),
272                            .current_delay = current_delay_,
273                            .target_delay = TargetDelayInternal(),
274                            .jitter_buffer_delay = jitter_delay_,
275                            .min_playout_delay = min_playout_delay_,
276                            .max_playout_delay = max_playout_delay_,
277                            .render_delay = render_delay_,
278                            .num_decoded_frames = num_decoded_frames_};
279 }
280 
SetTimingFrameInfo(const TimingFrameInfo & info)281 void VCMTiming::SetTimingFrameInfo(const TimingFrameInfo& info) {
282   MutexLock lock(&mutex_);
283   timing_frame_info_.emplace(info);
284 }
285 
GetTimingFrameInfo()286 absl::optional<TimingFrameInfo> VCMTiming::GetTimingFrameInfo() {
287   MutexLock lock(&mutex_);
288   return timing_frame_info_;
289 }
290 
SetMaxCompositionDelayInFrames(absl::optional<int> max_composition_delay_in_frames)291 void VCMTiming::SetMaxCompositionDelayInFrames(
292     absl::optional<int> max_composition_delay_in_frames) {
293   MutexLock lock(&mutex_);
294   max_composition_delay_in_frames_ = max_composition_delay_in_frames;
295 }
296 
297 }  // namespace webrtc
298