6#include <private/qabstractvideobuffer_p.h>
7#include <private/qmultimediautils_p.h>
8#include <private/qwindowsmultimediautils_p.h>
9#include <qtgui/qscreen_platform.h>
19#include <system_error>
27static Q_LOGGING_CATEGORY(qLcScreenCaptureDxgi,
"qt.multimedia.ffmpeg.screencapturedxgi")
29using namespace
std::chrono;
31using namespace
Qt::StringLiterals;
40 ComStatus() =
default;
41 ComStatus(
HRESULT hr) : m_hr{ hr } { }
44 ComStatus(
const ComStatus &) =
default;
45 ComStatus(ComStatus &&) =
default;
46 ComStatus &operator=(
const ComStatus &) =
default;
47 ComStatus &operator=(ComStatus &&) =
default;
49 explicit operator bool()
const {
return m_hr == S_OK; }
61 std::optional<QString> m_msg;
65using ComProduct = QMaybe<ComPtr<T>, ComStatus>;
95 D3D11_TEXTURE2D_DESC texDesc = {};
96 m_texture->GetDesc(&texDesc);
97 texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
98 texDesc.Usage = D3D11_USAGE_STAGING;
99 texDesc.MiscFlags = 0;
100 texDesc.BindFlags = 0;
102 HRESULT hr = m_device->CreateTexture2D(&texDesc,
nullptr, m_cpuTexture.GetAddressOf());
104 qCDebug(qLcScreenCaptureDxgi) <<
"Failed to create texture with CPU access"
105 << std::system_category().message(hr).c_str();
106 qCDebug(qLcScreenCaptureDxgi) << m_device->GetDeviceRemovedReason();
110 m_device->GetImmediateContext(m_ctx.GetAddressOf());
112 m_ctx->CopyResource(m_cpuTexture.Get(), m_texture.Get());
114 D3D11_MAPPED_SUBRESOURCE resource = {};
115 hr = m_ctx->Map(m_cpuTexture.Get(), 0, D3D11_MAP_READ, 0, &resource);
116 m_ctxMutex->unlock();
118 qCDebug(qLcScreenCaptureDxgi) <<
"Failed to map texture" << m_cpuTexture.Get()
119 << std::system_category().message(hr).c_str();
125 mapData.bytesPerLine[0] = int(resource.RowPitch);
126 mapData.data[0] =
reinterpret_cast<uchar*
>(resource.pData);
139 m_ctx->Unmap(m_cpuTexture.Get(), 0);
140 m_ctxMutex->unlock();
143 m_cpuTexture.Reset();
152 D3D11_TEXTURE2D_DESC desc{};
153 m_texture->GetDesc(&desc);
155 return {
static_cast<int>(desc.Width),
static_cast<int>(desc.Height) };
159 ComPtr<ID3D11Device> m_device;
160 ComPtr<ID3D11Texture2D> m_texture;
161 ComPtr<ID3D11Texture2D> m_cpuTexture;
162 ComPtr<ID3D11DeviceContext> m_ctx;
163 std::shared_ptr<QMutex> m_ctxMutex;
173 ComPtr<IDXGIAdapter1> adapter;
174 ComPtr<IDXGIOutput>
output;
181 m_dup->ReleaseFrame();
186 const QMaybe<DxgiScreen, ComStatus> dxgiScreen = findDxgiScreen(
screen);
188 return dxgiScreen.error();
190 const ComPtr<IDXGIAdapter1> adapter = dxgiScreen->adapter;
192 ComPtr<ID3D11Device> d3d11dev;
194 D3D11CreateDevice(adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN,
nullptr, 0,
nullptr, 0,
195 D3D11_SDK_VERSION, d3d11dev.GetAddressOf(),
nullptr,
nullptr);
197 return { hr,
"Failed to create ID3D11Device device"_L1 };
199 ComPtr<IDXGIOutput1>
output;
200 hr = dxgiScreen->output.As(&
output);
202 return { hr,
"Failed to create IDXGIOutput1"_L1 };
204 ComPtr<IDXGIOutputDuplication> dup;
205 hr =
output->DuplicateOutput(d3d11dev.Get(), dup.GetAddressOf());
207 return { hr,
"Failed to duplicate IDXGIOutput1"_L1 };
209 m_adapter = dxgiScreen->adapter;
216 bool valid()
const {
return m_dup !=
nullptr; }
218 QSize getFrameSize()
const
220 DXGI_OUTDUPL_DESC outputDesc = {};
221 m_dup->GetDesc(&outputDesc);
223 return {
static_cast<int>(outputDesc.ModeDesc.Width),
224 static_cast<int>(outputDesc.ModeDesc.Height) };
227 QMaybe<std::unique_ptr<QD3D11TextureVideoBuffer>, ComStatus> getNextVideoFrame()
229 const ComProduct<ID3D11Texture2D>
texture = getNextFrame();
234 return std::make_unique<QD3D11TextureVideoBuffer>(m_device, m_ctxMutex, *
texture,
239 ComProduct<ID3D11Texture2D> getNextFrame()
241 std::scoped_lock guard{ *m_ctxMutex };
243 if (m_releaseFrame) {
244 m_releaseFrame =
false;
246 HRESULT hr = m_dup->ReleaseFrame();
249 return ComStatus{ hr,
"Failed to release duplication frame."_L1 };
252 ComPtr<IDXGIResource>
frame;
253 DXGI_OUTDUPL_FRAME_INFO
info;
258 return {
unexpect, hr,
"Failed to grab the screen content"_L1 };
260 m_releaseFrame =
true;
262 ComPtr<ID3D11Texture2D> tex;
265 return {
unexpect, hr,
"Failed to obtain D3D11 texture"_L1 };
267 D3D11_TEXTURE2D_DESC texDesc = {};
268 tex->GetDesc(&texDesc);
269 texDesc.MiscFlags = 0;
270 texDesc.BindFlags = 0;
272 ComPtr<ID3D11Texture2D> texCopy;
273 hr = m_device->CreateTexture2D(&texDesc,
nullptr, texCopy.GetAddressOf());
275 return {
unexpect, hr,
"Failed to create texture with CPU access"_L1 };
277 ComPtr<ID3D11DeviceContext>
ctx;
278 m_device->GetImmediateContext(
ctx.GetAddressOf());
279 ctx->CopyResource(texCopy.Get(), tex.Get());
284 static QMaybe<DxgiScreen, ComStatus> findDxgiScreen(
const QScreen *
screen)
287 return {
unexpect, E_FAIL,
"Cannot find nullptr screen"_L1 };
290 HMONITOR
handle = winScreen ? winScreen->handle() :
nullptr;
295 return {
unexpect, hr,
"Failed to create IDXGIFactory"_L1 };
297 ComPtr<IDXGIAdapter1> adapter;
298 for (
quint32 i = 0;
factory->EnumAdapters1(
i, adapter.ReleaseAndGetAddressOf()) == S_OK;
i++) {
299 ComPtr<IDXGIOutput>
output;
300 for (
quint32 j = 0; adapter->EnumOutputs(
j,
output.ReleaseAndGetAddressOf()) == S_OK; ++
j) {
301 DXGI_OUTPUT_DESC
desc = {};
307 return DxgiScreen{ adapter,
output };
310 return {
unexpect, DXGI_ERROR_NOT_FOUND,
311 "Could not find screen adapter "_L1 +
screen->
name() };
314 ComPtr<IDXGIAdapter1> m_adapter;
315 ComPtr<IDXGIOutput> m_output;
316 ComPtr<ID3D11Device> m_device;
317 ComPtr<IDXGIOutputDuplication> m_dup;
318 bool m_releaseFrame =
false;
319 std::shared_ptr<QMutex> m_ctxMutex = std::make_shared<QMutex>();
328 const HMONITOR
handle = winScreen->handle();
338 return {
info.rcMonitor.right -
info.rcMonitor.left,
339 info.rcMonitor.bottom -
info.rcMonitor.top };
344 const QSize screenSize = getPhysicalSizePixels(
screen);
378 if (!m_duplication.valid()) {
379 const ComStatus status = m_duplication.initialize(m_screen);
381 if (status.code() == E_ACCESSDENIED) {
384 qCWarning(qLcScreenCaptureDxgi) << status.str();
390 auto maybeBuf = m_duplication.getNextVideoFrame();
391 const ComStatus &status = maybeBuf.error();
393 if (status.code() == DXGI_ERROR_WAIT_TIMEOUT) {
396 }
else if (status.code() == DXGI_ERROR_ACCESS_LOST) {
400 qCWarning(qLcScreenCaptureDxgi) << status.str();
401 }
else if (!status) {
403 qCWarning(qLcScreenCaptureDxgi) << status.str();
404 }
else if (maybeBuf) {
405 std::unique_ptr<QD3D11TextureVideoBuffer>
buffer = std::move(*maybeBuf);
420 m_duplication = DxgiDuplication();
421 const ComStatus status = m_duplication.initialize(m_screen);
431 const QScreen *m_screen =
nullptr;
433 DxgiDuplication m_duplication;
443 return m_grabber->format();
449 if (
static_cast<bool>(m_grabber) == active)
455 auto screen = source<ScreenSource>();
IOBluetoothDevice * device
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
void unmap() override
Releases the memory mapped by the map() function.
MapData map(QVideoFrame::MapMode mode) override
Independently maps the planes of a video buffer to memory.
~QD3D11TextureVideoBuffer()
QVideoFrame::MapMode mapMode() const override
QD3D11TextureVideoBuffer(const ComPtr< ID3D11Device > &device, std::shared_ptr< QMutex > &mutex, const ComPtr< ID3D11Texture2D > &texture, QSize size)
Grabber(QFFmpegScreenCaptureDxgi &screenCapture, QScreen *screen, const QVideoFrameFormat &format)
QVideoFrame grabFrame() override
QVideoFrameFormat format()
void initializeGrabbingContext() override
QVideoFrameFormat frameFormat() const override
~QFFmpegScreenCaptureDxgi() override
bool setActiveInternal(bool active) override
QFFmpegScreenCaptureDxgi()
void setFrameRate(qreal rate)
virtual void initializeGrabbingContext()
void errorUpdated(QPlatformSurfaceCapture::Error error, const QString &description)
void addFrameCallback(Object &object, Method method)
void updateError(QPlatformSurfaceCapture::Error error, const QString &description={})
Native interface to a screen.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
The QScreen class is used to query screen properties. \inmodule QtGui.
qreal refreshRate
the approximate vertical refresh rate of the screen in Hz
QString name
a user presentable string representing the screen
constexpr int height() const noexcept
Returns the height.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
The QVideoFrame class represents a frame of video data.
MapMode
Enumerates how a video buffer's data is mapped to system memory.
Combined button and popup list for selecting options.
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
static constexpr QUnexpect unexpect
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum format
static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame, unsigned char *baseAddress)
QLatin1StringView QLatin1String
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
QT_BEGIN_NAMESPACE typedef uchar * output
QItemEditorFactory * factory
char * toString(const MyType &t)
[31]