Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgdiwindowcapture.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include "qvideoframe.h"
8#include "private/qcapturablewindow_p.h"
9#include "private/qmemoryvideobuffer_p.h"
10
11#include <qt_windows.h>
12#include <QtCore/qloggingcategory.h>
13
14static Q_LOGGING_CATEGORY(qLcGdiWindowCapture, "qt.multimedia.ffmpeg.gdiwindowcapture");
15
17
19{
20public:
21 static std::unique_ptr<Grabber> create(QGdiWindowCapture &capture, HWND hWnd)
22 {
23 auto hdcWindow = GetDC(hWnd);
24 if (!hdcWindow) {
26 QLatin1String("Cannot create a window drawing context"));
27 return nullptr;
28 }
29
30 auto hdcMem = CreateCompatibleDC(hdcWindow);
31
32 if (!hdcMem) {
34 QLatin1String("Cannot create a compatible drawing context"));
35 return nullptr;
36 }
37
38 std::unique_ptr<Grabber> result(new Grabber(capture, hWnd, hdcWindow, hdcMem));
39 if (!result->update())
40 return nullptr;
41
42 result->start();
43 return result;
44 }
45
46 ~Grabber() override
47 {
48 stop();
49
50 if (m_hBitmap)
51 DeleteObject(m_hBitmap);
52
53 if (m_hdcMem)
54 DeleteDC(m_hdcMem);
55
56 if (m_hdcWindow)
57 ReleaseDC(m_hwnd, m_hdcWindow);
58 }
59
60 QVideoFrameFormat format() const { return m_format; }
61
62private:
63 Grabber(QGdiWindowCapture &capture, HWND hWnd, HDC hdcWindow, HDC hdcMem)
64 : m_hwnd(hWnd), m_hdcWindow(hdcWindow), m_hdcMem(hdcMem)
65 {
66 if (auto rate = GetDeviceCaps(hdcWindow, VREFRESH); rate > 0)
68
71 }
72
73 bool update()
74 {
75 RECT windowRect{};
76 if (!GetWindowRect(m_hwnd, &windowRect)) {
78 QLatin1String("Cannot get window size"));
79 return false;
80 }
81
82 const QSize size{ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
83
84 if (m_format.isValid() && size == m_format.frameSize() && m_hBitmap)
85 return true;
86
87 if (m_hBitmap)
88 DeleteObject(std::exchange(m_hBitmap, nullptr));
89
90 if (size.isEmpty()) {
91 m_format = {};
93 QLatin1String("Invalid window size"));
94 return false;
95 }
96
97 m_hBitmap = CreateCompatibleBitmap(m_hdcWindow, size.width(), size.height());
98
99 if (!m_hBitmap) {
100 m_format = {};
102 QLatin1String("Cannot create a compatible bitmap"));
103 return false;
104 }
105
107 format.setStreamFrameRate(frameRate());
108 m_format = format;
109 return true;
110 }
111
113 {
114 if (!update())
115 return {};
116
117 const auto oldBitmap = SelectObject(m_hdcMem, m_hBitmap);
118 auto deselect = qScopeGuard([&]() { SelectObject(m_hdcMem, oldBitmap); });
119
120 const auto size = m_format.frameSize();
121
122 if (!BitBlt(m_hdcMem, 0, 0, size.width(), size.height(), m_hdcWindow, 0, 0, SRCCOPY)) {
124 QLatin1String("Cannot copy image to the compatible DC"));
125 return {};
126 }
127
128 BITMAPINFO info{};
129 auto &header = info.bmiHeader;
130 header.biSize = sizeof(BITMAPINFOHEADER);
131 header.biWidth = size.width();
132 header.biHeight = -size.height(); // negative height to ensure top-down orientation
133 header.biPlanes = 1;
134 header.biBitCount = 32;
135 header.biCompression = BI_RGB;
136
137 const auto bytesPerLine = size.width() * 4;
138
139 QByteArray array(size.height() * bytesPerLine, Qt::Uninitialized);
140
141 const auto copiedHeight = GetDIBits(m_hdcMem, m_hBitmap, 0, size.height(), array.data(), &info, DIB_RGB_COLORS);
142 if (copiedHeight != size.height()) {
143 qCWarning(qLcGdiWindowCapture) << copiedHeight << "lines have been copied, expected:" << size.height();
144 // In practice, it might fail randomly first time after start. So we don't consider it as an error.
145 // TODO: investigate reasons and properly handle the error
146 // updateError(QPlatformSurfaceCapture::CaptureFailed,
147 // QLatin1String("Cannot get raw image data"));
148 return {};
149 }
150
151 if (header.biWidth != size.width() || header.biHeight != -size.height()
152 || header.biPlanes != 1 || header.biBitCount != 32 || header.biCompression != BI_RGB) {
154 QLatin1String("Output bitmap info is unexpected"));
155 return {};
156 }
157
158 return QVideoFrame(new QMemoryVideoBuffer(array, bytesPerLine), m_format);
159 }
160
161private:
162 HWND m_hwnd = {};
163 QVideoFrameFormat m_format;
164 HDC m_hdcWindow = {};
165 HDC m_hdcMem = {};
166 HBITMAP m_hBitmap = {};
167};
168
170
172
174{
175 return m_grabber ? m_grabber->format() : QVideoFrameFormat();
176}
177
179{
180 if (active == static_cast<bool>(m_grabber))
181 return true;
182
183 if (m_grabber) {
184 m_grabber.reset();
185 } else {
186 auto window = source<WindowSource>();
188
189 m_grabber = Grabber::create(*this, reinterpret_cast<HWND>(handle ? handle->id : 0));
190 }
191
192 return static_cast<bool>(m_grabber) == active;
193}
194
\inmodule QtCore
Definition qbytearray.h:57
static const QCapturableWindowPrivate * handle(const QCapturableWindow &window)
\inmodule QtMultimedia
void errorUpdated(QPlatformSurfaceCapture::Error error, const QString &description)
void addFrameCallback(Object &object, Method method)
void updateError(QPlatformSurfaceCapture::Error error, const QString &description={})
static std::unique_ptr< Grabber > create(QGdiWindowCapture &capture, HWND hWnd)
QVideoFrameFormat format() const
QVideoFrame grabFrame() override
QVideoFrameFormat frameFormat() const override
bool setActiveInternal(bool active) override
~QGdiWindowCapture() override
The QMemoryVideoBuffer class provides a system memory allocated video data buffer.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void updateError(Error error, const QString &errorString)
void newVideoFrame(const QVideoFrame &)
\inmodule QtCore
Definition qsize.h:25
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
bool isValid() const
Identifies if a video surface format has a valid pixel format and frame size.
QSize frameSize() const
Returns the dimensions of frames in a video stream.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
static QString header(const QString &name)
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum format
GLuint GLenum * rate
GLenum array
GLuint64EXT * result
[6]
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
aWidget window() -> setWindowTitle("New Window Title")
[2]
QHostInfo info
[0]