Implement single monitor capture on Mac.

BUG=2787, 2824
TESTED=MacBook Pro Retina with an external monitor; verified changing display configuration while capturing; add/remove monitor while capturing; verified cursor position.
R=sergeyu@chromium.org

Review URL: https://webrtc-codereview.appspot.com/7479004

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@5471 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/modules/desktop_capture/desktop_capture.gypi b/modules/desktop_capture/desktop_capture.gypi
index 7834d21..9463257 100644
--- a/modules/desktop_capture/desktop_capture.gypi
+++ b/modules/desktop_capture/desktop_capture.gypi
@@ -38,6 +38,8 @@
         "mac/desktop_configuration.mm",
         "mac/desktop_configuration_monitor.h",
         "mac/desktop_configuration_monitor.cc",
+        "mac/osx_version.h",
+        "mac/osx_version.cc",
         "mac/scoped_pixel_buffer_object.cc",
         "mac/scoped_pixel_buffer_object.h",
         "mouse_cursor.cc",
diff --git a/modules/desktop_capture/desktop_capture_types.h b/modules/desktop_capture/desktop_capture_types.h
index 0ad199c..3e41796 100644
--- a/modules/desktop_capture/desktop_capture_types.h
+++ b/modules/desktop_capture/desktop_capture_types.h
@@ -26,7 +26,11 @@
 
 const WindowId kNullWindowId = 0;
 
-typedef int ScreenId;
+// Type used to identify screens on the desktop. Values are platform-specific:
+//   - On Windows: integer display device index.
+//   - On OSX: CGDirectDisplayID cast to intptr_t.
+//   - On Linux (with X11): TBD.
+typedef intptr_t ScreenId;
 
 // The screen id corresponds to all screen combined together.
 const ScreenId kFullDesktopScreenId = -1;
diff --git a/modules/desktop_capture/mac/desktop_configuration.h b/modules/desktop_capture/mac/desktop_configuration.h
index 80a155d..031d92d 100644
--- a/modules/desktop_capture/mac/desktop_configuration.h
+++ b/modules/desktop_capture/mac/desktop_configuration.h
@@ -55,6 +55,10 @@
   // Returns true if the given desktop configuration equals this one.
   bool Equals(const MacDesktopConfiguration& other);
 
+  // Returns the pointer to the display configuration with the specified id.
+  const MacDisplayConfiguration* FindDisplayConfigurationById(
+      CGDirectDisplayID id);
+
   // Bounds of the desktop in Density-Independent Pixels (DIPs).
   DesktopRect bounds;
 
diff --git a/modules/desktop_capture/mac/desktop_configuration.mm b/modules/desktop_capture/mac/desktop_configuration.mm
index 5dac409..838973e 100644
--- a/modules/desktop_capture/mac/desktop_configuration.mm
+++ b/modules/desktop_capture/mac/desktop_configuration.mm
@@ -110,15 +110,8 @@
     MacDisplayConfiguration display_config =
         GetConfigurationForScreen([screens objectAtIndex: i]);
 
-    // Handling mixed-DPI is hard, so we only return displays that match the
-    // "primary" display's DPI.  The primary display is always the first in the
-    // list returned by [NSScreen screens].
-    if (i == 0) {
+    if (i == 0)
       desktop_config.dip_to_pixel_scale = display_config.dip_to_pixel_scale;
-    } else if (desktop_config.dip_to_pixel_scale !=
-               display_config.dip_to_pixel_scale) {
-      continue;
-    }
 
     // Cocoa uses bottom-up coordinates, so if the caller wants top-down then
     // we need to invert the positions of secondary monitors relative to the
@@ -126,8 +119,16 @@
     if (i > 0 && origin == TopLeftOrigin) {
       InvertRectYOrigin(desktop_config.displays[0].bounds,
                         &display_config.bounds);
-      InvertRectYOrigin(desktop_config.displays[0].pixel_bounds,
-                        &display_config.pixel_bounds);
+      // |display_bounds| is density dependent, so we need to convert the
+      // primay monitor's position into the secondary monitor's density context.
+      float scaling_factor = display_config.dip_to_pixel_scale /
+          desktop_config.displays[0].dip_to_pixel_scale;
+      DesktopRect primary_bounds = DesktopRect::MakeLTRB(
+          desktop_config.displays[0].pixel_bounds.left() * scaling_factor,
+          desktop_config.displays[0].pixel_bounds.top() * scaling_factor,
+          desktop_config.displays[0].pixel_bounds.right() * scaling_factor,
+          desktop_config.displays[0].pixel_bounds.bottom() * scaling_factor);
+      InvertRectYOrigin(primary_bounds, &display_config.pixel_bounds);
     }
 
     // Add the display to the configuration.
@@ -143,7 +144,6 @@
   return desktop_config;
 }
 
