linux v4l2编程

来源:互联网 发布:外汇复盘软件 编辑:程序博客网 时间:2024/06/03 21:39

参考文档:
https://linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html

v4l2 : video for linux api two version //也就是linux系统下视频设备驱动好后,应用程序怎样调用相关的视频设备的编程接口
视频设备: 摄像头, 硬件编解码设备, 图形加速等。
设备文件: /dev/video* //有这些设备文件就是表示相应的视频设备已经驱动好,应用程序就可以使用v4l2的编程接口来实现视频设备的调用.


   #include <sys/ioctl.h>int ioctl(int fd, int request, ...);  ioctl函数用于把硬件的配置参数传给相应的设备驱动,让设备驱动根据指定的参数来配置硬件.    也可用于从设备驱动里获取出硬件的现用配置参数.     如ioctl用于配置摄像头的数据格式,所用的分辨率等参数的配置

函数返回值, 0表示配置成功, -1表示失败.
参数: fd表示打开设备文件得到文件描述符
request表示让设备驱动具体的操作行为 //如配置分辨率,配置数据格式等
…表示是可选的第三个参数,request是配置参数用时, 第三个参数应为准备好的配置参数变量的地址

V4L2里常用的ioctl介绍:

int ioctl(int fd, VIDIOC_QUERYCAP, struct v4l2_capability *argp); //可用于判断是否一个摄像头设备及所支持的操作接口int ioctl(int fd, VIDIOC_ENUM_FMT, struct v4l2_fmtdesc *argp); //可查询摄像头支持的图像格式int ioctl(int fd, VIDIOC_ENUM_FRAMESIZES, struct v4l2_frmsizeenum *argp); //可查询摄像头在某种图像格式下所能支持的图形分辨率int ioctl(int fd, VIDIOC_ENUMINPUT, struct v4l2_input *argp); //查询摄像头的选择。如前置/后置摄像头int ioctl(int fd, VIDIOC_S_FMT, struct v4l2_format *argp); //设置摄像头的图形格式int ioctl(int fd, VIDIOC_S_INPUT, int *argp); //设置使用前置/后置摄像头int ioctl(int fd, VIDIOC_S_CTRL, struct v4l2_control *argp); //设置摄像头的亮度,对比度等信息int ioctl(int fd, VIDIOC_REQBUFS, struct v4l2_requestbuffers *argp); //设置摄像头驱动申请图像缓冲区int ioctl(int fd, VIDIOC_QBUF/VIDIOC_DQBUF, struct v4l2_buffer *argp); //把指定的图像缓冲区加入/退出图像采集队列int ioctl(int fd, VIDIOC_STREAMON/VIDIOC_STREAMOFF , const int *argp); //启动/停止图像采集

以上结构体及其的成员的含议可在文档或/usr/include/linux/videodev2.h查看

usb摄像常用的图像格式:

    V4L2_PIX_FMT_YUYV       V4L2_PIX_FMT_MJPG    V4L2_PIX_FMT_JPEGV4L2_PIX_FMT_MJPG/V4L2_PIX_FMT_JPEG其实取出来就是一张完整的jpg图像.

查询是否一个摄像头设备的代码:

        int fd, i;        fd = open("/dev/video0", O_RDWR);        if (fd < 0)        {            perror("open");            return -1;        }           struct v4l2_capability  cap;        if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)        {            perror("get capability failed");            return -1;        }        printf("driver : %s\n", cap.driver);        if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))        {            printf("not a camera\n");            return -2;        }

