Implement a work around for Chrome full-screen tab switch on Mac.

Chrome creates a new window in full-screen and minimizes the old window when a tab is switched to full-screen.
We try to find the new window to continue capturing for window sharing.

BUG=crbug/385294
R=sergeyu@chromium.org

Review URL: https://webrtc-codereview.appspot.com/19839004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6629 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
jiayl@webrtc.org 2014-07-08 22:05:24 +00:00
parent e55641d4f7
commit 12b4efefdd
12 changed files with 472 additions and 102 deletions

View File

@ -37,10 +37,12 @@ source_set("desktop_capture") {
"mac/desktop_configuration.mm",
"mac/desktop_configuration_monitor.h",
"mac/desktop_configuration_monitor.cc",
"mac/osx_version.h",
"mac/osx_version.cc",
"mac/full_screen_chrome_window_detector.cc",
"mac/full_screen_chrome_window_detector.h",
"mac/scoped_pixel_buffer_object.cc",
"mac/scoped_pixel_buffer_object.h",
"mac/window_list_utils.cc",
"mac/window_list_utils.h",
"mouse_cursor.cc",
"mouse_cursor.h",
"mouse_cursor_monitor.h",

View File

@ -1,4 +1,5 @@
alexeypa@chromium.org
jiayl@webrtc.org
sergeyu@chromium.org
wez@chromium.org

View File

@ -38,10 +38,12 @@
"mac/desktop_configuration.mm",
"mac/desktop_configuration_monitor.h",
"mac/desktop_configuration_monitor.cc",
"mac/osx_version.h",
"mac/osx_version.cc",
"mac/full_screen_chrome_window_detector.cc",
"mac/full_screen_chrome_window_detector.h",
"mac/scoped_pixel_buffer_object.cc",
"mac/scoped_pixel_buffer_object.h",
"mac/window_list_utils.cc",
"mac/window_list_utils.h",
"mouse_cursor.cc",
"mouse_cursor.h",
"mouse_cursor_monitor.h",

View File

@ -19,6 +19,7 @@
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
#endif
namespace webrtc {
@ -50,6 +51,14 @@ class DesktopCaptureOptions {
void set_configuration_monitor(scoped_refptr<DesktopConfigurationMonitor> m) {
configuration_monitor_ = m;
}
FullScreenChromeWindowDetector* full_screen_chrome_window_detector() const {
return full_screen_window_detector_;
}
void set_full_screen_chrome_window_detector(
scoped_refptr<FullScreenChromeWindowDetector> detector) {
full_screen_window_detector_ = detector;
}
#endif
// Flag indicating that the capturer should use screen change notifications.
@ -82,6 +91,7 @@ class DesktopCaptureOptions {
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
scoped_refptr<FullScreenChromeWindowDetector> full_screen_window_detector_;
#endif
#if defined(WEBRTC_WIN)

View File

@ -0,0 +1,244 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
#include <assert.h>
#include <libproc.h>
#include <string>
#include "webrtc/base/macutils.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/modules/desktop_capture/mac/window_list_utils.h"
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
namespace {
const int64_t kUpdateIntervalMs = 500;
// Returns true if the window is minimized.
bool IsWindowMinimized(CGWindowID id) {
CFArrayRef window_id_array =
CFArrayCreate(NULL, reinterpret_cast<const void **>(&id), 1, NULL);
CFArrayRef window_array =
CGWindowListCreateDescriptionFromArray(window_id_array);
bool minimized = false;
if (window_array && CFArrayGetCount(window_array)) {
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, 0));
CFBooleanRef on_screen = reinterpret_cast<CFBooleanRef>(
CFDictionaryGetValue(window, kCGWindowIsOnscreen));
minimized = !on_screen;
}
CFRelease(window_id_array);
CFRelease(window_array);
return minimized;
}
// Returns true if the window is occupying a full screen.
bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
CFDictionaryRef window) {
bool fullscreen = false;
CFDictionaryRef bounds_ref = reinterpret_cast<CFDictionaryRef>(
CFDictionaryGetValue(window, kCGWindowBounds));
CGRect bounds;
if (bounds_ref &&
CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds)) {
for (MacDisplayConfigurations::const_iterator it =
desktop_config.displays.begin();
it != desktop_config.displays.end(); ++it) {
if (it->bounds.equals(DesktopRect::MakeXYWH(bounds.origin.x,
bounds.origin.y,
bounds.size.width,
bounds.size.height))) {
fullscreen = true;
break;
}
}
}
return fullscreen;
}
std::string GetWindowTitle(CGWindowID id) {
CFArrayRef window_id_array =
CFArrayCreate(NULL, reinterpret_cast<const void **>(&id), 1, NULL);
CFArrayRef window_array =
CGWindowListCreateDescriptionFromArray(window_id_array);
std::string title;
if (window_array && CFArrayGetCount(window_array)) {
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, 0));
CFStringRef title_ref = reinterpret_cast<CFStringRef>(
CFDictionaryGetValue(window, kCGWindowName));
if (title_ref)
rtc::ToUtf8(title_ref, &title);
}
CFRelease(window_id_array);
CFRelease(window_array);
return title;
}
int GetWindowOwnerPid(CGWindowID id) {
CFArrayRef window_id_array =
CFArrayCreate(NULL, reinterpret_cast<const void **>(&id), 1, NULL);
CFArrayRef window_array =
CGWindowListCreateDescriptionFromArray(window_id_array);
int pid = 0;
if (window_array && CFArrayGetCount(window_array)) {
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, 0));
CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(window, kCGWindowOwnerPID));
if (pid_ref)
CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
}
CFRelease(window_id_array);
CFRelease(window_array);
return pid;
}
// Returns the window that is full-screen and has the same title and owner pid
// as the input window.
CGWindowID FindFullScreenWindowWithSamePidAndTitle(CGWindowID id) {
int pid = GetWindowOwnerPid(id);
std::string title = GetWindowTitle(id);
// Only get on screen, non-desktop windows.
CFArrayRef window_array = CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
kCGNullWindowID);
if (!window_array)
return kCGNullWindowID;
CGWindowID full_screen_window = kCGNullWindowID;
MacDesktopConfiguration desktop_config = MacDesktopConfiguration::GetCurrent(
MacDesktopConfiguration::TopLeftOrigin);
// Check windows to make sure they have an id, title, and use window layer
// other than 0.
CFIndex count = CFArrayGetCount(window_array);
for (CFIndex i = 0; i < count; ++i) {
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, i));
CFStringRef window_title_ref = reinterpret_cast<CFStringRef>(
CFDictionaryGetValue(window, kCGWindowName));
CFNumberRef window_id_ref = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(window, kCGWindowNumber));
CFNumberRef window_pid_ref = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(window, kCGWindowOwnerPID));
if (!window_title_ref || !window_id_ref || !window_pid_ref)
continue;
int window_pid = 0;
CFNumberGetValue(window_pid_ref, kCFNumberIntType, &window_pid);
if (window_pid != pid)
continue;
std::string window_title;
if (!rtc::ToUtf8(window_title_ref, &window_title) ||
window_title != title) {
continue;
}
CGWindowID window_id;
CFNumberGetValue(window_id_ref, kCFNumberIntType, &window_id);
if (IsWindowFullScreen(desktop_config, window)) {
full_screen_window = window_id;
break;
}
}
CFRelease(window_array);
return full_screen_window;
}
bool IsChromeWindow(CGWindowID id) {
int pid = GetWindowOwnerPid(id);
char buffer[PROC_PIDPATHINFO_MAXSIZE];
int path_length = proc_pidpath(pid, buffer, sizeof(buffer));
if (path_length <= 0)
return false;
const char* last_slash = strrchr(buffer, '/');
std::string name(last_slash ? last_slash + 1 : buffer);
return name.find("Google Chrome") == 0 || name == "Chromium";
}
} // namespace
FullScreenChromeWindowDetector::FullScreenChromeWindowDetector()
: ref_count_(0) {}
FullScreenChromeWindowDetector::~FullScreenChromeWindowDetector() {}
CGWindowID FullScreenChromeWindowDetector::FindFullScreenWindow(
CGWindowID original_window) {
if (!IsChromeWindow(original_window) || !IsWindowMinimized(original_window))
return kCGNullWindowID;
CGWindowID full_screen_window_id =
FindFullScreenWindowWithSamePidAndTitle(original_window);
if (full_screen_window_id == kCGNullWindowID)
return kCGNullWindowID;
for (WindowCapturer::WindowList::iterator it = previous_window_list_.begin();
it != previous_window_list_.end(); ++it) {
if (static_cast<CGWindowID>(it->id) != full_screen_window_id)
continue;
int64_t time_interval =
(TickTime::Now() - last_udpate_time_).Milliseconds();
LOG(LS_WARNING) << "The full-screen window exists in the list, "
<< "which was updated " << time_interval << "ms ago.";
return kCGNullWindowID;
}
return full_screen_window_id;
}
void FullScreenChromeWindowDetector::UpdateWindowListIfNeeded(
CGWindowID original_window) {
if (IsChromeWindow(original_window) &&
(TickTime::Now() - last_udpate_time_).Milliseconds()
> kUpdateIntervalMs) {
previous_window_list_.clear();
previous_window_list_.swap(current_window_list_);
// No need to update the window list when the window is minimized.
if (IsWindowMinimized(original_window)) {
previous_window_list_.clear();
return;
}
GetWindowList(&current_window_list_);
last_udpate_time_ = TickTime::Now();
}
}
} // namespace webrtc

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_
#include <ApplicationServices/ApplicationServices.h>
#include "webrtc/modules/desktop_capture/window_capturer.h"
#include "webrtc/system_wrappers/interface/atomic32.h"
#include "webrtc/system_wrappers/interface/tick_util.h"
namespace webrtc {
// This is a work around for the Chrome tab full-screen behavior: Chrome
// creates a new window in full-screen mode to show a tab full-screen and
// minimizes the old window. To continue capturing in this case, we try to
// find the new full-screen window using these criteria:
// 0. The original shared window is minimized.
// 1. The original shared window's owner application name is "Google Chrome".
// 2. The original window and the new window have the same title and owner
// pid.
// 3. The new window is full-screen.
// 4. The new window didn't exist at least 500 millisecond ago.
class FullScreenChromeWindowDetector {
public:
FullScreenChromeWindowDetector();
void AddRef() { ++ref_count_; }
void Release() {
if (--ref_count_ == 0)
delete this;
}
// Returns the full-screen window in place of the original window if all the
// criteria are met, or kCGNullWindowID if no such window found.
CGWindowID FindFullScreenWindow(CGWindowID original_window);
// The caller should call this function periodically, no less than twice per
// second.
void UpdateWindowListIfNeeded(CGWindowID original_window);
private:
~FullScreenChromeWindowDetector();
Atomic32 ref_count_;
// We cache the last two results of the window list, so
// |previous_window_list_| is taken at least 500ms before the next Capture()
// call. If we only save the last result, we may get false positive (i.e.
// full-screen window exists in the list) if Capture() is called too soon.
WindowCapturer::WindowList current_window_list_;
WindowCapturer::WindowList previous_window_list_;
TickTime last_udpate_time_;
DISALLOW_COPY_AND_ASSIGN(FullScreenChromeWindowDetector);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_CHROME_WINDOW_DETECTOR_H_

View File

@ -1,54 +0,0 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <sys/utsname.h>
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
namespace {
int GetDarwinVersion() {
struct utsname uname_info;
if (uname(&uname_info) != 0) {
LOG(LS_ERROR) << "uname failed";
return 0;
}
if (strcmp(uname_info.sysname, "Darwin") != 0)
return 0;
char* dot;
int result = strtol(uname_info.release, &dot, 10);
if (*dot != '.') {
LOG(LS_ERROR) << "Failed to parse version";
return 0;
}
return result;
}
} // namespace
bool IsOSLionOrLater() {
static int darwin_version = GetDarwinVersion();
// Verify that the version has been parsed correctly.
if (darwin_version < 6) {
LOG_F(LS_ERROR) << "Invalid Darwin version: " << darwin_version;
abort();
}
// Darwin major version 11 corresponds to OSX 10.7.
return darwin_version >= 11;
}
} // namespace webrtc

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/desktop_capture/mac/window_list_utils.h"
#include <ApplicationServices/ApplicationServices.h>
#include "webrtc/base/macutils.h"
namespace webrtc {
bool GetWindowList(WindowCapturer::WindowList* windows) {
// Only get on screen, non-desktop windows.
CFArrayRef window_array = CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
kCGNullWindowID);
if (!window_array)
return false;
// Check windows to make sure they have an id, title, and use window layer
// other than 0.
CFIndex count = CFArrayGetCount(window_array);
for (CFIndex i = 0; i < count; ++i) {
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, i));
CFStringRef window_title = reinterpret_cast<CFStringRef>(
CFDictionaryGetValue(window, kCGWindowName));
CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(window, kCGWindowNumber));
CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(window, kCGWindowLayer));
if (window_title && window_id && window_layer) {
// Skip windows with layer=0 (menu, dock).
int layer;
CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
if (layer != 0)
continue;
int id;
CFNumberGetValue(window_id, kCFNumberIntType, &id);
WindowCapturer::Window window;
window.id = id;
if (!rtc::ToUtf8(window_title, &(window.title)) ||
window.title.empty()) {
continue;
}
windows->push_back(window);
}
}
CFRelease(window_array);
return true;
}
} // namespace webrtc

