blob: 9f22c49586382672c7f178b1a872cfdffc4c5695 [file] [log] [blame]
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +01001/*
2 * Copyright (c) 2022 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 "video/decode_synchronizer.h"
12
13#include <iterator>
14#include <memory>
15#include <utility>
16#include <vector>
17
18#include "api/sequence_checker.h"
19#include "api/units/time_delta.h"
20#include "api/units/timestamp.h"
21#include "rtc_base/checks.h"
22#include "rtc_base/logging.h"
23#include "video/frame_decode_scheduler.h"
24#include "video/frame_decode_timing.h"
25
26namespace webrtc {
27
28DecodeSynchronizer::ScheduledFrame::ScheduledFrame(
29 uint32_t rtp_timestamp,
30 FrameDecodeTiming::FrameSchedule schedule,
31 FrameDecodeScheduler::FrameReleaseCallback callback)
32 : rtp_timestamp_(rtp_timestamp),
33 schedule_(std::move(schedule)),
34 callback_(std::move(callback)) {}
35
36void DecodeSynchronizer::ScheduledFrame::RunFrameReleaseCallback() && {
37 // Inspiration from Chromium base::OnceCallback. Move `*this` to a local
38 // before execution to ensure internal state is cleared after callback
39 // execution.
40 auto sf = std::move(*this);
41 sf.callback_(sf.rtp_timestamp_, sf.schedule_.render_time);
42}
43
44Timestamp DecodeSynchronizer::ScheduledFrame::LatestDecodeTime() const {
45 return schedule_.latest_decode_time;
46}
47
48DecodeSynchronizer::SynchronizedFrameDecodeScheduler::
49 SynchronizedFrameDecodeScheduler(DecodeSynchronizer* sync)
50 : sync_(sync) {
51 RTC_DCHECK(sync_);
52}
53
54DecodeSynchronizer::SynchronizedFrameDecodeScheduler::
55 ~SynchronizedFrameDecodeScheduler() {
56 RTC_DCHECK(!next_frame_);
57 RTC_DCHECK(stopped_);
58}
59
60absl::optional<uint32_t>
61DecodeSynchronizer::SynchronizedFrameDecodeScheduler::ScheduledRtpTimestamp() {
62 return next_frame_.has_value()
63 ? absl::make_optional(next_frame_->rtp_timestamp())
64 : absl::nullopt;
65}
66
67DecodeSynchronizer::ScheduledFrame
68DecodeSynchronizer::SynchronizedFrameDecodeScheduler::ReleaseNextFrame() {
69 RTC_DCHECK(next_frame_);
70 auto res = std::move(*next_frame_);
71 next_frame_.reset();
72 return res;
73}
74
75Timestamp
76DecodeSynchronizer::SynchronizedFrameDecodeScheduler::LatestDecodeTime() {
77 RTC_DCHECK(next_frame_);
78 return next_frame_->LatestDecodeTime();
79}
80
81void DecodeSynchronizer::SynchronizedFrameDecodeScheduler::ScheduleFrame(
82 uint32_t rtp,
83 FrameDecodeTiming::FrameSchedule schedule,
84 FrameReleaseCallback cb) {
85 RTC_DCHECK(!next_frame_) << "Can not schedule two frames at once.";
86 next_frame_ = ScheduledFrame(rtp, std::move(schedule), std::move(cb));
87 sync_->OnFrameScheduled(this);
88}
89
90void DecodeSynchronizer::SynchronizedFrameDecodeScheduler::CancelOutstanding() {
91 next_frame_.reset();
92}
93
94void DecodeSynchronizer::SynchronizedFrameDecodeScheduler::Stop() {
95 CancelOutstanding();
96 stopped_ = true;
97 sync_->RemoveFrameScheduler(this);
98}
99
100DecodeSynchronizer::DecodeSynchronizer(Clock* clock,
101 Metronome* metronome,
102 TaskQueueBase* worker_queue)
103 : clock_(clock), worker_queue_(worker_queue), metronome_(metronome) {
104 RTC_DCHECK(metronome_);
105 RTC_DCHECK(worker_queue_);
106}
107
108DecodeSynchronizer::~DecodeSynchronizer() {
109 RTC_DCHECK(schedulers_.empty());
110}
111
112std::unique_ptr<FrameDecodeScheduler>
113DecodeSynchronizer::CreateSynchronizedFrameScheduler() {
114 RTC_DCHECK_RUN_ON(worker_queue_);
115 auto scheduler = std::make_unique<SynchronizedFrameDecodeScheduler>(this);
116 auto [it, inserted] = schedulers_.emplace(scheduler.get());
117 // If this is the first `scheduler` added, start listening to the metronome.
118 if (inserted && schedulers_.size() == 1) {
119 RTC_DLOG(LS_VERBOSE) << "Listening to metronome";
120 metronome_->AddListener(this);
121 }
122
123 return std::move(scheduler);
124}
125
126void DecodeSynchronizer::OnFrameScheduled(
127 SynchronizedFrameDecodeScheduler* scheduler) {
128 RTC_DCHECK_RUN_ON(worker_queue_);
129 RTC_DCHECK(scheduler->ScheduledRtpTimestamp());
130
131 Timestamp now = clock_->CurrentTime();
132 Timestamp next_tick = expected_next_tick_;
133 // If no tick has registered yet assume it will occur in the tick period.
134 if (next_tick.IsInfinite()) {
135 next_tick = now + metronome_->TickPeriod();
136 }
137
138 // Release the frame right away if the decode time is too soon. Otherwise
139 // the stream may fall behind too much.
140 bool decode_before_next_tick =
141 scheduler->LatestDecodeTime() <
142 (next_tick - FrameDecodeTiming::kMaxAllowedFrameDelay);
143 // Decode immediately if the decode time is in the past.
144 bool decode_time_in_past = scheduler->LatestDecodeTime() < now;
145
146 if (decode_before_next_tick || decode_time_in_past) {
147 ScheduledFrame scheduled_frame = scheduler->ReleaseNextFrame();
148 std::move(scheduled_frame).RunFrameReleaseCallback();
149 }
150}
151
152void DecodeSynchronizer::RemoveFrameScheduler(
153 SynchronizedFrameDecodeScheduler* scheduler) {
154 RTC_DCHECK_RUN_ON(worker_queue_);
155 RTC_DCHECK(scheduler);
156 auto it = schedulers_.find(scheduler);
157 if (it == schedulers_.end()) {
158 return;
159 }
160 schedulers_.erase(it);
161 // If there are no more schedulers active, stop listening for metronome ticks.
162 if (schedulers_.empty()) {
163 RTC_DLOG(LS_VERBOSE) << "Not listening to metronome";
164 metronome_->RemoveListener(this);
165 expected_next_tick_ = Timestamp::PlusInfinity();
166 }
167}
168
169void DecodeSynchronizer::OnTick() {
170 RTC_DCHECK_RUN_ON(worker_queue_);
171 expected_next_tick_ = clock_->CurrentTime() + metronome_->TickPeriod();
172
173 for (auto* scheduler : schedulers_) {
174 if (scheduler->ScheduledRtpTimestamp() &&
175 scheduler->LatestDecodeTime() < expected_next_tick_) {
176 auto scheduled_frame = scheduler->ReleaseNextFrame();
177 std::move(scheduled_frame).RunFrameReleaseCallback();
178 }
179 }
180}
181
182TaskQueueBase* DecodeSynchronizer::OnTickTaskQueue() {
183 return worker_queue_;
184}
185
186} // namespace webrtc