c++ 在x11中捕获高分辨率/帧速率

beq87vna  于 2023-03-05  发布在  其他
关注(0)|答案(1)|浏览(279)

我正在做c++项目,这是一个在linux上实现高分辨率/帧率的屏幕录制器。linux上有很多库是用c写的,所以我也用它们。
我已经使用nvfbc编写了屏幕捕获(只有nvidia),但我想代码回退,如果nvfbc是不可能使用或如果其他供应商卡正在使用,所以我决定去与xcb,因为它比XLib快,但我发现,即使在2560 x1440 xcb不能捕捉甚至60 fps,因此,对于我的目的来说,它太慢了。由于它是后备实现,它可能会比nvfbc慢,但xcb慢得无法接受。我发现xcb + shmxcb + dri3性能更高,更适合我的用例。但是文档太少了,很难找到最好的屏幕捕捉方法。下面是我只用xcb捕捉屏幕的方法:

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

现在,它就像是测试变体,因为我做了很多修改并测量了性能。什么可以代替xcb来提高性能,并提供在所有现代显卡上使用的能力,如nvidia/amd/intel,我在哪里可以找到更多的文档?

4ktjp1zp

4ktjp1zp1#

多亏了@rustyx,我发现了如何使用xcb + shm来完成这一点。下面是我的一个示例和评论,供那些也在努力寻找它如何工作的人参考:

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

相关问题