Home | History | Annotate | Download | only in media
      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/renderer_host/media/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/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"
     82 
     83 namespace content {
     84 
     85 namespace {
     86 
     87 // Compute a letterbox region, aligned to even coordinates.
     88 gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size,
     89                                      const gfx::Size& content_size) {
     90 
     91   gfx::Rect result = media::ComputeLetterboxRegion(gfx::Rect(frame_size),
     92                                                    content_size);
     93 
     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())));
     98 
     99   return result;
    100 }
    101 
    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::Time timestamp,
    107     bool frame_captured) {
    108   capture_frame_cb.Run(timestamp, frame_captured);
    109 }
    110 
    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) {}
    119 
    120   virtual bool ShouldCaptureFrame(
    121       base::Time present_time,
    122       scoped_refptr<media::VideoFrame>* storage,
    123       RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback*
    124           deliver_frame_cb) OVERRIDE;
    125 
    126  private:
    127   const VideoCaptureOracle::Event event_type_;
    128   scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
    129 };
    130 
    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.
    135 //
    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.
    142 //
    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<void(
    149       const base::Time&,
    150       const scoped_refptr<media::VideoFrame>&,
    151       const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)>
    152           CaptureCallback;
    153 
    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();
    162 
    163   // content::NotificationObserver implementation.
    164   virtual void Observe(int type,
    165                        const content::NotificationSource& source,
    166                        const content::NotificationDetails& details) OVERRIDE;
    167 
    168  private:
    169   void OnTimer();
    170 
    171   const int render_process_id_;
    172   const int render_view_id_;
    173 
    174   FrameSubscriber paint_subscriber_;
    175   FrameSubscriber timer_subscriber_;
    176   content::NotificationRegistrar registrar_;
    177   CaptureCallback capture_callback_;
    178   base::Timer timer_;
    179 
    180   DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription);
    181 };
    182 
    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.
    187 //
    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);
    195 
    196 // Keeps track of the RenderView to be sourced, and executes copying of the
    197 // backing store on the UI BrowserThread.
    198 //
    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();
    210 
    211   // VideoCaptureMachine overrides.
    212   virtual bool Start(
    213       const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
    214   virtual void Stop() OVERRIDE;
    215 
    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|.
    219   //
    220   // This may be used as a ContentCaptureSubscription::CaptureCallback.
    221   void Capture(
    222       const base::Time& start_time,
    223       const scoped_refptr<media::VideoFrame>& target,
    224       const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
    225           deliver_frame_cb);
    226 
    227   // content::WebContentsObserver implementation.
    228   virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
    229     fullscreen_widget_id_ = routing_id;
    230     RenewFrameSubscription();
    231   }
    232 
    233   virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE {
    234     DCHECK_EQ(fullscreen_widget_id_, routing_id);
    235     fullscreen_widget_id_ = MSG_ROUTING_NONE;
    236     RenewFrameSubscription();
    237   }
    238 
    239   virtual void RenderViewReady() OVERRIDE {
    240     RenewFrameSubscription();
    241   }
    242 
    243   virtual void AboutToNavigateRenderView(RenderViewHost* rvh) OVERRIDE {
    244     RenewFrameSubscription();
    245   }
    246 
    247   virtual void DidNavigateMainFrame(
    248       const LoadCommittedDetails& details,
    249       const FrameNavigateParams& params) OVERRIDE {
    250     RenewFrameSubscription();
    251   }
    252 
    253   virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
    254 
    255  private:
    256   // Starts observing the web contents, returning false if lookup fails.
    257   bool StartObservingWebContents();
    258 
    259   // Helper function to determine the view that we are currently tracking.
    260   RenderWidgetHost* GetTarget();
    261 
    262   // Response callback for RenderWidgetHost::CopyFromBackingStore().
    263   void DidCopyFromBackingStore(
    264       const base::Time& start_time,
    265       const scoped_refptr<media::VideoFrame>& target,
    266       const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
    267           deliver_frame_cb,
    268       bool success,
    269       const SkBitmap& bitmap);
    270 
    271   // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
    272   void DidCopyFromCompositingSurfaceToVideoFrame(
    273       const base::Time& start_time,
    274       const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
    275           deliver_frame_cb,
    276       bool success);
    277 
    278   // Remove the old subscription, and start a new one. This should be called
    279   // after any change to the WebContents that affects the RenderWidgetHost or
    280   // attached views.
    281   void RenewFrameSubscription();
    282 
    283   // Parameters saved in constructor.
    284   const int initial_render_process_id_;
    285   const int initial_render_view_id_;
    286 
    287   // A dedicated worker thread on which SkBitmap->VideoFrame conversion will
    288   // occur. Only used when this activity cannot be done on the GPU.
    289   base::Thread render_thread_;
    290 
    291   // Makes all the decisions about which frames to copy, and how.
    292   scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
    293 
    294   // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
    295   // otherwise.
    296   int fullscreen_widget_id_;
    297 
    298   // Last known RenderView size.
    299   gfx::Size last_view_size_;
    300 
    301   // Responsible for forwarding events from the active RenderWidgetHost to the
    302   // oracle, and initiating captures accordingly.
    303   scoped_ptr<ContentCaptureSubscription> subscription_;
    304 
    305   DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine);
    306 };
    307 
    308 // Responsible for logging the effective frame rate.
    309 // TODO(nick): Make this compatible with the push model and hook it back up.
    310 class VideoFrameDeliveryLog {
    311  public:
    312   VideoFrameDeliveryLog();
    313 
    314   // Treat |frame_number| as having been delivered, and update the
    315   // frame rate statistics accordingly.
    316   void ChronicleFrameDelivery(int frame_number);
    317 
    318  private:
    319   // The following keep track of and log the effective frame rate whenever
    320   // verbose logging is turned on.
    321   base::Time last_frame_rate_log_time_;
    322   int count_frames_rendered_;
    323   int last_frame_number_;
    324 
    325   DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
    326 };
    327 
    328 bool FrameSubscriber::ShouldCaptureFrame(
    329     base::Time present_time,
    330     scoped_refptr<media::VideoFrame>* storage,
    331     DeliverFrameCallback* deliver_frame_cb) {
    332   TRACE_EVENT1("mirroring", "FrameSubscriber::ShouldCaptureFrame",
    333                "instance", this);
    334 
    335   ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
    336   bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture(
    337       event_type_, present_time, storage, &capture_frame_cb);
    338 
    339   *deliver_frame_cb = base::Bind(&InvokeCaptureFrameCallback, capture_frame_cb);
    340   return oracle_decision;
    341 }
    342 
    343 ContentCaptureSubscription::ContentCaptureSubscription(
    344     const RenderWidgetHost& source,
    345     const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
    346     const CaptureCallback& capture_callback)
    347     : render_process_id_(source.GetProcess()->GetID()),
    348       render_view_id_(source.GetRoutingID()),
    349       paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy),
    350       timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy),
    351       capture_callback_(capture_callback),
    352       timer_(true, true) {
    353   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    354 
    355   RenderWidgetHostViewPort* view =
    356       RenderWidgetHostViewPort::FromRWHV(source.GetView());
    357 
    358   // Subscribe to accelerated presents. These will be serviced directly by the
    359   // oracle.
    360   if (view && kAcceleratedSubscriberIsSupported) {
    361     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
    362         new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate,
    363             oracle_proxy));
    364     view->BeginFrameSubscription(subscriber.Pass());
    365   }
    366 
    367   // Subscribe to software paint events. This instance will service these by
    368   // reflecting them back to the WebContentsCaptureMachine via
    369   // |capture_callback|.
    370   registrar_.Add(
    371       this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
    372       Source<RenderWidgetHost>(&source));
    373 
    374   // Subscribe to timer events. This instance will service these as well.
    375   timer_.Start(FROM_HERE, oracle_proxy->capture_period(),
    376                base::Bind(&ContentCaptureSubscription::OnTimer,
    377                           base::Unretained(this)));
    378 }
    379 
    380 ContentCaptureSubscription::~ContentCaptureSubscription() {
    381   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    382   if (kAcceleratedSubscriberIsSupported) {
    383     RenderViewHost* source = RenderViewHost::FromID(render_process_id_,
    384                                                     render_view_id_);
    385     if (source) {
    386       RenderWidgetHostViewPort* view =
    387           RenderWidgetHostViewPort::FromRWHV(source->GetView());
    388       if (view)
    389         view->EndFrameSubscription();
    390     }
    391   }
    392 }
    393 
    394 void ContentCaptureSubscription::Observe(
    395     int type,
    396     const content::NotificationSource& source,
    397     const content::NotificationDetails& details) {
    398   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    399   DCHECK_EQ(NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, type);
    400 
    401   RenderWidgetHostImpl* rwh =
    402       RenderWidgetHostImpl::From(Source<RenderWidgetHost>(source).ptr());
    403 
    404   // This message occurs on window resizes and visibility changes even when
    405   // accelerated compositing is active, so we need to filter out these cases.
    406   if (!rwh || !rwh->GetView() || (rwh->is_accelerated_compositing_active() &&
    407                                   rwh->GetView()->IsSurfaceAvailableForCopy()))
    408     return;
    409 
    410   TRACE_EVENT1("mirroring", "ContentCaptureSubscription::Observe",
    411                "instance", this);
    412 
    413   base::Closure copy_done_callback;
    414   scoped_refptr<media::VideoFrame> frame;
    415   RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
    416   const base::Time start_time = base::Time::Now();
    417   if (paint_subscriber_.ShouldCaptureFrame(start_time,
    418                                            &frame,
    419                                            &deliver_frame_cb)) {
    420     // This message happens just before paint. If we post a task to do the copy,
    421     // it should run soon after the paint.
    422     BrowserThread::PostTask(
    423         BrowserThread::UI, FROM_HERE,
    424         base::Bind(capture_callback_, start_time, frame, deliver_frame_cb));
    425   }
    426 }
    427 
    428 void ContentCaptureSubscription::OnTimer() {
    429   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    430   TRACE_EVENT0("mirroring", "ContentCaptureSubscription::OnTimer");
    431 
    432   scoped_refptr<media::VideoFrame> frame;
    433   RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
    434 
    435   const base::Time start_time = base::Time::Now();
    436   if (timer_subscriber_.ShouldCaptureFrame(start_time,
    437                                            &frame,
    438                                            &deliver_frame_cb)) {
    439     capture_callback_.Run(start_time, frame, deliver_frame_cb);
    440   }
    441 }
    442 
    443 void RenderVideoFrame(const SkBitmap& input,
    444                       const scoped_refptr<media::VideoFrame>& output,
    445                       const base::Callback<void(bool)>& done_cb) {
    446   base::ScopedClosureRunner failure_handler(base::Bind(done_cb, false));
    447 
    448   SkAutoLockPixels locker(input);
    449 
    450   // Sanity-check the captured bitmap.
    451   if (input.empty() ||
    452       !input.readyToDraw() ||
    453       input.config() != SkBitmap::kARGB_8888_Config ||
    454       input.width() < 2 || input.height() < 2) {
    455     DVLOG(1) << "input unacceptable (size="
    456              << input.getSize()
    457              << ", ready=" << input.readyToDraw()
    458              << ", config=" << input.config() << ')';
    459     return;
    460   }
    461 
    462   // Sanity-check the output buffer.
    463   if (output->format() != media::VideoFrame::I420) {
    464     NOTREACHED();
    465     return;
    466   }
    467 
    468   // Calculate the width and height of the content region in the |output|, based
    469   // on the aspect ratio of |input|.
    470   gfx::Rect region_in_frame = ComputeYV12LetterboxRegion(
    471       output->coded_size(), gfx::Size(input.width(), input.height()));
    472 
    473   // Scale the bitmap to the required size, if necessary.
    474   SkBitmap scaled_bitmap;
    475   if (input.width() != region_in_frame.width() ||
    476       input.height() != region_in_frame.height()) {
    477 
    478     skia::ImageOperations::ResizeMethod method;
    479     if (input.width() < region_in_frame.width() ||
    480         input.height() < region_in_frame.height()) {
    481       // Avoid box filtering when magnifying, because it's actually
    482       // nearest-neighbor.
    483       method = skia::ImageOperations::RESIZE_HAMMING1;
    484     } else {
    485       method = skia::ImageOperations::RESIZE_BOX;
    486     }
    487 
    488     TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", output.get(), "Scale");
    489     scaled_bitmap = skia::ImageOperations::Resize(input, method,
    490                                                   region_in_frame.width(),
    491                                                   region_in_frame.height());
    492   } else {
    493     scaled_bitmap = input;
    494   }
    495 
    496   TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", output.get(), "YUV");
    497   {
    498     SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
    499 
    500     media::CopyRGBToVideoFrame(
    501         reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
    502         scaled_bitmap.rowBytes(),
    503         region_in_frame,
    504         output.get());
    505   }
    506 
    507   // The result is now ready.
    508   ignore_result(failure_handler.Release());
    509   done_cb.Run(true);
    510 }
    511 
    512 VideoFrameDeliveryLog::VideoFrameDeliveryLog()
    513     : last_frame_rate_log_time_(),
    514       count_frames_rendered_(0),
    515       last_frame_number_(0) {
    516 }
    517 
    518 void VideoFrameDeliveryLog::ChronicleFrameDelivery(int frame_number) {
    519   // Log frame rate, if verbose logging is turned on.
    520   static const base::TimeDelta kFrameRateLogInterval =
    521       base::TimeDelta::FromSeconds(10);
    522   const base::Time now = base::Time::Now();
    523   if (last_frame_rate_log_time_.is_null()) {
    524     last_frame_rate_log_time_ = now;
    525     count_frames_rendered_ = 0;
    526     last_frame_number_ = frame_number;
    527   } else {
    528     ++count_frames_rendered_;
    529     const base::TimeDelta elapsed = now - last_frame_rate_log_time_;
    530     if (elapsed >= kFrameRateLogInterval) {
    531       const double measured_fps =
    532           count_frames_rendered_ / elapsed.InSecondsF();
    533       const int frames_elapsed = frame_number - last_frame_number_;
    534       const int count_frames_dropped = frames_elapsed - count_frames_rendered_;
    535       DCHECK_LE(0, count_frames_dropped);
    536       UMA_HISTOGRAM_PERCENTAGE(
    537           "TabCapture.FrameDropPercentage",
    538           (count_frames_dropped * 100 + frames_elapsed / 2) / frames_elapsed);
    539       UMA_HISTOGRAM_COUNTS(
    540           "TabCapture.FrameRate",
    541           static_cast<int>(measured_fps));
    542       VLOG(1) << "Current measured frame rate for "
    543               << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS.";
    544       last_frame_rate_log_time_ = now;
    545       count_frames_rendered_ = 0;
    546       last_frame_number_ = frame_number;
    547     }
    548   }
    549 }
    550 
    551 WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id,
    552                                                      int render_view_id)
    553     : initial_render_process_id_(render_process_id),
    554       initial_render_view_id_(render_view_id),
    555       render_thread_("WebContentsVideo_RenderThread"),
    556       fullscreen_widget_id_(MSG_ROUTING_NONE) {}
    557 
    558 WebContentsCaptureMachine::~WebContentsCaptureMachine() {}
    559 
    560 bool WebContentsCaptureMachine::Start(
    561     const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
    562   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    563   DCHECK(!started_);
    564 
    565   DCHECK(oracle_proxy.get());
    566   oracle_proxy_ = oracle_proxy;
    567 
    568   if (!render_thread_.Start()) {
    569     DVLOG(1) << "Failed to spawn render thread.";
    570     return false;
    571   }
    572 
    573   if (!StartObservingWebContents())
    574     return false;
    575 
    576   started_ = true;
    577   return true;
    578 }
    579 
    580 void WebContentsCaptureMachine::Stop() {
    581   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    582   subscription_.reset();
    583   if (web_contents()) {
    584     web_contents()->DecrementCapturerCount();
    585     Observe(NULL);
    586   }
    587   render_thread_.Stop();
    588   started_ = false;
    589 }
    590 
    591 void WebContentsCaptureMachine::Capture(
    592     const base::Time& start_time,
    593     const scoped_refptr<media::VideoFrame>& target,
    594     const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
    595         deliver_frame_cb) {
    596   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    597 
    598   RenderWidgetHost* rwh = GetTarget();
    599   RenderWidgetHostViewPort* view =
    600       rwh ? RenderWidgetHostViewPort::FromRWHV(rwh->GetView()) : NULL;
    601   if (!view || !rwh) {
    602     deliver_frame_cb.Run(base::Time(), false);
    603     return;
    604   }
    605 
    606   gfx::Size video_size = target->coded_size();
    607   gfx::Size view_size = view->GetViewBounds().size();
    608   gfx::Size fitted_size;
    609   if (!view_size.IsEmpty()) {
    610     fitted_size = ComputeYV12LetterboxRegion(video_size, view_size).size();
    611   }
    612   if (view_size != last_view_size_) {
    613     last_view_size_ = view_size;
    614 
    615     // Measure the number of kilopixels.
    616     UMA_HISTOGRAM_COUNTS_10000(
    617         "TabCapture.ViewChangeKiloPixels",
    618         view_size.width() * view_size.height() / 1024);
    619   }
    620 
    621   if (!view->IsSurfaceAvailableForCopy()) {
    622     // Fallback to the more expensive renderer-side copy if the surface and
    623     // backing store are not accessible.
    624     rwh->GetSnapshotFromRenderer(
    625         gfx::Rect(),
    626         base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
    627                    this->AsWeakPtr(), start_time, target, deliver_frame_cb));
    628   } else if (view->CanCopyToVideoFrame()) {
    629     view->CopyFromCompositingSurfaceToVideoFrame(
    630         gfx::Rect(view_size),
    631         target,
    632         base::Bind(&WebContentsCaptureMachine::
    633                         DidCopyFromCompositingSurfaceToVideoFrame,
    634                    this->AsWeakPtr(), start_time, deliver_frame_cb));
    635   } else {
    636     rwh->CopyFromBackingStore(
    637         gfx::Rect(),
    638         fitted_size,  // Size here is a request not always honored.
    639         base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
    640                    this->AsWeakPtr(), start_time, target, deliver_frame_cb));
    641   }
    642 }
    643 
    644 bool WebContentsCaptureMachine::StartObservingWebContents() {
    645   // Look-up the RenderViewHost and, from that, the WebContents that wraps it.
    646   // If successful, begin observing the WebContents instance.
    647   //
    648   // Why this can be unsuccessful: The request for mirroring originates in a
    649   // render process, and this request is based on the current RenderView
    650   // associated with a tab.  However, by the time we get up-and-running here,
    651   // there have been multiple back-and-forth IPCs between processes, as well as
    652   // a bit of indirection across threads.  It's easily possible that, in the
    653   // meantime, the original RenderView may have gone away.
    654   RenderViewHost* const rvh =
    655       RenderViewHost::FromID(initial_render_process_id_,
    656                              initial_render_view_id_);
    657   DVLOG_IF(1, !rvh) << "RenderViewHost::FromID("
    658                     << initial_render_process_id_ << ", "
    659                     << initial_render_view_id_ << ") returned NULL.";
    660   Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL);
    661 
    662   WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
    663   if (contents) {
    664     contents->IncrementCapturerCount();
    665     fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID();
    666     RenewFrameSubscription();
    667     return true;
    668   }
    669 
    670   DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL.";
    671   return false;
    672 }
    673 
    674 void WebContentsCaptureMachine::WebContentsDestroyed(
    675     WebContents* web_contents) {
    676   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    677 
    678   subscription_.reset();
    679   web_contents->DecrementCapturerCount();
    680   oracle_proxy_->ReportError();
    681 }
    682 
    683 RenderWidgetHost* WebContentsCaptureMachine::GetTarget() {
    684   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    685   if (!web_contents())
    686     return NULL;
    687 
    688   RenderWidgetHost* rwh = NULL;
    689   if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
    690     RenderProcessHost* process = web_contents()->GetRenderProcessHost();
    691     if (process)
    692       rwh = RenderWidgetHost::FromID(process->GetID(), fullscreen_widget_id_);
    693   } else {
    694     rwh = web_contents()->GetRenderViewHost();
    695   }
    696 
    697   return rwh;
    698 }
    699 
    700 void WebContentsCaptureMachine::DidCopyFromBackingStore(
    701     const base::Time& start_time,
    702     const scoped_refptr<media::VideoFrame>& target,
    703     const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
    704         deliver_frame_cb,
    705     bool success,
    706     const SkBitmap& bitmap) {
    707   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    708 
    709   base::Time now = base::Time::Now();
    710   if (success) {
    711     UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time);
    712     TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", target.get(),
    713                                  "Render");
    714     render_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
    715         &RenderVideoFrame, bitmap, target,
    716         base::Bind(deliver_frame_cb, start_time)));
    717   } else {
    718     // Capture can fail due to transient issues, so just skip this frame.
    719     DVLOG(1) << "CopyFromBackingStore failed; skipping frame.";
    720     deliver_frame_cb.Run(start_time, false);
    721   }
    722 }
    723 
    724 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
    725     const base::Time& start_time,
    726     const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
    727         deliver_frame_cb,
    728     bool success) {
    729   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    730   base::Time now = base::Time::Now();
    731 
    732   if (success) {
    733     UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time);
    734   } else {
    735     // Capture can fail due to transient issues, so just skip this frame.
    736     DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame.";
    737   }
    738   deliver_frame_cb.Run(start_time, success);
    739 }
    740 
    741 void WebContentsCaptureMachine::RenewFrameSubscription() {
    742   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    743 
    744   // Always destroy the old subscription before creating a new one.
    745   subscription_.reset();
    746 
    747   RenderWidgetHost* rwh = GetTarget();
    748   if (!rwh || !rwh->GetView())
    749     return;
    750 
    751   subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
    752       base::Bind(&WebContentsCaptureMachine::Capture, this->AsWeakPtr())));
    753 }
    754 
    755 }  // namespace
    756 
    757 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
    758     int render_process_id, int render_view_id)
    759     : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
    760         new WebContentsCaptureMachine(render_process_id, render_view_id)))) {}
    761 
    762 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() {
    763   DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying.";
    764 }
    765 
    766 // static
    767 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create(
    768     const std::string& device_id) {
    769   // Parse device_id into render_process_id and render_view_id.
    770   int render_process_id = -1;
    771   int render_view_id = -1;
    772   if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
    773            device_id, &render_process_id, &render_view_id)) {
    774     return NULL;
    775   }
    776 
    777   return new WebContentsVideoCaptureDevice(render_process_id, render_view_id);
    778 }
    779 
    780 void WebContentsVideoCaptureDevice::AllocateAndStart(
    781     const media::VideoCaptureParams& params,
    782     scoped_ptr<Client> client) {
    783   DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
    784   impl_->AllocateAndStart(params, client.Pass());
    785 }
    786 
    787 void WebContentsVideoCaptureDevice::StopAndDeAllocate() {
    788   impl_->StopAndDeAllocate();
    789 }
    790 
    791 }  // namespace content
    792