This reverts commit d1208c26b1cdb536fdec942207033711101d5d26. Reason for revert: This cl causes the crashing issue as in chromium:916961 at starting desktop capture on Windows. Original change's description: > Desktop capturer: Add OnDisplayChanged callback > > This adds support for a new DesktopCapturer::Callback method > OnDisplayChanged that is sent at the start of a desktop capture > session and whenever the display geometry changes. > > This cl adds the basic structure to call this api at the start > of the capture session. Currently Windows only. > > A follow-up cl will add support to call this whenever the display > geometry changes. > > Bug: webrtc:10122, chromium:915411 > Change-Id: Ie7283be5992454180daab1a60f58a3b2efdfed56 > Reviewed-on: https://webrtc-review.googlesource.com/c/114020 > Commit-Queue: Gary Kacmarcik <garykac@chromium.org> > Reviewed-by: Brave Yao <braveyao@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#26053} TBR=jamiewalch@chromium.org,braveyao@webrtc.org,braveyao@chromium.org,garykac@chromium.org # Not skipping CQ checks because original CL landed > 1 day ago. Bug: webrtc:10122, chromium:915411, chromium:916961 Change-Id: Id0471e01bb90bb5accdf58262ae2b130cf343ecd Reviewed-on: https://webrtc-review.googlesource.com/c/115433 Commit-Queue: Brave Yao <braveyao@webrtc.org> Reviewed-by: Brave Yao <braveyao@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26095}
231 lines
7.5 KiB
C++
231 lines
7.5 KiB
C++
/*
|
|
* 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 "modules/desktop_capture/win/screen_capturer_win_gdi.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "modules/desktop_capture/desktop_capture_options.h"
|
|
#include "modules/desktop_capture/desktop_frame.h"
|
|
#include "modules/desktop_capture/desktop_frame_win.h"
|
|
#include "modules/desktop_capture/desktop_region.h"
|
|
#include "modules/desktop_capture/mouse_cursor.h"
|
|
#include "modules/desktop_capture/win/cursor.h"
|
|
#include "modules/desktop_capture/win/desktop.h"
|
|
#include "modules/desktop_capture/win/screen_capture_utils.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "rtc_base/timeutils.h"
|
|
#include "rtc_base/trace_event.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
|
|
// Constants from dwmapi.h.
|
|
const UINT DWM_EC_DISABLECOMPOSITION = 0;
|
|
const UINT DWM_EC_ENABLECOMPOSITION = 1;
|
|
|
|
const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
|
|
|
|
} // namespace
|
|
|
|
ScreenCapturerWinGdi::ScreenCapturerWinGdi(
|
|
const DesktopCaptureOptions& options) {
|
|
if (options.disable_effects()) {
|
|
// Load dwmapi.dll dynamically since it is not available on XP.
|
|
if (!dwmapi_library_)
|
|
dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);
|
|
|
|
if (dwmapi_library_) {
|
|
composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
|
|
GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
|
|
}
|
|
}
|
|
}
|
|
|
|
ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
|
|
if (desktop_dc_)
|
|
ReleaseDC(NULL, desktop_dc_);
|
|
if (memory_dc_)
|
|
DeleteDC(memory_dc_);
|
|
|
|
// Restore Aero.
|
|
if (composition_func_)
|
|
(*composition_func_)(DWM_EC_ENABLECOMPOSITION);
|
|
|
|
if (dwmapi_library_)
|
|
FreeLibrary(dwmapi_library_);
|
|
}
|
|
|
|
void ScreenCapturerWinGdi::SetSharedMemoryFactory(
|
|
std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
|
|
shared_memory_factory_ = std::move(shared_memory_factory);
|
|
}
|
|
|
|
void ScreenCapturerWinGdi::CaptureFrame() {
|
|
TRACE_EVENT0("webrtc", "ScreenCapturerWinGdi::CaptureFrame");
|
|
int64_t capture_start_time_nanos = rtc::TimeNanos();
|
|
|
|
queue_.MoveToNextFrame();
|
|
RTC_DCHECK(!queue_.current_frame() || !queue_.current_frame()->IsShared());
|
|
|
|
// Make sure the GDI capture resources are up-to-date.
|
|
PrepareCaptureResources();
|
|
|
|
if (!CaptureImage()) {
|
|
RTC_LOG(WARNING) << "Failed to capture screen by GDI.";
|
|
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
|
|
return;
|
|
}
|
|
|
|
// Emit the current frame.
|
|
std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share();
|
|
frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
|
|
GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
|
|
frame->mutable_updated_region()->SetRect(
|
|
DesktopRect::MakeSize(frame->size()));
|
|
frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
|
|
rtc::kNumNanosecsPerMillisec);
|
|
frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinGdi);
|
|
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
|
|
}
|
|
|
|
bool ScreenCapturerWinGdi::GetSourceList(SourceList* sources) {
|
|
return webrtc::GetScreenList(sources);
|
|
}
|
|
|
|
bool ScreenCapturerWinGdi::SelectSource(SourceId id) {
|
|
bool valid = IsScreenValid(id, ¤t_device_key_);
|
|
if (valid)
|
|
current_screen_id_ = id;
|
|
return valid;
|
|
}
|
|
|
|
void ScreenCapturerWinGdi::Start(Callback* callback) {
|
|
RTC_DCHECK(!callback_);
|
|
RTC_DCHECK(callback);
|
|
|
|
callback_ = callback;
|
|
|
|
// Vote to disable Aero composited desktop effects while capturing. Windows
|
|
// will restore Aero automatically if the process exits. This has no effect
|
|
// under Windows 8 or higher. See crbug.com/124018.
|
|
if (composition_func_)
|
|
(*composition_func_)(DWM_EC_DISABLECOMPOSITION);
|
|
}
|
|
|
|
void ScreenCapturerWinGdi::PrepareCaptureResources() {
|
|
// Switch to the desktop receiving user input if different from the current
|
|
// one.
|
|
std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
|
|
if (input_desktop && !desktop_.IsSame(*input_desktop)) {
|
|
// Release GDI resources otherwise SetThreadDesktop will fail.
|
|
if (desktop_dc_) {
|
|
ReleaseDC(NULL, desktop_dc_);
|
|
desktop_dc_ = nullptr;
|
|
}
|
|
|
|
if (memory_dc_) {
|
|
DeleteDC(memory_dc_);
|
|
memory_dc_ = nullptr;
|
|
}
|
|
|
|
// If SetThreadDesktop() fails, the thread is still assigned a desktop.
|
|
// So we can continue capture screen bits, just from the wrong desktop.
|
|
desktop_.SetThreadDesktop(input_desktop.release());
|
|
|
|
// Re-assert our vote to disable Aero.
|
|
// See crbug.com/124018 and crbug.com/129906.
|
|
if (composition_func_) {
|
|
(*composition_func_)(DWM_EC_DISABLECOMPOSITION);
|
|
}
|
|
}
|
|
|
|
// If the display configurations have changed then recreate GDI resources.
|
|
if (display_configuration_monitor_.IsChanged()) {
|
|
if (desktop_dc_) {
|
|
ReleaseDC(NULL, desktop_dc_);
|
|
desktop_dc_ = nullptr;
|
|
}
|
|
if (memory_dc_) {
|
|
DeleteDC(memory_dc_);
|
|
memory_dc_ = nullptr;
|
|
}
|
|
}
|
|
|
|
if (!desktop_dc_) {
|
|
RTC_DCHECK(!memory_dc_);
|
|
|
|
// Create GDI device contexts to capture from the desktop into memory.
|
|
desktop_dc_ = GetDC(nullptr);
|
|
RTC_CHECK(desktop_dc_);
|
|
memory_dc_ = CreateCompatibleDC(desktop_dc_);
|
|
RTC_CHECK(memory_dc_);
|
|
|
|
// Make sure the frame buffers will be reallocated.
|
|
queue_.Reset();
|
|
}
|
|
}
|
|
|
|
bool ScreenCapturerWinGdi::CaptureImage() {
|
|
DesktopRect screen_rect =
|
|
GetScreenRect(current_screen_id_, current_device_key_);
|
|
if (screen_rect.is_empty()) {
|
|
RTC_LOG(LS_WARNING) << "Failed to get screen rect.";
|
|
return false;
|
|
}
|
|
|
|
DesktopSize size = screen_rect.size();
|
|
// If the current buffer is from an older generation then allocate a new one.
|
|
// Note that we can't reallocate other buffers at this point, since the caller
|
|
// may still be reading from them.
|
|
if (!queue_.current_frame() ||
|
|
!queue_.current_frame()->size().equals(screen_rect.size())) {
|
|
RTC_DCHECK(desktop_dc_);
|
|
RTC_DCHECK(memory_dc_);
|
|
|
|
std::unique_ptr<DesktopFrame> buffer = DesktopFrameWin::Create(
|
|
size, shared_memory_factory_.get(), desktop_dc_);
|
|
if (!buffer) {
|
|
RTC_LOG(LS_WARNING) << "Failed to create frame buffer.";
|
|
return false;
|
|
}
|
|
queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(buffer)));
|
|
}
|
|
queue_.current_frame()->set_top_left(
|
|
screen_rect.top_left().subtract(GetFullscreenRect().top_left()));
|
|
|
|
// Select the target bitmap into the memory dc and copy the rect from desktop
|
|
// to memory.
|
|
DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
|
|
queue_.current_frame()->GetUnderlyingFrame());
|
|
HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
|
|
if (!previous_object || previous_object == HGDI_ERROR) {
|
|
RTC_LOG(LS_WARNING) << "Failed to select current bitmap into memery dc.";
|
|
return false;
|
|
}
|
|
|
|
bool result = (BitBlt(memory_dc_, 0, 0, screen_rect.width(),
|
|
screen_rect.height(), desktop_dc_, screen_rect.left(),
|
|
screen_rect.top(), SRCCOPY | CAPTUREBLT) != FALSE);
|
|
if (!result) {
|
|
RTC_LOG_GLE(LS_WARNING) << "BitBlt failed";
|
|
}
|
|
|
|
// Select back the previously selected object to that the device contect
|
|
// could be destroyed independently of the bitmap if needed.
|
|
SelectObject(memory_dc_, previous_object);
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace webrtc
|