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

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, 11 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/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(&region);
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698