From 76bb611f077321a06f5df9a22e414bb99722c479 Mon Sep 17 00:00:00 2001 From: isheriff Date: Fri, 25 Mar 2016 18:11:38 -0700 Subject: cast: Add window activity tracker for mac This adds window activity detector for mac which enables the following features to be enabled on tab mirroring: - Low latency interactive mode will be turned on when user is interactive with non-animating content - Frame subscriber will enable frame capture in response to mouse events resulting in smooth rendering of mouse while mirroring BUG=567735 Review URL: https://codereview.chromium.org/1835493002 Cr-Commit-Position: refs/heads/master@{#383431} --- content/browser/media/capture/cursor_renderer.h | 2 + .../browser/media/capture/cursor_renderer_aura.cc | 6 + .../browser/media/capture/cursor_renderer_mac.mm | 5 + .../capture/web_contents_video_capture_device.cc | 153 ++++++++------------- .../media/capture/window_activity_tracker.cc | 54 ++++++++ .../media/capture/window_activity_tracker.h | 31 ++++- .../media/capture/window_activity_tracker_aura.cc | 46 ++----- .../media/capture/window_activity_tracker_aura.h | 15 -- .../media/capture/window_activity_tracker_mac.h | 62 +++++++++ .../media/capture/window_activity_tracker_mac.mm | 81 +++++++++++ content/content_browser.gypi | 3 + 11 files changed, 314 insertions(+), 144 deletions(-) create mode 100644 content/browser/media/capture/window_activity_tracker.cc create mode 100644 content/browser/media/capture/window_activity_tracker_mac.h create mode 100644 content/browser/media/capture/window_activity_tracker_mac.mm diff --git a/content/browser/media/capture/cursor_renderer.h b/content/browser/media/capture/cursor_renderer.h index 7416413..7d3536a 100644 --- a/content/browser/media/capture/cursor_renderer.h +++ b/content/browser/media/capture/cursor_renderer.h @@ -21,6 +21,8 @@ namespace content { // will listen to mouse events. class CONTENT_EXPORT CursorRenderer { public: + static scoped_ptr Create(gfx::NativeView view); + virtual ~CursorRenderer() {} // Clears the cursor state being tracked. Called when there is a need to diff --git a/content/browser/media/capture/cursor_renderer_aura.cc b/content/browser/media/capture/cursor_renderer_aura.cc index 2b446e0..e5d3fc2 100644 --- a/content/browser/media/capture/cursor_renderer_aura.cc +++ b/content/browser/media/capture/cursor_renderer_aura.cc @@ -35,6 +35,12 @@ inline int alpha_blend(int alpha, int src, int dst) { } // namespace +// static +scoped_ptr CursorRenderer::Create(gfx::NativeWindow window) { + return scoped_ptr( + new CursorRendererAura(window, kCursorEnabledOnMouseMovement)); +} + CursorRendererAura::CursorRendererAura( aura::Window* window, CursorDisplaySetting cursor_display_setting) diff --git a/content/browser/media/capture/cursor_renderer_mac.mm b/content/browser/media/capture/cursor_renderer_mac.mm index 05c3fdd..bf382bf 100644 --- a/content/browser/media/capture/cursor_renderer_mac.mm +++ b/content/browser/media/capture/cursor_renderer_mac.mm @@ -30,6 +30,11 @@ inline int alpha_blend(int alpha, int src, int dst) { } // namespace +// static +scoped_ptr CursorRenderer::Create(gfx::NativeView view) { + return scoped_ptr(new CursorRendererMac(view)); +} + CursorRendererMac::CursorRendererMac(NSView* view) : view_(view), weak_factory_(this) { Clear(); 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 03895dd..73ba6c5 100644 --- a/content/browser/media/capture/web_contents_video_capture_device.cc +++ b/content/browser/media/capture/web_contents_video_capture_device.cc @@ -93,13 +93,6 @@ #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" -#include "content/browser/media/capture/window_activity_tracker_aura.h" -#elif defined(OS_MACOSX) -#include "content/browser/media/capture/cursor_renderer_mac.h" -#endif - namespace content { namespace { @@ -199,10 +192,10 @@ class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber { // autonomously on some other thread. class ContentCaptureSubscription { public: - typedef base::Callback< - void(const base::TimeTicks&, - const scoped_refptr&, - const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)> + typedef base::Callback&, + const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)> CaptureCallback; // Create a subscription. Whenever a manual capture is required, the @@ -360,8 +353,8 @@ bool FrameSubscriber::ShouldCaptureFrame( base::TimeTicks present_time, scoped_refptr* storage, DeliverFrameCallback* deliver_frame_cb) { - TRACE_EVENT1("gpu.capture", "FrameSubscriber::ShouldCaptureFrame", - "instance", this); + TRACE_EVENT1("gpu.capture", "FrameSubscriber::ShouldCaptureFrame", "instance", + this); media::ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; @@ -438,19 +431,11 @@ ContentCaptureSubscription::ContentCaptureSubscription( DCHECK_CURRENTLY_ON(BrowserThread::UI); RenderWidgetHostView* const view = source.GetView(); -// TODO(isheriff): Implement activity tracker on mac. -// https://crbug.com/567735 -#if defined(USE_AURA) - if (view) { - cursor_renderer_.reset(new content::CursorRendererAura( - view->GetNativeView(), kCursorEnabledOnMouseMovement)); - window_activity_tracker_.reset( - new content::WindowActivityTrackerAura(view->GetNativeView())); - } -#elif defined(OS_MACOSX) +#if defined(USE_AURA) || defined(OS_MACOSX) if (view) { - cursor_renderer_.reset( - new content::CursorRendererMac(view->GetNativeView())); + cursor_renderer_ = CursorRenderer::Create(view->GetNativeView()); + window_activity_tracker_ = + WindowActivityTracker::Create(view->GetNativeView()); } #endif timer_subscriber_.reset(new FrameSubscriber( @@ -536,12 +521,10 @@ void RenderVideoFrame( 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() + 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; @@ -572,11 +555,10 @@ void RenderVideoFrame( 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()); + 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; } @@ -585,10 +567,9 @@ void RenderVideoFrame( { // 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); + 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; @@ -604,9 +585,7 @@ void RenderVideoFrame( } VideoFrameDeliveryLog::VideoFrameDeliveryLog() - : last_frame_rate_log_time_(), - count_frames_rendered_(0) { -} + : last_frame_rate_log_time_(), count_frames_rendered_(0) {} void VideoFrameDeliveryLog::ChronicleFrameDelivery(base::TimeTicks frame_time) { // Log frame rate, if verbose logging is turned on. @@ -619,11 +598,9 @@ void VideoFrameDeliveryLog::ChronicleFrameDelivery(base::TimeTicks frame_time) { ++count_frames_rendered_; const base::TimeDelta elapsed = frame_time - last_frame_rate_log_time_; if (elapsed >= kFrameRateLogInterval) { - const double measured_fps = - count_frames_rendered_ / elapsed.InSecondsF(); - UMA_HISTOGRAM_COUNTS( - "TabCapture.FrameRate", - static_cast(measured_fps)); + const double measured_fps = count_frames_rendered_ / elapsed.InSecondsF(); + UMA_HISTOGRAM_COUNTS("TabCapture.FrameRate", + static_cast(measured_fps)); VLOG(1) << "Current measured frame rate for " << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS."; last_frame_rate_log_time_ = frame_time; @@ -641,8 +618,8 @@ WebContentsCaptureMachine::WebContentsCaptureMachine( tracker_(new WebContentsTracker(true)), auto_throttling_enabled_(enable_auto_throttling), weak_ptr_factory_(this) { - DVLOG(1) << "Created WebContentsCaptureMachine for " - << render_process_id << ':' << main_render_frame_id + DVLOG(1) << "Created WebContentsCaptureMachine for " << render_process_id + << ':' << main_render_frame_id << (auto_throttling_enabled_ ? " with auto-throttling enabled" : ""); } @@ -659,12 +636,9 @@ void WebContentsCaptureMachine::Start( const base::Callback callback) { // Starts the capture machine asynchronously. BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::UI, - FROM_HERE, + BrowserThread::UI, FROM_HERE, base::Bind(&WebContentsCaptureMachine::InternalStart, - base::Unretained(this), - oracle_proxy, - params), + base::Unretained(this), oracle_proxy, params), callback); } @@ -699,11 +673,9 @@ bool WebContentsCaptureMachine::InternalStart( 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)); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&WebContentsCaptureMachine::InternalStop, + base::Unretained(this), callback)); } void WebContentsCaptureMachine::InternalStop(const base::Closure& callback) { @@ -727,9 +699,8 @@ void WebContentsCaptureMachine::InternalStop(const base::Closure& callback) { // to the thread pool used for blocking operations. if (render_thread_) { BrowserThread::PostBlockingPoolTask( - FROM_HERE, - base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_), - callback)); + FROM_HERE, base::Bind(&DeleteOnWorkerThread, + base::Passed(&render_thread_), callback)); } } @@ -753,29 +724,28 @@ void WebContentsCaptureMachine::Capture( last_view_size_ = view_size; // Measure the number of kilopixels. - UMA_HISTOGRAM_COUNTS_10000( - "TabCapture.ViewChangeKiloPixels", - view_size.width() * view_size.height() / 1024); + UMA_HISTOGRAM_COUNTS_10000("TabCapture.ViewChangeKiloPixels", + view_size.width() * view_size.height() / 1024); } if (view->CanCopyToVideoFrame()) { view->CopyFromCompositingSurfaceToVideoFrame( - gfx::Rect(view_size), - target, + gfx::Rect(view_size), target, base::Bind(&WebContentsCaptureMachine:: - DidCopyFromCompositingSurfaceToVideoFrame, - weak_ptr_factory_.GetWeakPtr(), - start_time, deliver_frame_cb)); + 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(); + const gfx::Size fitted_size = + view_size.IsEmpty() + ? gfx::Size() + : media::ComputeLetterboxRegion(target->visible_rect(), view_size) + .size(); 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, + weak_ptr_factory_.GetWeakPtr(), start_time, target, deliver_frame_cb), kN32_SkColorType); } @@ -801,18 +771,17 @@ gfx::Size WebContentsCaptureMachine::ComputeOptimalViewSize() const { // issues caused by "one pixel stretching" and/or odd-to-even dimension // scaling, and to improve the performance of consumers of the captured // video. - const auto HasIntendedAspectRatio = - [](const gfx::Size& size, int width_units, int height_units) { + const auto HasIntendedAspectRatio = []( + const gfx::Size& size, int width_units, int height_units) { const int a = height_units * size.width(); const int b = width_units * size.height(); const int percentage_diff = 100 * std::abs((a - b)) / b; return percentage_diff <= 1; // Effectively, anything strictly <2%. }; - const auto RoundToExactAspectRatio = - [](const gfx::Size& size, int width_step, int height_step) { - const int adjusted_height = - std::max(size.height() - (size.height() % height_step), - height_step); + const auto RoundToExactAspectRatio = [](const gfx::Size& size, + int width_step, int height_step) { + const int adjusted_height = std::max( + size.height() - (size.height() % height_step), height_step); DCHECK_EQ((adjusted_height * width_step) % height_step, 0); return gfx::Size(adjusted_height * width_step / height_step, adjusted_height); @@ -865,8 +834,8 @@ void WebContentsCaptureMachine::DidCopyFromBackingStore( "Render"); render_thread_->task_runner()->PostTask( FROM_HERE, media::BindToCurrentLoop( - base::Bind(&RenderVideoFrame, bitmap, target, - base::Bind(deliver_frame_cb, start_time)))); + 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."; @@ -920,9 +889,9 @@ void WebContentsCaptureMachine::RenewFrameSubscription(bool had_target) { if (!had_subscription && tracker_->web_contents()) tracker_->web_contents()->IncrementCapturerCount(ComputeOptimalViewSize()); - subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_, - base::Bind(&WebContentsCaptureMachine::Capture, - weak_ptr_factory_.GetWeakPtr()))); + subscription_.reset(new ContentCaptureSubscription( + *rwh, oracle_proxy_, base::Bind(&WebContentsCaptureMachine::Capture, + weak_ptr_factory_.GetWeakPtr()))); } void WebContentsCaptureMachine::UpdateCaptureSize() { @@ -955,10 +924,10 @@ WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( int main_render_frame_id, bool enable_auto_throttling) : core_(new media::ScreenCaptureDeviceCore( - scoped_ptr(new WebContentsCaptureMachine( - render_process_id, - main_render_frame_id, - enable_auto_throttling)))) {} + scoped_ptr( + new WebContentsCaptureMachine(render_process_id, + main_render_frame_id, + enable_auto_throttling)))) {} WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() { DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying."; diff --git a/content/browser/media/capture/window_activity_tracker.cc b/content/browser/media/capture/window_activity_tracker.cc new file mode 100644 index 0000000..394792d --- /dev/null +++ b/content/browser/media/capture/window_activity_tracker.cc @@ -0,0 +1,54 @@ +// Copyright 2016 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. + +#include "content/browser/media/capture/window_activity_tracker.h" + +#include "base/time/time.h" + +namespace content { + +namespace { + +// The time period within which a triggered UI event is considered +// currently active. +const int kTimePeriodUiEventMicros = 100000; // 100 ms + +// Minimum number of user interactions before we consider the user to be in +// interactive mode. The goal is to prevent user interactions to launch +// animated content from causing target playout time flip-flop. +const int kMinUserInteractions = 5; + +} // namespace + +WindowActivityTracker::WindowActivityTracker() { + Reset(); +} + +WindowActivityTracker::~WindowActivityTracker() {} + +bool WindowActivityTracker::IsUiInteractionActive() const { + return ui_events_count_ > kMinUserInteractions; +} + +void WindowActivityTracker::RegisterMouseInteractionObserver( + const base::Closure& observer) { + mouse_interaction_observer_ = observer; +} + +void WindowActivityTracker::Reset() { + ui_events_count_ = 0; + last_time_ui_event_detected_ = base::TimeTicks(); +} + +void WindowActivityTracker::OnMouseActivity() { + if (!mouse_interaction_observer_.is_null()) + mouse_interaction_observer_.Run(); + if (base::TimeTicks::Now() - last_time_ui_event_detected_ > + base::TimeDelta::FromMicroseconds(kTimePeriodUiEventMicros)) { + ui_events_count_++; + } + last_time_ui_event_detected_ = base::TimeTicks::Now(); +} + +} // namespace content diff --git a/content/browser/media/capture/window_activity_tracker.h b/content/browser/media/capture/window_activity_tracker.h index d7b8d0f..7bdef1e 100644 --- a/content/browser/media/capture/window_activity_tracker.h +++ b/content/browser/media/capture/window_activity_tracker.h @@ -5,9 +5,11 @@ #ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WINDOW_ACTIVITY_TRACKER_H_ #define CONTENT_BROWSER_MEDIA_CAPTURE_WINDOW_ACTIVITY_TRACKER_H_ +#include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" +#include "ui/gfx/native_widget_types.h" namespace content { @@ -15,20 +17,39 @@ namespace content { // whether the user is actively interacting with UI. class CONTENT_EXPORT WindowActivityTracker { public: - virtual ~WindowActivityTracker() {} + static scoped_ptr Create(gfx::NativeView view); + + WindowActivityTracker(); + virtual ~WindowActivityTracker(); // Returns true if UI interaction is active. - virtual bool IsUiInteractionActive() const = 0; + bool IsUiInteractionActive() const; // Reports on every mouse movement activity on the window. - virtual void RegisterMouseInteractionObserver( - const base::Closure& observer) {} + void RegisterMouseInteractionObserver(const base::Closure& observer); // Resets any previous UI activity tracked. - virtual void Reset() = 0; + void Reset(); // Returns a weak pointer. virtual base::WeakPtr GetWeakPtr() = 0; + + protected: + void OnMouseActivity(); + + private: + // The last time a UI event was detected. + base::TimeTicks last_time_ui_event_detected_; + + // Runs on any mouse interaction from user. + base::Closure mouse_interaction_observer_; + + // The number of UI events detected so far. In case of continuous events + // such as mouse movement, a single continuous movement is treated + // as one event. + int ui_events_count_; + + DISALLOW_COPY_AND_ASSIGN(WindowActivityTracker); }; } // namespace content diff --git a/content/browser/media/capture/window_activity_tracker_aura.cc b/content/browser/media/capture/window_activity_tracker_aura.cc index 035c0ca..bc139aa 100644 --- a/content/browser/media/capture/window_activity_tracker_aura.cc +++ b/content/browser/media/capture/window_activity_tracker_aura.cc @@ -11,21 +11,15 @@ namespace content { -namespace { -// The time period within which a triggered UI event is considered -// currently active. -const int kTimePeriodUiEventMicros = 100000; // 100 ms - -// Minimum number of user interactions before we consider the user to be in -// interactive mode. The goal is to prevent user interactions to launch -// animated content from causing target playout time flip-flop. -const int kMinUserInteractions = 5; -} // namespace +// static +scoped_ptr WindowActivityTracker::Create( + gfx::NativeView window) { + return scoped_ptr( + new WindowActivityTrackerAura(window)); +} WindowActivityTrackerAura::WindowActivityTrackerAura(aura::Window* window) : window_(window), - last_time_ui_event_detected_(base::TimeTicks()), - ui_events_count_(0), weak_factory_(this) { if (window_) { window_->AddObserver(this); @@ -44,28 +38,16 @@ base::WeakPtr WindowActivityTrackerAura::GetWeakPtr() { return weak_factory_.GetWeakPtr(); } -bool WindowActivityTrackerAura::IsUiInteractionActive() const { - return ui_events_count_ > kMinUserInteractions; -} - -void WindowActivityTrackerAura::RegisterMouseInteractionObserver( - const base::Closure& observer) { - mouse_interaction_observer_ = observer; -} - -void WindowActivityTrackerAura::Reset() { - ui_events_count_ = 0; - last_time_ui_event_detected_ = base::TimeTicks(); -} - void WindowActivityTrackerAura::OnEvent(ui::Event* event) { - if (!mouse_interaction_observer_.is_null() && event->IsMouseEvent()) - mouse_interaction_observer_.Run(); - if (base::TimeTicks::Now() - last_time_ui_event_detected_ > - base::TimeDelta::FromMicroseconds(kTimePeriodUiEventMicros)) { - ui_events_count_++; + switch (event->type()) { + case ui::ET_MOUSE_PRESSED: + case ui::ET_MOUSE_RELEASED: + case ui::ET_MOUSE_MOVED: + WindowActivityTracker::OnMouseActivity(); + break; + default: + break; } - last_time_ui_event_detected_ = base::TimeTicks::Now(); } void WindowActivityTrackerAura::OnWindowDestroying(aura::Window* window) { diff --git a/content/browser/media/capture/window_activity_tracker_aura.h b/content/browser/media/capture/window_activity_tracker_aura.h index 48a277f..be5f29c 100644 --- a/content/browser/media/capture/window_activity_tracker_aura.h +++ b/content/browser/media/capture/window_activity_tracker_aura.h @@ -24,10 +24,6 @@ class CONTENT_EXPORT WindowActivityTrackerAura : public WindowActivityTracker, explicit WindowActivityTrackerAura(aura::Window* window); ~WindowActivityTrackerAura() final; - // WindowActivityTracker overrides. - bool IsUiInteractionActive() const final; - void RegisterMouseInteractionObserver(const base::Closure& observer) final; - void Reset() final; base::WeakPtr GetWeakPtr() final; private: @@ -39,17 +35,6 @@ class CONTENT_EXPORT WindowActivityTrackerAura : public WindowActivityTracker, aura::Window* window_; - // The last time a UI event was detected. - base::TimeTicks last_time_ui_event_detected_; - - // Runs on mouse movement or mouse cursor changes. - base::Closure mouse_interaction_observer_; - - // The number of UI events detected so far. In case of continuous events - // such as mouse movement, a single continuous movement is treated - // as one event. - int ui_events_count_; - base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(WindowActivityTrackerAura); diff --git a/content/browser/media/capture/window_activity_tracker_mac.h b/content/browser/media/capture/window_activity_tracker_mac.h new file mode 100644 index 0000000..64c91a0 --- /dev/null +++ b/content/browser/media/capture/window_activity_tracker_mac.h @@ -0,0 +1,62 @@ +// Copyright 2016 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. + +#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_WINDOW_ACTIVITY_TRACKER_MAC_H_ +#define CONTENT_BROWSER_MEDIA_CAPTURE_WINDOW_ACTIVITY_TRACKER_MAC_H_ + +#import +#import + +#include "base/callback.h" +#include "base/mac/scoped_nsobject.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "content/browser/media/capture/window_activity_tracker.h" +#include "content/common/content_export.h" +#include "ui/base/cocoa/tracking_area.h" +#include "ui/gfx/native_widget_types.h" + +@interface MouseTracker : NSObject { + @private + ui::ScopedCrTrackingArea trackingArea_; + + // The view on which mouse movement is detected. + NSView* nsView_; + + // Runs on any mouse interaction from user. + base::Closure mouseInteractionObserver_; +} + +- (instancetype)initWithView:(NSView*)nsView; + +// Register an observer for mouse interaction. +- (void)registerMouseInteractionObserver:(const base::Closure&)observer; + +@end + +namespace content { + +// Tracks UI events and makes a decision on whether the user has been +// actively interacting with a specified window. +class CONTENT_EXPORT WindowActivityTrackerMac : public WindowActivityTracker { + public: + explicit WindowActivityTrackerMac(NSView* view); + ~WindowActivityTrackerMac() final; + + base::WeakPtr GetWeakPtr() final; + + private: + void OnMouseActivity(); + + NSView* const view_; + base::scoped_nsobject mouse_tracker_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(WindowActivityTrackerMac); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_MEDIA_CAPTURE_WINDOW_ACTIVITY_TRACKER_MAC_H_ diff --git a/content/browser/media/capture/window_activity_tracker_mac.mm b/content/browser/media/capture/window_activity_tracker_mac.mm new file mode 100644 index 0000000..3b71cd6 --- /dev/null +++ b/content/browser/media/capture/window_activity_tracker_mac.mm @@ -0,0 +1,81 @@ +// Copyright 2016 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. + +#include "content/browser/media/capture/window_activity_tracker_mac.h" + +#include +#include + +#include "base/bind.h" +#include "ui/base/cocoa/tracking_area.h" + +@implementation MouseTracker + +- (instancetype)initWithView:(NSView*)nsView { + self = [super init]; + // TODO(isheriff): why are there no pressed/released events ? + NSTrackingAreaOptions trackingOptions = + NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect; + trackingArea_.reset([[CrTrackingArea alloc] initWithRect:NSZeroRect + options:trackingOptions + owner:self + userInfo:nil]); + [nsView addTrackingArea:trackingArea_.get()]; + nsView_ = nsView; + return self; +} + +- (void)stopTracking { + if (trackingArea_.get()) { + [nsView_ removeTrackingArea:trackingArea_.get()]; + trackingArea_.reset(); + } +} + +- (void)registerMouseInteractionObserver:(const base::Closure&)observer { + mouseInteractionObserver_ = observer; +} + +- (void)mouseMoved:(NSEvent*)theEvent { + mouseInteractionObserver_.Run(); +} + +- (void)mouseEntered:(NSEvent*)theEvent { +} + +- (void)mouseExited:(NSEvent*)theEvent { +} + +@end + +namespace content { + +// static +scoped_ptr WindowActivityTracker::Create( + gfx::NativeView view) { + return scoped_ptr(new WindowActivityTrackerMac(view)); +} + +WindowActivityTrackerMac::WindowActivityTrackerMac(NSView* view) + : view_(view), weak_factory_(this) { + mouse_tracker_.reset([[MouseTracker alloc] initWithView:view]); + [mouse_tracker_ registerMouseInteractionObserver: + base::Bind(&WindowActivityTrackerMac::OnMouseActivity, + base::Unretained(this))]; +} + +WindowActivityTrackerMac::~WindowActivityTrackerMac() { + [mouse_tracker_ stopTracking]; +} + +void WindowActivityTrackerMac::OnMouseActivity() { + WindowActivityTracker::OnMouseActivity(); +} + +base::WeakPtr WindowActivityTrackerMac::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +} // namespace content diff --git a/content/content_browser.gypi b/content/content_browser.gypi index b359e05a8..63dd939 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -1066,9 +1066,12 @@ 'browser/media/capture/web_contents_tracker.h', 'browser/media/capture/web_contents_video_capture_device.cc', 'browser/media/capture/web_contents_video_capture_device.h', + 'browser/media/capture/window_activity_tracker.cc', 'browser/media/capture/window_activity_tracker.h', 'browser/media/capture/window_activity_tracker_aura.cc', 'browser/media/capture/window_activity_tracker_aura.h', + 'browser/media/capture/window_activity_tracker_mac.mm', + 'browser/media/capture/window_activity_tracker_mac.h', 'browser/media/media_internals.cc', 'browser/media/media_internals.h', 'browser/media/media_internals_handler.cc', -- cgit v1.1