7#include <private/qabstractvideobuffer_p.h>
8#include <private/qmultimediautils_p.h>
9#include <private/qwindowsmultimediautils_p.h>
10#include <qpa/qplatformscreen_p.h>
20#include <system_error>
26static Q_LOGGING_CATEGORY(qLcScreenCaptureDxgi,
"qt.multimedia.ffmpeg.screencapturedxgi")
28using namespace
std::chrono;
30using namespace
Qt::StringLiterals;
39 ComStatus() =
default;
40 ComStatus(
HRESULT hr) : m_hr{ hr } { }
43 ComStatus(
const ComStatus &) =
default;
44 ComStatus(ComStatus &&) =
default;
45 ComStatus &operator=(
const ComStatus &) =
default;
46 ComStatus &operator=(ComStatus &&) =
default;
48 explicit operator bool()
const {
return m_hr == S_OK; }
60 std::optional<QString> m_msg;
94 D3D11_TEXTURE2D_DESC texDesc = {};
95 m_texture->GetDesc(&texDesc);
96 texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
97 texDesc.Usage = D3D11_USAGE_STAGING;
98 texDesc.MiscFlags = 0;
99 texDesc.BindFlags = 0;
101 HRESULT hr = m_device->CreateTexture2D(&texDesc,
nullptr, m_cpuTexture.GetAddressOf());
103 qCDebug(qLcScreenCaptureDxgi) <<
"Failed to create texture with CPU access"
104 << std::system_category().message(hr).c_str();
105 qCDebug(qLcScreenCaptureDxgi) << m_device->GetDeviceRemovedReason();
109 m_device->GetImmediateContext(m_ctx.GetAddressOf());
111 m_ctx->CopyResource(m_cpuTexture.Get(), m_texture.Get());
113 D3D11_MAPPED_SUBRESOURCE resource = {};
114 hr = m_ctx->Map(m_cpuTexture.Get(), 0, D3D11_MAP_READ, 0, &resource);
115 m_ctxMutex->unlock();
117 qCDebug(qLcScreenCaptureDxgi) <<
"Failed to map texture" << m_cpuTexture.Get()
118 << std::system_category().message(hr).c_str();
138 m_ctx->Unmap(m_cpuTexture.Get(), 0);
139 m_ctxMutex->unlock();
142 m_cpuTexture.Reset();
147 ComPtr<ID3D11Device> m_device;
148 ComPtr<ID3D11Texture2D> m_texture;
149 ComPtr<ID3D11Texture2D> m_cpuTexture;
150 ComPtr<ID3D11DeviceContext> m_ctx;
151 std::shared_ptr<QMutex> m_ctxMutex;
161 ComPtr<IDXGIAdapter1> adapter;
162 ComPtr<IDXGIOutput>
output;
169 m_dup->ReleaseFrame();
176 return dxgiScreen.
error();
178 const ComPtr<IDXGIAdapter1> adapter = dxgiScreen->adapter;
180 ComPtr<ID3D11Device> d3d11dev;
182 D3D11CreateDevice(adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN,
nullptr, 0,
nullptr, 0,
183 D3D11_SDK_VERSION, d3d11dev.GetAddressOf(),
nullptr,
nullptr);
185 return { hr,
"Failed to create ID3D11Device device"_L1 };
187 ComPtr<IDXGIOutput1>
output;
188 hr = dxgiScreen->output.As(&
output);
190 return { hr,
"Failed to create IDXGIOutput1"_L1 };
192 ComPtr<IDXGIOutputDuplication> dup;
193 hr =
output->DuplicateOutput(d3d11dev.Get(), dup.GetAddressOf());
195 return { hr,
"Failed to duplicate IDXGIOutput1"_L1 };
197 m_adapter = dxgiScreen->adapter;
204 bool valid()
const {
return m_dup !=
nullptr; }
206 QSize getFrameSize()
const
208 DXGI_OUTDUPL_DESC outputDesc = {};
209 m_dup->GetDesc(&outputDesc);
211 return {
static_cast<int>(outputDesc.ModeDesc.Width),
212 static_cast<int>(outputDesc.ModeDesc.Height) };
217 const ComProduct<ID3D11Texture2D>
texture = getNextFrame();
222 return std::make_unique<QD3D11TextureVideoBuffer>(m_device, m_ctxMutex, *
texture,
227 ComProduct<ID3D11Texture2D> getNextFrame()
229 std::scoped_lock guard{ *m_ctxMutex };
231 if (m_releaseFrame) {
232 m_releaseFrame =
false;
234 HRESULT hr = m_dup->ReleaseFrame();
237 return ComStatus{ hr,
"Failed to release duplication frame."_L1 };
240 ComPtr<IDXGIResource>
frame;
241 DXGI_OUTDUPL_FRAME_INFO
info;
246 return {
unexpect, hr,
"Failed to grab the screen content"_L1 };
248 m_releaseFrame =
true;
250 ComPtr<ID3D11Texture2D> tex;
253 return {
unexpect, hr,
"Failed to obtain D3D11 texture"_L1 };
255 D3D11_TEXTURE2D_DESC texDesc = {};
256 tex->GetDesc(&texDesc);
257 texDesc.MiscFlags = 0;
258 texDesc.BindFlags = 0;
260 ComPtr<ID3D11Texture2D> texCopy;
261 hr = m_device->CreateTexture2D(&texDesc,
nullptr, texCopy.GetAddressOf());
263 return {
unexpect, hr,
"Failed to create texture with CPU access"_L1 };
265 ComPtr<ID3D11DeviceContext>
ctx;
266 m_device->GetImmediateContext(
ctx.GetAddressOf());
267 ctx->CopyResource(texCopy.Get(), tex.Get());
275 return {
unexpect, E_FAIL,
"Cannot find nullptr screen"_L1 };
278 HMONITOR
handle = winScreen ? winScreen->handle() :
nullptr;
283 return {
unexpect, hr,
"Failed to create IDXGIFactory"_L1 };
285 ComPtr<IDXGIAdapter1> adapter;
286 for (
quint32 i = 0;
factory->EnumAdapters1(
i, adapter.ReleaseAndGetAddressOf()) == S_OK;
i++) {
287 ComPtr<IDXGIOutput>
output;
288 for (
quint32 j = 0; adapter->EnumOutputs(
j,
output.ReleaseAndGetAddressOf()) == S_OK; ++
j) {
289 DXGI_OUTPUT_DESC
desc = {};
295 return DxgiScreen{ adapter,
output };
298 return {
unexpect, DXGI_ERROR_NOT_FOUND,
299 "Could not find screen adapter "_L1 +
screen->
name() };
302 ComPtr<IDXGIAdapter1> m_adapter;
303 ComPtr<IDXGIOutput> m_output;
304 ComPtr<ID3D11Device> m_device;
305 ComPtr<IDXGIOutputDuplication> m_dup;
306 bool m_releaseFrame =
false;
307 std::shared_ptr<QMutex> m_ctxMutex = std::make_shared<QMutex>();
329 m_duplication = DxgiDuplication();
330 const ComStatus status = m_duplication.initialize(m_screen);
335 m_formatMutex.
lock();
351 m_waitForFormat.
wait(&m_formatMutex);
358 if (!m_duplication.valid()) {
359 const ComStatus status = m_duplication.initialize(m_screen);
361 if (status.code() == E_ACCESSDENIED) {
364 qCWarning(qLcScreenCaptureDxgi) << status.str();
370 auto maybeBuf = m_duplication.getNextVideoFrame();
371 const ComStatus &status = maybeBuf.error();
373 if (status.code() == DXGI_ERROR_WAIT_TIMEOUT) {
376 }
else if (status.code() == DXGI_ERROR_ACCESS_LOST) {
380 qCWarning(qLcScreenCaptureDxgi) << status.str();
381 }
else if (!status) {
383 qCWarning(qLcScreenCaptureDxgi) << status.str();
384 }
else if (maybeBuf) {
385 std::unique_ptr<QD3D11TextureVideoBuffer>
buffer = std::move(*maybeBuf);
393 QScreen *
const m_screen =
nullptr;
397 DxgiDuplication m_duplication;
407 return m_grabber->format();
413 if (
static_cast<bool>(m_grabber) == active)
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)
QVideoFrame grabFrame() override
QVideoFrameFormat format()
QVideoFrameFormat frameFormat() const override
~QFFmpegScreenCaptureDxgi() override
bool setActiveInternal(bool active) override
QFFmpegScreenCaptureDxgi()
void addFrameCallback(Object &object, Method method)
void errorUpdated(QPlatformSurfaceCapture::Error error, const QString &description)
void setFrameRate(qreal rate)
constexpr const Error & error() const
void unlock() noexcept
Unlocks the mutex.
void lock() noexcept
Locks the mutex.
Native interface to QScreen, to be retrieved from QPlatformIntegration. \inmodule QtGui.
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.
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
Combined button and popup list for selecting options.
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLsizei GLsizei GLchar * source
static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame, unsigned char *baseAddress)
static constexpr QSize frameSize(const T &frame)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
QT_BEGIN_NAMESPACE typedef uchar * output
QFileInfo info(fileName)
[8]
QItemEditorFactory * factory
char * toString(const MyType &t)
[31]