Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / screen_capturer_mac.mm
1 /*
2  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10
11 #include "webrtc/modules/desktop_capture/screen_capturer.h"
12
13 #include <stddef.h>
14 #include <set>
15
16 #include <ApplicationServices/ApplicationServices.h>
17 #include <Cocoa/Cocoa.h>
18 #include <dlfcn.h>
19 #include <IOKit/pwr_mgt/IOPMLib.h>
20 #include <OpenGL/CGLMacro.h>
21 #include <OpenGL/OpenGL.h>
22
23 #include "webrtc/base/macutils.h"
24 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
25 #include "webrtc/modules/desktop_capture/desktop_frame.h"
26 #include "webrtc/modules/desktop_capture/desktop_geometry.h"
27 #include "webrtc/modules/desktop_capture/desktop_region.h"
28 #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
29 #include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
30 #include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h"
31 #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
32 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
33 #include "webrtc/system_wrappers/interface/logging.h"
34 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
35 #include "webrtc/system_wrappers/interface/tick_util.h"
36
37 namespace webrtc {
38
39 namespace {
40
41 // Definitions used to dynamic-link to deprecated OS 10.6 functions.
42 const char* kApplicationServicesLibraryName =
43     "/System/Library/Frameworks/ApplicationServices.framework/"
44     "ApplicationServices";
45 typedef void* (*CGDisplayBaseAddressFunc)(CGDirectDisplayID);
46 typedef size_t (*CGDisplayBytesPerRowFunc)(CGDirectDisplayID);
47 typedef size_t (*CGDisplayBitsPerPixelFunc)(CGDirectDisplayID);
48 const char* kOpenGlLibraryName =
49     "/System/Library/Frameworks/OpenGL.framework/OpenGL";
50 typedef CGLError (*CGLSetFullScreenFunc)(CGLContextObj);
51
52 // Standard Mac displays have 72dpi, but we report 96dpi for
53 // consistency with Windows and Linux.
54 const int kStandardDPI = 96;
55
56 // Scales all coordinates of a rect by a specified factor.
57 DesktopRect ScaleAndRoundCGRect(const CGRect& rect, float scale) {
58   return DesktopRect::MakeLTRB(
59     static_cast<int>(floor(rect.origin.x * scale)),
60     static_cast<int>(floor(rect.origin.y * scale)),
61     static_cast<int>(ceil((rect.origin.x + rect.size.width) * scale)),
62     static_cast<int>(ceil((rect.origin.y + rect.size.height) * scale)));
63 }
64
65 // Copy pixels in the |rect| from |src_place| to |dest_plane|. |rect| should be
66 // relative to the origin of |src_plane| and |dest_plane|.
67 void CopyRect(const uint8_t* src_plane,
68               int src_plane_stride,
69               uint8_t* dest_plane,
70               int dest_plane_stride,
71               int bytes_per_pixel,
72               const DesktopRect& rect) {
73   // Get the address of the starting point.
74   const int src_y_offset = src_plane_stride * rect.top();
75   const int dest_y_offset = dest_plane_stride * rect.top();
76   const int x_offset = bytes_per_pixel * rect.left();
77   src_plane += src_y_offset + x_offset;
78   dest_plane += dest_y_offset + x_offset;
79
80   // Copy pixels in the rectangle line by line.
81   const int bytes_per_line = bytes_per_pixel * rect.width();
82   const int height = rect.height();
83   for (int i = 0 ; i < height; ++i) {
84     memcpy(dest_plane, src_plane, bytes_per_line);
85     src_plane += src_plane_stride;
86     dest_plane += dest_plane_stride;
87   }
88 }
89
90 // Returns an array of CGWindowID for all the on-screen windows except
91 // |window_to_exclude|, or NULL if the window is not found or it fails. The
92 // caller should release the returned CFArrayRef.
93 CFArrayRef CreateWindowListWithExclusion(CGWindowID window_to_exclude) {
94   if (!window_to_exclude)
95     return NULL;
96
97   CFArrayRef all_windows = CGWindowListCopyWindowInfo(
98       kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
99   if (!all_windows)
100     return NULL;
101
102   CFMutableArrayRef returned_array = CFArrayCreateMutable(
103       NULL, CFArrayGetCount(all_windows), NULL);
104
105   bool found = false;
106   for (CFIndex i = 0; i < CFArrayGetCount(all_windows); ++i) {
107     CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
108         CFArrayGetValueAtIndex(all_windows, i));
109
110     CFNumberRef id_ref = reinterpret_cast<CFNumberRef>(
111         CFDictionaryGetValue(window, kCGWindowNumber));
112
113     CGWindowID id;
114     CFNumberGetValue(id_ref, kCFNumberIntType, &id);
115     if (id == window_to_exclude) {
116       found = true;
117       continue;
118     }
119     CFArrayAppendValue(returned_array, reinterpret_cast<void *>(id));
120   }
121   CFRelease(all_windows);
122
123   if (!found) {
124     CFRelease(returned_array);
125     returned_array = NULL;
126   }
127   return returned_array;
128 }
129
130 // Returns the bounds of |window| in physical pixels, enlarged by a small amount
131 // on four edges to take account of the border/shadow effects.
132 DesktopRect GetExcludedWindowPixelBounds(CGWindowID window,
133                                          float dip_to_pixel_scale) {
134   // The amount of pixels to add to the actual window bounds to take into
135   // account of the border/shadow effects.
136   static const int kBorderEffectSize = 20;
137   CGRect rect;
138   CGWindowID ids[1];
139   ids[0] = window;
140
141   CFArrayRef window_id_array =
142       CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
143   CFArrayRef window_array =
144       CGWindowListCreateDescriptionFromArray(window_id_array);
145
146   if (CFArrayGetCount(window_array) > 0) {
147     CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
148         CFArrayGetValueAtIndex(window_array, 0));
149     CFDictionaryRef bounds_ref = reinterpret_cast<CFDictionaryRef>(
150         CFDictionaryGetValue(window, kCGWindowBounds));
151     CGRectMakeWithDictionaryRepresentation(bounds_ref, &rect);
152   }
153
154   CFRelease(window_id_array);
155   CFRelease(window_array);
156
157   rect.origin.x -= kBorderEffectSize;
158   rect.origin.y -= kBorderEffectSize;
159   rect.size.width += kBorderEffectSize * 2;
160   rect.size.height += kBorderEffectSize * 2;
161   // |rect| is in DIP, so convert to physical pixels.
162   return ScaleAndRoundCGRect(rect, dip_to_pixel_scale);
163 }
164
165 // Create an image of the given region using the given |window_list|.
166 // |pixel_bounds| should be in the primary display's coordinate in physical
167 // pixels. The caller should release the returned CGImageRef and CFDataRef.
168 CGImageRef CreateExcludedWindowRegionImage(const DesktopRect& pixel_bounds,
169                                            float dip_to_pixel_scale,
170                                            CFArrayRef window_list,
171                                            CFDataRef* data_ref) {
172   CGRect window_bounds;
173   // The origin is in DIP while the size is in physical pixels. That's what
174   // CGWindowListCreateImageFromArray expects.
175   window_bounds.origin.x = pixel_bounds.left() / dip_to_pixel_scale;
176   window_bounds.origin.y = pixel_bounds.top() / dip_to_pixel_scale;
177   window_bounds.size.width = pixel_bounds.width();
178   window_bounds.size.height = pixel_bounds.height();
179
180   CGImageRef excluded_image = CGWindowListCreateImageFromArray(
181       window_bounds, window_list, kCGWindowImageDefault);
182
183   CGDataProviderRef provider = CGImageGetDataProvider(excluded_image);
184   *data_ref = CGDataProviderCopyData(provider);
185   assert(*data_ref);
186   return excluded_image;
187 }
188
189 // A class to perform video frame capturing for mac.
190 class ScreenCapturerMac : public ScreenCapturer {
191  public:
192   explicit ScreenCapturerMac(
193       scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor);
194   virtual ~ScreenCapturerMac();
195
196   bool Init();
197
198   // Overridden from ScreenCapturer:
199   virtual void Start(Callback* callback) OVERRIDE;
200   virtual void Capture(const DesktopRegion& region) OVERRIDE;
201   virtual void SetExcludedWindow(WindowId window) OVERRIDE;
202   virtual bool GetScreenList(ScreenList* screens) OVERRIDE;
203   virtual bool SelectScreen(ScreenId id) OVERRIDE;
204
205  private:
206   void GlBlitFast(const DesktopFrame& frame,
207                   const DesktopRegion& region);
208   void GlBlitSlow(const DesktopFrame& frame);
209   void CgBlitPreLion(const DesktopFrame& frame,
210                      const DesktopRegion& region);
211   // Returns false if the selected screen is no longer valid.
212   bool CgBlitPostLion(const DesktopFrame& frame,
213                       const DesktopRegion& region);
214
215   // Called when the screen configuration is changed.
216   void ScreenConfigurationChanged();
217
218   bool RegisterRefreshAndMoveHandlers();
219   void UnregisterRefreshAndMoveHandlers();
220
221   void ScreenRefresh(CGRectCount count, const CGRect *rect_array);
222   void ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
223                         size_t count,
224                         const CGRect *rect_array);
225   static void ScreenRefreshCallback(CGRectCount count,
226                                     const CGRect *rect_array,
227                                     void *user_parameter);
228   static void ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta,
229                                        size_t count,
230                                        const CGRect *rect_array,
231                                        void *user_parameter);
232   void ReleaseBuffers();
233
234   DesktopFrame* CreateFrame();
235
236   Callback* callback_;
237
238   CGLContextObj cgl_context_;
239   ScopedPixelBufferObject pixel_buffer_object_;
240
241   // Queue of the frames buffers.
242   ScreenCaptureFrameQueue queue_;
243
244   // Current display configuration.
245   MacDesktopConfiguration desktop_config_;
246
247   // Currently selected display, or 0 if the full desktop is selected. On OS X
248   // 10.6 and before, this is always 0.
249   CGDirectDisplayID current_display_;
250
251   // The physical pixel bounds of the current screen.
252   DesktopRect screen_pixel_bounds_;
253
254   // The dip to physical pixel scale of the current screen.
255   float dip_to_pixel_scale_;
256
257   // A thread-safe list of invalid rectangles, and the size of the most
258   // recently captured screen.
259   ScreenCapturerHelper helper_;
260
261   // Contains an invalid region from the previous capture.
262   DesktopRegion last_invalid_region_;
263
264   // Monitoring display reconfiguration.
265   scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
266
267   // Power management assertion to prevent the screen from sleeping.
268   IOPMAssertionID power_assertion_id_display_;
269
270   // Power management assertion to indicate that the user is active.
271   IOPMAssertionID power_assertion_id_user_;
272
273   // Dynamically link to deprecated APIs for Mac OS X 10.6 support.
274   void* app_services_library_;
275   CGDisplayBaseAddressFunc cg_display_base_address_;
276   CGDisplayBytesPerRowFunc cg_display_bytes_per_row_;
277   CGDisplayBitsPerPixelFunc cg_display_bits_per_pixel_;
278   void* opengl_library_;
279   CGLSetFullScreenFunc cgl_set_full_screen_;
280
281   CGWindowID excluded_window_;
282
283   DISALLOW_COPY_AND_ASSIGN(ScreenCapturerMac);
284 };
285
286 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
287 // stride.
288 class InvertedDesktopFrame : public DesktopFrame {
289  public:
290   // Takes ownership of |frame|.
291   InvertedDesktopFrame(DesktopFrame* frame)
292       : DesktopFrame(
293             frame->size(), -frame->stride(),
294             frame->data() + (frame->size().height() - 1) * frame->stride(),
295             frame->shared_memory()),
296         original_frame_(frame) {
297     set_dpi(frame->dpi());
298     set_capture_time_ms(frame->capture_time_ms());
299     mutable_updated_region()->Swap(frame->mutable_updated_region());
300   }
301   virtual ~InvertedDesktopFrame() {}
302
303  private:
304   scoped_ptr<DesktopFrame> original_frame_;
305
306   DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
307 };
308
309 ScreenCapturerMac::ScreenCapturerMac(
310     scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
311     : callback_(NULL),
312       cgl_context_(NULL),
313       current_display_(0),
314       dip_to_pixel_scale_(1.0f),
315       desktop_config_monitor_(desktop_config_monitor),
316       power_assertion_id_display_(kIOPMNullAssertionID),
317       power_assertion_id_user_(kIOPMNullAssertionID),
318       app_services_library_(NULL),
319       cg_display_base_address_(NULL),
320       cg_display_bytes_per_row_(NULL),
321       cg_display_bits_per_pixel_(NULL),
322       opengl_library_(NULL),
323       cgl_set_full_screen_(NULL),
324       excluded_window_(0) {
325 }
326
327 ScreenCapturerMac::~ScreenCapturerMac() {
328   if (power_assertion_id_display_ != kIOPMNullAssertionID) {
329     IOPMAssertionRelease(power_assertion_id_display_);
330     power_assertion_id_display_ = kIOPMNullAssertionID;
331   }
332   if (power_assertion_id_user_ != kIOPMNullAssertionID) {
333     IOPMAssertionRelease(power_assertion_id_user_);
334     power_assertion_id_user_ = kIOPMNullAssertionID;
335   }
336
337   ReleaseBuffers();
338   UnregisterRefreshAndMoveHandlers();
339   dlclose(app_services_library_);
340   dlclose(opengl_library_);
341 }
342
343 bool ScreenCapturerMac::Init() {
344   if (!RegisterRefreshAndMoveHandlers()) {
345     return false;
346   }
347   desktop_config_monitor_->Lock();
348   desktop_config_ = desktop_config_monitor_->desktop_configuration();
349   desktop_config_monitor_->Unlock();
350   ScreenConfigurationChanged();
351   return true;
352 }
353
354 void ScreenCapturerMac::ReleaseBuffers() {
355   if (cgl_context_) {
356     pixel_buffer_object_.Release();
357     CGLDestroyContext(cgl_context_);
358     cgl_context_ = NULL;
359   }
360   // The buffers might be in use by the encoder, so don't delete them here.
361   // Instead, mark them as "needs update"; next time the buffers are used by
362   // the capturer, they will be recreated if necessary.
363   queue_.Reset();
364 }
365
366 void ScreenCapturerMac::Start(Callback* callback) {
367   assert(!callback_);
368   assert(callback);
369
370   callback_ = callback;
371
372   // Create power management assertions to wake the display and prevent it from
373   // going to sleep on user idle.
374   // TODO(jamiewalch): Use IOPMAssertionDeclareUserActivity on 10.7.3 and above
375   //                   instead of the following two assertions.
376   IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
377                               kIOPMAssertionLevelOn,
378                               CFSTR("Chrome Remote Desktop connection active"),
379                               &power_assertion_id_display_);
380   // This assertion ensures that the display is woken up if it  already asleep
381   // (as used by Apple Remote Desktop).
382   IOPMAssertionCreateWithName(CFSTR("UserIsActive"),
383                               kIOPMAssertionLevelOn,
384                               CFSTR("Chrome Remote Desktop connection active"),
385                               &power_assertion_id_user_);
386 }
387
388 void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) {
389   TickTime capture_start_time = TickTime::Now();
390
391   queue_.MoveToNextFrame();
392
393   desktop_config_monitor_->Lock();
394   MacDesktopConfiguration new_config =
395       desktop_config_monitor_->desktop_configuration();
396   if (!desktop_config_.Equals(new_config)) {
397     desktop_config_ = new_config;
398     // If the display configuraiton has changed then refresh capturer data
399     // structures. Occasionally, the refresh and move handlers are lost when
400     // the screen mode changes, so re-register them here.
401     UnregisterRefreshAndMoveHandlers();
402     RegisterRefreshAndMoveHandlers();
403     ScreenConfigurationChanged();
404   }
405
406   DesktopRegion region;
407   helper_.TakeInvalidRegion(&region);
408
409   // If the current buffer is from an older generation then allocate a new one.
410   // Note that we can't reallocate other buffers at this point, since the caller
411   // may still be reading from them.
412   if (!queue_.current_frame())
413     queue_.ReplaceCurrentFrame(CreateFrame());
414
415   DesktopFrame* current_frame = queue_.current_frame();
416
417   bool flip = false;  // GL capturers need flipping.
418   if (rtc::GetOSVersionName() >= rtc::kMacOSLion) {
419     // Lion requires us to use their new APIs for doing screen capture. These
420     // APIS currently crash on 10.6.8 if there is no monitor attached.
421     if (!CgBlitPostLion(*current_frame, region)) {
422       desktop_config_monitor_->Unlock();
423       callback_->OnCaptureCompleted(NULL);
424       return;
425     }
426   } else if (cgl_context_) {
427     flip = true;
428     if (pixel_buffer_object_.get() != 0) {
429       GlBlitFast(*current_frame, region);
430     } else {
431       // See comment in ScopedPixelBufferObject::Init about why the slow
432       // path is always used on 10.5.
433       GlBlitSlow(*current_frame);
434     }
435   } else {
436     CgBlitPreLion(*current_frame, region);
437   }
438
439   DesktopFrame* new_frame = queue_.current_frame()->Share();
440   *new_frame->mutable_updated_region() = region;
441
442   if (flip)
443     new_frame = new InvertedDesktopFrame(new_frame);
444
445   helper_.set_size_most_recent(new_frame->size());
446
447   // Signal that we are done capturing data from the display framebuffer,
448   // and accessing display structures.
449   desktop_config_monitor_->Unlock();
450
451   new_frame->set_capture_time_ms(
452       (TickTime::Now() - capture_start_time).Milliseconds());
453   callback_->OnCaptureCompleted(new_frame);
454 }
455
456 void ScreenCapturerMac::SetExcludedWindow(WindowId window) {
457   excluded_window_ = window;
458 }
459
460 bool ScreenCapturerMac::GetScreenList(ScreenList* screens) {
461   assert(screens->size() == 0);
462   if (rtc::GetOSVersionName() < rtc::kMacOSLion) {
463     // Single monitor cast is not supported on pre OS X 10.7.
464     Screen screen;
465     screen.id = kFullDesktopScreenId;
466     screens->push_back(screen);
467     return true;
468   }
469
470   for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin();
471        it != desktop_config_.displays.end(); ++it) {
472     Screen screen;
473     screen.id = static_cast<ScreenId>(it->id);
474     screens->push_back(screen);
475   }
476   return true;
477 }
478
479 bool ScreenCapturerMac::SelectScreen(ScreenId id) {
480   if (rtc::GetOSVersionName() < rtc::kMacOSLion) {
481     // Ignore the screen selection on unsupported OS.
482     assert(!current_display_);
483     return id == kFullDesktopScreenId;
484   }
485
486   if (id == kFullDesktopScreenId) {
487     current_display_ = 0;
488   } else {
489     const MacDisplayConfiguration* config =
490         desktop_config_.FindDisplayConfigurationById(
491             static_cast<CGDirectDisplayID>(id));
492     if (!config)
493       return false;
494     current_display_ = config->id;
495   }
496
497   ScreenConfigurationChanged();
498   return true;
499 }
500
501 void ScreenCapturerMac::GlBlitFast(const DesktopFrame& frame,
502                                    const DesktopRegion& region) {
503   // Clip to the size of our current screen.
504   DesktopRect clip_rect = DesktopRect::MakeSize(frame.size());
505   if (queue_.previous_frame()) {
506     // We are doing double buffer for the capture data so we just need to copy
507     // the invalid region from the previous capture in the current buffer.
508     // TODO(hclam): We can reduce the amount of copying here by subtracting
509     // |capturer_helper_|s region from |last_invalid_region_|.
510     // http://crbug.com/92354
511
512     // Since the image obtained from OpenGL is upside-down, need to do some
513     // magic here to copy the correct rectangle.
514     const int y_offset = (frame.size().height() - 1) * frame.stride();
515     for (DesktopRegion::Iterator i(last_invalid_region_);
516          !i.IsAtEnd(); i.Advance()) {
517       DesktopRect copy_rect = i.rect();
518       copy_rect.IntersectWith(clip_rect);
519       if (!copy_rect.is_empty()) {
520         CopyRect(queue_.previous_frame()->data() + y_offset,
521                  -frame.stride(),
522                  frame.data() + y_offset,
523                  -frame.stride(),
524                  DesktopFrame::kBytesPerPixel,
525                  copy_rect);
526       }
527     }
528   }
529   last_invalid_region_ = region;
530
531   CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
532   glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pixel_buffer_object_.get());
533   glReadPixels(0, 0, frame.size().width(), frame.size().height(), GL_BGRA,
534                GL_UNSIGNED_BYTE, 0);
535   GLubyte* ptr = static_cast<GLubyte*>(
536       glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB));
537   if (ptr == NULL) {
538     // If the buffer can't be mapped, assume that it's no longer valid and
539     // release it.
540     pixel_buffer_object_.Release();
541   } else {
542     // Copy only from the dirty rects. Since the image obtained from OpenGL is
543     // upside-down we need to do some magic here to copy the correct rectangle.
544     const int y_offset = (frame.size().height() - 1) * frame.stride();
545     for (DesktopRegion::Iterator i(region);
546          !i.IsAtEnd(); i.Advance()) {
547       DesktopRect copy_rect = i.rect();
548       copy_rect.IntersectWith(clip_rect);
549       if (!copy_rect.is_empty()) {
550         CopyRect(ptr + y_offset,
551                  -frame.stride(),
552                  frame.data() + y_offset,
553                  -frame.stride(),
554                  DesktopFrame::kBytesPerPixel,
555                  copy_rect);
556       }
557     }
558   }
559   if (!glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB)) {
560     // If glUnmapBuffer returns false, then the contents of the data store are
561     // undefined. This might be because the screen mode has changed, in which
562     // case it will be recreated in ScreenConfigurationChanged, but releasing
563     // the object here is the best option. Capturing will fall back on
564     // GlBlitSlow until such time as the pixel buffer object is recreated.
565     pixel_buffer_object_.Release();
566   }
567   glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
568 }
569
570 void ScreenCapturerMac::GlBlitSlow(const DesktopFrame& frame) {
571   CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
572   glReadBuffer(GL_FRONT);
573   glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
574   glPixelStorei(GL_PACK_ALIGNMENT, 4);  // Force 4-byte alignment.
575   glPixelStorei(GL_PACK_ROW_LENGTH, 0);
576   glPixelStorei(GL_PACK_SKIP_ROWS, 0);
577   glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
578   // Read a block of pixels from the frame buffer.
579   glReadPixels(0, 0, frame.size().width(), frame.size().height(),
580                GL_BGRA, GL_UNSIGNED_BYTE, frame.data());
581   glPopClientAttrib();
582 }
583
584 void ScreenCapturerMac::CgBlitPreLion(const DesktopFrame& frame,
585                                       const DesktopRegion& region) {
586   // Copy the entire contents of the previous capture buffer, to capture over.
587   // TODO(wez): Get rid of this as per crbug.com/145064, or implement
588   // crbug.com/92354.
589   if (queue_.previous_frame()) {
590     memcpy(frame.data(),
591            queue_.previous_frame()->data(),
592            frame.stride() * frame.size().height());
593   }
594
595   for (size_t i = 0; i < desktop_config_.displays.size(); ++i) {
596     const MacDisplayConfiguration& display_config = desktop_config_.displays[i];
597
598     // Use deprecated APIs to determine the display buffer layout.
599     assert(cg_display_base_address_ && cg_display_bytes_per_row_ &&
600         cg_display_bits_per_pixel_);
601     uint8_t* display_base_address = reinterpret_cast<uint8_t*>(
602         (*cg_display_base_address_)(display_config.id));
603     assert(display_base_address);
604     int src_bytes_per_row = (*cg_display_bytes_per_row_)(display_config.id);
605     int src_bytes_per_pixel =
606         (*cg_display_bits_per_pixel_)(display_config.id) / 8;
607
608     // Determine the display's position relative to the desktop, in pixels.
609     DesktopRect display_bounds = display_config.pixel_bounds;
610     display_bounds.Translate(-desktop_config_.pixel_bounds.left(),
611                              -desktop_config_.pixel_bounds.top());
612
613     // Determine which parts of the blit region, if any, lay within the monitor.
614     DesktopRegion copy_region = region;
615     copy_region.IntersectWith(display_bounds);
616     if (copy_region.is_empty())
617       continue;
618
619     // Translate the region to be copied into display-relative coordinates.
620     copy_region.Translate(-display_bounds.left(), -display_bounds.top());
621
622     // Calculate where in the output buffer the display's origin is.
623     uint8_t* out_ptr = frame.data() +
624          (display_bounds.left() * src_bytes_per_pixel) +
625          (display_bounds.top() * frame.stride());
626
627     // Copy the dirty region from the display buffer into our desktop buffer.
628     for (DesktopRegion::Iterator i(copy_region); !i.IsAtEnd(); i.Advance()) {
629       CopyRect(display_base_address,
630                src_bytes_per_row,
631                out_ptr,
632                frame.stride(),
633                src_bytes_per_pixel,
634                i.rect());
635     }
636   }
637 }
638
639 bool ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
640                                        const DesktopRegion& region) {
641   // Copy the entire contents of the previous capture buffer, to capture over.
642   // TODO(wez): Get rid of this as per crbug.com/145064, or implement
643   // crbug.com/92354.
644   if (queue_.previous_frame()) {
645     memcpy(frame.data(),
646            queue_.previous_frame()->data(),
647            frame.stride() * frame.size().height());
648   }
649
650   MacDisplayConfigurations displays_to_capture;
651   if (current_display_) {
652     // Capturing a single screen. Note that the screen id may change when
653     // screens are added or removed.
654     const MacDisplayConfiguration* config =
655         desktop_config_.FindDisplayConfigurationById(current_display_);
656     if (config) {
657       displays_to_capture.push_back(*config);
658     } else {
659       LOG(LS_ERROR) << "The selected screen cannot be found for capturing.";
660       return false;
661     }
662   } else {
663     // Capturing the whole desktop.
664     displays_to_capture = desktop_config_.displays;
665   }
666
667   // Create the window list once for all displays.
668   CFArrayRef window_list = CreateWindowListWithExclusion(excluded_window_);
669
670   for (size_t i = 0; i < displays_to_capture.size(); ++i) {
671     const MacDisplayConfiguration& display_config = displays_to_capture[i];
672
673     // Capturing mixed-DPI on one surface is hard, so we only return displays
674     // that match the "primary" display's DPI. The primary display is always
675     // the first in the list.
676     if (i > 0 && display_config.dip_to_pixel_scale !=
677         displays_to_capture[0].dip_to_pixel_scale) {
678       continue;
679     }
680     // Determine the display's position relative to the desktop, in pixels.
681     DesktopRect display_bounds = display_config.pixel_bounds;
682     display_bounds.Translate(-screen_pixel_bounds_.left(),
683                              -screen_pixel_bounds_.top());
684
685     // Determine which parts of the blit region, if any, lay within the monitor.
686     DesktopRegion copy_region = region;
687     copy_region.IntersectWith(display_bounds);
688     if (copy_region.is_empty())
689       continue;
690
691     // Translate the region to be copied into display-relative coordinates.
692     copy_region.Translate(-display_bounds.left(), -display_bounds.top());
693
694     DesktopRect excluded_window_bounds;
695     CGImageRef excluded_image = NULL;
696     CFDataRef excluded_window_region_data = NULL;
697     if (excluded_window_ && window_list) {
698       // Get the region of the excluded window relative the primary display.
699       excluded_window_bounds = GetExcludedWindowPixelBounds(
700           excluded_window_, display_config.dip_to_pixel_scale);
701       excluded_window_bounds.IntersectWith(display_config.pixel_bounds);
702
703       // Create the image under the excluded window first, because it's faster
704       // than captuing the whole display.
705       if (!excluded_window_bounds.is_empty()) {
706         excluded_image = CreateExcludedWindowRegionImage(
707             excluded_window_bounds,
708             display_config.dip_to_pixel_scale,
709             window_list,
710             &excluded_window_region_data);
711       }
712     }
713
714     // Create an image containing a snapshot of the display.
715     CGImageRef image = CGDisplayCreateImage(display_config.id);
716     if (image == NULL)
717       continue;
718
719     // Request access to the raw pixel data via the image's DataProvider.
720     CGDataProviderRef provider = CGImageGetDataProvider(image);
721     CFDataRef data = CGDataProviderCopyData(provider);
722     assert(data);
723
724     const uint8_t* display_base_address = CFDataGetBytePtr(data);
725     int src_bytes_per_row = CGImageGetBytesPerRow(image);
726     int src_bytes_per_pixel = CGImageGetBitsPerPixel(image) / 8;
727
728     // Calculate where in the output buffer the display's origin is.
729     uint8_t* out_ptr = frame.data() +
730         (display_bounds.left() * src_bytes_per_pixel) +
731         (display_bounds.top() * frame.stride());
732
733     // Copy the dirty region from the display buffer into our desktop buffer.
734     for (DesktopRegion::Iterator i(copy_region); !i.IsAtEnd(); i.Advance()) {
735       CopyRect(display_base_address,
736                src_bytes_per_row,
737                out_ptr,
738                frame.stride(),
739                src_bytes_per_pixel,
740                i.rect());
741     }
742
743     // Copy the region of the excluded window to the frame.
744     if (excluded_image) {
745       assert(excluded_window_region_data);
746       display_base_address = CFDataGetBytePtr(excluded_window_region_data);
747       src_bytes_per_row = CGImageGetBytesPerRow(excluded_image);
748
749       // Translate the bounds relative to the desktop, because |frame| data
750       // starts from the desktop top-left corner.
751       DesktopRect window_bounds_relative_to_desktop(excluded_window_bounds);
752       window_bounds_relative_to_desktop.Translate(
753           -screen_pixel_bounds_.left(), -screen_pixel_bounds_.top());
754       out_ptr = frame.data() +
755           (window_bounds_relative_to_desktop.left() * src_bytes_per_pixel) +
756           (window_bounds_relative_to_desktop.top() * frame.stride());
757
758       CopyRect(display_base_address,
759                src_bytes_per_row,
760                out_ptr,
761                frame.stride(),
762                src_bytes_per_pixel,
763                DesktopRect::MakeSize(excluded_window_bounds.size()));
764       CFRelease(excluded_window_region_data);
765       CFRelease(excluded_image);
766     }
767
768     CFRelease(data);
769     CFRelease(image);
770   }
771   if (window_list)
772     CFRelease(window_list);
773   return true;
774 }
775
776 void ScreenCapturerMac::ScreenConfigurationChanged() {
777   if (current_display_) {
778     const MacDisplayConfiguration* config =
779         desktop_config_.FindDisplayConfigurationById(current_display_);
780     screen_pixel_bounds_ = config ? config->pixel_bounds : DesktopRect();
781     dip_to_pixel_scale_ = config ? config->dip_to_pixel_scale : 1.0f;
782   } else {
783     screen_pixel_bounds_ = desktop_config_.pixel_bounds;
784     dip_to_pixel_scale_ = desktop_config_.dip_to_pixel_scale;
785   }
786
787   // Release existing buffers, which will be of the wrong size.
788   ReleaseBuffers();
789
790   // Clear the dirty region, in case the display is down-sizing.
791   helper_.ClearInvalidRegion();
792
793   // Re-mark the entire desktop as dirty.
794   helper_.InvalidateScreen(screen_pixel_bounds_.size());
795
796   // Make sure the frame buffers will be reallocated.
797   queue_.Reset();
798
799   // CgBlitPostLion uses CGDisplayCreateImage() to snapshot each display's
800   // contents. Although the API exists in OS 10.6, it crashes the caller if
801   // the machine has no monitor connected, so we fall back to depcreated APIs
802   // when running on 10.6.
803   if (rtc::GetOSVersionName() >= rtc::kMacOSLion) {
804     LOG(LS_INFO) << "Using CgBlitPostLion.";
805     // No need for any OpenGL support on Lion
806     return;
807   }
808
809   // Dynamically link to the deprecated pre-Lion capture APIs.
810   app_services_library_ = dlopen(kApplicationServicesLibraryName,
811                                  RTLD_LAZY);
812   if (!app_services_library_) {
813     LOG_F(LS_ERROR) << "Failed to open " << kApplicationServicesLibraryName;
814     abort();
815   }
816
817   opengl_library_ = dlopen(kOpenGlLibraryName, RTLD_LAZY);
818   if (!opengl_library_) {
819     LOG_F(LS_ERROR) << "Failed to open " << kOpenGlLibraryName;
820     abort();
821   }
822
823   cg_display_base_address_ = reinterpret_cast<CGDisplayBaseAddressFunc>(
824       dlsym(app_services_library_, "CGDisplayBaseAddress"));
825   cg_display_bytes_per_row_ = reinterpret_cast<CGDisplayBytesPerRowFunc>(
826       dlsym(app_services_library_, "CGDisplayBytesPerRow"));
827   cg_display_bits_per_pixel_ = reinterpret_cast<CGDisplayBitsPerPixelFunc>(
828       dlsym(app_services_library_, "CGDisplayBitsPerPixel"));
829   cgl_set_full_screen_ = reinterpret_cast<CGLSetFullScreenFunc>(
830       dlsym(opengl_library_, "CGLSetFullScreen"));
831   if (!(cg_display_base_address_ && cg_display_bytes_per_row_ &&
832         cg_display_bits_per_pixel_ && cgl_set_full_screen_)) {
833     LOG_F(LS_ERROR);
834     abort();
835   }
836
837   if (desktop_config_.displays.size() > 1) {
838     LOG(LS_INFO) << "Using CgBlitPreLion (Multi-monitor).";
839     return;
840   }
841
842   CGDirectDisplayID mainDevice = CGMainDisplayID();
843   if (!CGDisplayUsesOpenGLAcceleration(mainDevice)) {
844     LOG(LS_INFO) << "Using CgBlitPreLion (OpenGL unavailable).";
845     return;
846   }
847
848   LOG(LS_INFO) << "Using GlBlit";
849
850   CGLPixelFormatAttribute attributes[] = {
851     // This function does an early return if GetOSVersionName() >= kMacOSLion,
852     // this code only runs on 10.6 and can be deleted once 10.6 support is
853     // dropped.  So just keep using kCGLPFAFullScreen even though it was
854     // deprecated in 10.6 -- it's still functional there, and it's not used on
855     // newer OS X versions.
856 #pragma clang diagnostic push
857 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
858     kCGLPFAFullScreen,
859 #pragma clang diagnostic pop
860     kCGLPFADisplayMask,
861     (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(mainDevice),
862     (CGLPixelFormatAttribute)0
863   };
864   CGLPixelFormatObj pixel_format = NULL;
865   GLint matching_pixel_format_count = 0;
866   CGLError err = CGLChoosePixelFormat(attributes,
867                                       &pixel_format,
868                                       &matching_pixel_format_count);
869   assert(err == kCGLNoError);
870   err = CGLCreateContext(pixel_format, NULL, &cgl_context_);
871   assert(err == kCGLNoError);
872   CGLDestroyPixelFormat(pixel_format);
873   (*cgl_set_full_screen_)(cgl_context_);
874   CGLSetCurrentContext(cgl_context_);
875
876   size_t buffer_size = screen_pixel_bounds_.width() *
877                        screen_pixel_bounds_.height() *
878                        sizeof(uint32_t);
879   pixel_buffer_object_.Init(cgl_context_, buffer_size);
880 }
881
882 bool ScreenCapturerMac::RegisterRefreshAndMoveHandlers() {
883   CGError err = CGRegisterScreenRefreshCallback(
884       ScreenCapturerMac::ScreenRefreshCallback, this);
885   if (err != kCGErrorSuccess) {
886     LOG(LS_ERROR) << "CGRegisterScreenRefreshCallback " << err;
887     return false;
888   }
889
890   err = CGScreenRegisterMoveCallback(
891       ScreenCapturerMac::ScreenUpdateMoveCallback, this);
892   if (err != kCGErrorSuccess) {
893     LOG(LS_ERROR) << "CGScreenRegisterMoveCallback " << err;
894     return false;
895   }
896
897   return true;
898 }
899
900 void ScreenCapturerMac::UnregisterRefreshAndMoveHandlers() {
901   CGUnregisterScreenRefreshCallback(
902       ScreenCapturerMac::ScreenRefreshCallback, this);
903   CGScreenUnregisterMoveCallback(
904       ScreenCapturerMac::ScreenUpdateMoveCallback, this);
905 }
906
907 void ScreenCapturerMac::ScreenRefresh(CGRectCount count,
908                                       const CGRect* rect_array) {
909   if (screen_pixel_bounds_.is_empty())
910     return;
911
912   DesktopRegion region;
913   DesktopVector translate_vector =
914       DesktopVector().subtract(screen_pixel_bounds_.top_left());
915   for (CGRectCount i = 0; i < count; ++i) {
916     // Convert from Density-Independent Pixel to physical pixel coordinates.
917     DesktopRect rect = ScaleAndRoundCGRect(rect_array[i], dip_to_pixel_scale_);
918     // Translate from local desktop to capturer framebuffer coordinates.
919     rect.Translate(translate_vector);
920     region.AddRect(rect);
921   }
922
923   helper_.InvalidateRegion(region);
924 }
925
926 void ScreenCapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
927                                          size_t count,
928                                          const CGRect* rect_array) {
929   // Translate |rect_array| to identify the move's destination.
930   CGRect refresh_rects[count];
931   for (CGRectCount i = 0; i < count; ++i) {
932     refresh_rects[i] = CGRectOffset(rect_array[i], delta.dX, delta.dY);
933   }
934
935   // Currently we just treat move events the same as refreshes.
936   ScreenRefresh(count, refresh_rects);
937 }
938
939 void ScreenCapturerMac::ScreenRefreshCallback(CGRectCount count,
940                                               const CGRect* rect_array,
941                                               void* user_parameter) {
942   ScreenCapturerMac* capturer =
943       reinterpret_cast<ScreenCapturerMac*>(user_parameter);
944   if (capturer->screen_pixel_bounds_.is_empty())
945     capturer->ScreenConfigurationChanged();
946   capturer->ScreenRefresh(count, rect_array);
947 }
948
949 void ScreenCapturerMac::ScreenUpdateMoveCallback(
950     CGScreenUpdateMoveDelta delta,
951     size_t count,
952     const CGRect* rect_array,
953     void* user_parameter) {
954   ScreenCapturerMac* capturer =
955       reinterpret_cast<ScreenCapturerMac*>(user_parameter);
956   capturer->ScreenUpdateMove(delta, count, rect_array);
957 }
958
959 DesktopFrame* ScreenCapturerMac::CreateFrame() {
960   scoped_ptr<DesktopFrame> frame(
961       new BasicDesktopFrame(screen_pixel_bounds_.size()));
962
963   frame->set_dpi(DesktopVector(kStandardDPI * dip_to_pixel_scale_,
964                                kStandardDPI * dip_to_pixel_scale_));
965   return frame.release();
966 }
967
968 }  // namespace
969
970 // static
971 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
972   if (!options.configuration_monitor())
973     return NULL;
974
975   scoped_ptr<ScreenCapturerMac> capturer(
976       new ScreenCapturerMac(options.configuration_monitor()));
977   if (!capturer->Init())
978     capturer.reset();
979   return capturer.release();
980 }
981
982 }  // namespace webrtc