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>( |