diff --git a/webrtc/modules/desktop_capture/desktop_capture.gypi b/webrtc/modules/desktop_capture/desktop_capture.gypi index eb3bc9a29d..7834d21dc8 100644 --- a/webrtc/modules/desktop_capture/desktop_capture.gypi +++ b/webrtc/modules/desktop_capture/desktop_capture.gypi @@ -36,6 +36,8 @@ "differ_block.h", "mac/desktop_configuration.h", "mac/desktop_configuration.mm", + "mac/desktop_configuration_monitor.h", + "mac/desktop_configuration_monitor.cc", "mac/scoped_pixel_buffer_object.cc", "mac/scoped_pixel_buffer_object.h", "mouse_cursor.cc", diff --git a/webrtc/modules/desktop_capture/desktop_capture_options.cc b/webrtc/modules/desktop_capture/desktop_capture_options.cc index a4fa02547b..26044e127f 100644 --- a/webrtc/modules/desktop_capture/desktop_capture_options.cc +++ b/webrtc/modules/desktop_capture/desktop_capture_options.cc @@ -28,6 +28,9 @@ DesktopCaptureOptions DesktopCaptureOptions::CreateDefault() { DesktopCaptureOptions result; #if defined(USE_X11) result.set_x_display(SharedXDisplay::CreateDefault()); +#endif +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + result.set_configuration_monitor(new DesktopConfigurationMonitor()); #endif return result; } diff --git a/webrtc/modules/desktop_capture/desktop_capture_options.h b/webrtc/modules/desktop_capture/desktop_capture_options.h index f0c76b1733..2a188a03a3 100644 --- a/webrtc/modules/desktop_capture/desktop_capture_options.h +++ b/webrtc/modules/desktop_capture/desktop_capture_options.h @@ -11,11 +11,16 @@ #define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_ #include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/system_wrappers/interface/scoped_refptr.h" #if defined(USE_X11) #include "webrtc/modules/desktop_capture/x11/shared_x_display.h" #endif +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h" +#endif + namespace webrtc { // An object that stores initialization parameters for screen and window @@ -38,6 +43,15 @@ class DesktopCaptureOptions { } #endif +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + DesktopConfigurationMonitor* configuration_monitor() const { + return configuration_monitor_; + } + void set_configuration_monitor(scoped_refptr m) { + configuration_monitor_ = m; + } +#endif + // Flag indicating that the capturer should use screen change notifications. // Enables/disables use of XDAMAGE in the X11 capturer. bool use_update_notifications() const { return use_update_notifications_; } @@ -56,6 +70,10 @@ class DesktopCaptureOptions { #if defined(USE_X11) scoped_refptr x_display_; #endif + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + scoped_refptr configuration_monitor_; +#endif bool use_update_notifications_; bool disable_effects_; }; diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration.h b/webrtc/modules/desktop_capture/mac/desktop_configuration.h index 433040a04e..80a155d8a9 100644 --- a/webrtc/modules/desktop_capture/mac/desktop_configuration.h +++ b/webrtc/modules/desktop_capture/mac/desktop_configuration.h @@ -52,6 +52,9 @@ struct MacDesktopConfiguration { // increase as you move up the screen) or Carbon-style "top-down" coordinates. static MacDesktopConfiguration GetCurrent(Origin origin); + // Returns true if the given desktop configuration equals this one. + bool Equals(const MacDesktopConfiguration& other); + // Bounds of the desktop in Density-Independent Pixels (DIPs). DesktopRect bounds; diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration.mm b/webrtc/modules/desktop_capture/mac/desktop_configuration.mm index a917b5dc05..5dac409ca8 100644 --- a/webrtc/modules/desktop_capture/mac/desktop_configuration.mm +++ b/webrtc/modules/desktop_capture/mac/desktop_configuration.mm @@ -143,4 +143,22 @@ MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) { return desktop_config; } + +// For convenience of comparing MacDisplayConfigurations in +// MacDesktopConfiguration::Equals. +bool operator==(const MacDisplayConfiguration& left, + const MacDisplayConfiguration& right) { + return left.id == right.id && + left.bounds.equals(right.bounds) && + left.pixel_bounds.equals(right.pixel_bounds) && + left.dip_to_pixel_scale == right.dip_to_pixel_scale; +} + +bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) { + return bounds.equals(other.bounds) && + pixel_bounds.equals(other.pixel_bounds) && + dip_to_pixel_scale == other.dip_to_pixel_scale && + displays == other.displays; +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc b/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc new file mode 100644 index 0000000000..f0d5c34be6 --- /dev/null +++ b/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h" + +#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" + +namespace webrtc { + +// The amount of time allowed for displays to reconfigure. +static const int64_t kDisplayConfigurationEventTimeoutMs = 10 * 1000; + +DesktopConfigurationMonitor::DesktopConfigurationMonitor() + : ref_count_(0), + display_configuration_capture_event_(EventWrapper::Create()) { + CGError err = CGDisplayRegisterReconfigurationCallback( + DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this); + if (err != kCGErrorSuccess) { + LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err; + abort(); + } + display_configuration_capture_event_->Set(); + + desktop_configuration_ = MacDesktopConfiguration::GetCurrent( + MacDesktopConfiguration::TopLeftOrigin); +} + +DesktopConfigurationMonitor::~DesktopConfigurationMonitor() { + CGError err = CGDisplayRemoveReconfigurationCallback( + DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this); + if (err != kCGErrorSuccess) + LOG(LS_ERROR) << "CGDisplayRemoveReconfigurationCallback " << err; +} + +void DesktopConfigurationMonitor::Lock() { + if (!display_configuration_capture_event_->Wait( + kDisplayConfigurationEventTimeoutMs)) { + LOG_F(LS_ERROR) << "Event wait timed out."; + abort(); + } +} + +void DesktopConfigurationMonitor::Unlock() { + display_configuration_capture_event_->Set(); +} + +// static +void DesktopConfigurationMonitor::DisplaysReconfiguredCallback( + CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void *user_parameter) { + DesktopConfigurationMonitor* monitor = + reinterpret_cast(user_parameter); + monitor->DisplaysReconfigured(display, flags); +} + +void DesktopConfigurationMonitor::DisplaysReconfigured( + CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags) { + if (flags & kCGDisplayBeginConfigurationFlag) { + if (reconfiguring_displays_.empty()) { + // If this is the first display to start reconfiguring then wait on + // |display_configuration_capture_event_| to block the capture thread + // from accessing display memory until the reconfiguration completes. + if (!display_configuration_capture_event_->Wait( + kDisplayConfigurationEventTimeoutMs)) { + LOG_F(LS_ERROR) << "Event wait timed out."; + abort(); + } + } + reconfiguring_displays_.insert(display); + } else { + reconfiguring_displays_.erase(display); + if (reconfiguring_displays_.empty()) { + desktop_configuration_ = MacDesktopConfiguration::GetCurrent( + MacDesktopConfiguration::TopLeftOrigin); + display_configuration_capture_event_->Set(); + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h b/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h new file mode 100644 index 0000000000..27143a84e1 --- /dev/null +++ b/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_ + +#include + +#include + +#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" +#include "webrtc/system_wrappers/interface/atomic32.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class EventWrapper; + +// The class provides functions to synchronize capturing and display +// reconfiguring across threads, and the up-to-date MacDesktopConfiguration. +class DesktopConfigurationMonitor { + public: + DesktopConfigurationMonitor(); + // Acquires a lock on the current configuration. + void Lock(); + // Releases the lock previously acquired. + void Unlock(); + // Returns the current desktop configuration. Should only be called when the + // lock has been acquired. + const MacDesktopConfiguration& desktop_configuration() { + return desktop_configuration_; + } + + void AddRef() { ++ref_count_; } + void Release() { + if (--ref_count_ == 0) + delete this; + } + + private: + static void DisplaysReconfiguredCallback(CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, + void *user_parameter); + ~DesktopConfigurationMonitor(); + + void DisplaysReconfigured(CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags); + + Atomic32 ref_count_; + std::set reconfiguring_displays_; + MacDesktopConfiguration desktop_configuration_; + scoped_ptr display_configuration_capture_event_; + + DISALLOW_COPY_AND_ASSIGN(DesktopConfigurationMonitor); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_ diff --git a/webrtc/modules/desktop_capture/screen_capturer_mac.mm b/webrtc/modules/desktop_capture/screen_capturer_mac.mm index 7aec06cd81..46118dd722 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_mac.mm +++ b/webrtc/modules/desktop_capture/screen_capturer_mac.mm @@ -26,11 +26,11 @@ #include "webrtc/modules/desktop_capture/desktop_geometry.h" #include "webrtc/modules/desktop_capture/desktop_region.h" #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" +#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h" #include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h" #include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" #include "webrtc/modules/desktop_capture/screen_capturer_helper.h" -#include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/system_wrappers/interface/tick_util.h" @@ -120,13 +120,11 @@ bool IsOSLionOrLater() { return darwin_version >= 11; } -// The amount of time allowed for displays to reconfigure. -const int64_t kDisplayConfigurationEventTimeoutMs = 10 * 1000; - // A class to perform video frame capturing for mac. class ScreenCapturerMac : public ScreenCapturer { public: - ScreenCapturerMac(); + explicit ScreenCapturerMac( + scoped_refptr desktop_config_monitor); virtual ~ScreenCapturerMac(); bool Init(); @@ -160,8 +158,6 @@ class ScreenCapturerMac : public ScreenCapturer { void ScreenUpdateMove(CGScreenUpdateMoveDelta delta, size_t count, const CGRect *rect_array); - void DisplaysReconfigured(CGDirectDisplayID display, - CGDisplayChangeSummaryFlags flags); static void ScreenRefreshCallback(CGRectCount count, const CGRect *rect_array, void *user_parameter); @@ -169,10 +165,6 @@ class ScreenCapturerMac : public ScreenCapturer { size_t count, const CGRect *rect_array, void *user_parameter); - static void DisplaysReconfiguredCallback(CGDirectDisplayID display, - CGDisplayChangeSummaryFlags flags, - void *user_parameter); - void ReleaseBuffers(); Callback* callback_; @@ -184,9 +176,6 @@ class ScreenCapturerMac : public ScreenCapturer { // Queue of the frames buffers. ScreenCaptureFrameQueue queue_; - // Current display configuration. - MacDesktopConfiguration desktop_config_; - // A thread-safe list of invalid rectangles, and the size of the most // recently captured screen. ScreenCapturerHelper helper_; @@ -197,13 +186,12 @@ class ScreenCapturerMac : public ScreenCapturer { // Contains an invalid region from the previous capture. DesktopRegion last_invalid_region_; - // Used to ensure that frame captures do not take place while displays - // are being reconfigured. - scoped_ptr display_configuration_capture_event_; + // Monitoring display reconfiguration. + scoped_refptr desktop_config_monitor_; - // Records the Ids of attached displays which are being reconfigured. - // Accessed on the thread on which we are notified of display events. - std::set reconfiguring_displays_; + // The desktop configuration obtained from desktop_config_monitor_ the last + // time of capturing. + MacDesktopConfiguration desktop_config_; // Power management assertion to prevent the screen from sleeping. IOPMAssertionID power_assertion_id_display_; @@ -258,11 +246,12 @@ DesktopFrame* CreateFrame( return frame.release(); } -ScreenCapturerMac::ScreenCapturerMac() +ScreenCapturerMac::ScreenCapturerMac( + scoped_refptr desktop_config_monitor) : callback_(NULL), mouse_shape_observer_(NULL), cgl_context_(NULL), - display_configuration_capture_event_(EventWrapper::Create()), + desktop_config_monitor_(desktop_config_monitor), power_assertion_id_display_(kIOPMNullAssertionID), power_assertion_id_user_(kIOPMNullAssertionID), app_services_library_(NULL), @@ -271,7 +260,6 @@ ScreenCapturerMac::ScreenCapturerMac() cg_display_bits_per_pixel_(NULL), opengl_library_(NULL), cgl_set_full_screen_(NULL) { - display_configuration_capture_event_->Set(); } ScreenCapturerMac::~ScreenCapturerMac() { @@ -286,11 +274,6 @@ ScreenCapturerMac::~ScreenCapturerMac() { ReleaseBuffers(); UnregisterRefreshAndMoveHandlers(); - CGError err = CGDisplayRemoveReconfigurationCallback( - ScreenCapturerMac::DisplaysReconfiguredCallback, this); - if (err != kCGErrorSuccess) - LOG(LS_ERROR) << "CGDisplayRemoveReconfigurationCallback " << err; - dlclose(app_services_library_); dlclose(opengl_library_); } @@ -299,14 +282,6 @@ bool ScreenCapturerMac::Init() { if (!RegisterRefreshAndMoveHandlers()) { return false; } - - CGError err = CGDisplayRegisterReconfigurationCallback( - ScreenCapturerMac::DisplaysReconfiguredCallback, this); - if (err != kCGErrorSuccess) { - LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err; - return false; - } - ScreenConfigurationChanged(); return true; } @@ -351,14 +326,17 @@ void ScreenCapturerMac::Capture( queue_.MoveToNextFrame(); - // Wait until the display configuration is stable. If one or more displays - // are reconfiguring then |display_configuration_capture_event_| will not be - // set until the reconfiguration completes. - // TODO(wez): Replace this with an early-exit (See crbug.com/104542). - if (!display_configuration_capture_event_->Wait( - kDisplayConfigurationEventTimeoutMs)) { - LOG_F(LS_ERROR) << "Event wait timed out."; - abort(); + desktop_config_monitor_->Lock(); + MacDesktopConfiguration new_config = + desktop_config_monitor_->desktop_configuration(); + if (!desktop_config_.Equals(new_config)) { + desktop_config_ = new_config; + // If the display configuraiton has changed then refresh capturer data + // structures. Occasionally, the refresh and move handlers are lost when + // the screen mode changes, so re-register them here. + UnregisterRefreshAndMoveHandlers(); + RegisterRefreshAndMoveHandlers(); + ScreenConfigurationChanged(); } DesktopRegion region; @@ -400,7 +378,7 @@ void ScreenCapturerMac::Capture( // Signal that we are done capturing data from the display framebuffer, // and accessing display structures. - display_configuration_capture_event_->Set(); + desktop_config_monitor_->Unlock(); // Capture the current cursor shape and notify |callback_| if it has changed. CaptureCursor(); @@ -693,14 +671,8 @@ void ScreenCapturerMac::ScreenConfigurationChanged() { // Clear the dirty region, in case the display is down-sizing. helper_.ClearInvalidRegion(); - // Refresh the cached desktop configuration. - desktop_config_ = MacDesktopConfiguration::GetCurrent( - MacDesktopConfiguration::TopLeftOrigin); - // Re-mark the entire desktop as dirty. - helper_.InvalidateScreen( - DesktopSize(desktop_config_.pixel_bounds.width(), - desktop_config_.pixel_bounds.height())); + helper_.InvalidateScreen(desktop_config_.pixel_bounds.size()); // Make sure the frame buffers will be reallocated. queue_.Reset(); @@ -847,39 +819,6 @@ void ScreenCapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta, ScreenRefresh(count, refresh_rects); } -void ScreenCapturerMac::DisplaysReconfigured( - CGDirectDisplayID display, - CGDisplayChangeSummaryFlags flags) { - if (flags & kCGDisplayBeginConfigurationFlag) { - if (reconfiguring_displays_.empty()) { - // If this is the first display to start reconfiguring then wait on - // |display_configuration_capture_event_| to block the capture thread - // from accessing display memory until the reconfiguration completes. - if (!display_configuration_capture_event_->Wait( - kDisplayConfigurationEventTimeoutMs)) { - LOG_F(LS_ERROR) << "Event wait timed out."; - abort(); - } - } - - reconfiguring_displays_.insert(display); - } else { - reconfiguring_displays_.erase(display); - - if (reconfiguring_displays_.empty()) { - // If no other displays are reconfiguring then refresh capturer data - // structures and un-block the capturer thread. Occasionally, the - // refresh and move handlers are lost when the screen mode changes, - // so re-register them here (the same does not appear to be true for - // the reconfiguration handler itself). - UnregisterRefreshAndMoveHandlers(); - RegisterRefreshAndMoveHandlers(); - ScreenConfigurationChanged(); - display_configuration_capture_event_->Set(); - } - } -} - void ScreenCapturerMac::ScreenRefreshCallback(CGRectCount count, const CGRect* rect_array, void* user_parameter) { @@ -900,20 +839,15 @@ void ScreenCapturerMac::ScreenUpdateMoveCallback( capturer->ScreenUpdateMove(delta, count, rect_array); } -void ScreenCapturerMac::DisplaysReconfiguredCallback( - CGDirectDisplayID display, - CGDisplayChangeSummaryFlags flags, - void* user_parameter) { - ScreenCapturerMac* capturer = - reinterpret_cast(user_parameter); - capturer->DisplaysReconfigured(display, flags); -} - } // namespace // static ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) { - scoped_ptr capturer(new ScreenCapturerMac()); + if (!options.configuration_monitor()) + return NULL; + + scoped_ptr capturer( + new ScreenCapturerMac(options.configuration_monitor())); if (!capturer->Init()) capturer.reset(); return capturer.release(); diff --git a/webrtc/system_wrappers/interface/scoped_refptr.h b/webrtc/system_wrappers/interface/scoped_refptr.h index a8a0074a2d..b344d211b1 100644 --- a/webrtc/system_wrappers/interface/scoped_refptr.h +++ b/webrtc/system_wrappers/interface/scoped_refptr.h @@ -11,6 +11,8 @@ #ifndef SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_ #define SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_ +#include + namespace webrtc { // Extracted from Chromium's src/base/memory/ref_counted.h.