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