diff --git a/modules/desktop_capture/window_capturer_win.cc b/modules/desktop_capture/window_capturer_win.cc index 3dbec83423..0c63893f3c 100644 --- a/modules/desktop_capture/window_capturer_win.cc +++ b/modules/desktop_capture/window_capturer_win.cc @@ -12,6 +12,7 @@ #include +#include "modules/desktop_capture/cropped_desktop_frame.h" #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_frame_win.h" #include "modules/desktop_capture/win/screen_capture_utils.h" @@ -91,34 +92,6 @@ BOOL CALLBACK WindowsEnumerationHandler(HWND hwnd, LPARAM param) { return TRUE; } -// Retrieves the rectangle of the window rect which is drawable by either OS or -// the owner application. The returned DesktopRect is in system coordinates. -// This function returns false if native APIs fail. -// -// When |window| is maximized, its borders and shadow effect will be ignored by -// OS and leave black. So we prefer to use GetCroppedWindowRect() when capturing -// its content to avoid the black area in the final DesktopFrame. But when the -// window is in normal mode, borders and shadow should be included. -bool GetWindowDrawableRect(HWND window, - DesktopRect* drawable_rect, - DesktopRect* original_rect) { - if (!GetWindowRect(window, original_rect)) { - return false; - } - - bool is_maximized = false; - if (!IsWindowMaximized(window, &is_maximized)) { - return false; - } - - if (is_maximized) { - return GetCroppedWindowRect(window, drawable_rect, - /* original_rect */ nullptr); - } - *drawable_rect = *original_rect; - return true; -} - class WindowCapturerWin : public DesktopCapturer { public: WindowCapturerWin(); @@ -235,7 +208,7 @@ void WindowCapturerWin::CaptureFrame() { DesktopRect cropped_rect; DesktopRect original_rect; - if (!GetWindowDrawableRect(window_, &cropped_rect, &original_rect)) { + if (!GetCroppedWindowRect(window_, &cropped_rect, &original_rect)) { RTC_LOG(LS_WARNING) << "Failed to get drawable window area: " << GetLastError(); callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); @@ -287,7 +260,7 @@ void WindowCapturerWin::CaptureFrame() { } std::unique_ptr frame( - DesktopFrameWin::Create(cropped_rect.size(), nullptr, window_dc)); + DesktopFrameWin::Create(original_rect.size(), nullptr, window_dc)); if (!frame.get()) { RTC_LOG(LS_WARNING) << "Failed to create frame."; ReleaseDC(window_, window_dc); @@ -315,17 +288,32 @@ void WindowCapturerWin::CaptureFrame() { // PrintWindow() whenever window size changes, including the first time of // capturing - it somehow affects what we get from BitBlt() on the subsequent // captures. + // + // For Windows 8.1 and later, we want to always use PrintWindow when the + // cropping screen capturer falls back to the window capturer. I.e. + // on Windows 8.1 and later, PrintWindow is only used when the window is + // occluded. When the window is not occluded, it is much faster to capture + // the screen and to crop it to the window position and size. + if (rtc::IsWindows8OrLater()) { + // Special flag that makes PrintWindow to work on Windows 8.1 and later. + // Indeed certain apps (e.g. those using DirectComposition rendering) can't + // be captured using BitBlt or PrintWindow without this flag. Note that on + // Windows 8.0 this flag is not supported so the block below will fallback + // to the other call to PrintWindow. It seems to be very tricky to detect + // Windows 8.0 vs 8.1 so a try/fallback is more approriate here. + const UINT flags = PW_RENDERFULLCONTENT; + result = PrintWindow(window_, mem_dc, flags); + } - if (!window_capture_helper_.IsAeroEnabled() || - !previous_size_.equals(frame->size())) { + if (!result && (!window_capture_helper_.IsAeroEnabled() || + !previous_size_.equals(frame->size()))) { result = PrintWindow(window_, mem_dc, 0); } // Aero is enabled or PrintWindow() failed, use BitBlt. if (!result) { result = BitBlt(mem_dc, 0, 0, frame->size().width(), frame->size().height(), - window_dc, cropped_rect.left() - original_rect.left(), - cropped_rect.top() - original_rect.top(), SRCCOPY); + window_dc, 0, 0, SRCCOPY); } SelectObject(mem_dc, previous_object); @@ -338,14 +326,20 @@ void WindowCapturerWin::CaptureFrame() { frame->mutable_updated_region()->SetRect( DesktopRect::MakeSize(frame->size())); frame->set_top_left( - cropped_rect.top_left().subtract(GetFullscreenRect().top_left())); + original_rect.top_left().subtract(GetFullscreenRect().top_left())); - if (result) { - callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); - } else { + if (!result) { RTC_LOG(LS_ERROR) << "Both PrintWindow() and BitBlt() failed."; callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); } + + // Rect for the data is relative to the first pixel of the frame. + cropped_rect.Translate(-original_rect.left(), -original_rect.top()); + std::unique_ptr cropped_frame = + CreateCroppedDesktopFrame(std::move(frame), cropped_rect); + RTC_DCHECK(cropped_frame); + + callback_->OnCaptureResult(Result::SUCCESS, std::move(cropped_frame)); } } // namespace