Linux DRM ( DRI ) 无法像以前一样使用 FBDEV 进行屏幕抓取 /dev/fb0

Linux DRM ( DRI ) Cannot Screen Scrape /dev/fb0 as before with FBDEV

在使用 FBDEV 驱动程序(Raspberry Pi.. 等)的其他 Linux 机器上,我可以映射 /dev/fb0 设备并直接创建一个 BMP 文件来保存上面的内容屏幕。

现在,我正尝试在 TI Sitara AM57XX (Beagleboard X-15) 上对 DRM 做同样的事情。用于使用 FBDEV 的代码如下所示。

这个 mmap 似乎不再适用于 DRM。我正在使用带有 Qt 平台 linuxfb 插件的非常简单的 Qt5 应用程序。它很好地绘制到 /dev/fb0 并正确显示在屏幕上,但是我无法使用内存映射指针从 /dev/fb0 读回并将屏幕图像保存到文件中。看起来乱码是这样的:

代码:

#ifdef FRAMEBUFFER_CAPTURE

    repaint();
    QCoreApplication::processEvents();

    // Setup framebuffer to desired format
    struct fb_var_screeninfo var;
    struct fb_fix_screeninfo finfo;
    memset(&finfo, 0, sizeof(finfo));
    memset(&var, 0, sizeof(var));
    /* Get variable screen information. Variable screen information
    * gives information like size of the image, bites per pixel,
    * virtual size of the image etc. */
    int fbFd = open("/dev/fb0", O_RDWR);
    int fdRet = ioctl(fbFd, FBIOGET_VSCREENINFO, &var);
    if (fdRet < 0) {
        qDebug() << "Error opening /dev/fb0!";
        close(fbFd);
        return -1;
    }

    if (ioctl(fbFd, FBIOPUT_VSCREENINFO, &var)<0) {
        qDebug() << "Error setting up framebuffer!";
        close(fbFd);
        return -1;
    } else {
        qDebug() << "Success setting up framebuffer!";
    }

    //Get fixed screen information
    if (ioctl(fbFd, FBIOGET_FSCREENINFO, &finfo) < 0) {
        qDebug() << "Error getting fixed screen information!";
        close(fbFd);
        return -1;
    } else {
        qDebug() << "Success getting fixed screen information!";
    }

    //int screensize = var.xres * var.yres * var.bits_per_pixel / 8;
    //int screensize = var.yres_virtual * finfo.line_length;
    //int screensize = finfo.smem_len;
    int screensize = finfo.line_length * var.yres_virtual;
    qDebug() << "Framebuffer size is: " << var.xres << var.yres << var.bits_per_pixel << screensize;
    int linuxFbWidth = var.xres;
    int linuxFbHeight = var.yres;

    int location = (var.xoffset) * (var.bits_per_pixel/8) +
                           (var.yoffset) * finfo.line_length;

    // Perform memory mapping of linux framebuffer
    char* frameBufferMmapPixels = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbFd, 0);
    assert(frameBufferMmapPixels != MAP_FAILED);

    QImage toSave((uchar*)frameBufferMmapPixels,linuxFbWidth,linuxFbHeight,QImage::Format_ARGB32);
    toSave.save("/usr/bin/test.bmp");
    sync();

#endif

这是代码运行时的输出:

Success setting up framebuffer!
Success getting fixed screen information!
Framebuffer size is:  800 480 32 1966080

这是显示像素格式的 fbset 输出:

mode "800x480"
    geometry 800 480 800 480 32
    timings 0 0 0 0 0 0 0
    accel true
    rgba 8/16,8/8,8/0,8/24
endmode

root@am57xx-evm:~#

finfo.line_length给出实际物理扫描线的字节大小。它不一定与屏幕宽度乘以像素大小相同,因为可能会填充扫描线。

然而,您使用的 QImage 构造函数假定没有填充。

如果 xoffset 为零,则应该可以使用带有 bytesPerLine 参数的构造函数直接从帧缓冲区数据构造 QImage。否则有两个选项:

  • 分配一个单独的缓冲区并仅将每条扫描线的可见部分复制到其中
  • 从整个缓冲区(包括填充)创建图像,然后裁剪它

如果您使用的是 DRM,那么 /dev/fb0 可能指向一个完全不同的缓冲区(不是当前可见的缓冲区)或具有不同的格式。

fbdev 实际上只适用于尚未移植的旧遗留 DRM/KMS 并且只有非常有限的模组设置功能。

顺便说一句:您使用的是哪个内核?希望不是那个古老而破碎的 TI 供应商内核......