Add a FrameHostMsg_BeginNavigation IPC
[chromium-blink-merge.git] / content / browser / media / capture / web_contents_video_capture_device.cc
blob47993b2a2e5a6ea701e0623b4e0882ff09928858
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/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/renderer_host/render_widget_host_impl.h"
69 #include "content/browser/renderer_host/render_widget_host_view_base.h"
70 #include "content/browser/web_contents/web_contents_impl.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/render_widget_host_view_frame_subscriber.h"
77 #include "content/public/browser/web_contents_observer.h"
78 #include "media/base/video_util.h"
79 #include "media/video/capture/video_capture_types.h"
80 #include "skia/ext/image_operations.h"
81 #include "third_party/skia/include/core/SkBitmap.h"
82 #include "third_party/skia/include/core/SkColor.h"
84 namespace content {
86 namespace {
88 // Compute a letterbox region, aligned to even coordinates.
89 gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size,
90 const gfx::Size& content_size) {
92 gfx::Rect result = media::ComputeLetterboxRegion(gfx::Rect(frame_size),
93 content_size);
95 result.set_x(MakeEven(result.x()));
96 result.set_y(MakeEven(result.y()));
97 result.set_width(std::max(kMinFrameWidth, MakeEven(result.width())));
98 result.set_height(std::max(kMinFrameHeight, MakeEven(result.height())));
100 return result;
103 void DeleteOnWorkerThread(scoped_ptr<base::Thread> render_thread,
104 const base::Closure& callback) {
105 render_thread.reset();
107 // After thread join call the callback on UI thread.
108 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
111 // Responsible for logging the effective frame rate.
112 class VideoFrameDeliveryLog {
113 public:
114 VideoFrameDeliveryLog();
116 // Report that the frame posted with |frame_time| has been delivered.
117 void ChronicleFrameDelivery(base::TimeTicks frame_time);
119 private:
120 // The following keep track of and log the effective frame rate whenever
121 // verbose logging is turned on.
122 base::TimeTicks last_frame_rate_log_time_;
123 int count_frames_rendered_;
125 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
128 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible
129 // with RenderWidgetHostViewFrameSubscriber. We create one per event type.
130 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
131 public:
132 FrameSubscriber(VideoCaptureOracle::Event event_type,
133 const scoped_refptr<ThreadSafeCaptureOracle>& oracle,
134 VideoFrameDeliveryLog* delivery_log)
135 : event_type_(event_type),
136 oracle_proxy_(oracle),
137 delivery_log_(delivery_log) {}
139 virtual bool ShouldCaptureFrame(
140 base::TimeTicks present_time,
141 scoped_refptr<media::VideoFrame>* storage,
142 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback*
143 deliver_frame_cb) OVERRIDE;
145 private:
146 const VideoCaptureOracle::Event event_type_;
147 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
148 VideoFrameDeliveryLog* const delivery_log_;
151 // ContentCaptureSubscription is the relationship between a RenderWidgetHost
152 // whose content is updating, a subscriber that is deciding which of these
153 // updates to capture (and where to deliver them to), and a callback that
154 // knows how to do the capture and prepare the result for delivery.
156 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in
157 // the RenderWidgetHostView, to process updates that occur via accelerated
158 // compositing, (b) installing itself as an observer of updates to the
159 // RenderWidgetHost's backing store, to hook updates that occur via software
160 // rendering, and (c) running a timer to possibly initiate non-event-driven
161 // captures that the subscriber might request.
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 : public content::NotificationObserver {
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 virtual ~ContentCaptureSubscription();
183 // content::NotificationObserver implementation.
184 virtual void Observe(int type,
185 const content::NotificationSource& source,
186 const content::NotificationDetails& details) OVERRIDE;
188 private:
189 void OnTimer();
191 const int render_process_id_;
192 const int render_view_id_;
194 VideoFrameDeliveryLog delivery_log_;
195 FrameSubscriber paint_subscriber_;
196 FrameSubscriber timer_subscriber_;
197 content::NotificationRegistrar registrar_;
198 CaptureCallback capture_callback_;
199 base::Timer timer_;
201 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription);
204 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then
205 // invoke |done_cb| to indicate success or failure. |input| is expected to be
206 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done.
207 // Scaling and letterboxing will be done as needed.
209 // This software implementation should be used only when GPU acceleration of
210 // these activities is not possible. This operation may be expensive (tens to
211 // hundreds of milliseconds), so the caller should ensure that it runs on a
212 // thread where such a pause would cause UI jank.
213 void RenderVideoFrame(const SkBitmap& input,
214 const scoped_refptr<media::VideoFrame>& output,
215 const base::Callback<void(bool)>& done_cb);
217 // Keeps track of the RenderView to be sourced, and executes copying of the
218 // backing store on the UI BrowserThread.
220 // TODO(nick): It would be nice to merge this with WebContentsTracker, but its
221 // implementation is currently asynchronous -- in our case, the "rvh changed"
222 // notification would get posted back to the UI thread and processed later, and
223 // this seems disadvantageous.
224 class WebContentsCaptureMachine
225 : public VideoCaptureMachine,
226 public WebContentsObserver {
227 public:
228 WebContentsCaptureMachine(int render_process_id, int render_view_id);
229 virtual ~WebContentsCaptureMachine();
231 // VideoCaptureMachine overrides.
232 virtual bool Start(const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
233 const media::VideoCaptureParams& params) OVERRIDE;
234 virtual void Stop(const base::Closure& callback) OVERRIDE;
236 // Starts a copy from the backing store or the composited surface. Must be run
237 // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation
238 // completes. The copy will occur to |target|.
240 // This may be used as a ContentCaptureSubscription::CaptureCallback.
241 void Capture(const base::TimeTicks& start_time,
242 const scoped_refptr<media::VideoFrame>& target,
243 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
244 deliver_frame_cb);
246 // content::WebContentsObserver implementation.
247 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
248 fullscreen_widget_id_ = routing_id;
249 RenewFrameSubscription();
252 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE {
253 DCHECK_EQ(fullscreen_widget_id_, routing_id);
254 fullscreen_widget_id_ = MSG_ROUTING_NONE;
255 RenewFrameSubscription();
258 virtual void RenderViewReady() OVERRIDE {
259 RenewFrameSubscription();
262 virtual void AboutToNavigateRenderView(RenderViewHost* rvh) OVERRIDE {
263 RenewFrameSubscription();
266 virtual void DidNavigateMainFrame(
267 const LoadCommittedDetails& details,
268 const FrameNavigateParams& params) OVERRIDE {
269 RenewFrameSubscription();
272 virtual void WebContentsDestroyed() OVERRIDE;
274 private:
275 // Starts observing the web contents, returning false if lookup fails.
276 bool StartObservingWebContents();
278 // Helper function to determine the view that we are currently tracking.
279 RenderWidgetHost* GetTarget();
281 // Response callback for RenderWidgetHost::CopyFromBackingStore().
282 void DidCopyFromBackingStore(
283 const base::TimeTicks& start_time,
284 const scoped_refptr<media::VideoFrame>& target,
285 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
286 deliver_frame_cb,
287 bool success,
288 const SkBitmap& bitmap);
290 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
291 void DidCopyFromCompositingSurfaceToVideoFrame(
292 const base::TimeTicks& start_time,
293 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
294 deliver_frame_cb,
295 bool success);
297 // Remove the old subscription, and start a new one. This should be called
298 // after any change to the WebContents that affects the RenderWidgetHost or
299 // attached views.
300 void RenewFrameSubscription();
302 // Parameters saved in constructor.
303 const int initial_render_process_id_;
304 const int initial_render_view_id_;
306 // A dedicated worker thread on which SkBitmap->VideoFrame conversion will
307 // occur. Only used when this activity cannot be done on the GPU.
308 scoped_ptr<base::Thread> render_thread_;
310 // Makes all the decisions about which frames to copy, and how.
311 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
313 // Video capture parameters that this machine is started with.
314 media::VideoCaptureParams capture_params_;
316 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
317 // otherwise.
318 int fullscreen_widget_id_;
320 // Last known RenderView size.
321 gfx::Size last_view_size_;
323 // Responsible for forwarding events from the active RenderWidgetHost to the
324 // oracle, and initiating captures accordingly.
325 scoped_ptr<ContentCaptureSubscription> subscription_;
327 // Weak pointer factory used to invalidate callbacks.
328 // NOTE: Weak pointers must be invalidated before all other member variables.
329 base::WeakPtrFactory<WebContentsCaptureMachine> weak_ptr_factory_;
331 DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine);
334 bool FrameSubscriber::ShouldCaptureFrame(
335 base::TimeTicks present_time,
336 scoped_refptr<media::VideoFrame>* storage,
337 DeliverFrameCallback* deliver_frame_cb) {
338 TRACE_EVENT1("mirroring", "FrameSubscriber::ShouldCaptureFrame",
339 "instance", this);
341 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
342 bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture(
343 event_type_, present_time, storage, &capture_frame_cb);
345 if (!capture_frame_cb.is_null())
346 *deliver_frame_cb = base::Bind(capture_frame_cb, *storage);
347 if (oracle_decision)
348 delivery_log_->ChronicleFrameDelivery(present_time);
349 return oracle_decision;
352 ContentCaptureSubscription::ContentCaptureSubscription(
353 const RenderWidgetHost& source,
354 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
355 const CaptureCallback& capture_callback)
356 : render_process_id_(source.GetProcess()->GetID()),
357 render_view_id_(source.GetRoutingID()),
358 delivery_log_(),
359 paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy,
360 &delivery_log_),
361 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy,
362 &delivery_log_),
363 capture_callback_(capture_callback),
364 timer_(true, true) {
365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
367 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
368 source.GetView());
370 // Subscribe to accelerated presents. These will be serviced directly by the
371 // oracle.
372 if (view && kAcceleratedSubscriberIsSupported) {
373 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
374 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate,
375 oracle_proxy, &delivery_log_));
376 view->BeginFrameSubscription(subscriber.Pass());
379 // Subscribe to software paint events. This instance will service these by
380 // reflecting them back to the WebContentsCaptureMachine via
381 // |capture_callback|.
382 registrar_.Add(
383 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
384 Source<RenderWidgetHost>(&source));
386 // Subscribe to timer events. This instance will service these as well.
387 timer_.Start(FROM_HERE, oracle_proxy->capture_period(),
388 base::Bind(&ContentCaptureSubscription::OnTimer,
389 base::Unretained(this)));
392 ContentCaptureSubscription::~ContentCaptureSubscription() {
393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
394 if (kAcceleratedSubscriberIsSupported) {
395 RenderViewHost* source = RenderViewHost::FromID(render_process_id_,
396 render_view_id_);
397 if (source) {
398 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
399 source->GetView());
400 if (view)
401 view->EndFrameSubscription();
406 void ContentCaptureSubscription::Observe(
407 int type,
408 const content::NotificationSource& source,
409 const content::NotificationDetails& details) {
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
411 DCHECK_EQ(NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, type);
413 RenderWidgetHostImpl* rwh =
414 RenderWidgetHostImpl::From(Source<RenderWidgetHost>(source).ptr());
416 // This message occurs on window resizes and visibility changes even when
417 // accelerated compositing is active, so we need to filter out these cases.
418 if (!rwh || !rwh->GetView())
419 return;
420 // Mac sends DID_UPDATE_BACKING_STORE messages to inform the capture system
421 // of new software compositor frames, so always treat these messages as
422 // signals of a new frame on Mac.
423 // http://crbug.com/333986
424 #if !defined(OS_MACOSX)
425 if (rwh->GetView()->IsSurfaceAvailableForCopy())
426 return;
427 #endif
429 TRACE_EVENT1("mirroring", "ContentCaptureSubscription::Observe",
430 "instance", this);
432 base::Closure copy_done_callback;
433 scoped_refptr<media::VideoFrame> frame;
434 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
435 const base::TimeTicks start_time = base::TimeTicks::Now();
436 if (paint_subscriber_.ShouldCaptureFrame(start_time,
437 &frame,
438 &deliver_frame_cb)) {
439 // This message happens just before paint. If we post a task to do the copy,
440 // it should run soon after the paint.
441 BrowserThread::PostTask(
442 BrowserThread::UI, FROM_HERE,
443 base::Bind(capture_callback_, start_time, frame, deliver_frame_cb));
447 void ContentCaptureSubscription::OnTimer() {
448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449 TRACE_EVENT0("mirroring", "ContentCaptureSubscription::OnTimer");
451 scoped_refptr<media::VideoFrame> frame;
452 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
454 const base::TimeTicks start_time = base::TimeTicks::Now();
455 if (timer_subscriber_.ShouldCaptureFrame(start_time,
456 &frame,
457 &deliver_frame_cb)) {
458 capture_callback_.Run(start_time, frame, deliver_frame_cb);
462 void RenderVideoFrame(const SkBitmap& input,
463 const scoped_refptr<media::VideoFrame>& output,
464 const base::Callback<void(bool)>& done_cb) {
465 base::ScopedClosureRunner failure_handler(base::Bind(done_cb, false));
467 SkAutoLockPixels locker(input);
469 // Sanity-check the captured bitmap.
470 if (input.empty() ||
471 !input.readyToDraw() ||
472 input.colorType() != kN32_SkColorType ||
473 input.width() < 2 || input.height() < 2) {
474 DVLOG(1) << "input unacceptable (size="
475 << input.getSize()
476 << ", ready=" << input.readyToDraw()
477 << ", colorType=" << input.colorType() << ')';
478 return;
481 // Sanity-check the output buffer.
482 if (output->format() != media::VideoFrame::I420) {
483 NOTREACHED();
484 return;
487 // Calculate the width and height of the content region in the |output|, based
488 // on the aspect ratio of |input|.
489 gfx::Rect region_in_frame = ComputeYV12LetterboxRegion(
490 output->coded_size(), gfx::Size(input.width(), input.height()));
492 // Scale the bitmap to the required size, if necessary.
493 SkBitmap scaled_bitmap;
494 if (input.width() != region_in_frame.width() ||
495 input.height() != region_in_frame.height()) {
497 skia::ImageOperations::ResizeMethod method;
498 if (input.width() < region_in_frame.width() ||
499 input.height() < region_in_frame.height()) {
500 // Avoid box filtering when magnifying, because it's actually
501 // nearest-neighbor.
502 method = skia::ImageOperations::RESIZE_HAMMING1;
503 } else {
504 method = skia::ImageOperations::RESIZE_BOX;
507 TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", output.get(), "Scale");
508 scaled_bitmap = skia::ImageOperations::Resize(input, method,
509 region_in_frame.width(),
510 region_in_frame.height());
511 } else {
512 scaled_bitmap = input;
515 TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", output.get(), "YUV");
517 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
519 media::CopyRGBToVideoFrame(
520 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
521 scaled_bitmap.rowBytes(),
522 region_in_frame,
523 output.get());
526 // The result is now ready.
527 ignore_result(failure_handler.Release());
528 done_cb.Run(true);
531 VideoFrameDeliveryLog::VideoFrameDeliveryLog()
532 : last_frame_rate_log_time_(),
533 count_frames_rendered_(0) {
536 void VideoFrameDeliveryLog::ChronicleFrameDelivery(base::TimeTicks frame_time) {
537 // Log frame rate, if verbose logging is turned on.
538 static const base::TimeDelta kFrameRateLogInterval =
539 base::TimeDelta::FromSeconds(10);
540 if (last_frame_rate_log_time_.is_null()) {
541 last_frame_rate_log_time_ = frame_time;
542 count_frames_rendered_ = 0;
543 } else {
544 ++count_frames_rendered_;
545 const base::TimeDelta elapsed = frame_time - last_frame_rate_log_time_;
546 if (elapsed >= kFrameRateLogInterval) {
547 const double measured_fps =
548 count_frames_rendered_ / elapsed.InSecondsF();
549 UMA_HISTOGRAM_COUNTS(
550 "TabCapture.FrameRate",
551 static_cast<int>(measured_fps));
552 VLOG(1) << "Current measured frame rate for "
553 << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS.";
554 last_frame_rate_log_time_ = frame_time;
555 count_frames_rendered_ = 0;
560 WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id,
561 int render_view_id)
562 : initial_render_process_id_(render_process_id),
563 initial_render_view_id_(render_view_id),
564 fullscreen_widget_id_(MSG_ROUTING_NONE),
565 weak_ptr_factory_(this) {}
567 WebContentsCaptureMachine::~WebContentsCaptureMachine() {
568 BrowserThread::PostBlockingPoolTask(
569 FROM_HERE,
570 base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_),
571 base::Bind(&base::DoNothing)));
574 bool WebContentsCaptureMachine::Start(
575 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
576 const media::VideoCaptureParams& params) {
577 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
578 DCHECK(!started_);
580 DCHECK(oracle_proxy.get());
581 oracle_proxy_ = oracle_proxy;
582 capture_params_ = params;
584 render_thread_.reset(new base::Thread("WebContentsVideo_RenderThread"));
585 if (!render_thread_->Start()) {
586 DVLOG(1) << "Failed to spawn render thread.";
587 render_thread_.reset();
588 return false;
591 if (!StartObservingWebContents()) {
592 DVLOG(1) << "Failed to observe web contents.";
593 render_thread_.reset();
594 return false;
597 started_ = true;
598 return true;
601 void WebContentsCaptureMachine::Stop(const base::Closure& callback) {
602 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
603 subscription_.reset();
604 if (web_contents()) {
605 web_contents()->DecrementCapturerCount();
606 Observe(NULL);
609 // Any callback that intend to use render_thread_ will not work after it is
610 // passed.
611 weak_ptr_factory_.InvalidateWeakPtrs();
613 // The render thread cannot be stopped on the UI thread, so post a message
614 // to the thread pool used for blocking operations.
615 BrowserThread::PostBlockingPoolTask(
616 FROM_HERE,
617 base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_),
618 callback));
620 started_ = false;
623 void WebContentsCaptureMachine::Capture(
624 const base::TimeTicks& start_time,
625 const scoped_refptr<media::VideoFrame>& target,
626 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
627 deliver_frame_cb) {
628 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
630 RenderWidgetHost* rwh = GetTarget();
631 RenderWidgetHostViewBase* view =
632 rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL;
633 if (!view || !rwh) {
634 deliver_frame_cb.Run(base::TimeTicks(), false);
635 return;
638 gfx::Size video_size = target->coded_size();
639 gfx::Size view_size = view->GetViewBounds().size();
640 gfx::Size fitted_size;
641 if (!view_size.IsEmpty()) {
642 fitted_size = ComputeYV12LetterboxRegion(video_size, view_size).size();
644 if (view_size != last_view_size_) {
645 last_view_size_ = view_size;
647 // Measure the number of kilopixels.
648 UMA_HISTOGRAM_COUNTS_10000(
649 "TabCapture.ViewChangeKiloPixels",
650 view_size.width() * view_size.height() / 1024);
653 if (view->CanCopyToVideoFrame()) {
654 view->CopyFromCompositingSurfaceToVideoFrame(
655 gfx::Rect(view_size),
656 target,
657 base::Bind(&WebContentsCaptureMachine::
658 DidCopyFromCompositingSurfaceToVideoFrame,
659 weak_ptr_factory_.GetWeakPtr(),
660 start_time, deliver_frame_cb));
661 } else {
662 rwh->CopyFromBackingStore(
663 gfx::Rect(),
664 fitted_size, // Size here is a request not always honored.
665 base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
666 weak_ptr_factory_.GetWeakPtr(),
667 start_time,
668 target,
669 deliver_frame_cb),
670 kN32_SkColorType);
674 bool WebContentsCaptureMachine::StartObservingWebContents() {
675 // Look-up the RenderViewHost and, from that, the WebContents that wraps it.
676 // If successful, begin observing the WebContents instance.
678 // Why this can be unsuccessful: The request for mirroring originates in a
679 // render process, and this request is based on the current RenderView
680 // associated with a tab. However, by the time we get up-and-running here,
681 // there have been multiple back-and-forth IPCs between processes, as well as
682 // a bit of indirection across threads. It's easily possible that, in the
683 // meantime, the original RenderView may have gone away.
684 RenderViewHost* const rvh =
685 RenderViewHost::FromID(initial_render_process_id_,
686 initial_render_view_id_);
687 DVLOG_IF(1, !rvh) << "RenderViewHost::FromID("
688 << initial_render_process_id_ << ", "
689 << initial_render_view_id_ << ") returned NULL.";
690 Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL);
692 WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
693 if (contents) {
694 contents->IncrementCapturerCount(oracle_proxy_->GetCaptureSize());
695 fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID();
696 RenewFrameSubscription();
697 return true;
700 DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL.";
701 return false;
704 void WebContentsCaptureMachine::WebContentsDestroyed() {
705 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
707 subscription_.reset();
708 web_contents()->DecrementCapturerCount();
709 oracle_proxy_->ReportError("WebContentsDestroyed()");
712 RenderWidgetHost* WebContentsCaptureMachine::GetTarget() {
713 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
714 if (!web_contents())
715 return NULL;
717 RenderWidgetHost* rwh = NULL;
718 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
719 RenderProcessHost* process = web_contents()->GetRenderProcessHost();
720 if (process)
721 rwh = RenderWidgetHost::FromID(process->GetID(), fullscreen_widget_id_);
722 } else {
723 rwh = web_contents()->GetRenderViewHost();
726 return rwh;
729 void WebContentsCaptureMachine::DidCopyFromBackingStore(
730 const base::TimeTicks& start_time,
731 const scoped_refptr<media::VideoFrame>& target,
732 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
733 deliver_frame_cb,
734 bool success,
735 const SkBitmap& bitmap) {
736 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
738 base::TimeTicks now = base::TimeTicks::Now();
739 DCHECK(render_thread_.get());
740 if (success) {
741 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time);
742 TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", target.get(),
743 "Render");
744 render_thread_->message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
745 &RenderVideoFrame, bitmap, target,
746 base::Bind(deliver_frame_cb, start_time)));
747 } else {
748 // Capture can fail due to transient issues, so just skip this frame.
749 DVLOG(1) << "CopyFromBackingStore failed; skipping frame.";
750 deliver_frame_cb.Run(start_time, false);
754 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
755 const base::TimeTicks& start_time,
756 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
757 deliver_frame_cb,
758 bool success) {
759 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
760 base::TimeTicks now = base::TimeTicks::Now();
762 if (success) {
763 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time);
764 } else {
765 // Capture can fail due to transient issues, so just skip this frame.
766 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame.";
768 deliver_frame_cb.Run(start_time, success);
771 void WebContentsCaptureMachine::RenewFrameSubscription() {
772 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
774 // Always destroy the old subscription before creating a new one.
775 subscription_.reset();
777 RenderWidgetHost* rwh = GetTarget();
778 if (!rwh || !rwh->GetView())
779 return;
781 subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
782 base::Bind(&WebContentsCaptureMachine::Capture,
783 weak_ptr_factory_.GetWeakPtr())));
786 } // namespace
788 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
789 int render_process_id, int render_view_id)
790 : core_(new ContentVideoCaptureDeviceCore(scoped_ptr<VideoCaptureMachine>(
791 new WebContentsCaptureMachine(render_process_id, render_view_id)))) {}
793 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() {
794 DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying.";
797 // static
798 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create(
799 const std::string& device_id) {
800 // Parse device_id into render_process_id and render_view_id.
801 int render_process_id = -1;
802 int render_view_id = -1;
803 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
804 device_id, &render_process_id, &render_view_id)) {
805 return NULL;
808 return new WebContentsVideoCaptureDevice(render_process_id, render_view_id);
811 void WebContentsVideoCaptureDevice::AllocateAndStart(
812 const media::VideoCaptureParams& params,
813 scoped_ptr<Client> client) {
814 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
815 core_->AllocateAndStart(params, client.Pass());
818 void WebContentsVideoCaptureDevice::StopAndDeAllocate() {
819 core_->StopAndDeAllocate();
822 } // namespace content