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

Side by Side Diff: media/video/capture/screen/screen_capturer_mac.mm

Issue 15692018: Remove screen capturers from media/video/capture/screen. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 6 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 "media/video/capture/screen/screen_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 <sys/utsname.h>
14 #include <stddef.h>
15 #include <set>
16
17 #include "base/logging.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/synchronization/waitable_event.h"
20 #include "media/video/capture/screen/mac/desktop_configuration.h"
21 #include "media/video/capture/screen/mac/scoped_pixel_buffer_object.h"
22 #include "media/video/capture/screen/mouse_cursor_shape.h"
23 #include "media/video/capture/screen/screen_capture_frame_queue.h"
24 #include "media/video/capture/screen/screen_capturer_helper.h"
25 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
26 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
27 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
28
29 namespace media {
30
31 namespace {
32
33 // Definitions used to dynamic-link to deprecated OS 10.6 functions.
34 const char* kApplicationServicesLibraryName =
35 "/System/Library/Frameworks/ApplicationServices.framework/"
36 "ApplicationServices";
37 typedef void* (*CGDisplayBaseAddressFunc)(CGDirectDisplayID);
38 typedef size_t (*CGDisplayBytesPerRowFunc)(CGDirectDisplayID);
39 typedef size_t (*CGDisplayBitsPerPixelFunc)(CGDirectDisplayID);
40 const char* kOpenGlLibraryName =
41 "/System/Library/Frameworks/OpenGL.framework/OpenGL";
42 typedef CGLError (*CGLSetFullScreenFunc)(CGLContextObj);
43
44 // Standard Mac displays have 72dpi, but we report 96dpi for
45 // consistency with Windows and Linux.
46 const int kStandardDPI = 96;
47
48 // Scales all coordinates of a rect by a specified factor.
49 webrtc::DesktopRect ScaleAndRoundCGRect(const CGRect& rect, float scale) {
50 return webrtc::DesktopRect::MakeLTRB(
51 static_cast<int>(floor(rect.origin.x * scale)),
52 static_cast<int>(floor(rect.origin.y * scale)),
53 static_cast<int>(ceil((rect.origin.x + rect.size.width) * scale)),
54 static_cast<int>(ceil((rect.origin.y + rect.size.height) * scale)));
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 webrtc::DesktopRect& 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 int GetDarwinVersion() {
82 struct utsname uname_info;
83 if (uname(&uname_info) != 0) {
84 LOG(ERROR) << "uname failed";
85 return 0;
86 }
87
88 if (strcmp(uname_info.sysname, "Darwin") != 0)
89 return 0;
90
91 char* dot;
92 int result = strtol(uname_info.release, &dot, 10);
93 if (*dot != '.') {
94 LOG(ERROR) << "Failed to parse version";
95 return 0;
96 }
97
98 return result;
99 }
100
101 bool IsOSLionOrLater() {
102 static int darwin_version = GetDarwinVersion();
103
104 // Verify that the version has been parsed correctly.
105 CHECK(darwin_version >= 6);
106
107 // Darwin major version 11 corresponds to OSX 10.7.
108 return darwin_version >= 11;
109 }
110
111 // The amount of time allowed for displays to reconfigure.
112 const int64 kDisplayConfigurationEventTimeoutInSeconds = 10;
113
114 // A class to perform video frame capturing for mac.
115 class ScreenCapturerMac : public ScreenCapturer {
116 public:
117 ScreenCapturerMac();
118 virtual ~ScreenCapturerMac();
119
120 bool Init();
121
122 // Overridden from ScreenCapturer:
123 virtual void Start(Callback* callback) OVERRIDE;
124 virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE;
125 virtual void SetMouseShapeObserver(
126 MouseShapeObserver* mouse_shape_observer) OVERRIDE;
127
128 private:
129 void CaptureCursor();
130
131 void GlBlitFast(const webrtc::DesktopFrame& frame,
132 const webrtc::DesktopRegion& region);
133 void GlBlitSlow(const webrtc::DesktopFrame& frame);
134 void CgBlitPreLion(const webrtc::DesktopFrame& frame,
135 const webrtc::DesktopRegion& region);
136 void CgBlitPostLion(const webrtc::DesktopFrame& frame,
137 const webrtc::DesktopRegion& region);
138
139 // Called when the screen configuration is changed.
140 void ScreenConfigurationChanged();
141
142 bool RegisterRefreshAndMoveHandlers();
143 void UnregisterRefreshAndMoveHandlers();
144
145 void ScreenRefresh(CGRectCount count, const CGRect *rect_array);
146 void ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
147 size_t count,
148 const CGRect *rect_array);
149 void DisplaysReconfigured(CGDirectDisplayID display,
150 CGDisplayChangeSummaryFlags flags);
151 static void ScreenRefreshCallback(CGRectCount count,
152 const CGRect *rect_array,
153 void *user_parameter);
154 static void ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta,
155 size_t count,
156 const CGRect *rect_array,
157 void *user_parameter);
158 static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
159 CGDisplayChangeSummaryFlags flags,
160 void *user_parameter);
161
162 void ReleaseBuffers();
163
164 Callback* callback_;
165 MouseShapeObserver* mouse_shape_observer_;
166
167 CGLContextObj cgl_context_;
168 ScopedPixelBufferObject pixel_buffer_object_;
169
170 // Queue of the frames buffers.
171 ScreenCaptureFrameQueue queue_;
172
173 // Current display configuration.
174 MacDesktopConfiguration desktop_config_;
175
176 // A thread-safe list of invalid rectangles, and the size of the most
177 // recently captured screen.
178 ScreenCapturerHelper helper_;
179
180 // The last cursor that we sent to the client.
181 MouseCursorShape last_cursor_;
182
183 // Contains an invalid region from the previous capture.
184 webrtc::DesktopRegion last_invalid_region_;
185
186 // Used to ensure that frame captures do not take place while displays
187 // are being reconfigured.
188 base::WaitableEvent display_configuration_capture_event_;
189
190 // Records the Ids of attached displays which are being reconfigured.
191 // Accessed on the thread on which we are notified of display events.
192 std::set<CGDirectDisplayID> reconfiguring_displays_;
193
194 // Power management assertion to prevent the screen from sleeping.
195 IOPMAssertionID power_assertion_id_display_;
196
197 // Power management assertion to indicate that the user is active.
198 IOPMAssertionID power_assertion_id_user_;
199
200 // Dynamically link to deprecated APIs for Mac OS X 10.6 support.
201 void* app_services_library_;
202 CGDisplayBaseAddressFunc cg_display_base_address_;
203 CGDisplayBytesPerRowFunc cg_display_bytes_per_row_;
204 CGDisplayBitsPerPixelFunc cg_display_bits_per_pixel_;
205 void* opengl_library_;
206 CGLSetFullScreenFunc cgl_set_full_screen_;
207
208 DISALLOW_COPY_AND_ASSIGN(ScreenCapturerMac);
209 };
210
211 scoped_ptr<webrtc::DesktopFrame> CreateFrame(
212 const MacDesktopConfiguration& desktop_config) {
213
214 webrtc::DesktopSize size(desktop_config.pixel_bounds.width(),
215 desktop_config.pixel_bounds.height());
216 scoped_ptr<webrtc::DesktopFrame> frame(new webrtc::BasicDesktopFrame(size));
217
218 frame->set_dpi(webrtc::DesktopVector(
219 kStandardDPI * desktop_config.dip_to_pixel_scale,
220 kStandardDPI * desktop_config.dip_to_pixel_scale));
221 return frame.Pass();
222 }
223
224 ScreenCapturerMac::ScreenCapturerMac()
225 : callback_(NULL),
226 mouse_shape_observer_(NULL),
227 cgl_context_(NULL),
228 display_configuration_capture_event_(false, true),
229 power_assertion_id_display_(kIOPMNullAssertionID),
230 power_assertion_id_user_(kIOPMNullAssertionID),
231 app_services_library_(NULL),
232 cg_display_base_address_(NULL),
233 cg_display_bytes_per_row_(NULL),
234 cg_display_bits_per_pixel_(NULL),
235 opengl_library_(NULL),
236 cgl_set_full_screen_(NULL) {
237 }
238
239 ScreenCapturerMac::~ScreenCapturerMac() {
240 if (power_assertion_id_display_ != kIOPMNullAssertionID) {
241 IOPMAssertionRelease(power_assertion_id_display_);
242 power_assertion_id_display_ = kIOPMNullAssertionID;
243 }
244 if (power_assertion_id_user_ != kIOPMNullAssertionID) {
245 IOPMAssertionRelease(power_assertion_id_user_);
246 power_assertion_id_user_ = kIOPMNullAssertionID;
247 }
248
249 ReleaseBuffers();
250 UnregisterRefreshAndMoveHandlers();
251 CGError err = CGDisplayRemoveReconfigurationCallback(
252 ScreenCapturerMac::DisplaysReconfiguredCallback, this);
253 if (err != kCGErrorSuccess)
254 LOG(ERROR) << "CGDisplayRemoveReconfigurationCallback " << err;
255
256 dlclose(app_services_library_);
257 dlclose(opengl_library_);
258 }
259
260 bool ScreenCapturerMac::Init() {
261 if (!RegisterRefreshAndMoveHandlers()) {
262 return false;
263 }
264
265 CGError err = CGDisplayRegisterReconfigurationCallback(
266 ScreenCapturerMac::DisplaysReconfiguredCallback, this);
267 if (err != kCGErrorSuccess) {
268 LOG(ERROR) << "CGDisplayRegisterReconfigurationCallback " << err;
269 return false;
270 }
271
272 ScreenConfigurationChanged();
273 return true;
274 }
275
276 void ScreenCapturerMac::ReleaseBuffers() {
277 if (cgl_context_) {
278 pixel_buffer_object_.Release();
279 CGLDestroyContext(cgl_context_);
280 cgl_context_ = NULL;
281 }
282 // The buffers might be in use by the encoder, so don't delete them here.
283 // Instead, mark them as "needs update"; next time the buffers are used by
284 // the capturer, they will be recreated if necessary.
285 queue_.Reset();
286 }
287
288 void ScreenCapturerMac::Start(Callback* callback) {
289 DCHECK(!callback_);
290 DCHECK(callback);
291
292 callback_ = callback;
293
294 // Create power management assertions to wake the display and prevent it from
295 // going to sleep on user idle.
296 // TODO(jamiewalch): Use IOPMAssertionDeclareUserActivity on 10.7.3 and above
297 // instead of the following two assertions.
298 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
299 kIOPMAssertionLevelOn,
300 CFSTR("Chrome Remote Desktop connection active"),
301 &power_assertion_id_display_);
302 // This assertion ensures that the display is woken up if it already asleep
303 // (as used by Apple Remote Desktop).
304 IOPMAssertionCreateWithName(CFSTR("UserIsActive"),
305 kIOPMAssertionLevelOn,
306 CFSTR("Chrome Remote Desktop connection active"),
307 &power_assertion_id_user_);
308 }
309
310 void ScreenCapturerMac::Capture(
311 const webrtc::DesktopRegion& region_to_capture) {
312 base::Time capture_start_time = base::Time::Now();
313
314 queue_.MoveToNextFrame();
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 webrtc::DesktopRegion region;
325 helper_.TakeInvalidRegion(&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())
331 queue_.ReplaceCurrentFrame(CreateFrame(desktop_config_));
332
333 webrtc::DesktopFrame* current_frame = queue_.current_frame();
334
335 bool flip = false; // GL capturers need flipping.
336 if (IsOSLionOrLater()) {
337 // Lion requires us to use their new APIs for doing screen capture. These
338 // APIS currently crash on 10.6.8 if there is no monitor attached.
339 CgBlitPostLion(*current_frame, region);
340 } else if (cgl_context_) {
341 flip = true;
342 if (pixel_buffer_object_.get() != 0) {
343 GlBlitFast(*current_frame, region);
344 } else {
345 // See comment in ScopedPixelBufferObject::Init about why the slow
346 // path is always used on 10.5.
347 GlBlitSlow(*current_frame);
348 }
349 } else {
350 CgBlitPreLion(*current_frame, region);
351 }
352
353 uint8* buffer = current_frame->data();
354 int stride = current_frame->stride();
355 if (flip) {
356 stride = -stride;
357 buffer += (current_frame->size().height() - 1) * current_frame->stride();
358 }
359
360 webrtc::DesktopFrame* new_frame = queue_.current_frame()->Share();
361 *new_frame->mutable_updated_region() = region;
362
363 helper_.set_size_most_recent(new_frame->size());
364
365 // Signal that we are done capturing data from the display framebuffer,
366 // and accessing display structures.
367 display_configuration_capture_event_.Signal();
368
369 // Capture the current cursor shape and notify |callback_| if it has changed.
370 CaptureCursor();
371
372 new_frame->set_capture_time_ms(
373 (base::Time::Now() - capture_start_time).InMillisecondsRoundedUp());
374 callback_->OnCaptureCompleted(new_frame);
375 }
376
377 void ScreenCapturerMac::SetMouseShapeObserver(
378 MouseShapeObserver* mouse_shape_observer) {
379 DCHECK(!mouse_shape_observer_);
380 DCHECK(mouse_shape_observer);
381 mouse_shape_observer_ = mouse_shape_observer;
382 }
383
384 void ScreenCapturerMac::CaptureCursor() {
385 if (!mouse_shape_observer_)
386 return;
387
388 NSCursor* cursor = [NSCursor currentSystemCursor];
389 if (cursor == nil)
390 return;
391
392 NSImage* nsimage = [cursor image];
393 NSPoint hotspot = [cursor hotSpot];
394 NSSize size = [nsimage size];
395 CGImageRef image = [nsimage CGImageForProposedRect:NULL
396 context:nil
397 hints:nil];
398 if (image == nil)
399 return;
400
401 if (CGImageGetBitsPerPixel(image) != 32 ||
402 CGImageGetBytesPerRow(image) != (size.width * 4) ||
403 CGImageGetBitsPerComponent(image) != 8) {
404 return;
405 }
406
407 CGDataProviderRef provider = CGImageGetDataProvider(image);
408 CFDataRef image_data_ref = CGDataProviderCopyData(provider);
409 if (image_data_ref == NULL)
410 return;
411
412 const char* cursor_src_data =
413 reinterpret_cast<const char*>(CFDataGetBytePtr(image_data_ref));
414 int data_size = CFDataGetLength(image_data_ref);
415
416 // Create a MouseCursorShape that describes the cursor and pass it to
417 // the client.
418 scoped_ptr<MouseCursorShape> cursor_shape(new MouseCursorShape());
419 cursor_shape->size.set(size.width, size.height);
420 cursor_shape->hotspot.set(hotspot.x, hotspot.y);
421 cursor_shape->data.assign(cursor_src_data, cursor_src_data + data_size);
422
423 CFRelease(image_data_ref);
424
425 // Compare the current cursor with the last one we sent to the client. If
426 // they're the same, then don't bother sending the cursor again.
427 if (last_cursor_.size.equals(cursor_shape->size) &&
428 last_cursor_.hotspot.equals(cursor_shape->hotspot) &&
429 last_cursor_.data == cursor_shape->data) {
430 return;
431 }
432
433 // Record the last cursor image that we sent to the client.
434 last_cursor_ = *cursor_shape;
435
436 VLOG(3) << "Sending cursor: " << size.width << "x" << size.height;
437 mouse_shape_observer_->OnCursorShapeChanged(cursor_shape.Pass());
438 }
439
440 void ScreenCapturerMac::GlBlitFast(const webrtc::DesktopFrame& frame,
441 const webrtc::DesktopRegion& region) {
442 // Clip to the size of our current screen.
443 webrtc::DesktopRect clip_rect = webrtc::DesktopRect::MakeSize(frame.size());
444 if (queue_.previous_frame()) {
445 // We are doing double buffer for the capture data so we just need to copy
446 // the invalid region from the previous capture in the current buffer.
447 // TODO(hclam): We can reduce the amount of copying here by subtracting
448 // |capturer_helper_|s region from |last_invalid_region_|.
449 // http://crbug.com/92354
450
451 // Since the image obtained from OpenGL is upside-down, need to do some
452 // magic here to copy the correct rectangle.
453 const int y_offset = (frame.size().width() - 1) * frame.stride();
454 for (webrtc::DesktopRegion::Iterator i(last_invalid_region_);
455 !i.IsAtEnd(); i.Advance()) {
456 webrtc::DesktopRect copy_rect = i.rect();
457 copy_rect.IntersectWith(clip_rect);
458 if (!copy_rect.is_empty()) {
459 CopyRect(queue_.previous_frame()->data() + y_offset,
460 -frame.stride(),
461 frame.data() + y_offset,
462 -frame.stride(),
463 webrtc::DesktopFrame::kBytesPerPixel,
464 copy_rect);
465 }
466 }
467 }
468 last_invalid_region_ = region;
469
470 CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
471 glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pixel_buffer_object_.get());
472 glReadPixels(0, 0, frame.size().height(), frame.size().width(), GL_BGRA,
473 GL_UNSIGNED_BYTE, 0);
474 GLubyte* ptr = static_cast<GLubyte*>(
475 glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB));
476 if (ptr == NULL) {
477 // If the buffer can't be mapped, assume that it's no longer valid and
478 // release it.
479 pixel_buffer_object_.Release();
480 } else {
481 // Copy only from the dirty rects. Since the image obtained from OpenGL is
482 // upside-down we need to do some magic here to copy the correct rectangle.
483 const int y_offset = (frame.size().height() - 1) * frame.stride();
484 for (webrtc::DesktopRegion::Iterator i(region);
485 !i.IsAtEnd(); i.Advance()) {
486 webrtc::DesktopRect copy_rect = i.rect();
487 copy_rect.IntersectWith(clip_rect);
488 if (!copy_rect.is_empty()) {
489 CopyRect(ptr + y_offset,
490 -frame.stride(),
491 frame.data() + y_offset,
492 -frame.stride(),
493 webrtc::DesktopFrame::kBytesPerPixel,
494 copy_rect);
495 }
496 }
497 }
498 if (!glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB)) {
499 // If glUnmapBuffer returns false, then the contents of the data store are
500 // undefined. This might be because the screen mode has changed, in which
501 // case it will be recreated in ScreenConfigurationChanged, but releasing
502 // the object here is the best option. Capturing will fall back on
503 // GlBlitSlow until such time as the pixel buffer object is recreated.
504 pixel_buffer_object_.Release();
505 }
506 glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
507 }
508
509 void ScreenCapturerMac::GlBlitSlow(const webrtc::DesktopFrame& frame) {
510 CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
511 glReadBuffer(GL_FRONT);
512 glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
513 glPixelStorei(GL_PACK_ALIGNMENT, 4); // Force 4-byte alignment.
514 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
515 glPixelStorei(GL_PACK_SKIP_ROWS, 0);
516 glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
517 // Read a block of pixels from the frame buffer.
518 glReadPixels(0, 0, frame.size().width(), frame.size().height(),
519 GL_BGRA, GL_UNSIGNED_BYTE, frame.data());
520 glPopClientAttrib();
521 }
522
523 void ScreenCapturerMac::CgBlitPreLion(const webrtc::DesktopFrame& frame,
524 const webrtc::DesktopRegion& region) {
525 // Copy the entire contents of the previous capture buffer, to capture over.
526 // TODO(wez): Get rid of this as per crbug.com/145064, or implement
527 // crbug.com/92354.
528 if (queue_.previous_frame()) {
529 memcpy(frame.data(),
530 queue_.previous_frame()->data(),
531 frame.stride() * frame.size().height());
532 }
533
534 for (size_t i = 0; i < desktop_config_.displays.size(); ++i) {
535 const MacDisplayConfiguration& display_config = desktop_config_.displays[i];
536
537 // Use deprecated APIs to determine the display buffer layout.
538 DCHECK(cg_display_base_address_ && cg_display_bytes_per_row_ &&
539 cg_display_bits_per_pixel_);
540 uint8* display_base_address =
541 reinterpret_cast<uint8*>((*cg_display_base_address_)(display_config.id));
542 CHECK(display_base_address);
543 int src_bytes_per_row = (*cg_display_bytes_per_row_)(display_config.id);
544 int src_bytes_per_pixel =
545 (*cg_display_bits_per_pixel_)(display_config.id) / 8;
546
547 // Determine the display's position relative to the desktop, in pixels.
548 webrtc::DesktopRect display_bounds = display_config.pixel_bounds;
549 display_bounds.Translate(-desktop_config_.pixel_bounds.left(),
550 -desktop_config_.pixel_bounds.top());
551
552 // Determine which parts of the blit region, if any, lay within the monitor.
553 webrtc::DesktopRegion copy_region = region;
554 copy_region.IntersectWith(display_bounds);
555 if (copy_region.is_empty())
556 continue;
557
558 // Translate the region to be copied into display-relative coordinates.
559 copy_region.Translate(-display_bounds.left(), -display_bounds.top());
560
561 // Calculate where in the output buffer the display's origin is.
562 uint8* out_ptr = frame.data() +
563 (display_bounds.left() * src_bytes_per_pixel) +
564 (display_bounds.top() * frame.stride());
565
566 // Copy the dirty region from the display buffer into our desktop buffer.
567 for (webrtc::DesktopRegion::Iterator i(copy_region);
568 !i.IsAtEnd(); i.Advance()) {
569 CopyRect(display_base_address,
570 src_bytes_per_row,
571 out_ptr,
572 frame.stride(),
573 src_bytes_per_pixel,
574 i.rect());
575 }
576 }
577 }
578
579 void ScreenCapturerMac::CgBlitPostLion(const webrtc::DesktopFrame& frame,
580 const webrtc::DesktopRegion& region) {
581 // Copy the entire contents of the previous capture buffer, to capture over.
582 // TODO(wez): Get rid of this as per crbug.com/145064, or implement
583 // crbug.com/92354.
584 if (queue_.previous_frame()) {
585 memcpy(frame.data(),
586 queue_.previous_frame()->data(),
587 frame.stride() * frame.size().height());
588 }
589
590 for (size_t i = 0; i < desktop_config_.displays.size(); ++i) {
591 const MacDisplayConfiguration& display_config = desktop_config_.displays[i];
592
593 // Determine the display's position relative to the desktop, in pixels.
594 webrtc::DesktopRect display_bounds = display_config.pixel_bounds;
595 display_bounds.Translate(-desktop_config_.pixel_bounds.left(),
596 -desktop_config_.pixel_bounds.top());
597
598 // Determine which parts of the blit region, if any, lay within the monitor.
599 webrtc::DesktopRegion copy_region = region;
600 copy_region.IntersectWith(display_bounds);
601 if (copy_region.is_empty())
602 continue;
603
604 // Translate the region to be copied into display-relative coordinates.
605 copy_region.Translate(-display_bounds.left(), -display_bounds.top());
606
607 // Create an image containing a snapshot of the display.
608 CGImageRef image = CGDisplayCreateImage(display_config.id);
609 if (image == NULL)
610 continue;
611
612 // Request access to the raw pixel data via the image's DataProvider.
613 CGDataProviderRef provider = CGImageGetDataProvider(image);
614 CFDataRef data = CGDataProviderCopyData(provider);
615 CHECK(data);
616
617 const uint8* display_base_address = CFDataGetBytePtr(data);
618 int src_bytes_per_row = CGImageGetBytesPerRow(image);
619 int src_bytes_per_pixel = CGImageGetBitsPerPixel(image) / 8;
620
621 // Calculate where in the output buffer the display's origin is.
622 uint8* out_ptr = frame.data() +
623 (display_bounds.left() * src_bytes_per_pixel) +
624 (display_bounds.top() * frame.stride());
625
626 // Copy the dirty region from the display buffer into our desktop buffer.
627 for (webrtc::DesktopRegion::Iterator i(copy_region);
628 !i.IsAtEnd(); i.Advance()) {
629 CopyRect(display_base_address,
630 src_bytes_per_row,
631 out_ptr,
632 frame.stride(),
633 src_bytes_per_pixel,
634 i.rect());
635 }
636
637 CFRelease(data);
638 CFRelease(image);
639 }
640 }
641
642 void ScreenCapturerMac::ScreenConfigurationChanged() {
643 // Release existing buffers, which will be of the wrong size.
644 ReleaseBuffers();
645
646 // Clear the dirty region, in case the display is down-sizing.
647 helper_.ClearInvalidRegion();
648
649 // Refresh the cached desktop configuration.
650 desktop_config_ = MacDesktopConfiguration::GetCurrent(
651 MacDesktopConfiguration::TopLeftOrigin);
652
653 // Re-mark the entire desktop as dirty.
654 helper_.InvalidateScreen(
655 webrtc::DesktopSize(desktop_config_.pixel_bounds.width(),
656 desktop_config_.pixel_bounds.height()));
657
658 // Make sure the frame buffers will be reallocated.
659 queue_.Reset();
660
661 // CgBlitPostLion uses CGDisplayCreateImage() to snapshot each display's
662 // contents. Although the API exists in OS 10.6, it crashes the caller if
663 // the machine has no monitor connected, so we fall back to depcreated APIs
664 // when running on 10.6.
665 if (IsOSLionOrLater()) {
666 LOG(INFO) << "Using CgBlitPostLion.";
667 // No need for any OpenGL support on Lion
668 return;
669 }
670
671 // Dynamically link to the deprecated pre-Lion capture APIs.
672 app_services_library_ = dlopen(kApplicationServicesLibraryName,
673 RTLD_LAZY);
674 CHECK(app_services_library_)
675 << "Failed to open " << kApplicationServicesLibraryName;
676
677 opengl_library_ = dlopen(kOpenGlLibraryName, RTLD_LAZY);
678 CHECK(opengl_library_) << "Failed to open " << kOpenGlLibraryName;
679
680 cg_display_base_address_ = reinterpret_cast<CGDisplayBaseAddressFunc>(
681 dlsym(app_services_library_, "CGDisplayBaseAddress"));
682 cg_display_bytes_per_row_ = reinterpret_cast<CGDisplayBytesPerRowFunc>(
683 dlsym(app_services_library_, "CGDisplayBytesPerRow"));
684 cg_display_bits_per_pixel_ = reinterpret_cast<CGDisplayBitsPerPixelFunc>(
685 dlsym(app_services_library_, "CGDisplayBitsPerPixel"));
686 cgl_set_full_screen_ = reinterpret_cast<CGLSetFullScreenFunc>(
687 dlsym(opengl_library_, "CGLSetFullScreen"));
688 CHECK(cg_display_base_address_ && cg_display_bytes_per_row_ &&
689 cg_display_bits_per_pixel_ && cgl_set_full_screen_);
690
691 if (desktop_config_.displays.size() > 1) {
692 LOG(INFO) << "Using CgBlitPreLion (Multi-monitor).";
693 return;
694 }
695
696 CGDirectDisplayID mainDevice = CGMainDisplayID();
697 if (!CGDisplayUsesOpenGLAcceleration(mainDevice)) {
698 LOG(INFO) << "Using CgBlitPreLion (OpenGL unavailable).";
699 return;
700 }
701
702 LOG(INFO) << "Using GlBlit";
703
704 CGLPixelFormatAttribute attributes[] = {
705 kCGLPFAFullScreen,
706 kCGLPFADisplayMask,
707 (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(mainDevice),
708 (CGLPixelFormatAttribute)0
709 };
710 CGLPixelFormatObj pixel_format = NULL;
711 GLint matching_pixel_format_count = 0;
712 CGLError err = CGLChoosePixelFormat(attributes,
713 &pixel_format,
714 &matching_pixel_format_count);
715 DCHECK_EQ(err, kCGLNoError);
716 err = CGLCreateContext(pixel_format, NULL, &cgl_context_);
717 DCHECK_EQ(err, kCGLNoError);
718 CGLDestroyPixelFormat(pixel_format);
719 (*cgl_set_full_screen_)(cgl_context_);
720 CGLSetCurrentContext(cgl_context_);
721
722 size_t buffer_size = desktop_config_.pixel_bounds.width() *
723 desktop_config_.pixel_bounds.height() *
724 sizeof(uint32_t);
725 pixel_buffer_object_.Init(cgl_context_, buffer_size);
726 }
727
728 bool ScreenCapturerMac::RegisterRefreshAndMoveHandlers() {
729 CGError err = CGRegisterScreenRefreshCallback(
730 ScreenCapturerMac::ScreenRefreshCallback, this);
731 if (err != kCGErrorSuccess) {
732 LOG(ERROR) << "CGRegisterScreenRefreshCallback " << err;
733 return false;
734 }
735
736 err = CGScreenRegisterMoveCallback(
737 ScreenCapturerMac::ScreenUpdateMoveCallback, this);
738 if (err != kCGErrorSuccess) {
739 LOG(ERROR) << "CGScreenRegisterMoveCallback " << err;
740 return false;
741 }
742
743 return true;
744 }
745
746 void ScreenCapturerMac::UnregisterRefreshAndMoveHandlers() {
747 CGUnregisterScreenRefreshCallback(
748 ScreenCapturerMac::ScreenRefreshCallback, this);
749 CGScreenUnregisterMoveCallback(
750 ScreenCapturerMac::ScreenUpdateMoveCallback, this);
751 }
752
753 void ScreenCapturerMac::ScreenRefresh(CGRectCount count,
754 const CGRect* rect_array) {
755 if (desktop_config_.pixel_bounds.is_empty())
756 return;
757
758 webrtc::DesktopRegion region;
759
760 for (CGRectCount i = 0; i < count; ++i) {
761 // Convert from Density-Independent Pixel to physical pixel coordinates.
762 webrtc::DesktopRect rect =
763 ScaleAndRoundCGRect(rect_array[i], desktop_config_.dip_to_pixel_scale);
764
765 // Translate from local desktop to capturer framebuffer coordinates.
766 rect.Translate(-desktop_config_.pixel_bounds.left(),
767 -desktop_config_.pixel_bounds.top());
768
769 region.AddRect(rect);
770 }
771
772 helper_.InvalidateRegion(region);
773 }
774
775 void ScreenCapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
776 size_t count,
777 const CGRect* rect_array) {
778 // Translate |rect_array| to identify the move's destination.
779 CGRect refresh_rects[count];
780 for (CGRectCount i = 0; i < count; ++i) {
781 refresh_rects[i] = CGRectOffset(rect_array[i], delta.dX, delta.dY);
782 }
783
784 // Currently we just treat move events the same as refreshes.
785 ScreenRefresh(count, refresh_rects);
786 }
787
788 void ScreenCapturerMac::DisplaysReconfigured(
789 CGDirectDisplayID display,
790 CGDisplayChangeSummaryFlags flags) {
791 if (flags & kCGDisplayBeginConfigurationFlag) {
792 if (reconfiguring_displays_.empty()) {
793 // If this is the first display to start reconfiguring then wait on
794 // |display_configuration_capture_event_| to block the capture thread
795 // from accessing display memory until the reconfiguration completes.
796 CHECK(display_configuration_capture_event_.TimedWait(
797 base::TimeDelta::FromSeconds(
798 kDisplayConfigurationEventTimeoutInSeconds)));
799 }
800
801 reconfiguring_displays_.insert(display);
802 } else {
803 reconfiguring_displays_.erase(display);
804
805 if (reconfiguring_displays_.empty()) {
806 // If no other displays are reconfiguring then refresh capturer data
807 // structures and un-block the capturer thread. Occasionally, the
808 // refresh and move handlers are lost when the screen mode changes,
809 // so re-register them here (the same does not appear to be true for
810 // the reconfiguration handler itself).
811 UnregisterRefreshAndMoveHandlers();
812 RegisterRefreshAndMoveHandlers();
813 ScreenConfigurationChanged();
814 display_configuration_capture_event_.Signal();
815 }
816 }
817 }
818
819 void ScreenCapturerMac::ScreenRefreshCallback(CGRectCount count,
820 const CGRect* rect_array,
821 void* user_parameter) {
822 ScreenCapturerMac* capturer = reinterpret_cast<ScreenCapturerMac*>(
823 user_parameter);
824 if (capturer->desktop_config_.pixel_bounds.is_empty()) {
825 capturer->ScreenConfigurationChanged();
826 }
827 capturer->ScreenRefresh(count, rect_array);
828 }
829
830 void ScreenCapturerMac::ScreenUpdateMoveCallback(
831 CGScreenUpdateMoveDelta delta,
832 size_t count,
833 const CGRect* rect_array,
834 void* user_parameter) {
835 ScreenCapturerMac* capturer = reinterpret_cast<ScreenCapturerMac*>(
836 user_parameter);
837 capturer->ScreenUpdateMove(delta, count, rect_array);
838 }
839
840 void ScreenCapturerMac::DisplaysReconfiguredCallback(
841 CGDirectDisplayID display,
842 CGDisplayChangeSummaryFlags flags,
843 void* user_parameter) {
844 ScreenCapturerMac* capturer = reinterpret_cast<ScreenCapturerMac*>(
845 user_parameter);
846 capturer->DisplaysReconfigured(display, flags);
847 }
848
849 } // namespace
850
851 // static
852 scoped_ptr<ScreenCapturer> ScreenCapturer::Create() {
853 scoped_ptr<ScreenCapturerMac> capturer(new ScreenCapturerMac());
854 if (!capturer->Init())
855 capturer.reset();
856 return capturer.PassAs<ScreenCapturer>();
857 }
858
859 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698