音视频之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

三、示例:抓取一帧图像

c 复制代码
#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;
}

测试结果:

bash 复制代码
Driver is uvcvideo, version is 267201
VIDIOC_S_FMT success
VIDIOC_REQBUFS success
buf length:1572864
get picture
相关推荐
轻松Ai享生活9 小时前
5 节课深入学习Linux Cgroups
linux
christine-rr10 小时前
linux常用命令(4)——压缩命令
linux·服务器·redis
kaixin_啊啊10 小时前
突破限制:Melody远程音频管理新体验
音视频
三坛海会大神55510 小时前
LVS与Keepalived详解(二)LVS负载均衡实现实操
linux·负载均衡·lvs
東雪蓮☆10 小时前
深入理解 LVS-DR 模式与 Keepalived 高可用集群
linux·运维·服务器·lvs
乌萨奇也要立志学C++10 小时前
【Linux】进程概念(二):进程查看与 fork 初探
linux·运维·服务器
ai产品老杨11 小时前
解锁仓储智能调度、运输路径优化、数据实时追踪,全功能降本提效的智慧物流开源了
javascript·人工智能·开源·音视频·能源
MThinker11 小时前
02-Media-8-uvc_with_csc.py 使用硬件解码的USB摄像头(UVC)捕获视频并显示的程序
音视频·智能硬件·micropython·canmv·k230
向阳花开_miemie11 小时前
Android音频学习(十八)——混音流程
学习·音视频
獭.獭.12 小时前
Linux -- 信号【上】
linux·运维·服务器