| OLD | NEW | 
 | (Empty) | 
|    1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |  | 
|    2 // Use of this source code is governed by a BSD-style license that can be |  | 
|    3 // found in the LICENSE file. |  | 
|    4  |  | 
|    5 #include "remoting/capturer/video_frame_capturer.h" |  | 
|    6  |  | 
|    7 #include <ApplicationServices/ApplicationServices.h> |  | 
|    8 #include <Cocoa/Cocoa.h> |  | 
|    9 #include <dlfcn.h> |  | 
|   10 #include <IOKit/pwr_mgt/IOPMLib.h> |  | 
|   11 #include <OpenGL/CGLMacro.h> |  | 
|   12 #include <OpenGL/OpenGL.h> |  | 
|   13 #include <set> |  | 
|   14 #include <stddef.h> |  | 
|   15  |  | 
|   16 #include "base/logging.h" |  | 
|   17 #include "base/file_path.h" |  | 
|   18 #include "base/mac/mac_util.h" |  | 
|   19 #include "base/mac/scoped_cftyperef.h" |  | 
|   20 #include "base/memory/scoped_ptr.h" |  | 
|   21 #include "base/scoped_native_library.h" |  | 
|   22 #include "base/synchronization/waitable_event.h" |  | 
|   23 #include "base/time.h" |  | 
|   24 #include "remoting/capturer/capture_data.h" |  | 
|   25 #include "remoting/capturer/mac/scoped_pixel_buffer_object.h" |  | 
|   26 #include "remoting/capturer/mouse_cursor_shape.h" |  | 
|   27 #include "remoting/capturer/video_frame.h" |  | 
|   28 #include "remoting/capturer/video_frame_capturer_helper.h" |  | 
|   29 #include "remoting/capturer/video_frame_queue.h" |  | 
|   30  |  | 
|   31 namespace remoting { |  | 
|   32  |  | 
|   33 namespace { |  | 
|   34  |  | 
|   35 // Definitions used to dynamic-link to deprecated OS 10.6 functions. |  | 
|   36 const char* kApplicationServicesLibraryName = |  | 
|   37     "/System/Library/Frameworks/ApplicationServices.framework/" |  | 
|   38     "ApplicationServices"; |  | 
|   39 typedef void* (*CGDisplayBaseAddressFunc)(CGDirectDisplayID); |  | 
|   40 typedef size_t (*CGDisplayBytesPerRowFunc)(CGDirectDisplayID); |  | 
|   41 typedef size_t (*CGDisplayBitsPerPixelFunc)(CGDirectDisplayID); |  | 
|   42 const char* kOpenGlLibraryName = |  | 
|   43     "/System/Library/Frameworks/OpenGL.framework/OpenGL"; |  | 
|   44 typedef CGLError (*CGLSetFullScreenFunc)(CGLContextObj); |  | 
|   45  |  | 
|   46 // skia/ext/skia_utils_mac.h only defines CGRectToSkRect(). |  | 
|   47 SkIRect CGRectToSkIRect(const CGRect& rect) { |  | 
|   48   SkIRect sk_rect = { |  | 
|   49     SkScalarRound(rect.origin.x), |  | 
|   50     SkScalarRound(rect.origin.y), |  | 
|   51     SkScalarRound(rect.origin.x + rect.size.width), |  | 
|   52     SkScalarRound(rect.origin.y + rect.size.height) |  | 
|   53   }; |  | 
|   54   return sk_rect; |  | 
|   55 } |  | 
|   56  |  | 
|   57 // Copy pixels in the |rect| from |src_place| to |dest_plane|. |  | 
|   58 void CopyRect(const uint8* src_plane, |  | 
|   59               int src_plane_stride, |  | 
|   60               uint8* dest_plane, |  | 
|   61               int dest_plane_stride, |  | 
|   62               int bytes_per_pixel, |  | 
|   63               const SkIRect& rect) { |  | 
|   64   // Get the address of the starting point. |  | 
|   65   const int src_y_offset = src_plane_stride * rect.top(); |  | 
|   66   const int dest_y_offset = dest_plane_stride * rect.top(); |  | 
|   67   const int x_offset = bytes_per_pixel * rect.left(); |  | 
|   68   src_plane += src_y_offset + x_offset; |  | 
|   69   dest_plane += dest_y_offset + x_offset; |  | 
|   70  |  | 
|   71   // Copy pixels in the rectangle line by line. |  | 
|   72   const int bytes_per_line = bytes_per_pixel * rect.width(); |  | 
|   73   const int height = rect.height(); |  | 
|   74   for (int i = 0 ; i < height; ++i) { |  | 
|   75     memcpy(dest_plane, src_plane, bytes_per_line); |  | 
|   76     src_plane += src_plane_stride; |  | 
|   77     dest_plane += dest_plane_stride; |  | 
|   78   } |  | 
|   79 } |  | 
|   80  |  | 
|   81 // The amount of time allowed for displays to reconfigure. |  | 
|   82 const int64 kDisplayConfigurationEventTimeoutInSeconds = 10; |  | 
|   83  |  | 
|   84 // A class representing a full-frame pixel buffer. |  | 
|   85 class VideoFrameMac : public VideoFrame { |  | 
|   86  public: |  | 
|   87   explicit VideoFrameMac(const SkISize& size); |  | 
|   88   virtual ~VideoFrameMac(); |  | 
|   89  |  | 
|   90   const SkIPoint& dpi() const { return dpi_; } |  | 
|   91  |  | 
|   92  private: |  | 
|   93   // Allocated pixel buffer. |  | 
|   94   scoped_array<uint8> data_; |  | 
|   95  |  | 
|   96   // DPI settings for this buffer. |  | 
|   97   SkIPoint dpi_; |  | 
|   98  |  | 
|   99   DISALLOW_COPY_AND_ASSIGN(VideoFrameMac); |  | 
|  100 }; |  | 
|  101  |  | 
|  102 // A class to perform video frame capturing for mac. |  | 
|  103 class VideoFrameCapturerMac : public VideoFrameCapturer { |  | 
|  104  public: |  | 
|  105   VideoFrameCapturerMac(); |  | 
|  106   virtual ~VideoFrameCapturerMac(); |  | 
|  107  |  | 
|  108   bool Init(); |  | 
|  109  |  | 
|  110   // Overridden from VideoFrameCapturer: |  | 
|  111   virtual void Start(Delegate* delegate) OVERRIDE; |  | 
|  112   virtual void Stop() OVERRIDE; |  | 
|  113   virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE; |  | 
|  114   virtual void CaptureFrame() OVERRIDE; |  | 
|  115  |  | 
|  116  private: |  | 
|  117   void CaptureCursor(); |  | 
|  118  |  | 
|  119   void GlBlitFast(const VideoFrame& buffer, const SkRegion& region); |  | 
|  120   void GlBlitSlow(const VideoFrame& buffer); |  | 
|  121   void CgBlitPreLion(const VideoFrame& buffer, const SkRegion& region); |  | 
|  122   void CgBlitPostLion(const VideoFrame& buffer, const SkRegion& region); |  | 
|  123  |  | 
|  124   // Called when the screen configuration is changed. |  | 
|  125   void ScreenConfigurationChanged(); |  | 
|  126  |  | 
|  127   void ScreenRefresh(CGRectCount count, const CGRect *rect_array); |  | 
|  128   void ScreenUpdateMove(CGScreenUpdateMoveDelta delta, |  | 
|  129                         size_t count, |  | 
|  130                         const CGRect *rect_array); |  | 
|  131   void DisplaysReconfigured(CGDirectDisplayID display, |  | 
|  132                             CGDisplayChangeSummaryFlags flags); |  | 
|  133   static void ScreenRefreshCallback(CGRectCount count, |  | 
|  134                                     const CGRect *rect_array, |  | 
|  135                                     void *user_parameter); |  | 
|  136   static void ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta, |  | 
|  137                                        size_t count, |  | 
|  138                                        const CGRect *rect_array, |  | 
|  139                                        void *user_parameter); |  | 
|  140   static void DisplaysReconfiguredCallback(CGDirectDisplayID display, |  | 
|  141                                            CGDisplayChangeSummaryFlags flags, |  | 
|  142                                            void *user_parameter); |  | 
|  143  |  | 
|  144   void ReleaseBuffers(); |  | 
|  145  |  | 
|  146   Delegate* delegate_; |  | 
|  147  |  | 
|  148   CGLContextObj cgl_context_; |  | 
|  149   ScopedPixelBufferObject pixel_buffer_object_; |  | 
|  150  |  | 
|  151   // Queue of the frames buffers. |  | 
|  152   VideoFrameQueue queue_; |  | 
|  153  |  | 
|  154   // Current display configuration. |  | 
|  155   std::vector<CGDirectDisplayID> display_ids_; |  | 
|  156   SkIRect desktop_bounds_; |  | 
|  157  |  | 
|  158   // A thread-safe list of invalid rectangles, and the size of the most |  | 
|  159   // recently captured screen. |  | 
|  160   VideoFrameCapturerHelper helper_; |  | 
|  161  |  | 
|  162   // Image of the last cursor that we sent to the client. |  | 
|  163   base::mac::ScopedCFTypeRef<CGImageRef> current_cursor_; |  | 
|  164  |  | 
|  165   // Contains an invalid region from the previous capture. |  | 
|  166   SkRegion last_invalid_region_; |  | 
|  167  |  | 
|  168   // Used to ensure that frame captures do not take place while displays |  | 
|  169   // are being reconfigured. |  | 
|  170   base::WaitableEvent display_configuration_capture_event_; |  | 
|  171  |  | 
|  172   // Records the Ids of attached displays which are being reconfigured. |  | 
|  173   // Accessed on the thread on which we are notified of display events. |  | 
|  174   std::set<CGDirectDisplayID> reconfiguring_displays_; |  | 
|  175  |  | 
|  176   // Power management assertion to prevent the screen from sleeping. |  | 
|  177   IOPMAssertionID power_assertion_id_display_; |  | 
|  178  |  | 
|  179   // Power management assertion to indicate that the user is active. |  | 
|  180   IOPMAssertionID power_assertion_id_user_; |  | 
|  181  |  | 
|  182   // Dynamically link to deprecated APIs for Mac OS X 10.6 support. |  | 
|  183   base::ScopedNativeLibrary app_services_library_; |  | 
|  184   CGDisplayBaseAddressFunc cg_display_base_address_; |  | 
|  185   CGDisplayBytesPerRowFunc cg_display_bytes_per_row_; |  | 
|  186   CGDisplayBitsPerPixelFunc cg_display_bits_per_pixel_; |  | 
|  187   base::ScopedNativeLibrary opengl_library_; |  | 
|  188   CGLSetFullScreenFunc cgl_set_full_screen_; |  | 
|  189  |  | 
|  190   DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerMac); |  | 
|  191 }; |  | 
|  192  |  | 
|  193 VideoFrameMac::VideoFrameMac(const SkISize& size) { |  | 
|  194   set_bytes_per_row(size.width() * sizeof(uint32_t)); |  | 
|  195   set_dimensions(size); |  | 
|  196  |  | 
|  197   size_t buffer_size = size.width() * size.height() * sizeof(uint32_t); |  | 
|  198   data_.reset(new  uint8[buffer_size]); |  | 
|  199   set_pixels(data_.get()); |  | 
|  200  |  | 
|  201   // TODO(wez): Move the ugly DPI code into a helper. |  | 
|  202   NSScreen* screen = [NSScreen mainScreen]; |  | 
|  203   NSDictionary* attr = [screen deviceDescription]; |  | 
|  204   NSSize resolution = [[attr objectForKey: NSDeviceResolution] sizeValue]; |  | 
|  205   dpi_.set(resolution.width, resolution.height); |  | 
|  206 } |  | 
|  207  |  | 
|  208 VideoFrameMac::~VideoFrameMac() { |  | 
|  209 } |  | 
|  210  |  | 
|  211 VideoFrameCapturerMac::VideoFrameCapturerMac() |  | 
|  212     : delegate_(NULL), |  | 
|  213       cgl_context_(NULL), |  | 
|  214       display_configuration_capture_event_(false, true), |  | 
|  215       power_assertion_id_display_(kIOPMNullAssertionID), |  | 
|  216       power_assertion_id_user_(kIOPMNullAssertionID), |  | 
|  217       cg_display_base_address_(NULL), |  | 
|  218       cg_display_bytes_per_row_(NULL), |  | 
|  219       cg_display_bits_per_pixel_(NULL), |  | 
|  220       cgl_set_full_screen_(NULL) |  | 
|  221 { |  | 
|  222 } |  | 
|  223  |  | 
|  224 VideoFrameCapturerMac::~VideoFrameCapturerMac() { |  | 
|  225   ReleaseBuffers(); |  | 
|  226   CGUnregisterScreenRefreshCallback( |  | 
|  227       VideoFrameCapturerMac::ScreenRefreshCallback, this); |  | 
|  228   CGScreenUnregisterMoveCallback( |  | 
|  229       VideoFrameCapturerMac::ScreenUpdateMoveCallback, this); |  | 
|  230   CGError err = CGDisplayRemoveReconfigurationCallback( |  | 
|  231       VideoFrameCapturerMac::DisplaysReconfiguredCallback, this); |  | 
|  232   if (err != kCGErrorSuccess) { |  | 
|  233     LOG(ERROR) << "CGDisplayRemoveReconfigurationCallback " << err; |  | 
|  234   } |  | 
|  235 } |  | 
|  236  |  | 
|  237 bool VideoFrameCapturerMac::Init() { |  | 
|  238   CGError err = CGRegisterScreenRefreshCallback( |  | 
|  239       VideoFrameCapturerMac::ScreenRefreshCallback, this); |  | 
|  240   if (err != kCGErrorSuccess) { |  | 
|  241     LOG(ERROR) << "CGRegisterScreenRefreshCallback " << err; |  | 
|  242     return false; |  | 
|  243   } |  | 
|  244  |  | 
|  245   err = CGScreenRegisterMoveCallback( |  | 
|  246       VideoFrameCapturerMac::ScreenUpdateMoveCallback, this); |  | 
|  247   if (err != kCGErrorSuccess) { |  | 
|  248     LOG(ERROR) << "CGScreenRegisterMoveCallback " << err; |  | 
|  249     return false; |  | 
|  250   } |  | 
|  251   err = CGDisplayRegisterReconfigurationCallback( |  | 
|  252       VideoFrameCapturerMac::DisplaysReconfiguredCallback, this); |  | 
|  253   if (err != kCGErrorSuccess) { |  | 
|  254     LOG(ERROR) << "CGDisplayRegisterReconfigurationCallback " << err; |  | 
|  255     return false; |  | 
|  256   } |  | 
|  257  |  | 
|  258   ScreenConfigurationChanged(); |  | 
|  259   return true; |  | 
|  260 } |  | 
|  261  |  | 
|  262 void VideoFrameCapturerMac::ReleaseBuffers() { |  | 
|  263   if (cgl_context_) { |  | 
|  264     pixel_buffer_object_.Release(); |  | 
|  265     CGLDestroyContext(cgl_context_); |  | 
|  266     cgl_context_ = NULL; |  | 
|  267   } |  | 
|  268   // The buffers might be in use by the encoder, so don't delete them here. |  | 
|  269   // Instead, mark them as "needs update"; next time the buffers are used by |  | 
|  270   // the capturer, they will be recreated if necessary. |  | 
|  271   queue_.SetAllFramesNeedUpdate(); |  | 
|  272 } |  | 
|  273  |  | 
|  274 void VideoFrameCapturerMac::Start(Delegate* delegate) { |  | 
|  275   DCHECK(delegate_ == NULL); |  | 
|  276  |  | 
|  277   delegate_ = delegate; |  | 
|  278  |  | 
|  279   // Create power management assertions to wake the display and prevent it from |  | 
|  280   // going to sleep on user idle. |  | 
|  281   // TODO(jamiewalch): Use IOPMAssertionDeclareUserActivity on 10.7.3 and above |  | 
|  282   //                   instead of the following two assertions. |  | 
|  283   IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, |  | 
|  284                               kIOPMAssertionLevelOn, |  | 
|  285                               CFSTR("Chrome Remote Desktop connection active"), |  | 
|  286                               &power_assertion_id_display_); |  | 
|  287   // This assertion ensures that the display is woken up if it  already asleep |  | 
|  288   // (as used by Apple Remote Desktop). |  | 
|  289   IOPMAssertionCreateWithName(CFSTR("UserIsActive"), |  | 
|  290                               kIOPMAssertionLevelOn, |  | 
|  291                               CFSTR("Chrome Remote Desktop connection active"), |  | 
|  292                               &power_assertion_id_user_); |  | 
|  293 } |  | 
|  294  |  | 
|  295 void VideoFrameCapturerMac::Stop() { |  | 
|  296   if (power_assertion_id_display_ != kIOPMNullAssertionID) { |  | 
|  297     IOPMAssertionRelease(power_assertion_id_display_); |  | 
|  298     power_assertion_id_display_ = kIOPMNullAssertionID; |  | 
|  299   } |  | 
|  300   if (power_assertion_id_user_ != kIOPMNullAssertionID) { |  | 
|  301     IOPMAssertionRelease(power_assertion_id_user_); |  | 
|  302     power_assertion_id_user_ = kIOPMNullAssertionID; |  | 
|  303   } |  | 
|  304 } |  | 
|  305  |  | 
|  306 void VideoFrameCapturerMac::InvalidateRegion(const SkRegion& invalid_region) { |  | 
|  307   helper_.InvalidateRegion(invalid_region); |  | 
|  308 } |  | 
|  309  |  | 
|  310 void VideoFrameCapturerMac::CaptureFrame() { |  | 
|  311   // Only allow captures when the display configuration is not occurring. |  | 
|  312   scoped_refptr<CaptureData> data; |  | 
|  313  |  | 
|  314   base::Time capture_start_time = base::Time::Now(); |  | 
|  315  |  | 
|  316   // Wait until the display configuration is stable. If one or more displays |  | 
|  317   // are reconfiguring then |display_configuration_capture_event_| will not be |  | 
|  318   // set until the reconfiguration completes. |  | 
|  319   // TODO(wez): Replace this with an early-exit (See crbug.com/104542). |  | 
|  320   CHECK(display_configuration_capture_event_.TimedWait( |  | 
|  321       base::TimeDelta::FromSeconds( |  | 
|  322           kDisplayConfigurationEventTimeoutInSeconds))); |  | 
|  323  |  | 
|  324   SkRegion region; |  | 
|  325   helper_.SwapInvalidRegion(®ion); |  | 
|  326  |  | 
|  327   // If the current buffer is from an older generation then allocate a new one. |  | 
|  328   // Note that we can't reallocate other buffers at this point, since the caller |  | 
|  329   // may still be reading from them. |  | 
|  330   if (queue_.current_frame_needs_update()) { |  | 
|  331     scoped_ptr<VideoFrameMac> buffer(new VideoFrameMac( |  | 
|  332         SkISize::Make(desktop_bounds_.width(), desktop_bounds_.height()))); |  | 
|  333     queue_.ReplaceCurrentFrame(buffer.PassAs<VideoFrame>()); |  | 
|  334   } |  | 
|  335  |  | 
|  336   VideoFrame* current_buffer = queue_.current_frame(); |  | 
|  337  |  | 
|  338   bool flip = false;  // GL capturers need flipping. |  | 
|  339   if (base::mac::IsOSLionOrLater()) { |  | 
|  340     // Lion requires us to use their new APIs for doing screen capture. These |  | 
|  341     // APIS currently crash on 10.6.8 if there is no monitor attached. |  | 
|  342     CgBlitPostLion(*current_buffer, region); |  | 
|  343   } else if (cgl_context_) { |  | 
|  344     flip = true; |  | 
|  345     if (pixel_buffer_object_.get() != 0) { |  | 
|  346       GlBlitFast(*current_buffer, region); |  | 
|  347     } else { |  | 
|  348       // See comment in ScopedPixelBufferObject::Init about why the slow |  | 
|  349       // path is always used on 10.5. |  | 
|  350       GlBlitSlow(*current_buffer); |  | 
|  351     } |  | 
|  352   } else { |  | 
|  353     CgBlitPreLion(*current_buffer, region); |  | 
|  354   } |  | 
|  355  |  | 
|  356   uint8* buffer = current_buffer->pixels(); |  | 
|  357   int stride = current_buffer->bytes_per_row(); |  | 
|  358   if (flip) { |  | 
|  359     stride = -stride; |  | 
|  360     buffer += (current_buffer->dimensions().height() - 1) * |  | 
|  361         current_buffer->bytes_per_row(); |  | 
|  362   } |  | 
|  363  |  | 
|  364   data = new CaptureData(buffer, stride, current_buffer->dimensions()); |  | 
|  365   data->set_dpi(static_cast<VideoFrameMac*>(current_buffer)->dpi()); |  | 
|  366   data->mutable_dirty_region() = region; |  | 
|  367  |  | 
|  368   helper_.set_size_most_recent(data->size()); |  | 
|  369  |  | 
|  370   // Signal that we are done capturing data from the display framebuffer, |  | 
|  371   // and accessing display structures. |  | 
|  372   display_configuration_capture_event_.Signal(); |  | 
|  373  |  | 
|  374   // Capture the current cursor shape and notify |delegate_| if it has changed. |  | 
|  375   CaptureCursor(); |  | 
|  376  |  | 
|  377   // Move the capture frame buffer queue on to the next buffer. |  | 
|  378   queue_.DoneWithCurrentFrame(); |  | 
|  379  |  | 
|  380   data->set_capture_time_ms( |  | 
|  381       (base::Time::Now() - capture_start_time).InMillisecondsRoundedUp()); |  | 
|  382   delegate_->OnCaptureCompleted(data); |  | 
|  383 } |  | 
|  384  |  | 
|  385 void VideoFrameCapturerMac::CaptureCursor() { |  | 
|  386   NSCursor* cursor = [NSCursor currentSystemCursor]; |  | 
|  387   if (cursor == nil) { |  | 
|  388     return; |  | 
|  389   } |  | 
|  390  |  | 
|  391   NSImage* nsimage = [cursor image]; |  | 
|  392   NSPoint hotspot = [cursor hotSpot]; |  | 
|  393   NSSize size = [nsimage size]; |  | 
|  394   CGImageRef image = [nsimage CGImageForProposedRect:NULL |  | 
|  395                                              context:nil |  | 
|  396                                                hints:nil]; |  | 
|  397   if (image == nil) { |  | 
|  398     return; |  | 
|  399   } |  | 
|  400  |  | 
|  401   if (CGImageGetBitsPerPixel(image) != 32 || |  | 
|  402       CGImageGetBytesPerRow(image) != (size.width * 4) || |  | 
|  403       CGImageGetBitsPerComponent(image) != 8) { |  | 
|  404     return; |  | 
|  405   } |  | 
|  406  |  | 
|  407   // Compare the current cursor with the last one we sent to the client |  | 
|  408   // and exit if the cursor is the same. |  | 
|  409   if (current_cursor_.get() != NULL) { |  | 
|  410     CGImageRef current = current_cursor_.get(); |  | 
|  411     if (CGImageGetWidth(image) == CGImageGetWidth(current) && |  | 
|  412         CGImageGetHeight(image) == CGImageGetHeight(current) && |  | 
|  413         CGImageGetBitsPerPixel(image) == CGImageGetBitsPerPixel(current) && |  | 
|  414         CGImageGetBytesPerRow(image) == CGImageGetBytesPerRow(current) && |  | 
|  415         CGImageGetBitsPerComponent(image) == |  | 
|  416             CGImageGetBitsPerComponent(current)) { |  | 
|  417       CGDataProviderRef provider_new = CGImageGetDataProvider(image); |  | 
|  418       base::mac::ScopedCFTypeRef<CFDataRef> data_ref_new( |  | 
|  419           CGDataProviderCopyData(provider_new)); |  | 
|  420       CGDataProviderRef provider_current = CGImageGetDataProvider(current); |  | 
|  421       base::mac::ScopedCFTypeRef<CFDataRef> data_ref_current( |  | 
|  422           CGDataProviderCopyData(provider_current)); |  | 
|  423  |  | 
|  424       if (data_ref_new.get() != NULL && data_ref_current.get() != NULL) { |  | 
|  425         int data_size = CFDataGetLength(data_ref_new); |  | 
|  426         CHECK(data_size == CFDataGetLength(data_ref_current)); |  | 
|  427         const uint8* data_new = CFDataGetBytePtr(data_ref_new); |  | 
|  428         const uint8* data_current = CFDataGetBytePtr(data_ref_current); |  | 
|  429         if (memcmp(data_new, data_current, data_size) == 0) { |  | 
|  430           return; |  | 
|  431         } |  | 
|  432       } |  | 
|  433     } |  | 
|  434   } |  | 
|  435  |  | 
|  436   // Record the last cursor image. |  | 
|  437   current_cursor_.reset(CGImageCreateCopy(image)); |  | 
|  438  |  | 
|  439   VLOG(3) << "Sending cursor: " << size.width << "x" << size.height; |  | 
|  440  |  | 
|  441   CGDataProviderRef provider = CGImageGetDataProvider(image); |  | 
|  442   base::mac::ScopedCFTypeRef<CFDataRef> image_data_ref( |  | 
|  443       CGDataProviderCopyData(provider)); |  | 
|  444   if (image_data_ref.get() == NULL) { |  | 
|  445     return; |  | 
|  446   } |  | 
|  447   const char* cursor_src_data = |  | 
|  448       reinterpret_cast<const char*>(CFDataGetBytePtr(image_data_ref)); |  | 
|  449   int data_size = CFDataGetLength(image_data_ref); |  | 
|  450  |  | 
|  451   // Create a MouseCursorShape that describes the cursor and pass it to |  | 
|  452   // the client. |  | 
|  453   scoped_ptr<MouseCursorShape> cursor_shape(new MouseCursorShape()); |  | 
|  454   cursor_shape->size.set(size.width, size.height); |  | 
|  455   cursor_shape->hotspot.set(hotspot.x, hotspot.y); |  | 
|  456   cursor_shape->data.assign(cursor_src_data, cursor_src_data + data_size); |  | 
|  457  |  | 
|  458   delegate_->OnCursorShapeChanged(cursor_shape.Pass()); |  | 
|  459 } |  | 
|  460  |  | 
|  461 void VideoFrameCapturerMac::GlBlitFast(const VideoFrame& buffer, |  | 
|  462                                        const SkRegion& region) { |  | 
|  463   const int buffer_height = buffer.dimensions().height(); |  | 
|  464   const int buffer_width = buffer.dimensions().width(); |  | 
|  465  |  | 
|  466   // Clip to the size of our current screen. |  | 
|  467   SkIRect clip_rect = SkIRect::MakeWH(buffer_width, buffer_height); |  | 
|  468   if (queue_.previous_frame()) { |  | 
|  469     // We are doing double buffer for the capture data so we just need to copy |  | 
|  470     // the invalid region from the previous capture in the current buffer. |  | 
|  471     // TODO(hclam): We can reduce the amount of copying here by subtracting |  | 
|  472     // |capturer_helper_|s region from |last_invalid_region_|. |  | 
|  473     // http://crbug.com/92354 |  | 
|  474  |  | 
|  475     // Since the image obtained from OpenGL is upside-down, need to do some |  | 
|  476     // magic here to copy the correct rectangle. |  | 
|  477     const int y_offset = (buffer_height - 1) * buffer.bytes_per_row(); |  | 
|  478     for(SkRegion::Iterator i(last_invalid_region_); !i.done(); i.next()) { |  | 
|  479       SkIRect copy_rect = i.rect(); |  | 
|  480       if (copy_rect.intersect(clip_rect)) { |  | 
|  481         CopyRect(queue_.previous_frame()->pixels() + y_offset, |  | 
|  482                  -buffer.bytes_per_row(), |  | 
|  483                  buffer.pixels() + y_offset, |  | 
|  484                  -buffer.bytes_per_row(), |  | 
|  485                  4,  // Bytes for pixel for RGBA. |  | 
|  486                  copy_rect); |  | 
|  487       } |  | 
|  488     } |  | 
|  489   } |  | 
|  490   last_invalid_region_ = region; |  | 
|  491  |  | 
|  492   CGLContextObj CGL_MACRO_CONTEXT = cgl_context_; |  | 
|  493   glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pixel_buffer_object_.get()); |  | 
|  494   glReadPixels(0, 0, buffer_width, buffer_height, GL_BGRA, GL_UNSIGNED_BYTE, 0); |  | 
|  495   GLubyte* ptr = static_cast<GLubyte*>( |  | 
|  496       glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB)); |  | 
|  497   if (ptr == NULL) { |  | 
|  498     // If the buffer can't be mapped, assume that it's no longer valid and |  | 
|  499     // release it. |  | 
|  500     pixel_buffer_object_.Release(); |  | 
|  501   } else { |  | 
|  502     // Copy only from the dirty rects. Since the image obtained from OpenGL is |  | 
|  503     // upside-down we need to do some magic here to copy the correct rectangle. |  | 
|  504     const int y_offset = (buffer_height - 1) * buffer.bytes_per_row(); |  | 
|  505     for(SkRegion::Iterator i(region); !i.done(); i.next()) { |  | 
|  506       SkIRect copy_rect = i.rect(); |  | 
|  507       if (copy_rect.intersect(clip_rect)) { |  | 
|  508         CopyRect(ptr + y_offset, |  | 
|  509            -buffer.bytes_per_row(), |  | 
|  510            buffer.pixels() + y_offset, |  | 
|  511            -buffer.bytes_per_row(), |  | 
|  512            4,  // Bytes for pixel for RGBA. |  | 
|  513            copy_rect); |  | 
|  514       } |  | 
|  515     } |  | 
|  516   } |  | 
|  517   if (!glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB)) { |  | 
|  518     // If glUnmapBuffer returns false, then the contents of the data store are |  | 
|  519     // undefined. This might be because the screen mode has changed, in which |  | 
|  520     // case it will be recreated in ScreenConfigurationChanged, but releasing |  | 
|  521     // the object here is the best option. Capturing will fall back on |  | 
|  522     // GlBlitSlow until such time as the pixel buffer object is recreated. |  | 
|  523     pixel_buffer_object_.Release(); |  | 
|  524   } |  | 
|  525   glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); |  | 
|  526 } |  | 
|  527  |  | 
|  528 void VideoFrameCapturerMac::GlBlitSlow(const VideoFrame& buffer) { |  | 
|  529   CGLContextObj CGL_MACRO_CONTEXT = cgl_context_; |  | 
|  530   glReadBuffer(GL_FRONT); |  | 
|  531   glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); |  | 
|  532   glPixelStorei(GL_PACK_ALIGNMENT, 4);  // Force 4-byte alignment. |  | 
|  533   glPixelStorei(GL_PACK_ROW_LENGTH, 0); |  | 
|  534   glPixelStorei(GL_PACK_SKIP_ROWS, 0); |  | 
|  535   glPixelStorei(GL_PACK_SKIP_PIXELS, 0); |  | 
|  536   // Read a block of pixels from the frame buffer. |  | 
|  537   glReadPixels(0, 0, buffer.dimensions().width(), buffer.dimensions().height(), |  | 
|  538                GL_BGRA, GL_UNSIGNED_BYTE, buffer.pixels()); |  | 
|  539   glPopClientAttrib(); |  | 
|  540 } |  | 
|  541  |  | 
|  542 void VideoFrameCapturerMac::CgBlitPreLion(const VideoFrame& buffer, |  | 
|  543                                           const SkRegion& region) { |  | 
|  544   const int buffer_height = buffer.dimensions().height(); |  | 
|  545  |  | 
|  546   // Copy the entire contents of the previous capture buffer, to capture over. |  | 
|  547   // TODO(wez): Get rid of this as per crbug.com/145064, or implement |  | 
|  548   // crbug.com/92354. |  | 
|  549   if (queue_.previous_frame()) { |  | 
|  550     memcpy(buffer.pixels(), |  | 
|  551            queue_.previous_frame()->pixels(), |  | 
|  552            buffer.bytes_per_row() * buffer_height); |  | 
|  553   } |  | 
|  554  |  | 
|  555   for (unsigned int d = 0; d < display_ids_.size(); ++d) { |  | 
|  556     // Use deprecated APIs to determine the display buffer layout. |  | 
|  557     DCHECK(cg_display_base_address_ && cg_display_bytes_per_row_ && |  | 
|  558         cg_display_bits_per_pixel_); |  | 
|  559     uint8* display_base_address = |  | 
|  560       reinterpret_cast<uint8*>((*cg_display_base_address_)(display_ids_[d])); |  | 
|  561     CHECK(display_base_address); |  | 
|  562     int src_bytes_per_row = (*cg_display_bytes_per_row_)(display_ids_[d]); |  | 
|  563     int src_bytes_per_pixel = |  | 
|  564         (*cg_display_bits_per_pixel_)(display_ids_[d]) / 8; |  | 
|  565  |  | 
|  566     // Determine the position of the display in the buffer. |  | 
|  567     SkIRect display_bounds = CGRectToSkIRect(CGDisplayBounds(display_ids_[d])); |  | 
|  568     display_bounds.offset(-desktop_bounds_.left(), -desktop_bounds_.top()); |  | 
|  569  |  | 
|  570     // Determine which parts of the blit region, if any, lay within the monitor. |  | 
|  571     SkRegion copy_region; |  | 
|  572     if (!copy_region.op(region, display_bounds, SkRegion::kIntersect_Op)) |  | 
|  573       continue; |  | 
|  574  |  | 
|  575     // Translate the region to be copied into display-relative coordinates. |  | 
|  576     copy_region.translate(-display_bounds.left(), -display_bounds.top()); |  | 
|  577  |  | 
|  578     // Calculate where in the output buffer the display's origin is. |  | 
|  579     uint8* out_ptr = buffer.pixels() + |  | 
|  580          (display_bounds.left() * src_bytes_per_pixel) + |  | 
|  581          (display_bounds.top() * buffer.bytes_per_row()); |  | 
|  582  |  | 
|  583     // Copy the dirty region from the display buffer into our desktop buffer. |  | 
|  584     for(SkRegion::Iterator i(copy_region); !i.done(); i.next()) { |  | 
|  585       CopyRect(display_base_address, |  | 
|  586                src_bytes_per_row, |  | 
|  587                out_ptr, |  | 
|  588                buffer.bytes_per_row(), |  | 
|  589                src_bytes_per_pixel, |  | 
|  590                i.rect()); |  | 
|  591     } |  | 
|  592   } |  | 
|  593 } |  | 
|  594  |  | 
|  595 void VideoFrameCapturerMac::CgBlitPostLion(const VideoFrame& buffer, |  | 
|  596                                            const SkRegion& region) { |  | 
|  597   const int buffer_height = buffer.dimensions().height(); |  | 
|  598  |  | 
|  599   // Copy the entire contents of the previous capture buffer, to capture over. |  | 
|  600   // TODO(wez): Get rid of this as per crbug.com/145064, or implement |  | 
|  601   // crbug.com/92354. |  | 
|  602   if (queue_.previous_frame()) { |  | 
|  603     memcpy(buffer.pixels(), |  | 
|  604            queue_.previous_frame()->pixels(), |  | 
|  605            buffer.bytes_per_row() * buffer_height); |  | 
|  606   } |  | 
|  607  |  | 
|  608   for (unsigned int d = 0; d < display_ids_.size(); ++d) { |  | 
|  609     // Determine the position of the display in the buffer. |  | 
|  610     SkIRect display_bounds = CGRectToSkIRect(CGDisplayBounds(display_ids_[d])); |  | 
|  611     display_bounds.offset(-desktop_bounds_.left(), -desktop_bounds_.top()); |  | 
|  612  |  | 
|  613     // Determine which parts of the blit region, if any, lay within the monitor. |  | 
|  614     SkRegion copy_region; |  | 
|  615     if (!copy_region.op(region, display_bounds, SkRegion::kIntersect_Op)) |  | 
|  616       continue; |  | 
|  617  |  | 
|  618     // Translate the region to be copied into display-relative coordinates. |  | 
|  619     copy_region.translate(-display_bounds.left(), -display_bounds.top()); |  | 
|  620  |  | 
|  621     // Create an image containing a snapshot of the display. |  | 
|  622     base::mac::ScopedCFTypeRef<CGImageRef> image( |  | 
|  623         CGDisplayCreateImage(display_ids_[d])); |  | 
|  624     if (image.get() == NULL) |  | 
|  625       continue; |  | 
|  626  |  | 
|  627     // Request access to the raw pixel data via the image's DataProvider. |  | 
|  628     CGDataProviderRef provider = CGImageGetDataProvider(image); |  | 
|  629     base::mac::ScopedCFTypeRef<CFDataRef> data( |  | 
|  630         CGDataProviderCopyData(provider)); |  | 
|  631     if (data.get() == NULL) |  | 
|  632       continue; |  | 
|  633  |  | 
|  634     const uint8* display_base_address = CFDataGetBytePtr(data); |  | 
|  635     int src_bytes_per_row = CGImageGetBytesPerRow(image); |  | 
|  636     int src_bytes_per_pixel = CGImageGetBitsPerPixel(image) / 8; |  | 
|  637  |  | 
|  638     // Calculate where in the output buffer the display's origin is. |  | 
|  639     uint8* out_ptr = buffer.pixels() + |  | 
|  640         (display_bounds.left() * src_bytes_per_pixel) + |  | 
|  641         (display_bounds.top() * buffer.bytes_per_row()); |  | 
|  642  |  | 
|  643     // Copy the dirty region from the display buffer into our desktop buffer. |  | 
|  644     for(SkRegion::Iterator i(copy_region); !i.done(); i.next()) { |  | 
|  645       CopyRect(display_base_address, |  | 
|  646                src_bytes_per_row, |  | 
|  647                out_ptr, |  | 
|  648                buffer.bytes_per_row(), |  | 
|  649                src_bytes_per_pixel, |  | 
|  650                i.rect()); |  | 
|  651     } |  | 
|  652   } |  | 
|  653 } |  | 
|  654  |  | 
|  655 void VideoFrameCapturerMac::ScreenConfigurationChanged() { |  | 
|  656   // Release existing buffers, which will be of the wrong size. |  | 
|  657   ReleaseBuffers(); |  | 
|  658  |  | 
|  659   // Clear the dirty region, in case the display is down-sizing. |  | 
|  660   helper_.ClearInvalidRegion(); |  | 
|  661  |  | 
|  662   // Fetch the list if active displays and calculate their bounds. |  | 
|  663   CGDisplayCount display_count; |  | 
|  664   CGError error = CGGetActiveDisplayList(0, NULL, &display_count); |  | 
|  665   CHECK_EQ(error, CGDisplayNoErr); |  | 
|  666  |  | 
|  667   display_ids_.resize(display_count); |  | 
|  668   error = CGGetActiveDisplayList(display_count, &display_ids_[0], |  | 
|  669                                  &display_count); |  | 
|  670   CHECK_EQ(error, CGDisplayNoErr); |  | 
|  671   CHECK_EQ(display_count, display_ids_.size()); |  | 
|  672  |  | 
|  673   desktop_bounds_ = SkIRect::MakeEmpty(); |  | 
|  674   for (unsigned int d = 0; d < display_count; ++d) { |  | 
|  675     CGRect display_bounds = CGDisplayBounds(display_ids_[d]); |  | 
|  676     desktop_bounds_.join(CGRectToSkIRect(display_bounds)); |  | 
|  677   } |  | 
|  678  |  | 
|  679   // Re-mark the entire desktop as dirty. |  | 
|  680   helper_.InvalidateScreen(SkISize::Make(desktop_bounds_.width(), |  | 
|  681                                          desktop_bounds_.height())); |  | 
|  682  |  | 
|  683   // Make sure the frame buffers will be reallocated. |  | 
|  684   queue_.SetAllFramesNeedUpdate(); |  | 
|  685  |  | 
|  686   // CgBlitPostLion uses CGDisplayCreateImage() to snapshot each display's |  | 
|  687   // contents. Although the API exists in OS 10.6, it crashes the caller if |  | 
|  688   // the machine has no monitor connected, so we fall back to depcreated APIs |  | 
|  689   // when running on 10.6. |  | 
|  690   if (base::mac::IsOSLionOrLater()) { |  | 
|  691     LOG(INFO) << "Using CgBlitPostLion."; |  | 
|  692     // No need for any OpenGL support on Lion |  | 
|  693     return; |  | 
|  694   } |  | 
|  695  |  | 
|  696   // Dynamically link to the deprecated pre-Lion capture APIs. |  | 
|  697   std::string app_services_library_error; |  | 
|  698   FilePath app_services_path(kApplicationServicesLibraryName); |  | 
|  699   app_services_library_.Reset( |  | 
|  700       base::LoadNativeLibrary(app_services_path, &app_services_library_error)); |  | 
|  701   CHECK(app_services_library_.is_valid()) << app_services_library_error; |  | 
|  702  |  | 
|  703   std::string opengl_library_error; |  | 
|  704   FilePath opengl_path(kOpenGlLibraryName); |  | 
|  705   opengl_library_.Reset( |  | 
|  706       base::LoadNativeLibrary(opengl_path, &opengl_library_error)); |  | 
|  707   CHECK(opengl_library_.is_valid()) << opengl_library_error; |  | 
|  708  |  | 
|  709   cg_display_base_address_ = reinterpret_cast<CGDisplayBaseAddressFunc>( |  | 
|  710       app_services_library_.GetFunctionPointer("CGDisplayBaseAddress")); |  | 
|  711   cg_display_bytes_per_row_ = reinterpret_cast<CGDisplayBytesPerRowFunc>( |  | 
|  712       app_services_library_.GetFunctionPointer("CGDisplayBytesPerRow")); |  | 
|  713   cg_display_bits_per_pixel_ = reinterpret_cast<CGDisplayBitsPerPixelFunc>( |  | 
|  714       app_services_library_.GetFunctionPointer("CGDisplayBitsPerPixel")); |  | 
|  715   cgl_set_full_screen_ = reinterpret_cast<CGLSetFullScreenFunc>( |  | 
|  716       opengl_library_.GetFunctionPointer("CGLSetFullScreen")); |  | 
|  717   CHECK(cg_display_base_address_ && cg_display_bytes_per_row_ && |  | 
|  718         cg_display_bits_per_pixel_ && cgl_set_full_screen_); |  | 
|  719  |  | 
|  720   if (display_ids_.size() > 1) { |  | 
|  721     LOG(INFO) << "Using CgBlitPreLion (Multi-monitor)."; |  | 
|  722     return; |  | 
|  723   } |  | 
|  724  |  | 
|  725   CGDirectDisplayID mainDevice = CGMainDisplayID(); |  | 
|  726   if (!CGDisplayUsesOpenGLAcceleration(mainDevice)) { |  | 
|  727     LOG(INFO) << "Using CgBlitPreLion (OpenGL unavailable)."; |  | 
|  728     return; |  | 
|  729   } |  | 
|  730  |  | 
|  731   LOG(INFO) << "Using GlBlit"; |  | 
|  732  |  | 
|  733   CGLPixelFormatAttribute attributes[] = { |  | 
|  734     kCGLPFAFullScreen, |  | 
|  735     kCGLPFADisplayMask, |  | 
|  736     (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(mainDevice), |  | 
|  737     (CGLPixelFormatAttribute)0 |  | 
|  738   }; |  | 
|  739   CGLPixelFormatObj pixel_format = NULL; |  | 
|  740   GLint matching_pixel_format_count = 0; |  | 
|  741   CGLError err = CGLChoosePixelFormat(attributes, |  | 
|  742                                       &pixel_format, |  | 
|  743                                       &matching_pixel_format_count); |  | 
|  744   DCHECK_EQ(err, kCGLNoError); |  | 
|  745   err = CGLCreateContext(pixel_format, NULL, &cgl_context_); |  | 
|  746   DCHECK_EQ(err, kCGLNoError); |  | 
|  747   CGLDestroyPixelFormat(pixel_format); |  | 
|  748   (*cgl_set_full_screen_)(cgl_context_); |  | 
|  749   CGLSetCurrentContext(cgl_context_); |  | 
|  750  |  | 
|  751   size_t buffer_size = desktop_bounds_.width() * desktop_bounds_.height() * |  | 
|  752                        sizeof(uint32_t); |  | 
|  753   pixel_buffer_object_.Init(cgl_context_, buffer_size); |  | 
|  754 } |  | 
|  755  |  | 
|  756 void VideoFrameCapturerMac::ScreenRefresh(CGRectCount count, |  | 
|  757                                           const CGRect* rect_array) { |  | 
|  758   if (desktop_bounds_.isEmpty()) { |  | 
|  759     return; |  | 
|  760   } |  | 
|  761   SkIRect skirect_array[count]; |  | 
|  762   for (CGRectCount i = 0; i < count; ++i) { |  | 
|  763     skirect_array[i] = CGRectToSkIRect(rect_array[i]); |  | 
|  764     skirect_array[i].offset(-desktop_bounds_.left(), -desktop_bounds_.top()); |  | 
|  765   } |  | 
|  766   SkRegion region; |  | 
|  767   region.setRects(skirect_array, count); |  | 
|  768   InvalidateRegion(region); |  | 
|  769 } |  | 
|  770  |  | 
|  771 void VideoFrameCapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta, |  | 
|  772                                              size_t count, |  | 
|  773                                              const CGRect* rect_array) { |  | 
|  774   SkIRect skirect_array[count]; |  | 
|  775   for (CGRectCount i = 0; i < count; ++i) { |  | 
|  776     CGRect rect = rect_array[i]; |  | 
|  777     rect = CGRectOffset(rect, delta.dX, delta.dY); |  | 
|  778     skirect_array[i] = CGRectToSkIRect(rect); |  | 
|  779     skirect_array[i].offset(-desktop_bounds_.left(), -desktop_bounds_.top()); |  | 
|  780   } |  | 
|  781   SkRegion region; |  | 
|  782   region.setRects(skirect_array, count); |  | 
|  783   InvalidateRegion(region); |  | 
|  784 } |  | 
|  785  |  | 
|  786 void VideoFrameCapturerMac::DisplaysReconfigured( |  | 
|  787     CGDirectDisplayID display, |  | 
|  788     CGDisplayChangeSummaryFlags flags) { |  | 
|  789   if (flags & kCGDisplayBeginConfigurationFlag) { |  | 
|  790     if (reconfiguring_displays_.empty()) { |  | 
|  791       // If this is the first display to start reconfiguring then wait on |  | 
|  792       // |display_configuration_capture_event_| to block the capture thread |  | 
|  793       // from accessing display memory until the reconfiguration completes. |  | 
|  794       CHECK(display_configuration_capture_event_.TimedWait( |  | 
|  795                 base::TimeDelta::FromSeconds( |  | 
|  796                     kDisplayConfigurationEventTimeoutInSeconds))); |  | 
|  797     } |  | 
|  798  |  | 
|  799     reconfiguring_displays_.insert(display); |  | 
|  800   } else { |  | 
|  801     reconfiguring_displays_.erase(display); |  | 
|  802  |  | 
|  803     if (reconfiguring_displays_.empty()) { |  | 
|  804       // If no other displays are reconfiguring then refresh capturer data |  | 
|  805       // structures and un-block the capturer thread. |  | 
|  806       ScreenConfigurationChanged(); |  | 
|  807       display_configuration_capture_event_.Signal(); |  | 
|  808     } |  | 
|  809   } |  | 
|  810 } |  | 
|  811  |  | 
|  812 void VideoFrameCapturerMac::ScreenRefreshCallback(CGRectCount count, |  | 
|  813                                                   const CGRect* rect_array, |  | 
|  814                                                   void* user_parameter) { |  | 
|  815   VideoFrameCapturerMac* capturer = reinterpret_cast<VideoFrameCapturerMac*>( |  | 
|  816       user_parameter); |  | 
|  817   if (capturer->desktop_bounds_.isEmpty()) { |  | 
|  818     capturer->ScreenConfigurationChanged(); |  | 
|  819   } |  | 
|  820   capturer->ScreenRefresh(count, rect_array); |  | 
|  821 } |  | 
|  822  |  | 
|  823 void VideoFrameCapturerMac::ScreenUpdateMoveCallback( |  | 
|  824     CGScreenUpdateMoveDelta delta, |  | 
|  825     size_t count, |  | 
|  826     const CGRect* rect_array, |  | 
|  827     void* user_parameter) { |  | 
|  828   VideoFrameCapturerMac* capturer = reinterpret_cast<VideoFrameCapturerMac*>( |  | 
|  829       user_parameter); |  | 
|  830   capturer->ScreenUpdateMove(delta, count, rect_array); |  | 
|  831 } |  | 
|  832  |  | 
|  833 void VideoFrameCapturerMac::DisplaysReconfiguredCallback( |  | 
|  834     CGDirectDisplayID display, |  | 
|  835     CGDisplayChangeSummaryFlags flags, |  | 
|  836     void* user_parameter) { |  | 
|  837   VideoFrameCapturerMac* capturer = reinterpret_cast<VideoFrameCapturerMac*>( |  | 
|  838       user_parameter); |  | 
|  839   capturer->DisplaysReconfigured(display, flags); |  | 
|  840 } |  | 
|  841  |  | 
|  842 }  // namespace |  | 
|  843  |  | 
|  844 // static |  | 
|  845 scoped_ptr<VideoFrameCapturer> VideoFrameCapturer::Create() { |  | 
|  846   scoped_ptr<VideoFrameCapturerMac> capturer(new VideoFrameCapturerMac()); |  | 
|  847   if (!capturer->Init()) |  | 
|  848     capturer.reset(); |  | 
|  849   return capturer.PassAs<VideoFrameCapturer>(); |  | 
|  850 } |  | 
|  851  |  | 
|  852 // static |  | 
|  853 scoped_ptr<VideoFrameCapturer> VideoFrameCapturer::CreateWithFactory( |  | 
|  854     SharedBufferFactory* shared_buffer_factory) { |  | 
|  855   NOTIMPLEMENTED(); |  | 
|  856   return scoped_ptr<VideoFrameCapturer>(); |  | 
|  857 } |  | 
|  858  |  | 
|  859 }  // namespace remoting |  | 
| OLD | NEW |