Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(332)

Side by Side Diff: content/browser/renderer_host/media/desktop_capture_device.cc

Issue 172003004: Fixit: Move tab and desktop capture code to new location. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698