Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/host/video_scheduler.h" | 5 #include "remoting/host/video_scheduler.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/callback.h" | 10 #include "base/callback.h" |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | 23 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
| 24 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_shape.h" | 24 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_shape.h" |
| 25 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" | 25 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" |
| 26 | 26 |
| 27 namespace remoting { | 27 namespace remoting { |
| 28 | 28 |
| 29 // Maximum number of frames that can be processed simultaneously. | 29 // Maximum number of frames that can be processed simultaneously. |
| 30 // TODO(hclam): Move this value to CaptureScheduler. | 30 // TODO(hclam): Move this value to CaptureScheduler. |
| 31 static const int kMaxPendingFrames = 2; | 31 static const int kMaxPendingFrames = 2; |
| 32 | 32 |
| 33 // Interval between empty keep-alive frames. These frames are sent only | |
| 34 // when there are no real video frame being sent. | |
|
Wez
2014/05/24 05:57:08
typo: frame -> frames
Sergey Ulanov
2014/05/24 09:06:28
Done.
| |
| 35 static const int kKeepAlivePacketIntervalMs = 500; | |
| 36 | |
| 33 VideoScheduler::VideoScheduler( | 37 VideoScheduler::VideoScheduler( |
| 34 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, | 38 scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, |
| 35 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, | 39 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, |
| 36 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, | 40 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, |
| 37 scoped_ptr<webrtc::ScreenCapturer> capturer, | 41 scoped_ptr<webrtc::ScreenCapturer> capturer, |
| 38 scoped_ptr<VideoEncoder> encoder, | 42 scoped_ptr<VideoEncoder> encoder, |
| 39 protocol::CursorShapeStub* cursor_stub, | 43 protocol::CursorShapeStub* cursor_stub, |
| 40 protocol::VideoStub* video_stub) | 44 protocol::VideoStub* video_stub) |
| 41 : capture_task_runner_(capture_task_runner), | 45 : capture_task_runner_(capture_task_runner), |
| 42 encode_task_runner_(encode_task_runner), | 46 encode_task_runner_(encode_task_runner), |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 61 | 65 |
| 62 webrtc::SharedMemory* VideoScheduler::CreateSharedMemory(size_t size) { | 66 webrtc::SharedMemory* VideoScheduler::CreateSharedMemory(size_t size) { |
| 63 return NULL; | 67 return NULL; |
| 64 } | 68 } |
| 65 | 69 |
| 66 void VideoScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) { | 70 void VideoScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) { |
| 67 DCHECK(capture_task_runner_->BelongsToCurrentThread()); | 71 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 68 | 72 |
| 69 capture_pending_ = false; | 73 capture_pending_ = false; |
| 70 | 74 |
| 75 if (!frame) { | |
| 76 LOG(ERROR) << "Capture failed."; | |
| 77 return; | |
| 78 } | |
| 79 | |
| 71 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); | 80 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); |
| 72 | 81 |
| 73 if (frame) { | 82 scheduler_.RecordCaptureTime( |
| 74 scheduler_.RecordCaptureTime( | 83 base::TimeDelta::FromMilliseconds(frame->capture_time_ms())); |
| 75 base::TimeDelta::FromMilliseconds(frame->capture_time_ms())); | 84 |
| 85 // Encode and send only non-empty frames. | |
| 86 if (!frame->updated_region().is_empty()) { | |
| 87 encode_task_runner_->PostTask( | |
| 88 FROM_HERE, base::Bind(&VideoScheduler::EncodeFrame, this, | |
| 89 base::Passed(&owned_frame), sequence_number_)); | |
| 76 } | 90 } |
| 77 | 91 |
| 78 encode_task_runner_->PostTask( | |
| 79 FROM_HERE, base::Bind(&VideoScheduler::EncodeFrame, this, | |
| 80 base::Passed(&owned_frame), sequence_number_)); | |
| 81 | |
| 82 // If a frame was skipped, try to capture it again. | 92 // If a frame was skipped, try to capture it again. |
| 83 if (did_skip_frame_) { | 93 if (did_skip_frame_) { |
| 84 capture_task_runner_->PostTask( | 94 capture_task_runner_->PostTask( |
| 85 FROM_HERE, base::Bind(&VideoScheduler::CaptureNextFrame, this)); | 95 FROM_HERE, base::Bind(&VideoScheduler::CaptureNextFrame, this)); |
| 86 } | 96 } |
| 87 } | 97 } |
| 88 | 98 |
| 89 void VideoScheduler::OnCursorShapeChanged( | 99 void VideoScheduler::OnCursorShapeChanged( |
| 90 webrtc::MouseCursorShape* cursor_shape) { | 100 webrtc::MouseCursorShape* cursor_shape) { |
| 91 DCHECK(capture_task_runner_->BelongsToCurrentThread()); | 101 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 116 FROM_HERE, base::Bind(&VideoScheduler::StartOnCaptureThread, this)); | 126 FROM_HERE, base::Bind(&VideoScheduler::StartOnCaptureThread, this)); |
| 117 } | 127 } |
| 118 | 128 |
| 119 void VideoScheduler::Stop() { | 129 void VideoScheduler::Stop() { |
| 120 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 130 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| 121 | 131 |
| 122 // Clear stubs to prevent further updates reaching the client. | 132 // Clear stubs to prevent further updates reaching the client. |
| 123 cursor_stub_ = NULL; | 133 cursor_stub_ = NULL; |
| 124 video_stub_ = NULL; | 134 video_stub_ = NULL; |
| 125 | 135 |
| 126 capture_task_runner_->PostTask(FROM_HERE, | 136 keep_alive_timer_.reset(); |
| 127 base::Bind(&VideoScheduler::StopOnCaptureThread, this)); | 137 |
| 138 capture_task_runner_->PostTask( | |
| 139 FROM_HERE, base::Bind(&VideoScheduler::StopOnCaptureThread, this)); | |
| 128 } | 140 } |
| 129 | 141 |
| 130 void VideoScheduler::Pause(bool pause) { | 142 void VideoScheduler::Pause(bool pause) { |
| 131 if (!capture_task_runner_->BelongsToCurrentThread()) { | 143 if (!capture_task_runner_->BelongsToCurrentThread()) { |
| 132 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 144 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| 133 capture_task_runner_->PostTask( | 145 capture_task_runner_->PostTask( |
| 134 FROM_HERE, base::Bind(&VideoScheduler::Pause, this, pause)); | 146 FROM_HERE, base::Bind(&VideoScheduler::Pause, this, pause)); |
| 135 return; | 147 return; |
| 136 } | 148 } |
| 137 | 149 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 165 | 177 |
| 166 void VideoScheduler::StartOnCaptureThread() { | 178 void VideoScheduler::StartOnCaptureThread() { |
| 167 DCHECK(capture_task_runner_->BelongsToCurrentThread()); | 179 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 168 DCHECK(!capture_timer_); | 180 DCHECK(!capture_timer_); |
| 169 | 181 |
| 170 // Start the capturer and let it notify us if cursor shape changes. | 182 // Start the capturer and let it notify us if cursor shape changes. |
| 171 capturer_->SetMouseShapeObserver(this); | 183 capturer_->SetMouseShapeObserver(this); |
| 172 capturer_->Start(this); | 184 capturer_->Start(this); |
| 173 | 185 |
| 174 capture_timer_.reset(new base::OneShotTimer<VideoScheduler>()); | 186 capture_timer_.reset(new base::OneShotTimer<VideoScheduler>()); |
| 187 keep_alive_timer_.reset(new base::DelayTimer<VideoScheduler>( | |
| 188 FROM_HERE, base::TimeDelta::FromMilliseconds(kKeepAlivePacketIntervalMs), | |
| 189 this, &VideoScheduler::SendKeepAlivePacket)); | |
| 175 | 190 |
| 176 // Capture first frame immedately. | 191 // Capture first frame immedately. |
| 177 CaptureNextFrame(); | 192 CaptureNextFrame(); |
| 178 } | 193 } |
| 179 | 194 |
| 180 void VideoScheduler::StopOnCaptureThread() { | 195 void VideoScheduler::StopOnCaptureThread() { |
| 181 DCHECK(capture_task_runner_->BelongsToCurrentThread()); | 196 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 182 | 197 |
| 183 // This doesn't deleted already captured frames, so encoder can keep using the | 198 // This doesn't deleted already captured frames, so encoder can keep using the |
| 184 // frames that were captured previously. | 199 // frames that were captured previously. |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 | 257 |
| 243 // Network thread -------------------------------------------------------------- | 258 // Network thread -------------------------------------------------------------- |
| 244 | 259 |
| 245 void VideoScheduler::SendVideoPacket(scoped_ptr<VideoPacket> packet) { | 260 void VideoScheduler::SendVideoPacket(scoped_ptr<VideoPacket> packet) { |
| 246 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 261 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| 247 | 262 |
| 248 if (!video_stub_) | 263 if (!video_stub_) |
| 249 return; | 264 return; |
| 250 | 265 |
| 251 video_stub_->ProcessVideoPacket( | 266 video_stub_->ProcessVideoPacket( |
| 252 packet.Pass(), base::Bind(&VideoScheduler::VideoFrameSentCallback, this)); | 267 packet.Pass(), base::Bind(&VideoScheduler::OnVideoPacketSent, this)); |
| 253 } | 268 } |
| 254 | 269 |
| 255 void VideoScheduler::VideoFrameSentCallback() { | 270 void VideoScheduler::OnVideoPacketSent() { |
| 256 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 271 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| 257 | 272 |
| 258 if (!video_stub_) | 273 if (!video_stub_) |
| 259 return; | 274 return; |
| 260 | 275 |
| 276 keep_alive_timer_->Reset(); | |
| 277 | |
| 261 capture_task_runner_->PostTask( | 278 capture_task_runner_->PostTask( |
| 262 FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this)); | 279 FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this)); |
| 263 } | 280 } |
| 264 | 281 |
| 282 void VideoScheduler::SendKeepAlivePacket() { | |
| 283 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 284 | |
| 285 if (!video_stub_) | |
| 286 return; | |
| 287 | |
| 288 video_stub_->ProcessVideoPacket( | |
| 289 scoped_ptr<VideoPacket>(new VideoPacket()), | |
| 290 base::Bind(&VideoScheduler::OnKeepAlivePacketSent, this)); | |
| 291 } | |
| 292 | |
| 293 void VideoScheduler::OnKeepAlivePacketSent() { | |
| 294 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
| 295 | |
| 296 if (keep_alive_timer_) | |
| 297 keep_alive_timer_->Reset(); | |
| 298 } | |
| 299 | |
| 265 void VideoScheduler::SendCursorShape( | 300 void VideoScheduler::SendCursorShape( |
| 266 scoped_ptr<protocol::CursorShapeInfo> cursor_shape) { | 301 scoped_ptr<protocol::CursorShapeInfo> cursor_shape) { |
| 267 DCHECK(network_task_runner_->BelongsToCurrentThread()); | 302 DCHECK(network_task_runner_->BelongsToCurrentThread()); |
| 268 | 303 |
| 269 if (!cursor_stub_) | 304 if (!cursor_stub_) |
| 270 return; | 305 return; |
| 271 | 306 |
| 272 cursor_stub_->SetCursorShape(*cursor_shape); | 307 cursor_stub_->SetCursorShape(*cursor_shape); |
| 273 } | 308 } |
| 274 | 309 |
| 275 // Encoder thread -------------------------------------------------------------- | 310 // Encoder thread -------------------------------------------------------------- |
| 276 | 311 |
| 277 void VideoScheduler::EncodeFrame( | 312 void VideoScheduler::EncodeFrame( |
| 278 scoped_ptr<webrtc::DesktopFrame> frame, | 313 scoped_ptr<webrtc::DesktopFrame> frame, |
| 279 int64 sequence_number) { | 314 int64 sequence_number) { |
| 280 DCHECK(encode_task_runner_->BelongsToCurrentThread()); | 315 DCHECK(encode_task_runner_->BelongsToCurrentThread()); |
| 281 | 316 DCHECK(!frame->updated_region().is_empty()); |
| 282 // If there is nothing to encode then send an empty keep-alive packet. | |
| 283 if (!frame || frame->updated_region().is_empty()) { | |
| 284 scoped_ptr<VideoPacket> packet(new VideoPacket()); | |
| 285 packet->set_client_sequence_number(sequence_number); | |
| 286 network_task_runner_->PostTask( | |
| 287 FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, | |
| 288 base::Passed(&packet))); | |
| 289 capture_task_runner_->DeleteSoon(FROM_HERE, frame.release()); | |
| 290 return; | |
| 291 } | |
| 292 | 317 |
| 293 scoped_ptr<VideoPacket> packet = encoder_->Encode(*frame); | 318 scoped_ptr<VideoPacket> packet = encoder_->Encode(*frame); |
| 294 packet->set_client_sequence_number(sequence_number); | 319 packet->set_client_sequence_number(sequence_number); |
| 295 | 320 |
| 296 // Destroy the frame before sending |packet| because SendVideoPacket() may | 321 // Destroy the frame before sending |packet| because SendVideoPacket() may |
| 297 // trigger another frame to be captured, and the screen capturer expects the | 322 // trigger another frame to be captured, and the screen capturer expects the |
| 298 // old frame to be freed by then. | 323 // old frame to be freed by then. |
| 299 frame.reset(); | 324 frame.reset(); |
| 300 | 325 |
| 301 scheduler_.RecordEncodeTime( | 326 scheduler_.RecordEncodeTime( |
| 302 base::TimeDelta::FromMilliseconds(packet->encode_time_ms())); | 327 base::TimeDelta::FromMilliseconds(packet->encode_time_ms())); |
| 303 network_task_runner_->PostTask( | 328 network_task_runner_->PostTask( |
| 304 FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, | 329 FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, |
| 305 base::Passed(&packet))); | 330 base::Passed(&packet))); |
| 306 } | 331 } |
| 307 | 332 |
| 308 } // namespace remoting | 333 } // namespace remoting |
| OLD | NEW |