93 std::unique_ptr<Grabber> result(
new Grabber(capture));
94 return result->init(screen) ?
std::move(result) :
nullptr;
99 std::unique_ptr<Grabber> result(
new Grabber(capture));
100 return result->init(wid) ?
std::move(result) :
nullptr;
113 Grabber(QX11SurfaceCapture &capture)
115 addFrameCallback(capture, &QX11SurfaceCapture::newVideoFrame);
116 connect(
this, &Grabber::errorUpdated, &capture, &QX11SurfaceCapture::updateError);
122 m_display.reset(XOpenDisplay(
nullptr));
125 updateError(QPlatformSurfaceCapture::InternalError,
126 QStringLiteral(
"Cannot open X11 display"));
128 return m_display !=
nullptr;
133 if (
auto screen = QGuiApplication::primaryScreen())
134 setFrameRate(screen->refreshRate());
136 return createDisplay() && initWithXID(
static_cast<XID>(wid));
139 bool init(QScreen *screen)
142 updateError(QPlatformSurfaceCapture::NotFound, QStringLiteral(
"Screen Not Found"));
146 if (!createDisplay())
149 auto screenNumber = screenNumberByName(m_display.get(), screen->name());
151 if (screenNumber < 0)
154 setFrameRate(screen->refreshRate());
156 return initWithXID(RootWindow(m_display.get(), screenNumber));
159 bool initWithXID(XID xid)
173 if (
std::exchange(m_attached,
false)) {
174 XShmDetach(m_display.get(), &m_shmInfo);
175 shmdt(m_shmInfo.shmaddr);
176 shmctl(m_shmInfo.shmid, IPC_RMID,
nullptr);
182 Q_ASSERT(!m_attached);
185 shmget(IPC_PRIVATE, m_xImage->bytes_per_line * m_xImage->height, IPC_CREAT | 0777);
187 if (m_shmInfo.shmid == -1)
190 m_shmInfo.readOnly =
false;
191 m_shmInfo.shmaddr = m_xImage->data = (
char *)shmat(m_shmInfo.shmid,
nullptr, 0);
193 m_attached = XShmAttach(m_display.get(), &m_shmInfo);
199 XWindowAttributes wndattr;
205 Visual *visual =
nullptr;
207 if (!XGetWindowAttributes(m_display.get(), win, &wndattr)) {
208 updateError(QPlatformSurfaceCapture::CaptureFailed,
209 QStringLiteral(
"Cannot get window attributes"));
213 if (wndattr.map_state != IsViewable) {
214 updateError(QPlatformSurfaceCapture::CaptureFailed,
215 QStringLiteral(
"Window is not viewable"));
218 width = wndattr.width;
219 height = wndattr.height;
220 depth = wndattr.depth;
221 visual = wndattr.visual;
223 if (win == wndattr.root)
228 Window root, *children;
229 unsigned int nchildren;
230 if (!XQueryTree(m_display.get(), win, &root, &win, &children, &nchildren)) {
231 updateError(QPlatformSurfaceCapture::CaptureFailed,
232 QStringLiteral(
"Cannot get parent window"));
235 if (children) XFree(children);
238 m_xOffset = qMax(0, -xPos);
239 m_yOffset = qMax(0, -yPos);
240 width = qMin(width, wndattr.width - xPos) - m_xOffset;
241 height = qMin(height, wndattr.height - yPos) - m_yOffset;
242 if (width <= 0 || height <= 0) {
243 updateError(QPlatformSurfaceCapture::CaptureFailed,
244 QStringLiteral(
"Window is completely out of the screen borders"));
250 if (!m_xImage || width != m_xImage->width || height != m_xImage->height
251 || depth != m_xImage->depth || visual->visualid != m_visualID) {
253 qCDebug(qLcX11SurfaceCapture) <<
"recreate ximage: " << width << height
254 << depth << visual->visualid;
259 m_visualID = wndattr.visual->visualid;
260 m_xImage.reset(XShmCreateImage(m_display.get(), visual, depth, ZPixmap,
261 nullptr, &m_shmInfo, width, height));
264 updateError(QPlatformSurfaceCapture::CaptureFailed,
265 QStringLiteral(
"Cannot create image"));
269 const auto pixelFormat = xImagePixelFormat(*m_xImage);
272 if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
273 updateError(QPlatformSurfaceCapture::CaptureFailed,
274 QStringLiteral(
"Not handled pixel format, bpp=")
275 + QString::number(m_xImage->bits_per_pixel));
282 updateError(QPlatformSurfaceCapture::CaptureFailed,
283 QStringLiteral(
"Cannot attach shared memory"));
287 QVideoFrameFormat format(QSize(m_xImage->width, m_xImage->height), pixelFormat);
288 format.setStreamFrameRate(frameRate());
301 QByteArray data(m_xImage->bytes_per_line * m_xImage->height, Qt::Uninitialized);
303 const auto pixelSrc =
reinterpret_cast<
const uint32_t *>(m_xImage->data);
304 const auto pixelDst =
reinterpret_cast<uint32_t *>(data.data());
305 const auto pixelCount = data.size() / 4;
306 const auto xImageAlphaVaries =
false;
308 qCopyPixelsWithAlphaMask(pixelDst, pixelSrc, pixelCount, m_format.pixelFormat(),
311 auto buffer = std::make_unique<QMemoryVideoBuffer>(data, m_xImage->bytes_per_line);
312 return QVideoFramePrivate::createFrame(std::move(buffer), m_format);
318 for (
int i = 0; i < 10; i++) {
321 if (XShmGetImage(m_display.get(), m_xid, m_xImage.get(),
322 m_xOffset, m_yOffset, AllPlanes))
326 updateError(QPlatformSurfaceCapture::CaptureFailed,
328 "Cannot get ximage; the window geometry may be undergoing change"));
332 std::optional<QPlatformSurfaceCapture::Error> m_prevGrabberError;
336 std::unique_ptr<Display,
decltype(&XCloseDisplay)> m_display{
nullptr, &
XCloseDisplay };
337 std::unique_ptr<XImage,
decltype(&destroyXImage)> m_xImage{
nullptr, &
destroyXImage };
338 XShmSegmentInfo m_shmInfo{};
339 bool m_attached =
false;
340 VisualID m_visualID = None;
341 QVideoFrameFormat m_format;