linux v4l2编程

参考文档:
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_JPEG

V4L2_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  3

int 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;
}   
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值