Linux v4l2-util移植及使用

一、下载链接

链接: https://www.linuxtv.org/downloads/v4l-utils/v4l-utils-1.22.1.tar.bz2

二、解压

解压:tar xvfj v4l-utils-1.22.1.tar.bz2 && cd v4l-utils-1.22.1

 配置编译选项:

 ./configure -h

export PATH=your_path/aarch64-linux-gnu/bin:$PATH //设置交叉编译工具链 

export PATH=your_path/aarch64-linux-gnu/bin:$PATH //设置交叉编译工具链

 export PKG_CONFIG_LIBDIR=your_path/aarch64-linux-gnu/lib //设置交叉编译库

 ./configure --host=aarch64-linux-gnu --prefix=your_install_path //设置安装位置,生成makefile

三、 一个简单的摄像头RGB 采集代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <errno.h>

struct plane_start {
	void * start;
};

struct buffer {
	struct plane_start* plane_start;
	struct v4l2_plane* planes_buffer;
};

int main(int argc, char **argv)
{
	int fd;
	fd_set fds;
	FILE *file_fd;
	struct timeval tv;
	int ret = -1, i, j, r;
	int num_planes;
	struct v4l2_capability cap;
	struct v4l2_format fmt;
	struct v4l2_requestbuffers req;
	struct v4l2_buffer buf;
	struct v4l2_plane* planes_buffer;
	struct plane_start* plane_start;
	struct buffer *buffers;
	enum v4l2_buf_type type;
	int num = 0;
	struct v4l2_plane *tmp_plane;

	if (argc != 4) {
		printf("Usage: v4l2_test <device> <frame_num> <save_file>\n");
		printf("example: v4l2_test /dev/video0 10 test.yuv\n");
		return ret;
	}

	fd = open(argv[1], O_RDWR);

	if (fd < 0) {
		printf("open device: %s fail\n", argv[1]);
		goto err;
	}

	file_fd = fopen(argv[3], "wb+");
	if (!file_fd) {
		printf("open save_file: %s fail\n", argv[3]);
		goto err1;
	}

	if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
		printf("Get video capability error!\n");
		goto err1;
	}

	if (!(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE_MPLANE)) {
		printf("Video device not support capture!\n");
		goto err1;
	}

	printf("Support capture!\n");

	memset(&fmt, 0, sizeof(struct v4l2_format));
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
	fmt.fmt.pix_mp.width = 3840;
	fmt.fmt.pix_mp.height = 2160;
	fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_RGB24;
	fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;

	if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {  //1、摄像头采集参数设置
		printf("Set format fail\n");
		goto err1;
	}

	printf("width = %d\n", fmt.fmt.pix_mp.width);
	printf("height = %d\n", fmt.fmt.pix_mp.height);
	printf("nmplane = %d\n", fmt.fmt.pix_mp.num_planes);

	req.count = 4;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
	req.memory = V4L2_MEMORY_MMAP;
	if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) { //2、申请4块帧缓冲
		printf("Reqbufs fail\n");
		goto err1;
	}

	printf("buffer number: %d\n", req.count);

	num_planes = fmt.fmt.pix_mp.num_planes;

	buffers = (struct buffer *)malloc(req.count * sizeof(*buffers)); //

	for(i = 0; i < req.count; i++) {
		memset(&buf, 0, sizeof(buf));
		planes_buffer = (struct v4l2_plane* )calloc(num_planes, sizeof(*planes_buffer));
		plane_start = (struct plane_start*)calloc(num_planes, sizeof(*plane_start));
		memset(planes_buffer, 0, sizeof(*planes_buffer));
		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;	//多平面输入设备
		buf.memory = V4L2_MEMORY_MMAP;
		buf.m.planes = planes_buffer;	//多平面输入设备的第几个plane,planes_buffer + index
		buf.length = num_planes;		//多平面输入设备表示几个平面,VIDEO_CAPTUR表示缓冲区长度
		buf.index = i;
		if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) {  //设置缓冲区,包括映射方式/ID等,获取到buf的offset,VIDEO_CAPTURE_MPLANE是buf.m.planes->m.mem_offset
			printf("Querybuf fail\n");					//VIDEO_CAPTURE是buf.m.mem_offset
			req.count = i;
			goto err2;
		}

		(buffers + i)->planes_buffer = planes_buffer;  //第几个缓冲区的多平面起始点
		(buffers + i)->plane_start = plane_start;
		for(j = 0; j < num_planes; j++) {
			printf("plane[%d]: length = %d\n", j, (planes_buffer + j)->length);
			printf("plane[%d]: offset = %d\n", j, (planes_buffer + j)->m.mem_offset);
			(plane_start + j)->start = mmap (NULL /* start anywhere */,		//第几个缓冲区第几个平面的内存映射起始点
									(planes_buffer + j)->length,
									PROT_READ | PROT_WRITE /* required */,
									MAP_SHARED /* recommended */,
									fd,
									(planes_buffer + j)->m.mem_offset);
			if (MAP_FAILED == (plane_start +j)->start) {
				printf ("mmap failed\n");
				req.count = i;
				goto unmmap;
			}
		}
	}

	printf("num_planes:%d\n",num_planes);

	for (i = 0; i < req.count; ++i) {	//监听缓冲区是否更新,如果有,获取缓冲区,取出RGB 数据
		memset(&buf, 0, sizeof(buf));

		buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
		buf.memory      = V4L2_MEMORY_MMAP;
		buf.length   	= num_planes;
		buf.index       = i;
		buf.m.planes 	= (buffers + i)->planes_buffer;

		if (ioctl (fd, VIDIOC_QBUF, &buf) < 0)
			printf ("VIDIOC_QBUF failed\n");
	}

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

	if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
		printf ("VIDIOC_STREAMON failed\n");

	tmp_plane = (struct v4l2_plane *)calloc(num_planes, sizeof(*tmp_plane));

	printf("num_planes:%d\n",num_planes);

	while (1){
		FD_ZERO (&fds);
		FD_SET(fd, &fds);
		tv.tv_sec = 5;
		tv.tv_usec = 0;

		r = select (fd + 1, &fds, NULL, NULL, &tv);

		if (-1 == r)
		{
			if (EINTR == errno)
				continue;
			printf ("select err\n");
		}
		if (0 == r)
		{
			fprintf (stderr, "select timeout\n");
			exit (EXIT_FAILURE);
		}

		memset(&buf, 0, sizeof(buf));
		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.m.planes = tmp_plane;
		buf.length = num_planes;
		if (ioctl (fd, VIDIOC_DQBUF, &buf) < 0)
			printf("dqbuf fail\n");

		for (j = 0; j < num_planes; j++) {
			printf("plane[%d] start = %p, bytesused = %d\n", j, ((buffers + buf.index)->plane_start + j)->start, (tmp_plane + j)->bytesused);
			fwrite(((buffers + buf.index)->plane_start + j)->start, (tmp_plane + j)->bytesused, 1, file_fd);
		}

		num++;
		if(num >= atoi(argv[2]))
			break;

		if (ioctl (fd, VIDIOC_QBUF, &buf) < 0)
			printf("failture VIDIOC_QBUF\n");
	}

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
	if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0)
		printf("VIDIOC_STREAMOFF fail\n");

	free(tmp_plane);

	ret = 0;
