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/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