1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "remoting/host/video_scheduler.h" 6 7 #include <algorithm> 8 9 #include "base/bind.h" 10 #include "base/callback.h" 11 #include "base/logging.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/message_loop/message_loop_proxy.h" 14 #include "base/stl_util.h" 15 #include "base/sys_info.h" 16 #include "base/time/time.h" 17 #include "remoting/proto/control.pb.h" 18 #include "remoting/proto/internal.pb.h" 19 #include "remoting/proto/video.pb.h" 20 #include "remoting/protocol/cursor_shape_stub.h" 21 #include "remoting/protocol/message_decoder.h" 22 #include "remoting/protocol/video_stub.h" 23 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 24 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_shape.h" 25 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" 26 27 namespace remoting { 28 29 // Maximum number of frames that can be processed simultaneously. 30 // TODO(hclam): Move this value to CaptureScheduler. 31 static const int kMaxPendingFrames = 2; 32 33 // Interval between empty keep-alive frames. These frames are sent only 34 // when there are no real video frames being sent. To prevent PseudoTCP from 35 // resetting congestion window this value must be smaller that then the minimum 36 // RTO used in PseudoTCP, which is 250ms. 37 static const int kKeepAlivePacketIntervalMs = 200; 38 39 VideoScheduler::VideoScheduler( 40 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, 41 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, 42 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, 43 scoped_ptr<webrtc::ScreenCapturer> capturer, 44 scoped_ptr<VideoEncoder> encoder, 45 protocol::CursorShapeStub* cursor_stub, 46 protocol::VideoStub* video_stub) 47 : capture_task_runner_(capture_task_runner), 48 encode_task_runner_(encode_task_runner), 49 network_task_runner_(network_task_runner), 50 capturer_(capturer.Pass()), 51 encoder_(encoder.Pass()), 52 cursor_stub_(cursor_stub), 53 video_stub_(video_stub), 54 pending_frames_(0), 55 capture_pending_(false), 56 did_skip_frame_(false), 57 is_paused_(false), 58 sequence_number_(0) { 59 DCHECK(network_task_runner_->BelongsToCurrentThread()); 60 DCHECK(capturer_); 61 DCHECK(encoder_); 62 DCHECK(cursor_stub_); 63 DCHECK(video_stub_); 64 } 65 66 // Public methods -------------------------------------------------------------- 67 68 webrtc::SharedMemory* VideoScheduler::CreateSharedMemory(size_t size) { 69 return NULL; 70 } 71 72 void VideoScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) { 73 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 74 75 capture_pending_ = false; 76 77 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); 78 79 if (owned_frame) { 80 scheduler_.RecordCaptureTime( 81 base::TimeDelta::FromMilliseconds(owned_frame->capture_time_ms())); 82 } 83 84 // Even when |frame| is NULL we still need to post it to the encode thread 85 // to make sure frames are freed in the same order they are received and 86 // that we don't start capturing frame n+2 before frame n is freed. 87 encode_task_runner_->PostTask( 88 FROM_HERE, base::Bind(&VideoScheduler::EncodeFrame, this, 89 base::Passed(&owned_frame), sequence_number_)); 90 91 // If a frame was skipped, try to capture it again. 92 if (did_skip_frame_) { 93 capture_task_runner_->PostTask( 94 FROM_HERE, base::Bind(&VideoScheduler::CaptureNextFrame, this)); 95 } 96 } 97 98 void VideoScheduler::OnCursorShapeChanged( 99 webrtc::MouseCursorShape* cursor_shape) { 100 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 101 102 scoped_ptr<webrtc::MouseCursorShape> owned_cursor(cursor_shape); 103 104 // Do nothing if the scheduler is being stopped. 105 if (!capturer_) 106 return; 107 108 scoped_ptr<protocol::CursorShapeInfo> cursor_proto( 109 new protocol::CursorShapeInfo()); 110 cursor_proto->set_width(cursor_shape->size.width()); 111 cursor_proto->set_height(cursor_shape->size.height()); 112 cursor_proto->set_hotspot_x(cursor_shape->hotspot.x()); 113 cursor_proto->set_hotspot_y(cursor_shape->hotspot.y()); 114 cursor_proto->set_data(cursor_shape->data); 115 116 network_task_runner_->PostTask( 117 FROM_HERE, base::Bind(&VideoScheduler::SendCursorShape, this, 118 base::Passed(&cursor_proto))); 119 } 120 121 void VideoScheduler::Start() { 122 DCHECK(network_task_runner_->BelongsToCurrentThread()); 123 124 capture_task_runner_->PostTask( 125 FROM_HERE, base::Bind(&VideoScheduler::StartOnCaptureThread, this)); 126 } 127 128 void VideoScheduler::Stop() { 129 DCHECK(network_task_runner_->BelongsToCurrentThread()); 130 131 // Clear stubs to prevent further updates reaching the client. 132 cursor_stub_ = NULL; 133 video_stub_ = NULL; 134 135 keep_alive_timer_.reset(); 136 137 capture_task_runner_->PostTask( 138 FROM_HERE, base::Bind(&VideoScheduler::StopOnCaptureThread, this)); 139 } 140 141 void VideoScheduler::Pause(bool pause) { 142 if (!capture_task_runner_->BelongsToCurrentThread()) { 143 DCHECK(network_task_runner_->BelongsToCurrentThread()); 144 capture_task_runner_->PostTask( 145 FROM_HERE, base::Bind(&VideoScheduler::Pause, this, pause)); 146 return; 147 } 148 149 if (is_paused_ != pause) { 150 is_paused_ = pause; 151 152 // Restart captures if we're resuming and there are none scheduled. 153 if (!is_paused_ && capture_timer_ && !capture_timer_->IsRunning()) 154 CaptureNextFrame(); 155 } 156 } 157 158 void VideoScheduler::UpdateSequenceNumber(int64 sequence_number) { 159 if (!capture_task_runner_->BelongsToCurrentThread()) { 160 DCHECK(network_task_runner_->BelongsToCurrentThread()); 161 capture_task_runner_->PostTask( 162 FROM_HERE, base::Bind(&VideoScheduler::UpdateSequenceNumber, 163 this, sequence_number)); 164 return; 165 } 166 167 sequence_number_ = sequence_number; 168 } 169 170 void VideoScheduler::SetLosslessEncode(bool want_lossless) { 171 if (!encode_task_runner_->BelongsToCurrentThread()) { 172 DCHECK(network_task_runner_->BelongsToCurrentThread()); 173 encode_task_runner_->PostTask( 174 FROM_HERE, base::Bind(&VideoScheduler::SetLosslessEncode, 175 this, want_lossless)); 176 return; 177 } 178 179 encoder_->SetLosslessEncode(want_lossless); 180 } 181 182 void VideoScheduler::SetLosslessColor(bool want_lossless) { 183 if (!encode_task_runner_->BelongsToCurrentThread()) { 184 DCHECK(network_task_runner_->BelongsToCurrentThread()); 185 encode_task_runner_->PostTask( 186 FROM_HERE, base::Bind(&VideoScheduler::SetLosslessColor, 187 this, want_lossless)); 188 return; 189 } 190 191 encoder_->SetLosslessColor(want_lossless); 192 } 193 194 // Private methods ----------------------------------------------------------- 195 196 VideoScheduler::~VideoScheduler() { 197 } 198 199 // Capturer thread ------------------------------------------------------------- 200 201 void VideoScheduler::StartOnCaptureThread() { 202 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 203 DCHECK(!capture_timer_); 204 205 // Start the capturer and let it notify us if cursor shape changes. 206 capturer_->SetMouseShapeObserver(this); 207 capturer_->Start(this); 208 209 capture_timer_.reset(new base::OneShotTimer<VideoScheduler>()); 210 keep_alive_timer_.reset(new base::DelayTimer<VideoScheduler>( 211 FROM_HERE, base::TimeDelta::FromMilliseconds(kKeepAlivePacketIntervalMs), 212 this, &VideoScheduler::SendKeepAlivePacket)); 213 214 // Capture first frame immedately. 215 CaptureNextFrame(); 216 } 217 218 void VideoScheduler::StopOnCaptureThread() { 219 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 220 221 // This doesn't deleted already captured frames, so encoder can keep using the 222 // frames that were captured previously. 223 capturer_.reset(); 224 225 // |capture_timer_| must be destroyed on the thread on which it is used. 226 capture_timer_.reset(); 227 } 228 229 void VideoScheduler::ScheduleNextCapture() { 230 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 231 232 capture_timer_->Start(FROM_HERE, 233 scheduler_.NextCaptureDelay(), 234 this, 235 &VideoScheduler::CaptureNextFrame); 236 } 237 238 void VideoScheduler::CaptureNextFrame() { 239 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 240 241 // If we are stopping (|capturer_| is NULL), or paused, then don't capture. 242 if (!capturer_ || is_paused_) 243 return; 244 245 // Make sure we have at most two outstanding recordings. We can simply return 246 // if we can't make a capture now, the next capture will be started by the 247 // end of an encode operation. 248 if (pending_frames_ >= kMaxPendingFrames || capture_pending_) { 249 did_skip_frame_ = true; 250 return; 251 } 252 253 did_skip_frame_ = false; 254 255 // At this point we are going to perform one capture so save the current time. 256 pending_frames_++; 257 DCHECK_LE(pending_frames_, kMaxPendingFrames); 258 259 // Before doing a capture schedule for the next one. 260 ScheduleNextCapture(); 261 262 capture_pending_ = true; 263 264 // And finally perform one capture. 265 capturer_->Capture(webrtc::DesktopRegion()); 266 } 267 268 void VideoScheduler::FrameCaptureCompleted() { 269 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 270 271 // Decrement the pending capture count. 272 pending_frames_--; 273 DCHECK_GE(pending_frames_, 0); 274 275 // If we've skipped a frame capture because too we had too many captures 276 // pending then schedule one now. 277 if (did_skip_frame_) 278 CaptureNextFrame(); 279 } 280 281 // Network thread -------------------------------------------------------------- 282 283 void VideoScheduler::SendVideoPacket(scoped_ptr<VideoPacket> packet) { 284 DCHECK(network_task_runner_->BelongsToCurrentThread()); 285 286 if (!video_stub_) 287 return; 288 289 video_stub_->ProcessVideoPacket( 290 packet.Pass(), base::Bind(&VideoScheduler::OnVideoPacketSent, this)); 291 } 292 293 void VideoScheduler::OnVideoPacketSent() { 294 DCHECK(network_task_runner_->BelongsToCurrentThread()); 295 296 if (!video_stub_) 297 return; 298 299 keep_alive_timer_->Reset(); 300 301 capture_task_runner_->PostTask( 302 FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this)); 303 } 304 305 void VideoScheduler::SendKeepAlivePacket() { 306 DCHECK(network_task_runner_->BelongsToCurrentThread()); 307 308 if (!video_stub_) 309 return; 310 311 video_stub_->ProcessVideoPacket( 312 scoped_ptr<VideoPacket>(new VideoPacket()), 313 base::Bind(&VideoScheduler::OnKeepAlivePacketSent, this)); 314 } 315 316 void VideoScheduler::OnKeepAlivePacketSent() { 317 DCHECK(network_task_runner_->BelongsToCurrentThread()); 318 319 if (keep_alive_timer_) 320 keep_alive_timer_->Reset(); 321 } 322 323 void VideoScheduler::SendCursorShape( 324 scoped_ptr<protocol::CursorShapeInfo> cursor_shape) { 325 DCHECK(network_task_runner_->BelongsToCurrentThread()); 326 327 if (!cursor_stub_) 328 return; 329 330 cursor_stub_->SetCursorShape(*cursor_shape); 331 } 332 333 // Encoder thread -------------------------------------------------------------- 334 335 void VideoScheduler::EncodeFrame( 336 scoped_ptr<webrtc::DesktopFrame> frame, 337 int64 sequence_number) { 338 DCHECK(encode_task_runner_->BelongsToCurrentThread()); 339 340 // Drop the frame if there were no changes. 341 if (!frame || frame->updated_region().is_empty()) { 342 capture_task_runner_->DeleteSoon(FROM_HERE, frame.release()); 343 capture_task_runner_->PostTask( 344 FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this)); 345 return; 346 } 347 348 scoped_ptr<VideoPacket> packet = encoder_->Encode(*frame); 349 packet->set_client_sequence_number(sequence_number); 350 351 // Destroy the frame before sending |packet| because SendVideoPacket() may 352 // trigger another frame to be captured, and the screen capturer expects the 353 // old frame to be freed by then. 354 frame.reset(); 355 356 scheduler_.RecordEncodeTime( 357 base::TimeDelta::FromMilliseconds(packet->encode_time_ms())); 358 network_task_runner_->PostTask( 359 FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, 360 base::Passed(&packet))); 361 } 362 363 } // namespace remoting 364