I'm doing c++
project which is screen recorder for high resolutions/frame rates on linux.
A lot of libraries on linux written in c
, so I also use them.
I've already written screen capturing using nvfbc
(only nvidia), but I want to code fallback if nvfbc is not possible to use or if other vendor card is in use, so I decided to go with xcb
as it's faster than XLib
, but I discovered that even in 2560x1440 xcb
can't capture even 60 fps, so it's too slow for my purposes. Since it's fallback implementation, it's probably will be slower than nvfbc
, but xcb
is unacceptably slow. I found that xcb
+ shm
or xcb
+ dri3
will be more performant and more suits my use case, but there's really few documentation and it's quite difficult to find the best way for screen capturing. This is how I capture screen using xcb
only:
void X11Capture::load() {
conn = xcb_connect(nullptr, nullptr);
if (xcb_connection_has_error(conn))
errHandler("Failed to connect to X server", -1);
const auto setup = xcb_get_setup(conn);
screen = xcb_setup_roots_iterator(setup).data;
if (!isResolutionSet) {
scWidth = screen->width_in_pixels;
scHeight = screen->height_in_pixels;
}
isInitialized = true;
}
void X11Capture::startCapture() {
if (!isInitialized)
errHandler("X11Capture::load() were not called or was executed "
"with errors",
-1);
isScreenCaptured.store(true);
xcb_get_image_reply_t *image = nullptr;
while (isScreenCaptured.load()) {
image = xcb_get_image_reply(
conn,
xcb_get_image_unchecked(conn, XCB_IMAGE_FORMAT_Z_PIXMAP,
screen->root, 0, 0, scWidth, scHeight,
~0),
nullptr);
const auto frame = xcb_get_image_data(image);
const auto &length = xcb_get_image_data_length(image);
newFrameHandler(static_cast<void *>(frame), length);
free(image);
}
}
For now, it's like test variant since I do a lot of changes and measure performance. What can be used instead of xcb
to improve performance and provide ability to be used on all modern videocards like nvidia/amd/intel and where can I find more documentation for all of that?
Thanks to @rustyx , I've found how this can be done using xcb + shm. There's example with comments from me for those who also struggle with finding how it works:
#include <xcb/shm.h>
#include <xcb/xcb.h>
#include <sys/shm.h>
int main() {
// Connect to X server
auto conn = xcb_connect(nullptr, nullptr);
if (xcb_connection_has_error(conn)) { /* do something on error*/
}
// Is needed to access screen/visual data
const auto setup = xcb_get_setup(conn);
// Getting screen from xcb
auto screen = xcb_setup_roots_iterator(setup).data;
// Generating id for our future shared memory
auto seg = xcb_generate_id(conn);
// Allocating shared memory for 1920x1080 screen
auto shmid = shmget(IPC_PRIVATE, 1920 * 1080 * 4, IPC_CREAT | 0777);
if (shmid == -1) { /* do something on error*/
}
// Attaching our shared memory to xcb, so X server will know what shared
// memory we are talking about
xcb_shm_attach(conn, seg, shmid, false);
// Retrieving pointer to the shared memory and setting it to our buffer
std::uint8_t *buffer = static_cast<std::uint8_t *>(
shmat(shmid, nullptr, 0));
xcb_shm_get_image_cookie_t cookie;
while (true) {
// Creating cookie for our image request. 0,0,1920,1080 are x-y
// coordinates for our screen and width-height for our screen
cookie = xcb_shm_get_image_unchecked(conn, screen->root, 0, 0, 1920,
1080, ~0,
XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0);
// freeing image reply. Needs to be done as xcb says that we need to
// free it ourselves to avoid memory leaks
free(xcb_shm_get_image_reply(conn, cookie, nullptr));
// At this moment, we can do what we want with our image. Image is
// written into 'buffer' and in our case has length 1920*1080*4. Image
// is encoded in bgr0, so for every pixel there's 4 memory bytes, 3 for
// data and 1 for align
}
// Detaching shared memory
shmdt(buffer);
}