IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / renderer_host / media / web_contents_video_capture_device.cc
blob4f215398230fad03079605dbb1950823ffb53d2c
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 RenderView'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/renderer_host/media/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/message_loop/message_loop_proxy.h"
59 #include "base/metrics/histogram.h"
60 #include "base/sequenced_task_runner.h"
61 #include "base/threading/thread.h"
62 #include "base/threading/thread_checker.h"
63 #include "base/time/time.h"
64 #include "content/browser/renderer_host/media/video_capture_device_impl.h"
65 #include "content/browser/renderer_host/media/video_capture_oracle.h"
66 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
67 #include "content/browser/renderer_host/render_widget_host_impl.h"
68 #include "content/browser/web_contents/web_contents_impl.h"
69 #include "content/port/browser/render_widget_host_view_frame_subscriber.h"
70 #include "content/port/browser/render_widget_host_view_port.h"
71 #include "content/public/browser/browser_thread.h"
72 #include "content/public/browser/notification_source.h"
73 #include "content/public/browser/notification_types.h"
74 #include "content/public/browser/render_view_host.h"
75 #include "content/public/browser/render_widget_host_view.h"
76 #include "content/public/browser/web_contents_observer.h"
77 #include "media/base/video_util.h"
78 #include "media/video/capture/video_capture_types.h"
79 #include "skia/ext/image_operations.h"
80 #include "third_party/skia/include/core/SkBitmap.h"
81 #include "third_party/skia/include/core/SkColor.h"
83 namespace content {
85 namespace {
87 // Compute a letterbox region, aligned to even coordinates.
88 gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size,
89 const gfx::Size& content_size) {
91 gfx::Rect result = media::ComputeLetterboxRegion(gfx::Rect(frame_size),
92 content_size);
94 result.set_x(MakeEven(result.x()));
95 result.set_y(MakeEven(result.y()));
96 result.set_width(std::max(kMinFrameWidth, MakeEven(result.width())));
97 result.set_height(std::max(kMinFrameHeight, MakeEven(result.height())));
99 return result;
102 // Wrapper function to invoke ThreadSafeCaptureOracle::CaptureFrameCallback, is
103 // compatible with RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback.
104 void InvokeCaptureFrameCallback(
105 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
106 base::TimeTicks timestamp,
107 bool frame_captured) {
108 capture_frame_cb.Run(timestamp, frame_captured);
111 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible
112 // with RenderWidgetHostViewFrameSubscriber. We create one per event type.
113 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
114 public:
115 FrameSubscriber(VideoCaptureOracle::Event event_type,
116 const scoped_refptr<ThreadSafeCaptureOracle>& oracle)
117 : event_type_(event_type),
118 oracle_proxy_(oracle) {}
120 virtual bool ShouldCaptureFrame(
121 base::TimeTicks present_time,
122 scoped_refptr<media::VideoFrame>* storage,
123 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback*
124 deliver_frame_cb) OVERRIDE;
126 private:
127 const VideoCaptureOracle::Event event_type_;
128 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
131 // ContentCaptureSubscription is the relationship between a RenderWidgetHost
132 // whose content is updating, a subscriber that is deciding which of these
133 // updates to capture (and where to deliver them to), and a callback that
134 // knows how to do the capture and prepare the result for delivery.
136 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in
137 // the RenderWidgetHostView, to process updates that occur via accelerated
138 // compositing, (b) installing itself as an observer of updates to the
139 // RenderWidgetHost's backing store, to hook updates that occur via software
140 // rendering, and (c) running a timer to possibly initiate non-event-driven
141 // captures that the subscriber might request.
143 // All of this happens on the UI thread, although the
144 // RenderWidgetHostViewFrameSubscriber we install may be dispatching updates
145 // autonomously on some other thread.
146 class ContentCaptureSubscription : public content::NotificationObserver {
147 public:
148 typedef base::Callback<
149 void(const base::TimeTicks&,
150 const scoped_refptr<media::VideoFrame>&,
151 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)>
152 CaptureCallback;
154 // Create a subscription. Whenever a manual capture is required, the
155 // subscription will invoke |capture_callback| on the UI thread to do the
156 // work.
157 ContentCaptureSubscription(
158 const RenderWidgetHost& source,
159 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
160 const CaptureCallback& capture_callback);
161 virtual ~ContentCaptureSubscription();
163 // content::NotificationObserver implementation.
164 virtual void Observe(int type,
165 const content::NotificationSource& source,
166 const content::NotificationDetails& details) OVERRIDE;
168 private:
169 void OnTimer();
171 const int render_process_id_;
172 const int render_view_id_;
174 FrameSubscriber paint_subscriber_;
175 FrameSubscriber timer_subscriber_;
176 content::NotificationRegistrar registrar_;
177 CaptureCallback capture_callback_;
178 base::Timer timer_;
180 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription);
183 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then
184 // invoke |done_cb| to indicate success or failure. |input| is expected to be
185 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done.
186 // Scaling and letterboxing will be done as needed.
188 // This software implementation should be used only when GPU acceleration of
189 // these activities is not possible. This operation may be expensive (tens to
190 // hundreds of milliseconds), so the caller should ensure that it runs on a
191 // thread where such a pause would cause UI jank.
192 void RenderVideoFrame(const SkBitmap& input,
193 const scoped_refptr<media::VideoFrame>& output,
194 const base::Callback<void(bool)>& done_cb);
196 // Keeps track of the RenderView to be sourced, and executes copying of the
197 // backing store on the UI BrowserThread.
199 // TODO(nick): It would be nice to merge this with WebContentsTracker, but its
200 // implementation is currently asynchronous -- in our case, the "rvh changed"
201 // notification would get posted back to the UI thread and processed later, and
202 // this seems disadvantageous.
203 class WebContentsCaptureMachine
204 : public VideoCaptureMachine,
205 public WebContentsObserver,
206 public base::SupportsWeakPtr<WebContentsCaptureMachine> {
207 public:
208 WebContentsCaptureMachine(int render_process_id, int render_view_id);
209 virtual ~WebContentsCaptureMachine();
211 // VideoCaptureMachine overrides.
212 virtual bool Start(
213 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
214 virtual void Stop() OVERRIDE;
216 // Starts a copy from the backing store or the composited surface. Must be run
217 // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation
218 // completes. The copy will occur to |target|.
220 // This may be used as a ContentCaptureSubscription::CaptureCallback.
221 void Capture(const base::TimeTicks& start_time,
222 const scoped_refptr<media::VideoFrame>& target,
223 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
224 deliver_frame_cb);
226 // content::WebContentsObserver implementation.
227 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
228 fullscreen_widget_id_ = routing_id;
229 RenewFrameSubscription();
232 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE {
233 DCHECK_EQ(fullscreen_widget_id_, routing_id);
234 fullscreen_widget_id_ = MSG_ROUTING_NONE;
235 RenewFrameSubscription();
238 virtual void RenderViewReady() OVERRIDE {
239 RenewFrameSubscription();
242 virtual void AboutToNavigateRenderView(RenderViewHost* rvh) OVERRIDE {
243 RenewFrameSubscription();
246 virtual void DidNavigateMainFrame(
247 const LoadCommittedDetails& details,
248 const FrameNavigateParams& params) OVERRIDE {
249 RenewFrameSubscription();
252 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
254 private:
255 // Starts observing the web contents, returning false if lookup fails.
256 bool StartObservingWebContents();
258 // Helper function to determine the view that we are currently tracking.
259 RenderWidgetHost* GetTarget();
261 // Response callback for RenderWidgetHost::CopyFromBackingStore().
262 void DidCopyFromBackingStore(
263 const base::TimeTicks& start_time,
264 const scoped_refptr<media::VideoFrame>& target,
265 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
266 deliver_frame_cb,
267 bool success,
268 const SkBitmap& bitmap);
270 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
271 void DidCopyFromCompositingSurfaceToVideoFrame(
272 const base::TimeTicks& start_time,
273 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
274 deliver_frame_cb,
275 bool success);
277 // Remove the old subscription, and start a new one. This should be called
278 // after any change to the WebContents that affects the RenderWidgetHost or
279 // attached views.
280 void RenewFrameSubscription();
282 // Parameters saved in constructor.
283 const int initial_render_process_id_;
284 const int initial_render_view_id_;
286 // A dedicated worker thread on which SkBitmap->VideoFrame conversion will
287 // occur. Only used when this activity cannot be done on the GPU.
288 base::Thread render_thread_;
290 // Makes all the decisions about which frames to copy, and how.
291 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
293 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
294 // otherwise.
295 int fullscreen_widget_id_;
297 // Last known RenderView size.
298 gfx::Size last_view_size_;
300 // Responsible for forwarding events from the active RenderWidgetHost to the
301 // oracle, and initiating captures accordingly.
302 scoped_ptr<ContentCaptureSubscription> subscription_;
304 DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine);
307 // Responsible for logging the effective frame rate.
308 // TODO(nick): Make this compatible with the push model and hook it back up.
309 class VideoFrameDeliveryLog {
310 public:
311 VideoFrameDeliveryLog();
313 // Treat |frame_number| as having been delivered, and update the
314 // frame rate statistics accordingly.
315 void ChronicleFrameDelivery(int frame_number);
317 private:
318 // The following keep track of and log the effective frame rate whenever
319 // verbose logging is turned on.
320 base::TimeTicks last_frame_rate_log_time_;
321 int count_frames_rendered_;
322 int last_frame_number_;
324 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
327 bool FrameSubscriber::ShouldCaptureFrame(
328 base::TimeTicks present_time,
329 scoped_refptr<media::VideoFrame>* storage,
330 DeliverFrameCallback* deliver_frame_cb) {
331 TRACE_EVENT1("mirroring", "FrameSubscriber::ShouldCaptureFrame",
332 "instance", this);
334 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
335 bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture(
336 event_type_, present_time, storage, &capture_frame_cb);
338 *deliver_frame_cb = base::Bind(&InvokeCaptureFrameCallback, capture_frame_cb);
339 return oracle_decision;
342 ContentCaptureSubscription::ContentCaptureSubscription(
343 const RenderWidgetHost& source,
344 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
345 const CaptureCallback& capture_callback)
346 : render_process_id_(source.GetProcess()->GetID()),
347 render_view_id_(source.GetRoutingID()),
348 paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy),
349 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy),
350 capture_callback_(capture_callback),
351 timer_(true, true) {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354 RenderWidgetHostViewPort* view =
355 RenderWidgetHostViewPort::FromRWHV(source.GetView());
357 // Subscribe to accelerated presents. These will be serviced directly by the
358 // oracle.
359 if (view && kAcceleratedSubscriberIsSupported) {
360 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
361 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate,
362 oracle_proxy));
363 view->BeginFrameSubscription(subscriber.Pass());
366 // Subscribe to software paint events. This instance will service these by
367 // reflecting them back to the WebContentsCaptureMachine via
368 // |capture_callback|.
369 registrar_.Add(
370 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
371 Source<RenderWidgetHost>(&source));
373 // Subscribe to timer events. This instance will service these as well.
374 timer_.Start(FROM_HERE, oracle_proxy->capture_period(),
375 base::Bind(&ContentCaptureSubscription::OnTimer,
376 base::Unretained(this)));
379 ContentCaptureSubscription::~ContentCaptureSubscription() {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
381 if (kAcceleratedSubscriberIsSupported) {
382 RenderViewHost* source = RenderViewHost::FromID(render_process_id_,
383 render_view_id_);
384 if (source) {
385 RenderWidgetHostViewPort* view =
386 RenderWidgetHostViewPort::FromRWHV(source->GetView());
387 if (view)
388 view->EndFrameSubscription();
393 void ContentCaptureSubscription::Observe(
394 int type,
395 const content::NotificationSource& source,
396 const content::NotificationDetails& details) {
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
398 DCHECK_EQ(NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, type);
400 RenderWidgetHostImpl* rwh =
401 RenderWidgetHostImpl::From(Source<RenderWidgetHost>(source).ptr());
403 // This message occurs on window resizes and visibility changes even when
404 // accelerated compositing is active, so we need to filter out these cases.
405 if (!rwh || !rwh->GetView() || (rwh->is_accelerated_compositing_active() &&
406 rwh->GetView()->IsSurfaceAvailableForCopy()))
407 return;
409 TRACE_EVENT1("mirroring", "ContentCaptureSubscription::Observe",
410 "instance", this);
412 base::Closure copy_done_callback;
413 scoped_refptr<media::VideoFrame> frame;
414 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
415 const base::TimeTicks start_time = base::TimeTicks::Now();
416 if (paint_subscriber_.ShouldCaptureFrame(start_time,
417 &frame,
418 &deliver_frame_cb)) {
419 // This message happens just before paint. If we post a task to do the copy,
420 // it should run soon after the paint.
421 BrowserThread::PostTask(
422 BrowserThread::UI, FROM_HERE,
423 base::Bind(capture_callback_, start_time, frame, deliver_frame_cb));
427 void ContentCaptureSubscription::OnTimer() {
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
429 TRACE_EVENT0("mirroring", "ContentCaptureSubscription::OnTimer");
431 scoped_refptr<media::VideoFrame> frame;
432 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
434 const base::TimeTicks start_time = base::TimeTicks::Now();
435 if (timer_subscriber_.ShouldCaptureFrame(start_time,
436 &frame,
437 &deliver_frame_cb)) {
438 capture_callback_.Run(start_time, frame, deliver_frame_cb);
442 void RenderVideoFrame(const SkBitmap& input,
443 const scoped_refptr<media::VideoFrame>& output,
444 const base::Callback<void(bool)>& done_cb) {
445 base::ScopedClosureRunner failure_handler(base::Bind(done_cb, false));
447 SkAutoLockPixels locker(input);
449 // Sanity-check the captured bitmap.
450 if (input.empty() ||
451 !input.readyToDraw() ||
452 input.config() != SkBitmap::kARGB_8888_Config ||
453 input.width() < 2 || input.height() < 2) {
454 DVLOG(1) << "input unacceptable (size="
455 << input.getSize()
456 << ", ready=" << input.readyToDraw()
457 << ", config=" << input.config() << ')';
458 return;
461 // Sanity-check the output buffer.
462 if (output->format() != media::VideoFrame::I420) {
463 NOTREACHED();
464 return;
467 // Calculate the width and height of the content region in the |output|, based
468 // on the aspect ratio of |input|.
469 gfx::Rect region_in_frame = ComputeYV12LetterboxRegion(
470 output->coded_size(), gfx::Size(input.width(), input.height()));
472 // Scale the bitmap to the required size, if necessary.
473 SkBitmap scaled_bitmap;
474 if (input.width() != region_in_frame.width() ||
475 input.height() != region_in_frame.height()) {
477 skia::ImageOperations::ResizeMethod method;
478 if (input.width() < region_in_frame.width() ||
479 input.height() < region_in_frame.height()) {
480 // Avoid box filtering when magnifying, because it's actually
481 // nearest-neighbor.
482 method = skia::ImageOperations::RESIZE_HAMMING1;
483 } else {
484 method = skia::ImageOperations::RESIZE_BOX;
487 TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", output.get(), "Scale");
488 scaled_bitmap = skia::ImageOperations::Resize(input, method,
489 region_in_frame.width(),
490 region_in_frame.height());
491 } else {
492 scaled_bitmap = input;
495 TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", output.get(), "YUV");
497 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
499 media::CopyRGBToVideoFrame(
500 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
501 scaled_bitmap.rowBytes(),
502 region_in_frame,
503 output.get());
506 // The result is now ready.
507 ignore_result(failure_handler.Release());
508 done_cb.Run(true);
511 VideoFrameDeliveryLog::VideoFrameDeliveryLog()
512 : last_frame_rate_log_time_(),
513 count_frames_rendered_(0),
514 last_frame_number_(0) {
517 void VideoFrameDeliveryLog::ChronicleFrameDelivery(int frame_number) {
518 // Log frame rate, if verbose logging is turned on.
519 static const base::TimeDelta kFrameRateLogInterval =
520 base::TimeDelta::FromSeconds(10);
521 const base::TimeTicks now = base::TimeTicks::Now();
522 if (last_frame_rate_log_time_.is_null()) {
523 last_frame_rate_log_time_ = now;
524 count_frames_rendered_ = 0;
525 last_frame_number_ = frame_number;
526 } else {
527 ++count_frames_rendered_;
528 const base::TimeDelta elapsed = now - last_frame_rate_log_time_;
529 if (elapsed >= kFrameRateLogInterval) {
530 const double measured_fps =
531 count_frames_rendered_ / elapsed.InSecondsF();
532 const int frames_elapsed = frame_number - last_frame_number_;
533 const int count_frames_dropped = frames_elapsed - count_frames_rendered_;
534 DCHECK_LE(0, count_frames_dropped);
535 UMA_HISTOGRAM_PERCENTAGE(
536 "TabCapture.FrameDropPercentage",
537 (count_frames_dropped * 100 + frames_elapsed / 2) / frames_elapsed);
538 UMA_HISTOGRAM_COUNTS(
539 "TabCapture.FrameRate",
540 static_cast<int>(measured_fps));
541 VLOG(1) << "Current measured frame rate for "
542 << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS.";
543 last_frame_rate_log_time_ = now;
544 count_frames_rendered_ = 0;
545 last_frame_number_ = frame_number;
550 WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id,
551 int render_view_id)
552 : initial_render_process_id_(render_process_id),
553 initial_render_view_id_(render_view_id),
554 render_thread_("WebContentsVideo_RenderThread"),
555 fullscreen_widget_id_(MSG_ROUTING_NONE) {}
557 WebContentsCaptureMachine::~WebContentsCaptureMachine() {}
559 bool WebContentsCaptureMachine::Start(
560 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
562 DCHECK(!started_);
564 DCHECK(oracle_proxy.get());
565 oracle_proxy_ = oracle_proxy;
567 if (!render_thread_.Start()) {
568 DVLOG(1) << "Failed to spawn render thread.";
569 return false;
572 if (!StartObservingWebContents())
573 return false;
575 started_ = true;
576 return true;
579 void WebContentsCaptureMachine::Stop() {
580 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
581 subscription_.reset();
582 if (web_contents()) {
583 web_contents()->DecrementCapturerCount();
584 Observe(NULL);
586 render_thread_.Stop();
587 started_ = false;
590 void WebContentsCaptureMachine::Capture(
591 const base::TimeTicks& start_time,
592 const scoped_refptr<media::VideoFrame>& target,
593 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
594 deliver_frame_cb) {
595 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
597 RenderWidgetHost* rwh = GetTarget();
598 RenderWidgetHostViewPort* view =
599 rwh ? RenderWidgetHostViewPort::FromRWHV(rwh->GetView()) : NULL;
600 if (!view || !rwh) {
601 deliver_frame_cb.Run(base::TimeTicks(), false);
602 return;
605 gfx::Size video_size = target->coded_size();
606 gfx::Size view_size = view->GetViewBounds().size();
607 gfx::Size fitted_size;
608 if (!view_size.IsEmpty()) {
609 fitted_size = ComputeYV12LetterboxRegion(video_size, view_size).size();
611 if (view_size != last_view_size_) {
612 last_view_size_ = view_size;
614 // Measure the number of kilopixels.
615 UMA_HISTOGRAM_COUNTS_10000(
616 "TabCapture.ViewChangeKiloPixels",
617 view_size.width() * view_size.height() / 1024);
620 if (!view->IsSurfaceAvailableForCopy()) {
621 // Fallback to the more expensive renderer-side copy if the surface and
622 // backing store are not accessible.
623 rwh->GetSnapshotFromRenderer(
624 gfx::Rect(),
625 base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
626 this->AsWeakPtr(), start_time, target, deliver_frame_cb));
627 } else if (view->CanCopyToVideoFrame()) {
628 view->CopyFromCompositingSurfaceToVideoFrame(
629 gfx::Rect(view_size),
630 target,
631 base::Bind(&WebContentsCaptureMachine::
632 DidCopyFromCompositingSurfaceToVideoFrame,
633 this->AsWeakPtr(), start_time, deliver_frame_cb));
634 } else {
635 rwh->CopyFromBackingStore(
636 gfx::Rect(),
637 fitted_size, // Size here is a request not always honored.
638 base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
639 this->AsWeakPtr(), start_time, target, deliver_frame_cb));
643 bool WebContentsCaptureMachine::StartObservingWebContents() {
644 // Look-up the RenderViewHost and, from that, the WebContents that wraps it.
645 // If successful, begin observing the WebContents instance.
647 // Why this can be unsuccessful: The request for mirroring originates in a
648 // render process, and this request is based on the current RenderView
649 // associated with a tab. However, by the time we get up-and-running here,
650 // there have been multiple back-and-forth IPCs between processes, as well as
651 // a bit of indirection across threads. It's easily possible that, in the
652 // meantime, the original RenderView may have gone away.
653 RenderViewHost* const rvh =
654 RenderViewHost::FromID(initial_render_process_id_,
655 initial_render_view_id_);
656 DVLOG_IF(1, !rvh) << "RenderViewHost::FromID("
657 << initial_render_process_id_ << ", "
658 << initial_render_view_id_ << ") returned NULL.";
659 Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL);
661 WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
662 if (contents) {
663 contents->IncrementCapturerCount();
664 fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID();
665 RenewFrameSubscription();
666 return true;
669 DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL.";
670 return false;
673 void WebContentsCaptureMachine::WebContentsDestroyed(
674 WebContents* web_contents) {
675 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
677 subscription_.reset();
678 web_contents->DecrementCapturerCount();
679 oracle_proxy_->ReportError();
682 RenderWidgetHost* WebContentsCaptureMachine::GetTarget() {
683 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
684 if (!web_contents())
685 return NULL;
687 RenderWidgetHost* rwh = NULL;
688 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
689 RenderProcessHost* process = web_contents()->GetRenderProcessHost();
690 if (process)
691 rwh = RenderWidgetHost::FromID(process->GetID(), fullscreen_widget_id_);
692 } else {
693 rwh = web_contents()->GetRenderViewHost();
696 return rwh;
699 void WebContentsCaptureMachine::DidCopyFromBackingStore(
700 const base::TimeTicks& start_time,
701 const scoped_refptr<media::VideoFrame>& target,
702 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
703 deliver_frame_cb,
704 bool success,
705 const SkBitmap& bitmap) {
706 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
708 base::TimeTicks now = base::TimeTicks::Now();
709 if (success) {
710 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time);
711 TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", target.get(),
712 "Render");
713 render_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
714 &RenderVideoFrame, bitmap, target,
715 base::Bind(deliver_frame_cb, start_time)));
716 } else {
717 // Capture can fail due to transient issues, so just skip this frame.
718 DVLOG(1) << "CopyFromBackingStore failed; skipping frame.";
719 deliver_frame_cb.Run(start_time, false);
723 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
724 const base::TimeTicks& start_time,
725 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
726 deliver_frame_cb,
727 bool success) {
728 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
729 base::TimeTicks now = base::TimeTicks::Now();
731 if (success) {
732 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time);
733 } else {
734 // Capture can fail due to transient issues, so just skip this frame.
735 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame.";
737 deliver_frame_cb.Run(start_time, success);
740 void WebContentsCaptureMachine::RenewFrameSubscription() {
741 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
743 // Always destroy the old subscription before creating a new one.
744 subscription_.reset();
746 RenderWidgetHost* rwh = GetTarget();
747 if (!rwh || !rwh->GetView())
748 return;
750 subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
751 base::Bind(&WebContentsCaptureMachine::Capture, this->AsWeakPtr())));
754 } // namespace
756 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
757 int render_process_id, int render_view_id)
758 : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
759 new WebContentsCaptureMachine(render_process_id, render_view_id)))) {}
761 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() {
762 DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying.";
765 // static
766 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create(
767 const std::string& device_id) {
768 // Parse device_id into render_process_id and render_view_id.
769 int render_process_id = -1;
770 int render_view_id = -1;
771 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
772 device_id, &render_process_id, &render_view_id)) {
773 return NULL;
776 return new WebContentsVideoCaptureDevice(render_process_id, render_view_id);
779 void WebContentsVideoCaptureDevice::AllocateAndStart(
780 const media::VideoCaptureParams& params,
781 scoped_ptr<Client> client) {
782 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
783 impl_->AllocateAndStart(params, client.Pass());
786 void WebContentsVideoCaptureDevice::StopAndDeAllocate() {
787 impl_->StopAndDeAllocate();
790 } // namespace content