Use ScreenCapturer to capture the whole and clip to the window rect when the shared window is on the top.

BUG=crbug/403703, crbug/316603
R=wez@chromium.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7684 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
jiayl@webrtc.org 2014-11-11 18:15:55 +00:00
parent a367aeab82
commit 0e71070207
11 changed files with 499 additions and 10 deletions

View File

@ -110,6 +110,13 @@ inline bool IsWindowsXpOrLater() {
(major == kWindows2000 && minor >= 1)));
}
inline bool IsWindows8OrLater() {
int major, minor;
return (GetOsVersion(&major, &minor, NULL) &&
(major > kWindowsVista ||
(major == kWindowsVista && minor >= 2)));
}
// Determine the current integrity level of the process.
bool GetCurrentProcessIntegrityLevel(int* level);
@ -125,5 +132,5 @@ bool AdjustCurrentProcessPrivilege(const TCHAR* privilege, bool to_enable);
} // namespace rtc
#endif // WEBRTC_WIN
#endif // WEBRTC_WIN
#endif // WEBRTC_BASE_WIN32_H_

View File

@ -14,6 +14,11 @@ use_desktop_capture_differ_sse2 =
source_set("desktop_capture") {
sources = [
"cropped_desktop_frame.cc",
"cropped_desktop_frame.h",
"cropping_window_capturer.cc",
"cropping_window_capturer.h",
"cropping_window_capturer_win.cc",
"desktop_and_cursor_composer.cc",
"desktop_and_cursor_composer.h",
"desktop_capture_types.h",

View File

@ -0,0 +1,45 @@
/*
* 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/cropped_desktop_frame.h"
namespace webrtc {
// A DesktopFrame that is a sub-rect of another DesktopFrame.
class CroppedDesktopFrame : public DesktopFrame {
public:
CroppedDesktopFrame(DesktopFrame* frame, const DesktopRect& rect);
private:
scoped_ptr<DesktopFrame> frame_;
DISALLOW_COPY_AND_ASSIGN(CroppedDesktopFrame);
};
DesktopFrame*
CreateCroppedDesktopFrame(DesktopFrame* frame, const DesktopRect& rect) {
if (!DesktopRect::MakeSize(frame->size()).ContainsRect(rect)) {
delete frame;
return NULL;
}
return new CroppedDesktopFrame(frame, rect);
}
CroppedDesktopFrame::CroppedDesktopFrame(DesktopFrame* frame,
const DesktopRect& rect)
: DesktopFrame(rect.size(),
frame->stride(),
frame->GetFrameDataAtPos(rect.top_left()),
frame->shared_memory()),
frame_(frame) {
}
} // namespace webrtc

View File

@ -0,0 +1,25 @@
/*
* 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_CROPPED_DESKTOP_FRAME_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_CROPPED_DESKTOP_FRAME_H_
#include "webrtc/modules/desktop_capture/desktop_frame.h"
namespace webrtc {
// Always takes ownership of |frame|. Returns NULL if |rect| is not contained
// by the bounds of |frame|.
DesktopFrame* CreateCroppedDesktopFrame(DesktopFrame* frame,
const DesktopRect& rect);
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_CROPPED_DESKTOP_FRAME_H_

View File

@ -0,0 +1,111 @@
/*
* 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/cropping_window_capturer.h"
#include "webrtc/modules/desktop_capture/cropped_desktop_frame.h"
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
CroppingWindowCapturer::CroppingWindowCapturer(
const DesktopCaptureOptions& options)
: options_(options),
callback_(NULL),
window_capturer_(WindowCapturer::Create(options)),
selected_window_(kNullWindowId),
excluded_window_(kNullWindowId) {
}
CroppingWindowCapturer::~CroppingWindowCapturer() {}
void CroppingWindowCapturer::Start(DesktopCapturer::Callback* callback) {
callback_ = callback;
window_capturer_->Start(callback);
}
void CroppingWindowCapturer::Capture(const DesktopRegion& region) {
if (ShouldUseScreenCapturer()) {
if (!screen_capturer_.get()) {
screen_capturer_.reset(ScreenCapturer::Create(options_));
if (excluded_window_) {
screen_capturer_->SetExcludedWindow(excluded_window_);
}
screen_capturer_->Start(this);
}
screen_capturer_->Capture(region);
} else {
window_capturer_->Capture(region);
}
}
void CroppingWindowCapturer::SetExcludedWindow(WindowId window) {
excluded_window_ = window;
if (screen_capturer_.get()) {
screen_capturer_->SetExcludedWindow(window);
}
}
bool CroppingWindowCapturer::GetWindowList(WindowList* windows) {
return window_capturer_->GetWindowList(windows);
}
bool CroppingWindowCapturer::SelectWindow(WindowId id) {
if (window_capturer_->SelectWindow(id)) {
selected_window_ = id;
return true;
}
return false;
}
bool CroppingWindowCapturer::BringSelectedWindowToFront() {
return window_capturer_->BringSelectedWindowToFront();
}
SharedMemory* CroppingWindowCapturer::CreateSharedMemory(size_t size) {
return callback_->CreateSharedMemory(size);
}
void CroppingWindowCapturer::OnCaptureCompleted(DesktopFrame* frame) {
scoped_ptr<DesktopFrame> screen_frame(frame);
if (!ShouldUseScreenCapturer()) {
LOG(LS_INFO) << "Window no longer on top when ScreenCapturer finishes";
window_capturer_->Capture(DesktopRegion());
return;
}
if (!frame) {
LOG(LS_WARNING) << "ScreenCapturer failed to capture a frame";
callback_->OnCaptureCompleted(NULL);
return;
}
DesktopRect window_rect = GetWindowRectInVirtualScreen();
if (window_rect.is_empty()) {
LOG(LS_WARNING) << "Window rect is empty";
callback_->OnCaptureCompleted(NULL);
return;
}
scoped_ptr<DesktopFrame> window_frame(
CreateCroppedDesktopFrame(screen_frame.release(), window_rect));
callback_->OnCaptureCompleted(window_frame.release());
}
#if !defined(OS_WIN)
// static
WindowCapturer*
CroppingWindowCapturer::Create(const DesktopCaptureOptions& options) {
return WindowCapturer::Create(options);
}
#endif
} // namespace webrtc

View File

@ -0,0 +1,74 @@
/*
* 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_CROPPING_WINDOW_CAPTURER_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_CROPPING_WINDOW_CAPTURER_H_
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/screen_capturer.h"
#include "webrtc/modules/desktop_capture/window_capturer.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
namespace webrtc {
// WindowCapturer implementation that uses a screen capturer to capture the
// whole screen and crops the video frame to the window area when the captured
// window is on top.
class CroppingWindowCapturer : public WindowCapturer,
public DesktopCapturer::Callback {
public:
static WindowCapturer* Create(const DesktopCaptureOptions& options);
virtual ~CroppingWindowCapturer();
// DesktopCapturer implementation.
virtual void Start(DesktopCapturer::Callback* callback) OVERRIDE;
virtual void Capture(const DesktopRegion& region) OVERRIDE;
virtual void SetExcludedWindow(WindowId window) OVERRIDE;
// WindowCapturer implementation.
virtual bool GetWindowList(WindowList* windows) OVERRIDE;
virtual bool SelectWindow(WindowId id) OVERRIDE;
virtual bool BringSelectedWindowToFront() OVERRIDE;
// DesktopCapturer::Callback implementation, passed to |screen_capturer_| to
// intercept the capture result.
virtual SharedMemory* CreateSharedMemory(size_t size) OVERRIDE;
virtual void OnCaptureCompleted(DesktopFrame* frame) OVERRIDE;
protected:
explicit CroppingWindowCapturer(const DesktopCaptureOptions& options);
// The platform implementation should override these methods.
// Returns true if it is OK to capture the whole screen and crop to the
// selected window, i.e. the selected window is opaque, rectangular, and not
// occluded.
virtual bool ShouldUseScreenCapturer() = 0;
// Returns the window area relative to the top left of the virtual screen
// within the bounds of the virtual screen.
virtual DesktopRect GetWindowRectInVirtualScreen() = 0;
WindowId selected_window() const { return selected_window_; }
WindowId excluded_window() const { return excluded_window_; }
private:
DesktopCaptureOptions options_;
DesktopCapturer::Callback* callback_;
scoped_ptr<WindowCapturer> window_capturer_;
scoped_ptr<ScreenCapturer> screen_capturer_;
WindowId selected_window_;
WindowId excluded_window_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_CROPPING_WINDOW_CAPTURER_H_

View File

@ -0,0 +1,216 @@
/*
* 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/cropping_window_capturer.h"
#include "webrtc/base/win32.h"
#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
#include "webrtc/modules/desktop_capture/win/window_capture_utils.h"
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
namespace {
// Used to pass input/output data during the EnumWindow call for verifying if
// the selected window is on top.
struct TopWindowVerifierContext {
TopWindowVerifierContext(HWND selected_window, HWND excluded_window)
: selected_window(selected_window),
excluded_window(excluded_window),
is_top_window(false),
selected_window_process_id(0) {}
HWND selected_window;
HWND excluded_window;
bool is_top_window;
DWORD selected_window_process_id;
DesktopRect selected_window_rect;
};
// The function is called during EnumWindow for every window enumerated and is
// responsible for verifying if the selected window is on top.
BOOL CALLBACK TopWindowVerifier(HWND hwnd, LPARAM param) {
TopWindowVerifierContext* context =
reinterpret_cast<TopWindowVerifierContext*>(param);
if (hwnd == context->selected_window) {
context->is_top_window = true;
return FALSE;
}
// Ignore the excluded window.
if (hwnd == context->excluded_window) {
return TRUE;
}
// Ignore hidden or minimized window.
if (IsIconic(hwnd) || !IsWindowVisible(hwnd)) {
return TRUE;
}
// Ignore descendant/owned windows since we want to capture them.
// This check does not work for tooltips and context menus. Drop down menus
// and popup windows are fine.
if (GetAncestor(hwnd, GA_ROOTOWNER) == context->selected_window) {
return TRUE;
}
// If |hwnd| has no title and belongs to the same process, assume it's a
// tooltip or context menu from the selected window and ignore it.
const size_t kTitleLength = 32;
WCHAR window_title[kTitleLength];
GetWindowText(hwnd, window_title, kTitleLength);
if (wcsnlen_s(window_title, kTitleLength) == 0) {
DWORD enumerated_process;
GetWindowThreadProcessId(hwnd, &enumerated_process);
if (!context->selected_window_process_id) {
GetWindowThreadProcessId(context->selected_window,
&context->selected_window_process_id);
}
if (context->selected_window_process_id == enumerated_process) {
return TRUE;
}
}
// Check if the enumerated window intersects with the selected window.
RECT enumerated_rect;
if (!GetWindowRect(hwnd, &enumerated_rect)) {
// Bail out if failed to get the window area.
context->is_top_window = false;
return FALSE;
}
DesktopRect intersect_rect = context->selected_window_rect;
DesktopRect enumerated_desktop_rect =
DesktopRect::MakeLTRB(enumerated_rect.left,
enumerated_rect.top,
enumerated_rect.right,
enumerated_rect.bottom);
intersect_rect.IntersectWith(enumerated_desktop_rect);
// If intersection is not empty, the selected window is not on top.
if (!intersect_rect.is_empty()) {
context->is_top_window = false;
return FALSE;
}
// Otherwise, keep enumerating.
return TRUE;
}
class CroppingWindowCapturerWin : public CroppingWindowCapturer {
public:
CroppingWindowCapturerWin(
const DesktopCaptureOptions& options)
: CroppingWindowCapturer(options) {}
private:
virtual bool ShouldUseScreenCapturer() OVERRIDE;
virtual DesktopRect GetWindowRectInVirtualScreen() OVERRIDE;
// The region from GetWindowRgn in the desktop coordinate if the region is
// rectangular, or the rect from GetWindowRect if the region is not set.
DesktopRect window_region_rect_;
};
bool CroppingWindowCapturerWin::ShouldUseScreenCapturer() {
if (!rtc::IsWindows8OrLater())
return false;
// Check if the window is a translucent layered window.
HWND selected = reinterpret_cast<HWND>(selected_window());
LONG window_ex_style = GetWindowLong(selected, GWL_EXSTYLE);
if (window_ex_style & WS_EX_LAYERED) {
COLORREF color_ref_key = 0;
BYTE alpha = 0;
DWORD flags = 0;
// GetLayeredWindowAttributes fails if the window was setup with
// UpdateLayeredWindow. We have no way to know the opacity of the window in
// that case. This happens for Stiky Note (crbug/412726).
if (!GetLayeredWindowAttributes(selected, &color_ref_key, &alpha, &flags))
return false;
// UpdateLayeredWindow is the only way to set per-pixel alpha and will cause
// the previous GetLayeredWindowAttributes to fail. So we only need to check
// the window wide color key or alpha.
if ((flags & LWA_COLORKEY) || ((flags & LWA_ALPHA) && (alpha < 255)))
return false;
}
TopWindowVerifierContext context(
selected, reinterpret_cast<HWND>(excluded_window()));
RECT selected_window_rect;
if (!GetWindowRect(selected, &selected_window_rect)) {
return false;
}
context.selected_window_rect = DesktopRect::MakeLTRB(
selected_window_rect.left,
selected_window_rect.top,
selected_window_rect.right,
selected_window_rect.bottom);
// Get the window region and check if it is rectangular.
win::ScopedGDIObject<HRGN, win::DeleteObjectTraits<HRGN> >
scoped_hrgn(CreateRectRgn(0, 0, 0, 0));
int region_type = GetWindowRgn(selected, scoped_hrgn.Get());
// Do not use the screen capturer if the region is empty or not rectangular.
if (region_type == COMPLEXREGION || region_type == NULLREGION)
return false;
if (region_type == SIMPLEREGION) {
RECT region_rect;
GetRgnBox(scoped_hrgn.Get(), &region_rect);
DesktopRect rgn_rect =
DesktopRect::MakeLTRB(region_rect.left,
region_rect.top,
region_rect.right,
region_rect.bottom);
rgn_rect.Translate(context.selected_window_rect.left(),
context.selected_window_rect.top());
context.selected_window_rect.IntersectWith(rgn_rect);
}
window_region_rect_ = context.selected_window_rect;
// Check if the window is occluded by any other window, excluding the child
// windows, context menus, and |excluded_window_|.
EnumWindows(&TopWindowVerifier, reinterpret_cast<LPARAM>(&context));
return context.is_top_window;
}
DesktopRect CroppingWindowCapturerWin::GetWindowRectInVirtualScreen() {
DesktopRect original_rect;
DesktopRect window_rect;
HWND hwnd = reinterpret_cast<HWND>(selected_window());
if (!GetCroppedWindowRect(hwnd, &window_rect, &original_rect)) {
LOG(LS_WARNING) << "Failed to get window info: " << GetLastError();
return window_rect;
}
window_rect.IntersectWith(window_region_rect_);
// Convert |window_rect| to be relative to the top-left of the virtual screen.
DesktopRect screen_rect(GetScreenRect(kFullDesktopScreenId, L""));
window_rect.IntersectWith(screen_rect);
window_rect.Translate(-screen_rect.left(), -screen_rect.top());
return window_rect;
}
} // namespace
// static
WindowCapturer*
CroppingWindowCapturer::Create(const DesktopCaptureOptions& options) {
return new CroppingWindowCapturerWin(options);
}
} // namespace webrtc

View File

@ -16,6 +16,11 @@
'<(webrtc_root)/base/base.gyp:rtc_base',
],
'sources': [
'cropped_desktop_frame.cc',
'cropped_desktop_frame.h',
'cropping_window_capturer.cc',
'cropping_window_capturer.h',
'cropping_window_capturer_win.cc',
"desktop_and_cursor_composer.cc",
"desktop_and_cursor_composer.h",
"desktop_capture_types.h",

View File

@ -32,8 +32,7 @@ void DesktopFrame::CopyPixelsFrom(uint8_t* src_buffer, int src_stride,
const DesktopRect& dest_rect) {
assert(DesktopRect::MakeSize(size()).ContainsRect(dest_rect));
uint8_t* dest = data() + stride() * dest_rect.top() +
DesktopFrame::kBytesPerPixel * dest_rect.left();
uint8_t* dest = GetFrameDataAtPos(dest_rect.top_left());
for (int y = 0; y < dest_rect.height(); ++y) {
memcpy(dest, src_buffer, DesktopFrame::kBytesPerPixel * dest_rect.width());
src_buffer += src_stride;
@ -47,11 +46,14 @@ void DesktopFrame::CopyPixelsFrom(const DesktopFrame& src_frame,
assert(DesktopRect::MakeSize(src_frame.size()).ContainsRect(
DesktopRect::MakeOriginSize(src_pos, dest_rect.size())));
CopyPixelsFrom(src_frame.data() + src_frame.stride() * src_pos.y() +
DesktopFrame::kBytesPerPixel * src_pos.x(),
CopyPixelsFrom(src_frame.GetFrameDataAtPos(src_pos),
src_frame.stride(), dest_rect);
}
uint8_t* DesktopFrame::GetFrameDataAtPos(const DesktopVector& pos) const {
return data() + stride() * pos.y() + DesktopFrame::kBytesPerPixel * pos.x();
}
BasicDesktopFrame::BasicDesktopFrame(DesktopSize size)
: DesktopFrame(size, kBytesPerPixel * size.width(),
new uint8_t[kBytesPerPixel * size.width() * size.height()],

View File

@ -67,6 +67,9 @@ class DesktopFrame {
const DesktopVector& src_pos,
const DesktopRect& dest_rect);
// A helper to return the data pointer of a frame at the specified position.
uint8_t* GetFrameDataAtPos(const DesktopVector& pos) const;
protected:
DesktopFrame(DesktopSize size,
int stride,

View File

@ -52,11 +52,7 @@ class WindowCapturer : public DesktopCapturer {
// Bring the selected window to the front. Returns false in case of a
// failure or no window selected.
// TODO(jiayl): remove the default impl when FakeWindowCapturer is updated in
// Chromium.
virtual bool BringSelectedWindowToFront() {
return true;
}
virtual bool BringSelectedWindowToFront() = 0;
};
} // namespace webrtc