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 LOG(ERROR) << "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 |
95 VLOG(1) << "Capture overhead " | 123 base::TimeTicks captured_ticks = base::TimeTicks::Now(); |
96 << (base::TimeTicks::Now() - last_capture_ticks_).InMilliseconds(); | 124 int64_t capture_timestamp_ms = |
| 125 (captured_ticks - base::TimeTicks()).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_, |
124 ClearAndGetKeyFrameRequest()), | 164 static_cast<int64_t>(acc_frame_duration_.current()), |
| 165 ClearAndGetKeyFrameRequest(), capture_timestamp_ms), |
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, |
147 bool key_frame_request) { | 189 uint32_t target_bitrate_kbps, |
| 190 int64_t frame_duration_us, |
| 191 bool key_frame_request, |
| 192 int64_t capture_time_ms) { |
148 uint32_t flags = 0; | 193 uint32_t flags = 0; |
149 if (key_frame_request) | 194 if (key_frame_request) |
150 flags |= VideoEncoder::REQUEST_KEY_FRAME; | 195 flags |= VideoEncoder::REQUEST_KEY_FRAME; |
151 | 196 |
152 base::TimeTicks current = base::TimeTicks::Now(); | 197 base::TimeTicks current = base::TimeTicks::Now(); |
| 198 encoder->UpdateTargetBitrate(target_bitrate_kbps); |
153 std::unique_ptr<VideoPacket> packet = encoder->Encode(*frame, flags); | 199 std::unique_ptr<VideoPacket> packet = encoder->Encode(*frame, flags); |
154 if (!packet) | 200 if (!packet) |
155 return nullptr; | 201 return nullptr; |
| 202 packet->set_capture_time_ms(capture_time_ms); |
156 | 203 |
157 VLOG(1) << "Encode duration " | 204 VLOG(1) << "Encode duration " |
158 << (base::TimeTicks::Now() - current).InMilliseconds() | 205 << (base::TimeTicks::Now() - current).InMilliseconds() |
159 << " payload size " << packet->data().size(); | 206 << " payload size " << packet->data().size() << " quantizer " |
| 207 << packet->quantizer(); |
160 return packet; | 208 return packet; |
161 } | 209 } |
162 | 210 |
163 void WebRtcFrameScheduler::OnFrameEncoded(std::unique_ptr<VideoPacket> packet) { | 211 void WebRtcFrameScheduler::OnFrameEncoded(std::unique_ptr<VideoPacket> packet) { |
164 DCHECK(thread_checker_.CalledOnValidThread()); | 212 DCHECK(thread_checker_.CalledOnValidThread()); |
165 encode_pending_ = false; | 213 encode_pending_ = false; |
166 if (!packet) | 214 if (!packet) |
167 return; | 215 return; |
168 int64_t capture_timestamp_ms = | 216 last_quantizer_ = packet->quantizer(); |
169 (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()); |
| 222 |
| 223 // Simplistic adaptation of frame polling in the range 5 FPS to 30 FPS. |
| 224 uint32_t next_sched_ms = std::max( |
| 225 33, std::min(static_cast<int>(encoded_bits / target_bitrate_kbps_), 200)); |
171 // TODO(isheriff): Investigate why first frame fails to send at times. | 226 // TODO(isheriff): Investigate why first frame fails to send at times. |
172 // This gets resolved through a PLI request. | 227 // This gets resolved through a PLI request. |
173 webrtc_transport_->video_encoder_factory()->SendEncodedFrame( | 228 if (webrtc_transport_->video_encoder_factory()->SendEncodedFrame( |
174 capture_timestamp_ms, std::move(packet)); | 229 std::move(packet)) >= 0) { |
175 | 230 VLOG(1) << " Send duration " |
176 VLOG(1) << "Send duration " | 231 << (base::TimeTicks::Now() - current).InMilliseconds() |
177 << (base::TimeTicks::Now() - current).InMilliseconds(); | 232 << "next sched " << next_sched_ms; |
| 233 } else { |
| 234 LOG(ERROR) << "SendEncodedFrame() failed"; |
| 235 } |
| 236 capture_timer_->Start(FROM_HERE, |
| 237 base::TimeDelta::FromMilliseconds(next_sched_ms), this, |
| 238 &WebRtcFrameScheduler::CaptureNextFrame); |
178 } | 239 } |
179 | 240 |
180 } // namespace protocol | 241 } // namespace protocol |
181 } // namespace remoting | 242 } // namespace remoting |
OLD | NEW |