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