Linux下C语言通过v4l2对USB摄像头数据的获取

Linux下C语言通过v4l2对USB摄像头数据的获取 毛巾卷 2024-03-02 08:35:59 198

目录

usb摄像头

USB摄像头是一种通过USB接口连接到计算机的摄像头设备,用于捕捉图像和视频。它们通常具有内置的镜头和传感器,可以通过计算机上的软件应用程序进行控制和操作。USB摄像头广泛应用于视频通话、视频会议、网络直播、监控、视频录制等领域。
以下是一些USB摄像头的特点和优势:

  1. 易于安装和使用:USB摄像头通常只需插入计算机的USB端口即可自动识别和安装驱动程序,无需额外的电源供应。
  2. 高清图像和视频:许多USB摄像头支持高清图像和视频捕捉,可以提供清晰的视频质量。
  3. 灵活性:USB摄像头可以通过调整角度或位置来灵活拍摄不同的场景,适用于各种应用场景。
  4. 多功能性:USB摄像头通常具有多种功能,例如自动对焦、自动曝光、麦克风等,可以满足不同需求。
  5. 兼容性:USB接口是一种通用接口,几乎所有的计算机和设备都支持USB连接,因此USB摄像头具有较好的兼容性。
  6. 便携性:USB摄像头通常比传统的摄像头更小巧轻便,便于携带和移动。
    在选择USB摄像头时,您可以根据自己的需求和预算考虑一些因素,例如摄像头的分辨率、帧率、对焦方式、兼容性等。市场上有许多知名品牌生产的USB摄像头可供选择,您可以根据自己的需求选择适合的产品。

像我们笔记本电脑的摄像头就很多都是通过usb摄像头来实现的,就是通过芯片去采集sensor的数据,进过isp处理,然后再转换为标准usb uvc数据输出,这样电脑端就可以直接免驱(现在基本都预装在里面了不需要驱动)使用了,通过自带的照相机或者opencv等就可以直接打开使用拉。像海思、星辰的芯片都支持这样子处理,很多usb摄像头也都是用他们的方案。

v4l2

Video4Linux2(简称V4L2)是Linux操作系统下用于支持视频设备的内核驱动程序框架。它允许应用程序访问和控制各种视频设备,如摄像头、视频采集卡等。V4L2提供了一组API,使开发人员能够编写应用程序来捕获、处理和显示视频流。
以下是一些V4L2的特点和功能:

  1. 标准化接口:V4L2提供了标准化的接口,使开发人员能够编写与各种视频设备兼容的应用程序,而无需了解特定设备的细节。
  2. 支持多种视频设备:V4L2支持各种视频设备,包括USB摄像头、网络摄像头、视频采集卡等,使其成为Linux系统下广泛应用的视频设备驱动框架。
  3. 功能丰富:V4L2提供了丰富的功能和控制选项,如捕获图像、设置视频格式、调整曝光、对焦、白平衡等。
  4. 性能优化:V4L2旨在提供高性能的视频捕获和处理能力,可以满足各种实时视频应用的需求。
  5. 开源和社区支持:V4L2是开源项目,得到了广泛的社区支持和贡献,持续改进和更新。
    使用V4L2的应用程序可以通过调用相应的API来与视频设备进行交互,从而实现视频捕获、处理和显示等功能。开发人员可以利用V4L2来开发各种视频应用程序,如视频监控、视频会议、视频流媒体等。

有时候项目对摄像头的要求不是那么高,同时为了加快进度,就会选择直接拿别人开发好的usb摄像头来使用,这样我们就不需要做isp部分的功能,只需要通过v4l2直接采集数据即可了,非常的方便快捷,而且有些厂家的mipi驱动也会直接帮你接上v4l2驱动,整套移植完后就可以直接用v4l2来操作而不需要去用厂家的sdk,比如jetson、瑞芯微。

程序实现

默认情况下Linux内核是已经打开相关驱动了,如果没有的话就需要自己去内核源码里面打开uvc相关再重新编译烧写。
正常情况下,接入usb video设备后在/dev目录下会有对应的video设备,我们程序先通过open去打开,再对摄像头的格式进行配置,具体摄像头支持什么配置可以看你买的摄像头的资料,读取一下配置看看是否配置正确,再mmap申请缓冲区把内核空间映射到用户空间,然后再开始摄像头采集,后面再循环读取队列里面的数据即可,队列里面的数据即是摄像头输出的数据,但要转为显示的话就要看你摄像头输出的是什么,然后你的显示器支持显示什么,转换一下格式再显示即可。

