v4l2摄像头采集流程及应用程序

1 篇文章 0 订阅

        V4L2是linux上用于采集图片、视频、音频数据的一套框架,对上向应用程序提供统一的接口,对下支持各类复杂硬件的灵活扩展,在远程会议、视频监控系统、嵌入式多媒体终端中都有广泛的应用。 V4L2通过打开驱动中创建的/dev/videoX设备,就可以进行一系列的操作,比如申请内存、设置格式、设置属性、开启采集等等。

通过v4l2采集步骤

2

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/mman.h>
 
 
//用户层缓冲区保存结构体
typedef struct BUFTYPE
{
    void *start;
    int length;
}*usr_buf; 
unsigned int buf_num = 4;//指定缓冲区个数
 
int fd;//打开的设备fd
/**
 * @brief init_camera 初始化相机设备属性
 * @param dev 设备名称
 * @return 成功返回0,失败返回-1
 */
int init_camera(const char* dev)
{
    fd = open(dev, O_RDWR);
    if(fd < 0){
        printf("open \"%s\" error\n", dev);
        return -1;
    }
 
    /**
     * 查询设备属性
     */
    struct v4l2_capability cap;
    int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
    if (ret < 0) {
        printf("VIDIOC_QUERYCAP error\n");
        return -1;
    }
 
    printf("driver : %s\n",cap.driver);
    printf("device : %s\n",cap.card);
    printf("bus : %s\n",cap.bus_info);
    printf("version : %d\n",cap.version);
 
    if(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE){ /*判断是否为视频捕获设备*/
        if(cap.capabilities & V4L2_CAP_STREAMING){/*判断是否支持视频流捕获*/
            printf("support capture\n");
        }else{
            printf("unsupport capture\n");
        }
    }else {
        printf("error\n");
        return -1;
    }
 
    struct v4l2_fmtdesc fmtdesc;
    fmtdesc.index=0;
    fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
    {
        printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
        fmtdesc.index++;
    }
 
    /*设置格式*/
    struct v4l2_format fmt;
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头缓冲
    fmt.fmt.pix.width = 640;
    fmt.fmt.pix.height = 480;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)
    {
        printf("set foramt:V4L2_PIX_FMT_MJPEG failed\n");
        return -1;
    }
    return 0;
}
 
 
/**
 * @brief mmap_buffer 分配用户缓冲区内存,并建立内存映射
 * @return 成功返回0,失败返回-1
 */
int mmap_buffer()
{
    usr_buf = (BUFTYPE*)calloc(buf_num, sizeof(BUFTYPE));
    if (!usr_buf) {
        printf("calloc \"frame buffer\" error : Out of memory\n");
        return -1;
    }
 
    struct v4l2_requestbuffers req;
    req.count = buf_num;                    //帧缓冲数量
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获缓冲区类型
    req.memory = V4L2_MEMORY_MMAP;          //内存映射方式
    if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
        printf("VIDIOC_REQBUFS fail\n");
        return -1;
    }
 
 
    /*映射内核缓存区到用户空间缓冲区*/
    for(unsigned int i = 0; i < buf_num; ++i)
    {
        /*查询内核缓冲区信息*/
        struct v4l2_buffer v4l2_buf;
        memset(&v4l2_buf, 0, sizeof(v4l2_buf));
        v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        v4l2_buf.memory = V4L2_MEMORY_MMAP;
        v4l2_buf.index = i;
        if(ioctl(fd , VIDIOC_QUERYBUF, &v4l2_buf) < 0){
            printf("VIDIOC_QUERYBUF failed\n");
            return -1;
        }
 
        /* 建立映射关系
         * 注意这里的索引号,v4l2_buf.index 与 usr_buf 的索引是一一对应的,
         * 当我们将内核缓冲区出队时,可以通过查询内核缓冲区的索引来获取用户缓冲区的索引号,
         * 进而能够知道应该在第几个用户缓冲区中取数据
         */
        usr_buf[i].length = v4l2_buf.length;
        usr_buf[i].start = (char *)mmap(0, v4l2_buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, v4l2_buf.m.offset);
 
        if (MAP_FAILED == usr_buf[i].start){//若映射失败,打印错误
            printf("mmap failed: %d\n",i);
            return -1;
        }else{
            if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0){ // 若映射成功则将内核缓冲区入队
                printf("VIDIOC_QBUF failed\n");
                return -1;
            }
        }
    }
    return 0;
}
 
/**
 * @brief stream_on 开启视频流
 * @return 成功返回0,失败返回-1
 */
int stream_on()
{
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
    {
        printf("VIDIOC_STREAMON failed\n");
        return -1;
    }
    return 0;
}
 
/**
 * @brief write_frame 读取一帧图像
 * @return  返回图像帧的索引index,读取失败返回-1
 */
