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

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

Issue 1135823004: Implement all resolution change policies for desktop and tab capture. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@resolution_change_policy_constraints_ITEM1_CR1
Patch Set: Addressed Dale's comments. Created 5 years, 7 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
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/media/capture/desktop_capture_device.h" 5 #include "content/browser/media/capture/desktop_capture_device.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/location.h" 8 #include "base/location.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_number_conversions.h"
12 #include "base/synchronization/lock.h" 12 #include "base/synchronization/lock.h"
13 #include "base/threading/thread.h" 13 #include "base/threading/thread.h"
14 #include "base/timer/timer.h" 14 #include "base/timer/timer.h"
15 #include "content/browser/media/capture/capture_resolution_evaluator.h"
15 #include "content/browser/media/capture/desktop_capture_device_uma_types.h" 16 #include "content/browser/media/capture/desktop_capture_device_uma_types.h"
16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/desktop_media_id.h" 18 #include "content/public/browser/desktop_media_id.h"
18 #include "content/public/browser/power_save_blocker.h" 19 #include "content/public/browser/power_save_blocker.h"
19 #include "media/base/video_util.h" 20 #include "media/base/video_util.h"
20 #include "third_party/libyuv/include/libyuv/scale_argb.h" 21 #include "third_party/libyuv/include/libyuv/scale_argb.h"
21 #include "third_party/webrtc/modules/desktop_capture/cropping_window_capturer.h" 22 #include "third_party/webrtc/modules/desktop_capture/cropping_window_capturer.h"
22 #include "third_party/webrtc/modules/desktop_capture/desktop_and_cursor_composer .h" 23 #include "third_party/webrtc/modules/desktop_capture/desktop_and_cursor_composer .h"
23 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" 24 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
24 #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" 25 #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 scoped_ptr<Client> client); 66 scoped_ptr<Client> client);
66 67
67 void SetNotificationWindowId(gfx::NativeViewId window_id); 68 void SetNotificationWindowId(gfx::NativeViewId window_id);
68 69
69 private: 70 private:
70 71
71 // webrtc::DesktopCapturer::Callback interface 72 // webrtc::DesktopCapturer::Callback interface
72 webrtc::SharedMemory* CreateSharedMemory(size_t size) override; 73 webrtc::SharedMemory* CreateSharedMemory(size_t size) override;
73 void OnCaptureCompleted(webrtc::DesktopFrame* frame) override; 74 void OnCaptureCompleted(webrtc::DesktopFrame* frame) override;
74 75
75 // Chooses new output properties based on the supplied source size and the
76 // properties requested to Allocate(), and dispatches OnFrameInfo[Changed]
77 // notifications.
78 void RefreshCaptureFormat(const webrtc::DesktopSize& frame_size);
79
80 // Method that is scheduled on |task_runner_| to be called on regular interval 76 // Method that is scheduled on |task_runner_| to be called on regular interval
81 // to capture a frame. 77 // to capture a frame.
82 void OnCaptureTimer(); 78 void OnCaptureTimer();
83 79
84 // Captures a frame and schedules timer for the next one. 80 // Captures a frame and schedules timer for the next one.
85 void CaptureFrameAndScheduleNext(); 81 void CaptureFrameAndScheduleNext();
86 82
87 // Captures a single frame. 83 // Captures a single frame.
88 void DoCapture(); 84 void DoCapture();
89 85
90 // Task runner used for capturing operations. 86 // Task runner used for capturing operations.
91 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; 87 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
92 88
93 // The underlying DesktopCapturer instance used to capture frames. 89 // The underlying DesktopCapturer instance used to capture frames.
94 scoped_ptr<webrtc::DesktopCapturer> desktop_capturer_; 90 scoped_ptr<webrtc::DesktopCapturer> desktop_capturer_;
95 91
96 // The device client which proxies device events to the controller. Accessed 92 // The device client which proxies device events to the controller. Accessed
97 // on the task_runner_ thread. 93 // on the task_runner_ thread.
98 scoped_ptr<Client> client_; 94 scoped_ptr<Client> client_;
99 95
100 // Requested video capture format (width, height, frame rate, etc). 96 // Requested video capture frame rate.
101 media::VideoCaptureParams requested_params_; 97 float requested_frame_rate_;
102
103 // Actual video capture format being generated.
104 media::VideoCaptureFormat capture_format_;
105 98
106 // Size of frame most recently captured from the source. 99 // Size of frame most recently captured from the source.
107 webrtc::DesktopSize previous_frame_size_; 100 webrtc::DesktopSize previous_frame_size_;
108 101
102 // Determines the size of frames to deliver to the |client_|.
103 scoped_ptr<CaptureResolutionEvaluator> resolution_evaluator_;
104
109 // DesktopFrame into which captured frames are down-scaled and/or letterboxed, 105 // DesktopFrame into which captured frames are down-scaled and/or letterboxed,
110 // depending upon the caller's requested capture capabilities. If frames can 106 // depending upon the caller's requested capture capabilities. If frames can
111 // be returned to the caller directly then this is NULL. 107 // be returned to the caller directly then this is NULL.
112 scoped_ptr<webrtc::DesktopFrame> output_frame_; 108 scoped_ptr<webrtc::DesktopFrame> output_frame_;
113 109
114 // Sub-rectangle of |output_frame_| into which the source will be scaled
115 // and/or letterboxed.
116 webrtc::DesktopRect output_rect_;
117
118 // Timer used to capture the frame. 110 // Timer used to capture the frame.
119 base::OneShotTimer<Core> capture_timer_; 111 base::OneShotTimer<Core> capture_timer_;
120 112
121 // True when waiting for |desktop_capturer_| to capture current frame. 113 // True when waiting for |desktop_capturer_| to capture current frame.
122 bool capture_in_progress_; 114 bool capture_in_progress_;
123 115
124 // True if the first capture call has returned. Used to log the first capture 116 // True if the first capture call has returned. Used to log the first capture
125 // result. 117 // result.
126 bool first_capture_returned_; 118 bool first_capture_returned_;
127 119
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 const media::VideoCaptureParams& params, 152 const media::VideoCaptureParams& params,
161 scoped_ptr<Client> client) { 153 scoped_ptr<Client> client) {
162 DCHECK(task_runner_->BelongsToCurrentThread()); 154 DCHECK(task_runner_->BelongsToCurrentThread());
163 DCHECK_GT(params.requested_format.frame_size.GetArea(), 0); 155 DCHECK_GT(params.requested_format.frame_size.GetArea(), 0);
164 DCHECK_GT(params.requested_format.frame_rate, 0); 156 DCHECK_GT(params.requested_format.frame_rate, 0);
165 DCHECK(desktop_capturer_); 157 DCHECK(desktop_capturer_);
166 DCHECK(client.get()); 158 DCHECK(client.get());
167 DCHECK(!client_.get()); 159 DCHECK(!client_.get());
168 160
169 client_ = client.Pass(); 161 client_ = client.Pass();
170 requested_params_ = params; 162 requested_frame_rate_ = params.requested_format.frame_rate;
171 163 resolution_evaluator_.reset(new CaptureResolutionEvaluator(
172 capture_format_ = requested_params_.requested_format; 164 params.requested_format.frame_size,
173 165 params.resolution_change_policy));
174 // This capturer always outputs ARGB, non-interlaced.
175 capture_format_.pixel_format = media::PIXEL_FORMAT_ARGB;
176 166
177 power_save_blocker_.reset( 167 power_save_blocker_.reset(
178 PowerSaveBlocker::Create( 168 PowerSaveBlocker::Create(
179 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, 169 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
180 PowerSaveBlocker::kReasonOther, 170 PowerSaveBlocker::kReasonOther,
181 "DesktopCaptureDevice is running").release()); 171 "DesktopCaptureDevice is running").release());
182 172
183 desktop_capturer_->Start(this); 173 desktop_capturer_->Start(this);
184 174
185 CaptureFrameAndScheduleNext(); 175 CaptureFrameAndScheduleNext();
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 // The two UMA_ blocks must be put in its own scope since it creates a static 221 // The two UMA_ blocks must be put in its own scope since it creates a static
232 // variable which expected constant histogram name. 222 // variable which expected constant histogram name.
233 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) { 223 if (capturer_type_ == DesktopMediaID::TYPE_SCREEN) {
234 UMA_HISTOGRAM_TIMES(kUmaScreenCaptureTime, capture_time); 224 UMA_HISTOGRAM_TIMES(kUmaScreenCaptureTime, capture_time);
235 } else { 225 } else {
236 UMA_HISTOGRAM_TIMES(kUmaWindowCaptureTime, capture_time); 226 UMA_HISTOGRAM_TIMES(kUmaWindowCaptureTime, capture_time);
237 } 227 }
238 228
239 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); 229 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame);
240 230
231 // If the frame size has changed, drop the output frame (if any), and
232 // determine the new output size.
233 if (!previous_frame_size_.equals(frame->size())) {
234 output_frame_.reset();
235 resolution_evaluator_->UpdateForNewSourceSize(
236 gfx::Size(frame->size().width(), frame->size().height()));
237 previous_frame_size_ = frame->size();
238 }
239 // Align to 2x2 pixel boundaries, as required by OnIncomingCapturedData() so
240 // it can convert the frame to I420 format.
241 const webrtc::DesktopSize output_size(
242 resolution_evaluator_->capture_size().width() & ~1,
243 resolution_evaluator_->capture_size().height() & ~1);
244 if (output_size.is_empty())
245 return;
246
241 // On OSX We receive a 1x1 frame when the shared window is minimized. It 247 // On OSX We receive a 1x1 frame when the shared window is minimized. It
242 // cannot be subsampled to I420 and will be dropped downstream. So we replace 248 // cannot be subsampled to I420 and will be dropped downstream. So we replace
243 // it with a black frame to avoid the video appearing frozen at the last 249 // it with a black frame to avoid the video appearing frozen at the last
244 // frame. 250 // frame.
245 if (frame->size().width() == 1 || frame->size().height() == 1) { 251 if (frame->size().width() == 1 || frame->size().height() == 1) {
246 if (!black_frame_.get()) { 252 if (!black_frame_.get()) {
247 black_frame_.reset( 253 black_frame_.reset(new webrtc::BasicDesktopFrame(output_size));
248 new webrtc::BasicDesktopFrame(
249 webrtc::DesktopSize(capture_format_.frame_size.width(),
250 capture_format_.frame_size.height())));
251 memset(black_frame_->data(), 254 memset(black_frame_->data(),
252 0, 255 0,
253 black_frame_->stride() * black_frame_->size().height()); 256 black_frame_->stride() * black_frame_->size().height());
254 } 257 }
255 owned_frame.reset(); 258 owned_frame.reset();
256 frame = black_frame_.get(); 259 frame = black_frame_.get();
257 } 260 }
258 261
259 // Handle initial frame size and size changes.
260 RefreshCaptureFormat(frame->size());
261
262 webrtc::DesktopSize output_size(capture_format_.frame_size.width(),
263 capture_format_.frame_size.height());
264 size_t output_bytes = output_size.width() * output_size.height() * 262 size_t output_bytes = output_size.width() * output_size.height() *
265 webrtc::DesktopFrame::kBytesPerPixel; 263 webrtc::DesktopFrame::kBytesPerPixel;
266 const uint8_t* output_data = NULL; 264 const uint8_t* output_data = NULL;
267 265
268 if (!frame->size().equals(output_size)) { 266 if (!frame->size().equals(output_size)) {
269 // Down-scale and/or letterbox to the target format if the frame does not 267 // Down-scale and/or letterbox to the target format if the frame does not
270 // match the output size. 268 // match the output size.
271 269
272 // Allocate a buffer of the correct size to scale the frame into. 270 // Allocate a buffer of the correct size to scale the frame into.
273 // |output_frame_| is cleared whenever |output_rect_| changes, so we don't 271 // |output_frame_| is cleared whenever the output size changes, so we don't
274 // need to worry about clearing out stale pixel data in letterboxed areas. 272 // need to worry about clearing out stale pixel data in letterboxed areas.
275 if (!output_frame_) { 273 if (!output_frame_) {
276 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); 274 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size));
277 memset(output_frame_->data(), 0, output_bytes); 275 memset(output_frame_->data(), 0, output_bytes);
278 } 276 }
279 DCHECK(output_frame_->size().equals(output_size)); 277 DCHECK(output_frame_->size().equals(output_size));
280 278
281 // TODO(wez): Optimize this to scale only changed portions of the output, 279 // TODO(wez): Optimize this to scale only changed portions of the output,
282 // using ARGBScaleClip(). 280 // using ARGBScaleClip().
281 const webrtc::DesktopRect output_rect =
282 ComputeLetterboxRect(output_size, frame->size());
283 uint8_t* output_rect_data = output_frame_->data() + 283 uint8_t* output_rect_data = output_frame_->data() +
284 output_frame_->stride() * output_rect_.top() + 284 output_frame_->stride() * output_rect.top() +
285 webrtc::DesktopFrame::kBytesPerPixel * output_rect_.left(); 285 webrtc::DesktopFrame::kBytesPerPixel * output_rect.left();
286 libyuv::ARGBScale(frame->data(), frame->stride(), 286 libyuv::ARGBScale(frame->data(), frame->stride(),
287 frame->size().width(), frame->size().height(), 287 frame->size().width(), frame->size().height(),
288 output_rect_data, output_frame_->stride(), 288 output_rect_data, output_frame_->stride(),
289 output_rect_.width(), output_rect_.height(), 289 output_rect.width(), output_rect.height(),
290 libyuv::kFilterBilinear); 290 libyuv::kFilterBilinear);
291 output_data = output_frame_->data(); 291 output_data = output_frame_->data();
292 } else if (IsFrameUnpackedOrInverted(frame)) { 292 } else if (IsFrameUnpackedOrInverted(frame)) {
293 // If |frame| is not packed top-to-bottom then create a packed top-to-bottom 293 // If |frame| is not packed top-to-bottom then create a packed top-to-bottom
294 // copy. 294 // copy.
295 // This is required if the frame is inverted (see crbug.com/306876), or if 295 // This is required if the frame is inverted (see crbug.com/306876), or if
296 // |frame| is cropped form a larger frame (see crbug.com/437740). 296 // |frame| is cropped form a larger frame (see crbug.com/437740).
297 if (!output_frame_) { 297 if (!output_frame_) {
298 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size)); 298 output_frame_.reset(new webrtc::BasicDesktopFrame(output_size));
299 memset(output_frame_->data(), 0, output_bytes); 299 memset(output_frame_->data(), 0, output_bytes);
300 } 300 }
301 301
302 output_frame_->CopyPixelsFrom( 302 output_frame_->CopyPixelsFrom(
303 *frame, 303 *frame,
304 webrtc::DesktopVector(), 304 webrtc::DesktopVector(),
305 webrtc::DesktopRect::MakeSize(frame->size())); 305 webrtc::DesktopRect::MakeSize(frame->size()));
306 output_data = output_frame_->data(); 306 output_data = output_frame_->data();
307 } else { 307 } else {
308 // If the captured frame matches the output size, we can return the pixel 308 // If the captured frame matches the output size, we can return the pixel
309 // data directly. 309 // data directly.
310 output_data = frame->data(); 310 output_data = frame->data();
311 } 311 }
312 312
313 client_->OnIncomingCapturedData( 313 client_->OnIncomingCapturedData(
314 output_data, output_bytes, capture_format_, 0, base::TimeTicks::Now()); 314 output_data,
315 } 315 output_bytes,
316 316 media::VideoCaptureFormat(gfx::Size(output_size.width(),
317 void DesktopCaptureDevice::Core::RefreshCaptureFormat( 317 output_size.height()),
318 const webrtc::DesktopSize& frame_size) { 318 requested_frame_rate_,
319 if (previous_frame_size_.equals(frame_size)) 319 media::PIXEL_FORMAT_ARGB),
320 return; 320 0,
321 321 base::TimeTicks::Now());
322 // Clear the output frame, if any, since it will either need resizing, or
323 // clearing of stale data in letterbox areas, anyway.
324 output_frame_.reset();
325
326 if (previous_frame_size_.is_empty() ||
327 requested_params_.resolution_change_policy ==
328 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT) {
329 // If this is the first frame, or the receiver supports variable resolution
330 // then determine the output size by treating the requested width & height
331 // as maxima.
332 if (frame_size.width() >
333 requested_params_.requested_format.frame_size.width() ||
334 frame_size.height() >
335 requested_params_.requested_format.frame_size.height()) {
336 output_rect_ = ComputeLetterboxRect(
337 webrtc::DesktopSize(
338 requested_params_.requested_format.frame_size.width(),
339 requested_params_.requested_format.frame_size.height()),
340 frame_size);
341 output_rect_.Translate(-output_rect_.left(), -output_rect_.top());
342 } else {
343 output_rect_ = webrtc::DesktopRect::MakeSize(frame_size);
344 }
345 capture_format_.frame_size.SetSize(output_rect_.width(),
346 output_rect_.height());
347 } else {
348 // Otherwise the output frame size cannot change, so just scale and
349 // letterbox.
350 output_rect_ = ComputeLetterboxRect(
351 webrtc::DesktopSize(capture_format_.frame_size.width(),
352 capture_format_.frame_size.height()),
353 frame_size);
354 }
355
356 previous_frame_size_ = frame_size;
357 } 322 }
358 323
359 void DesktopCaptureDevice::Core::OnCaptureTimer() { 324 void DesktopCaptureDevice::Core::OnCaptureTimer() {
360 DCHECK(task_runner_->BelongsToCurrentThread()); 325 DCHECK(task_runner_->BelongsToCurrentThread());
361 326
362 if (!client_) 327 if (!client_)
363 return; 328 return;
364 329
365 CaptureFrameAndScheduleNext(); 330 CaptureFrameAndScheduleNext();
366 } 331 }
367 332
368 void DesktopCaptureDevice::Core::CaptureFrameAndScheduleNext() { 333 void DesktopCaptureDevice::Core::CaptureFrameAndScheduleNext() {
369 DCHECK(task_runner_->BelongsToCurrentThread()); 334 DCHECK(task_runner_->BelongsToCurrentThread());
370 335
371 base::TimeTicks started_time = base::TimeTicks::Now(); 336 base::TimeTicks started_time = base::TimeTicks::Now();
372 DoCapture(); 337 DoCapture();
373 base::TimeDelta last_capture_duration = base::TimeTicks::Now() - started_time; 338 base::TimeDelta last_capture_duration = base::TimeTicks::Now() - started_time;
374 339
375 // Limit frame-rate to reduce CPU consumption. 340 // Limit frame-rate to reduce CPU consumption.
376 base::TimeDelta capture_period = std::max( 341 base::TimeDelta capture_period = std::max(
377 (last_capture_duration * 100) / kMaximumCpuConsumptionPercentage, 342 (last_capture_duration * 100) / kMaximumCpuConsumptionPercentage,
378 base::TimeDelta::FromSeconds(1) / capture_format_.frame_rate); 343 base::TimeDelta::FromMicroseconds(static_cast<int64>(
344 1000000.0 / requested_frame_rate_ + 0.5 /* round to nearest int */)));
379 345
380 // Schedule a task for the next frame. 346 // Schedule a task for the next frame.
381 capture_timer_.Start(FROM_HERE, capture_period - last_capture_duration, 347 capture_timer_.Start(FROM_HERE, capture_period - last_capture_duration,
382 this, &Core::OnCaptureTimer); 348 this, &Core::OnCaptureTimer);
383 } 349 }
384 350
385 void DesktopCaptureDevice::Core::DoCapture() { 351 void DesktopCaptureDevice::Core::DoCapture() {
386 DCHECK(task_runner_->BelongsToCurrentThread()); 352 DCHECK(task_runner_->BelongsToCurrentThread());
387 DCHECK(!capture_in_progress_); 353 DCHECK(!capture_in_progress_);
388 354
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
488 #else 454 #else
489 base::MessageLoop::Type thread_type = base::MessageLoop::TYPE_DEFAULT; 455 base::MessageLoop::Type thread_type = base::MessageLoop::TYPE_DEFAULT;
490 #endif 456 #endif
491 457
492 thread_.StartWithOptions(base::Thread::Options(thread_type, 0)); 458 thread_.StartWithOptions(base::Thread::Options(thread_type, 0));
493 459
494 core_.reset(new Core(thread_.task_runner(), capturer.Pass(), type)); 460 core_.reset(new Core(thread_.task_runner(), capturer.Pass(), type));
495 } 461 }
496 462
497 } // namespace content 463 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698