92 std::unique_ptr<Grabber> result(
new Grabber(capture));
93 return result->init(screen) ?
std::move(result) :
nullptr;
98 std::unique_ptr<Grabber> result(
new Grabber(capture));
99 return result->init(wid) ?
std::move(result) :
nullptr;
112 Grabber(QX11SurfaceCapture &capture)
114 addFrameCallback(capture, &QX11SurfaceCapture::newVideoFrame);
115 connect(
this, &Grabber::errorUpdated, &capture, &QX11SurfaceCapture::updateError);
121 m_display.reset(XOpenDisplay(
nullptr));
124 updateError(QPlatformSurfaceCapture::InternalError,
125 QStringLiteral(
"Cannot open X11 display"));
127 return m_display !=
nullptr;
132 if (
auto screen = QGuiApplication::primaryScreen())
133 setFrameRate(screen->refreshRate());
135 return createDisplay() && initWithXID(
static_cast<XID>(wid));
138 bool init(QScreen *screen)
141 updateError(QPlatformSurfaceCapture::NotFound, QStringLiteral(
"Screen Not Found"));
145 if (!createDisplay())
148 auto screenNumber = screenNumberByName(m_display.get(), screen->name());
150 if (screenNumber < 0)
153 setFrameRate(screen->refreshRate());
155 return initWithXID(RootWindow(m_display.get(), screenNumber));
158 bool initWithXID(XID xid)
172 if (
std::exchange(m_attached,
false)) {
173 XShmDetach(m_display.get(), &m_shmInfo);
174 shmdt(m_shmInfo.shmaddr);
175 shmctl(m_shmInfo.shmid, IPC_RMID,
nullptr);
181 Q_ASSERT(!m_attached);
184 shmget(IPC_PRIVATE, m_xImage->bytes_per_line * m_xImage->height, IPC_CREAT | 0777);
186 if (m_shmInfo.shmid == -1)
189 m_shmInfo.readOnly =
false;
190 m_shmInfo.shmaddr = m_xImage->data = (
char *)shmat(m_shmInfo.shmid,
nullptr, 0);
192 m_attached = XShmAttach(m_display.get(), &m_shmInfo);
198 XWindowAttributes wndattr;
204 Visual *visual =
nullptr;
206 if (!XGetWindowAttributes(m_display.get(), win, &wndattr)) {
207 updateError(QPlatformSurfaceCapture::CaptureFailed,
208 QStringLiteral(
"Cannot get window attributes"));
212 if (wndattr.map_state != IsViewable) {
213 updateError(QPlatformSurfaceCapture::CaptureFailed,
214 QStringLiteral(
"Window is not viewable"));
217 width = wndattr.width;
218 height = wndattr.height;
219 depth = wndattr.depth;
220 visual = wndattr.visual;
222 if (win == wndattr.root)
227 Window root, *children;
228 unsigned int nchildren;
229 if (!XQueryTree(m_display.get(), win, &root, &win, &children, &nchildren)) {
230 updateError(QPlatformSurfaceCapture::CaptureFailed,
231 QStringLiteral(
"Cannot get parent window"));
234 if (children) XFree(children);
237 m_xOffset = qMax(0, -xPos);
238 m_yOffset = qMax(0, -yPos);
239 width = qMin(width, wndattr.width - xPos) - m_xOffset;
240 height = qMin(height, wndattr.height - yPos) - m_yOffset;
241 if (width <= 0 || height <= 0) {
242 updateError(QPlatformSurfaceCapture::CaptureFailed,
243 QStringLiteral(
"Window is completely out of the screen borders"));
249 if (!m_xImage || width != m_xImage->width || height != m_xImage->height
250 || depth != m_xImage->depth || visual->visualid != m_visualID) {
252 qCDebug(qLcX11SurfaceCapture) <<
"recreate ximage: " << width << height
253 << depth << visual->visualid;
258 m_visualID = wndattr.visual->visualid;
259 m_xImage.reset(XShmCreateImage(m_display.get(), visual, depth, ZPixmap,
260 nullptr, &m_shmInfo, width, height));
263 updateError(QPlatformSurfaceCapture::CaptureFailed,
264 QStringLiteral(
"Cannot create image"));
268 const auto pixelFormat = xImagePixelFormat(*m_xImage);
271 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
272 updateError(QPlatformSurfaceCapture::CaptureFailed,
273 QStringLiteral(
"Not handled pixel format, bpp=")
274 + QString::number(m_xImage->bits_per_pixel));
281 updateError(QPlatformSurfaceCapture::CaptureFailed,
282 QStringLiteral(
"Cannot attach shared memory"));
286 QVideoFrameFormat format(QSize(m_xImage->width, m_xImage->height), pixelFormat);
287 format.setStreamFrameRate(frameRate());
300 QByteArray data(m_xImage->bytes_per_line * m_xImage->height, Qt::Uninitialized);
302 const auto pixelSrc =
reinterpret_cast<
const uint32_t *>(m_xImage->data);
303 const auto pixelDst =
reinterpret_cast<uint32_t *>(data.data());
304 const auto pixelCount = data.size() / 4;
305 const auto xImageAlphaVaries =
false;
307 qCopyPixelsWithAlphaMask(pixelDst, pixelSrc, pixelCount, m_format.pixelFormat(),
310 auto buffer = std::make_unique<QMemoryVideoBuffer>(data, m_xImage->bytes_per_line);
311 return QVideoFramePrivate::createFrame(std::move(buffer), m_format);
317 for (
int i = 0; i < 10; i++) {
320 if (XShmGetImage(m_display.get(), m_xid, m_xImage.get(),
321 m_xOffset, m_yOffset, AllPlanes))
325 updateError(QPlatformSurfaceCapture::CaptureFailed,
327 "Cannot get ximage; the window geometry may be undergoing change"));
331 std::optional<QPlatformSurfaceCapture::Error> m_prevGrabberError;
335 std::unique_ptr<Display,
decltype(&XCloseDisplay)> m_display{
nullptr, &
XCloseDisplay };
336 std::unique_ptr<XImage,
decltype(&destroyXImage)> m_xImage{
nullptr, &
destroyXImage };
337 XShmSegmentInfo m_shmInfo;
338 bool m_attached =
false;
339 VisualID m_visualID = None;
340 QVideoFrameFormat m_format;