xref: /aosp_15_r20/external/webrtc/modules/desktop_capture/win/wgc_capture_source.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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