DRM 软件开发 (五)ChatGPT 与 DeepSeek 写 DMA-BUF程序

提问:

        通过 DMA-BUF 将 camera 的数据送给 libdrm 的例子

ChatGPT

ChatGPT 说:

        下面是一个通过 DMA-BUFcamera 数据送给 libdrm 的简化示例。这个过程结合了 V4L2 来获取摄像头数据,使用 DMA-BUF 作为共享内存,最后通过 libdrm 将该数据发送到 GPU 进行显示。

示例流程:

  1. 使用 V4L2 捕获摄像头数据,获取一个 DMA-BUF 文件描述符。
  2. 使用 libdrm 创建 DRM framebuffer,将 DMA-BUF 传递给显卡。
  3. 将 DMA-BUF 映射到显存,并渲染到屏幕上。

示例代码:

1.  打开摄像头设备并获取 DMA-BUF 文件描述符

首先,我们使用 V4L2 打开摄像头设备,并请求 DMA-BUF 类型的缓冲区。

#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

int open_camera_device(const char *device) {
    int fd = open(device, O_RDWR);
    if (fd == -1) {
        perror("Error opening camera device");
        exit(1);
    }
    return fd;
}

int request_dma_buf(int fd, int width, int height, int format) {
    struct v4l2_format fmt;
    struct v4l2_requestbuffers reqbuf;
    struct v4l2_buffer buf;

    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = width;
    fmt.fmt.pix.height = height;
    fmt.fmt.pix.pixelformat = format;
    fmt.fmt.pix.field = V4L2_FIELD_NONE;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
        perror("VIDIOC_S_FMT");
        exit(1);
    }

    memset(&reqbuf, 0, sizeof(reqbuf));
    reqbuf.count = 1;  // 请求一个缓冲区
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_DMABUF;  // 使用 DMA-BUF 类型的缓冲区

    if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
        perror("VIDIOC_REQBUFS");
        exit(1);
    }

    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_DMABUF;

    if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
        perror("VIDIOC_QUERYBUF");
        exit(1);
    }

    return buf.m.fd;  // 返回 DMA-BUF 文件描述符
}

        在这里,我们请求一个 DMA-BUF 类型的缓冲区,并通过 VIDIOC_QUERYBUF 获取 DMA-BUF 文件描述符。

2.  使用 libdrm 创建 framebuffer 并传递 DMA-BUF

        然后,使用 libdrm 将 DMA-BUF 传递给显卡,并创建一个 framebuffer。

#include <drm/drm.h>
#include <drm/drm_mode.h>
#include <drm/drm_prime.h>
#include <fcntl.h>
#include <unistd.h>

int open_drm_device() {
    int fd = open("/dev/dri/card0", O_RDWR);
    if (fd == -1) {
        perror("Error opening DRM device");
        exit(1);
    }
    return fd;
}

void create_framebuffer(int drm_fd, int dma_buf_fd, int width, int height) {
    struct drm_prime_handle prime_handle = {};
    prime_handle.fd = dma_buf_fd;

    uint32_t handle;
    if (ioctl(drm_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime_handle) == -1) {
        perror("DRM_IOCTL_PRIME_FD_TO_HANDLE");
        exit(1);
    }

    handle = prime_handle.handle;

    uint32_t fb_id;
    int bpp = 32;  // 每像素32位,假设我们是 RGBA 格式
    int pitch = width * bpp / 8;

    if (drmModeAddFB(drm_fd, width, height, 24, 32, pitch, handle, &fb_id) != 0) {
        perror("drmModeAddFB");
        exit(1);
    }

    printf("Framebuffer created with ID: %u\n", fb_id);
}

        这里,我们使用 DRM_IOCTL_PRIME_FD_TO_HANDLE 将 DMA-BUF 文件描述符转化为 DRM 句柄,然后通过 drmModeAddFB 创建 framebuffer。

3.  渲染数据到屏幕

        最后,将 framebuffer 显示到屏幕上,使用 DRM plane

void display_on_screen(int drm_fd, uint32_t fb_id, uint32_t plane_id, uint32_t crtc_id, int width, int height) {
    if (drmModeSetPlane(drm_fd, plane_id, crtc_id, fb_id, 0, 0, 0, width, height) != 0) {
        perror("drmModeSetPlane");
        exit(1);
    }

    printf("Displaying framebuffer on screen\n");
}

        drmModeSetPlane 用来将 framebuffer 绑定到一个 plane 上,这样 GPU 可以直接将数据渲染到屏幕。

主程序

        在 main() 函数中,我们依次打开摄像头设备、获取 DMA-BUF 文件描述符、创建 framebuffer,并将图像显示到屏幕。

int main() {
    int width = 1920;
    int height = 1080;
    int format = V4L2_PIX_FMT_YUYV;  // 选择一个适合的像素格式

    // 打开摄像头设备并请求 DMA-BUF
    int v4l2_fd = open_camera_device("/dev/video0");
    int dma_buf_fd = request_dma_buf(v4l2_fd, width, height, format);

    // 打开 DRM 设备
    int drm_fd = open_drm_device();

    // 使用 DMA-BUF 创建 framebuffer
    create_framebuffer(drm_fd, dma_buf_fd, width, height);

    // 假设我们已经知道 plane_id 和 crtc_id
    uint32_t plane_id = 0;  // 这是一个示例,应该根据实际设备设置
    uint32_t crtc_id = 0;   // 同上

    // 将图像显示到屏幕
    display_on_screen(drm_fd, fb_id, plane_id, crtc_id, width, height);

    // 清理工作
    close(v4l2_fd);
    close(drm_fd);

    return 0;
}

