Home | History | Annotate | Download | only in host
      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