| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/protocol/webrtc_frame_scheduler.h" | 5 #include "remoting/protocol/webrtc_frame_scheduler.h" |
| 6 | 6 |
| 7 #include <algorithm> |
| 7 #include <memory> | 8 #include <memory> |
| 8 | 9 |
| 9 #include "base/logging.h" | 10 #include "base/logging.h" |
| 10 #include "remoting/base/constants.h" | 11 #include "remoting/base/constants.h" |
| 11 #include "remoting/proto/video.pb.h" | 12 #include "remoting/proto/video.pb.h" |
| 12 | 13 |
| 13 namespace remoting { | 14 namespace remoting { |
| 14 namespace protocol { | 15 namespace protocol { |
| 15 | 16 |
| 17 namespace { |
| 18 |
| 19 // Amount of time in microseconds after which the accumulated average is halved. |
| 20 const int kAccFrameDurationHalfLifeUs = 200000; |
| 21 |
| 22 // Starting value on the expected duration of a frame. |
| 23 const int kStartingFrameDurationUs = 100000; |
| 24 |
| 25 // Target quantizer at which stop the encoding top-off. |
| 26 const int kTargetQuantizerForTopOff = 30; |
| 27 |
| 28 } // namespace |
| 29 |
| 16 // The frame scheduler currently uses a simple polling technique | 30 // The frame scheduler currently uses a simple polling technique |
| 17 // at 30 FPS to capture, encode and send frames over webrtc transport. | 31 // at 30 FPS to capture, encode and send frames over webrtc transport. |
| 18 // An improved solution will use target bitrate feedback to pace out | 32 // An improved solution will use target bitrate feedback to pace out |
| 19 // the capture rate. | 33 // the capture rate. |
| 20 WebRtcFrameScheduler::WebRtcFrameScheduler( | 34 WebRtcFrameScheduler::WebRtcFrameScheduler( |
| 21 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, | 35 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, |
| 22 std::unique_ptr<webrtc::DesktopCapturer> capturer, | 36 std::unique_ptr<webrtc::DesktopCapturer> capturer, |
| 23 WebrtcTransport* webrtc_transport, | 37 WebrtcTransport* webrtc_transport, |
| 24 std::unique_ptr<VideoEncoder> encoder) | 38 std::unique_ptr<VideoEncoder> encoder) |
| 25 : encode_task_runner_(encode_task_runner), | 39 : acc_frame_duration_( |
| 40 base::TimeDelta::FromMicroseconds(kAccFrameDurationHalfLifeUs)), |
| 41 encode_task_runner_(encode_task_runner), |
| 26 capturer_(std::move(capturer)), | 42 capturer_(std::move(capturer)), |
| 27 webrtc_transport_(webrtc_transport), | 43 webrtc_transport_(webrtc_transport), |
| 28 encoder_(std::move(encoder)), | 44 encoder_(std::move(encoder)), |
| 29 weak_factory_(this) { | 45 weak_factory_(this) { |
| 30 DCHECK(encode_task_runner_); | 46 DCHECK(encode_task_runner_); |
| 31 DCHECK(capturer_); | 47 DCHECK(capturer_); |
| 32 DCHECK(webrtc_transport_); | 48 DCHECK(webrtc_transport_); |
| 33 DCHECK(encoder_); | 49 DCHECK(encoder_); |
| 34 // Does not really start anything. Registers callback on this class. | 50 // Does not really start anything. Registers callback on this class. |
| 35 capturer_->Start(this); | 51 capturer_->Start(this); |
| 36 capture_timer_.reset(new base::RepeatingTimer()); | 52 capture_timer_.reset(new base::RepeatingTimer()); |
| 37 } | 53 } |
| 38 | 54 |
| 39 WebRtcFrameScheduler::~WebRtcFrameScheduler() { | 55 WebRtcFrameScheduler::~WebRtcFrameScheduler() { |
| 40 encode_task_runner_->DeleteSoon(FROM_HERE, encoder_.release()); | 56 encode_task_runner_->DeleteSoon(FROM_HERE, encoder_.release()); |
| 41 } | 57 } |
| 42 | 58 |
| 43 void WebRtcFrameScheduler::Start() { | 59 void WebRtcFrameScheduler::Start() { |
| 44 // Register for PLI requests. | 60 // Register for PLI requests. |
| 45 webrtc_transport_->video_encoder_factory()->SetKeyFrameRequestCallback( | 61 webrtc_transport_->video_encoder_factory()->SetKeyFrameRequestCallback( |
| 46 base::Bind(&WebRtcFrameScheduler::SetKeyFrameRequest, | 62 base::Bind(&WebRtcFrameScheduler::SetKeyFrameRequest, |
| 47 base::Unretained(this))); | 63 base::Unretained(this))); |
| 64 // Register for target bitrate notifications. |
| 65 webrtc_transport_->video_encoder_factory()->SetTargetBitrateCallback( |
| 66 base::Bind(&WebRtcFrameScheduler::SetTargetBitrate, |
| 67 base::Unretained(this))); |
| 48 capture_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1) / 30, this, | 68 capture_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1) / 30, this, |
| 49 &WebRtcFrameScheduler::CaptureNextFrame); | 69 &WebRtcFrameScheduler::CaptureNextFrame); |
| 50 } | 70 } |
| 51 | 71 |
| 52 void WebRtcFrameScheduler::Stop() { | 72 void WebRtcFrameScheduler::Stop() { |
| 53 // Clear PLI request callback. | 73 // Clear PLI request callback. |
| 54 webrtc_transport_->video_encoder_factory()->SetKeyFrameRequestCallback( | 74 webrtc_transport_->video_encoder_factory()->SetKeyFrameRequestCallback( |
| 55 base::Closure()); | 75 base::Closure()); |
| 76 webrtc_transport_->video_encoder_factory()->SetTargetBitrateCallback( |
| 77 TargetBitrateCallback()); |
| 56 // Cancel any pending encode. | 78 // Cancel any pending encode. |
| 57 task_tracker_.TryCancelAll(); | 79 task_tracker_.TryCancelAll(); |
| 58 capture_timer_->Stop(); | 80 capture_timer_->Stop(); |
| 59 } | 81 } |
| 60 | 82 |
| 61 void WebRtcFrameScheduler::Pause(bool pause) { | 83 void WebRtcFrameScheduler::Pause(bool pause) { |
| 62 if (pause) { | 84 if (pause) { |
| 63 Stop(); | 85 Stop(); |
| 64 } else { | 86 } else { |
| 65 Start(); | 87 Start(); |
| 66 } | 88 } |
| 67 } | 89 } |
| 68 | 90 |
| 69 void WebRtcFrameScheduler::SetSizeCallback( | 91 void WebRtcFrameScheduler::SetSizeCallback( |
| 70 const VideoStream::SizeCallback& callback) { | 92 const VideoStream::SizeCallback& callback) { |
| 71 size_callback_ = callback; | 93 size_callback_ = callback; |
| 72 } | 94 } |
| 73 | 95 |
| 74 void WebRtcFrameScheduler::SetKeyFrameRequest() { | 96 void WebRtcFrameScheduler::SetKeyFrameRequest() { |
| 75 VLOG(1) << "Request key frame"; | 97 VLOG(1) << "Request key frame"; |
| 76 base::AutoLock lock(lock_); | 98 base::AutoLock lock(lock_); |
| 77 key_frame_request_ = true; | 99 key_frame_request_ = true; |
| 78 } | 100 } |
| 79 | 101 |
| 102 void WebRtcFrameScheduler::SetTargetBitrate(uint32_t target_bitrate_kbps) { |
| 103 VLOG(1) << "Set Target bitrate " << target_bitrate_kbps; |
| 104 base::AutoLock lock(lock_); |
| 105 target_bitrate_kbps_ = target_bitrate_kbps; |
| 106 } |
| 107 |
| 80 bool WebRtcFrameScheduler::ClearAndGetKeyFrameRequest() { | 108 bool WebRtcFrameScheduler::ClearAndGetKeyFrameRequest() { |
| 81 base::AutoLock lock(lock_); | 109 base::AutoLock lock(lock_); |
| 82 bool key_frame_request = key_frame_request_; | 110 bool key_frame_request = key_frame_request_; |
| 83 key_frame_request_ = false; | 111 key_frame_request_ = false; |
| 84 return key_frame_request; | 112 return key_frame_request; |
| 85 } | 113 } |
| 86 | 114 |
| 87 webrtc::SharedMemory* WebRtcFrameScheduler::CreateSharedMemory(size_t size) { | 115 webrtc::SharedMemory* WebRtcFrameScheduler::CreateSharedMemory(size_t size) { |
| 88 DCHECK(thread_checker_.CalledOnValidThread()); | 116 DCHECK(thread_checker_.CalledOnValidThread()); |
| 89 return nullptr; | 117 return nullptr; |
| 90 } | 118 } |
| 91 | 119 |
| 92 void WebRtcFrameScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) { | 120 void WebRtcFrameScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) { |
| 93 DCHECK(thread_checker_.CalledOnValidThread()); | 121 DCHECK(thread_checker_.CalledOnValidThread()); |
| 94 | 122 |
| 123 base::TimeTicks captured_ticks = base::TimeTicks::Now(); |
| 95 VLOG(1) << "Capture overhead " | 124 VLOG(1) << "Capture overhead " |
| 96 << (base::TimeTicks::Now() - last_capture_ticks_).InMilliseconds(); | 125 << (captured_ticks - last_capture_started_ticks_).InMilliseconds(); |
| 97 capture_pending_ = false; | 126 capture_pending_ = false; |
| 98 | 127 |
| 99 std::unique_ptr<webrtc::DesktopFrame> owned_frame(frame); | 128 std::unique_ptr<webrtc::DesktopFrame> owned_frame(frame); |
| 100 | 129 |
| 101 if (encode_pending_) { | 130 if (encode_pending_) { |
| 102 // TODO(isheriff): consider queuing here | 131 // TODO(isheriff): consider queuing here |
| 103 VLOG(1) << "Dropping captured frame since encoder is still busy"; | 132 VLOG(1) << "Dropping captured frame since encoder is still busy"; |
| 104 return; | 133 return; |
| 105 } | 134 } |
| 106 if (!frame || frame->updated_region().is_empty()) | 135 |
| 136 // If unchanged and does not need top-off, return. |
| 137 if (!frame || (frame->updated_region().is_empty() && |
| 138 last_quantizer_ <= kTargetQuantizerForTopOff)) |
| 107 return; | 139 return; |
| 108 | 140 |
| 141 if ((last_capture_completed_ticks_ - base::TimeTicks()).is_zero()) { |
| 142 acc_frame_duration_.Reset(kStartingFrameDurationUs, captured_ticks); |
| 143 } else { |
| 144 base::TimeDelta duration = captured_ticks - last_capture_completed_ticks_; |
| 145 acc_frame_duration_.Update(duration.InMicroseconds(), captured_ticks); |
| 146 } |
| 147 last_capture_completed_ticks_ = captured_ticks; |
| 148 |
| 109 webrtc::DesktopVector dpi = | 149 webrtc::DesktopVector dpi = |
| 110 frame->dpi().is_zero() ? webrtc::DesktopVector(kDefaultDpi, kDefaultDpi) | 150 frame->dpi().is_zero() ? webrtc::DesktopVector(kDefaultDpi, kDefaultDpi) |
| 111 : frame->dpi(); | 151 : frame->dpi(); |
| 112 | 152 |
| 113 if (!frame_size_.equals(frame->size()) || !frame_dpi_.equals(dpi)) { | 153 if (!frame_size_.equals(frame->size()) || !frame_dpi_.equals(dpi)) { |
| 114 frame_size_ = frame->size(); | 154 frame_size_ = frame->size(); |
| 115 frame_dpi_ = dpi; | 155 frame_dpi_ = dpi; |
| 116 if (!size_callback_.is_null()) | 156 if (!size_callback_.is_null()) |
| 117 size_callback_.Run(frame_size_, frame_dpi_); | 157 size_callback_.Run(frame_size_, frame_dpi_); |
| 118 } | 158 } |
| 119 encode_pending_ = true; | 159 encode_pending_ = true; |
| 120 task_tracker_.PostTaskAndReplyWithResult( | 160 task_tracker_.PostTaskAndReplyWithResult( |
| 121 encode_task_runner_.get(), FROM_HERE, | 161 encode_task_runner_.get(), FROM_HERE, |
| 122 base::Bind(&WebRtcFrameScheduler::EncodeFrame, encoder_.get(), | 162 base::Bind(&WebRtcFrameScheduler::EncodeFrame, encoder_.get(), |
| 123 base::Passed(std::move(owned_frame)), | 163 base::Passed(std::move(owned_frame)), target_bitrate_kbps_, |
| 164 static_cast<int64_t>(acc_frame_duration_.current()), |
| 124 ClearAndGetKeyFrameRequest()), | 165 ClearAndGetKeyFrameRequest()), |
| 125 base::Bind(&WebRtcFrameScheduler::OnFrameEncoded, | 166 base::Bind(&WebRtcFrameScheduler::OnFrameEncoded, |
| 126 weak_factory_.GetWeakPtr())); | 167 weak_factory_.GetWeakPtr())); |
| 127 } | 168 } |
| 128 | 169 |
| 129 void WebRtcFrameScheduler::CaptureNextFrame() { | 170 void WebRtcFrameScheduler::CaptureNextFrame() { |
| 130 DCHECK(thread_checker_.CalledOnValidThread()); | 171 DCHECK(thread_checker_.CalledOnValidThread()); |
| 131 | 172 |
| 132 if (capture_pending_ || encode_pending_) { | 173 if (capture_pending_ || encode_pending_) { |
| 133 VLOG(1) << "Capture/encode still pending.."; | 174 VLOG(1) << "Capture/encode still pending.."; |
| 134 return; | 175 return; |
| 135 } | 176 } |
| 136 capture_pending_ = true; | 177 capture_pending_ = true; |
| 137 VLOG(1) << "Capture next frame after " | 178 VLOG(1) << "Capture next frame after " |
| 138 << (base::TimeTicks::Now() - last_capture_ticks_).InMilliseconds(); | 179 << (base::TimeTicks::Now() - last_capture_started_ticks_) |
| 139 last_capture_ticks_ = base::TimeTicks::Now(); | 180 .InMilliseconds(); |
| 181 last_capture_started_ticks_ = base::TimeTicks::Now(); |
| 140 capturer_->Capture(webrtc::DesktopRegion()); | 182 capturer_->Capture(webrtc::DesktopRegion()); |
| 141 } | 183 } |
| 142 | 184 |
| 143 // static | 185 // static |
| 144 std::unique_ptr<VideoPacket> WebRtcFrameScheduler::EncodeFrame( | 186 std::unique_ptr<VideoPacket> WebRtcFrameScheduler::EncodeFrame( |
| 145 VideoEncoder* encoder, | 187 VideoEncoder* encoder, |
| 146 std::unique_ptr<webrtc::DesktopFrame> frame, | 188 std::unique_ptr<webrtc::DesktopFrame> frame, |
| 189 uint32_t target_bitrate_kbps, |
| 190 int64_t frame_duration_us, |
| 147 bool key_frame_request) { | 191 bool key_frame_request) { |
| 148 uint32_t flags = 0; | 192 uint32_t flags = 0; |
| 149 if (key_frame_request) | 193 if (key_frame_request) |
| 150 flags |= VideoEncoder::REQUEST_KEY_FRAME; | 194 flags |= VideoEncoder::REQUEST_KEY_FRAME; |
| 151 | 195 |
| 152 base::TimeTicks current = base::TimeTicks::Now(); | 196 base::TimeTicks current = base::TimeTicks::Now(); |
| 197 encoder->UpdateTargetBitrate(target_bitrate_kbps); |
| 153 std::unique_ptr<VideoPacket> packet = encoder->Encode(*frame, flags); | 198 std::unique_ptr<VideoPacket> packet = encoder->Encode(*frame, flags); |
| 154 if (!packet) | 199 if (!packet) |
| 155 return nullptr; | 200 return nullptr; |
| 156 | 201 |
| 157 VLOG(1) << "Encode duration " | 202 VLOG(1) << "Encode duration " |
| 158 << (base::TimeTicks::Now() - current).InMilliseconds() | 203 << (base::TimeTicks::Now() - current).InMilliseconds() |
| 159 << " payload size " << packet->data().size(); | 204 << " payload size " << packet->data().size() << " quantizer " |
| 205 << packet->quantizer(); |
| 160 return packet; | 206 return packet; |
| 161 } | 207 } |
| 162 | 208 |
| 163 void WebRtcFrameScheduler::OnFrameEncoded(std::unique_ptr<VideoPacket> packet) { | 209 void WebRtcFrameScheduler::OnFrameEncoded(std::unique_ptr<VideoPacket> packet) { |
| 164 DCHECK(thread_checker_.CalledOnValidThread()); | 210 DCHECK(thread_checker_.CalledOnValidThread()); |
| 165 encode_pending_ = false; | 211 encode_pending_ = false; |
| 166 if (!packet) | 212 if (!packet) |
| 167 return; | 213 return; |
| 214 last_quantizer_ = packet->quantizer(); |
| 168 int64_t capture_timestamp_ms = | 215 int64_t capture_timestamp_ms = |
| 169 (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds(); | 216 (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds(); |
| 170 base::TimeTicks current = base::TimeTicks::Now(); | 217 base::TimeTicks current = base::TimeTicks::Now(); |
| 218 uint32_t encoded_bits = packet->data().size() * 8.0; |
| 219 VLOG(1) << "frame duration " << acc_frame_duration_.current() |
| 220 << " encoded bitrate (kbps) " |
| 221 << (encoded_bits * 1000 / acc_frame_duration_.current()); |
| 171 // TODO(isheriff): Investigate why first frame fails to send at times. | 222 // TODO(isheriff): Investigate why first frame fails to send at times. |
| 172 // This gets resolved through a PLI request. | 223 // This gets resolved through a PLI request. |
| 173 webrtc_transport_->video_encoder_factory()->SendEncodedFrame( | 224 webrtc_transport_->video_encoder_factory()->SendEncodedFrame( |
| 174 capture_timestamp_ms, std::move(packet)); | 225 capture_timestamp_ms, std::move(packet)); |
| 175 | 226 |
| 227 // Simplistic adaptation of frame polling in the range 5 FPS to 30 FPS. |
| 228 uint32_t next_sched_ms = std::max( |
| 229 33, std::min(static_cast<int>(encoded_bits / target_bitrate_kbps_), 200)); |
| 176 VLOG(1) << "Send duration " | 230 VLOG(1) << "Send duration " |
| 177 << (base::TimeTicks::Now() - current).InMilliseconds(); | 231 << (base::TimeTicks::Now() - current).InMilliseconds() |
| 232 << "next sched " << next_sched_ms; |
| 233 capture_timer_->Start(FROM_HERE, |
| 234 base::TimeDelta::FromMilliseconds(next_sched_ms), this, |
| 235 &WebRtcFrameScheduler::CaptureNextFrame); |
| 178 } | 236 } |
| 179 | 237 |
| 180 } // namespace protocol | 238 } // namespace protocol |
| 181 } // namespace remoting | 239 } // namespace remoting |
| OLD | NEW |