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/util.h" 23 #include "remoting/protocol/video_stub.h" 24 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 25 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_shape.h" 26 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" 27 28 namespace remoting { 29 30 // Maximum number of frames that can be processed simultaneously. 31 // TODO(hclam): Move this value to CaptureScheduler. 32 static const int kMaxPendingFrames = 2; 33 34 VideoScheduler::VideoScheduler( 35 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, 36 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, 37 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, 38 scoped_ptr<webrtc::ScreenCapturer> capturer, 39 scoped_ptr<VideoEncoder> encoder, 40 protocol::CursorShapeStub* cursor_stub, 41 protocol::VideoStub* video_stub) 42 : capture_task_runner_(capture_task_runner), 43 encode_task_runner_(encode_task_runner), 44 network_task_runner_(network_task_runner), 45 capturer_(capturer.Pass()), 46 encoder_(encoder.Pass()), 47 cursor_stub_(cursor_stub), 48 video_stub_(video_stub), 49 pending_frames_(0), 50 capture_pending_(false), 51 did_skip_frame_(false), 52 is_paused_(false), 53 sequence_number_(0) { 54 DCHECK(network_task_runner_->BelongsToCurrentThread()); 55 DCHECK(capturer_); 56 DCHECK(encoder_); 57 DCHECK(cursor_stub_); 58 DCHECK(video_stub_); 59 } 60 61 // Public methods -------------------------------------------------------------- 62 63 webrtc::SharedMemory* VideoScheduler::CreateSharedMemory(size_t size) { 64 return NULL; 65 } 66 67 void VideoScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) { 68 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 69 70 capture_pending_ = false; 71 72 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); 73 74 if (frame) { 75 scheduler_.RecordCaptureTime( 76 base::TimeDelta::FromMilliseconds(frame->capture_time_ms())); 77 } 78 79 encode_task_runner_->PostTask( 80 FROM_HERE, base::Bind(&VideoScheduler::EncodeFrame, this, 81 base::Passed(&owned_frame), sequence_number_)); 82 83 // If a frame was skipped, try to capture it again. 84 if (did_skip_frame_) { 85 capture_task_runner_->PostTask( 86 FROM_HERE, base::Bind(&VideoScheduler::CaptureNextFrame, this)); 87 } 88 } 89 90 void VideoScheduler::OnCursorShapeChanged( 91 webrtc::MouseCursorShape* cursor_shape) { 92 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 93 94 scoped_ptr<webrtc::MouseCursorShape> owned_cursor(cursor_shape); 95 96 // Do nothing if the scheduler is being stopped. 97 if (!capturer_) 98 return; 99 100 scoped_ptr<protocol::CursorShapeInfo> cursor_proto( 101 new protocol::CursorShapeInfo()); 102 cursor_proto->set_width(cursor_shape->size.width()); 103 cursor_proto->set_height(cursor_shape->size.height()); 104 cursor_proto->set_hotspot_x(cursor_shape->hotspot.x()); 105 cursor_proto->set_hotspot_y(cursor_shape->hotspot.y()); 106 cursor_proto->set_data(cursor_shape->data); 107 108 network_task_runner_->PostTask( 109 FROM_HERE, base::Bind(&VideoScheduler::SendCursorShape, this, 110 base::Passed(&cursor_proto))); 111 } 112 113 void VideoScheduler::Start() { 114 DCHECK(network_task_runner_->BelongsToCurrentThread()); 115 116 capture_task_runner_->PostTask( 117 FROM_HERE, base::Bind(&VideoScheduler::StartOnCaptureThread, this)); 118 } 119 120 void VideoScheduler::Stop() { 121 DCHECK(network_task_runner_->BelongsToCurrentThread()); 122 123 // Clear stubs to prevent further updates reaching the client. 124 cursor_stub_ = NULL; 125 video_stub_ = NULL; 126 127 capture_task_runner_->PostTask(FROM_HERE, 128 base::Bind(&VideoScheduler::StopOnCaptureThread, this)); 129 } 130 131 void VideoScheduler::Pause(bool pause) { 132 if (!capture_task_runner_->BelongsToCurrentThread()) { 133 DCHECK(network_task_runner_->BelongsToCurrentThread()); 134 capture_task_runner_->PostTask( 135 FROM_HERE, base::Bind(&VideoScheduler::Pause, this, pause)); 136 return; 137 } 138 139 if (is_paused_ != pause) { 140 is_paused_ = pause; 141 142 // Restart captures if we're resuming and there are none scheduled. 143 if (!is_paused_ && capture_timer_ && !capture_timer_->IsRunning()) 144 CaptureNextFrame(); 145 } 146 } 147 148 void VideoScheduler::UpdateSequenceNumber(int64 sequence_number) { 149 if (!capture_task_runner_->BelongsToCurrentThread()) { 150 DCHECK(network_task_runner_->BelongsToCurrentThread()); 151 capture_task_runner_->PostTask( 152 FROM_HERE, base::Bind(&VideoScheduler::UpdateSequenceNumber, 153 this, sequence_number)); 154 return; 155 } 156 157 sequence_number_ = sequence_number; 158 } 159 160 // Private methods ----------------------------------------------------------- 161 162 VideoScheduler::~VideoScheduler() { 163 } 164 165 // Capturer thread ------------------------------------------------------------- 166 167 void VideoScheduler::StartOnCaptureThread() { 168 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 169 DCHECK(!capture_timer_); 170 171 // Start the capturer and let it notify us if cursor shape changes. 172 capturer_->SetMouseShapeObserver(this); 173 capturer_->Start(this); 174 175 capture_timer_.reset(new base::OneShotTimer<VideoScheduler>()); 176 177 // Capture first frame immedately. 178 CaptureNextFrame(); 179 } 180 181 void VideoScheduler::StopOnCaptureThread() { 182 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 183 184 // This doesn't deleted already captured frames, so encoder can keep using the 185 // frames that were captured previously. 186 capturer_.reset(); 187 188 // |capture_timer_| must be destroyed on the thread on which it is used. 189 capture_timer_.reset(); 190 } 191 192 void VideoScheduler::ScheduleNextCapture() { 193 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 194 195 capture_timer_->Start(FROM_HERE, 196 scheduler_.NextCaptureDelay(), 197 this, 198 &VideoScheduler::CaptureNextFrame); 199 } 200 201 void VideoScheduler::CaptureNextFrame() { 202 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 203 204 // If we are stopping (|capturer_| is NULL), or paused, then don't capture. 205 if (!capturer_ || is_paused_) 206 return; 207 208 // Make sure we have at most two outstanding recordings. We can simply return 209 // if we can't make a capture now, the next capture will be started by the 210 // end of an encode operation. 211 if (pending_frames_ >= kMaxPendingFrames || capture_pending_) { 212 did_skip_frame_ = true; 213 return; 214 } 215 216 did_skip_frame_ = false; 217 218 // At this point we are going to perform one capture so save the current time. 219 pending_frames_++; 220 DCHECK_LE(pending_frames_, kMaxPendingFrames); 221 222 // Before doing a capture schedule for the next one. 223 ScheduleNextCapture(); 224 225 capture_pending_ = true; 226 227 // And finally perform one capture. 228 capturer_->Capture(webrtc::DesktopRegion()); 229 } 230 231 void VideoScheduler::FrameCaptureCompleted() { 232 DCHECK(capture_task_runner_->BelongsToCurrentThread()); 233 234 // Decrement the pending capture count. 235 pending_frames_--; 236 DCHECK_GE(pending_frames_, 0); 237 238 // If we've skipped a frame capture because too we had too many captures 239 // pending then schedule one now. 240 if (did_skip_frame_) 241 CaptureNextFrame(); 242 } 243 244 // Network thread -------------------------------------------------------------- 245 246 void VideoScheduler::SendVideoPacket(scoped_ptr<VideoPacket> packet) { 247 DCHECK(network_task_runner_->BelongsToCurrentThread()); 248 249 if (!video_stub_) 250 return; 251 252 base::Closure callback; 253 if ((packet->flags() & VideoPacket::LAST_PARTITION) != 0) 254 callback = base::Bind(&VideoScheduler::VideoFrameSentCallback, this); 255 256 video_stub_->ProcessVideoPacket(packet.Pass(), callback); 257 } 258 259 void VideoScheduler::VideoFrameSentCallback() { 260 DCHECK(network_task_runner_->BelongsToCurrentThread()); 261 262 if (!video_stub_) 263 return; 264 265 capture_task_runner_->PostTask( 266 FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this)); 267 } 268 269 void VideoScheduler::SendCursorShape( 270 scoped_ptr<protocol::CursorShapeInfo> cursor_shape) { 271 DCHECK(network_task_runner_->BelongsToCurrentThread()); 272 273 if (!cursor_stub_) 274 return; 275 276 cursor_stub_->SetCursorShape(*cursor_shape); 277 } 278 279 // Encoder thread -------------------------------------------------------------- 280 281 void VideoScheduler::EncodeFrame( 282 scoped_ptr<webrtc::DesktopFrame> frame, 283 int64 sequence_number) { 284 DCHECK(encode_task_runner_->BelongsToCurrentThread()); 285 286 // If there is nothing to encode then send an empty keep-alive packet. 287 if (!frame || frame->updated_region().is_empty()) { 288 scoped_ptr<VideoPacket> packet(new VideoPacket()); 289 packet->set_flags(VideoPacket::LAST_PARTITION); 290 packet->set_client_sequence_number(sequence_number); 291 network_task_runner_->PostTask( 292 FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, 293 base::Passed(&packet))); 294 capture_task_runner_->DeleteSoon(FROM_HERE, frame.release()); 295 return; 296 } 297 298 encoder_->Encode( 299 frame.get(), base::Bind(&VideoScheduler::EncodedDataAvailableCallback, 300 this, sequence_number)); 301 capture_task_runner_->DeleteSoon(FROM_HERE, frame.release()); 302 } 303 304 void VideoScheduler::EncodedDataAvailableCallback( 305 int64 sequence_number, 306 scoped_ptr<VideoPacket> packet) { 307 DCHECK(encode_task_runner_->BelongsToCurrentThread()); 308 309 packet->set_client_sequence_number(sequence_number); 310 311 bool last = (packet->flags() & VideoPacket::LAST_PACKET) != 0; 312 if (last) { 313 scheduler_.RecordEncodeTime( 314 base::TimeDelta::FromMilliseconds(packet->encode_time_ms())); 315 } 316 317 network_task_runner_->PostTask( 318 FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, 319 base::Passed(&packet))); 320 } 321 322 } // namespace remoting 323