/* * Copyright (c) 2020 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/wgc_capture_source.h" #include #include #include #include #include "modules/desktop_capture/win/screen_capture_utils.h" #include "modules/desktop_capture/win/window_capture_utils.h" #include "rtc_base/win/get_activation_factory.h" using Microsoft::WRL::ComPtr; namespace WGC = ABI::Windows::Graphics::Capture; namespace webrtc { WgcCaptureSource::WgcCaptureSource(DesktopCapturer::SourceId source_id) : source_id_(source_id) {} WgcCaptureSource::~WgcCaptureSource() = default; bool WgcCaptureSource::ShouldBeCapturable() { return true; } bool WgcCaptureSource::IsCapturable() { // If we can create a capture item, then we can capture it. Unfortunately, // we can't cache this item because it may be created in a different COM // apartment than where capture will eventually start from. ComPtr item; return SUCCEEDED(CreateCaptureItem(&item)); } bool WgcCaptureSource::FocusOnSource() { return false; } ABI::Windows::Graphics::SizeInt32 WgcCaptureSource::GetSize() { if (!item_) return {0, 0}; ABI::Windows::Graphics::SizeInt32 item_size; HRESULT hr = item_->get_Size(&item_size); if (FAILED(hr)) return {0, 0}; return item_size; } HRESULT WgcCaptureSource::GetCaptureItem( ComPtr* result) { HRESULT hr = S_OK; if (!item_) hr = CreateCaptureItem(&item_); *result = item_; return hr; } WgcCaptureSourceFactory::~WgcCaptureSourceFactory() = default; WgcWindowSourceFactory::WgcWindowSourceFactory() = default; WgcWindowSourceFactory::~WgcWindowSourceFactory() = default; std::unique_ptr WgcWindowSourceFactory::CreateCaptureSource( DesktopCapturer::SourceId source_id) { return std::make_unique(source_id); } WgcScreenSourceFactory::WgcScreenSourceFactory() = default; WgcScreenSourceFactory::~WgcScreenSourceFactory() = default; std::unique_ptr WgcScreenSourceFactory::CreateCaptureSource( DesktopCapturer::SourceId source_id) { return std::make_unique(source_id); } WgcWindowSource::WgcWindowSource(DesktopCapturer::SourceId source_id) : WgcCaptureSource(source_id) {} WgcWindowSource::~WgcWindowSource() = default; DesktopVector WgcWindowSource::GetTopLeft() { DesktopRect window_rect; if (!GetWindowRect(reinterpret_cast(GetSourceId()), &window_rect)) return DesktopVector(); return window_rect.top_left(); } ABI::Windows::Graphics::SizeInt32 WgcWindowSource::GetSize() { RECT window_rect; HRESULT hr = ::DwmGetWindowAttribute( reinterpret_cast(GetSourceId()), DWMWA_EXTENDED_FRAME_BOUNDS, reinterpret_cast(&window_rect), sizeof(window_rect)); if (FAILED(hr)) return WgcCaptureSource::GetSize(); return {window_rect.right - window_rect.left, window_rect.bottom - window_rect.top}; } bool WgcWindowSource::ShouldBeCapturable() { return IsWindowValidAndVisible(reinterpret_cast(GetSourceId())); } bool WgcWindowSource::IsCapturable() { if (!ShouldBeCapturable()) { return false; } return WgcCaptureSource::IsCapturable(); } bool WgcWindowSource::FocusOnSource() { if (!IsWindowValidAndVisible(reinterpret_cast(GetSourceId()))) return false; return ::BringWindowToTop(reinterpret_cast(GetSourceId())) && ::SetForegroundWindow(reinterpret_cast(GetSourceId())); } HRESULT WgcWindowSource::CreateCaptureItem( ComPtr* result) { if (!ResolveCoreWinRTDelayload()) return E_FAIL; ComPtr interop; HRESULT hr = GetActivationFactory< IGraphicsCaptureItemInterop, RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop); if (FAILED(hr)) return hr; ComPtr item; hr = interop->CreateForWindow(reinterpret_cast(GetSourceId()), IID_PPV_ARGS(&item)); if (FAILED(hr)) return hr; if (!item) return E_HANDLE; *result = std::move(item); return hr; } WgcScreenSource::WgcScreenSource(DesktopCapturer::SourceId source_id) : WgcCaptureSource(source_id) { // Getting the HMONITOR could fail if the source_id is invalid. In that case, // we leave hmonitor_ uninitialized and `IsCapturable()` will fail. HMONITOR hmon; if (GetHmonitorFromDeviceIndex(GetSourceId(), &hmon)) hmonitor_ = hmon; } WgcScreenSource::~WgcScreenSource() = default; DesktopVector WgcScreenSource::GetTopLeft() { if (!hmonitor_) return DesktopVector(); return GetMonitorRect(*hmonitor_).top_left(); } ABI::Windows::Graphics::SizeInt32 WgcScreenSource::GetSize() { ABI::Windows::Graphics::SizeInt32 size = WgcCaptureSource::GetSize(); if (!hmonitor_ || (size.Width != 0 && size.Height != 0)) return size; DesktopRect rect = GetMonitorRect(*hmonitor_); return {rect.width(), rect.height()}; } bool WgcScreenSource::IsCapturable() { if (!hmonitor_) return false; if (!IsMonitorValid(*hmonitor_)) return false; return WgcCaptureSource::IsCapturable(); } HRESULT WgcScreenSource::CreateCaptureItem( ComPtr* result) { if (!hmonitor_) return E_ABORT; if (!ResolveCoreWinRTDelayload()) return E_FAIL; ComPtr interop; HRESULT hr = GetActivationFactory< IGraphicsCaptureItemInterop, RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop); if (FAILED(hr)) return hr; // Ensure the monitor is still valid (hasn't disconnected) before trying to // create the item. On versions of Windows before Win11, `CreateForMonitor` // will crash if no displays are connected. if (!IsMonitorValid(hmonitor_.value())) return E_ABORT; ComPtr item; hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item)); if (FAILED(hr)) return hr; if (!item) return E_HANDLE; *result = std::move(item); return hr; } } // namespace webrtc