Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Unified Diff: content/browser/media/capture/web_contents_video_capture_device.cc

Issue 1412173003: cast: support cursor rendering for tab capture (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Disable cursor rendering on windows until resources are available Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 e8e55ad0b25ba3acbfdd864de490de589ca1a442..3a432d856c068ed7f638913321e573c37922f0ea 100644
--- a/content/browser/media/capture/web_contents_video_capture_device.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device.cc
@@ -50,6 +50,8 @@
#include "content/browser/media/capture/web_contents_video_capture_device.h"
+#include <algorithm>
+
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
@@ -63,6 +65,7 @@
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
+#include "content/browser/media/capture/cursor_renderer.h"
#include "content/browser/media/capture/web_contents_capture_util.h"
#include "content/browser/media/capture/web_contents_tracker.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
@@ -72,6 +75,7 @@
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/browser/web_contents.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/video_capture_types.h"
#include "media/base/video_util.h"
#include "media/capture/content/screen_capture_device_core.h"
@@ -84,6 +88,10 @@
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/size_conversions.h"
+#if defined(USE_AURA)
+#include "content/browser/media/capture/cursor_renderer_aura.h"
+#endif
+
namespace content {
namespace {
@@ -119,10 +127,13 @@ class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
public:
FrameSubscriber(media::VideoCaptureOracle::Event event_type,
const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle,
- VideoFrameDeliveryLog* delivery_log)
+ VideoFrameDeliveryLog* delivery_log,
+ base::WeakPtr<content::CursorRenderer> cursor_renderer)
: event_type_(event_type),
oracle_proxy_(oracle),
- delivery_log_(delivery_log) {}
+ delivery_log_(delivery_log),
+ cursor_renderer_(cursor_renderer),
+ weak_ptr_factory_(this) {}
bool ShouldCaptureFrame(
const gfx::Rect& damage_rect,
@@ -131,10 +142,23 @@ class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback*
deliver_frame_cb) override;
+ static void DidCaptureFrame(
+ base::WeakPtr<FrameSubscriber> frame_subscriber_,
+ const media::ThreadSafeCaptureOracle::CaptureFrameCallback&
+ capture_frame_cb,
+ const scoped_refptr<media::VideoFrame>& frame,
+ base::TimeTicks timestamp,
+ const gfx::Rect& region_in_frame,
+ bool success);
+
private:
const media::VideoCaptureOracle::Event event_type_;
scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_;
VideoFrameDeliveryLog* const delivery_log_;
+ // We need a weak pointer since FrameSubscriber is owned externally and
+ // may outlive the cursor renderer.
+ base::WeakPtr<CursorRenderer> cursor_renderer_;
+ base::WeakPtrFactory<FrameSubscriber> weak_ptr_factory_;
};
// ContentCaptureSubscription is the relationship between a RenderWidgetHost
@@ -177,10 +201,15 @@ class ContentCaptureSubscription {
const int render_widget_id_;
VideoFrameDeliveryLog delivery_log_;
- FrameSubscriber timer_subscriber_;
+ scoped_ptr<FrameSubscriber> timer_subscriber_;
CaptureCallback capture_callback_;
base::Timer timer_;
+ // Responsible for tracking the cursor state and input events to make
+ // decisions and then render the mouse cursor on the video frame after
+ // capture is completed.
+ scoped_ptr<content::CursorRenderer> cursor_renderer_;
+
DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription);
};
@@ -193,9 +222,10 @@ class ContentCaptureSubscription {
// 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(bool)>& done_cb);
+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.
@@ -249,6 +279,7 @@ class WebContentsCaptureMachine : public media::VideoCaptureMachine {
const base::TimeTicks& start_time,
const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
deliver_frame_cb,
+ const gfx::Rect& region_in_frame,
bool success);
// Remove the old subscription, and attempt to start a new one if |had_target|
@@ -304,16 +335,44 @@ bool FrameSubscriber::ShouldCaptureFrame(
"instance", this);
media::ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
+
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(capture_frame_cb, *storage);
+ *deliver_frame_cb =
+ base::Bind(&FrameSubscriber::DidCaptureFrame,
+ weak_ptr_factory_.GetWeakPtr(), capture_frame_cb, *storage);
if (oracle_decision)
delivery_log_->ChronicleFrameDelivery(present_time);
return oracle_decision;
}
+void FrameSubscriber::DidCaptureFrame(
+ base::WeakPtr<FrameSubscriber> frame_subscriber_,
+ const media::ThreadSafeCaptureOracle::CaptureFrameCallback&
+ capture_frame_cb,
+ const scoped_refptr<media::VideoFrame>& frame,
+ 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) {
+ 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_) {
+ if (frame_subscriber_->cursor_renderer_->SnapshotCursorState(
+ region_in_frame))
+ frame_subscriber_->cursor_renderer_->RenderOnVideoFrame(frame);
+ }
+ }
+ capture_frame_cb.Run(frame, timestamp, success);
+}
+
ContentCaptureSubscription::ContentCaptureSubscription(
const RenderWidgetHost& source,
const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
@@ -321,20 +380,32 @@ ContentCaptureSubscription::ContentCaptureSubscription(
: render_process_id_(source.GetProcess()->GetID()),
render_widget_id_(source.GetRoutingID()),
delivery_log_(),
- timer_subscriber_(media::VideoCaptureOracle::kTimerPoll, oracle_proxy,
- &delivery_log_),
capture_callback_(capture_callback),
timer_(true, true) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderWidgetHostView* const view = source.GetView();
+// TODO(isheriff): Cursor resources currently only available on linux. Remove
+// this once we add the necessary resources for windows.
+#if defined(USE_AURA) && defined(OS_LINUX)
+ if (view)
+ cursor_renderer_.reset(
+ new content::CursorRendererAura(view->GetNativeView()));
+#endif
+ timer_subscriber_.reset(new FrameSubscriber(
+ media::VideoCaptureOracle::kTimerPoll, oracle_proxy, &delivery_log_,
+ cursor_renderer_ ? cursor_renderer_->GetWeakPtr()
+ : base::WeakPtr<CursorRenderer>()));
// Subscribe to compositor updates. These will be serviced directly by the
// oracle.
if (view) {
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
new FrameSubscriber(media::VideoCaptureOracle::kCompositorUpdate,
- oracle_proxy, &delivery_log_));
+ oracle_proxy, &delivery_log_,
+ cursor_renderer_
+ ? cursor_renderer_->GetWeakPtr()
+ : base::WeakPtr<CursorRenderer>()));
view->BeginFrameSubscription(subscriber.Pass());
}
@@ -370,18 +441,18 @@ void ContentCaptureSubscription::OnTimer() {
RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
const base::TimeTicks start_time = base::TimeTicks::Now();
- if (timer_subscriber_.ShouldCaptureFrame(gfx::Rect(),
- start_time,
- &frame,
- &deliver_frame_cb)) {
+ if (timer_subscriber_->ShouldCaptureFrame(gfx::Rect(), start_time, &frame,
+ &deliver_frame_cb)) {
capture_callback_.Run(start_time, frame, deliver_frame_cb);
}
}
-void RenderVideoFrame(const SkBitmap& input,
- const scoped_refptr<media::VideoFrame>& output,
- const base::Callback<void(bool)>& done_cb) {
- base::ScopedClosureRunner failure_handler(base::Bind(done_cb, false));
+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);
@@ -412,7 +483,6 @@ void RenderVideoFrame(const SkBitmap& input,
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()) {
@@ -453,7 +523,7 @@ void RenderVideoFrame(const SkBitmap& input,
// The result is now ready.
ignore_result(failure_handler.Release());
- done_cb.Run(true);
+ done_cb.Run(region_in_frame, true);
}
VideoFrameDeliveryLog::VideoFrameDeliveryLog()
@@ -597,7 +667,7 @@ void WebContentsCaptureMachine::Capture(
RenderWidgetHostViewBase* view =
rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL;
if (!view) {
- deliver_frame_cb.Run(base::TimeTicks(), false);
+ deliver_frame_cb.Run(base::TimeTicks(), gfx::Rect(), false);
return;
}
@@ -717,12 +787,13 @@ void WebContentsCaptureMachine::DidCopyFromBackingStore(
TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture", "Capture", target.get(),
"Render");
render_thread_->task_runner()->PostTask(
- FROM_HERE, base::Bind(&RenderVideoFrame, bitmap, target,
- base::Bind(deliver_frame_cb, start_time)));
+ 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, false);
+ deliver_frame_cb.Run(start_time, gfx::Rect(), false);
}
}
@@ -730,6 +801,7 @@ void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
const base::TimeTicks& start_time,
const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
deliver_frame_cb,
+ const gfx::Rect& region_in_frame,
bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::TimeTicks now = base::TimeTicks::Now();
@@ -740,7 +812,7 @@ void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
// Capture can fail due to transient issues, so just skip this frame.
DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame.";
}
- deliver_frame_cb.Run(start_time, success);
+ deliver_frame_cb.Run(start_time, region_in_frame, success);
}
void WebContentsCaptureMachine::RenewFrameSubscription(bool had_target) {

Powered by Google App Engine
This is Rietveld 408576698