Search code examples
c++linuxx11

capture high res/frame rate in x11


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?


Solution

  • 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);
    }