-
 // For convenience of comparing MacDisplayConfigurations in
 // MacDesktopConfiguration::Equals.
 bool operator==(const MacDisplayConfiguration& left,
@@ -161,4 +161,16 @@
       displays == other.displays;
 }
 
+// Finds the display configuration with the specified id.
+const MacDisplayConfiguration*
+MacDesktopConfiguration::FindDisplayConfigurationById(
+    CGDirectDisplayID id) {
+  for (MacDisplayConfigurations::const_iterator it = displays.begin();
+      it != displays.end(); ++it) {
+    if (it->id == id)
+      return &(*it);
+  }
+  return NULL;
+}
+
 }  // namespace webrtc
diff --git a/modules/desktop_capture/mac/osx_version.cc b/modules/desktop_capture/mac/osx_version.cc
new file mode 100644
index 0000000..7466f20
--- /dev/null
+++ b/modules/desktop_capture/mac/osx_version.cc
@@ -0,0 +1,54 @@
+/*
+ *  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 <sys/utsname.h>
+
+#include "webrtc/system_wrappers/interface/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+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;
+}
+
+}  // namespace
+
+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;
+}
+
+}  // namespace webrtc
diff --git a/modules/desktop_capture/mac/osx_version.h b/modules/desktop_capture/mac/osx_version.h
new file mode 100644
index 0000000..0ba49a4
--- /dev/null
+++ b/modules/desktop_capture/mac/osx_version.h
@@ -0,0 +1,16 @@
+/*
+ *  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.
+ */
+
+namespace webrtc {
+
+// Returns true if the OS version >= OSX 10.7.
+bool IsOSLionOrLater();
+
+}  // namespace webrtc
diff --git a/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/modules/desktop_capture/mouse_cursor_monitor_mac.mm
index 25d7e7a..e880633 100644
--- a/modules/desktop_capture/mouse_cursor_monitor_mac.mm
+++ b/modules/desktop_capture/mouse_cursor_monitor_mac.mm
@@ -17,8 +17,11 @@
 
 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.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/mouse_cursor.h"
+#include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
 #include "webrtc/system_wrappers/interface/scoped_refptr.h"
 
@@ -27,31 +30,44 @@
 class MouseCursorMonitorMac : public MouseCursorMonitor {
  public:
   MouseCursorMonitorMac(const DesktopCaptureOptions& options,
-                        CGWindowID window_id);
+                        CGWindowID window_id,
+                        ScreenId screen_id);
   virtual ~MouseCursorMonitorMac();
 
   virtual void Init(Callback* callback, Mode mode) OVERRIDE;
   virtual void Capture() OVERRIDE;
 
  private:
+  static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
+                                           CGDisplayChangeSummaryFlags flags,
+                                           void *user_parameter);
+  void DisplaysReconfigured(CGDirectDisplayID display,
+                            CGDisplayChangeSummaryFlags flags);
+
   void CaptureImage();
 
   scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
   CGWindowID window_id_;
-
+  ScreenId screen_id_;
   Callback* callback_;
   Mode mode_;
-
   scoped_ptr<MouseCursor> last_cursor_;
 };
 
 MouseCursorMonitorMac::MouseCursorMonitorMac(
     const DesktopCaptureOptions& options,
-    CGWindowID window_id)
+    CGWindowID window_id,
+    ScreenId screen_id)
     : configuration_monitor_(options.configuration_monitor()),
       window_id_(window_id),
+      screen_id_(screen_id),
       callback_(NULL),
       mode_(SHAPE_AND_POSITION) {
+  assert(window_id == kCGNullWindowID || screen_id == kInvalidScreenId);
+  if (screen_id != kInvalidScreenId && !IsOSLionOrLater()) {
+    // Single screen capture is not supported on pre OS X 10.7.
+    screen_id_ = kFullDesktopScreenId;
+  }
 }
 
 MouseCursorMonitorMac::~MouseCursorMonitorMac() {}
@@ -80,6 +96,21 @@
 
   DesktopVector position(gc_position.x, gc_position.y);
 
+  configuration_monitor_->Lock();
+  MacDesktopConfiguration configuration =
+      configuration_monitor_->desktop_configuration();
+  configuration_monitor_->Unlock();
+  float scale = 1.0f;
+
+  // Find the dpi to physical pixel scale for the screen where the mouse cursor
+  // is.
+  for (MacDisplayConfigurations::iterator it = configuration.displays.begin();
+      it != configuration.displays.end(); ++it) {
+    if (it->bounds.Contains(position)) {
+      scale = it->dip_to_pixel_scale;
+      break;
+    }
+  }
   // If we are capturing cursor for a specific window then we need to figure out
   // if the current mouse position is covered by another window and also adjust
   // |position| to make it relative to the window origin.
@@ -142,10 +173,8 @@
           }
         }
       }
-
       CFRelease(window_array);
     }
