6#include <private/qabstractvideobuffer_p.h>
10#include <QtCore/private/qfactorycacheregistration_p.h>
15template <
typename Async>
16auto wait_for(Async
const& async, Windows::Foundation::TimeSpan
const&
timeout);
18#include <winrt/Windows.Foundation.Collections.h>
19#include <winrt/Windows.Graphics.Capture.h>
20#include <winrt/Windows.Graphics.DirectX.h>
21#include <winrt/Windows.Graphics.DirectX.Direct3D11.h>
22#include <Windows.Graphics.Capture.h>
23#include <Windows.Graphics.Capture.Interop.h>
24#include <windows.graphics.directx.direct3d11.interop.h>
28#include <lowlevelmonitorconfigurationapi.h>
29#include <physicalmonitorenumerationapi.h>
36#include <private/qmultimediautils_p.h>
37#include <private/qwindowsmultimediautils_p.h>
38#include <private/qcapturablewindow_p.h>
39#include <qpa/qplatformscreen_p.h>
42#include <system_error>
46using namespace winrt::Windows::Graphics::Capture;
47using namespace winrt::Windows::Graphics::DirectX;
48using namespace winrt::Windows::Graphics::DirectX::Direct3D11;
49using namespace Windows::Graphics::DirectX::Direct3D11;
52using winrt::check_hresult;
60winrt::Windows::Graphics::SizeInt32 getWindowSize(HWND hwnd)
63 ::GetWindowRect(hwnd, &windowRect);
65 return { windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
68QSize asQSize(winrt::Windows::Graphics::SizeInt32
size)
73struct MultithreadedApartment
75 MultithreadedApartment(
const MultithreadedApartment &) =
delete;
76 MultithreadedApartment &operator=(
const MultithreadedApartment &) =
delete;
78 MultithreadedApartment() { winrt::init_apartment(); }
79 ~MultithreadedApartment() { winrt::uninit_apartment(); }
85 QUwpTextureVideoBuffer(com_ptr<IDXGISurface> &&surface)
90 ~QUwpTextureVideoBuffer()
override { QUwpTextureVideoBuffer::unmap(); }
100 DXGI_MAPPED_RECT
rect = {};
103 DXGI_SURFACE_DESC
desc = {};
104 hr = m_surface->GetDesc(&desc);
108 md.bytesPerLine[0] =
rect.Pitch;
109 md.data[0] =
rect.pBits;
110 md.size[0] =
rect.Pitch *
desc.Height;
124 void unmap()
override
129 const HRESULT hr = m_surface->Unmap();
138 com_ptr<IDXGISurface> m_surface;
143 WindowGrabber() =
default;
145 WindowGrabber(IDXGIAdapter1 *adapter, HWND hwnd)
146 : m_frameSize{ getWindowSize(hwnd) }, m_captureWindow{ hwnd }
148 check_hresult(D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN,
nullptr, 0,
nullptr, 0,
149 D3D11_SDK_VERSION, m_device.put(),
nullptr,
nullptr));
151 const auto captureItem = createCaptureItem(hwnd);
153 m_framePool = Direct3D11CaptureFramePool::CreateFreeThreaded(
154 getCaptureDevice(m_device), m_pixelFormat, 1,
157 m_session = m_framePool.CreateCaptureSession(captureItem);
160 if (
const auto session2 =
m_session.try_as<IGraphicsCaptureSession2>())
161 session2.IsCursorCaptureEnabled(
true);
164 if (
const auto session3 =
m_session.try_as<IGraphicsCaptureSession3>())
165 session3.IsBorderRequired(
false);
176 com_ptr<IDXGISurface> tryGetFrame()
178 const Direct3D11CaptureFrame
frame = m_framePool.TryGetNextFrame();
185 if (!IsWindow(m_captureWindow))
186 throw std::runtime_error(
"Window was closed");
193 if (m_frameSize !=
frame.ContentSize()) {
194 m_frameSize =
frame.ContentSize();
195 m_framePool.Recreate(getCaptureDevice(m_device), m_pixelFormat, 1,
frame.ContentSize());
199 return copyTexture(m_device,
frame.Surface());
203 static GraphicsCaptureItem createCaptureItem(HWND hwnd)
205 const auto factory = winrt::get_activation_factory<GraphicsCaptureItem>();
206 const auto interop =
factory.as<IGraphicsCaptureItemInterop>();
208 GraphicsCaptureItem
item = {
nullptr };
209 winrt::hresult status = S_OK;
214 constexpr int maxRetry = 10;
215 constexpr std::chrono::milliseconds retryDelay{ 100 };
216 for (
int retryNum = 0; retryNum < maxRetry; ++retryNum) {
218 status = interop->CreateForWindow(hwnd, winrt::guid_of<GraphicsCaptureItem>(),
219 winrt::put_abi(
item));
221 if (status != E_INVALIDARG)
225 <<
"Failed to create capture item:"
227 <<
"Retry number" << retryNum;
229 if (retryNum + 1 < maxRetry)
234 check_hresult(status);
239 static IDirect3DDevice getCaptureDevice(
const com_ptr<ID3D11Device> &d3dDevice)
241 const auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
243 com_ptr<IInspectable>
device;
244 check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.get(),
device.put()));
246 return device.as<IDirect3DDevice>();
249 static com_ptr<IDXGISurface> copyTexture(
const com_ptr<ID3D11Device> &
device,
250 const IDirect3DSurface &capturedTexture)
252 const auto dxgiInterop{ capturedTexture.as<IDirect3DDxgiInterfaceAccess>() };
256 com_ptr<IDXGISurface> dxgiSurface;
257 check_hresult(dxgiInterop->GetInterface(guid_of<IDXGISurface>(), dxgiSurface.put_void()));
259 DXGI_SURFACE_DESC
desc = {};
260 check_hresult(dxgiSurface->GetDesc(&desc));
262 D3D11_TEXTURE2D_DESC texDesc = {};
263 texDesc.Width =
desc.Width;
264 texDesc.Height =
desc.Height;
265 texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
266 texDesc.Usage = D3D11_USAGE_STAGING;
267 texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
268 texDesc.MiscFlags = 0;
269 texDesc.BindFlags = 0;
270 texDesc.ArraySize = 1;
271 texDesc.MipLevels = 1;
272 texDesc.SampleDesc = { 1, 0 };
274 com_ptr<ID3D11Texture2D>
texture;
275 check_hresult(
device->CreateTexture2D(&texDesc,
nullptr,
texture.put()));
277 com_ptr<ID3D11DeviceContext>
ctx;
279 ctx->CopyResource(
texture.get(), dxgiSurface.as<ID3D11Resource>().get());
281 return texture.as<IDXGISurface>();
284 MultithreadedApartment m_comApartment{};
285 HWND m_captureWindow{};
286 winrt::Windows::Graphics::SizeInt32 m_frameSize{};
287 com_ptr<ID3D11Device> m_device;
288 Direct3D11CaptureFramePool m_framePool{
nullptr };
289 GraphicsCaptureSession
m_session{
nullptr };
290 const DirectXPixelFormat m_pixelFormat = DirectXPixelFormat::R8G8B8A8UIntNormalized;
304 const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
305 m_adapter = getAdapter(monitor);
307 const qreal refreshRate = getMonitorRefreshRateHz(monitor);
324 if (!m_adapter || !IsWindow(m_hwnd))
328 m_windowGrabber = std::make_unique<WindowGrabber>(m_adapter.get(), m_hwnd);
331 }
catch (
const winrt::hresult_error &err) {
343 m_windowGrabber =
nullptr;
349 com_ptr<IDXGISurface>
texture = m_windowGrabber->tryGetFrame();
359 }
catch (
const winrt::hresult_error &err) {
365 }
catch (
const std::runtime_error& e) {
373 static com_ptr<IDXGIAdapter1> getAdapter(HMONITOR
handle)
375 com_ptr<IDXGIFactory1>
factory;
376 check_hresult(CreateDXGIFactory1(guid_of<IDXGIFactory1>(),
factory.put_void()));
378 com_ptr<IDXGIAdapter1> adapter;
379 for (
quint32 i = 0;
factory->EnumAdapters1(
i, adapter.put()) == S_OK; adapter =
nullptr,
i++) {
380 com_ptr<IDXGIOutput>
output;
382 DXGI_OUTPUT_DESC desc = {};
384 if (hr == S_OK && desc.Monitor ==
handle)
391 static QSize getTextureSize(
const com_ptr<IDXGISurface> &surf)
396 DXGI_SURFACE_DESC
desc;
397 check_hresult(surf->GetDesc(&desc));
399 return {
static_cast<int>(
desc.Width),
static_cast<int>(
desc.Height) };
402 static qreal getMonitorRefreshRateHz(HMONITOR
handle)
405 if (GetNumberOfPhysicalMonitorsFromHMONITOR(
handle, &
count)) {
408 for (
const auto &monitor :
std::as_const(
monitors)) {
409 MC_TIMING_REPORT screenTiming = {};
410 if (GetTimingReport(monitor.hPhysicalMonitor, &screenTiming)) {
414 return static_cast<qreal>(screenTiming.dwVerticalFrequencyInHZ) / 100.0;
423 com_ptr<IDXGIAdapter1> m_adapter{};
424 std::unique_ptr<WindowGrabber> m_windowGrabber;
430 qCDebug(qLcWindowCaptureUwp) <<
"Creating UWP screen capture";
438 return "Invalid window handle";
440 if (hwnd == GetShellWindow())
441 return "Cannot capture the shell window";
446 return "Cannot capture windows without a class name";
448 if (!IsWindowVisible(hwnd))
449 return "Cannot capture invisible windows";
451 if (GetAncestor(hwnd, GA_ROOT) != hwnd)
452 return "Can only capture root windows";
454 const LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
455 if (style & WS_DISABLED)
456 return "Cannot capture disabled windows";
458 const LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
459 if (exStyle & WS_EX_TOOLWINDOW)
460 return "No tooltips";
462 DWORD cloaked = FALSE;
463 const HRESULT hr = DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked,
sizeof(cloaked));
464 if (SUCCEEDED(hr) && cloaked == DWM_CLOAKED_SHELL)
465 return "Cannot capture cloaked windows";
472 if (
static_cast<bool>(m_grabber) == active)
480 const auto window = source<WindowSource>();
483 const auto hwnd =
reinterpret_cast<HWND
>(
handle ?
handle->id : 0);
489 m_grabber = std::make_unique<Grabber>(*
this, hwnd);
497 return GraphicsCaptureSession::IsSupported();
503 return m_grabber->frameFormat();
509#include "qffmpegwindowcapture_uwp.moc"
AVFCameraSession * m_session
IOBluetoothDevice * device
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
static const QCapturableWindowPrivate * handle(const QCapturableWindow &window)
void setFrameRate(qreal rate)
virtual void initializeGrabbingContext()
void errorUpdated(QPlatformSurfaceCapture::Error error, const QString &description)
virtual void finalizeGrabbingContext()
void addFrameCallback(Object &object, Method method)
void updateError(QPlatformSurfaceCapture::Error error, const QString &description={})
QVideoFrameFormat frameFormat() const
QVideoFrame grabFrame() override
Grabber(QFFmpegWindowCaptureUwp &capture, HWND hwnd)
void finalizeGrabbingContext() override
void initializeGrabbingContext() override
bool setActiveInternal(bool active) override
QFFmpegWindowCaptureUwp()
static bool isSupported()
~QFFmpegWindowCaptureUwp() override
QVideoFrameFormat frameFormat() const override
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
static QString fromStdWString(const std::wstring &s)
Returns a copy of the str string.
static void sleep(unsigned long)
The QVideoFrame class represents a frame of video data.
MapMode
Enumerates how a video buffer's data is mapped to system memory.
QMap< QString, QString > map
[6]
Combined button and popup list for selecting options.
auto wait_for(Async const &async, Windows::Foundation::TimeSpan const &timeout)
static constexpr qreal DefaultScreenCaptureFrameRate
static QString isCapturableWindow(HWND hwnd)
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLbitfield GLuint64 timeout
[4]
GLuint GLsizei const GLchar * message
QLatin1StringView QLatin1String
QT_BEGIN_NAMESPACE typedef uchar * output
const char className[16]
[1]
QItemEditorFactory * factory