int write_frame()
{
    struct v4l2_buffer v4l2_buf;
    v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_buf.memory = V4L2_MEMORY_MMAP;
    if(ioctl(fd, VIDIOC_DQBUF, &v4l2_buf) < 0) // 内核缓冲区出队列
    {
        printf("VIDIOC_DQBUF failed, dropped frame\n");
        return -1;
    }
 
 
    /*
     * 因为内核缓冲区与用户缓冲区建立的映射,所以可以通过用户空间缓冲区直接访问这个缓冲区的数据
     */
    char buffer[256];
    sprintf(buffer,"/home/fox/qt_project/build-qt_cpp-Debug/%d.mjpg",v4l2_buf.index);
    int file_fd = open(buffer,O_RDWR | O_CREAT); // 若打开失败则不存储该帧图像
    if(file_fd > 0){
        printf("saving %d images\n",v4l2_buf.index);
        write(file_fd,usr_buf[v4l2_buf.index].start,v4l2_buf.bytesused);
        close(file_fd);
    }
 
 
    if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0) //缓冲区重新入队
    {
        printf("VIDIOC_QBUF failed, dropped frame\n");
        return -1;
    }
    return v4l2_buf.index;
}
 
 
/**
 * @brief stream_off 关闭视频流
 * @return 成功返回0,失败返回-1
 */
int stream_off()
{
    /*关闭视频流*/
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(ioctl(fd,VIDIOC_STREAMOFF,&type) == -1)
    {
        printf("Fail to ioctl 'VIDIOC_STREAMOFF'");
        return -1;
    }
    return 0;
}
 
/**
 * @brief unmap_buffer 解除缓冲区映射
 * @return 成功返回0,失败返回-1
 */
int unmap_buffer()
{
    /*解除内核缓冲区到用户缓冲区的映射*/
    for(unsigned int i = 0; i < buf_num; i++)
    {
        int ret = munmap(usr_buf[i].start, usr_buf[i].length);
        if (ret < 0)
        {
            printf("munmap failed\n");
            return -1;
        }
    }
    free(usr_buf); // 释放用户缓冲区内存
    return 0;
}
 
/**
 * @brief release_camera 关闭设备
 */
void release_camera()
{
    close(fd);
}
 
int main(void)
{
    int ret = init_camera("/dev/video1");
    if(ret < 0){
        printf("init_camera error\n");
        return -1;
    }
 
    ret = mmap_buffer();
    if(ret < 0){
        printf("mmap_buffer error\n");
        return -1;
    }
 
    ret = stream_on();
    if(ret < 0){
        printf("stream_on error\n");
        return -1;
    }
 
    for(int i=0;i<5;i++)
    {
        write_frame();
    }
 
    ret = stream_off();
    if(ret < 0){
        printf("stream_off error\n");
        return -1;
    }
 
    ret = unmap_buffer();
    if(ret < 0){
        printf("unmap_buffer error\n");
        return -1;
    }
 
    release_camera();
    return 0;
}

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
在 Android 上使用 V4L2 进行摄像头采集需要以下步骤: 1. 打开摄像头设备:可以使用 `open()` 系统调用来打开摄像头设备,例如 `/dev/video0`。 2. 查询摄像头设备支持的格式和分辨率:可以使用 `ioctl()` 系统调用和 `VIDIOC_QUERYCAP` 命令来查询设备的支持情况。 3. 配置摄像头设备参数:可以使用 `ioctl()` 系统调用和 `VIDIOC_S_FMT` 命令来设置摄像头设备的参数,例如图像格式、分辨率、帧率等。 4. 分配缓冲区:可以使用 `ioctl()` 系统调用和 `VIDIOC_REQBUFS` 命令来分配摄像头设备的缓冲区。 5. 将缓冲区映射到用户空间:可以使用 `mmap()` 系统调用将摄像头设备的缓冲区映射到用户空间中。 6. 启动摄像头采集:可以使用 `ioctl()` 系统调用和 `VIDIOC_STREAMON` 命令来启动摄像头采集。 7. 读取采集的数据:可以使用 `read()` 系统调用从摄像头设备的缓冲区中读取采集到的数据。 8. 停止摄像头采集:可以使用 `ioctl()` 系统调用和 `VIDIOC_STREAMOFF` 命令来停止摄像头采集。 9. 释放缓冲区:可以使用 `ioctl()` 系统调用和 `VIDIOC_REQBUFS` 命令来释放摄像头设备的缓冲区。 10. 关闭摄像头设备:可以使用 `close()` 系统调用来关闭摄像头设备。 这些步骤可以通过编写 C/C++ 代码来实现。在 Android 平台上,也可以使用 Camera2 API 或 CameraX API 来进行摄像头采集,这些 API 都提供了更高级别的抽象和功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值