总结:

  1. V4L2 通过 V4L2_MEMORY_DMABUF 获取 DMA-BUF 文件描述符,避免了内存拷贝。
  2. libdrm 使用 DRM_IOCTL_PRIME_FD_TO_HANDLE 将 DMA-BUF 文件描述符转换为显卡可以理解的 DRM 句柄,并创建 framebuffer。
  3. 通过 drmModeSetPlane 将 framebuffer 数据渲染到屏幕上。

这种方式确保了数据传输过程中的零拷贝,直接利用 GPU 显示摄像头数据,效率更高。

DeepSeep

DeepSeek 说:

        通过 DMA-BUF 将相机数据传递给 libdrm 的过程涉及多个步骤,包括从相机获取数据、创建 DMA-BUF 缓冲区、将缓冲区映射到 libdrm 并进行显示。以下是一个简单的示例框架,展示如何实现这一流程。

1. 前提条件

  • 确保系统支持 DMA-BUF 和 libdrm

  • 安装必要的库和工具:

    • libdrm(用于 DRM 显示)

    • v4l2-utils(用于相机数据捕获)

    • libcamera(可选,用于现代相机支持)

2. 示例代码框架

        以下是一个简化的示例代码框架,展示如何通过 DMA-BUF 将相机数据传递给 libdrm

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <drm_fourcc.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#include <EGL/egl.h>

// 定义相机设备
#define CAMERA_DEVICE "/dev/video0"
#define DRM_DEVICE "/dev/dri/card0"

// 初始化相机
int init_camera(int *camera_fd) {
    *camera_fd = open(CAMERA_DEVICE, O_RDWR);
    if (*camera_fd < 0) {
        perror("Failed to open camera");
        return -1;
    }

    // 设置相机格式(例如:V4L2_PIX_FMT_YUYV)
    struct v4l2_format fmt = {0};
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = 640;
    fmt.fmt.pix.height = 480;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    fmt.fmt.pix.field = V4L2_FIELD_NONE;

    if (ioctl(*camera_fd, VIDIOC_S_FMT, &fmt) < 0) {
        perror("Failed to set camera format");
        return -1;
    }

    return 0;
}

// 初始化 DRM
int init_drm(int *drm_fd) {
    *drm_fd = open(DRM_DEVICE, O_RDWR);
    if (*drm_fd < 0) {
        perror("Failed to open DRM device");
        return -1;
    }

    // 获取 DRM 资源
    drmModeRes *resources = drmModeGetResources(*drm_fd);
    if (!resources) {
        perror("Failed to get DRM resources");
        return -1;
    }

    // 查找并设置 CRTC 和 Connector
    // 这里省略具体实现,需要根据硬件配置选择合适的 CRTC 和 Connector

    drmModeFreeResources(resources);
    return 0;
}

// 创建 DMA-BUF 缓冲区
int create_dma_buf(int drm_fd, int width, int height, int *dma_buf_fd) {
    // 使用 DRM 创建 GBM 缓冲区
    struct gbm_device *gbm_dev = gbm_create_device(drm_fd);
    if (!gbm_dev) {
        perror("Failed to create GBM device");
        return -1;
    }

    // 创建 GBM 缓冲区对象
    struct gbm_bo *bo = gbm_bo_create(gbm_dev, width, height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
    if (!bo) {
        perror("Failed to create GBM buffer object");
        return -1;
    }

    // 获取 DMA-BUF 文件描述符
    *dma_buf_fd = gbm_bo_get_fd(bo);
    if (*dma_buf_fd < 0) {
        perror("Failed to get DMA-BUF fd");
        return -1;
    }

    return 0;
}

// 主函数
int main() {
    int camera_fd, drm_fd, dma_buf_fd;

    // 初始化相机
    if (init_camera(&camera_fd) < 0) {
        return -1;
    }

    // 初始化 DRM
    if (init_drm(&drm_fd) < 0) {
        return -1;
    }

    // 创建 DMA-BUF 缓冲区
    if (create_dma_buf(drm_fd, 640, 480, &dma_buf_fd) < 0) {
        return -1;
    }

    // 将相机数据映射到 DMA-BUF 缓冲区
    // 这里省略具体实现,需要使用 V4L2 的 VIDIOC_QBUF 和 VIDIOC_DQBUF 操作

    // 将 DMA-BUF 缓冲区传递给 DRM 进行显示
    // 这里省略具体实现,需要使用 DRM 的 atomic 或 legacy API

    // 清理资源
    close(camera_fd);
    close(drm_fd);
    close(dma_buf_fd);

    return 0;
}

3. 关键步骤说明

  1. 初始化相机

    • 使用 V4L2 API 打开相机设备并设置格式。

    • 通过 VIDIOC_S_FMT 设置分辨率、像素格式等。

  2. 初始化 DRM

    • 打开 DRM 设备并获取资源(CRTC、Connector 等)。

    • 配置显示模式。

  3. 创建 DMA-BUF 缓冲区

    • 使用 GBM(Generic Buffer Management)创建缓冲区。

    • 获取 DMA-BUF 文件描述符。

  4. 映射相机数据到 DMA-BUF

    • 使用 V4L2 的 VIDIOC_QBUF 和 VIDIOC_DQBUF 操作将相机数据映射到 DMA-BUF。

  5. 显示 DMA-BUF 数据

    • 使用 DRM 的 atomic 或 legacy API 将 DMA-BUF 缓冲区显示到屏幕上。

4. 注意事项

  • 需要根据具体硬件和驱动支持调整代码。

  • 如果使用现代相机(如 Raspberry Pi 的 libcamera),可能需要使用 libcamera 替代 V4L2。

  • 确保系统内核支持 DMA-BUF 和 DRM

5. 参考文档

如果有进一步的问题或需要更详细的实现,请随时提问!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值