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.
5 #include "content/browser/renderer_host/media/screen_capture_device.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/sequenced_task_runner.h"
11 #include "base/synchronization/lock.h"
12 #include "media/base/video_util.h"
13 #include "third_party/libyuv/include/libyuv/scale_argb.h"
14 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
15 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_shape.h"
16 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
21 const int kBytesPerPixel
= 4;
24 class ScreenCaptureDevice::Core
25 : public base::RefCountedThreadSafe
<Core
>,
26 public webrtc::DesktopCapturer::Callback
{
28 explicit Core(scoped_refptr
<base::SequencedTaskRunner
> task_runner
);
30 // Helper used in tests to supply a fake capturer.
31 void SetScreenCapturerForTest(scoped_ptr
<webrtc::ScreenCapturer
> capturer
) {
32 screen_capturer_
= capturer
.Pass();
35 // Implementation of VideoCaptureDevice methods.
36 void Allocate(int width
, int height
,
38 EventHandler
* event_handler
);
44 friend class base::RefCountedThreadSafe
<Core
>;
47 // webrtc::DesktopCapturer::Callback interface
48 virtual webrtc::SharedMemory
* CreateSharedMemory(size_t size
) OVERRIDE
;
49 virtual void OnCaptureCompleted(webrtc::DesktopFrame
* frame
) OVERRIDE
;
51 // Helper methods that run on the |task_runner_|. Posted from the
52 // corresponding public methods.
53 void DoAllocate(int width
, int height
, int frame_rate
);
58 // Helper to schedule capture tasks.
59 void ScheduleCaptureTimer();
61 // Method that is scheduled on |task_runner_| to be called on regular interval
62 // to capture the screen.
63 void OnCaptureTimer();
65 // Captures a single frame.
68 // Task runner used for screen capturing operations.
69 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
71 // |event_handler_lock_| must be locked whenever |event_handler_| is used.
72 // It's necessary because DeAllocate() needs to reset it on the calling thread
73 // to ensure that the event handler is not called once DeAllocate() returns.
74 base::Lock event_handler_lock_
;
75 EventHandler
* event_handler_
;
77 // Requested size specified to Allocate().
78 webrtc::DesktopSize requested_size_
;
80 // Frame rate specified to Allocate().
83 // The underlying ScreenCapturer instance used to capture frames.
84 scoped_ptr
<webrtc::ScreenCapturer
> screen_capturer_
;
86 // Empty until the first frame has been captured, and the output dimensions
87 // chosen based on the capture frame's size, and any caller-supplied
89 webrtc::DesktopSize output_size_
;
91 // Size of the most recently received frame.
92 webrtc::DesktopSize previous_frame_size_
;
94 // DesktopFrame into which captured frames are scaled, if the source size does
95 // not match |output_size_|. If the source and output have the same dimensions
97 scoped_ptr
<webrtc::DesktopFrame
> scaled_frame_
;
99 // True between DoStart() and DoStop(). Can't just check |event_handler_|
100 // because |event_handler_| is used on the caller thread.
103 // True when we have delayed OnCaptureTimer() task posted on
105 bool capture_task_posted_
;
107 // True when waiting for |screen_capturer_| to capture current frame.
108 bool capture_in_progress_
;
110 DISALLOW_COPY_AND_ASSIGN(Core
);
113 ScreenCaptureDevice::Core::Core(
114 scoped_refptr
<base::SequencedTaskRunner
> task_runner
)
115 : task_runner_(task_runner
),
116 event_handler_(NULL
),
118 capture_task_posted_(false),
119 capture_in_progress_(false) {
122 ScreenCaptureDevice::Core::~Core() {
125 void ScreenCaptureDevice::Core::Allocate(int width
, int height
,
127 EventHandler
* event_handler
) {
129 DCHECK_GT(height
, 0);
130 DCHECK_GT(frame_rate
, 0);
133 base::AutoLock
auto_lock(event_handler_lock_
);
134 event_handler_
= event_handler
;
137 task_runner_
->PostTask(
139 base::Bind(&Core::DoAllocate
, this, width
, height
, frame_rate
));
142 void ScreenCaptureDevice::Core::Start() {
143 task_runner_
->PostTask(
144 FROM_HERE
, base::Bind(&Core::DoStart
, this));
147 void ScreenCaptureDevice::Core::Stop() {
148 task_runner_
->PostTask(
149 FROM_HERE
, base::Bind(&Core::DoStop
, this));
152 void ScreenCaptureDevice::Core::DeAllocate() {
154 base::AutoLock
auto_lock(event_handler_lock_
);
155 event_handler_
= NULL
;
157 task_runner_
->PostTask(FROM_HERE
, base::Bind(&Core::DoDeAllocate
, this));
160 webrtc::SharedMemory
*
161 ScreenCaptureDevice::Core::CreateSharedMemory(size_t size
) {
165 void ScreenCaptureDevice::Core::OnCaptureCompleted(
166 webrtc::DesktopFrame
* frame
) {
167 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
168 DCHECK(capture_in_progress_
);
170 capture_in_progress_
= false;
173 LOG(ERROR
) << "Failed to capture a frame.";
174 event_handler_
->OnError();
178 scoped_ptr
<webrtc::DesktopFrame
> owned_frame(frame
);
180 // If an |output_size_| hasn't yet been chosen then choose one, based upon
181 // the source frame size and the requested size supplied to Allocate().
182 if (output_size_
.is_empty()) {
183 // Treat the requested size as upper bounds on width & height.
184 // TODO(wez): Constraints should be passed from getUserMedia to Allocate.
186 std::min(frame
->size().width(), requested_size_
.width()),
187 std::min(frame
->size().height(), requested_size_
.height()));
189 // Inform the EventHandler of the output dimensions, format and frame rate.
190 media::VideoCaptureCapability caps
;
191 caps
.width
= output_size_
.width();
192 caps
.height
= output_size_
.height();
193 caps
.frame_rate
= frame_rate_
;
194 caps
.color
= media::VideoCaptureCapability::kARGB
;
195 caps
.expected_capture_delay
=
196 base::Time::kMillisecondsPerSecond
/ frame_rate_
;
197 caps
.interlaced
= false;
199 base::AutoLock
auto_lock(event_handler_lock_
);
201 event_handler_
->OnFrameInfo(caps
);
207 size_t output_bytes
= output_size_
.width() * output_size_
.height() *
208 webrtc::DesktopFrame::kBytesPerPixel
;
210 if (frame
->size().equals(output_size_
)) {
211 // If the captured frame matches the output size, we can return the pixel
212 // data directly, without scaling.
213 scaled_frame_
.reset();
215 base::AutoLock
auto_lock(event_handler_lock_
);
216 if (event_handler_
) {
217 event_handler_
->OnIncomingCapturedFrame(
218 frame
->data(), output_bytes
, base::Time::Now(), 0, false, false);
223 // If the output size differs from the frame size (e.g. the source has changed
224 // from its original dimensions, or the caller specified size constraints)
225 // then we need to scale the image.
227 scaled_frame_
.reset(new webrtc::BasicDesktopFrame(output_size_
));
228 DCHECK(scaled_frame_
->size().equals(output_size_
));
230 // If the source frame size changed then clear |scaled_frame_|'s pixels.
231 if (!previous_frame_size_
.equals(frame
->size())) {
232 previous_frame_size_
= frame
->size();
233 memset(scaled_frame_
->data(), 0, output_bytes
);
236 // Determine the output size preserving aspect, and center in output buffer.
237 gfx::Rect scaled_rect
= media::ComputeLetterboxRegion(
238 gfx::Rect(0, 0, output_size_
.width(), output_size_
.height()),
239 gfx::Size(frame
->size().width(), frame
->size().height()));
240 uint8
* scaled_data
= scaled_frame_
->data() +
241 scaled_frame_
->stride() * scaled_rect
.y() +
242 webrtc::DesktopFrame::kBytesPerPixel
* scaled_rect
.x();
244 // TODO(wez): Optimize this to scale only changed portions of the output,
245 // using ARGBScaleClip().
246 libyuv::ARGBScale(frame
->data(), frame
->stride(),
247 frame
->size().width(), frame
->size().height(),
248 scaled_data
, scaled_frame_
->stride(),
249 scaled_rect
.width(), scaled_rect
.height(),
250 libyuv::kFilterBilinear
);
252 base::AutoLock
auto_lock(event_handler_lock_
);
253 if (event_handler_
) {
254 event_handler_
->OnIncomingCapturedFrame(
255 scaled_frame_
->data(), output_bytes
,
256 base::Time::Now(), 0, false, false);
260 void ScreenCaptureDevice::Core::DoAllocate(int width
,
263 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
265 requested_size_
.set(width
, height
);
266 output_size_
.set(0, 0);
267 frame_rate_
= frame_rate
;
269 // Create and start frame capturer.
270 if (!screen_capturer_
) {
271 #if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) && defined(USE_X11)
272 // ScreenCapturerX11 polls by default, due to poor driver support for
273 // DAMAGE. ChromeOS' drivers [can be patched to] support DAMAGE properly, so
274 // use it. However ARM driver seems to not support this properly, so disable
275 // it for ARM. See http://crbug.com/230105.
276 screen_capturer_
.reset(webrtc::ScreenCapturer::CreateWithXDamage(true));
277 #elif defined(OS_WIN)
278 // ScreenCapturerWin disables Aero by default. We don't want it disabled for
279 // WebRTC screen capture, though.
280 screen_capturer_
.reset(
281 webrtc::ScreenCapturer::CreateWithDisableAero(false));
283 screen_capturer_
.reset(webrtc::ScreenCapturer::Create());
287 if (screen_capturer_
)
288 screen_capturer_
->Start(this);
290 // Capture first frame, so that we can call OnFrameInfo() callback.
294 void ScreenCaptureDevice::Core::DoStart() {
295 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
297 if (!capture_task_posted_
) {
298 ScheduleCaptureTimer();
303 void ScreenCaptureDevice::Core::DoStop() {
304 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
306 scaled_frame_
.reset();
309 void ScreenCaptureDevice::Core::DoDeAllocate() {
310 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
312 screen_capturer_
.reset();
313 output_size_
.set(0, 0);
316 void ScreenCaptureDevice::Core::ScheduleCaptureTimer() {
317 DCHECK(!capture_task_posted_
);
318 capture_task_posted_
= true;
319 task_runner_
->PostDelayedTask(
320 FROM_HERE
, base::Bind(&Core::OnCaptureTimer
, this),
321 base::TimeDelta::FromSeconds(1) / frame_rate_
);
324 void ScreenCaptureDevice::Core::OnCaptureTimer() {
325 DCHECK(capture_task_posted_
);
326 capture_task_posted_
= false;
331 // Schedule a task for the next frame.
332 ScheduleCaptureTimer();
336 void ScreenCaptureDevice::Core::DoCapture() {
337 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
338 DCHECK(!capture_in_progress_
);
340 capture_in_progress_
= true;
341 screen_capturer_
->Capture(webrtc::DesktopRegion());
343 // Currently only synchronous implementations of DesktopCapturer are
345 DCHECK(!capture_in_progress_
);
348 ScreenCaptureDevice::ScreenCaptureDevice(
349 scoped_refptr
<base::SequencedTaskRunner
> task_runner
)
350 : core_(new Core(task_runner
)) {
351 name_
.device_name
= "Screen";
354 ScreenCaptureDevice::~ScreenCaptureDevice() {
358 void ScreenCaptureDevice::SetScreenCapturerForTest(
359 scoped_ptr
<webrtc::ScreenCapturer
> capturer
) {
360 core_
->SetScreenCapturerForTest(capturer
.Pass());
363 void ScreenCaptureDevice::Allocate(int width
, int height
,
365 EventHandler
* event_handler
) {
366 core_
->Allocate(width
, height
, frame_rate
, event_handler
);
369 void ScreenCaptureDevice::Start() {
373 void ScreenCaptureDevice::Stop() {
377 void ScreenCaptureDevice::DeAllocate() {
381 const media::VideoCaptureDevice::Name
& ScreenCaptureDevice::device_name() {
385 } // namespace content