Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / media / capture / web_contents_video_capture_device.cc
blob8380682fc5064f11b55813af04cb4549994a244e
1 // Copyright (c) 2012 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 // Implementation notes: This needs to work on a variety of hardware
6 // configurations where the speed of the CPU and GPU greatly affect overall
7 // performance. Spanning several threads, the process of capturing has been
8 // split up into four conceptual stages:
9 //
10 // 1. Reserve Buffer: Before a frame can be captured, a slot in the client's
11 // shared-memory IPC buffer is reserved. There are only a few of these;
12 // when they run out, it indicates that the downstream client -- likely a
13 // video encoder -- is the performance bottleneck, and that the rate of
14 // frame capture should be throttled back.
16 // 2. Capture: A bitmap is snapshotted/copied from the RenderWidget's backing
17 // store. This is initiated on the UI BrowserThread, and often occurs
18 // asynchronously. Where supported, the GPU scales and color converts
19 // frames to our desired size, and the readback happens directly into the
20 // shared-memory buffer. But this is not always possible, particularly when
21 // accelerated compositing is disabled.
23 // 3. Render (if needed): If the web contents cannot be captured directly into
24 // our target size and color format, scaling and colorspace conversion must
25 // be done on the CPU. A dedicated thread is used for this operation, to
26 // avoid blocking the UI thread. The Render stage always reads from a
27 // bitmap returned by Capture, and writes into the reserved slot in the
28 // shared-memory buffer.
30 // 4. Deliver: The rendered video frame is returned to the client (which
31 // implements the VideoCaptureDevice::Client interface). Because all
32 // paths have written the frame into the IPC buffer, this step should
33 // never need to do an additional copy of the pixel data.
35 // In the best-performing case, the Render step is bypassed: Capture produces
36 // ready-to-Deliver frames. But when accelerated readback is not possible, the
37 // system is designed so that Capture and Render may run concurrently. A timing
38 // diagram helps illustrate this point (@30 FPS):
40 // Time: 0ms 33ms 66ms 99ms
41 // thread1: |-Capture-f1------v |-Capture-f2------v |-Capture-f3----v |-Capt
42 // thread2: |-Render-f1-----v |-Render-f2-----v |-Render-f3
44 // In the above example, both capturing and rendering *each* take almost the
45 // full 33 ms available between frames, yet we see that the required throughput
46 // is obtained.
48 // Turning on verbose logging will cause the effective frame rate to be logged
49 // at 5-second intervals.
51 #include "content/browser/media/capture/web_contents_video_capture_device.h"
53 #include "base/basictypes.h"
54 #include "base/bind.h"
55 #include "base/callback_helpers.h"
56 #include "base/logging.h"
57 #include "base/memory/scoped_ptr.h"
58 #include "base/memory/weak_ptr.h"
59 #include "base/message_loop/message_loop_proxy.h"
60 #include "base/metrics/histogram.h"
61 #include "base/sequenced_task_runner.h"
62 #include "base/threading/thread.h"
63 #include "base/threading/thread_checker.h"
64 #include "base/time/time.h"
65 #include "content/browser/media/capture/content_video_capture_device_core.h"
66 #include "content/browser/media/capture/video_capture_oracle.h"
67 #include "content/browser/media/capture/web_contents_capture_util.h"
68 #include "content/browser/media/capture/web_contents_tracker.h"
69 #include "content/browser/renderer_host/render_widget_host_impl.h"
70 #include "content/browser/renderer_host/render_widget_host_view_base.h"
71 #include "content/public/browser/browser_thread.h"
72 #include "content/public/browser/render_process_host.h"
73 #include "content/public/browser/render_widget_host_view.h"
74 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
75 #include "content/public/browser/web_contents.h"
76 #include "media/base/video_capture_types.h"
77 #include "media/base/video_util.h"
78 #include "skia/ext/image_operations.h"
79 #include "third_party/skia/include/core/SkBitmap.h"
80 #include "third_party/skia/include/core/SkColor.h"
81 #include "ui/gfx/display.h"
82 #include "ui/gfx/geometry/size.h"
83 #include "ui/gfx/geometry/size_conversions.h"
84 #include "ui/gfx/screen.h"
86 namespace content {
88 namespace {
90 // Compute a letterbox region, aligned to even coordinates.
91 gfx::Rect ComputeYV12LetterboxRegion(const gfx::Rect& visible_rect,
92 const gfx::Size& content_size) {
94 gfx::Rect result = media::ComputeLetterboxRegion(visible_rect, content_size);
96 result.set_x(MakeEven(result.x()));
97 result.set_y(MakeEven(result.y()));
98 result.set_width(std::max(kMinFrameWidth, MakeEven(result.width())));
99 result.set_height(std::max(kMinFrameHeight, MakeEven(result.height())));
101 return result;
104 void DeleteOnWorkerThread(scoped_ptr<base::Thread> render_thread,
105 const base::Closure& callback) {
106 render_thread.reset();
108 // After thread join call the callback on UI thread.
109 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
112 // Responsible for logging the effective frame rate.
113 class VideoFrameDeliveryLog {
114 public:
115 VideoFrameDeliveryLog();
117 // Report that the frame posted with |frame_time| has been delivered.
118 void ChronicleFrameDelivery(base::TimeTicks frame_time);
120 private:
121 // The following keep track of and log the effective frame rate whenever
122 // verbose logging is turned on.
123 base::TimeTicks last_frame_rate_log_time_;
124 int count_frames_rendered_;
126 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
129 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible
130 // with RenderWidgetHostViewFrameSubscriber. We create one per event type.
131 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
132 public:
133 FrameSubscriber(VideoCaptureOracle::Event event_type,
134 const scoped_refptr<ThreadSafeCaptureOracle>& oracle,
135 VideoFrameDeliveryLog* delivery_log)
136 : event_type_(event_type),
137 oracle_proxy_(oracle),
138 delivery_log_(delivery_log) {}
140 bool ShouldCaptureFrame(
141 const gfx::Rect& damage_rect,
142 base::TimeTicks present_time,
143 scoped_refptr<media::VideoFrame>* storage,
144 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback*
145 deliver_frame_cb) override;
147 private:
148 const VideoCaptureOracle::Event event_type_;
149 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
150 VideoFrameDeliveryLog* const delivery_log_;
153 // ContentCaptureSubscription is the relationship between a RenderWidgetHost
154 // whose content is updating, a subscriber that is deciding which of these
155 // updates to capture (and where to deliver them to), and a callback that
156 // knows how to do the capture and prepare the result for delivery.
158 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in
159 // the RenderWidgetHostView, to process compositor updates, and (b) running a
160 // timer to possibly initiate forced, non-event-driven captures needed by
161 // downstream consumers that require frame repeats of unchanged content.
163 // All of this happens on the UI thread, although the
164 // RenderWidgetHostViewFrameSubscriber we install may be dispatching updates
165 // autonomously on some other thread.
166 class ContentCaptureSubscription {
167 public:
168 typedef base::Callback<
169 void(const base::TimeTicks&,
170 const scoped_refptr<media::VideoFrame>&,
171 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)>
172 CaptureCallback;
174 // Create a subscription. Whenever a manual capture is required, the
175 // subscription will invoke |capture_callback| on the UI thread to do the
176 // work.
177 ContentCaptureSubscription(
178 const RenderWidgetHost& source,
179 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
180 const CaptureCallback& capture_callback);
181 ~ContentCaptureSubscription();
183 private:
184 void OnTimer();
186 // Maintain a weak reference to the RenderWidgetHost (via its routing ID),
187 // since the instance could be destroyed externally during the lifetime of
188 // |this|.
189 const int render_process_id_;
190 const int render_widget_id_;
192 VideoFrameDeliveryLog delivery_log_;
193 FrameSubscriber timer_subscriber_;
194 CaptureCallback capture_callback_;
195 base::Timer timer_;
197 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription);
200 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then
201 // invoke |done_cb| to indicate success or failure. |input| is expected to be
202 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done.
203 // Scaling and letterboxing will be done as needed.
205 // This software implementation should be used only when GPU acceleration of
206 // these activities is not possible. This operation may be expensive (tens to
207 // hundreds of milliseconds), so the caller should ensure that it runs on a
208 // thread where such a pause would cause UI jank.
209 void RenderVideoFrame(const SkBitmap& input,
210 const scoped_refptr<media::VideoFrame>& output,
211 const base::Callback<void(bool)>& done_cb);
213 // Renews capture subscriptions based on feedback from WebContentsTracker, and
214 // also executes copying of the backing store on the UI BrowserThread.
215 class WebContentsCaptureMachine : public VideoCaptureMachine {
216 public:
217 WebContentsCaptureMachine(int render_process_id, int main_render_frame_id);
218 ~WebContentsCaptureMachine() override;
220 // VideoCaptureMachine overrides.
221 bool Start(const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
222 const media::VideoCaptureParams& params) override;
223 void Stop(const base::Closure& callback) override;
225 // Starts a copy from the backing store or the composited surface. Must be run
226 // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation
227 // completes. The copy will occur to |target|.
229 // This may be used as a ContentCaptureSubscription::CaptureCallback.
230 void Capture(const base::TimeTicks& start_time,
231 const scoped_refptr<media::VideoFrame>& target,
232 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
233 deliver_frame_cb);
235 private:
236 bool IsStarted() const;
238 // Computes the preferred size of the target RenderWidget for optimal capture.
239 gfx::Size ComputeOptimalTargetSize() const;
241 // Response callback for RenderWidgetHost::CopyFromBackingStore().
242 void DidCopyFromBackingStore(
243 const base::TimeTicks& start_time,
244 const scoped_refptr<media::VideoFrame>& target,
245 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
246 deliver_frame_cb,
247 const SkBitmap& bitmap,
248 ReadbackResponse response);
250 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
251 void DidCopyFromCompositingSurfaceToVideoFrame(
252 const base::TimeTicks& start_time,
253 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
254 deliver_frame_cb,
255 bool success);
257 // Remove the old subscription, and start a new one if |rwh| is not NULL.
258 void RenewFrameSubscription(RenderWidgetHost* rwh);
260 // Parameters saved in constructor.
261 const int initial_render_process_id_;
262 const int initial_main_render_frame_id_;
264 // Tracks events and calls back to RenewFrameSubscription() to maintain
265 // capture on the correct RenderWidgetHost.
266 const scoped_refptr<WebContentsTracker> tracker_;
268 // A dedicated worker thread on which SkBitmap->VideoFrame conversion will
269 // occur. Only used when this activity cannot be done on the GPU.
270 scoped_ptr<base::Thread> render_thread_;
272 // Makes all the decisions about which frames to copy, and how.
273 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
275 // Video capture parameters that this machine is started with.
276 media::VideoCaptureParams capture_params_;
278 // Last known RenderView size.
279 gfx::Size last_view_size_;
281 // Responsible for forwarding events from the active RenderWidgetHost to the
282 // oracle, and initiating captures accordingly.
283 scoped_ptr<ContentCaptureSubscription> subscription_;
285 // Weak pointer factory used to invalidate callbacks.
286 // NOTE: Weak pointers must be invalidated before all other member variables.
287 base::WeakPtrFactory<WebContentsCaptureMachine> weak_ptr_factory_;
289 DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine);
292 bool FrameSubscriber::ShouldCaptureFrame(
293 const gfx::Rect& damage_rect,
294 base::TimeTicks present_time,
295 scoped_refptr<media::VideoFrame>* storage,
296 DeliverFrameCallback* deliver_frame_cb) {
297 TRACE_EVENT1("gpu.capture", "FrameSubscriber::ShouldCaptureFrame",
298 "instance", this);
300 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
301 bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture(
302 event_type_, damage_rect, present_time, storage, &capture_frame_cb);
304 if (!capture_frame_cb.is_null())
305 *deliver_frame_cb = base::Bind(capture_frame_cb, *storage);
306 if (oracle_decision)
307 delivery_log_->ChronicleFrameDelivery(present_time);
308 return oracle_decision;
311 ContentCaptureSubscription::ContentCaptureSubscription(
312 const RenderWidgetHost& source,
313 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
314 const CaptureCallback& capture_callback)
315 : render_process_id_(source.GetProcess()->GetID()),
316 render_widget_id_(source.GetRoutingID()),
317 delivery_log_(),
318 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy,
319 &delivery_log_),
320 capture_callback_(capture_callback),
321 timer_(true, true) {
322 DCHECK_CURRENTLY_ON(BrowserThread::UI);
324 RenderWidgetHostView* const view = source.GetView();
326 // Subscribe to compositor updates. These will be serviced directly by the
327 // oracle.
328 if (view) {
329 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
330 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate,
331 oracle_proxy, &delivery_log_));
332 view->BeginFrameSubscription(subscriber.Pass());
335 // Subscribe to timer events. This instance will service these as well.
336 timer_.Start(FROM_HERE, oracle_proxy->min_capture_period(),
337 base::Bind(&ContentCaptureSubscription::OnTimer,
338 base::Unretained(this)));
341 ContentCaptureSubscription::~ContentCaptureSubscription() {
342 // If the BrowserThreads have been torn down, then the browser is in the final
343 // stages of exiting and it is dangerous to take any further action. We must
344 // return early. http://crbug.com/396413
345 if (!BrowserThread::IsMessageLoopValid(BrowserThread::UI))
346 return;
348 DCHECK_CURRENTLY_ON(BrowserThread::UI);
349 RenderWidgetHost* const source =
350 RenderWidgetHost::FromID(render_process_id_, render_widget_id_);
351 RenderWidgetHostView* const view = source ? source->GetView() : NULL;
352 if (view)
353 view->EndFrameSubscription();
356 void ContentCaptureSubscription::OnTimer() {
357 DCHECK_CURRENTLY_ON(BrowserThread::UI);
358 TRACE_EVENT0("gpu.capture", "ContentCaptureSubscription::OnTimer");
360 scoped_refptr<media::VideoFrame> frame;
361 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
363 const base::TimeTicks start_time = base::TimeTicks::Now();
364 if (timer_subscriber_.ShouldCaptureFrame(gfx::Rect(),
365 start_time,
366 &frame,
367 &deliver_frame_cb)) {
368 capture_callback_.Run(start_time, frame, deliver_frame_cb);
372 void RenderVideoFrame(const SkBitmap& input,
373 const scoped_refptr<media::VideoFrame>& output,
374 const base::Callback<void(bool)>& done_cb) {
375 base::ScopedClosureRunner failure_handler(base::Bind(done_cb, false));
377 SkAutoLockPixels locker(input);
379 // Sanity-check the captured bitmap.
380 if (input.empty() ||
381 !input.readyToDraw() ||
382 input.colorType() != kN32_SkColorType ||
383 input.width() < 2 || input.height() < 2) {
384 DVLOG(1) << "input unacceptable (size="
385 << input.getSize()
386 << ", ready=" << input.readyToDraw()
387 << ", colorType=" << input.colorType() << ')';
388 return;
391 // Sanity-check the output buffer.
392 if (output->format() != media::VideoFrame::I420) {
393 NOTREACHED();
394 return;
397 // Calculate the width and height of the content region in the |output|, based
398 // on the aspect ratio of |input|.
399 gfx::Rect region_in_frame = ComputeYV12LetterboxRegion(
400 output->visible_rect(), gfx::Size(input.width(), input.height()));
402 // Scale the bitmap to the required size, if necessary.
403 SkBitmap scaled_bitmap;
404 if (input.width() != region_in_frame.width() ||
405 input.height() != region_in_frame.height()) {
407 skia::ImageOperations::ResizeMethod method;
408 if (input.width() < region_in_frame.width() ||
409 input.height() < region_in_frame.height()) {
410 // Avoid box filtering when magnifying, because it's actually
411 // nearest-neighbor.
412 method = skia::ImageOperations::RESIZE_HAMMING1;
413 } else {
414 method = skia::ImageOperations::RESIZE_BOX;
417 TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture",
418 "Capture", output.get(), "Scale");
419 scaled_bitmap = skia::ImageOperations::Resize(input, method,
420 region_in_frame.width(),
421 region_in_frame.height());
422 } else {
423 scaled_bitmap = input;
426 TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture", "Capture", output.get(), "YUV");
428 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
430 media::CopyRGBToVideoFrame(
431 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
432 scaled_bitmap.rowBytes(),
433 region_in_frame,
434 output.get());
437 // The result is now ready.
438 ignore_result(failure_handler.Release());
439 done_cb.Run(true);
442 VideoFrameDeliveryLog::VideoFrameDeliveryLog()
443 : last_frame_rate_log_time_(),
444 count_frames_rendered_(0) {
447 void VideoFrameDeliveryLog::ChronicleFrameDelivery(base::TimeTicks frame_time) {
448 // Log frame rate, if verbose logging is turned on.
449 static const base::TimeDelta kFrameRateLogInterval =
450 base::TimeDelta::FromSeconds(10);
451 if (last_frame_rate_log_time_.is_null()) {
452 last_frame_rate_log_time_ = frame_time;
453 count_frames_rendered_ = 0;
454 } else {
455 ++count_frames_rendered_;
456 const base::TimeDelta elapsed = frame_time - last_frame_rate_log_time_;
457 if (elapsed >= kFrameRateLogInterval) {
458 const double measured_fps =
459 count_frames_rendered_ / elapsed.InSecondsF();
460 UMA_HISTOGRAM_COUNTS(
461 "TabCapture.FrameRate",
462 static_cast<int>(measured_fps));
463 VLOG(1) << "Current measured frame rate for "
464 << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS.";
465 last_frame_rate_log_time_ = frame_time;
466 count_frames_rendered_ = 0;
471 WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id,
472 int main_render_frame_id)
473 : initial_render_process_id_(render_process_id),
474 initial_main_render_frame_id_(main_render_frame_id),
475 tracker_(new WebContentsTracker(true)),
476 weak_ptr_factory_(this) {}
478 WebContentsCaptureMachine::~WebContentsCaptureMachine() {}
480 bool WebContentsCaptureMachine::IsStarted() const {
481 DCHECK_CURRENTLY_ON(BrowserThread::UI);
482 return weak_ptr_factory_.HasWeakPtrs();
485 bool WebContentsCaptureMachine::Start(
486 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
487 const media::VideoCaptureParams& params) {
488 DCHECK_CURRENTLY_ON(BrowserThread::UI);
489 DCHECK(!IsStarted());
491 DCHECK(oracle_proxy.get());
492 oracle_proxy_ = oracle_proxy;
493 capture_params_ = params;
495 render_thread_.reset(new base::Thread("WebContentsVideo_RenderThread"));
496 if (!render_thread_->Start()) {
497 DVLOG(1) << "Failed to spawn render thread.";
498 render_thread_.reset();
499 return false;
502 // Note: Creation of the first WeakPtr in the following statement will cause
503 // IsStarted() to return true from now on.
504 tracker_->Start(initial_render_process_id_, initial_main_render_frame_id_,
505 base::Bind(&WebContentsCaptureMachine::RenewFrameSubscription,
506 weak_ptr_factory_.GetWeakPtr()));
508 return true;
511 void WebContentsCaptureMachine::Stop(const base::Closure& callback) {
512 DCHECK_CURRENTLY_ON(BrowserThread::UI);
514 if (!IsStarted()) {
515 callback.Run();
516 return;
519 // The following cancels any outstanding callbacks and causes IsStarted() to
520 // return false from here onward.
521 weak_ptr_factory_.InvalidateWeakPtrs();
523 // Note: RenewFrameSubscription() must be called before stopping |tracker_| so
524 // the web_contents() can be notified that the capturing is ending.
525 RenewFrameSubscription(NULL);
526 tracker_->Stop();
528 // The render thread cannot be stopped on the UI thread, so post a message
529 // to the thread pool used for blocking operations.
530 if (render_thread_.get()) {
531 BrowserThread::PostBlockingPoolTask(
532 FROM_HERE,
533 base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_),
534 callback));
538 void WebContentsCaptureMachine::Capture(
539 const base::TimeTicks& start_time,
540 const scoped_refptr<media::VideoFrame>& target,
541 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
542 deliver_frame_cb) {
543 DCHECK_CURRENTLY_ON(BrowserThread::UI);
545 RenderWidgetHost* rwh = tracker_->GetTargetRenderWidgetHost();
546 RenderWidgetHostViewBase* view =
547 rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL;
548 if (!view) {
549 deliver_frame_cb.Run(base::TimeTicks(), false);
550 return;
553 gfx::Size view_size = view->GetViewBounds().size();
554 gfx::Size fitted_size;
555 if (!view_size.IsEmpty()) {
556 fitted_size = ComputeYV12LetterboxRegion(target->visible_rect(),
557 view_size).size();
559 if (view_size != last_view_size_) {
560 last_view_size_ = view_size;
562 // Measure the number of kilopixels.
563 UMA_HISTOGRAM_COUNTS_10000(
564 "TabCapture.ViewChangeKiloPixels",
565 view_size.width() * view_size.height() / 1024);
568 if (view->CanCopyToVideoFrame()) {
569 view->CopyFromCompositingSurfaceToVideoFrame(
570 gfx::Rect(view_size),
571 target,
572 base::Bind(&WebContentsCaptureMachine::
573 DidCopyFromCompositingSurfaceToVideoFrame,
574 weak_ptr_factory_.GetWeakPtr(),
575 start_time, deliver_frame_cb));
576 } else {
577 rwh->CopyFromBackingStore(
578 gfx::Rect(),
579 fitted_size, // Size here is a request not always honored.
580 base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
581 weak_ptr_factory_.GetWeakPtr(),
582 start_time,
583 target,
584 deliver_frame_cb),
585 kN32_SkColorType);
589 gfx::Size WebContentsCaptureMachine::ComputeOptimalTargetSize() const {
590 DCHECK_CURRENTLY_ON(BrowserThread::UI);
592 gfx::Size optimal_size = oracle_proxy_->GetCaptureSize();
594 // If the ratio between physical and logical pixels is greater than 1:1,
595 // shrink |optimal_size| by that amount. Then, when external code resizes the
596 // render widget to the "preferred size," the widget will be physically
597 // rendered at the exact capture size, thereby eliminating unnecessary scaling
598 // operations in the graphics pipeline.
599 RenderWidgetHost* const rwh = tracker_->GetTargetRenderWidgetHost();
600 RenderWidgetHostView* const rwhv = rwh ? rwh->GetView() : NULL;
601 if (rwhv) {
602 const gfx::NativeView view = rwhv->GetNativeView();
603 gfx::Screen* const screen = gfx::Screen::GetScreenFor(view);
604 const gfx::Display display = screen->GetDisplayNearestWindow(view);
605 const float scale = display.device_scale_factor();
606 if (scale > 1.0f) {
607 const gfx::Size shrunk_size(
608 gfx::ToFlooredSize(gfx::ScaleSize(optimal_size, 1.0f / scale)));
609 if (shrunk_size.width() > 0 && shrunk_size.height() > 0)
610 optimal_size = shrunk_size;
614 VLOG(1) << "Computed optimal target size: " << optimal_size.ToString();
615 return optimal_size;
618 void WebContentsCaptureMachine::DidCopyFromBackingStore(
619 const base::TimeTicks& start_time,
620 const scoped_refptr<media::VideoFrame>& target,
621 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
622 deliver_frame_cb,
623 const SkBitmap& bitmap,
624 ReadbackResponse response) {
625 DCHECK_CURRENTLY_ON(BrowserThread::UI);
627 base::TimeTicks now = base::TimeTicks::Now();
628 DCHECK(render_thread_.get());
629 if (response == READBACK_SUCCESS) {
630 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time);
631 TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture", "Capture", target.get(),
632 "Render");
633 render_thread_->message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
634 &RenderVideoFrame, bitmap, target,
635 base::Bind(deliver_frame_cb, start_time)));
636 } else {
637 // Capture can fail due to transient issues, so just skip this frame.
638 DVLOG(1) << "CopyFromBackingStore failed; skipping frame.";
639 deliver_frame_cb.Run(start_time, false);
643 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
644 const base::TimeTicks& start_time,
645 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
646 deliver_frame_cb,
647 bool success) {
648 DCHECK_CURRENTLY_ON(BrowserThread::UI);
649 base::TimeTicks now = base::TimeTicks::Now();
651 if (success) {
652 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time);
653 } else {
654 // Capture can fail due to transient issues, so just skip this frame.
655 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame.";
657 deliver_frame_cb.Run(start_time, success);
660 void WebContentsCaptureMachine::RenewFrameSubscription(RenderWidgetHost* rwh) {
661 DCHECK_CURRENTLY_ON(BrowserThread::UI);
663 // Always destroy the old subscription before creating a new one.
664 const bool had_subscription = !!subscription_;
665 subscription_.reset();
667 DVLOG(1) << "Renewing frame subscription to RWH@" << rwh
668 << ", had_subscription=" << had_subscription;
670 if (!rwh) {
671 if (had_subscription && tracker_->web_contents())
672 tracker_->web_contents()->DecrementCapturerCount();
673 if (IsStarted()) {
674 // Tracking of WebContents and/or its main frame has failed before Stop()
675 // was called, so report this as an error:
676 oracle_proxy_->ReportError("WebContents and/or main frame are gone.");
678 return;
681 if (!had_subscription && tracker_->web_contents()) {
682 tracker_->web_contents()->IncrementCapturerCount(
683 ComputeOptimalTargetSize());
686 subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
687 base::Bind(&WebContentsCaptureMachine::Capture,
688 weak_ptr_factory_.GetWeakPtr())));
691 } // namespace
693 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
694 int render_process_id, int main_render_frame_id)
695 : core_(new ContentVideoCaptureDeviceCore(scoped_ptr<VideoCaptureMachine>(
696 new WebContentsCaptureMachine(
697 render_process_id, main_render_frame_id)))) {}
699 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() {
700 DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying.";
703 // static
704 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create(
705 const std::string& device_id) {
706 // Parse device_id into render_process_id and main_render_frame_id.
707 int render_process_id = -1;
708 int main_render_frame_id = -1;
709 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
710 device_id, &render_process_id, &main_render_frame_id)) {
711 return NULL;
714 return new WebContentsVideoCaptureDevice(
715 render_process_id, main_render_frame_id);
718 void WebContentsVideoCaptureDevice::AllocateAndStart(
719 const media::VideoCaptureParams& params,
720 scoped_ptr<Client> client) {
721 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
722 core_->AllocateAndStart(params, client.Pass());
725 void WebContentsVideoCaptureDevice::StopAndDeAllocate() {
726 core_->StopAndDeAllocate();
729 } // namespace content