获取摄像头设备支持的图像格式:

    int fd, i;    fd = open("/dev/video0", O_RDWR);    if (fd < 0)    {        perror("open");        return -1;    }       struct v4l2_fmtdesc fmtdesc;    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕捉类型    for (i = 0; ; i++)    {        fmtdesc.index = i; //获取摄像头所支持的第几种格式        if (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0)            break;        printf("%s\n", fmtdesc.description);    }

获取摄像头设备支持的图像格式的图像分辨率:

    int fd, i, j;    fd = open("/dev/video0", O_RDWR);    if (fd < 0)    {        perror("open");        return -1;    }       struct v4l2_fmtdesc fmtdesc;    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕捉类型    struct v4l2_frmsizeenum frmsize;    for (i = 0; ; i++)    {        fmtdesc.index = i; //获取摄像头所支持的第几种格式        if (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0)            break;        printf("%s\n", fmtdesc.description);        //每种格式下又会支持几种不同的分辨率        frmsize.pixel_format = fmtdesc.pixelformat; //把获取出来的格式传给frmsize`        for (j = 0; ; j++)        {            frmsize.index = j;            if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) < 0)                break;            printf("w = %d, h = %d\n", frmsize.discrete.width, frmsize.discrete.height);        }    }

获取摄像头图像数据的步骤流程:

#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include <linux/videodev2.h>#include <sys/ioctl.h>#include <sys/mman.h>#define W      640#define H      480#define FMT    V4L2_PIX_FMT_YUYV  // YUV 4:2:2 #define COUNT  3int main(void){    int fd, i;    char *data[COUNT];    // 1. 打开摄像头的设备文件    fd = open("/dev/video0", O_RDWR);    if (fd < 0)    {        perror("open");        return -1;    }       // 2. 检查是否是一个摄像头的设备文件    struct v4l2_capability  cap;    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)    {        perror("get capability failed");        return -1;    }    printf("driver : %s\n", cap.driver);    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))    {        printf("no a camera\n");        return -2;    }    // 3. 设置摄像头的数据格式, 分辨率    struct v4l2_format fmt;    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    fmt.fmt.pix.width = W;    fmt.fmt.pix.height = H;    fmt.fmt.pix.pixelformat = FMT;     if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)    {        perror("set format");        return -1;    }    // 4. 让驱动申请出存放图像数据的缓冲区, 每个缓冲区存放一帧图像    // 三个缓冲区    struct v4l2_requestbuffers bufs;    bufs.count = COUNT;    bufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    bufs.memory = V4L2_MEMORY_MMAP;   //指定申请出来的缓冲区用于mmap映射到用户进程使用    if (ioctl(fd, VIDIOC_REQBUFS, &bufs) < 0)    {        perror("request bufs");        return -2;    }    //5.查询缓冲区的状态。 前面让驱动申请出3个缓冲区, 实际上是分配出存放三个图像数据的连续的一块内存区域. 所以需要查出每个缓冲区的开始地址,大小等信息    struct v4l2_buffer buffer;    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    buffer.memory = V4L2_MEMORY_MMAP;    for (i = 0; i < COUNT ; i++)    {        buffer.index = i; //指定要查询的是第几个缓冲区           if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0)        {            perror("query buf");            return -2;        }           printf("%d: len = %d, offset = %d\n", i,  buffer.length, buffer.m.offset);        // 6. 把驱动里的缓冲区映射到用户进程来使用        data[i] = mmap(NULL, buffer.length, PROT_READ, MAP_SHARED, fd, buffer.m.offset);        if (MAP_FAILED == data[i])        {            perror("mmap failed");            return -1;        }               // 7. 把每个缓冲区加入图像数据的采集队列. 只有在队列里的缓冲区才会被驱动填充新的图像数据.        // 当取缓冲区的数据时,需让缓冲区退出采集队列,数据取好后, 再重新加入采集队列        if (ioctl(fd, VIDIOC_QBUF, &buffer) < 0)        {            perror("qbuf");            return -2;        }       }       //8. 让驱动开始采集图像数据, 采集到的数据将会填入缓冲区里    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)    {        perror("stream on");        return -3;    }       // 9. 取出缓冲区里的图像数据。先让一个已采好数据的缓冲区退出采集队列    // 取出10张图像    struct v4l2_buffer buf;    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    buf.memory = V4L2_MEMORY_MMAP;    int dst_fd;    char file_name[100];    //如果是MJPG/JPEG的图像格式,直接存起来就是一张jpg图像了.    for (i = 0; i < 10; i++)    {        if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0)        {            perror("dqbuf");            return -3;        }         sprintf(file_name, "my%02d.yuyv", i);        dst_fd = open(file_name, O_WRONLY|O_CREAT|O_TRUNC, 0644);        write(dst_fd, data[buf.index], buf.bytesused);        close(dst_fd);        //重新加入采集队列        ioctl(fd, VIDIOC_QBUF, &buf);    }    return 0;}   
原创粉丝点击