1 // Copyright (c) 2013 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 "content/browser/renderer_host/media/desktop_capture_device.h" 6 7 #include "base/bind.h" 8 #include "base/location.h" 9 #include "base/logging.h" 10 #include "base/sequenced_task_runner.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/synchronization/lock.h" 13 #include "base/threading/sequenced_worker_pool.h" 14 #include "content/public/browser/browser_thread.h" 15 #include "content/public/common/desktop_media_id.h" 16 #include "media/base/video_util.h" 17 #include "third_party/libyuv/include/libyuv/scale_argb.h" 18 #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" 19 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 20 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" 21 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h" 22 23 namespace content { 24 25 namespace { 26 const int kBytesPerPixel = 4; 27 } // namespace 28 29 class DesktopCaptureDevice::Core 30 : public base::RefCountedThreadSafe<Core>, 31 public webrtc::DesktopCapturer::Callback { 32 public: 33 Core(scoped_refptr<base::SequencedTaskRunner> task_runner, 34 scoped_ptr<webrtc::DesktopCapturer> capturer); 35 36 // Implementation of VideoCaptureDevice methods. 37 void Allocate(int width, int height, 38 int frame_rate, 39 EventHandler* event_handler); 40 void Start(); 41 void Stop(); 42 void DeAllocate(); 43 44 private: 45 friend class base::RefCountedThreadSafe<Core>; 46 virtual ~Core(); 47 48 // webrtc::DesktopCapturer::Callback interface 49 virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; 50 virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; 51 52 // Helper methods that run on the |task_runner_|. Posted from the 53 // corresponding public methods. 54 void DoAllocate(int width, int height, int frame_rate); 55 void DoStart(); 56 void DoStop(); 57 void DoDeAllocate(); 58 59 // Helper to schedule capture tasks. 60 void ScheduleCaptureTimer(); 61 62 // Method that is scheduled on |task_runner_| to be called on regular interval 63 // to capture a frame. 64 void OnCaptureTimer(); 65 66 // Captures a single frame. 67 void DoCapture(); 68 69 // Task runner used for capturing operations. 70 scoped_refptr<base::SequencedTaskRunner> task_runner_; 71 72 // The underlying DesktopCapturer instance used to capture frames. 73 scoped_ptr<webrtc::DesktopCapturer> desktop_capturer_; 74 75 // |event_handler_lock_| must be locked whenever |event_handler_| is used. 76 // It's necessary because DeAllocate() needs to reset it on the calling thread 77 // to ensure that the event handler is not called once DeAllocate() returns. 78 base::Lock event_handler_lock_; 79 EventHandler* event_handler_; 80 81 // Requested size specified to Allocate(). 82 webrtc::DesktopSize requested_size_; 83 84 // Frame rate specified to Allocate(). 85 int frame_rate_; 86 87 // Empty until the first frame has been captured, and the output dimensions 88 // chosen based on the capture frame's size, and any caller-supplied 89 // size constraints. 90 webrtc::DesktopSize output_size_; 91 92 // Size of the most recently received frame. 93 webrtc::DesktopSize previous_frame_size_; 94 95 // DesktopFrame into which captured frames are scaled, if the source size does 96 // not match |output_size_|. If the source and output have the same dimensions 97 // then this is NULL. 98 scoped_ptr<webrtc::DesktopFrame> scaled_frame_; 99 100 // True between DoStart() and DoStop(). Can't just check |event_handler_| 101 // because |event_handler_| is used on the caller thread. 102 bool started_; 103 104 // True when we have delayed OnCaptureTimer() task posted on 105 // |task_runner_|. 106 bool capture_task_posted_; 107 108 // True when waiting for |desktop_capturer_| to capture current frame. 109 bool capture_in_progress_; 110 111 DISALLOW_COPY_AND_ASSIGN(Core); 112 }; 113 114 DesktopCaptureDevice::Core::Core( 115 scoped_refptr<base::SequencedTaskRunner> task_runner, 116 scoped_ptr<webrtc::DesktopCapturer> capturer) 117 : task_runner_(task_runner), 118 desktop_capturer_(capturer.Pass()), 119 event_handler_(NULL), 120 started_(false), 121 capture_task_posted_(false), 122 capture_in_progress_(false) { 123 } 124 125 DesktopCaptureDevice::Core::~Core() { 126 } 127 128 void DesktopCaptureDevice::Core::Allocate(int width, int height, 129 int frame_rate, 130 EventHandler* event_handler) { 131 DCHECK_GT(width, 0); 132 DCHECK_GT(height, 0); 133 DCHECK_GT(frame_rate, 0); 134 135 { 136 base::AutoLock auto_lock(event_handler_lock_); 137 event_handler_ = event_handler; 138 } 139 140 task_runner_->PostTask( 141 FROM_HERE, 142 base::Bind(&Core::DoAllocate, this, width, height, frame_rate)); 143 } 144 145 void DesktopCaptureDevice::Core::Start() { 146 task_runner_->PostTask( 147 FROM_HERE, base::Bind(&Core::DoStart, this)); 148 } 149 150 void DesktopCaptureDevice::Core::Stop() { 151 task_runner_->PostTask( 152 FROM_HERE, base::Bind(&Core::DoStop, this)); 153 } 154 155 void DesktopCaptureDevice::Core::DeAllocate() { 156 { 157 base::AutoLock auto_lock(event_handler_lock_); 158 event_handler_ = NULL; 159 } 160 task_runner_->PostTask(FROM_HERE, base::Bind(&Core::DoDeAllocate, this)); 161 } 162 163 webrtc::SharedMemory* 164 DesktopCaptureDevice::Core::CreateSharedMemory(size_t size) { 165 return NULL; 166 } 167 168 void DesktopCaptureDevice::Core::OnCaptureCompleted( 169 webrtc::DesktopFrame* frame) { 170 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 171 DCHECK(capture_in_progress_); 172 173 capture_in_progress_ = false; 174 175 if (!frame) { 176 LOG(ERROR) << "Failed to capture a frame."; 177 event_handler_->OnError(); 178 return; 179 } 180 181 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); 182 183 // If an |output_size_| hasn't yet been chosen then choose one, based upon 184 // the source frame size and the requested size supplied to Allocate(). 185 if (output_size_.is_empty()) { 186 // Treat the requested size as upper bounds on width & height. 187 // TODO(wez): Constraints should be passed from getUserMedia to Allocate. 188 output_size_.set( 189 std::min(frame->size().width(), requested_size_.width()), 190 std::min(frame->size().height(), requested_size_.height())); 191 192 // Inform the EventHandler of the output dimensions, format and frame rate. 193 media::VideoCaptureCapability caps; 194 caps.width = output_size_.width(); 195 caps.height = output_size_.height(); 196 caps.frame_rate = frame_rate_; 197 caps.color = media::VideoCaptureCapability::kARGB; 198 caps.expected_capture_delay = 199 base::Time::kMillisecondsPerSecond / frame_rate_; 200 caps.interlaced = false; 201 202 base::AutoLock auto_lock(event_handler_lock_); 203 if (event_handler_) 204 event_handler_->OnFrameInfo(caps); 205 } 206 207 if (!started_) 208 return; 209 210 size_t output_bytes = output_size_.width() * output_size_.height() * 211 webrtc::DesktopFrame::kBytesPerPixel; 212 213 if (frame->size().equals(output_size_)) { 214 // If the captured frame matches the output size, we can return the pixel 215 // data directly, without scaling. 216 scaled_frame_.reset(); 217 218 base::AutoLock auto_lock(event_handler_lock_); 219 if (event_handler_) { 220 event_handler_->OnIncomingCapturedFrame( 221 frame->data(), output_bytes, base::Time::Now(), 0, false, false); 222 } 223 return; 224 } 225 226 // If the output size differs from the frame size (e.g. the source has changed 227 // from its original dimensions, or the caller specified size constraints) 228 // then we need to scale the image. 229 if (!scaled_frame_) 230 scaled_frame_.reset(new webrtc::BasicDesktopFrame(output_size_)); 231 DCHECK(scaled_frame_->size().equals(output_size_)); 232 233 // If the source frame size changed then clear |scaled_frame_|'s pixels. 234 if (!previous_frame_size_.equals(frame->size())) { 235 previous_frame_size_ = frame->size(); 236 memset(scaled_frame_->data(), 0, output_bytes); 237 } 238 239 // Determine the output size preserving aspect, and center in output buffer. 240 gfx::Rect scaled_rect = media::ComputeLetterboxRegion( 241 gfx::Rect(0, 0, output_size_.width(), output_size_.height()), 242 gfx::Size(frame->size().width(), frame->size().height())); 243 uint8* scaled_data = scaled_frame_->data() + 244 scaled_frame_->stride() * scaled_rect.y() + 245 webrtc::DesktopFrame::kBytesPerPixel * scaled_rect.x(); 246 247 // TODO(wez): Optimize this to scale only changed portions of the output, 248 // using ARGBScaleClip(). 249 libyuv::ARGBScale(frame->data(), frame->stride(), 250 frame->size().width(), frame->size().height(), 251 scaled_data, scaled_frame_->stride(), 252 scaled_rect.width(), scaled_rect.height(), 253 libyuv::kFilterBilinear); 254 255 base::AutoLock auto_lock(event_handler_lock_); 256 if (event_handler_) { 257 event_handler_->OnIncomingCapturedFrame( 258 scaled_frame_->data(), output_bytes, 259 base::Time::Now(), 0, false, false); 260 } 261 } 262 263 void DesktopCaptureDevice::Core::DoAllocate(int width, 264 int height, 265 int frame_rate) { 266 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 267 DCHECK(desktop_capturer_); 268 269 requested_size_.set(width, height); 270 output_size_.set(0, 0); 271 frame_rate_ = frame_rate; 272 273 desktop_capturer_->Start(this); 274 275 // Capture first frame, so that we can call OnFrameInfo() callback. 276 DoCapture(); 277 } 278 279 void DesktopCaptureDevice::Core::DoStart() { 280 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 281 started_ = true; 282 if (!capture_task_posted_) { 283 ScheduleCaptureTimer(); 284 DoCapture(); 285 } 286 } 287 288 void DesktopCaptureDevice::Core::DoStop() { 289 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 290 started_ = false; 291 scaled_frame_.reset(); 292 } 293 294 void DesktopCaptureDevice::Core::DoDeAllocate() { 295 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 296 DoStop(); 297 desktop_capturer_.reset(); 298 output_size_.set(0, 0); 299 } 300 301 void DesktopCaptureDevice::Core::ScheduleCaptureTimer() { 302 DCHECK(!capture_task_posted_); 303 capture_task_posted_ = true; 304 task_runner_->PostDelayedTask( 305 FROM_HERE, base::Bind(&Core::OnCaptureTimer, this), 306 base::TimeDelta::FromSeconds(1) / frame_rate_); 307 } 308 309 void DesktopCaptureDevice::Core::OnCaptureTimer() { 310 DCHECK(capture_task_posted_); 311 capture_task_posted_ = false; 312 313 if (!started_) 314 return; 315 316 // Schedule a task for the next frame. 317 ScheduleCaptureTimer(); 318 DoCapture(); 319 } 320 321 void DesktopCaptureDevice::Core::DoCapture() { 322 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 323 DCHECK(!capture_in_progress_); 324 325 capture_in_progress_ = true; 326 desktop_capturer_->Capture(webrtc::DesktopRegion()); 327 328 // Currently only synchronous implementations of DesktopCapturer are 329 // supported. 330 DCHECK(!capture_in_progress_); 331 } 332 333 // static 334 scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create( 335 const DesktopMediaID& source) { 336 scoped_refptr<base::SequencedWorkerPool> blocking_pool = 337 BrowserThread::GetBlockingPool(); 338 scoped_refptr<base::SequencedTaskRunner> task_runner = 339 blocking_pool->GetSequencedTaskRunner( 340 blocking_pool->GetSequenceToken()); 341 342 switch (source.type) { 343 case DesktopMediaID::TYPE_SCREEN: { 344 scoped_ptr<webrtc::DesktopCapturer> capturer; 345 346 #if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) && defined(USE_X11) 347 // ScreenCapturerX11 polls by default, due to poor driver support for 348 // DAMAGE. ChromeOS' drivers [can be patched to] support DAMAGE properly, 349 // so use it. However ARM driver seems to not support this properly, so 350 // disable it for ARM. See http://crbug.com/230105 . 351 capturer.reset(webrtc::ScreenCapturer::CreateWithXDamage(true)); 352 #elif defined(OS_WIN) 353 // ScreenCapturerWin disables Aero by default. We don't want it disabled 354 // for WebRTC screen capture, though. 355 capturer.reset( 356 webrtc::ScreenCapturer::CreateWithDisableAero(false)); 357 #else 358 capturer.reset(webrtc::ScreenCapturer::Create()); 359 #endif 360 361 return scoped_ptr<media::VideoCaptureDevice>(new DesktopCaptureDevice( 362 task_runner, capturer.Pass())); 363 } 364 365 case DesktopMediaID::TYPE_WINDOW: { 366 scoped_ptr<webrtc::WindowCapturer> capturer( 367 webrtc::WindowCapturer::Create()); 368 369 if (!capturer || !capturer->SelectWindow(source.id)) { 370 return scoped_ptr<media::VideoCaptureDevice>(); 371 } 372 373 return scoped_ptr<media::VideoCaptureDevice>(new DesktopCaptureDevice( 374 task_runner, capturer.PassAs<webrtc::DesktopCapturer>())); 375 } 376 377 default: { 378 NOTREACHED(); 379 return scoped_ptr<media::VideoCaptureDevice>(); 380 } 381 } 382 } 383 384 DesktopCaptureDevice::DesktopCaptureDevice( 385 scoped_refptr<base::SequencedTaskRunner> task_runner, 386 scoped_ptr<webrtc::DesktopCapturer> capturer) 387 : core_(new Core(task_runner, capturer.Pass())), 388 name_("", "") { 389 } 390 391 DesktopCaptureDevice::~DesktopCaptureDevice() { 392 DeAllocate(); 393 } 394 395 void DesktopCaptureDevice::Allocate( 396 const media::VideoCaptureCapability& capture_format, 397 EventHandler* observer) { 398 core_->Allocate(capture_format.width, 399 capture_format.height, 400 capture_format.frame_rate, 401 observer); 402 } 403 404 void DesktopCaptureDevice::Start() { 405 core_->Start(); 406 } 407 408 void DesktopCaptureDevice::Stop() { 409 core_->Stop(); 410 } 411 412 void DesktopCaptureDevice::DeAllocate() { 413 core_->DeAllocate(); 414 } 415 416 const media::VideoCaptureDevice::Name& DesktopCaptureDevice::device_name() { 417 return name_; 418 } 419 420 } // namespace content 421