blob: 7d4da3d47a16d36c907fbb188e3371cdd9c17584 [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);
Evan Shrubsolec5a91442022-09-05 14:09:29 +000041 std::move(sf.callback_)(sf.rtp_timestamp_, sf.schedule_.render_time);
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +010042}
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() {
Markus Handellbe400e42022-11-08 12:14:23 +0100109 RTC_DCHECK_RUN_ON(worker_queue_);
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100110 RTC_DCHECK(schedulers_.empty());
111}
112
113std::unique_ptr<FrameDecodeScheduler>
114DecodeSynchronizer::CreateSynchronizedFrameScheduler() {
115 RTC_DCHECK_RUN_ON(worker_queue_);
116 auto scheduler = std::make_unique<SynchronizedFrameDecodeScheduler>(this);
117 auto [it, inserted] = schedulers_.emplace(scheduler.get());
118 // If this is the first `scheduler` added, start listening to the metronome.
119 if (inserted && schedulers_.size() == 1) {
120 RTC_DLOG(LS_VERBOSE) << "Listening to metronome";
Markus Handellbe400e42022-11-08 12:14:23 +0100121 ScheduleNextTick();
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100122 }
123
124 return std::move(scheduler);
125}
126
127void DecodeSynchronizer::OnFrameScheduled(
128 SynchronizedFrameDecodeScheduler* scheduler) {
129 RTC_DCHECK_RUN_ON(worker_queue_);
130 RTC_DCHECK(scheduler->ScheduledRtpTimestamp());
131
132 Timestamp now = clock_->CurrentTime();
133 Timestamp next_tick = expected_next_tick_;
134 // If no tick has registered yet assume it will occur in the tick period.
135 if (next_tick.IsInfinite()) {
136 next_tick = now + metronome_->TickPeriod();
137 }
138
139 // Release the frame right away if the decode time is too soon. Otherwise
140 // the stream may fall behind too much.
141 bool decode_before_next_tick =
142 scheduler->LatestDecodeTime() <
143 (next_tick - FrameDecodeTiming::kMaxAllowedFrameDelay);
144 // Decode immediately if the decode time is in the past.
145 bool decode_time_in_past = scheduler->LatestDecodeTime() < now;
146
147 if (decode_before_next_tick || decode_time_in_past) {
148 ScheduledFrame scheduled_frame = scheduler->ReleaseNextFrame();
149 std::move(scheduled_frame).RunFrameReleaseCallback();
150 }
151}
152
153void DecodeSynchronizer::RemoveFrameScheduler(
154 SynchronizedFrameDecodeScheduler* scheduler) {
155 RTC_DCHECK_RUN_ON(worker_queue_);
156 RTC_DCHECK(scheduler);
157 auto it = schedulers_.find(scheduler);
158 if (it == schedulers_.end()) {
159 return;
160 }
161 schedulers_.erase(it);
162 // If there are no more schedulers active, stop listening for metronome ticks.
163 if (schedulers_.empty()) {
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100164 expected_next_tick_ = Timestamp::PlusInfinity();
165 }
166}
167
Markus Handellbe400e42022-11-08 12:14:23 +0100168void DecodeSynchronizer::ScheduleNextTick() {
169 RTC_DCHECK_RUN_ON(worker_queue_);
170 metronome_->RequestCallOnNextTick(
171 SafeTask(safety_.flag(), [this] { OnTick(); }));
172}
173
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100174void DecodeSynchronizer::OnTick() {
175 RTC_DCHECK_RUN_ON(worker_queue_);
176 expected_next_tick_ = clock_->CurrentTime() + metronome_->TickPeriod();
177
178 for (auto* scheduler : schedulers_) {
179 if (scheduler->ScheduledRtpTimestamp() &&
180 scheduler->LatestDecodeTime() < expected_next_tick_) {
181 auto scheduled_frame = scheduler->ReleaseNextFrame();
182 std::move(scheduled_frame).RunFrameReleaseCallback();
183 }
184 }
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100185
Markus Handellbe400e42022-11-08 12:14:23 +0100186 if (!schedulers_.empty())
187 ScheduleNextTick();
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100188}
189
190} // namespace webrtc