V4L2学习笔记
+ -

Linux打开V4L2摄像头并存储Camera数据流

2022-05-30 1391 1

Linux系统下打开UVC摄像头,并将从CAMERA读取到的数据存储在文件中。

源代码版权归老吕、所有。感谢老吕、的无私贡献。
v4l2_capture_demo.c

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

void enumerate_menu(int fd);
unsigned int CAP_BUF_NUM=4;

int get_now_time(){
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return tv.tv_sec*1000+tv.tv_usec/1000;
}

//保存内核映射缓冲区的地址和大小
typedef struct VideoBuffer{
    void *start;
    size_t length;
}VideoBuffer;
unsigned int CAP_W=640;
unsigned int CAP_H=480;
int main(int argc,char **argv){
    if(argc!=3){
        printf("默认分辨率为%d*%d\n",CAP_W,CAP_H);
        printf("是否需要重新设定y/n:\n");
        char c;
        scanf("%c",&c);
        if(c=='y'){
          printf("请输入新的分辨率:\n");
          scanf("%d%d",&CAP_W,&CAP_H);
          printf("你设置的分辨率为:%d*%d\n",CAP_W,CAP_H);
        }
    }

    int ret,i,j;
    int fd;
    fd=open("/dev/video1",O_RDWR);
    if(-1==fd){
        printf("open video0 failed!\n");
        return -1;
    }
    struct v4l2_capability cap;
    ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
    if(-1==ret){
        printf("ioctl VIDIOC_QUERYCAY ERROR!\n");
        close(fd);
        return -1;
    }
    if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)
    {
        printf("是视频输出设备\n");
    }
    if(cap.capabilities & V4L2_CAP_VIDEO_OVERLAY )
    {
        printf("支持预览功能\n");
    }
    if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
    {
       /* 如果为摄像头设备则打印摄像头驱动名字 */
        printf("Driver    Name: %s\n", cap.driver);
        printf("Card      Name: %s\n",cap.card);
    }
    else
    {
        printf("open file is not video\n");
        close(fd);
        return -2;
    }
    //查询支持的视频格式
    struct v4l2_fmtdesc fmtdesc;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 指定需要枚举的类型
    for (i = 0; ; i++)                      // 有可能摄像头支持的图像格式不止一种
    {
        fmtdesc.index = i;
        ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
        if (-1 == ret)                      // 获取所有格式完成
        {
            break;
        }
        /* 打印摄像头图像格式 */
        printf("Picture Format: %s\n", fmtdesc.description);
        /* 查询该图像格式所支持的分辨率 */
        struct v4l2_frmsizeenum frmsize;
        frmsize.pixel_format = fmtdesc.pixelformat;
        for (j = 0; ; j++)                  // 该格式支持分辨率不止一种
        {
            frmsize.index = j;
            ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize);
            if (-1 == ret)                  // 获取所有分辨率完成
            {
                break;
            }
            /* 打印分辨率 */
            printf("width: %d height: %d\n",
                    frmsize.discrete.width,frmsize.discrete.height);
        }
    }

    struct v4l2_format    fmt;
    memset(&fmt,0,sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width=CAP_W;
    fmt.fmt.pix.height=CAP_H;
    //fmt.fmt.pix.pixelformat= V4L2_PIX_FMT_YUYV;
    //fmt.fmt.pix.pixelformat= V4L2_PIX_FMT_MJPEG;
    fmt.fmt.pix.pixelformat= V4L2_PIX_FMT_H264;
    fmt.fmt.pix.field=V4L2_FIELD_NONE;
    if(ioctl(fd,VIDIOC_S_FMT,&fmt)==-1){
        printf("设置视频格式出错!\n");
        return -1;
    }
    struct v4l2_requestbuffers  req;
    req.count=CAP_BUF_NUM;
    req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory=V4L2_MEMORY_MMAP;
    if(-1==ioctl(fd,VIDIOC_REQBUFS,&req)){
        printf("申请帧缓冲出错!\n");
        return -1;
    }
    struct v4l2_buffer buf;
    VideoBuffer *buffers=calloc(req.count,sizeof(*buffers));
    unsigned int num;
    for(num=0;num<req.count;num++){
        memset(&buf,0,sizeof(buf));
        buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory=V4L2_MEMORY_MMAP;
        buf.index=num;
        if(ioctl(fd,VIDIOC_QUERYBUF,&buf)==-1){
            printf("分配内存出错!\n");
            return -1;
        }
        buffers[num].length=buf.length;
        //内存映射到用户空间
        buffers[num].start=mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);
        if(buffers[num].start==MAP_FAILED){
            printf("mmap error!\n");
            return -1;
        }
        //把帧放入队列
        if(ioctl(fd,VIDIOC_QBUF,&buf)==-1){
            printf("VIDIOC_QBUF error!\n");
            return -1;
        }

    }
    enum v4l2_buf_type type;
    type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    //开始采集
    ioctl (fd, VIDIOC_STREAMON, &type);
    memset(&buf,0,sizeof(buf));
    buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory=V4L2_MEMORY_MMAP;
    int count=100;
    FILE *fp;
    fp=fopen("./test_640_480.h264","wb+");
    if(fp==NULL){
       printf("open failed!\n");
       return -1;
    }
    long int lastTime=get_now_time();
    printf("start:%ld ms\n",lastTime);
    while(count--){
        printf("count:%d\n",count);
        //从队列中取出帧
        ret=ioctl(fd,VIDIOC_DQBUF,&buf);
        if(ret<0){  
            printf("VIDIOC_DQBUF FAILED!\n");
            return -1;
        }
        fwrite(buffers[buf.index].start,1,buf.bytesused,fp);
        //把帧放回队列
        ret=ioctl(fd,VIDIOC_QBUF,&buf);
        if(ret<0){
            printf("VIDIOC_QBUF FAILED!\n");
            fclose(fp);
            ioctl(fd,VIDIOC_STREAMOFF,&type);
            //断开内存映射连接
            for(i=0;i<CAP_BUF_NUM;i++){
                munmap(buffers[i].start,buffers[i].length);
            }
            close(fd);
            return -1;
        }
    }
    long int culTime=get_now_time();
    printf("Stop Time:%ld\n",culTime);
    printf("Total time:%ld ms\n",culTime-lastTime);
    fclose(fp);
    ioctl(fd,VIDIOC_STREAMOFF,&type);
    //断开内存映射连接
    for(i=0;i<CAP_BUF_NUM;i++){
        munmap(buffers[i].start,buffers[i].length);
    }
    close(fd);
    return 0;
}

