Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(294)

Side by Side Diff: remoting/capturer/video_frame_capturer_mac.mm

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

Powered by Google App Engine
This is Rietveld 408576698