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

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

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

Powered by Google App Engine
This is Rietveld 408576698