| Index: content/browser/media/capture/web_contents_video_capture_device.cc
|
| diff --git a/content/browser/media/capture/web_contents_video_capture_device.cc b/content/browser/media/capture/web_contents_video_capture_device.cc
|
| index aae4ed1de690b7d6c0d94d6dca7dbe580873eec1..d36e816ccedce70c81d27bad9cfccb2e38517286 100644
|
| --- a/content/browser/media/capture/web_contents_video_capture_device.cc
|
| +++ b/content/browser/media/capture/web_contents_video_capture_device.cc
|
| @@ -1,49 +1,6 @@
|
| // Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
| -//
|
| -// Implementation notes: This needs to work on a variety of hardware
|
| -// configurations where the speed of the CPU and GPU greatly affect overall
|
| -// performance. Spanning several threads, the process of capturing has been
|
| -// split up into four conceptual stages:
|
| -//
|
| -// 1. Reserve Buffer: Before a frame can be captured, a slot in the client's
|
| -// shared-memory IPC buffer is reserved. There are only a few of these;
|
| -// when they run out, it indicates that the downstream client -- likely a
|
| -// video encoder -- is the performance bottleneck, and that the rate of
|
| -// frame capture should be throttled back.
|
| -//
|
| -// 2. Capture: A bitmap is snapshotted/copied from the RenderWidget's backing
|
| -// store. This is initiated on the UI BrowserThread, and often occurs
|
| -// asynchronously. Where supported, the GPU scales and color converts
|
| -// frames to our desired size, and the readback happens directly into the
|
| -// shared-memory buffer. But this is not always possible, particularly when
|
| -// accelerated compositing is disabled.
|
| -//
|
| -// 3. Render (if needed): If the web contents cannot be captured directly into
|
| -// our target size and color format, scaling and colorspace conversion must
|
| -// be done on the CPU. A dedicated thread is used for this operation, to
|
| -// avoid blocking the UI thread. The Render stage always reads from a
|
| -// bitmap returned by Capture, and writes into the reserved slot in the
|
| -// shared-memory buffer.
|
| -//
|
| -// 4. Deliver: The rendered video frame is returned to the client (which
|
| -// implements the VideoCaptureDevice::Client interface). Because all
|
| -// paths have written the frame into the IPC buffer, this step should
|
| -// never need to do an additional copy of the pixel data.
|
| -//
|
| -// In the best-performing case, the Render step is bypassed: Capture produces
|
| -// ready-to-Deliver frames. But when accelerated readback is not possible, the
|
| -// system is designed so that Capture and Render may run concurrently. A timing
|
| -// diagram helps illustrate this point (@30 FPS):
|
| -//
|
| -// Time: 0ms 33ms 66ms 99ms
|
| -// thread1: |-Capture-f1------v |-Capture-f2------v |-Capture-f3----v |-Capt
|
| -// thread2: |-Render-f1-----v |-Render-f2-----v |-Render-f3
|
| -//
|
| -// In the above example, both capturing and rendering *each* take almost the
|
| -// full 33 ms available between frames, yet we see that the required throughput
|
| -// is obtained.
|
|
|
| #include "content/browser/media/capture/web_contents_video_capture_device.h"
|
|
|
| @@ -51,19 +8,14 @@
|
|
|
| #include <algorithm>
|
| #include <memory>
|
| -#include <utility>
|
| +#include <string>
|
|
|
| #include "base/bind.h"
|
| #include "base/callback_helpers.h"
|
| #include "base/location.h"
|
| #include "base/logging.h"
|
| #include "base/macros.h"
|
| -#include "base/memory/ptr_util.h"
|
| #include "base/memory/weak_ptr.h"
|
| -#include "base/sequenced_task_runner.h"
|
| -#include "base/single_thread_task_runner.h"
|
| -#include "base/threading/thread.h"
|
| -#include "base/threading/thread_checker.h"
|
| #include "base/time/time.h"
|
| #include "build/build_config.h"
|
| #include "content/browser/media/capture/cursor_renderer.h"
|
| @@ -77,16 +29,11 @@
|
| #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "content/public/browser/web_contents_media_capture_id.h"
|
| -#include "media/base/bind_to_current_loop.h"
|
| #include "media/base/video_frame_metadata.h"
|
| -#include "media/base/video_util.h"
|
| #include "media/capture/content/screen_capture_device_core.h"
|
| #include "media/capture/content/thread_safe_capture_oracle.h"
|
| #include "media/capture/content/video_capture_oracle.h"
|
| #include "media/capture/video_capture_types.h"
|
| -#include "skia/ext/image_operations.h"
|
| -#include "third_party/skia/include/core/SkBitmap.h"
|
| -#include "third_party/skia/include/core/SkColor.h"
|
| #include "ui/base/layout.h"
|
| #include "ui/gfx/geometry/dip_util.h"
|
| #include "ui/gfx/geometry/size_conversions.h"
|
| @@ -95,32 +42,22 @@ namespace content {
|
|
|
| namespace {
|
|
|
| -enum InteractiveModeSettings {
|
| - // Minimum amount of time for which there should be no animation detected
|
| - // to consider interactive mode being active. This is to prevent very brief
|
| - // periods of animated content not being detected (due to CPU fluctations for
|
| - // example) from causing a flip-flop on interactive mode.
|
| - kMinPeriodNoAnimationMillis = 3000
|
| -};
|
| -
|
| -void DeleteOnWorkerThread(std::unique_ptr<base::Thread> render_thread,
|
| - const base::Closure& callback) {
|
| - render_thread.reset();
|
| -
|
| - // After thread join call the callback on UI thread.
|
| - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
|
| -}
|
| +// Minimum amount of time for which there should be no animation detected
|
| +// to consider interactive mode being active. This is to prevent very brief
|
| +// periods of animated content not being detected (due to CPU fluctations for
|
| +// example) from causing a flip-flop on interactive mode.
|
| +constexpr int64_t kMinPeriodNoAnimationMillis = 3000;
|
|
|
| // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible
|
| -// with RenderWidgetHostViewFrameSubscriber. We create one per event type.
|
| +// with RenderWidgetHostViewFrameSubscriber. One is created per event type.
|
| class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
|
| public:
|
| FrameSubscriber(media::VideoCaptureOracle::Event event_type,
|
| - const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle,
|
| + scoped_refptr<media::ThreadSafeCaptureOracle> oracle,
|
| base::WeakPtr<content::CursorRenderer> cursor_renderer,
|
| base::WeakPtr<content::WindowActivityTracker> tracker)
|
| : event_type_(event_type),
|
| - oracle_proxy_(oracle),
|
| + oracle_proxy_(std::move(oracle)),
|
| cursor_renderer_(cursor_renderer),
|
| window_activity_tracker_(tracker),
|
| weak_ptr_factory_(this) {}
|
| @@ -145,13 +82,13 @@ class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
|
|
|
| private:
|
| const media::VideoCaptureOracle::Event event_type_;
|
| - scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_;
|
| + const scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_;
|
| // We need a weak pointer since FrameSubscriber is owned externally and
|
| // may outlive the cursor renderer.
|
| - base::WeakPtr<CursorRenderer> cursor_renderer_;
|
| + const base::WeakPtr<CursorRenderer> cursor_renderer_;
|
| // We need a weak pointer since FrameSubscriber is owned externally and
|
| // may outlive the ui activity tracker.
|
| - base::WeakPtr<WindowActivityTracker> window_activity_tracker_;
|
| + const base::WeakPtr<WindowActivityTracker> window_activity_tracker_;
|
| base::WeakPtrFactory<FrameSubscriber> weak_ptr_factory_;
|
| };
|
|
|
| @@ -162,29 +99,31 @@ class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
|
| //
|
| // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in
|
| // the RenderWidgetHostView, to process compositor updates, and (b) occasionally
|
| -// initiating forced, non-event-driven captures needed by downstream consumers
|
| -// that request "refresh frames" of unchanged content.
|
| +// using the capture callback to force additional captures that are needed for
|
| +// things like mouse cursor rendering updates and/or refresh frames.
|
| //
|
| // All of this happens on the UI thread, although the
|
| // RenderWidgetHostViewFrameSubscriber we install may be dispatching updates
|
| // autonomously on some other thread.
|
| class ContentCaptureSubscription {
|
| public:
|
| - typedef base::Callback<void(
|
| - const base::TimeTicks&,
|
| - const scoped_refptr<media::VideoFrame>&,
|
| - const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)>
|
| - CaptureCallback;
|
| + using CaptureCallback = base::Callback<void(
|
| + base::TimeTicks,
|
| + scoped_refptr<media::VideoFrame>,
|
| + const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)>;
|
|
|
| - // Create a subscription. Whenever a manual capture is required, the
|
| + // Create a subscription. Whenever a non-compositor capture is required, the
|
| // subscription will invoke |capture_callback| on the UI thread to do the
|
| // work.
|
| ContentCaptureSubscription(
|
| base::WeakPtr<RenderWidgetHostViewBase> source_view,
|
| - const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
|
| + scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy,
|
| const CaptureCallback& capture_callback);
|
| ~ContentCaptureSubscription();
|
|
|
| + // Called when a condition occurs that requires a refresh frame be
|
| + // captured. The subscribers will determine whether a frame was recently
|
| + // captured, or if a non-compositor initiated capture must be made.
|
| void MaybeCaptureForRefresh();
|
|
|
| private:
|
| @@ -209,22 +148,8 @@ class ContentCaptureSubscription {
|
| DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription);
|
| };
|
|
|
| -// Render the SkBitmap |input| into the given VideoFrame buffer |output|, then
|
| -// invoke |done_cb| to indicate success or failure. |input| is expected to be
|
| -// ARGB. |output| must be YV12 or I420. Colorspace conversion is always done.
|
| -// Scaling and letterboxing will be done as needed.
|
| -//
|
| -// This software implementation should be used only when GPU acceleration of
|
| -// these activities is not possible. This operation may be expensive (tens to
|
| -// hundreds of milliseconds), so the caller should ensure that it runs on a
|
| -// thread where such a pause would cause UI jank.
|
| -void RenderVideoFrame(
|
| - const SkBitmap& input,
|
| - const scoped_refptr<media::VideoFrame>& output,
|
| - const base::Callback<void(const gfx::Rect&, bool)>& done_cb);
|
| -
|
| // Renews capture subscriptions based on feedback from WebContentsTracker, and
|
| -// also executes copying of the backing store on the UI BrowserThread.
|
| +// also executes non-compositor-initiated frame captures.
|
| class WebContentsCaptureMachine : public media::VideoCaptureMachine {
|
| public:
|
| WebContentsCaptureMachine(int render_process_id,
|
| @@ -244,20 +169,20 @@ class WebContentsCaptureMachine : public media::VideoCaptureMachine {
|
| }
|
| void MaybeCaptureForRefresh() override;
|
|
|
| - // Starts a copy from the backing store or the composited surface. Must be run
|
| - // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation
|
| - // completes. The copy will occur to |target|.
|
| + private:
|
| + // Called for cases where events other than compositor updates require a frame
|
| + // capture from the composited surface. Must be run on the UI BrowserThread.
|
| + // |deliver_frame_cb| will be run when the operation completes. The copy will
|
| + // populate |target|.
|
| //
|
| - // This may be used as a ContentCaptureSubscription::CaptureCallback.
|
| - void Capture(const base::TimeTicks& start_time,
|
| - const scoped_refptr<media::VideoFrame>& target,
|
| + // This method is only used by a ContentCaptureSubscription::CaptureCallback.
|
| + void Capture(base::TimeTicks start_time,
|
| + scoped_refptr<media::VideoFrame> target,
|
| const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| deliver_frame_cb);
|
|
|
| - private:
|
| - bool InternalStart(
|
| - const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
|
| - const media::VideoCaptureParams& params);
|
| + bool InternalStart(scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy,
|
| + const media::VideoCaptureParams& params);
|
| void InternalSuspend();
|
| void InternalResume();
|
| void InternalStop(const base::Closure& callback);
|
| @@ -267,17 +192,8 @@ class WebContentsCaptureMachine : public media::VideoCaptureMachine {
|
| // Computes the preferred size of the target RenderWidget for optimal capture.
|
| gfx::Size ComputeOptimalViewSize() const;
|
|
|
| - // Response callback for RenderWidgetHost::CopyFromBackingStore().
|
| - void DidCopyFromBackingStore(
|
| - const base::TimeTicks& start_time,
|
| - const scoped_refptr<media::VideoFrame>& target,
|
| - const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| - deliver_frame_cb,
|
| - const SkBitmap& bitmap,
|
| - ReadbackResponse response);
|
| -
|
| // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
|
| - void DidCopyFromCompositingSurfaceToVideoFrame(
|
| + void DidCopyToVideoFrame(
|
| const base::TimeTicks& start_time,
|
| const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| deliver_frame_cb,
|
| @@ -305,10 +221,6 @@ class WebContentsCaptureMachine : public media::VideoCaptureMachine {
|
| // option in the WebContentsVideoCaptureDevice device ID.
|
| const bool auto_throttling_enabled_;
|
|
|
| - // A dedicated worker thread on which SkBitmap->VideoFrame conversion will
|
| - // occur. Only used when this activity cannot be done on the GPU.
|
| - std::unique_ptr<base::Thread> render_thread_;
|
| -
|
| // Makes all the decisions about which frames to copy, and how.
|
| scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_;
|
|
|
| @@ -340,15 +252,17 @@ bool FrameSubscriber::ShouldCaptureFrame(
|
| this);
|
|
|
| media::ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
|
| + if (!oracle_proxy_->ObserveEventAndDecideCapture(
|
| + event_type_, damage_rect, present_time, storage, &capture_frame_cb)) {
|
| + return false;
|
| + }
|
|
|
| - bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture(
|
| - event_type_, damage_rect, present_time, storage, &capture_frame_cb);
|
| -
|
| - if (!capture_frame_cb.is_null())
|
| - *deliver_frame_cb =
|
| - base::Bind(&FrameSubscriber::DidCaptureFrame,
|
| - weak_ptr_factory_.GetWeakPtr(), capture_frame_cb, *storage);
|
| - return oracle_decision;
|
| + DCHECK(*storage);
|
| + DCHECK(!capture_frame_cb.is_null());
|
| + *deliver_frame_cb =
|
| + base::Bind(&FrameSubscriber::DidCaptureFrame,
|
| + weak_ptr_factory_.GetWeakPtr(), capture_frame_cb, *storage);
|
| + return true;
|
| }
|
|
|
| void FrameSubscriber::DidCaptureFrame(
|
| @@ -359,14 +273,12 @@ void FrameSubscriber::DidCaptureFrame(
|
| base::TimeTicks timestamp,
|
| const gfx::Rect& region_in_frame,
|
| bool success) {
|
| - // We can get a callback in the shutdown sequence for the browser main loop
|
| - // and this can result in a DCHECK failure. Avoid this by doing DCHECK only
|
| - // on success.
|
| if (success) {
|
| + // We can get a callback in the shutdown sequence for the browser main loop
|
| + // and this can result in a DCHECK failure. Avoid this by doing DCHECK only
|
| + // on success.
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - // TODO(isheriff): Unclear if taking a snapshot of cursor here affects user
|
| - // experience in any particular scenarios. Doing it prior to capture may
|
| - // require evaluating region_in_frame in this file.
|
| +
|
| if (frame_subscriber_ && frame_subscriber_->cursor_renderer_) {
|
| CursorRenderer* cursor_renderer =
|
| frame_subscriber_->cursor_renderer_.get();
|
| @@ -381,11 +293,9 @@ void FrameSubscriber::DidCaptureFrame(
|
| }
|
|
|
| bool FrameSubscriber::IsUserInteractingWithContent() {
|
| + bool ui_activity = window_activity_tracker_ &&
|
| + window_activity_tracker_->IsUiInteractionActive();
|
| bool interactive_mode = false;
|
| - bool ui_activity = false;
|
| - if (window_activity_tracker_) {
|
| - ui_activity = window_activity_tracker_->IsUiInteractionActive();
|
| - }
|
| if (cursor_renderer_) {
|
| bool animation_active =
|
| (base::TimeTicks::Now() -
|
| @@ -393,7 +303,7 @@ bool FrameSubscriber::IsUserInteractingWithContent() {
|
| base::TimeDelta::FromMilliseconds(kMinPeriodNoAnimationMillis);
|
| if (ui_activity && !animation_active) {
|
| interactive_mode = true;
|
| - } else if (animation_active && window_activity_tracker_.get()) {
|
| + } else if (animation_active && window_activity_tracker_) {
|
| window_activity_tracker_->Reset();
|
| }
|
| }
|
| @@ -402,7 +312,7 @@ bool FrameSubscriber::IsUserInteractingWithContent() {
|
|
|
| ContentCaptureSubscription::ContentCaptureSubscription(
|
| base::WeakPtr<RenderWidgetHostViewBase> source_view,
|
| - const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
|
| + scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy,
|
| const CaptureCallback& capture_callback)
|
| : source_view_(source_view), capture_callback_(capture_callback) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| @@ -413,6 +323,10 @@ ContentCaptureSubscription::ContentCaptureSubscription(
|
| window_activity_tracker_ =
|
| WindowActivityTracker::Create(source_view_->GetNativeView());
|
| #endif
|
| + // Subscriptions for refresh frames and mouse cursor updates. When events
|
| + // outside of the normal content change/compositing workflow occur, these
|
| + // decide whether extra frames need to be captured. Such extra frame captures
|
| + // are initiated by running the |capture_callback_|.
|
| refresh_subscriber_.reset(new FrameSubscriber(
|
| media::VideoCaptureOracle::kActiveRefreshRequest, oracle_proxy,
|
| cursor_renderer_ ? cursor_renderer_->GetWeakPtr()
|
| @@ -426,8 +340,12 @@ ContentCaptureSubscription::ContentCaptureSubscription(
|
| window_activity_tracker_ ? window_activity_tracker_->GetWeakPtr()
|
| : base::WeakPtr<WindowActivityTracker>()));
|
|
|
| - // Subscribe to compositor updates. These will be serviced directly by the
|
| - // oracle.
|
| + // Main capture path: Subscribing to compositor updates. This means that any
|
| + // time the tab content has changed and compositing has taken place, the
|
| + // RenderWidgetHostView will consult the FrameSubscriber (which consults the
|
| + // oracle) to determine whether a frame should be captured. If so, the
|
| + // RenderWidgetHostView will initiate the frame capture and NOT the
|
| + // |capture_callback_|.
|
| std::unique_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
|
| new FrameSubscriber(
|
| media::VideoCaptureOracle::kCompositorUpdate, oracle_proxy,
|
| @@ -437,7 +355,8 @@ ContentCaptureSubscription::ContentCaptureSubscription(
|
| : base::WeakPtr<WindowActivityTracker>()));
|
| source_view_->BeginFrameSubscription(std::move(subscriber));
|
|
|
| - // Subscribe to mouse movement and mouse cursor update events.
|
| + // Begin monitoring for user activity that is used to signal "interactive
|
| + // content."
|
| if (window_activity_tracker_) {
|
| window_activity_tracker_->RegisterMouseInteractionObserver(
|
| base::Bind(&ContentCaptureSubscription::OnEvent, base::Unretained(this),
|
| @@ -483,79 +402,6 @@ void ContentCaptureSubscription::OnEvent(FrameSubscriber* subscriber) {
|
| }
|
| }
|
|
|
| -void RenderVideoFrame(
|
| - const SkBitmap& input,
|
| - const scoped_refptr<media::VideoFrame>& output,
|
| - const base::Callback<void(const gfx::Rect&, bool)>& done_cb) {
|
| - base::ScopedClosureRunner failure_handler(
|
| - base::Bind(done_cb, gfx::Rect(), false));
|
| -
|
| - SkAutoLockPixels locker(input);
|
| -
|
| - // Sanity-check the captured bitmap.
|
| - if (input.empty() || !input.readyToDraw() ||
|
| - input.colorType() != kN32_SkColorType || input.width() < 2 ||
|
| - input.height() < 2) {
|
| - DVLOG(1) << "input unacceptable (size=" << input.getSize()
|
| - << ", ready=" << input.readyToDraw()
|
| - << ", colorType=" << input.colorType() << ')';
|
| - return;
|
| - }
|
| -
|
| - // Sanity-check the output buffer.
|
| - if (output->format() != media::PIXEL_FORMAT_I420) {
|
| - NOTREACHED();
|
| - return;
|
| - }
|
| -
|
| - // Calculate the width and height of the content region in the |output|, based
|
| - // on the aspect ratio of |input|.
|
| - const gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
|
| - output->visible_rect(), gfx::Size(input.width(), input.height()));
|
| -
|
| - // Scale the bitmap to the required size, if necessary.
|
| - SkBitmap scaled_bitmap;
|
| - if (input.width() != region_in_frame.width() ||
|
| - input.height() != region_in_frame.height()) {
|
| - skia::ImageOperations::ResizeMethod method;
|
| - if (input.width() < region_in_frame.width() ||
|
| - input.height() < region_in_frame.height()) {
|
| - // Avoid box filtering when magnifying, because it's actually
|
| - // nearest-neighbor.
|
| - method = skia::ImageOperations::RESIZE_HAMMING1;
|
| - } else {
|
| - method = skia::ImageOperations::RESIZE_BOX;
|
| - }
|
| -
|
| - TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture", "Capture", output.get(),
|
| - "Scale");
|
| - scaled_bitmap = skia::ImageOperations::Resize(
|
| - input, method, region_in_frame.width(), region_in_frame.height());
|
| - } else {
|
| - scaled_bitmap = input;
|
| - }
|
| -
|
| - TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture", "Capture", output.get(), "YUV");
|
| - {
|
| - // Align to 2x2 pixel boundaries, as required by
|
| - // media::CopyRGBToVideoFrame().
|
| - const gfx::Rect region_in_yv12_frame(
|
| - region_in_frame.x() & ~1, region_in_frame.y() & ~1,
|
| - region_in_frame.width() & ~1, region_in_frame.height() & ~1);
|
| - if (region_in_yv12_frame.IsEmpty())
|
| - return;
|
| -
|
| - SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
|
| - media::CopyRGBToVideoFrame(
|
| - reinterpret_cast<uint8_t*>(scaled_bitmap.getPixels()),
|
| - scaled_bitmap.rowBytes(), region_in_yv12_frame, output.get());
|
| - }
|
| -
|
| - // The result is now ready.
|
| - ignore_result(failure_handler.Release());
|
| - done_cb.Run(region_in_frame, true);
|
| -}
|
| -
|
| WebContentsCaptureMachine::WebContentsCaptureMachine(
|
| int render_process_id,
|
| int main_render_frame_id,
|
| @@ -591,22 +437,15 @@ void WebContentsCaptureMachine::Start(
|
| }
|
|
|
| bool WebContentsCaptureMachine::InternalStart(
|
| - const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
|
| + scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy,
|
| const media::VideoCaptureParams& params) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| DCHECK(!IsStarted());
|
|
|
| DCHECK(oracle_proxy);
|
| - oracle_proxy_ = oracle_proxy;
|
| + oracle_proxy_ = std::move(oracle_proxy);
|
| capture_params_ = params;
|
|
|
| - render_thread_.reset(new base::Thread("WebContentsVideo_RenderThread"));
|
| - if (!render_thread_->Start()) {
|
| - DVLOG(1) << "Failed to spawn render thread.";
|
| - render_thread_.reset();
|
| - return false;
|
| - }
|
| -
|
| // Note: Creation of the first WeakPtr in the following statement will cause
|
| // IsStarted() to return true from now on.
|
| tracker_->SetResizeChangeCallback(
|
| @@ -653,7 +492,6 @@ void WebContentsCaptureMachine::InternalResume() {
|
| }
|
|
|
| void WebContentsCaptureMachine::Stop(const base::Closure& callback) {
|
| - // Stops the capture machine asynchronously.
|
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
| base::Bind(&WebContentsCaptureMachine::InternalStop,
|
| base::Unretained(this), callback));
|
| @@ -662,10 +500,10 @@ void WebContentsCaptureMachine::Stop(const base::Closure& callback) {
|
| void WebContentsCaptureMachine::InternalStop(const base::Closure& callback) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
| - if (!IsStarted()) {
|
| - callback.Run();
|
| + base::ScopedClosureRunner done_runner(callback);
|
| +
|
| + if (!IsStarted())
|
| return;
|
| - }
|
|
|
| // The following cancels any outstanding callbacks and causes IsStarted() to
|
| // return false from here onward.
|
| @@ -675,14 +513,6 @@ void WebContentsCaptureMachine::InternalStop(const base::Closure& callback) {
|
| if (WebContents* contents = tracker_->web_contents())
|
| contents->DecrementCapturerCount();
|
| tracker_->Stop();
|
| -
|
| - // The render thread cannot be stopped on the UI thread, so post a message
|
| - // to the thread pool used for blocking operations.
|
| - if (render_thread_) {
|
| - BrowserThread::PostBlockingPoolTask(
|
| - FROM_HERE, base::Bind(&DeleteOnWorkerThread,
|
| - base::Passed(&render_thread_), callback));
|
| - }
|
| }
|
|
|
| void WebContentsCaptureMachine::MaybeCaptureForRefresh() {
|
| @@ -701,43 +531,24 @@ void WebContentsCaptureMachine::InternalMaybeCaptureForRefresh() {
|
| }
|
|
|
| void WebContentsCaptureMachine::Capture(
|
| - const base::TimeTicks& start_time,
|
| - const scoped_refptr<media::VideoFrame>& target,
|
| + base::TimeTicks start_time,
|
| + scoped_refptr<media::VideoFrame> target,
|
| const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| deliver_frame_cb) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
| auto* const view =
|
| static_cast<RenderWidgetHostViewBase*>(tracker_->GetTargetView());
|
| - if (!view) {
|
| + if (!view || !view->CanCopyToVideoFrame()) {
|
| deliver_frame_cb.Run(base::TimeTicks(), gfx::Rect(), false);
|
| return;
|
| }
|
|
|
| const gfx::Size view_size = view->GetViewBounds().size();
|
| - if (view->CanCopyToVideoFrame()) {
|
| - view->CopyFromCompositingSurfaceToVideoFrame(
|
| - gfx::Rect(view_size), target,
|
| - base::Bind(&WebContentsCaptureMachine::
|
| - DidCopyFromCompositingSurfaceToVideoFrame,
|
| - weak_ptr_factory_.GetWeakPtr(), start_time,
|
| - deliver_frame_cb));
|
| - } else {
|
| - const gfx::Size fitted_size =
|
| - view_size.IsEmpty()
|
| - ? gfx::Size()
|
| - : media::ComputeLetterboxRegion(target->visible_rect(), view_size)
|
| - .size();
|
| - if (auto* rwh = view->GetRenderWidgetHost()) {
|
| - rwh->CopyFromBackingStore(
|
| - gfx::Rect(),
|
| - fitted_size, // Size here is a request not always honored.
|
| - base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore,
|
| - weak_ptr_factory_.GetWeakPtr(), start_time, target,
|
| - deliver_frame_cb),
|
| - kN32_SkColorType);
|
| - }
|
| - }
|
| + view->CopyFromCompositingSurfaceToVideoFrame(
|
| + gfx::Rect(view_size), std::move(target),
|
| + base::Bind(&WebContentsCaptureMachine::DidCopyToVideoFrame,
|
| + weak_ptr_factory_.GetWeakPtr(), start_time, deliver_frame_cb));
|
| }
|
|
|
| gfx::Size WebContentsCaptureMachine::ComputeOptimalViewSize() const {
|
| @@ -804,31 +615,7 @@ gfx::Size WebContentsCaptureMachine::ComputeOptimalViewSize() const {
|
| return optimal_size;
|
| }
|
|
|
| -void WebContentsCaptureMachine::DidCopyFromBackingStore(
|
| - const base::TimeTicks& start_time,
|
| - const scoped_refptr<media::VideoFrame>& target,
|
| - const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| - deliver_frame_cb,
|
| - const SkBitmap& bitmap,
|
| - ReadbackResponse response) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| -
|
| - DCHECK(render_thread_);
|
| - if (response == READBACK_SUCCESS) {
|
| - TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture", "Capture", target.get(),
|
| - "Render");
|
| - render_thread_->task_runner()->PostTask(
|
| - FROM_HERE, media::BindToCurrentLoop(
|
| - base::Bind(&RenderVideoFrame, bitmap, target,
|
| - base::Bind(deliver_frame_cb, start_time))));
|
| - } else {
|
| - // Capture can fail due to transient issues, so just skip this frame.
|
| - DVLOG(1) << "CopyFromBackingStore failed; skipping frame.";
|
| - deliver_frame_cb.Run(start_time, gfx::Rect(), false);
|
| - }
|
| -}
|
| -
|
| -void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
|
| +void WebContentsCaptureMachine::DidCopyToVideoFrame(
|
| const base::TimeTicks& start_time,
|
| const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
|
| deliver_frame_cb,
|
| @@ -923,7 +710,7 @@ WebContentsVideoCaptureDevice::Create(const std::string& device_id) {
|
| // Parse device_id into render_process_id and main_render_frame_id.
|
| WebContentsMediaCaptureId media_id;
|
| if (!WebContentsMediaCaptureId::Parse(device_id, &media_id)) {
|
| - return NULL;
|
| + return nullptr;
|
| }
|
|
|
| return std::unique_ptr<media::VideoCaptureDevice>(
|
|
|