View File

@ -8,9 +8,17 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_
#include "webrtc/modules/desktop_capture/window_capturer.h"
namespace webrtc {
// Returns true if the OS version >= OSX 10.7.
bool IsOSLionOrLater();
// A helper function to get the on-screen windows.
bool GetWindowList(WindowCapturer::WindowList* windows);
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_

View File

@ -15,11 +15,12 @@
#include <Cocoa/Cocoa.h>
#include <CoreFoundation/CoreFoundation.h>
#include "webrtc/base/macutils.h"
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#include "webrtc/modules/desktop_capture/mac/osx_version.h"
#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
@ -52,6 +53,8 @@ class MouseCursorMonitorMac : public MouseCursorMonitor {
Callback* callback_;
Mode mode_;
scoped_ptr<MouseCursor> last_cursor_;
scoped_refptr<FullScreenChromeWindowDetector>
full_screen_chrome_window_detector_;
};
MouseCursorMonitorMac::MouseCursorMonitorMac(
@ -62,9 +65,12 @@ MouseCursorMonitorMac::MouseCursorMonitorMac(
window_id_(window_id),
screen_id_(screen_id),
callback_(NULL),
mode_(SHAPE_AND_POSITION) {
mode_(SHAPE_AND_POSITION),
full_screen_chrome_window_detector_(
options.full_screen_chrome_window_detector()) {
assert(window_id == kCGNullWindowID || screen_id == kInvalidScreenId);
if (screen_id != kInvalidScreenId && !IsOSLionOrLater()) {
if (screen_id != kInvalidScreenId &&
rtc::GetOSVersionName() < rtc::kMacOSLion) {
// Single screen capture is not supported on pre OS X 10.7.
screen_id_ = kFullDesktopScreenId;
}
@ -115,14 +121,23 @@ void MouseCursorMonitorMac::Capture() {
// if the current mouse position is covered by another window and also adjust
// |position| to make it relative to the window origin.
if (window_id_ != kCGNullWindowID) {
// Get list of windows that may be covering parts of |window_id_|.
CGWindowID on_screen_window = window_id_;
if (full_screen_chrome_window_detector_) {
CGWindowID full_screen_window =
full_screen_chrome_window_detector_->FindFullScreenWindow(window_id_);
if (full_screen_window != kCGNullWindowID)
on_screen_window = full_screen_window;
}
// Get list of windows that may be covering parts of |on_screen_window|.
// CGWindowListCopyWindowInfo() returns windows in order from front to back,
// so |window_id_| is expected to be the last in the list.
// so |on_screen_window| is expected to be the last in the list.
CFArrayRef window_array =
CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly |
kCGWindowListOptionOnScreenAboveWindow |
kCGWindowListOptionIncludingWindow,
window_id_);
on_screen_window);
bool found_window = false;
if (window_array) {
CFIndex count = CFArrayGetCount(window_array);
@ -158,7 +173,7 @@ void MouseCursorMonitorMac::Capture() {
if (!CFNumberGetValue(window_number, kCFNumberIntType, &window_id))
continue;
if (window_id == window_id_) {
if (window_id == on_screen_window) {
found_window = true;
if (!window_rect.Contains(position))
state = OUTSIDE;

View File

@ -20,13 +20,13 @@
#include <OpenGL/CGLMacro.h>
#include <OpenGL/OpenGL.h>
#include "webrtc/base/macutils.h"
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
#include "webrtc/modules/desktop_capture/desktop_region.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#include "webrtc/modules/desktop_capture/mac/osx_version.h"
#include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h"
#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
@ -425,7 +425,7 @@ void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) {
DesktopFrame* current_frame = queue_.current_frame();
bool flip = false; // GL capturers need flipping.
if (IsOSLionOrLater()) {
if (rtc::GetOSVersionName() >= rtc::kMacOSLion) {
// Lion requires us to use their new APIs for doing screen capture. These
// APIS currently crash on 10.6.8 if there is no monitor attached.
if (!CgBlitPostLion(*current_frame, region)) {
@ -478,7 +478,7 @@ void ScreenCapturerMac::SetMouseShapeObserver(
bool ScreenCapturerMac::GetScreenList(ScreenList* screens) {
assert(screens->size() == 0);
if (!IsOSLionOrLater()) {
if (rtc::GetOSVersionName() < rtc::kMacOSLion) {
// Single monitor cast is not supported on pre OS X 10.7.
Screen screen;
screen.id = kFullDesktopScreenId;
@ -496,7 +496,7 @@ bool ScreenCapturerMac::GetScreenList(ScreenList* screens) {
}
bool ScreenCapturerMac::SelectScreen(ScreenId id) {
if (!IsOSLionOrLater()) {
if (rtc::GetOSVersionName() < rtc::kMacOSLion) {
// Ignore the screen selection on unsupported OS.
assert(!current_display_);
return id == kFullDesktopScreenId;
@ -874,7 +874,7 @@ void ScreenCapturerMac::ScreenConfigurationChanged() {
// contents. Although the API exists in OS 10.6, it crashes the caller if
// the machine has no monitor connected, so we fall back to depcreated APIs
// when running on 10.6.
if (IsOSLionOrLater()) {
if (rtc::GetOSVersionName() >= rtc::kMacOSLion) {
LOG(LS_INFO) << "Using CgBlitPostLion.";
// No need for any OpenGL support on Lion
return;
@ -922,10 +922,11 @@ void ScreenCapturerMac::ScreenConfigurationChanged() {
LOG(LS_INFO) << "Using GlBlit";
CGLPixelFormatAttribute attributes[] = {
// This function does an early return if IsOSLionOrLater(), this code only
// runs on 10.6 and can be deleted once 10.6 support is dropped. So just
// keep using kCGLPFAFullScreen even though it was deprecated in 10.6 --
// it's still functional there, and it's not used on newer OS X versions.
// This function does an early return if GetOSVersionName() >= kMacOSLion,
// this code only runs on 10.6 and can be deleted once 10.6 support is
// dropped. So just keep using kCGLPFAFullScreen even though it was
// deprecated in 10.6 -- it's still functional there, and it's not used on
// newer OS X versions.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
kCGLPFAFullScreen,

View File

@ -15,33 +15,21 @@
#include <Cocoa/Cocoa.h>
#include <CoreFoundation/CoreFoundation.h>
#include "webrtc/base/macutils.h"
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
#include "webrtc/modules/desktop_capture/mac/window_list_utils.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/scoped_refptr.h"
#include "webrtc/system_wrappers/interface/tick_util.h"
namespace webrtc {
namespace {
bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
assert(string);
assert(str_utf8);
CFIndex length = CFStringGetLength(string);
size_t max_length_utf8 =
CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
str_utf8->resize(max_length_utf8);
CFIndex used_bytes;
int result = CFStringGetBytes(
string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false,
reinterpret_cast<UInt8*>(&*str_utf8->begin()), max_length_utf8,
&used_bytes);
if (result != length) {
str_utf8->clear();
return false;
}
str_utf8->resize(used_bytes);
return true;
}
// Returns true if the window exists.
bool IsWindowValid(CGWindowID id) {
CFArrayRef window_id_array =
CFArrayCreate(NULL, reinterpret_cast<const void **>(&id), 1, NULL);
@ -56,7 +44,9 @@ bool IsWindowValid(CGWindowID id) {
class WindowCapturerMac : public WindowCapturer {
public:
WindowCapturerMac();
explicit WindowCapturerMac(
scoped_refptr<FullScreenChromeWindowDetector>
full_screen_chrome_window_detector);
virtual ~WindowCapturerMac();
// WindowCapturer interface.
@ -70,14 +60,22 @@ class WindowCapturerMac : public WindowCapturer {
private:
Callback* callback_;
// The window being captured.
CGWindowID window_id_;
scoped_refptr<FullScreenChromeWindowDetector>
full_screen_chrome_window_detector_;
DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
};
WindowCapturerMac::WindowCapturerMac()
WindowCapturerMac::WindowCapturerMac(
scoped_refptr<FullScreenChromeWindowDetector>
full_screen_chrome_window_detector)
: callback_(NULL),
window_id_(0) {
window_id_(0),
full_screen_chrome_window_detector_(full_screen_chrome_window_detector) {
}
WindowCapturerMac::~WindowCapturerMac() {
@ -114,7 +112,7 @@ bool WindowCapturerMac::GetWindowList(WindowList* windows) {
CFNumberGetValue(window_id, kCFNumberIntType, &id);
WindowCapturer::Window window;
window.id = id;
if (!CFStringRefToUtf8(window_title, &(window.title)) ||
if (!rtc::ToUtf8(window_title, &(window.title)) ||
window.title.empty()) {
continue;
}
@ -183,9 +181,18 @@ void WindowCapturerMac::Capture(const DesktopRegion& region) {
return;
}
CGWindowID on_screen_window = window_id_;
if (full_screen_chrome_window_detector_) {
CGWindowID full_screen_window =
full_screen_chrome_window_detector_->FindFullScreenWindow(window_id_);
if (full_screen_window != kCGNullWindowID)
on_screen_window = full_screen_window;
}
CGImageRef window_image = CGWindowListCreateImage(
CGRectNull, kCGWindowListOptionIncludingWindow,
window_id_, kCGWindowImageBoundsIgnoreFraming);
on_screen_window, kCGWindowImageBoundsIgnoreFraming);
if (!window_image) {
callback_->OnCaptureCompleted(NULL);
@ -218,13 +225,16 @@ void WindowCapturerMac::Capture(const DesktopRegion& region) {
CFRelease(window_image);
callback_->OnCaptureCompleted(frame);
if (full_screen_chrome_window_detector_)
full_screen_chrome_window_detector_->UpdateWindowListIfNeeded(window_id_);
}
} // namespace
// static
WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
return new WindowCapturerMac();
return new WindowCapturerMac(options.full_screen_chrome_window_detector());
}
} // namespace webrtc