Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / screen_capturer_mac.mm
index 7aec06c..100309f 100644 (file)
 #include <IOKit/pwr_mgt/IOPMLib.h>
 #include <OpenGL/CGLMacro.h>
 #include <OpenGL/OpenGL.h>
-#include <sys/utsname.h>
 
 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
 #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/osx_version.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"
@@ -87,46 +87,11 @@ void CopyRect(const uint8_t* src_plane,
   }
 }
 
-int GetDarwinVersion() {
-  struct utsname uname_info;
-  if (uname(&uname_info) != 0) {
-    LOG(LS_ERROR) << "uname failed";
-    return 0;
-  }
-
-  if (strcmp(uname_info.sysname, "Darwin") != 0)
-    return 0;
-
-  char* dot;
-  int result = strtol(uname_info.release, &dot, 10);
-  if (*dot != '.') {
-    LOG(LS_ERROR) << "Failed to parse version";
-    return 0;
-  }
-
-  return result;
-}
-
-bool IsOSLionOrLater() {
-  static int darwin_version = GetDarwinVersion();
-
-  // Verify that the version has been parsed correctly.
-  if (darwin_version < 6) {
-    LOG_F(LS_ERROR) << "Invalid Darwin version: " << darwin_version;
-    abort();
-  }
-
-  // Darwin major version 11 corresponds to OSX 10.7.
-  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<DesktopConfigurationMonitor> desktop_config_monitor);
   virtual ~ScreenCapturerMac();
 
   bool Init();
