音视频之V4L2的应用

  V4L2是Video for Linux2的简称,是Linux操作系统中支持实时视频采集的设备驱动和一系列API的集合。V4L2驱动会在文件系统中创建/dev/video*设备节点供视频采集操作使用。

一、V4L2的用法

  首先通过open()函数打开设备节点获取文件描述符,使用ioctl()函数查询设备状态、查询/设置参数、获取视频等。

二、常见命令如下

  • VIDIOC_REQBUFS:分配内存
  • VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
  • VIDIOC_QUERYCAP:查询支持的能力
  • VIDIOC_ENUM_FMT:获取支持的视频格式
  • VIDIOC_S_FMT:设置频捕获格式
  • VIDIOC_G_FMT:读取频捕获格式
  • VIDIOC_TRY_FMT:验证当前显示格式
  • VIDIOC_CROPCAP:查询驱动的裁剪能力
  • VIDIOC_S_CROP:设置视频信号的边框
  • VIDIOC_G_CROP:读取视频信号的边框
  • VIDIOC_QBUF:把数据放回缓存队列
  • VIDIOC_DQBUF:把数据从缓存中读取出来
  • VIDIOC_STREAMON:开始视频显示
  • VIDIOC_STREAMOFF:结束视频显示
  • VIDIOC_QUERYSTD:当前视频设备支持的标准,例如PAL或NTSC

三、示例:抓取一帧图像

#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/videodev2.h>

int video_camera_capture_test(char *dev_name)
{
    int fd = 0;
    void *start = NULL;
    FILE *mjpeg_fp = NULL;
    enum v4l2_buf_type type;
    struct v4l2_buffer buf;
    struct v4l2_capability cap;
    struct v4l2_format fmt;
	struct v4l2_requestbuffers req;

    fd = open(dev_name, O_RDWR|O_NONBLOCK);
    if (fd < 0) {
        printf("open dev[%s] failed\n", dev_name);
        return -1;
    }

    if (ioctl(fd, VIDIOC_QUERYCAP, &cap)<0) {
        printf("Not a v4lv2 driver.\n");
        return -1;
    }

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        printf("%s is not a video capture device\n", VIDEO_DEV);
        return -1;
    }
    
    if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
        printf("%s does not support streaming i/o\n", VIDEO_DEV);
        return -1;
    }

    printf("Driver is %s, version is %i\n", cap.driver, cap.version);

    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
    fmt.fmt.pix.width = 1080;
    fmt.fmt.pix.height = 720;
    fmt.fmt.pix.field = V4L2_FIELD_ANY;
    if(ioctl(fd, VIDIOC_S_FMT, &fmt) != 0) {
        printf("VIDIOC_S_FMT failed: %s. Read-only driver maybe ?\n",
                strerror(errno));
    } else {
        printf("VIDIOC_S_FMT success\n");
    }

	memset(&req,0,sizeof(req));
	req.count = 1;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;

	if (ioctl(fd, VIDIOC_REQBUFS, &req)<0) {
		printf("Error requesting info on mmap'd buffers: %s\n",
		        strerror(errno));
		return -1;
	} else {
        printf("VIDIOC_REQBUFS success\n");
    }

    memset(&buf,0,sizeof(buf));

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = 0;

    if (ioctl(fd, VIDIOC_QUERYBUF, &buf)<0){
        printf("Could not VIDIOC_QUERYBUF : %s\n",strerror(errno));
        return -1;
    }

    start = mmap(NULL /* start anywhere */,
        buf.length,
        PROT_READ | PROT_WRITE /* required */,
        MAP_SHARED /* recommended */,
        fd, buf.m.offset);

    printf("buf length:%d\n", buf.length);

    if (start==NULL){
        printf("Could not mmap: %s\n",strerror(errno));
        return -1;
    }

	memset(&buf, 0, sizeof(buf));
	buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory      = V4L2_MEMORY_MMAP;
	buf.index       = 0;
	if (-1 == ioctl (fd, VIDIOC_QBUF, &buf)){
		printf("VIDIOC_QBUF failed: %s\n", strerror(errno));
		return -1;
	}

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (-1 == ioctl(fd, VIDIOC_STREAMON, &type)){
        printf("VIDIOC_STREAMON failed: %s\n",strerror(errno));
        return -1;
    }

    memset(&buf,0,sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    int times = 10;
    while (times--) {
        if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
            switch (errno) {
                case EAGAIN:
                    usleep(20000);
                    break;
                default: 
                    printf("VIDIOC_DQBUF failed: %s\n",strerror(errno));
            }
        } else {
            printf("get picture\n");
            mjpeg_fp = fopen("./output.jpeg", "w");
            fwrite(start, buf.bytesused, 1, mjpeg_fp);
            fclose(mjpeg_fp);
            break;
        }
    }

    return 0;
}

int main(int argc, char *argv[])
{
    video_camera_capture_test("/dev/video0");

	return 0;
}

测试结果:

Driver is uvcvideo, version is 267201
VIDIOC_S_FMT success
VIDIOC_REQBUFS success
buf length:1572864
get picture
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪游东戴河

你就是这个世界的唯一

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值