linux v4l2 应用编程

编写v4l2应用程序基本步骤:

  1. 打开设备,打开一个文件准备输出摄像头数据
  2. 查看设备能力
  3. 根据需要设定设备输出格式,前提是摄像头支持
  4. 检查输入源,设定输入源
  5. 指定申请 VIDIOC_REQBUFS
  6. 映射buffer地址
  7. 把buffer放到输入队列
  8. 开启摄像头流
  9. select数据准备输入就绪
  10. 检查准备就绪的buffer
  11. 读出buffer到写到文件
  12. 把buffer添加到队列
  13. 关闭摄像头流
  14. 取消buffer映射
  15. 关闭设备文件和保存的摄像头流文件

代码:

#include "stdio.h"
#include "linux/videodev2.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "string.h"
#include 

#define CLEAR(type) memset(&type,0,sizeof(type))
#define PRINT_RET(ret) printf("%d ret: %d\n",__LINE__ ,ret)
void print_cap(struct v4l2_capability *cap);
void print_format( struct v4l2_format *format);
void print_input(struct v4l2_input*input);
void print_buff(struct v4l2_buffer*buff);
struct buf_addr{
    unsigned long size;
    void *addr;
};
int main(int argc,char*argv[])
{
    struct v4l2_capability cap;
    struct v4l2_format format;
    struct v4l2_input input;
    struct v4l2_requestbuffers req;
    struct v4l2_buffer buff;
    struct buf_addr buff_addr[5];
    fd_set fds;
    FILE *fp;
    int ret;
    int i;
    printf("-----------------test camera----------------\n");
    
    int fd = open("/dev/video0",O_RDWR|O_NONBLOCK);
    fp = fopen("./camera.yuv","wb");
    printf("fd: %d\n",fd);
    CLEAR(cap);
    CLEAR(format);
    CLEAR(input);
    CLEAR(req);
    CLEAR(buff);
    FD_ZERO(&fds);

    ret = ioctl(fd,VIDIOC_QUERYCAP,&cap);
    printf("1ret: %d\n",ret);
    input.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd,VIDIOC_ENUMINPUT,&input);PRINT_RET(ret);
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd,VIDIOC_G_FMT,&format);PRINT_RET(ret);

    req.count = 5;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    ret = ioctl(fd,VIDIOC_REQBUFS,&req);PRINT_RET(ret);

    for(i=0;i<5;i++){
        CLEAR(buff);
        buff.index = i;
        buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buff.memory = V4L2_MEMORY_MMAP;
        ret = ioctl(fd,VIDIOC_QUERYBUF,&buff);PRINT_RET(ret);
        buff_addr[i].size = buff.length;
        buff_addr[i].addr = mmap(NULL, buff.length,
        PROT_READ|PROT_WRITE, MAP_SHARED,
        fd,buff.m.offset);
        // printf("addr:%u size:%x\n",buff_addr[i].addr,buff_addr[i].size);
    }
    for(i=0;i<5;i++){
        CLEAR(buff);
        buff.index = i;
        buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buff.memory = V4L2_MEMORY_MMAP;
        ret = ioctl(fd,VIDIOC_QBUF,&buff);PRINT_RET(ret);
    }
    ret = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd,VIDIOC_STREAMON,&ret);PRINT_RET(ret);
    for(i=0;i<1000;i++){
        FD_ZERO(&fds);
        FD_SET(fd,&fds);
        struct timeval t;
        t.tv_sec = 5;
        t.tv_usec=5;
        ret = select(fd+1,&fds,NULL,NULL,&t);PRINT_RET(ret);

        CLEAR(buff);
       
        buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buff.memory = V4L2_MEMORY_MMAP;
        ret = ioctl(fd,VIDIOC_DQBUF,&buff);PRINT_RET(ret);
        
        /**********************************************/
        print_buff(&buff);
        fwrite(buff_addr[buff.index].addr,
        buff_addr[buff.index].size,1,fp);
        fflush(fp);
        /**********************************************/
        ret = ioctl(fd,VIDIOC_QBUF,&buff);PRINT_RET(ret);
    }
    print_cap(&cap);
    print_input(&input);
    print_format(&format);

    ret = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd,VIDIOC_STREAMOFF,&ret);PRINT_RET(ret);

    for(i=0;i<5;i++){
        munmap(buff_addr[i].addr,buff_addr[i].size);
    }

    close(fd);
    fclose(fp);
}


void print_cap(struct v4l2_capability *cap)
{
    if(!cap)return;
    printf("--------------print_cap------------------\n");
    printf("driver: %s\n",cap->driver);
    printf("card: %s\n",cap->card);
    printf("bus_info: %s\n",cap->bus_info);
    printf("version: %d\n",cap->version);
    printf("capabilities: %x\n",cap->capabilities);
    printf("device_caps: %x\n",cap->device_caps);
}   

void print_format( struct v4l2_format *format)
{
    if(!format)return;
    printf("--------------print_format------------------\n");
    printf("type: %d\n",format->type);
    printf("width: %d\n",format->fmt.pix.width);
    printf("height: %d\n",format->fmt.pix.height);
    printf("pixelformat: %c %c %c %c\n",
    format->fmt.pix.pixelformat&0xff,
    (format->fmt.pix.pixelformat>>8)&0xff,
    (format->fmt.pix.pixelformat>>16)&0xff,
    (format->fmt.pix.pixelformat>>24)&0xff);
    printf("field: %x\n",format->fmt.pix.field);
    printf("bytesperline: %d\n",format->fmt.pix.bytesperline);
    printf("sizeimage: %d\n",format->fmt.pix.sizeimage);
    printf("colorspace: %x\n",format->fmt.pix.colorspace);
}   

void print_input(struct v4l2_input*input)
{
    if(!input)return;
    printf("--------------print_input------------------\n");
    printf("index: %d\n",input->index);
    printf("name: %s\n",input->name);
    printf("type: %d\n",input->type);
    printf("audioset: %d\n",input->audioset);
    printf("tuner: %d\n",input->tuner);
}   

void print_buff(struct v4l2_buffer*buff)
{
    static unsigned long count=0;
    if(!buff)return;
    printf("--------------print_buff------------------\n");
    printf("count: %ld\n",count++);
    printf("index: %d\n",buff->index);
    printf("type: %d\n",buff->type);
    printf("bytesused: %d\n",buff->bytesused);
    printf("memory: %x\n",buff->memory);
    printf("length: %d\n",buff->length);
} 

注意:

  1. 文件打开时建议使用非阻塞读写权限方式
  2. 映射的时候使用共享映射
  3. 视频流就绪后select返回1
  4. 读取buffer的时候注意使用的索引不能溢出了
  5. buffer在读前需要出队列,读后入队列命令注意别写错了
  6. 在编程之前可以测试下摄像头是否正常可以用 xawtv 这个软件( install apt install xawtv)

我这里的摄像头输入的事yuyv格式,可用yuvplayer打开

linux v4l2 应用编程_第1张图片

播放器地址:播放yuv格式的视频支持常用的yuv和rgb格式-Linux文档类资源-CSDN文库

你可能感兴趣的:(linux应用编程,linux,运维,服务器)