OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "remoting/host/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 <stddef.h> | |
14 | |
15 #include "base/logging.h" | |
16 #include "base/mac/mac_util.h" | |
17 #include "base/mac/scoped_cftyperef.h" | |
18 #include "base/memory/scoped_ptr.h" | |
19 #include "base/synchronization/waitable_event.h" | |
20 #include "base/time.h" | |
21 #include "remoting/base/capture_data.h" | |
22 #include "remoting/base/util.h" | |
23 #include "remoting/host/capturer_helper.h" | |
24 #include "remoting/proto/control.pb.h" | |
25 | |
26 namespace remoting { | |
27 | |
28 namespace { | |
29 | |
30 SkIRect CGRectToSkIRect(const CGRect& rect) { | |
31 SkIRect sk_rect = { | |
32 SkScalarRound(rect.origin.x), | |
33 SkScalarRound(rect.origin.y), | |
34 SkScalarRound(rect.origin.x + rect.size.width), | |
35 SkScalarRound(rect.origin.y + rect.size.height) | |
36 }; | |
37 return sk_rect; | |
38 } | |
39 | |
40 #if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) | |
41 // Possibly remove CapturerMac::CgBlitPreLion as well depending on performance | |
42 // of CapturerMac::CgBlitPostLion on 10.6. | |
43 #error No longer need to import CGDisplayCreateImage. | |
44 #else | |
45 // Declared here because CGDisplayCreateImage does not exist in the 10.5 SDK, | |
46 // which we are currently compiling against, and it is required on 10.7 to do | |
47 // screen capture. | |
48 typedef CGImageRef (*CGDisplayCreateImageFunc)(CGDirectDisplayID displayID); | |
49 #endif | |
50 | |
51 // The amount of time allowed for displays to reconfigure. | |
52 const int64 kDisplayReconfigurationTimeoutInSeconds = 10; | |
53 | |
54 class scoped_pixel_buffer_object { | |
55 public: | |
56 scoped_pixel_buffer_object(); | |
57 ~scoped_pixel_buffer_object(); | |
58 | |
59 bool Init(CGLContextObj cgl_context, int size_in_bytes); | |
60 void Release(); | |
61 | |
62 GLuint get() const { return pixel_buffer_object_; } | |
63 | |
64 private: | |
65 CGLContextObj cgl_context_; | |
66 GLuint pixel_buffer_object_; | |
67 | |
68 DISALLOW_COPY_AND_ASSIGN(scoped_pixel_buffer_object); | |
69 }; | |
70 | |
71 scoped_pixel_buffer_object::scoped_pixel_buffer_object() | |
72 : cgl_context_(NULL), | |
73 pixel_buffer_object_(0) { | |
74 } | |
75 | |
76 scoped_pixel_buffer_object::~scoped_pixel_buffer_object() { | |
77 Release(); | |
78 } | |
79 | |
80 bool scoped_pixel_buffer_object::Init(CGLContextObj cgl_context, | |
81 int size_in_bytes) { | |
82 // The PBO path is only done on 10.6 (SnowLeopard) and above due to | |
83 // a driver issue that was found on 10.5 | |
84 // (specifically on a NVIDIA GeForce 7300 GT). | |
85 // http://crbug.com/87283 | |
86 if (base::mac::IsOSLeopardOrEarlier()) { | |
87 return false; | |
88 } | |
89 cgl_context_ = cgl_context; | |
90 CGLContextObj CGL_MACRO_CONTEXT = cgl_context_; | |
91 glGenBuffersARB(1, &pixel_buffer_object_); | |
92 if (glGetError() == GL_NO_ERROR) { | |
93 glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pixel_buffer_object_); | |
94 glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, size_in_bytes, NULL, | |
95 GL_STREAM_READ_ARB); | |
96 glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); | |
97 if (glGetError() != GL_NO_ERROR) { | |
98 Release(); | |
99 } | |
100 } else { | |
101 cgl_context_ = NULL; | |
102 pixel_buffer_object_ = 0; | |
103 } | |
104 return pixel_buffer_object_ != 0; | |
105 } | |
106 | |
107 void scoped_pixel_buffer_object::Release() { | |
108 if (pixel_buffer_object_) { | |
109 CGLContextObj CGL_MACRO_CONTEXT = cgl_context_; | |
110 glDeleteBuffersARB(1, &pixel_buffer_object_); | |
111 cgl_context_ = NULL; | |
112 pixel_buffer_object_ = 0; | |
113 } | |
114 } | |
115 | |
116 // A class representing a full-frame pixel buffer. | |
117 class VideoFrameBuffer { | |
118 public: | |
119 VideoFrameBuffer() : bytes_per_row_(0), needs_update_(true) { } | |
120 | |
121 // If the buffer is marked as needing to be updated (for example after the | |
122 // screen mode changes) and is the wrong size, then release the old buffer | |
123 // and create a new one. | |
124 void Update() { | |
125 if (needs_update_) { | |
126 needs_update_ = false; | |
127 CGDirectDisplayID mainDevice = CGMainDisplayID(); | |
128 int width = CGDisplayPixelsWide(mainDevice); | |
129 int height = CGDisplayPixelsHigh(mainDevice); | |
130 if (width != size_.width() || height != size_.height()) { | |
131 size_.set(width, height); | |
132 bytes_per_row_ = width * sizeof(uint32_t); | |
133 size_t buffer_size = width * height * sizeof(uint32_t); | |
134 ptr_.reset(new uint8[buffer_size]); | |
135 } | |
136 } | |
137 } | |
138 | |
139 SkISize size() const { return size_; } | |
140 int bytes_per_row() const { return bytes_per_row_; } | |
141 uint8* ptr() const { return ptr_.get(); } | |
142 | |
143 void set_needs_update() { needs_update_ = true; } | |
144 | |
145 private: | |
146 SkISize size_; | |
147 int bytes_per_row_; | |
148 scoped_array<uint8> ptr_; | |
149 bool needs_update_; | |
150 | |
151 DISALLOW_COPY_AND_ASSIGN(VideoFrameBuffer); | |
152 }; | |
153 | |
154 // A class to perform capturing for mac. | |
155 class CapturerMac : public Capturer { | |
156 public: | |
157 CapturerMac(); | |
158 virtual ~CapturerMac(); | |
159 | |
160 bool Init(); | |
161 | |
162 // Capturer interface. | |
163 virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE; | |
164 virtual void Stop() OVERRIDE; | |
165 virtual void ScreenConfigurationChanged() OVERRIDE; | |
166 virtual media::VideoFrame::Format pixel_format() const OVERRIDE; | |
167 virtual void ClearInvalidRegion() OVERRIDE; | |
168 virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE; | |
169 virtual void InvalidateScreen(const SkISize& size) OVERRIDE; | |
170 virtual void InvalidateFullScreen() OVERRIDE; | |
171 virtual void CaptureInvalidRegion( | |
172 const CaptureCompletedCallback& callback) OVERRIDE; | |
173 virtual const SkISize& size_most_recent() const OVERRIDE; | |
174 | |
175 private: | |
176 void CaptureCursor(); | |
177 | |
178 void GlBlitFast(const VideoFrameBuffer& buffer, const SkRegion& region); | |
179 void GlBlitSlow(const VideoFrameBuffer& buffer); | |
180 void CgBlitPreLion(const VideoFrameBuffer& buffer, const SkRegion& region); | |
181 void CgBlitPostLion(const VideoFrameBuffer& buffer, const SkRegion& region); | |
182 void CaptureRegion(const SkRegion& region, | |
183 const CaptureCompletedCallback& callback); | |
184 | |
185 void ScreenRefresh(CGRectCount count, const CGRect *rect_array); | |
186 void ScreenUpdateMove(CGScreenUpdateMoveDelta delta, | |
187 size_t count, | |
188 const CGRect *rect_array); | |
189 void DisplaysReconfigured(CGDirectDisplayID display, | |
190 CGDisplayChangeSummaryFlags flags); | |
191 static void ScreenRefreshCallback(CGRectCount count, | |
192 const CGRect *rect_array, | |
193 void *user_parameter); | |
194 static void ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta, | |
195 size_t count, | |
196 const CGRect *rect_array, | |
197 void *user_parameter); | |
198 static void DisplaysReconfiguredCallback(CGDirectDisplayID display, | |
199 CGDisplayChangeSummaryFlags flags, | |
200 void *user_parameter); | |
201 | |
202 void ReleaseBuffers(); | |
203 | |
204 CGLContextObj cgl_context_; | |
205 static const int kNumBuffers = 2; | |
206 scoped_pixel_buffer_object pixel_buffer_object_; | |
207 VideoFrameBuffer buffers_[kNumBuffers]; | |
208 | |
209 // A thread-safe list of invalid rectangles, and the size of the most | |
210 // recently captured screen. | |
211 CapturerHelper helper_; | |
212 | |
213 // Callback notified whenever the cursor shape is changed. | |
214 CursorShapeChangedCallback cursor_shape_changed_callback_; | |
215 | |
216 // Image of the last cursor that we sent to the client. | |
217 base::mac::ScopedCFTypeRef<CGImageRef> current_cursor_; | |
218 | |
219 // The current buffer with valid data for reading. | |
220 int current_buffer_; | |
221 | |
222 // The previous buffer into which we captured, or NULL for the first capture | |
223 // for a particular screen resolution. | |
224 uint8* last_buffer_; | |
225 | |
226 // Contains an invalid region from the previous capture. | |
227 SkRegion last_invalid_region_; | |
228 | |
229 // Format of pixels returned in buffer. | |
230 media::VideoFrame::Format pixel_format_; | |
231 | |
232 // Acts as a critical section around our display configuration data | |
233 // structures. Specifically cgl_context_ and pixel_buffer_object_. | |
234 base::WaitableEvent display_configuration_capture_event_; | |
235 | |
236 // Will be non-null on lion. | |
237 CGDisplayCreateImageFunc display_create_image_func_; | |
238 | |
239 // Power management assertion to prevent the screen from sleeping. | |
240 IOPMAssertionID power_assertion_id_display_; | |
241 | |
242 // Power management assertion to indicate that the user is active. | |
243 IOPMAssertionID power_assertion_id_user_; | |
244 | |
245 DISALLOW_COPY_AND_ASSIGN(CapturerMac); | |
246 }; | |
247 | |
248 CapturerMac::CapturerMac() | |
249 : cgl_context_(NULL), | |
250 current_buffer_(0), | |
251 last_buffer_(NULL), | |
252 pixel_format_(media::VideoFrame::RGB32), | |
253 display_configuration_capture_event_(false, true), | |
254 display_create_image_func_(NULL), | |
255 power_assertion_id_display_(kIOPMNullAssertionID), | |
256 power_assertion_id_user_(kIOPMNullAssertionID) { | |
257 } | |
258 | |
259 CapturerMac::~CapturerMac() { | |
260 ReleaseBuffers(); | |
261 CGUnregisterScreenRefreshCallback(CapturerMac::ScreenRefreshCallback, this); | |
262 CGScreenUnregisterMoveCallback(CapturerMac::ScreenUpdateMoveCallback, this); | |
263 CGError err = CGDisplayRemoveReconfigurationCallback( | |
264 CapturerMac::DisplaysReconfiguredCallback, this); | |
265 if (err != kCGErrorSuccess) { | |
266 LOG(ERROR) << "CGDisplayRemoveReconfigurationCallback " << err; | |
267 } | |
268 } | |
269 | |
270 bool CapturerMac::Init() { | |
271 CGError err = | |
272 CGRegisterScreenRefreshCallback(CapturerMac::ScreenRefreshCallback, | |
273 this); | |
274 if (err != kCGErrorSuccess) { | |
275 LOG(ERROR) << "CGRegisterScreenRefreshCallback " << err; | |
276 return false; | |
277 } | |
278 | |
279 err = CGScreenRegisterMoveCallback(CapturerMac::ScreenUpdateMoveCallback, | |
280 this); | |
281 if (err != kCGErrorSuccess) { | |
282 LOG(ERROR) << "CGScreenRegisterMoveCallback " << err; | |
283 return false; | |
284 } | |
285 err = CGDisplayRegisterReconfigurationCallback( | |
286 CapturerMac::DisplaysReconfiguredCallback, this); | |
287 if (err != kCGErrorSuccess) { | |
288 LOG(ERROR) << "CGDisplayRegisterReconfigurationCallback " << err; | |
289 return false; | |
290 } | |
291 | |
292 if (base::mac::IsOSLionOrLater()) { | |
293 display_create_image_func_ = | |
294 reinterpret_cast<CGDisplayCreateImageFunc>( | |
295 dlsym(RTLD_NEXT, "CGDisplayCreateImage")); | |
296 if (!display_create_image_func_) { | |
297 LOG(ERROR) << "Unable to load CGDisplayCreateImage on Lion"; | |
298 return false; | |
299 } | |
300 } | |
301 ScreenConfigurationChanged(); | |
302 return true; | |
303 } | |
304 | |
305 void CapturerMac::ReleaseBuffers() { | |
306 if (cgl_context_) { | |
307 pixel_buffer_object_.Release(); | |
308 CGLDestroyContext(cgl_context_); | |
309 cgl_context_ = NULL; | |
310 } | |
311 // The buffers might be in use by the encoder, so don't delete them here. | |
312 // Instead, mark them as "needs update"; next time the buffers are used by | |
313 // the capturer, they will be recreated if necessary. | |
314 for (int i = 0; i < kNumBuffers; ++i) { | |
315 buffers_[i].set_needs_update(); | |
316 } | |
317 } | |
318 | |
319 void CapturerMac::Start( | |
320 const CursorShapeChangedCallback& callback) { | |
321 cursor_shape_changed_callback_ = callback; | |
322 | |
323 // Create power management assertions to wake the display and prevent it from | |
324 // going to sleep on user idle. | |
325 IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, | |
326 kIOPMAssertionLevelOn, | |
327 &power_assertion_id_display_); | |
328 IOPMAssertionCreate(CFSTR("UserIsActive"), | |
329 kIOPMAssertionLevelOn, | |
330 &power_assertion_id_user_); | |
331 } | |
332 | |
333 void CapturerMac::Stop() { | |
334 if (power_assertion_id_display_ != kIOPMNullAssertionID) { | |
335 IOPMAssertionRelease(power_assertion_id_display_); | |
336 power_assertion_id_display_ = kIOPMNullAssertionID; | |
337 } | |
338 if (power_assertion_id_user_ != kIOPMNullAssertionID) { | |
339 IOPMAssertionRelease(power_assertion_id_user_); | |
340 power_assertion_id_user_ = kIOPMNullAssertionID; | |
341 } | |
342 } | |
343 | |
344 void CapturerMac::ScreenConfigurationChanged() { | |
345 ReleaseBuffers(); | |
346 helper_.ClearInvalidRegion(); | |
347 last_buffer_ = NULL; | |
348 | |
349 CGDirectDisplayID mainDevice = CGMainDisplayID(); | |
350 int width = CGDisplayPixelsWide(mainDevice); | |
351 int height = CGDisplayPixelsHigh(mainDevice); | |
352 InvalidateScreen(SkISize::Make(width, height)); | |
353 | |
354 if (!CGDisplayUsesOpenGLAcceleration(mainDevice)) { | |
355 VLOG(3) << "OpenGL support not available."; | |
356 return; | |
357 } | |
358 | |
359 if (display_create_image_func_ != NULL) { | |
360 // No need for any OpenGL support on Lion | |
361 return; | |
362 } | |
363 | |
364 CGLPixelFormatAttribute attributes[] = { | |
365 kCGLPFAFullScreen, | |
366 kCGLPFADisplayMask, | |
367 (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(mainDevice), | |
368 (CGLPixelFormatAttribute)0 | |
369 }; | |
370 CGLPixelFormatObj pixel_format = NULL; | |
371 GLint matching_pixel_format_count = 0; | |
372 CGLError err = CGLChoosePixelFormat(attributes, | |
373 &pixel_format, | |
374 &matching_pixel_format_count); | |
375 DCHECK_EQ(err, kCGLNoError); | |
376 err = CGLCreateContext(pixel_format, NULL, &cgl_context_); | |
377 DCHECK_EQ(err, kCGLNoError); | |
378 CGLDestroyPixelFormat(pixel_format); | |
379 CGLSetFullScreen(cgl_context_); | |
380 CGLSetCurrentContext(cgl_context_); | |
381 | |
382 size_t buffer_size = width * height * sizeof(uint32_t); | |
383 pixel_buffer_object_.Init(cgl_context_, buffer_size); | |
384 } | |
385 | |
386 media::VideoFrame::Format CapturerMac::pixel_format() const { | |
387 return pixel_format_; | |
388 } | |
389 | |
390 void CapturerMac::ClearInvalidRegion() { | |
391 helper_.ClearInvalidRegion(); | |
392 } | |
393 | |
394 void CapturerMac::InvalidateRegion(const SkRegion& invalid_region) { | |
395 helper_.InvalidateRegion(invalid_region); | |
396 } | |
397 | |
398 void CapturerMac::InvalidateScreen(const SkISize& size) { | |
399 helper_.InvalidateScreen(size); | |
400 } | |
401 | |
402 void CapturerMac::InvalidateFullScreen() { | |
403 helper_.InvalidateFullScreen(); | |
404 } | |
405 | |
406 void CapturerMac::CaptureInvalidRegion( | |
407 const CaptureCompletedCallback& callback) { | |
408 // Only allow captures when the display configuration is not occurring. | |
409 scoped_refptr<CaptureData> data; | |
410 | |
411 // Critical section shared with DisplaysReconfigured(...). | |
412 CHECK(display_configuration_capture_event_.TimedWait( | |
413 base::TimeDelta::FromSeconds(kDisplayReconfigurationTimeoutInSeconds))); | |
414 SkRegion region; | |
415 helper_.SwapInvalidRegion(®ion); | |
416 VideoFrameBuffer& current_buffer = buffers_[current_buffer_]; | |
417 current_buffer.Update(); | |
418 | |
419 bool flip = false; // GL capturers need flipping. | |
420 if (display_create_image_func_ != NULL) { | |
421 // Lion requires us to use their new APIs for doing screen capture. | |
422 CgBlitPostLion(current_buffer, region); | |
423 } else if (cgl_context_) { | |
424 flip = true; | |
425 if (pixel_buffer_object_.get() != 0) { | |
426 GlBlitFast(current_buffer, region); | |
427 } else { | |
428 // See comment in scoped_pixel_buffer_object::Init about why the slow | |
429 // path is always used on 10.5. | |
430 GlBlitSlow(current_buffer); | |
431 } | |
432 } else { | |
433 CgBlitPreLion(current_buffer, region); | |
434 } | |
435 | |
436 DataPlanes planes; | |
437 planes.data[0] = current_buffer.ptr(); | |
438 planes.strides[0] = current_buffer.bytes_per_row(); | |
439 if (flip) { | |
440 planes.strides[0] = -planes.strides[0]; | |
441 planes.data[0] += | |
442 (current_buffer.size().height() - 1) * current_buffer.bytes_per_row(); | |
443 } | |
444 | |
445 data = new CaptureData(planes, current_buffer.size(), pixel_format()); | |
446 data->mutable_dirty_region() = region; | |
447 | |
448 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; | |
449 helper_.set_size_most_recent(data->size()); | |
450 display_configuration_capture_event_.Signal(); | |
451 | |
452 CaptureCursor(); | |
453 | |
454 callback.Run(data); | |
455 } | |
456 | |
457 void CapturerMac::CaptureCursor() { | |
458 if (cursor_shape_changed_callback_.is_null()) { | |
459 return; | |
460 } | |
461 | |
462 NSCursor* cursor = [NSCursor currentSystemCursor]; | |
463 if (cursor == nil) { | |
464 return; | |
465 } | |
466 | |
467 NSImage* nsimage = [cursor image]; | |
468 NSPoint hotspot = [cursor hotSpot]; | |
469 NSSize size = [nsimage size]; | |
470 CGImageRef image = [nsimage CGImageForProposedRect:NULL | |
471 context:nil | |
472 hints:nil]; | |
473 if (image == nil) { | |
474 return; | |
475 } | |
476 | |
477 if (CGImageGetBitsPerPixel(image) != 32 || | |
478 CGImageGetBytesPerRow(image) != (size.width * 4) || | |
479 CGImageGetBitsPerComponent(image) != 8) { | |
480 return; | |
481 } | |
482 | |
483 // Compare the current cursor with the last one we sent to the client | |
484 // and exit if the cursor is the same. | |
485 if (current_cursor_.get() != NULL) { | |
486 CGImageRef current = current_cursor_.get(); | |
487 if (CGImageGetWidth(image) == CGImageGetWidth(current) && | |
488 CGImageGetHeight(image) == CGImageGetHeight(current) && | |
489 CGImageGetBitsPerPixel(image) == CGImageGetBitsPerPixel(current) && | |
490 CGImageGetBytesPerRow(image) == CGImageGetBytesPerRow(current) && | |
491 CGImageGetBitsPerComponent(image) == | |
492 CGImageGetBitsPerComponent(current)) { | |
493 CGDataProviderRef provider_new = CGImageGetDataProvider(image); | |
494 base::mac::ScopedCFTypeRef<CFDataRef> data_ref_new( | |
495 CGDataProviderCopyData(provider_new)); | |
496 CGDataProviderRef provider_current = CGImageGetDataProvider(current); | |
497 base::mac::ScopedCFTypeRef<CFDataRef> data_ref_current( | |
498 CGDataProviderCopyData(provider_current)); | |
499 | |
500 if (data_ref_new.get() != NULL && data_ref_current.get() != NULL) { | |
501 int data_size = CFDataGetLength(data_ref_new); | |
502 CHECK(data_size == CFDataGetLength(data_ref_current)); | |
503 const uint8* data_new = CFDataGetBytePtr(data_ref_new); | |
504 const uint8* data_current = CFDataGetBytePtr(data_ref_current); | |
505 if (memcmp(data_new, data_current, data_size) == 0) { | |
506 return; | |
507 } | |
508 } | |
509 } | |
510 } | |
511 | |
512 VLOG(3) << "Sending cursor: " << size.width << "x" << size.height; | |
513 | |
514 CGDataProviderRef provider = CGImageGetDataProvider(image); | |
515 base::mac::ScopedCFTypeRef<CFDataRef> image_data_ref( | |
516 CGDataProviderCopyData(provider)); | |
517 if (image_data_ref.get() == NULL) { | |
518 return; | |
519 } | |
520 const uint8* cursor_src_data = CFDataGetBytePtr(image_data_ref); | |
521 int data_size = CFDataGetLength(image_data_ref); | |
522 | |
523 // Create a CursorShapeInfo proto that describes the cursor and pass it to | |
524 // the client. | |
525 scoped_ptr<protocol::CursorShapeInfo> cursor_proto( | |
526 new protocol::CursorShapeInfo()); | |
527 cursor_proto->mutable_data()->resize(data_size); | |
528 uint8* cursor_tgt_data = const_cast<uint8*>(reinterpret_cast<const uint8*>( | |
529 cursor_proto->mutable_data()->data())); | |
530 | |
531 memcpy(cursor_tgt_data, cursor_src_data, data_size); | |
532 | |
533 cursor_proto->set_width(size.width); | |
534 cursor_proto->set_height(size.height); | |
535 cursor_proto->set_hotspot_x(hotspot.x); | |
536 cursor_proto->set_hotspot_y(hotspot.y); | |
537 cursor_shape_changed_callback_.Run(cursor_proto.Pass()); | |
538 | |
539 // Record the last cursor image that we sent. | |
540 current_cursor_.reset(CGImageCreateCopy(image)); | |
541 } | |
542 | |
543 void CapturerMac::GlBlitFast(const VideoFrameBuffer& buffer, | |
544 const SkRegion& region) { | |
545 const int buffer_height = buffer.size().height(); | |
546 const int buffer_width = buffer.size().width(); | |
547 | |
548 // Clip to the size of our current screen. | |
549 SkIRect clip_rect = SkIRect::MakeWH(buffer_width, buffer_height); | |
550 if (last_buffer_) { | |
551 // We are doing double buffer for the capture data so we just need to copy | |
552 // the invalid region from the previous capture in the current buffer. | |
553 // TODO(hclam): We can reduce the amount of copying here by subtracting | |
554 // |capturer_helper_|s region from |last_invalid_region_|. | |
555 // http://crbug.com/92354 | |
556 | |
557 // Since the image obtained from OpenGL is upside-down, need to do some | |
558 // magic here to copy the correct rectangle. | |
559 const int y_offset = (buffer_height - 1) * buffer.bytes_per_row(); | |
560 for(SkRegion::Iterator i(last_invalid_region_); !i.done(); i.next()) { | |
561 SkIRect copy_rect = i.rect(); | |
562 if (copy_rect.intersect(clip_rect)) { | |
563 CopyRect(last_buffer_ + y_offset, | |
564 -buffer.bytes_per_row(), | |
565 buffer.ptr() + y_offset, | |
566 -buffer.bytes_per_row(), | |
567 4, // Bytes for pixel for RGBA. | |
568 copy_rect); | |
569 } | |
570 } | |
571 } | |
572 last_buffer_ = buffer.ptr(); | |
573 last_invalid_region_ = region; | |
574 | |
575 CGLContextObj CGL_MACRO_CONTEXT = cgl_context_; | |
576 glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pixel_buffer_object_.get()); | |
577 glReadPixels(0, 0, buffer_width, buffer_height, GL_BGRA, GL_UNSIGNED_BYTE, 0); | |
578 GLubyte* ptr = static_cast<GLubyte*>( | |
579 glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB)); | |
580 if (ptr == NULL) { | |
581 // If the buffer can't be mapped, assume that it's no longer valid and | |
582 // release it. | |
583 pixel_buffer_object_.Release(); | |
584 } else { | |
585 // Copy only from the dirty rects. Since the image obtained from OpenGL is | |
586 // upside-down we need to do some magic here to copy the correct rectangle. | |
587 const int y_offset = (buffer_height - 1) * buffer.bytes_per_row(); | |
588 for(SkRegion::Iterator i(region); !i.done(); i.next()) { | |
589 SkIRect copy_rect = i.rect(); | |
590 if (copy_rect.intersect(clip_rect)) { | |
591 CopyRect(ptr + y_offset, | |
592 -buffer.bytes_per_row(), | |
593 buffer.ptr() + y_offset, | |
594 -buffer.bytes_per_row(), | |
595 4, // Bytes for pixel for RGBA. | |
596 copy_rect); | |
597 } | |
598 } | |
599 } | |
600 if (!glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB)) { | |
601 // If glUnmapBuffer returns false, then the contents of the data store are | |
602 // undefined. This might be because the screen mode has changed, in which | |
603 // case it will be recreated in ScreenConfigurationChanged, but releasing | |
604 // the object here is the best option. Capturing will fall back on | |
605 // GlBlitSlow until such time as the pixel buffer object is recreated. | |
606 pixel_buffer_object_.Release(); | |
607 } | |
608 glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); | |
609 } | |
610 | |
611 void CapturerMac::GlBlitSlow(const VideoFrameBuffer& buffer) { | |
612 CGLContextObj CGL_MACRO_CONTEXT = cgl_context_; | |
613 glReadBuffer(GL_FRONT); | |
614 glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); | |
615 glPixelStorei(GL_PACK_ALIGNMENT, 4); // Force 4-byte alignment. | |
616 glPixelStorei(GL_PACK_ROW_LENGTH, 0); | |
617 glPixelStorei(GL_PACK_SKIP_ROWS, 0); | |
618 glPixelStorei(GL_PACK_SKIP_PIXELS, 0); | |
619 // Read a block of pixels from the frame buffer. | |
620 glReadPixels(0, 0, buffer.size().width(), buffer.size().height(), | |
621 GL_BGRA, GL_UNSIGNED_BYTE, buffer.ptr()); | |
622 glPopClientAttrib(); | |
623 } | |
624 | |
625 void CapturerMac::CgBlitPreLion(const VideoFrameBuffer& buffer, | |
626 const SkRegion& region) { | |
627 const int buffer_height = buffer.size().height(); | |
628 const int buffer_width = buffer.size().width(); | |
629 | |
630 // Clip to the size of our current screen. | |
631 SkIRect clip_rect = SkIRect::MakeWH(buffer_width, buffer_height); | |
632 | |
633 if (last_buffer_) | |
634 memcpy(buffer.ptr(), last_buffer_, buffer.bytes_per_row() * buffer_height); | |
635 last_buffer_ = buffer.ptr(); | |
636 CGDirectDisplayID main_display = CGMainDisplayID(); | |
637 uint8* display_base_address = | |
638 reinterpret_cast<uint8*>(CGDisplayBaseAddress(main_display)); | |
639 int src_bytes_per_row = CGDisplayBytesPerRow(main_display); | |
640 int src_bytes_per_pixel = CGDisplayBitsPerPixel(main_display) / 8; | |
641 // TODO(hclam): We can reduce the amount of copying here by subtracting | |
642 // |capturer_helper_|s region from |last_invalid_region_|. | |
643 // http://crbug.com/92354 | |
644 for(SkRegion::Iterator i(region); !i.done(); i.next()) { | |
645 SkIRect copy_rect = i.rect(); | |
646 if (copy_rect.intersect(clip_rect)) { | |
647 CopyRect(display_base_address, | |
648 src_bytes_per_row, | |
649 buffer.ptr(), | |
650 buffer.bytes_per_row(), | |
651 src_bytes_per_pixel, | |
652 copy_rect); | |
653 } | |
654 } | |
655 } | |
656 | |
657 void CapturerMac::CgBlitPostLion(const VideoFrameBuffer& buffer, | |
658 const SkRegion& region) { | |
659 const int buffer_height = buffer.size().height(); | |
660 const int buffer_width = buffer.size().width(); | |
661 | |
662 // Clip to the size of our current screen. | |
663 SkIRect clip_rect = SkIRect::MakeWH(buffer_width, buffer_height); | |
664 | |
665 if (last_buffer_) | |
666 memcpy(buffer.ptr(), last_buffer_, buffer.bytes_per_row() * buffer_height); | |
667 last_buffer_ = buffer.ptr(); | |
668 CGDirectDisplayID display = CGMainDisplayID(); | |
669 base::mac::ScopedCFTypeRef<CGImageRef> image( | |
670 display_create_image_func_(display)); | |
671 if (image.get() == NULL) | |
672 return; | |
673 CGDataProviderRef provider = CGImageGetDataProvider(image); | |
674 base::mac::ScopedCFTypeRef<CFDataRef> data(CGDataProviderCopyData(provider)); | |
675 if (data.get() == NULL) | |
676 return; | |
677 const uint8* display_base_address = CFDataGetBytePtr(data); | |
678 int src_bytes_per_row = CGImageGetBytesPerRow(image); | |
679 int src_bytes_per_pixel = CGImageGetBitsPerPixel(image) / 8; | |
680 // TODO(hclam): We can reduce the amount of copying here by subtracting | |
681 // |capturer_helper_|s region from |last_invalid_region_|. | |
682 // http://crbug.com/92354 | |
683 for(SkRegion::Iterator i(region); !i.done(); i.next()) { | |
684 SkIRect copy_rect = i.rect(); | |
685 if (copy_rect.intersect(clip_rect)) { | |
686 CopyRect(display_base_address, | |
687 src_bytes_per_row, | |
688 buffer.ptr(), | |
689 buffer.bytes_per_row(), | |
690 src_bytes_per_pixel, | |
691 copy_rect); | |
692 } | |
693 } | |
694 } | |
695 | |
696 const SkISize& CapturerMac::size_most_recent() const { | |
697 return helper_.size_most_recent(); | |
698 } | |
699 | |
700 void CapturerMac::ScreenRefresh(CGRectCount count, const CGRect *rect_array) { | |
701 SkIRect skirect_array[count]; | |
702 for (CGRectCount i = 0; i < count; ++i) { | |
703 skirect_array[i] = CGRectToSkIRect(rect_array[i]); | |
704 } | |
705 SkRegion region; | |
706 region.setRects(skirect_array, count); | |
707 InvalidateRegion(region); | |
708 } | |
709 | |
710 void CapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta, | |
711 size_t count, | |
712 const CGRect *rect_array) { | |
713 SkIRect skirect_new_array[count]; | |
714 for (CGRectCount i = 0; i < count; ++i) { | |
715 CGRect rect = rect_array[i]; | |
716 rect = CGRectOffset(rect, delta.dX, delta.dY); | |
717 skirect_new_array[i] = CGRectToSkIRect(rect); | |
718 } | |
719 SkRegion region; | |
720 region.setRects(skirect_new_array, count); | |
721 InvalidateRegion(region); | |
722 } | |
723 | |
724 void CapturerMac::DisplaysReconfigured(CGDirectDisplayID display, | |
725 CGDisplayChangeSummaryFlags flags) { | |
726 if (display == CGMainDisplayID()) { | |
727 if (flags & kCGDisplayBeginConfigurationFlag) { | |
728 // Wait on |display_configuration_capture_event_| to prevent more | |
729 // captures from occurring on a different thread while the displays | |
730 // are being reconfigured. | |
731 CHECK(display_configuration_capture_event_.TimedWait( | |
732 base::TimeDelta::FromSeconds( | |
733 kDisplayReconfigurationTimeoutInSeconds))); | |
734 } else { | |
735 ScreenConfigurationChanged(); | |
736 // Now that the configuration has changed, signal the event. | |
737 display_configuration_capture_event_.Signal(); | |
738 } | |
739 } | |
740 } | |
741 | |
742 void CapturerMac::ScreenRefreshCallback(CGRectCount count, | |
743 const CGRect *rect_array, | |
744 void *user_parameter) { | |
745 CapturerMac *capturer = reinterpret_cast<CapturerMac *>(user_parameter); | |
746 capturer->ScreenRefresh(count, rect_array); | |
747 } | |
748 | |
749 void CapturerMac::ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta, | |
750 size_t count, | |
751 const CGRect *rect_array, | |
752 void *user_parameter) { | |
753 CapturerMac *capturer = reinterpret_cast<CapturerMac *>(user_parameter); | |
754 capturer->ScreenUpdateMove(delta, count, rect_array); | |
755 } | |
756 | |
757 void CapturerMac::DisplaysReconfiguredCallback( | |
758 CGDirectDisplayID display, | |
759 CGDisplayChangeSummaryFlags flags, | |
760 void *user_parameter) { | |
761 CapturerMac *capturer = reinterpret_cast<CapturerMac *>(user_parameter); | |
762 capturer->DisplaysReconfigured(display, flags); | |
763 } | |
764 | |
765 } // namespace | |
766 | |
767 // static | |
768 Capturer* Capturer::Create() { | |
769 CapturerMac* capturer = new CapturerMac(); | |
770 if (!capturer->Init()) { | |
771 delete capturer; | |
772 capturer = NULL; | |
773 } | |
774 return capturer; | |
775 } | |
776 | |
777 } // namespace remoting | |
OLD | NEW |