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.
15//
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.
22//
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.
29//
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.
34//
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):
39//
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
43//
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.
47//
48// Turning on verbose logging will cause the effective frame rate to be logged
49// at 5-second intervals.
50
51#include "content/browser/media/capture/web_contents_video_capture_device.h"
52
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"
83
84namespace content {
85
86namespace {
87
88// Compute a letterbox region, aligned to even coordinates.
89gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size,
90                                     const gfx::Size& content_size) {
91
92  gfx::Rect result = media::ComputeLetterboxRegion(gfx::Rect(frame_size),
93                                                   content_size);
94
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())));
99
100  return result;
101}
102
103void DeleteOnWorkerThread(scoped_ptr<base::Thread> render_thread,
104                          const base::Closure& callback) {
105  render_thread.reset();
106
107  // After thread join call the callback on UI thread.
108  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
109}
110
111// Responsible for logging the effective frame rate.
112class VideoFrameDeliveryLog {
113 public:
114  VideoFrameDeliveryLog();
115
116  // Report that the frame posted with |frame_time| has been delivered.
117  void ChronicleFrameDelivery(base::TimeTicks frame_time);
118
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_;
124
125  DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
126};
127
128// FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible
129// with RenderWidgetHostViewFrameSubscriber. We create one per event type.
130class 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) {}
138
139  virtual bool ShouldCaptureFrame(
140      base::TimeTicks present_time,
141      scoped_refptr<media::VideoFrame>* storage,
142      RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback*
143          deliver_frame_cb) OVERRIDE;
144
145 private:
146  const VideoCaptureOracle::Event event_type_;
147  scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
148  VideoFrameDeliveryLog* const delivery_log_;
149};
150
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.
155//
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.
162//
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.
166class 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;
173
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();
182
183  // content::NotificationObserver implementation.
184  virtual void Observe(int type,
185                       const content::NotificationSource& source,
186                       const content::NotificationDetails& details) OVERRIDE;
187
188 private:
189  void OnTimer();
190
191  const int render_process_id_;
192  const int render_view_id_;
193
194  VideoFrameDeliveryLog delivery_log_;
195  FrameSubscriber paint_subscriber_;
196  FrameSubscriber timer_subscriber_;
197  content::NotificationRegistrar registrar_;
198  CaptureCallback capture_callback_;
199  base::Timer timer_;
200
201  DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription);
202};
203
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.
208//
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.
213void RenderVideoFrame(const SkBitmap& input,
214                      const scoped_refptr<media::VideoFrame>& output,
215                      const base::Callback<void(bool)>& done_cb);
216
217// Keeps track of the RenderView to be sourced, and executes copying of the
218// backing store on the UI BrowserThread.
219//
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.
224class WebContentsCaptureMachine
225    : public VideoCaptureMachine,
226      public WebContentsObserver {
227 public:
228  WebContentsCaptureMachine(int render_process_id, int render_view_id);
229  virtual ~WebContentsCaptureMachine();
230
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;
235
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|.
239  //
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);
245
246  // content::WebContentsObserver implementation.
247  virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
248    fullscreen_widget_id_ = routing_id;
249    RenewFrameSubscription();
250  }
251
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();
256  }
257
258  virtual void RenderViewReady() OVERRIDE {
259    RenewFrameSubscription();
260  }
261
262  virtual void AboutToNavigateRenderView(RenderViewHost* rvh) OVERRIDE {
263    RenewFrameSubscription();
264  }
265
266  virtual void DidNavigateMainFrame(
267      const LoadCommittedDetails& details,
268      const FrameNavigateParams& params) OVERRIDE {
269    RenewFrameSubscription();
270  }
271
272  virtual void WebContentsDestroyed() OVERRIDE;
273
274 private:
275  // Starts observing the web contents, returning false if lookup fails.
276  bool StartObservingWebContents();
277
278  // Helper function to determine the view that we are currently tracking.
279  RenderWidgetHost* GetTarget();
280
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);
289
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);
296
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();
301
302  // Parameters saved in constructor.
303  const int initial_render_process_id_;
304  const int initial_render_view_id_;
305
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_;
309
310  // Makes all the decisions about which frames to copy, and how.
311  scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
312
313  // Video capture parameters that this machine is started with.
314  media::VideoCaptureParams capture_params_;
315
316  // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
317  // otherwise.
318  int fullscreen_widget_id_;
319
320  // Last known RenderView size.
321  gfx::Size last_view_size_;
322
323  // Responsible for forwarding events from the active RenderWidgetHost to the
324  // oracle, and initiating captures accordingly.
325  scoped_ptr<ContentCaptureSubscription> subscription_;
326
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_;
330
331  DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine);
332};
333
334bool 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);
340
341  ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
342  bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture(
343      event_type_, present_time, storage, &capture_frame_cb);
344
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;
350}
351
352ContentCaptureSubscription::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));
366
367  RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
368      source.GetView());
369
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());
377  }
378
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));
385
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)));
390}
391
392ContentCaptureSubscription::~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();
402    }
403  }
404}
405
406void 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);
412
413  RenderWidgetHostImpl* rwh =
414      RenderWidgetHostImpl::From(Source<RenderWidgetHost>(source).ptr());
415
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
428
429  TRACE_EVENT1("mirroring", "ContentCaptureSubscription::Observe",
430               "instance", this);
431
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));
444  }
445}
446
447void ContentCaptureSubscription::OnTimer() {
448  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449  TRACE_EVENT0("mirroring", "ContentCaptureSubscription::OnTimer");
450
451  scoped_refptr<media::VideoFrame> frame;
452  RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
453
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);
459  }
460}
461
462void 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));
466
467  SkAutoLockPixels locker(input);
468
469  // Sanity-check the captured bitmap.
470  if (input.empty() ||
471      !input.readyToDraw() ||
472      input.config() != SkBitmap::kARGB_8888_Config ||
473      input.width() < 2 || input.height() < 2) {
474    DVLOG(1) << "input unacceptable (size="
475             << input.getSize()
476             << ", ready=" << input.readyToDraw()
477             << ", config=" << input.config() << ')';
478    return;
479  }
480
481  // Sanity-check the output buffer.
482  if (output->format() != media::VideoFrame::I420) {
483    NOTREACHED();
484    return;
485  }
486
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()));
491
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()) {
496
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;
505    }
506
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;
513  }
514
515  TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", output.get(), "YUV");
516  {
517    SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
518
519    media::CopyRGBToVideoFrame(
520        reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
521        scaled_bitmap.rowBytes(),
522        region_in_frame,
523        output.get());
524  }
525
526  // The result is now ready.
527  ignore_result(failure_handler.Release());
528  done_cb.Run(true);
529}
530
531VideoFrameDeliveryLog::VideoFrameDeliveryLog()
532    : last_frame_rate_log_time_(),
533      count_frames_rendered_(0) {
534}
535
536void 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;
556    }
557  }
558}
559
560WebContentsCaptureMachine::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) {}
566
567WebContentsCaptureMachine::~WebContentsCaptureMachine() {
568  BrowserThread::PostBlockingPoolTask(
569      FROM_HERE,
570      base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_),
571                 base::Bind(&base::DoNothing)));
572}
573
574bool WebContentsCaptureMachine::Start(
575    const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
576    const media::VideoCaptureParams& params) {
577  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
578  DCHECK(!started_);
579
580  DCHECK(oracle_proxy.get());
581  oracle_proxy_ = oracle_proxy;
582  capture_params_ = params;
583
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;
589  }
590
591  if (!StartObservingWebContents()) {
592    DVLOG(1) << "Failed to observe web contents.";
593    render_thread_.reset();
594    return false;
595  }
596
597  started_ = true;
598  return true;
599}
600
601void 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);
607  }
608
609  // Any callback that intend to use render_thread_ will not work after it is
610  // passed.
611  weak_ptr_factory_.InvalidateWeakPtrs();
612
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));
619
620  started_ = false;
621}
622
623void 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));
629
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;
636  }
637
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();
643  }
644  if (view_size != last_view_size_) {
645    last_view_size_ = view_size;
646
647    // Measure the number of kilopixels.
648    UMA_HISTOGRAM_COUNTS_10000(
649        "TabCapture.ViewChangeKiloPixels",
650        view_size.width() * view_size.height() / 1024);
651  }
652
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        SkBitmap::kARGB_8888_Config);
671  }
672}
673
674bool WebContentsCaptureMachine::StartObservingWebContents() {
675  // Look-up the RenderViewHost and, from that, the WebContents that wraps it.
676  // If successful, begin observing the WebContents instance.
677  //
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);
691
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;
698  }
699
700  DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL.";
701  return false;
702}
703
704void WebContentsCaptureMachine::WebContentsDestroyed() {
705  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
706
707  subscription_.reset();
708  web_contents()->DecrementCapturerCount();
709  oracle_proxy_->ReportError("WebContentsDestroyed()");
710}
711
712RenderWidgetHost* WebContentsCaptureMachine::GetTarget() {
713  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
714  if (!web_contents())
715    return NULL;
716
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();
724  }
725
726  return rwh;
727}
728
729void 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));
737
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);
751  }
752}
753
754void 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();
761
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.";
767  }
768  deliver_frame_cb.Run(start_time, success);
769}
770
771void WebContentsCaptureMachine::RenewFrameSubscription() {
772  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
773
774  // Always destroy the old subscription before creating a new one.
775  subscription_.reset();
776
777  RenderWidgetHost* rwh = GetTarget();
778  if (!rwh || !rwh->GetView())
779    return;
780
781  subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
782      base::Bind(&WebContentsCaptureMachine::Capture,
783                 weak_ptr_factory_.GetWeakPtr())));
784}
785
786}  // namespace
787
788WebContentsVideoCaptureDevice::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)))) {}
792
793WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() {
794  DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying.";
795}
796
797// static
798media::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;
806  }
807
808  return new WebContentsVideoCaptureDevice(render_process_id, render_view_id);
809}
810
811void 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());
816}
817
818void WebContentsVideoCaptureDevice::StopAndDeAllocate() {
819  core_->StopAndDeAllocate();
820}
821
822}  // namespace content
823