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