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