Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / renderer_host / media / screen_capture_device.cc
blob21a638d6b4fa3972fde8e289e198deb0b74336d3
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"
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/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"
18 namespace content {
20 namespace {
21 const int kBytesPerPixel = 4;
22 } // namespace
24 class ScreenCaptureDevice::Core
25 : public base::RefCountedThreadSafe<Core>,
26 public webrtc::DesktopCapturer::Callback {
27 public:
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,
37 int frame_rate,
38 EventHandler* event_handler);
39 void Start();
40 void Stop();
41 void DeAllocate();
43 private:
44 friend class base::RefCountedThreadSafe<Core>;
45 virtual ~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);
54 void DoStart();
55 void DoStop();
56 void DoDeAllocate();
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.
66 void DoCapture();
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().
81 int frame_rate_;
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
88 // size constraints.
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
96 // then this is NULL.
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.
101 bool started_;
103 // True when we have delayed OnCaptureTimer() task posted on
104 // |task_runner_|.
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),
117 started_(false),
118 capture_task_posted_(false),
119 capture_in_progress_(false) {
122 ScreenCaptureDevice::Core::~Core() {
125 void ScreenCaptureDevice::Core::Allocate(int width, int height,
126 int frame_rate,
127 EventHandler* event_handler) {
128 DCHECK_GT(width, 0);
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(
138 FROM_HERE,
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) {
162 return NULL;
165 void ScreenCaptureDevice::Core::OnCaptureCompleted(
166 webrtc::DesktopFrame* frame) {
167 DCHECK(task_runner_->RunsTasksOnCurrentThread());
168 DCHECK(capture_in_progress_);
170 capture_in_progress_ = false;
172 if (!frame) {
173 LOG(ERROR) << "Failed to capture a frame.";
174 event_handler_->OnError();
175 return;
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.
185 output_size_.set(
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_);
200 if (event_handler_)
201 event_handler_->OnFrameInfo(caps);
204 if (!started_)
205 return;
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);
220 return;
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.
226 if (!scaled_frame_)
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,
261 int height,
262 int frame_rate) {
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));
282 #else
283 screen_capturer_.reset(webrtc::ScreenCapturer::Create());
284 #endif
287 if (screen_capturer_)
288 screen_capturer_->Start(this);
290 // Capture first frame, so that we can call OnFrameInfo() callback.
291 DoCapture();
294 void ScreenCaptureDevice::Core::DoStart() {
295 DCHECK(task_runner_->RunsTasksOnCurrentThread());
296 started_ = true;
297 if (!capture_task_posted_) {
298 ScheduleCaptureTimer();
299 DoCapture();
303 void ScreenCaptureDevice::Core::DoStop() {
304 DCHECK(task_runner_->RunsTasksOnCurrentThread());
305 started_ = false;
306 scaled_frame_.reset();
309 void ScreenCaptureDevice::Core::DoDeAllocate() {
310 DCHECK(task_runner_->RunsTasksOnCurrentThread());
311 DoStop();
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;
328 if (!started_)
329 return;
331 // Schedule a task for the next frame.
332 ScheduleCaptureTimer();
333 DoCapture();
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
344 // supported.
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() {
355 DeAllocate();
358 void ScreenCaptureDevice::SetScreenCapturerForTest(
359 scoped_ptr<webrtc::ScreenCapturer> capturer) {
360 core_->SetScreenCapturerForTest(capturer.Pass());
363 void ScreenCaptureDevice::Allocate(int width, int height,
364 int frame_rate,
365 EventHandler* event_handler) {
366 core_->Allocate(width, height, frame_rate, event_handler);
369 void ScreenCaptureDevice::Start() {
370 core_->Start();
373 void ScreenCaptureDevice::Stop() {
374 core_->Stop();
377 void ScreenCaptureDevice::DeAllocate() {
378 core_->DeAllocate();
381 const media::VideoCaptureDevice::Name& ScreenCaptureDevice::device_name() {
382 return name_;
385 } // namespace content