Home | History | Annotate | Download | only in host
      1 // Copyright 2014 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/cast_video_capturer_adapter.h"
      6 
      7 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
      8 
      9 namespace remoting {
     10 
     11 // Number of frames to be captured per second.
     12 const int kFramesPerSec = 10;
     13 
     14 CastVideoCapturerAdapter::CastVideoCapturerAdapter(
     15     scoped_ptr<webrtc::DesktopCapturer> capturer)
     16     : desktop_capturer_(capturer.Pass()) {
     17   DCHECK(desktop_capturer_);
     18 
     19   thread_checker_.DetachFromThread();
     20 
     21   // Disable video adaptation since we don't intend to use it.
     22   set_enable_video_adapter(false);
     23 }
     24 
     25 CastVideoCapturerAdapter::~CastVideoCapturerAdapter() {
     26   DCHECK(!capture_timer_);
     27 }
     28 
     29 webrtc::SharedMemory* CastVideoCapturerAdapter::CreateSharedMemory(
     30     size_t size) {
     31   return NULL;
     32 }
     33 
     34 void CastVideoCapturerAdapter::OnCaptureCompleted(webrtc::DesktopFrame* frame) {
     35   scoped_ptr<webrtc::DesktopFrame> owned_frame(frame);
     36 
     37   // Drop the owned_frame if there were no changes.
     38   if (!owned_frame || owned_frame->updated_region().is_empty()) {
     39     owned_frame.reset();
     40     return;
     41   }
     42 
     43   // Convert the webrtc::DesktopFrame to a cricket::CapturedFrame.
     44   cricket::CapturedFrame captured_frame;
     45   captured_frame.width = owned_frame->size().width();
     46   captured_frame.height = owned_frame->size().height();
     47   base::TimeTicks current_time = base::TimeTicks::Now();
     48   captured_frame.elapsed_time = (current_time - start_time_).InMicroseconds() *
     49                                 base::Time::kNanosecondsPerMicrosecond;
     50   captured_frame.time_stamp =
     51       current_time.ToInternalValue() * base::Time::kNanosecondsPerMicrosecond;
     52   captured_frame.data = owned_frame->data();
     53 
     54   // The data_size attribute must be set. If multiple formats are supported,
     55   // this should be set appropriately for each one.
     56   captured_frame.data_size =
     57       (captured_frame.width * webrtc::DesktopFrame::kBytesPerPixel * 8 + 7) /
     58       8 * captured_frame.height;
     59   captured_frame.fourcc = cricket::FOURCC_ARGB;
     60 
     61   SignalFrameCaptured(this, &captured_frame);
     62 }
     63 
     64 bool CastVideoCapturerAdapter::GetBestCaptureFormat(
     65     const cricket::VideoFormat& desired,
     66     cricket::VideoFormat* best_format) {
     67   DCHECK(thread_checker_.CalledOnValidThread());
     68 
     69   // For now, just used the desired width and height.
     70   best_format->width = desired.width;
     71   best_format->height = desired.height;
     72   best_format->fourcc = cricket::FOURCC_ARGB;
     73   best_format->interval = FPS_TO_INTERVAL(kFramesPerSec);
     74   return true;
     75 }
     76 
     77 cricket::CaptureState CastVideoCapturerAdapter::Start(
     78     const cricket::VideoFormat& capture_format) {
     79   DCHECK(thread_checker_.CalledOnValidThread());
     80   DCHECK(!capture_timer_);
     81   DCHECK_EQ(capture_format.fourcc, (static_cast<uint32>(cricket::FOURCC_ARGB)));
     82 
     83   if (!desktop_capturer_) {
     84     VLOG(1) << "CastVideoCapturerAdapter failed to start.";
     85     return cricket::CS_FAILED;
     86   }
     87 
     88   // This is required to tell the cricket::VideoCapturer base class what the
     89   // capture format will be.
     90   SetCaptureFormat(&capture_format);
     91 
     92   desktop_capturer_->Start(this);
     93 
     94   // Save the Start() time of |desktop_capturer_|. This will be used
     95   // to estimate the creation time of the frame source, to set the elapsed_time
     96   // of future CapturedFrames in OnCaptureCompleted().
     97   start_time_ = base::TimeTicks::Now();
     98   capture_timer_.reset(new base::RepeatingTimer<CastVideoCapturerAdapter>());
     99   capture_timer_->Start(FROM_HERE,
    100                         base::TimeDelta::FromMicroseconds(
    101                             GetCaptureFormat()->interval /
    102                             (base::Time::kNanosecondsPerMicrosecond)),
    103                         this,
    104                         &CastVideoCapturerAdapter::CaptureNextFrame);
    105 
    106   return cricket::CS_RUNNING;
    107 }
    108 
    109 // Similar to the base class implementation with some important differences:
    110 // 1. Does not call either Stop() or Start(), as those would affect the state of
    111 // |desktop_capturer_|.
    112 // 2. Does not support unpausing after stopping the capturer. It is unclear
    113 // if that flow needs to be supported.
    114 bool CastVideoCapturerAdapter::Pause(bool pause) {
    115   DCHECK(thread_checker_.CalledOnValidThread());
    116 
    117   if (pause) {
    118     if (capture_state() == cricket::CS_PAUSED)
    119       return true;
    120 
    121     bool running = capture_state() == cricket::CS_STARTING ||
    122                    capture_state() == cricket::CS_RUNNING;
    123 
    124     DCHECK_EQ(running, IsRunning());
    125 
    126     if (!running) {
    127       LOG(ERROR)
    128           << "Cannot pause CastVideoCapturerAdapter.";
    129       return false;
    130     }
    131 
    132     // Stop |capture_timer_| and set capture state to cricket::CS_PAUSED.
    133     capture_timer_->Stop();
    134     SetCaptureState(cricket::CS_PAUSED);
    135 
    136     VLOG(1) << "CastVideoCapturerAdapter paused.";
    137 
    138     return true;
    139   } else {  // Unpausing.
    140     if (capture_state() != cricket::CS_PAUSED || !GetCaptureFormat() ||
    141         !capture_timer_) {
    142       LOG(ERROR) << "Cannot unpause CastVideoCapturerAdapter.";
    143       return false;
    144     }
    145 
    146     // Restart |capture_timer_| and set capture state to cricket::CS_RUNNING;
    147     capture_timer_->Start(FROM_HERE,
    148                           base::TimeDelta::FromMicroseconds(
    149                               GetCaptureFormat()->interval /
    150                               (base::Time::kNanosecondsPerMicrosecond)),
    151                           this,
    152                           &CastVideoCapturerAdapter::CaptureNextFrame);
    153     SetCaptureState(cricket::CS_RUNNING);
    154 
    155     VLOG(1) << "CastVideoCapturerAdapter unpaused.";
    156   }
    157   return true;
    158 }
    159 
    160 void CastVideoCapturerAdapter::Stop() {
    161   DCHECK(thread_checker_.CalledOnValidThread());
    162   DCHECK_NE(capture_state(), cricket::CS_STOPPED);
    163 
    164   capture_timer_.reset();
    165 
    166   SetCaptureFormat(NULL);
    167   SetCaptureState(cricket::CS_STOPPED);
    168 
    169   VLOG(1) << "CastVideoCapturerAdapter stopped.";
    170 }
    171 
    172 
    173 bool CastVideoCapturerAdapter::IsRunning() {
    174   DCHECK(thread_checker_.CalledOnValidThread());
    175 
    176   return capture_timer_->IsRunning();
    177 }
    178 
    179 bool CastVideoCapturerAdapter::IsScreencast() const {
    180   return true;
    181 }
    182 
    183 bool CastVideoCapturerAdapter::GetPreferredFourccs(
    184     std::vector<uint32>* fourccs) {
    185   DCHECK(thread_checker_.CalledOnValidThread());
    186   if (!fourccs)
    187     return false;
    188   fourccs->push_back(cricket::FOURCC_ARGB);
    189   return true;
    190 }
    191 
    192 void CastVideoCapturerAdapter::CaptureNextFrame() {
    193   // If we are paused, then don't capture.
    194   if (!IsRunning())
    195     return;
    196 
    197   desktop_capturer_->Capture(webrtc::DesktopRegion());
    198 }
    199 
    200 }  // namespace remoting
    201 
    202