0 篇笔记 写笔记

Linux V4L2 UVC摄像头框架浅析
V4L2 :video for linux version 2 ,是 Linux 里一套标准的视频驱动,它支持 UVC 标准的摄像头。本文来分析一下它的核心框架。整个v4l2的框架分为三层:在应用层,我们可以在 /dev 目录发现 video0 类似的设备节点,上层的摄像头程序打开设备节点进行......
打开UVC摄像头(特定类请求)数据分析
通过上节可以知道,设备支持4种的视频格式,且每种格式如下:MPJPG数据格式bFormatIndexbFrameIndex分辩率及及帧率11640x480x(30,15,10)121280x720x(30,15,10)131920x1080x(30,15......
UVC摄像头扩展单元功能的开发步骤是什么?
写在扩展单元的题外话本人在开发支持UVC扩展单元的directShow应用时,必现并不需要注册接口,只需要在其源过滤器参照后续的代码枚举相关的接口如IKsControl,IKsTopologyInfo定位到指定的扩展单元接口后,直接使用其对应的IKsControl::KsProperty即可实现扩展......
UVC摄像头的延迟reduce latency
一般的ISP,200W30帧来说,sensor进来缓存一帧就是33ms+vpu处理时间大概10ms+jpeg编码时间16ms+USB传输时间200KB大概7、8ms,最后就是windows上的显示延迟时间,如果JPEG编码出来缓存不止一帧,时间就更长了,所以一般会超过100多ms另外,网络传输如使......
UVC Windows下UVC摄像头数据分析
这里我们分析一款UVC摄像头来进行数据分析。环境:win10 x64工具:bushound,usbviewer将摄像头插入PC后,打开usbviewer工具,可以看到关于此摄像头的相关信息。可以看到,这个摄像头其实是一个USB复合设备,所以其对应的系统驱动为USBCCGP,然后再由USB复合设......
简易摄像头playcap工程代码及应用程序
playcap是一个小巧的UVC摄像头测试工具,使用的是DirectShow,本人之前装饰部分代码发布到此网址:http://www.usbzh.com/article/detail-553.html但经常有人说编译不过,其实这都是工程配置问题引起的。现将工程代码及编译好的应用程序提供详细的下载地......
Windows下UVC虚拟摄像头的实现
最近在Windows10 x64环境下,开发了一个虚拟UVC摄像头驱动。确切的来说这不是摄像头驱动,而是一个虚拟USB总线驱动。使用该虚拟总线驱动使用应用软件通过IOCTL控制总线子设备的创建与卸载。驱动安装完成后,是一个单纯的USB虚拟总线。应用软件通过发送自定义IOCTL码IOCTL_BUSEN......
UVC摄像头的关闭流程及抓包分析
通过UVC协议规范可以知道,UVC的数据传输支持USB四种传输中的批量传输和同步传输,所以对于UVC摄像头,当我们在摄像头正在工作时,需要停止摄像头工作,执行的操作是不同的。在Linux的源代码中,摄像头的流关闭是由函数uvc_video_stop_streaming完成的。代码比较简单,我们直接给......
UVC摄像头批量传输的StreamOn和StreamOff
在UVC规范中,UVC摄像头视频数据的传输方式支持两种,分别为批量传输和同步传输。UVC摄像头数据传输的格式按负载数据头的方式按帧进行打包传输。根据USB规范可知,同步传输方式是只要带中带有同步端点的接口,系统会定时从设备中读取数据,无论设备中是否有数据。而如要要停止数据的传输,只需要选中不带有同步......
Linux源码分析UVC摄像头的初始化流程分析
UVC摄像头的初始化发生在硬件被接入USB集线器中,设备初USB驱动识别为摄像头的后续初始化流程。和Windows的AddDevice驱动函数一样,Linux设备的创建和侦测是通过int uvc_probe函数实现的。其函数的调用关系如下://linux/v5.11.11/source/drive......
Windows对红外相机摄像头的支持
从Windows10的1607版本开始,USB视频类(UVC)驱动程序支持产生红外(IR)流的摄像头。红外摄像头相机捕捉场景的亮度值,并通过USB以未压缩格式或压缩格式传输帧。这些相机及其流通过媒体捕获管道(pipeline)导出给应用程序。红外UVC摄像头相机帧类型红外UVC摄像头相机支持的格......
UVC 输入终端描述符
输入终端描述符用于向主机报告视频数据输入终端的相关信息。输入终端描述符使用bTerminalID描述输入终端的ID,且这个ID是惟一的。输入终端描述符位于视频控制接口中。输入终端描述符结构体定义如下:// Input Terminal Descriptortypedef struct _USB......
USB兼容驱动Composite层级引起的摄像头灯问题
跌跌撞撞,搞了快一个星期。一个定制厂商用的UVC摄像头指示搞了几天,今天算是可以蒙混过关了。关于调试过程中的一部分细节,可见本人的另一篇文章:关于UVC摄像头指示灯的调试过程总结 http://www.usbzh.com/article/detail-430.html在这里,可能只能算是总结二吧,没......
UVC 视频控制接口VC
视频控制接口包含于接口关联描述符IAD中,在USB设备枚举过程中,随配置描述符一起返回给主机。通过控制各UVC拓扑单元和终端,可以控制视频功能。视频控制接口包括以下:控制端点,是必须存在的,默认使用端点0.中断断点,一个可选(某些条件是必须的)的,用于状态返回。视频控制接口是访问视频功能内部的单个入......
关于UVC摄像头指示灯的调试过程总结
最近遇到了一个很是奇怪的UVC摄像头指示灯问题,现象如下:上层应用是一个会议系统软件,当需要进行会议时,点击会议按钮添加会议。这时应用软件打开摄像头,并开始与服务器进行网络连接。当然由于摄像头的打开,摄像头指示灯点亮。上层应用软件与服务器连接后,进行会议界面。这时突然摄像头指示灯熄灭,但会议正常,摄......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!