@@ -147,7 +112,8 @@ class ScreenCapturerMac : public ScreenCapturer {
   void GlBlitSlow(const DesktopFrame& frame);
   void CgBlitPreLion(const DesktopFrame& frame,
                      const DesktopRegion& region);
-  void CgBlitPostLion(const DesktopFrame& frame,
+  // Returns false if the selected screen is no longer valid.
+  bool CgBlitPostLion(const DesktopFrame& frame,
                       const DesktopRegion& region);
 
   // Called when the screen configuration is changed.
@@ -160,8 +126,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,12 +133,10 @@ 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();
 
+  DesktopFrame* CreateFrame();
+
   Callback* callback_;
   MouseShapeObserver* mouse_shape_observer_;
 
@@ -187,6 +149,16 @@ class ScreenCapturerMac : public ScreenCapturer {
   // Current display configuration.
   MacDesktopConfiguration desktop_config_;
 
+  // Currently selected display, or 0 if the full desktop is selected. On OS X
+  // 10.6 and before, this is always 0.
+  CGDirectDisplayID current_display_;
+
+  // The physical pixel bounds of the current screen.
+  DesktopRect screen_pixel_bounds_;
+
+  // The dip to physical pixel scale of the current screen.
+  float dip_to_pixel_scale_;
+
   // A thread-safe list of invalid rectangles, and the size of the most
   // recently captured screen.
   ScreenCapturerHelper helper_;
@@ -197,13 +169,8 @@ 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<EventWrapper> display_configuration_capture_event_;
-
-  // Records the Ids of attached displays which are being reconfigured.
-  // Accessed on the thread on which we are notified of display events.
-  std::set<CGDirectDisplayID> reconfiguring_displays_;
+  // Monitoring display reconfiguration.
+  scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
 
   // Power management assertion to prevent the screen from sleeping.
   IOPMAssertionID power_assertion_id_display_;
@@ -245,24 +212,14 @@ class InvertedDesktopFrame : public DesktopFrame {
   DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
 };
 
-DesktopFrame* CreateFrame(
-    const MacDesktopConfiguration& desktop_config) {
-
-  DesktopSize size(desktop_config.pixel_bounds.width(),
-                           desktop_config.pixel_bounds.height());
-  scoped_ptr<DesktopFrame> frame(new BasicDesktopFrame(size));
-
-  frame->set_dpi(DesktopVector(
-      kStandardDPI * desktop_config.dip_to_pixel_scale,
-      kStandardDPI * desktop_config.dip_to_pixel_scale));
-  return frame.release();
-}
-
-ScreenCapturerMac::ScreenCapturerMac()
+ScreenCapturerMac::ScreenCapturerMac(
+    scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
     : callback_(NULL),
       mouse_shape_observer_(NULL),
       cgl_context_(NULL),
-      display_configuration_capture_event_(EventWrapper::Create()),
+      current_display_(0),
+      dip_to_pixel_scale_(1.0f),
+      desktop_config_monitor_(desktop_config_monitor),
       power_assertion_id_display_(kIOPMNullAssertionID),
       power_assertion_id_user_(kIOPMNullAssertionID),
       app_services_library_(NULL),
@@ -271,7 +228,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 +242,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 +250,9 @@ bool ScreenCapturerMac::Init() {
   if (!RegisterRefreshAndMoveHandlers()) {
     return false;
   }
-
-  CGError err = CGDisplayRegisterReconfigurationCallback(
-      ScreenCapturerMac::DisplaysReconfiguredCallback, this);
-  if (err != kCGErrorSuccess) {
-    LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err;
-    return false;
-  }
-
+  desktop_config_monitor_->Lock();
+  desktop_config_ = desktop_config_monitor_->desktop_configuration();
+  desktop_config_monitor_->Unlock();
   ScreenConfigurationChanged();
   return true;
 }
@@ -351,14 +297,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;
@@ -368,7 +317,7 @@ void ScreenCapturerMac::Capture(
   // Note that we can't reallocate other buffers at this point, since the caller
   // may still be reading from them.
   if (!queue_.current_frame())
-    queue_.ReplaceCurrentFrame(CreateFrame(desktop_config_));
+    queue_.ReplaceCurrentFrame(CreateFrame());
 
   DesktopFrame* current_frame = queue_.current_frame();
 
@@ -376,7 +325,10 @@ void ScreenCapturerMac::Capture(
   if (IsOSLionOrLater()) {
     // Lion requires us to use their new APIs for doing screen capture. These
     // APIS currently crash on 10.6.8 if there is no monitor attached.
-    CgBlitPostLion(*current_frame, region);
+    if (!CgBlitPostLion(*current_frame, region)) {
+      callback_->OnCaptureCompleted(NULL);
+      return;
+    }
   } else if (cgl_context_) {
     flip = true;
     if (pixel_buffer_object_.get() != 0) {
@@ -400,7 +352,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();
@@ -419,15 +371,42 @@ void ScreenCapturerMac::SetMouseShapeObserver(
 
 bool ScreenCapturerMac::GetScreenList(ScreenList* screens) {
   assert(screens->size() == 0);
-  // TODO(jiayl): implement screen enumeration.
-  Screen default_screen;
-  default_screen.id = 0;
-  screens->push_back(default_screen);
+  if (!IsOSLionOrLater()) {
+    // Single monitor cast is not supported on pre OS X 10.7.
+    Screen screen;
+    screen.id = kFullDesktopScreenId;
+    screens->push_back(screen);
+    return true;
+  }
+
+  for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin();
+       it != desktop_config_.displays.end(); ++it) {
+    Screen screen;
+    screen.id = static_cast<ScreenId>(it->id);
+    screens->push_back(screen);
+  }
   return true;
 }
 
 bool ScreenCapturerMac::SelectScreen(ScreenId id) {
-  // TODO(jiayl): implement screen selection.
+  if (!IsOSLionOrLater()) {
+    // Ignore the screen selection on unsupported OS.
+    assert(!current_display_);
+    return id == kFullDesktopScreenId;
+  }
+
+  if (id == kFullDesktopScreenId) {
+    current_display_ = 0;
+  } else {
+    const MacDisplayConfiguration* config =
+        desktop_config_.FindDisplayConfigurationById(
+            static_cast<CGDirectDisplayID>(id));
+    if (!config)
+      return false;
+    current_display_ = config->id;
+  }
+
+  ScreenConfigurationChanged();
   return true;
 }
 
@@ -624,7 +603,7 @@ void ScreenCapturerMac::CgBlitPreLion(const DesktopFrame& frame,
   }
 }
 
-void ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
+bool ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
                                        const DesktopRegion& region) {
   // Copy the entire contents of the previous capture buffer, to capture over.
   // TODO(wez): Get rid of this as per crbug.com/145064, or implement
@@ -635,13 +614,37 @@ void ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
            frame.stride() * frame.size().height());
   }
 
-  for (size_t i = 0; i < desktop_config_.displays.size(); ++i) {
-    const MacDisplayConfiguration& display_config = desktop_config_.displays[i];
+  MacDisplayConfigurations displays_to_capture;
+  if (current_display_) {
+    // Capturing a single screen. Note that the screen id may change when
+    // screens are added or removed.
+    const MacDisplayConfiguration* config =
+        desktop_config_.FindDisplayConfigurationById(current_display_);
+    if (config) {
+      displays_to_capture.push_back(*config);
+    } else {
+      LOG(LS_ERROR) << "The selected screen cannot be found for capturing.";
+      return false;
+    }
+  } else {
+    // Capturing the whole desktop.
+    displays_to_capture = desktop_config_.displays;
+  }
 
+  for (size_t i = 0; i < displays_to_capture.size(); ++i) {
+    const MacDisplayConfiguration& display_config = displays_to_capture[i];
+
+    // Capturing mixed-DPI on one surface is hard, so we only return displays
+    // that match the "primary" display's DPI. The primary display is always
+    // the first in the list.
+    if (i > 0 && display_config.dip_to_pixel_scale !=
+        displays_to_capture[0].dip_to_pixel_scale) {
+      continue;
+    }
     // Determine the display's position relative to the desktop, in pixels.
     DesktopRect display_bounds = display_config.pixel_bounds;
-    display_bounds.Translate(-desktop_config_.pixel_bounds.left(),
-                             -desktop_config_.pixel_bounds.top());
+    display_bounds.Translate(-screen_pixel_bounds_.left(),
+                             -screen_pixel_bounds_.top());
 
     // Determine which parts of the blit region, if any, lay within the monitor.
     DesktopRegion copy_region = region;
@@ -684,23 +687,28 @@ void ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
     CFRelease(data);
     CFRelease(image);
   }
+  return true;
 }
 
 void ScreenCapturerMac::ScreenConfigurationChanged() {
+  if (current_display_) {
+    const MacDisplayConfiguration* config =
+        desktop_config_.FindDisplayConfigurationById(current_display_);
+    screen_pixel_bounds_ = config ? config->pixel_bounds : DesktopRect();
+    dip_to_pixel_scale_ = config ? config->dip_to_pixel_scale : 1.0f;
+  } else {
+    screen_pixel_bounds_ = desktop_config_.pixel_bounds;
+    dip_to_pixel_scale_ = desktop_config_.dip_to_pixel_scale;
+  }
+
   // Release existing buffers, which will be of the wrong size.
   ReleaseBuffers();
 
   // 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(screen_pixel_bounds_.size());
 
   // Make sure the frame buffers will be reallocated.
   queue_.Reset();
@@ -781,8 +789,8 @@ void ScreenCapturerMac::ScreenConfigurationChanged() {
   (*cgl_set_full_screen_)(cgl_context_);
   CGLSetCurrentContext(cgl_context_);
 
-  size_t buffer_size = desktop_config_.pixel_bounds.width() *
-                       desktop_config_.pixel_bounds.height() *
+  size_t buffer_size = screen_pixel_bounds_.width() *
+                       screen_pixel_bounds_.height() *
                        sizeof(uint32_t);
   pixel_buffer_object_.Init(cgl_context_, buffer_size);
 }
@@ -814,20 +822,17 @@ void ScreenCapturerMac::UnregisterRefreshAndMoveHandlers() {
 
 void ScreenCapturerMac::ScreenRefresh(CGRectCount count,
                                       const CGRect* rect_array) {
-  if (desktop_config_.pixel_bounds.is_empty())
+  if (screen_pixel_bounds_.is_empty())
     return;
 
   DesktopRegion region;
-
+  DesktopVector translate_vector =
+      DesktopVector().subtract(screen_pixel_bounds_.top_left());
   for (CGRectCount i = 0; i < count; ++i) {
     // Convert from Density-Independent Pixel to physical pixel coordinates.
-    DesktopRect rect =
-      ScaleAndRoundCGRect(rect_array[i], desktop_config_.dip_to_pixel_scale);
-
+    DesktopRect rect = ScaleAndRoundCGRect(rect_array[i], dip_to_pixel_scale_);
     // Translate from local desktop to capturer framebuffer coordinates.
-    rect.Translate(-desktop_config_.pixel_bounds.left(),
-                   -desktop_config_.pixel_bounds.top());
-
+    rect.Translate(translate_vector);
     region.AddRect(rect);
   }
 
@@ -847,45 +852,12 @@ 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) {
   ScreenCapturerMac* capturer =
       reinterpret_cast<ScreenCapturerMac*>(user_parameter);
-  if (capturer->desktop_config_.pixel_bounds.is_empty())
+  if (capturer->screen_pixel_bounds_.is_empty())
     capturer->ScreenConfigurationChanged();
   capturer->ScreenRefresh(count, rect_array);
 }
@@ -900,20 +872,24 @@ void ScreenCapturerMac::ScreenUpdateMoveCallback(
   capturer->ScreenUpdateMove(delta, count, rect_array);
 }
 
-void ScreenCapturerMac::DisplaysReconfiguredCallback(
-    CGDirectDisplayID display,
-    CGDisplayChangeSummaryFlags flags,
-    void* user_parameter) {
-  ScreenCapturerMac* capturer =
-      reinterpret_cast<ScreenCapturerMac*>(user_parameter);
-  capturer->DisplaysReconfigured(display, flags);
+DesktopFrame* ScreenCapturerMac::CreateFrame() {
+  scoped_ptr<DesktopFrame> frame(
+      new BasicDesktopFrame(screen_pixel_bounds_.size()));
+
+  frame->set_dpi(DesktopVector(kStandardDPI * dip_to_pixel_scale_,
+                               kStandardDPI * dip_to_pixel_scale_));
+  return frame.release();
 }
 
 }  // namespace
 
 // static
 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
-  scoped_ptr<ScreenCapturerMac> capturer(new ScreenCapturerMac());
+  if (!options.configuration_monitor())
+    return NULL;
+
+  scoped_ptr<ScreenCapturerMac> capturer(
+      new ScreenCapturerMac(options.configuration_monitor()));
   if (!capturer->Init())
     capturer.reset();
   return capturer.release();