wgc_capture_source.cc (6659B)
1 /* 2 * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/desktop_capture/win/wgc_capture_source.h" 12 13 #include <dwmapi.h> 14 #include <windows.graphics.capture.interop.h> 15 #include <windows.h> 16 17 #include <memory> 18 #include <utility> 19 20 #include "modules/desktop_capture/desktop_capturer.h" 21 #include "modules/desktop_capture/desktop_geometry.h" 22 #include "modules/desktop_capture/win/screen_capture_utils.h" 23 #include "modules/desktop_capture/win/window_capture_utils.h" 24 #include "rtc_base/win/get_activation_factory.h" 25 26 using Microsoft::WRL::ComPtr; 27 namespace WGC = ABI::Windows::Graphics::Capture; 28 29 namespace webrtc { 30 31 WgcCaptureSource::WgcCaptureSource(DesktopCapturer::SourceId source_id) 32 : source_id_(source_id) {} 33 WgcCaptureSource::~WgcCaptureSource() = default; 34 35 bool WgcCaptureSource::ShouldBeCapturable() { 36 return true; 37 } 38 39 bool WgcCaptureSource::IsCapturable() { 40 // If we can create a capture item, then we can capture it. Unfortunately, 41 // we can't cache this item because it may be created in a different COM 42 // apartment than where capture will eventually start from. 43 ComPtr<WGC::IGraphicsCaptureItem> item; 44 return SUCCEEDED(CreateCaptureItem(&item)); 45 } 46 47 bool WgcCaptureSource::FocusOnSource() { 48 return false; 49 } 50 51 ABI::Windows::Graphics::SizeInt32 WgcCaptureSource::GetSize() { 52 if (!item_) 53 return {0, 0}; 54 55 ABI::Windows::Graphics::SizeInt32 item_size; 56 HRESULT hr = item_->get_Size(&item_size); 57 if (FAILED(hr)) 58 return {0, 0}; 59 60 return item_size; 61 } 62 63 HRESULT WgcCaptureSource::GetCaptureItem( 64 ComPtr<WGC::IGraphicsCaptureItem>* result) { 65 HRESULT hr = S_OK; 66 if (!item_) 67 hr = CreateCaptureItem(&item_); 68 69 *result = item_; 70 return hr; 71 } 72 73 WgcCaptureSourceFactory::~WgcCaptureSourceFactory() = default; 74 75 WgcWindowSourceFactory::WgcWindowSourceFactory() = default; 76 WgcWindowSourceFactory::~WgcWindowSourceFactory() = default; 77 78 std::unique_ptr<WgcCaptureSource> WgcWindowSourceFactory::CreateCaptureSource( 79 DesktopCapturer::SourceId source_id) { 80 return std::make_unique<WgcWindowSource>(source_id); 81 } 82 83 WgcScreenSourceFactory::WgcScreenSourceFactory() = default; 84 WgcScreenSourceFactory::~WgcScreenSourceFactory() = default; 85 86 std::unique_ptr<WgcCaptureSource> WgcScreenSourceFactory::CreateCaptureSource( 87 DesktopCapturer::SourceId source_id) { 88 return std::make_unique<WgcScreenSource>(source_id); 89 } 90 91 WgcWindowSource::WgcWindowSource(DesktopCapturer::SourceId source_id) 92 : WgcCaptureSource(source_id) {} 93 WgcWindowSource::~WgcWindowSource() = default; 94 95 DesktopVector WgcWindowSource::GetTopLeft() { 96 DesktopRect window_rect; 97 if (!GetWindowRect(reinterpret_cast<HWND>(GetSourceId()), &window_rect)) 98 return DesktopVector(); 99 100 return window_rect.top_left(); 101 } 102 103 ABI::Windows::Graphics::SizeInt32 WgcWindowSource::GetSize() { 104 RECT window_rect; 105 HRESULT hr = ::DwmGetWindowAttribute( 106 reinterpret_cast<HWND>(GetSourceId()), DWMWA_EXTENDED_FRAME_BOUNDS, 107 reinterpret_cast<void*>(&window_rect), sizeof(window_rect)); 108 if (FAILED(hr)) 109 return WgcCaptureSource::GetSize(); 110 111 return {window_rect.right - window_rect.left, 112 window_rect.bottom - window_rect.top}; 113 } 114 115 bool WgcWindowSource::ShouldBeCapturable() { 116 return IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId())); 117 } 118 119 bool WgcWindowSource::IsCapturable() { 120 if (!ShouldBeCapturable()) { 121 return false; 122 } 123 124 return WgcCaptureSource::IsCapturable(); 125 } 126 127 bool WgcWindowSource::FocusOnSource() { 128 if (!IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId()))) 129 return false; 130 131 return ::BringWindowToTop(reinterpret_cast<HWND>(GetSourceId())) && 132 ::SetForegroundWindow(reinterpret_cast<HWND>(GetSourceId())); 133 } 134 135 HRESULT WgcWindowSource::CreateCaptureItem( 136 ComPtr<WGC::IGraphicsCaptureItem>* result) { 137 if (!ResolveCoreWinRTDelayload()) 138 return E_FAIL; 139 140 ComPtr<IGraphicsCaptureItemInterop> interop; 141 HRESULT hr = GetActivationFactory< 142 IGraphicsCaptureItemInterop, 143 RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop); 144 if (FAILED(hr)) 145 return hr; 146 147 ComPtr<WGC::IGraphicsCaptureItem> item; 148 hr = interop->CreateForWindow(reinterpret_cast<HWND>(GetSourceId()), 149 IID_PPV_ARGS(&item)); 150 if (FAILED(hr)) 151 return hr; 152 153 if (!item) 154 return E_HANDLE; 155 156 *result = std::move(item); 157 return hr; 158 } 159 160 WgcScreenSource::WgcScreenSource(DesktopCapturer::SourceId source_id) 161 : WgcCaptureSource(source_id) { 162 // Getting the HMONITOR could fail if the source_id is invalid. In that case, 163 // we leave hmonitor_ uninitialized and `IsCapturable()` will fail. 164 HMONITOR hmon; 165 if (GetHmonitorFromDeviceIndex(GetSourceId(), &hmon)) 166 hmonitor_ = hmon; 167 } 168 169 WgcScreenSource::~WgcScreenSource() = default; 170 171 DesktopVector WgcScreenSource::GetTopLeft() { 172 if (!hmonitor_) 173 return DesktopVector(); 174 175 return GetMonitorRect(*hmonitor_).top_left(); 176 } 177 178 ABI::Windows::Graphics::SizeInt32 WgcScreenSource::GetSize() { 179 ABI::Windows::Graphics::SizeInt32 size = WgcCaptureSource::GetSize(); 180 if (!hmonitor_ || (size.Width != 0 && size.Height != 0)) 181 return size; 182 183 DesktopRect rect = GetMonitorRect(*hmonitor_); 184 return {rect.width(), rect.height()}; 185 } 186 187 bool WgcScreenSource::IsCapturable() { 188 if (!hmonitor_) 189 return false; 190 191 if (!IsMonitorValid(*hmonitor_)) 192 return false; 193 194 return WgcCaptureSource::IsCapturable(); 195 } 196 197 HRESULT WgcScreenSource::CreateCaptureItem( 198 ComPtr<WGC::IGraphicsCaptureItem>* result) { 199 if (!hmonitor_) 200 return E_ABORT; 201 202 if (!ResolveCoreWinRTDelayload()) 203 return E_FAIL; 204 205 ComPtr<IGraphicsCaptureItemInterop> interop; 206 HRESULT hr = GetActivationFactory< 207 IGraphicsCaptureItemInterop, 208 RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop); 209 if (FAILED(hr)) 210 return hr; 211 212 // Ensure the monitor is still valid (hasn't disconnected) before trying to 213 // create the item. On versions of Windows before Win11, `CreateForMonitor` 214 // will crash if no displays are connected. 215 if (!IsMonitorValid(hmonitor_.value())) 216 return E_ABORT; 217 218 ComPtr<WGC::IGraphicsCaptureItem> item; 219 hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item)); 220 if (FAILED(hr)) 221 return hr; 222 223 if (!item) 224 return E_HANDLE; 225 226 *result = std::move(item); 227 return hr; 228 } 229 230 } // namespace webrtc