V4L2 摄像头采集图像开发笔记

一、简介

  • 一般采集过程:打开视频设备 → 设定属性(裁剪、缩放)→ 设定采集方式 → 开始采集,并处理采集数据(循环) → 关闭视频设备
  • 头文件:<linux/videodev2.h>

二、查询设备属性:VIDIOC_QUERYCAP

  • 相关结构体
struct v4l2_capability{
    u8 driver[16];    //驱动名
    u8 card[32];      //设备名
    u8 bus_info[32];  //设备在系统中位置
    u32 version;      //驱动版本号
    u32 capabilities; //设备支持操作
    u32 reserved[4];  //保留字段
}
  • 原型:int ioctl(int fd,int request,struct v4l2_capability *argp);
  • ioctl(fd,VIDIOC_QUERYCAP,&CAP);    //显示设备信息
  • capabilities常用:V4L2_CAP_VIDEO_CAPTURE //是否支持图像获取

三、查询并显示所有支持格式:VIDIOC_ENUM_FMT

  • 原型:int ioctl(int fd,int request,struct v4l2_fmtdesc* argp)
  • 相关结构体
struct v4l2_fmtdesc{
    u32 index;        //要查询的格式序号,应用程序设置
    enum v4l2_buf_type type;      //帧类型,应用程序设置
    u32 flags;        //是否为压缩格式
    u8 description[32];    //格式名称
    u32 pixelformat;    //格式
    u32 reserved[4];    //保留
}

四、显示(设置)当前帧的相关信息

  • 相关结构体
struct v4l2_format{
   enum v4l2_buf_type type;    //帧类型,应用程序设置,一般为V4L2_BUF_TYPE_VIDEO_CAPTURE
   union fmt{
       struct v4l2_pix_format pix;    //视频设备使用
       struct v4l2_window win;
       struct v4l2_vbi_format vbi;
       struct v4l2_sliced_vbi_format sliced;
       u8 raw_data[200];
   };
}

struct v4l2_pix_format{
    u32 width;    //帧宽,单位是像素
    u32 height;    //帧高,单位是像素
    u32 pixelformat;    //帧格式
    enum v4l2_field field;
    u32 bytesperline;
    u32 sizeimage;
    enum v4l2_colorspace colorspace;
    u32 priv;
};
  • 显示当前帧信息:ioctl(fd,VIDIOC_G_FMT,&fmt) 【struct v4l2_format fmt;】
  • 设置当前帧信息:ioctl(fd,VIDIOC_S_FMT,&fmt)

五、视频输入输出

  • request:VIDIOC_G_INPUT 和 VIDIOC_S_INPUT 。一个video设备节点可能对应多个视频源,通过S进行切换,通过G查询当前输入输出的index。
  • 可通过VIDIOC_ENUMINPUT进行列举
  • 相关结构体
struct v4l2_input{
    _u32 index;    //which input
    _u8 name[32];    //label
    _u32 type;    //type of input
    _u32 audioset;    //associated audios
    _u32 tuner;    //associated tuner
    v4l2_std_id std;
    _u32 status;
    _u32 reserved[4];
};

六、Video standards

  • 存在如NTSC 和 PAL 等多种视频标准,查询支持或当前标准
  • 相关结构体
typedef u64 v4l2_std_id;    //64位数表示的标准

struct v4l2_standard{
    u32 index;
    v4l2_std_id id;
    u8 name[24];
    struct v4l2_fract frameperiod;    //frame,not fields
    u32 framelines;
    u32 reserved[4];
};

七、申请、管理缓冲区

1、向设备申请缓冲区VIDIOC_REQBUFS

//相关结构体,该结构体在应用时候都要手动设置
struct v4l2_requsetbuffers{
    U32 count;    //缓冲区内缓冲帧数
    enum v4l2_buf_type type;    //缓冲帧格式
    enum v4l2_memory memory;    //区别内存映射还是用户指针方式
    U32 reserved[2];
}

enum v4l2_memory{
    V4L2_MEMORY_MMAP,V4L2_MEMORY_USERPTR
}
//例,申请4个缓冲帧的缓冲区

struct v4l2_requestbuffers req;
req.count=4;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
ioctl(fd,VIDIOC_REQBUFS,&req);

2、获取缓冲帧地址、长度:VIDIOC_QUERYBUF

  • 原型:int ioctl(int fd,int request,struct v4l2_buffer *argp);

  • 相关结构体

struct v4l2_buffer{
    U32 index;    //buffer序号
    enum v4l2_buf_type;
    U32 byteused;    //已用帧数
    U32 flags;    //MMAP or USERPTR
    enum v4l2_field field;
    struct timeval tiemstamp;    //获取第一个字节的系统时间
    struct v4l2_timecode timecode;
    enum v4l2_memory memory;
    union m{
        U32 offset;    //只对MMAP有效,缓冲帧地址
        unsigned long userptr;
    }
    U32 length;//缓冲帧长度
    U32 input;
    U32 reserved;
}

3、内存映射(MMAP)和定义一个结构来映射每个缓冲帧

  • 头文件:#include<sys/mman.h>中的一些函数
void * mmap(void* addr,size_t length,int prot, int flags, int fd,off_t offset)
/*
addr:映射起始地址,一般填NULL让内核自动选择
length:被映射的内存长度
prot:标志映射后能否被读写,值有:PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE
flags:确定次内存映射是否能被其他进程共享,有:MAP_SHARED,MAP_PRIVATE
fd,offset:确定被映射的内存地址返回成功映射后的地址
*/


int munmap(void* addr,size_t length);
//断开映射,addr为地址,length为映射后内存长度
  • 自己定义的结构体
struct buffer{
    void* start;
    unsigned int length;
}*buffers;
  • 例:将4个已申请的缓冲帧映射到应用程序,用buffer指针记录
buffer=(buffer*)calloc(req.count,sizeof(*buffers));
for(unsigned int n_buffers=0;n_buffers<req.cout;n_buffers++){
    struct v4l2_buffer buf;
    memset(&buf,0,sizeof(buf));

    buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.index=n_buffers;
    buf.memory=V4L2_MEMORY_MMAP;
    //查询序号为n_buffers的缓冲区,得到起始物理地址和大小
    if(ioctl(fd,VIDIOC_QUERYBUF,&buf))
        exit(-1);
    //映射内存
    buffers[n_buffers].start=mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);
}

八、获取数据

1、启动/停止数据流

VIDIOC_STREAMON 和 VIDIOC_STREAMOFF

int ioctl(fd,~,&V4L2_BUF_VIDEO_CAPTURE)

2、把帧放入队列和从队列中取出帧

VIDIOC_QBUF   VIDIOC_DQBUF

3、获取一帧并且处理

struct v4l2_buffer;
clear(buf);
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
while(1){
    ioctl(fd,VIDIOC_DQBUF,&buf)
    process(buffer[buf.index].start)
}

猜你喜欢

转载自blog.csdn.net/weixin_39731083/article/details/81211117