| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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/protocol/webrtc_video_capturer_adapter.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "remoting/base/constants.h" | |
| 10 #include "third_party/libyuv/include/libyuv/convert.h" | |
| 11 #include "third_party/webrtc/media/engine/webrtcvideoframe.h" | |
| 12 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | |
| 13 | |
| 14 namespace remoting { | |
| 15 namespace protocol { | |
| 16 | |
| 17 // Number of frames to be captured per second. | |
| 18 const int kFramesPerSec = 30; | |
| 19 | |
| 20 WebrtcVideoCapturerAdapter::WebrtcVideoCapturerAdapter( | |
| 21 std::unique_ptr<webrtc::DesktopCapturer> capturer) | |
| 22 : desktop_capturer_(std::move(capturer)), weak_factory_(this) { | |
| 23 DCHECK(desktop_capturer_); | |
| 24 | |
| 25 // Disable video adaptation since we don't intend to use it. | |
| 26 set_enable_video_adapter(false); | |
| 27 } | |
| 28 | |
| 29 WebrtcVideoCapturerAdapter::~WebrtcVideoCapturerAdapter() { | |
| 30 DCHECK(!capture_timer_); | |
| 31 } | |
| 32 | |
| 33 void WebrtcVideoCapturerAdapter::SetSizeCallback( | |
| 34 const VideoStream::SizeCallback& size_callback) { | |
| 35 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 36 size_callback_ = size_callback; | |
| 37 } | |
| 38 | |
| 39 base::WeakPtr<WebrtcVideoCapturerAdapter> | |
| 40 WebrtcVideoCapturerAdapter::GetWeakPtr() { | |
| 41 return weak_factory_.GetWeakPtr(); | |
| 42 } | |
| 43 | |
| 44 bool WebrtcVideoCapturerAdapter::GetBestCaptureFormat( | |
| 45 const cricket::VideoFormat& desired, | |
| 46 cricket::VideoFormat* best_format) { | |
| 47 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 48 | |
| 49 // The |capture_format| passed to Start() is always ignored, so copy | |
| 50 // |best_format| to |desired_format|. | |
| 51 *best_format = desired; | |
| 52 return true; | |
| 53 } | |
| 54 | |
| 55 cricket::CaptureState WebrtcVideoCapturerAdapter::Start( | |
| 56 const cricket::VideoFormat& capture_format) { | |
| 57 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 58 DCHECK(!capture_timer_); | |
| 59 | |
| 60 if (!desktop_capturer_) { | |
| 61 VLOG(1) << "WebrtcVideoCapturerAdapter failed to start."; | |
| 62 return cricket::CS_FAILED; | |
| 63 } | |
| 64 | |
| 65 desktop_capturer_->Start(this); | |
| 66 | |
| 67 capture_timer_.reset(new base::RepeatingTimer()); | |
| 68 capture_timer_->Start(FROM_HERE, | |
| 69 base::TimeDelta::FromSeconds(1) / kFramesPerSec, this, | |
| 70 &WebrtcVideoCapturerAdapter::CaptureNextFrame); | |
| 71 | |
| 72 return cricket::CS_RUNNING; | |
| 73 } | |
| 74 | |
| 75 bool WebrtcVideoCapturerAdapter::PauseCapturer(bool pause) { | |
| 76 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 77 | |
| 78 if (pause) { | |
| 79 if (paused_) | |
| 80 return true; | |
| 81 | |
| 82 bool running = capture_state() == cricket::CS_STARTING || | |
| 83 capture_state() == cricket::CS_RUNNING; | |
| 84 | |
| 85 DCHECK_EQ(running, IsRunning()); | |
| 86 | |
| 87 if (!running) { | |
| 88 LOG(ERROR) | |
| 89 << "Cannot pause WebrtcVideoCapturerAdapter."; | |
| 90 return false; | |
| 91 } | |
| 92 | |
| 93 capture_timer_->Stop(); | |
| 94 paused_ = true; | |
| 95 | |
| 96 VLOG(1) << "WebrtcVideoCapturerAdapter paused."; | |
| 97 | |
| 98 return true; | |
| 99 } else { // Unpausing. | |
| 100 if (!paused_ || !GetCaptureFormat() || !capture_timer_) { | |
| 101 LOG(ERROR) << "Cannot unpause WebrtcVideoCapturerAdapter."; | |
| 102 return false; | |
| 103 } | |
| 104 | |
| 105 capture_timer_->Start(FROM_HERE, | |
| 106 base::TimeDelta::FromMicroseconds( | |
| 107 GetCaptureFormat()->interval / | |
| 108 (base::Time::kNanosecondsPerMicrosecond)), | |
| 109 this, | |
| 110 &WebrtcVideoCapturerAdapter::CaptureNextFrame); | |
| 111 paused_ = false; | |
| 112 | |
| 113 VLOG(1) << "WebrtcVideoCapturerAdapter unpaused."; | |
| 114 } | |
| 115 return true; | |
| 116 } | |
| 117 | |
| 118 void WebrtcVideoCapturerAdapter::Stop() { | |
| 119 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 120 DCHECK_NE(capture_state(), cricket::CS_STOPPED); | |
| 121 | |
| 122 capture_timer_.reset(); | |
| 123 desktop_capturer_.reset(); | |
| 124 | |
| 125 SetCaptureFormat(nullptr); | |
| 126 SetCaptureState(cricket::CS_STOPPED); | |
| 127 | |
| 128 VLOG(1) << "WebrtcVideoCapturerAdapter stopped."; | |
| 129 } | |
| 130 | |
| 131 bool WebrtcVideoCapturerAdapter::IsRunning() { | |
| 132 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 133 return capture_timer_->IsRunning(); | |
| 134 } | |
| 135 | |
| 136 bool WebrtcVideoCapturerAdapter::IsScreencast() const { | |
| 137 return true; | |
| 138 } | |
| 139 | |
| 140 bool WebrtcVideoCapturerAdapter::GetPreferredFourccs( | |
| 141 std::vector<uint32_t>* fourccs) { | |
| 142 return false; | |
| 143 } | |
| 144 | |
| 145 webrtc::SharedMemory* WebrtcVideoCapturerAdapter::CreateSharedMemory( | |
| 146 size_t size) { | |
| 147 return nullptr; | |
| 148 } | |
| 149 | |
| 150 void WebrtcVideoCapturerAdapter::OnCaptureCompleted( | |
| 151 webrtc::DesktopFrame* frame) { | |
| 152 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 153 | |
| 154 DCHECK(capture_pending_); | |
| 155 capture_pending_ = false; | |
| 156 | |
| 157 if (!frame) | |
| 158 return; | |
| 159 | |
| 160 std::unique_ptr<webrtc::DesktopFrame> owned_frame(frame); | |
| 161 | |
| 162 // TODO(sergeyu): Currently the adapter keeps generating frames even when | |
| 163 // nothing is changing on the screen. This is necessary because the video | |
| 164 // sender drops frames. Obviously this is a suboptimal. The sending code in | |
| 165 // WebRTC needs to have some mechanism to notify when the bandwidth is | |
| 166 // exceeded, so the capturer can adapt frame rate. | |
| 167 | |
| 168 webrtc::DesktopVector dpi = | |
| 169 frame->dpi().is_zero() ? webrtc::DesktopVector(kDefaultDpi, kDefaultDpi) | |
| 170 : frame->dpi(); | |
| 171 if (!frame_size_.equals(frame->size()) || !frame_dpi_.equals(dpi)) { | |
| 172 frame_size_ = frame->size(); | |
| 173 frame_dpi_ = dpi; | |
| 174 if (!size_callback_.is_null()) | |
| 175 size_callback_.Run(frame_size_, frame_dpi_); | |
| 176 } | |
| 177 | |
| 178 if (!yuv_frame_ || | |
| 179 !frame_size_.equals( | |
| 180 webrtc::DesktopSize(yuv_frame_->width(), yuv_frame_->height()))) { | |
| 181 yuv_frame_ = new rtc::RefCountedObject<webrtc::I420Buffer>( | |
| 182 frame_size_.width(), frame_size_.height()); | |
| 183 // Set updated_region so the whole frame is converted to YUV below. | |
| 184 frame->mutable_updated_region()->SetRect( | |
| 185 webrtc::DesktopRect::MakeSize(frame_size_)); | |
| 186 } | |
| 187 | |
| 188 if (!yuv_frame_->HasOneRef()) { | |
| 189 // Frame is still used, typically by the encoder. We have to make | |
| 190 // a copy before modifying it. | |
| 191 // TODO(sergeyu): This will copy the buffer if it's being used. | |
| 192 // Optimize it by keeping a queue of frames. | |
| 193 yuv_frame_ = webrtc::I420Buffer::Copy(yuv_frame_); | |
| 194 } | |
| 195 | |
| 196 for (webrtc::DesktopRegion::Iterator i(frame->updated_region()); !i.IsAtEnd(); | |
| 197 i.Advance()) { | |
| 198 int left = i.rect().left(); | |
| 199 int top = i.rect().top(); | |
| 200 int width = i.rect().width(); | |
| 201 int height = i.rect().height(); | |
| 202 | |
| 203 if (left % 2 == 1) { | |
| 204 --left; | |
| 205 ++width; | |
| 206 } | |
| 207 if (top % 2 == 1) { | |
| 208 --top; | |
| 209 ++height; | |
| 210 } | |
| 211 | |
| 212 int y_stride = yuv_frame_->stride(webrtc::kYPlane); | |
| 213 int u_stride = yuv_frame_->stride(webrtc::kUPlane); | |
| 214 int v_stride = yuv_frame_->stride(webrtc::kVPlane); | |
| 215 libyuv::ARGBToI420( | |
| 216 frame->data() + frame->stride() * top + | |
| 217 left * webrtc::DesktopFrame::kBytesPerPixel, | |
| 218 frame->stride(), | |
| 219 yuv_frame_->MutableData(webrtc::kYPlane) + y_stride * top + left, | |
| 220 y_stride, yuv_frame_->MutableData(webrtc::kUPlane) + | |
| 221 u_stride * top / 2 + left / 2, | |
| 222 u_stride, yuv_frame_->MutableData(webrtc::kVPlane) + | |
| 223 v_stride * top / 2 + left / 2, | |
| 224 v_stride, width, height); | |
| 225 } | |
| 226 | |
| 227 cricket::WebRtcVideoFrame video_frame( | |
| 228 yuv_frame_, (base::TimeTicks::Now() - base::TimeTicks()) / | |
| 229 base::TimeDelta::FromMicroseconds(1) * 1000, | |
| 230 webrtc::kVideoRotation_0); | |
| 231 | |
| 232 OnFrame(this, &video_frame); | |
| 233 } | |
| 234 | |
| 235 void WebrtcVideoCapturerAdapter::CaptureNextFrame() { | |
| 236 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 237 | |
| 238 if (capture_pending_) | |
| 239 return; | |
| 240 capture_pending_ = true; | |
| 241 desktop_capturer_->Capture(webrtc::DesktopRegion()); | |
| 242 } | |
| 243 | |
| 244 } // namespace protocol | |
| 245 } // namespace remoting | |
| OLD | NEW |