-
     if (!found_window) {
       // If we failed to get list of windows or the window wasn't in the list
       // pretend that the cursor is outside the window. This can happen, e.g. if
@@ -153,17 +182,32 @@
       state = OUTSIDE;
       position.set(-1, -1);
     }
+  } else {
+    assert(screen_id_ >= kFullDesktopScreenId);
+    if (screen_id_ != kFullDesktopScreenId) {
+      // For single screen capturing, convert the position to relative to the
+      // target screen.
+      const MacDisplayConfiguration* config =
+          configuration.FindDisplayConfigurationById(
+              static_cast<CGDirectDisplayID>(screen_id_));
+      if (config) {
+        if (!config->pixel_bounds.Contains(position))
+          state = OUTSIDE;
+        position = position.subtract(config->bounds.top_left());
+      } else {
+        // The target screen is no longer valid.
+        state = OUTSIDE;
+        position.set(-1, -1);
+      }
+    } else {
+      position.subtract(configuration.bounds.top_left());
+    }
   }
-
-  // Convert Density Independent Pixel to physical pixel.
-  configuration_monitor_->Lock();
-  float scale =
-      configuration_monitor_->desktop_configuration().dip_to_pixel_scale;
-  configuration_monitor_->Unlock();
-
-  position = DesktopVector(round(position.x() * scale),
-                           round(position.y() * scale));
-
+  if (state == INSIDE) {
+    // Convert Density Independent Pixel to physical pixel.
+    position = DesktopVector(round(position.x() * scale),
+                             round(position.y() * scale));
+  }
   callback_->OnMouseCursorPosition(state, position);
 }
 
@@ -221,16 +265,15 @@
   callback_->OnMouseCursor(cursor.release());
 }
 
-
 MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
     const DesktopCaptureOptions& options, WindowId window) {
-  return new MouseCursorMonitorMac(options, window);
+  return new MouseCursorMonitorMac(options, window, kInvalidScreenId);
 }
 
 MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
     const DesktopCaptureOptions& options,
     ScreenId screen) {
-  return new MouseCursorMonitorMac(options, kCGNullWindowID);
+  return new MouseCursorMonitorMac(options, kCGNullWindowID, screen);
 }
 
 }  // namespace webrtc
diff --git a/modules/desktop_capture/screen_capturer_mac.mm b/modules/desktop_capture/screen_capturer_mac.mm
index 46118dd..100309f 100644
--- a/modules/desktop_capture/screen_capturer_mac.mm
+++ b/modules/desktop_capture/screen_capturer_mac.mm
@@ -19,7 +19,6 @@
 #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"
@@ -27,6 +26,7 @@
 #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"
@@ -87,39 +87,6 @@
   }
 }
 
-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;
-}
-
 // A class to perform video frame capturing for mac.
 class ScreenCapturerMac : public ScreenCapturer {
  public:
@@ -145,7 +112,8 @@
   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.
@@ -167,6 +135,8 @@
                                        void *user_parameter);
   void ReleaseBuffers();
 
+  DesktopFrame* CreateFrame();
+
   Callback* callback_;
   MouseShapeObserver* mouse_shape_observer_;
 
@@ -176,6 +146,19 @@
   // Queue of the frames buffers.
   ScreenCaptureFrameQueue queue_;
 
+  // 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_;
@@ -189,10 +172,6 @@
   // Monitoring display reconfiguration.
   scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
 
-  // 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_;
 
@@ -233,24 +212,13 @@
   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(
     scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
     : callback_(NULL),
       mouse_shape_observer_(NULL),
       cgl_context_(NULL),
+      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),
@@ -282,6 +250,9 @@
   if (!RegisterRefreshAndMoveHandlers()) {
     return false;
   }
+  desktop_config_monitor_->Lock();
+  desktop_config_ = desktop_config_monitor_->desktop_configuration();
+  desktop_config_monitor_->Unlock();
   ScreenConfigurationChanged();
   return true;
 }
@@ -346,7 +317,7 @@
   // 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();
 
@@ -354,7 +325,10 @@
   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) {
@@ -397,15 +371,42 @@
 
 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;
 }
 
@@ -602,7 +603,7 @@
   }
 }
 
-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
@@ -613,13 +614,37 @@
            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;
@@ -662,9 +687,20 @@
     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();
 
@@ -672,7 +708,7 @@
   helper_.ClearInvalidRegion();
 
   // Re-mark the entire desktop as dirty.
-  helper_.InvalidateScreen(desktop_config_.pixel_bounds.size());
+  helper_.InvalidateScreen(screen_pixel_bounds_.size());
 
   // Make sure the frame buffers will be reallocated.
   queue_.Reset();
@@ -753,8 +789,8 @@
   (*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);
 }
@@ -786,20 +822,17 @@
 
 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);
   }
 
@@ -824,7 +857,7 @@
                                               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);
 }
@@ -839,6 +872,15 @@
   capturer->ScreenUpdateMove(delta, count, rect_array);
 }
 
+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