Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / media / desktop_capture_device.cc
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/browser/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_and_cursor_composer.h"
19 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
20 #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
21 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
22 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
23 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
24 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
25
26 namespace content {
27
28 namespace {
29
30 // Maximum CPU time percentage of a single core that can be consumed for desktop
31 // capturing. This means that on systems where screen scraping is slow we may
32 // need to capture at frame rate lower than requested. This is necessary to keep
33 // UI responsive.
34 const int kMaximumCpuConsumptionPercentage = 50;
35
36 webrtc::DesktopRect ComputeLetterboxRect(
37     const webrtc::DesktopSize& max_size,
38     const webrtc::DesktopSize& source_size) {
39   gfx::Rect result = media::ComputeLetterboxRegion(
40       gfx::Rect(0, 0, max_size.width(), max_size.height()),
41       gfx::Size(source_size.width(), source_size.height()));
42   return webrtc::DesktopRect::MakeLTRB(
43       result.x(), result.y(), result.right(), result.bottom());
44 }
45
46 }  // namespace
47
48 class DesktopCaptureDevice::Core
49     : public base::RefCountedThreadSafe<Core>,
50       public webrtc::DesktopCapturer::Callback {
51  public:
52   Core(scoped_refptr<base::SequencedTaskRunner> task_runner,
53        scoped_ptr<webrtc::DesktopCapturer> capturer);
54
55   // Implementation of VideoCaptureDevice methods.
56   void AllocateAndStart(const media::VideoCaptureParams& params,
57                         scoped_ptr<Client> client);
58   void StopAndDeAllocate();
59
60  private:
61   friend class base::RefCountedThreadSafe<Core>;
62   virtual ~Core();
63
64   // webrtc::DesktopCapturer::Callback interface
65   virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE;
66   virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE;
67
68   // Helper methods that run on the |task_runner_|. Posted from the
69   // corresponding public methods.
70   void DoAllocateAndStart(const media::VideoCaptureParams& params,
71                           scoped_ptr<Client> client);
72   void DoStopAndDeAllocate();
73
74   // Chooses new output properties based on the supplied source size and the
75   // properties requested to Allocate(), and dispatches OnFrameInfo[Changed]
76   // notifications.
77   void RefreshCaptureFormat(const webrtc::DesktopSize& frame_size);
78
79   // Method that is scheduled on |task_runner_| to be called on regular interval
80   // to capture a frame.
81   void OnCaptureTimer();
82
83   // Captures a frame and schedules timer for the next one.
84   void CaptureFrameAndScheduleNext();
85
86   // Captures a single frame.
87   void DoCapture();
88
89   // Task runner used for capturing operations.
90   scoped_refptr<base::SequencedTaskRunner> task_runner_;
91
92   // The underlying DesktopCapturer instance used to capture frames.
93   scoped_ptr<webrtc::DesktopCapturer> desktop_capturer_;
94
95   // The device client which proxies device events to the controller. Accessed
96   // on the task_runner_ thread.
97   scoped_ptr<Client> client_;
98
99   // Requested video capture format (width, height, frame rate, etc).
100   media::VideoCaptureParams requested_params_;
101
102   // Actual video capture format being generated.
103   media::VideoCaptureFormat capture_format_;
104
105   // Size of frame most recently captured from the source.
106   webrtc::DesktopSize previous_frame_size_;
107
108   // DesktopFrame into which captured frames are down-scaled and/or letterboxed,
109   // depending upon the caller's requested capture capabilities. If frames can
110   // be returned to the caller directly then this is NULL.
111   scoped_ptr<webrtc::DesktopFrame> output_frame_;
112
113   // Sub-rectangle of |output_frame_| into which the source will be scaled
114   // and/or letterboxed.
115   webrtc::DesktopRect output_rect_;
116
117   // True when we have delayed OnCaptureTimer() task posted on
118   // |task_runner_|.
119   bool capture_task_posted_;
120
121   // True when waiting for |desktop_capturer_| to capture current frame.
122   bool capture_in_progress_;
123
124   DISALLOW_COPY_AND_ASSIGN(Core);
125 };
126
127 DesktopCaptureDevice::Core::Core(
128     scoped_refptr<base::SequencedTaskRunner> task_runner,
129     scoped_ptr<webrtc::DesktopCapturer> capturer)
130     : task_runner_(task_runner),
131       desktop_capturer_(capturer.Pass()),
132       capture_task_posted_(false),
133       capture_in_progress_(false) {}
134
135 DesktopCaptureDevice::Core::~Core() {
136 }
137
138 void DesktopCaptureDevice::Core::AllocateAndStart(
139     const media::VideoCaptureParams& params,
140     scoped_ptr<Client> client) {
141   DCHECK_GT(params.requested_format.frame_size.GetArea(), 0);
142   DCHECK_GT(params.requested_format.frame_rate, 0);
143
144   task_runner_->PostTask(
145       FROM_HERE,
146       base::Bind(
147           &Core::DoAllocateAndStart, this, params, base::Passed(&client)));
148 }
149
150 void DesktopCaptureDevice::Core::StopAndDeAllocate() {
151   task_runner_->PostTask(FROM_HERE,
152                          base::Bind(&Core::DoStopAndDeAllocate, this));
153 }
154
155 webrtc::SharedMemory*
156 DesktopCaptureDevice::Core::CreateSharedMemory(size_t size) {
157   return NULL;
158 }
159
160 void DesktopCaptureDevice::Core::OnCaptureCompleted(
161     webrtc::DesktopFrame* frame) {
162   DCHECK(task_runner_->RunsTasksOnCurrentThread());
163   DCHECK(capture_in_progress_);
164
165   capture_in_progress_ = false;
166
167   if (!frame) {
168     std::string log("Failed to capture a frame.");
169     LOG(ERROR) << log;
170     client_->OnError(log);
171     return;
172   }
173
174   if (!client_)
175     return;
176
177   scoped_ptr<webrtc::DesktopFrame> owned_frame(frame);
178
179   // Handle initial frame size and size changes.
180   RefreshCaptureFormat(frame->size());
181
182   webrtc::DesktopSize output_size(capture_format_.frame_size.width(),
183                                   capture_format_.frame_size.height());
184   size_t output_bytes = output_size.width() * output_size.height() *
185       webrtc::DesktopFrame::kBytesPerPixel;
186   const uint8_t* output_data = NULL;
187   scoped_ptr<uint8_t[]> flipped_frame_buffer;
188
189   if (frame->size().equals(output_size)) {
190     // If the captured frame matches the output size, we can return the pixel
191     // data directly, without scaling.
192     output_data = frame->data();
193
194     // If the |frame| generated by the screen capturer is inverted then we need
195     // to flip |frame|.
196     // This happens only on a specific platform. Refer to crbug.com/306876.
197     if (frame->stride() < 0) {
198       int height = frame->size().height();
199       int bytes_per_row =
200           frame->size().width() * webrtc::DesktopFrame::kBytesPerPixel;
201       flipped_frame_buffer.reset(new uint8_t[output_bytes]);
202       uint8_t* dest = flipped_frame_buffer.get();
203       for (int row = 0; row < height; ++row) {
204         memcpy(dest, output_data, bytes_per_row);
205         dest += bytes_per_row;
206         output_data += frame->stride();
207       }
208       output_data = flipped_frame_buffer.get();
209     }
210   } else {
211     // Otherwise we need to down-scale and/or letterbox to the target format.
212
213     // Allocate a buffer of the correct size to scale the frame into.
214     // |output_frame_| is cleared whenever |output_rect_| changes, so we don't
215     // need to worry about clearing out stale pixel data in letterboxed areas.
216     if (!output_frame_) {
217       output_frame_.reset(new webrtc::BasicDesktopFrame(output_size));
218       memset(output_frame_->data(), 0, output_bytes);
219     }
220     DCHECK(output_frame_->size().equals(output_size));
221
222     // TODO(wez): Optimize this to scale only changed portions of the output,
223     // using ARGBScaleClip().
224     uint8_t* output_rect_data = output_frame_->data() +
225         output_frame_->stride() * output_rect_.top() +
226         webrtc::DesktopFrame::kBytesPerPixel * output_rect_.left();
227     libyuv::ARGBScale(frame->data(), frame->stride(),
228                       frame->size().width(), frame->size().height(),
229                       output_rect_data, output_frame_->stride(),
230                       output_rect_.width(), output_rect_.height(),
231                       libyuv::kFilterBilinear);
232     output_data = output_frame_->data();
233   }
234
235   client_->OnIncomingCapturedFrame(
236       output_data, output_bytes, base::TimeTicks::Now(), 0, capture_format_);
237 }
238
239 void DesktopCaptureDevice::Core::DoAllocateAndStart(
240     const media::VideoCaptureParams& params,
241     scoped_ptr<Client> client) {
242   DCHECK(task_runner_->RunsTasksOnCurrentThread());
243   DCHECK(desktop_capturer_);
244   DCHECK(client.get());
245   DCHECK(!client_.get());
246
247   client_ = client.Pass();
248   requested_params_ = params;
249
250   capture_format_ = requested_params_.requested_format;
251
252   // This capturer always outputs ARGB, non-interlaced.
253   capture_format_.pixel_format = media::PIXEL_FORMAT_ARGB;
254
255   desktop_capturer_->Start(this);
256
257   CaptureFrameAndScheduleNext();
258 }
259
260 void DesktopCaptureDevice::Core::DoStopAndDeAllocate() {
261   DCHECK(task_runner_->RunsTasksOnCurrentThread());
262   client_.reset();
263   output_frame_.reset();
264   previous_frame_size_.set(0, 0);
265   desktop_capturer_.reset();
266 }
267
268 void DesktopCaptureDevice::Core::RefreshCaptureFormat(
269     const webrtc::DesktopSize& frame_size) {
270   if (previous_frame_size_.equals(frame_size))
271     return;
272
273   // Clear the output frame, if any, since it will either need resizing, or
274   // clearing of stale data in letterbox areas, anyway.
275   output_frame_.reset();
276
277   if (previous_frame_size_.is_empty() ||
278       requested_params_.allow_resolution_change) {
279     // If this is the first frame, or the receiver supports variable resolution
280     // then determine the output size by treating the requested width & height
281     // as maxima.
282     if (frame_size.width() >
283             requested_params_.requested_format.frame_size.width() ||
284         frame_size.height() >
285             requested_params_.requested_format.frame_size.height()) {
286       output_rect_ = ComputeLetterboxRect(
287           webrtc::DesktopSize(
288               requested_params_.requested_format.frame_size.width(),
289               requested_params_.requested_format.frame_size.height()),
290           frame_size);
291       output_rect_.Translate(-output_rect_.left(), -output_rect_.top());
292     } else {
293       output_rect_ = webrtc::DesktopRect::MakeSize(frame_size);
294     }
295     capture_format_.frame_size.SetSize(output_rect_.width(),
296                                        output_rect_.height());
297   } else {
298     // Otherwise the output frame size cannot change, so just scale and
299     // letterbox.
300     output_rect_ = ComputeLetterboxRect(
301         webrtc::DesktopSize(capture_format_.frame_size.width(),
302                             capture_format_.frame_size.height()),
303         frame_size);
304   }
305
306   previous_frame_size_ = frame_size;
307 }
308
309 void DesktopCaptureDevice::Core::OnCaptureTimer() {
310   DCHECK(capture_task_posted_);
311   capture_task_posted_ = false;
312
313   if (!client_)
314     return;
315
316   CaptureFrameAndScheduleNext();
317 }
318
319 void DesktopCaptureDevice::Core::CaptureFrameAndScheduleNext() {
320   DCHECK(task_runner_->RunsTasksOnCurrentThread());
321   DCHECK(!capture_task_posted_);
322
323   base::TimeTicks started_time = base::TimeTicks::Now();
324   DoCapture();
325   base::TimeDelta last_capture_duration = base::TimeTicks::Now() - started_time;
326
327   // Limit frame-rate to reduce CPU consumption.
328   base::TimeDelta capture_period = std::max(
329     (last_capture_duration * 100) / kMaximumCpuConsumptionPercentage,
330     base::TimeDelta::FromSeconds(1) / capture_format_.frame_rate);
331
332   // Schedule a task for the next frame.
333   capture_task_posted_ = true;
334   task_runner_->PostDelayedTask(
335       FROM_HERE, base::Bind(&Core::OnCaptureTimer, this),
336       capture_period - last_capture_duration);
337 }
338
339 void DesktopCaptureDevice::Core::DoCapture() {
340   DCHECK(task_runner_->RunsTasksOnCurrentThread());
341   DCHECK(!capture_in_progress_);
342
343   capture_in_progress_ = true;
344   desktop_capturer_->Capture(webrtc::DesktopRegion());
345
346   // Currently only synchronous implementations of DesktopCapturer are
347   // supported.
348   DCHECK(!capture_in_progress_);
349 }
350
351 // static
352 scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
353     const DesktopMediaID& source) {
354   scoped_refptr<base::SequencedWorkerPool> blocking_pool =
355       BrowserThread::GetBlockingPool();
356   scoped_refptr<base::SequencedTaskRunner> task_runner =
357       blocking_pool->GetSequencedTaskRunner(
358           blocking_pool->GetSequenceToken());
359
360   webrtc::DesktopCaptureOptions options =
361       webrtc::DesktopCaptureOptions::CreateDefault();
362   // Leave desktop effects enabled during WebRTC captures.
363   options.set_disable_effects(false);
364
365   scoped_ptr<webrtc::DesktopCapturer> capturer;
366
367   switch (source.type) {
368     case DesktopMediaID::TYPE_SCREEN: {
369       scoped_ptr<webrtc::ScreenCapturer> screen_capturer;
370       screen_capturer.reset(webrtc::ScreenCapturer::Create(options));
371       if (screen_capturer && screen_capturer->SelectScreen(source.id)) {
372         capturer.reset(new webrtc::DesktopAndCursorComposer(
373             screen_capturer.release(),
374             webrtc::MouseCursorMonitor::CreateForScreen(options, source.id)));
375       }
376       break;
377     }
378
379     case DesktopMediaID::TYPE_WINDOW: {
380       scoped_ptr<webrtc::WindowCapturer> window_capturer(
381           webrtc::WindowCapturer::Create(options));
382       if (window_capturer && window_capturer->SelectWindow(source.id)) {
383         capturer.reset(new webrtc::DesktopAndCursorComposer(
384             window_capturer.release(),
385             webrtc::MouseCursorMonitor::CreateForWindow(options, source.id)));
386       }
387       break;
388     }
389
390     default: {
391       NOTREACHED();
392     }
393   }
394
395   scoped_ptr<media::VideoCaptureDevice> result;
396   if (capturer)
397     result.reset(new DesktopCaptureDevice(task_runner, capturer.Pass()));
398
399   return result.Pass();
400 }
401
402 DesktopCaptureDevice::DesktopCaptureDevice(
403     scoped_refptr<base::SequencedTaskRunner> task_runner,
404     scoped_ptr<webrtc::DesktopCapturer> capturer)
405     : core_(new Core(task_runner, capturer.Pass())) {}
406
407 DesktopCaptureDevice::~DesktopCaptureDevice() {
408   StopAndDeAllocate();
409 }
410
411 void DesktopCaptureDevice::AllocateAndStart(
412     const media::VideoCaptureParams& params,
413     scoped_ptr<Client> client) {
414   core_->AllocateAndStart(params, client.Pass());
415 }
416
417 void DesktopCaptureDevice::StopAndDeAllocate() {
418   core_->StopAndDeAllocate();
419 }
420
421 }  // namespace content