从Linux framebuffer获取RGB像素值的方法?

6
我希望以最高效的方式获取屏幕像素的RGB值,使用Linux。因此,我决定使用C语言中的framebuffer库(fb.h)直接访问framebuffer设备(/dev/fb0)并从中读取。

以下是代码:

#include <stdint.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

int main() {

    int fb_fd;
    struct fb_fix_screeninfo finfo;
    struct fb_var_screeninfo vinfo;
    uint8_t *fb_p;

    /* Open the frame buffer device */
    fb_fd = open("/dev/fb0", O_RDWR);
    if (fb_fd < 0) {
        perror("Can't open /dev/fb0\n");
        exit(EXIT_FAILURE);
    }

    /* Get fixed info */
    if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) < 0) {
        perror("Can't get fixed info\n");
        exit(EXIT_FAILURE);
    }

    /* Get variable info */
    if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) {
        perror("Can't get variable info\n");
        exit(EXIT_FAILURE);
    }

    /* To access to the memory, it can be mapped*/
    fb_p = (uint8_t *) mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
    if (fb_p == MAP_FAILED) {
        perror("Can't map memory\n");
        exit(EXIT_FAILURE);
    }

    /* Print each byte of the frame buffer */
    for (int i = 0; i < finfo.smem_len; i++) {
        printf("%d\n", *(fb_p + i));

        // for (int j = 0; j < 500000000; j++);       /* Delay */
    }

    munmap(fb_p, 0);
        close(fb_fd);

    return 0;
}

但是当我打印这些值时,我并没有得到我所期望的结果...
如果我使用像“grabc”这样的工具选择像素(0, 0)的RGB值,我会得到以下结果:
#85377e
133,55,126

但是我的代码第一次打印出来的结果如下:

126
145
198
...

看起来我已经成功获取了第一个像素的第一个值,对应蓝色,但其余部分是错误的。


2
我没有Linux framebuffers的经验,但是你得到的像素缓冲区是否可能不是从你认为的(0,0)开始的呢?例如,你选择了左上角的值,但缓冲区是从右下角开始的。 - Banex
当您尝试读取帧缓冲区数据时,您是否在X Window系统中?也许这是不可能的。 - DaBler
1
关键问题在于XWindow可能根本不使用内核中的帧缓冲。据我所知,像AMD或NVIDIA这样的GPU并不依赖内核中的fb驱动程序。但是当通过ctrl+alt+fn切换到控制台时,我确定您可以获得正确的帧缓冲RGB值。 - Chris Tsui
fbgrab -b 24 test.png 是否给出了您期望的结果? - DaBler
无论如何,如果您使用X Window系统,这个链接应该会有所帮助。只需打开/tmp/fbe_buffer而不是/dev/fb0即可。 - DaBler
3个回答

6
如果您查看grabc源代码(https://www.muquit.com/muquit/software/grabc/grabc.html),您会发现他正在查询X Windows(而不是硬件帧缓冲区本身)。
root_window=XRootWindow(display,XDefaultScreen(display));
target_window=selectWindow(display,&x,&y);
...
ximage=XGetImage(display,target_window,x,y,1,1,AllPlanes,ZPixmap);
...
color->pixel=XGetPixel(ximage,0,0);
XDestroyImage(ximage);

无论价值如何 - 由于多种原因 - 我强烈建议您考虑做同样的事情。

您可能还会对以下内容感兴趣:


我认为这个问题涉及到Linux控制台,而不是X Window系统。 - DaBler
我对这个解决方案不感兴趣。我想直接从设备上读取它...我不知道是否可能。 - Sergio
1
是的,这是可能的。只是会有一些“挑战”存在。我引用的两个链接都涉及低级别的“帧缓冲区I/O”。DirectFB库可能有助于简化您的任务。几年前我也尝试过同样的事情……最终使用了Xlib而不是FB。再提供一个链接:http://www.ummon.eu/Linux/API/Devices/framebuffer.html - paulsm4
7
/dev/fb并不是直接来自设备,而是来自一个驱动程序。这个驱动程序可能对设备实际运行情况知之甚少——因为X服务器正在直接与设备通信。 - user2404501
1
@Wumpus Q. Wumbley - 说得好。此外,值得强调的是,/dev/fb仍然是一个“抽象层”(就像XLib一样),它隐藏(可能有用的)细节,并产生性能惩罚。还要注意,/dev/fb可能没有配置为与任何Xlib(即可见)图形相关联! - paulsm4

4
在一些使用fbdev的罕见设备中,您可能需要将mmap返回指针与下一个系统页面对齐,以便正确获取第一个像素,并且这样做可能还需要映射一个以上的页面。
我认为这不是你的情况,看起来你正在尝试使用linux fbdev获取xserver桌面像素,除非你的xserver配置为使用fbdev驱动程序(即使如此,我也不确定是否会有效)。
如果读取桌面像素是您的目标,则应查找某些屏幕截图工具。此外,fbdev驱动程序通常非常基本,虽然它是低级访问,但在内存映射和读取像素方面并不高效,Xlib或任何更高级的图形系统可能会意识到并支持您的图形加速卡,并且更高效(也许使用DMA将整个帧缓冲区读入您的系统内存而不加载CPU)。

2
在iOS上工作时,有一个名为iOMobileFrameBuffer的私有框架,可以创建一个可爱的屏幕录制软件。我之前曾经涉足过这方面,所以我会告诉你我知道的。iOS显然与Linux FrameBuffer不同(缺乏文档相当相似...),但我个人会看一下这个答案。虽然问题不符合您的要求,但该答案解释了PixelBuffer,并提供了一个小例子,正是您所要求的。请阅读并回复任何问题,我会尽力回答。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接