blob: 0cab3d25bf04446d21471e51f1642e0cf359839e [file]
// 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.h"
#include <algorithm>
#include <memory>
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "remoting/base/constants.h"
#include "remoting/proto/video.pb.h"
namespace remoting {
namespace protocol {
namespace {
// Default target bitrate in kbps
const int kDefaultTargetBitrateKbps = 1000;
} // namespace
// The frame scheduler currently uses a simple polling technique
// at 30 FPS to capture, encode and send frames over webrtc transport.
// An improved solution will use target bitrate feedback to pace out
// the capture rate.
WebrtcFrameScheduler::WebrtcFrameScheduler(
scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner,
std::unique_ptr<webrtc::DesktopCapturer> capturer,
WebrtcTransport* webrtc_transport,
std::unique_ptr<VideoEncoder> encoder)
: target_bitrate_kbps_(kDefaultTargetBitrateKbps),
main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
encode_task_runner_(encode_task_runner),
capturer_(std::move(capturer)),
webrtc_transport_(webrtc_transport),
encoder_(std::move(encoder)),
weak_factory_(this) {
DCHECK(encode_task_runner_);
DCHECK(capturer_);
DCHECK(webrtc_transport_);
DCHECK(encoder_);
// Does not really start anything. Registers callback on this class.
capturer_->Start(this);
capture_timer_.reset(new base::RepeatingTimer());
}
WebrtcFrameScheduler::~WebrtcFrameScheduler() {
encode_task_runner_->DeleteSoon(FROM_HERE, encoder_.release());
}
void WebrtcFrameScheduler::Start() {
// Register for PLI requests.
webrtc_transport_->video_encoder_factory()->SetKeyFrameRequestCallback(
base::Bind(&WebrtcFrameScheduler::SetKeyFrameRequest,
base::Unretained(this)));
// Register for target bitrate notifications.
webrtc_transport_->video_encoder_factory()->SetTargetBitrateCallback(
base::Bind(&WebrtcFrameScheduler::SetTargetBitrate,
base::Unretained(this)));
}
void WebrtcFrameScheduler::Stop() {
// Clear PLI request callback.
webrtc_transport_->video_encoder_factory()->SetKeyFrameRequestCallback(
base::Closure());
webrtc_transport_->video_encoder_factory()->SetTargetBitrateCallback(
TargetBitrateCallback());
// Cancel any pending encode.
task_tracker_.TryCancelAll();
capture_timer_->Stop();
}
void WebrtcFrameScheduler::Pause(bool pause) {
if (pause) {
Stop();
} else {
Start();
}
}
void WebrtcFrameScheduler::SetSizeCallback(
const VideoStream::SizeCallback& callback) {
size_callback_ = callback;
}
void WebrtcFrameScheduler::SetKeyFrameRequest() {
VLOG(1) << "Request key frame";
base::AutoLock lock(lock_);
key_frame_request_ = true;
if (!received_first_frame_request_) {
received_first_frame_request_ = true;
main_task_runner_->PostTask(
FROM_HERE, base::Bind(&WebrtcFrameScheduler::StartCaptureTimer,
weak_factory_.GetWeakPtr()));
}
}
void WebrtcFrameScheduler::StartCaptureTimer() {
capture_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1) / 30, this,
&WebrtcFrameScheduler::CaptureNextFrame);
}
void WebrtcFrameScheduler::SetTargetBitrate(int target_bitrate_kbps) {
VLOG(1) << "Set Target bitrate " << target_bitrate_kbps;
base::AutoLock lock(lock_);
target_bitrate_kbps_ = target_bitrate_kbps;
}
bool WebrtcFrameScheduler::ClearAndGetKeyFrameRequest() {
base::AutoLock lock(lock_);
bool key_frame_request = key_frame_request_;
key_frame_request_ = false;
return key_frame_request;
}
void WebrtcFrameScheduler::OnCaptureResult(
webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) {
DCHECK(thread_checker_.CalledOnValidThread());
base::TimeTicks captured_ticks = base::TimeTicks::Now();
int64_t capture_timestamp_ms =
(captured_ticks - base::TimeTicks()).InMilliseconds();
capture_pending_ = false;
// TODO(sergeyu): Handle ERROR_PERMANENT result here.
if (encode_pending_) {
// TODO(isheriff): consider queuing here
VLOG(1) << "Dropping captured frame since encoder is still busy";
return;
}
last_capture_completed_ticks_ = captured_ticks;
webrtc::DesktopVector dpi =
frame->dpi().is_zero() ? webrtc::DesktopVector(kDefaultDpi, kDefaultDpi)
: frame->dpi();
if (!frame_size_.equals(frame->size()) || !frame_dpi_.equals(dpi)) {
frame_size_ = frame->size();
frame_dpi_ = dpi;
if (!size_callback_.is_null())
size_callback_.Run(frame_size_, frame_dpi_);
}
encode_pending_ = true;
task_tracker_.PostTaskAndReplyWithResult(
encode_task_runner_.get(), FROM_HERE,
base::Bind(&WebrtcFrameScheduler::EncodeFrame, encoder_.get(),
base::Passed(&frame), target_bitrate_kbps_,
ClearAndGetKeyFrameRequest(), capture_timestamp_ms),
base::Bind(&WebrtcFrameScheduler::OnFrameEncoded,
weak_factory_.GetWeakPtr()));
}
void WebrtcFrameScheduler::CaptureNextFrame() {
DCHECK(thread_checker_.CalledOnValidThread());
if (capture_pending_ || encode_pending_) {
VLOG(1) << "Capture/encode still pending..";
return;
}
capture_pending_ = true;
VLOG(1) << "Capture next frame after "
<< (base::TimeTicks::Now() - last_capture_started_ticks_)
.InMilliseconds();
last_capture_started_ticks_ = base::TimeTicks::Now();
capturer_->Capture(webrtc::DesktopRegion());
}
// static
std::unique_ptr<VideoPacket> WebrtcFrameScheduler::EncodeFrame(
VideoEncoder* encoder,
std::unique_ptr<webrtc::DesktopFrame> frame,
uint32_t target_bitrate_kbps,
bool key_frame_request,
int64_t capture_time_ms) {
uint32_t flags = 0;
if (key_frame_request)
flags |= VideoEncoder::REQUEST_KEY_FRAME;
base::TimeTicks current = base::TimeTicks::Now();
encoder->UpdateTargetBitrate(target_bitrate_kbps);
std::unique_ptr<VideoPacket> packet = encoder->Encode(*frame, flags);
if (!packet)
return nullptr;
// TODO(isheriff): Note that while VideoPacket capture time is supposed
// to be capture duration, we (ab)use it for capture timestamp here. This
// will go away when we move away from VideoPacket.
packet->set_capture_time_ms(capture_time_ms);
VLOG(1) << "Encode duration "
<< (base::TimeTicks::Now() - current).InMilliseconds()
<< " payload size " << packet->data().size();
return packet;
}
void WebrtcFrameScheduler::OnFrameEncoded(std::unique_ptr<VideoPacket> packet) {
DCHECK(thread_checker_.CalledOnValidThread());
encode_pending_ = false;
if (!packet)
return;
base::TimeTicks current = base::TimeTicks::Now();
float encoded_bits = packet->data().size() * 8.0;
// Simplistic adaptation of frame polling in the range 5 FPS to 30 FPS.
uint32_t next_sched_ms = std::max(
33, std::min(static_cast<int>(encoded_bits / target_bitrate_kbps_), 200));
if (webrtc_transport_->video_encoder_factory()->SendEncodedFrame(
std::move(packet)) >= 0) {
VLOG(1) << " Send duration "
<< (base::TimeTicks::Now() - current).InMilliseconds()
<< "next sched " << next_sched_ms;
} else {
LOG(ERROR) << "SendEncodedFrame() failed";
}
capture_timer_->Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(next_sched_ms), this,
&WebrtcFrameScheduler::CaptureNextFrame);
}
} // namespace protocol
} // namespace remoting