//
#include "v4l2.h"

struct VideoFrame MFrame[COUNT];//定义保存映射空间的首地址和长度

int video_linux_init(const char *device, unsigned short width, unsigned short height)
{
    printf("%s %d %d \n", device, width, height);
    //1.打开摄像头设备文件
    int vfd = open(device,O_RDWR);
    if(vfd < 0)
    {
            perror("Video device open!");
            return -1;
    }


    //2.设置摄像头格式
    struct v4l2_format sfmt;
    sfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        sfmt.fmt.pix.width = width;
        sfmt.fmt.pix.height = height;
        sfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//(设置视频输出格式,但是要摄像头支持)
    int sret = ioctl(vfd, VIDIOC_S_FMT, &sfmt);
    if(sret < 0)
    {
        perror("set fmt fail");
        close(vfd);
        return -1;
    }

    //3.获取摄像头当前的采集格式
    struct v4l2_format vfmt;
    vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    int ret = ioctl(vfd, VIDIOC_G_FMT, &vfmt);
    if(ret < 0)
    {
        perror("Get FMT fail!");
        exit(1);    
    }

    char fmtstr[8] = {0};
    memcpy(fmtstr,&vfmt.fmt.pix.pixelformat,4);

    if(strcmp(fmtstr,"YUYV") != 0)
    {
        close(vfd);
        return -1;
    }

    printf("width:%d,height:%d,format:%s\n",vfmt.fmt.pix.width,vfmt.fmt.pix.height,(char *)fmtstr);

    return vfd;
}

uint8_t video_linux_mmap(int vfd)
{
    memset(MFrame, 0, sizeof(MFrame));
    //向内核申请视频缓存
    struct v4l2_requestbuffers reqbuf;
    memset(&reqbuf, 0, sizeof(reqbuf));
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.count = COUNT;//申请缓存区个数
    reqbuf.memory = V4L2_MEMORY_MMAP;
    int ret = ioctl(vfd, VIDIOC_REQBUFS, &reqbuf);
    if(ret < 0)
    {
        perror("request fail");    
        return 0;
    }

    //把内核空间映射到用户空间
    struct v4l2_buffer kbuf;
    memset(&kbuf, 0, sizeof(kbuf));
    int i = 0;
    for(i=0; i<(int)reqbuf.count; i++)
    {
        //把申请的缓存区空间一个一个取出来与用户空间映射
        kbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        kbuf.index = i;
        kbuf.memory = V4L2_MEMORY_MMAP;
        if(ioctl(vfd, VIDIOC_QUERYBUF, &kbuf)<0)
        {
            perror("query buf fail");
            return 0;
        }    

        //映射
        MFrame[i].length = kbuf.length;
        MFrame[i].start = (char *)mmap(NULL, kbuf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vfd, kbuf.m.offset);
        if(MFrame[i].start == MAP_FAILED)
        {
            perror("mmap fail");
            return 0;
        }

        //缓存入队
        if(ioctl(vfd, VIDIOC_QBUF, &kbuf) < 0)
        {
            perror("qbuf fail");
            return 0;
        }
    }

    return 1;
}

uint8_t video_linux_start(int vfd)
{
    //启动摄像头数据采集
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(ioctl(vfd, VIDIOC_STREAMON, &type)<0)
    {
        perror("start fail");
        return 0;
    }

    return 1;
}

uint8_t video_linux_get_frame(int vfd, FrameBuffer *frame)
{
    //出队
    struct v4l2_buffer readbuf;
    readbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    readbuf.memory = V4L2_MEMORY_MMAP;
    if(ioctl(vfd, VIDIOC_DQBUF, &readbuf) < 0)//取一帧数据
    {
        perror("dqbuf fail");
        return 0;
    }

    memcpy(frame->buf, MFrame[readbuf.index].start, MFrame[readbuf.index].length);
    frame->length = readbuf.length;
    //入队
    if(ioctl(vfd, VIDIOC_QBUF, &readbuf) < 0)
    {
        perror("qbuf fail");
        return 0;
    }

    return 1;
}

uint8_t video_linux_stop(int vfd)
{
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    //采集结束
    if(ioctl(vfd, VIDIOC_STREAMOFF, &type)<0)
    {
        perror("stop fail");
        return 0;
    }
    //释放映射空间
    int i=0;
    for(i=0; i<COUNT; i++)
    {
        munmap(MFrame[i].start, MFrame[i].length);
    }

    return 1;
}

void video_linux_destroy(int vfd)
{
    close(vfd);    
}
声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 1 1 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
毛巾卷
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区