blob: 4469f449a8b93ff72701a296a65b16810f462e67 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/protocol/webrtc_frame_scheduler_simple.h"
#include <algorithm>
#include "base/bind.h"
#include "base/cxx17_backports.h"
#include "base/time/default_tick_clock.h"
#include "remoting/base/constants.h"
#include "remoting/protocol/frame_stats.h"
#include "remoting/protocol/webrtc_bandwidth_estimator.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
namespace remoting {
namespace protocol {
namespace {
constexpr base::TimeDelta kTargetFrameInterval =
base::Milliseconds(1000 / kTargetFrameRate);
// Minimum interval between frames needed to keep the connection alive. The
// client will request a key-frame if it does not receive any frames for a
// 3-second period. This is effectively a minimum frame-rate, so the value
// should not be too small, otherwise the client may waste CPU cycles on
// processing and rendering lots of identical frames.
constexpr base::TimeDelta kKeepAliveInterval = base::Milliseconds(2000);
// Baseline bandwidth to use for scheduling captures. This is only used
// until the next OnTargetBitrateChanged() notification, typically after
// 2 or 3 capture/encode cycles. Any realistic value should work OK - the
// chosen value is the current upper limit for relay connections.
constexpr int kBaselineBandwidthKbps = 8000;
} // namespace
// TODO(zijiehe): Use |options| to select bandwidth estimator.
WebrtcFrameSchedulerSimple::WebrtcFrameSchedulerSimple(
const SessionOptions& options)
: tick_clock_(base::DefaultTickClock::GetInstance()),
pacing_bucket_(LeakyBucket::kUnlimitedDepth,
kBaselineBandwidthKbps * 1000 / 8),
bandwidth_estimator_(new WebrtcBandwidthEstimator()) {
// Set up bandwidth-estimators with an initial rate so that captures can be
// scheduled when the encoder is ready. With the standard encoding pipeline
// (has_internal_source == false), WebRTC does not create the encoder until
// after the first frame is captured and sent to the VideoTrack's output
// sink. Bandwidth updates cannot be received until after this occurs.
bandwidth_estimator_->OnBitrateEstimation(kBaselineBandwidthKbps);
processing_time_estimator_.SetBandwidthKbps(kBaselineBandwidthKbps);
}
WebrtcFrameSchedulerSimple::~WebrtcFrameSchedulerSimple() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void WebrtcFrameSchedulerSimple::OnKeyFrameRequested() {
DCHECK(thread_checker_.CalledOnValidThread());
key_frame_request_ = true;
ScheduleNextFrame();
}
void WebrtcFrameSchedulerSimple::OnTargetBitrateChanged(int bandwidth_kbps) {
DCHECK(thread_checker_.CalledOnValidThread());
bandwidth_estimator_->OnReceivedAck();
bandwidth_estimator_->OnBitrateEstimation(bandwidth_kbps);
processing_time_estimator_.SetBandwidthKbps(
bandwidth_estimator_->GetBitrateKbps());
pacing_bucket_.UpdateRate(bandwidth_estimator_->GetBitrateKbps() * 1000 / 8,
tick_clock_->NowTicks());
ScheduleNextFrame();
}
void WebrtcFrameSchedulerSimple::Start(
const base::RepeatingClosure& capture_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
capture_callback_ = capture_callback;
}
void WebrtcFrameSchedulerSimple::Pause(bool pause) {
DCHECK(thread_checker_.CalledOnValidThread());
paused_ = pause;
if (paused_) {
capture_timer_.Stop();
} else {
ScheduleNextFrame();
}
}
void WebrtcFrameSchedulerSimple::OnFrameCaptured(
const webrtc::DesktopFrame* frame) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(frame_pending_);
frame_pending_ = false;
// Null |frame| indicates a capturer error.
if (!frame) {
frame_pending_ = false;
ScheduleNextFrame();
return;
}
key_frame_request_ = false;
ScheduleNextFrame();
}
void WebrtcFrameSchedulerSimple::OnFrameEncoded(
WebrtcVideoEncoder::EncodeResult encode_result,
const WebrtcVideoEncoder::EncodedFrame* encoded_frame) {
DCHECK(thread_checker_.CalledOnValidThread());
base::TimeTicks now = tick_clock_->NowTicks();
// Calculate |send_pending_delay| before refilling |pacing_bucket_|.
if (encoded_frame && encoded_frame->stats) {
encoded_frame->stats->send_pending_delay =
std::max(base::TimeDelta(), pacing_bucket_.GetEmptyTime() - now);
}
// TODO(zijiehe): |encoded_frame|->data.empty() is unreasonable, we should try
// to get rid of it in WebrtcVideoEncoder layer.
if (encoded_frame && !encoded_frame->data.empty()) {
pacing_bucket_.RefillOrSpill(encoded_frame->data.size(), now);
processing_time_estimator_.FinishFrame(*encoded_frame);
}
ScheduleNextFrame();
bandwidth_estimator_->OnSendingFrame(*encoded_frame);
}
void WebrtcFrameSchedulerSimple::OnEncodedFrameSent(
webrtc::EncodedImageCallback::Result result,
const WebrtcVideoEncoder::EncodedFrame& frame) {}
void WebrtcFrameSchedulerSimple::SetMaxFramerateFps(int max_framerate_fps) {
// TODO(http://crbug.com/1268253): Implement this.
encoder_ready_ = true;
ScheduleNextFrame();
}
void WebrtcFrameSchedulerSimple::SetTickClockForTest(
const base::TickClock* tick_clock) {
tick_clock_ = tick_clock;
}
void WebrtcFrameSchedulerSimple::ScheduleNextFrame() {
DCHECK(thread_checker_.CalledOnValidThread());
base::TimeTicks now = tick_clock_->NowTicks();
if (!encoder_ready_ || paused_ || pacing_bucket_.rate() == 0 ||
capture_callback_.is_null() || frame_pending_) {
return;
}
base::TimeTicks target_capture_time;
if (!last_capture_started_time_.is_null()) {
// Try to set the capture time so that (if the estimated processing time is
// accurate) the new frame is ready to be sent just when the previous frame
// is finished sending.
target_capture_time = pacing_bucket_.GetEmptyTime() -
processing_time_estimator_.EstimatedProcessingTime(key_frame_request_);
// Ensure that the capture rate is capped by kTargetFrameInterval, to avoid
// excessive CPU usage by the capturer.
// Also ensure that the video does not freeze for excessively long periods.
// This protects against, for example, bugs in the b/w estimator or the
// LeakyBucket implementation which may result in unbounded wait times.
// If the network is such that it really takes > 2 or 3 seconds to send one
// video frame, then this upper-bound cap could result in packet-loss,
// triggering PLI (key-frame request). But the session would be already
// unusable under such network conditions. And the client would trigger PLI
// anyway if it doesn't receive any video for > 3 seconds.
target_capture_time = base::clamp(
target_capture_time, last_capture_started_time_ + kTargetFrameInterval,
last_capture_started_time_ + kKeepAliveInterval);
}
target_capture_time = std::max(target_capture_time, now);
capture_timer_.Start(
FROM_HERE, target_capture_time - now,
base::BindOnce(&WebrtcFrameSchedulerSimple::CaptureNextFrame,
base::Unretained(this)));
}
void WebrtcFrameSchedulerSimple::CaptureNextFrame() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!frame_pending_);
last_capture_started_time_ = tick_clock_->NowTicks();
frame_pending_ = true;
capture_callback_.Run();
}
} // namespace protocol
} // namespace remoting