unmmap:
err2:
	for (i = 0; i < req.count; i++) {
		for (j = 0; j < num_planes; j++) {
			if (MAP_FAILED != ((buffers + i)->plane_start + j)->start) {
				if (-1 == munmap(((buffers + i)->plane_start + j)->start, ((buffers + i)->planes_buffer + j)->length))
					printf ("munmap error\n");
			}
		}
	}

	for (i = 0; i < req.count; i++) {
		free((buffers + i)->planes_buffer);
		free((buffers + i)->plane_start);
	}

	free(buffers);

	fclose(file_fd);
err1:
	close(fd);
err:
	return ret;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: v4l2-ctl是一个命令行工具,用于对Video4Linux2 (V4L2)设备进行控制和调试。该工具的源代码可以在Linux内核源代码树中的/usr/src/linux-headers-<kernel-version>/tools/media/v4l2-utils/v4l2-ctl目录下找到。 v4l2-ctl的源代码主要包含了关于V4L2设备的控制和配置的功能实现。其中包括设备的打开和关闭、格式的设置、视频参数的设置以及缓冲区配置等。 在源代码中,核心的功能主要由v4l2-ctl.c文件实现。其中包含了命令行参数的解析和设备控制的实现代码。v4l2-ctl-util.c文件主要为工具提供了一些常见的实用函数,如日志输出、时间戳计算和缓冲区地址转换等。 此外,在源代码中,还涉及了一些相关的头文件和宏定义。例如,v4l2-ctl.h文件中包含了一些用于V4L2控制的结构体、枚举和函数原型的定义;v4l2-ioctl.h则包含了一些与ioctl系统调用相关的宏定义和结构体定义。 总之,v4l2-ctl的源代码提供了一个基本的框架,可以方便地扩展和修改其功能,从而满足不同应用场景的需求。它也可以作为学习V4L2设备控制和驱动开发的案例参考。 ### 回答2: v4l2-ctl 是一个基于 V4L2 接口的命令行工具,用于控制视频设备。其代码位于 Linux 内核源码的 drivers/media/v4l2-core/v4l2-ctl.c 文件中。 v4l2-ctl 主要功能包括列出和设置视频设备的属性(如亮度、对比度、饱和度等),获取并输出视频设备的参数以及测试视频设备的性能等。该工具实现了向设备发送 IOCTL 命令并读取响应数据的逻辑,因此可以将其视为 V4L2 接口的命令行接口。 在源码中,v4l2-ctl 主要由 main() 函数和一系列辅助函数构成。其中,main() 函数主要负责解析用户输入的命令行参数,并调用相应的函数实现对视频设备的操作。辅助函数包括获取设备信息、列出和修改设备属性等,大部分代码都是基于 V4L2 的 IOCTL 接口实现的。 总之,v4l2-ctl 的源码并不复杂,但实现了对视频设备的基本控制和调试功能,为后续开发者提供了不少便利。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值