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