i.MX6 V4L2编程学习记录之获取单张图像数据(三)

在获取数据之前需要先初始化,初始化部分可移步imx6平台V4L2编程学习记录之初始化(二)查看。

接着上一篇,已经初始化完毕。那么需要说明的是,按我实现该功能的方法,获取单张图像数据只需要申请1块帧缓冲区即可,即上一篇的BUFFER_COUNT 赋值为1。

采集单张图像数据的流程:
1、启动视频采集。
2、将申请的帧缓冲区放入队列然后取出。
3、停止视频采集。

步骤(函数上方的宏及全局变量为参数说明)
1、3:由于启动和停止我做在了一个函数,就一起说明了

/*******************************************************************
** 函数描述:   视频采集命令
** 参数:      enable:1 - start, 0 - stop
********************************************************************/
void v4l2_enable_collect(int enable)
{
	int ret;
	int i;
	enum v4l2_buf_type type;
	
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	
	if (enable){
		ret = ioctl(video_fd, VIDIOC_STREAMON, &type);
	}else{
		ret = ioctl(video_fd, VIDIOC_STREAMOFF, &type); 
	}
	
	if (ret < 0) {  
	    printf("VIDIOC_STREAMON failed (%d)\n", ret);   
	}
}

2、将帧缓冲区放入队列而后取出使用
这个过程就好像拿桶打水一个道理。
可以这么理解:当视频采集开启后,视频流是一直不停的流动更新,将帧缓冲区放入队列后帧缓冲区装满的是放下的那一刻的图像数据,然后取出后即可使用,使用完再放入队列再取出。
一次操作为:放入、取出、使用。

之所以这样做的原因是如果在初始化时就将所有申请的帧缓冲区放入队列中则所有的帧缓冲区会储存初始化那一时刻的图像,然后当要拍摄一次照片获取数据时却是前一刻的数据。
在初始化将所有缓冲区放入队列的做法适用于获取视频流而不适用于单次拍摄。

将帧缓冲区放入队列:

static int video_fd;                               /* 摄像头设备文件描述符 */
static struct v4l2_buffer buf;                     /* 包含应用程序和驱动程序交换的数据的缓冲区 */

/*******************************************************************
** 函数描述:将帧缓冲区放入队列
********************************************************************/
static void qbuf()
{
	int ret;
	
	ret = ioctl(video_fd, VIDIOC_QBUF, &buf);
	if (ret < 0) {
	    printf("VIDIOC_QBUF failed (%d)\n", ret);
	}
}

从队列取出帧缓冲区:

static int video_fd;                               /* 摄像头设备文件描述符 */
static struct v4l2_buffer buf;                     /* 包含应用程序和驱动程序交换的数据的缓冲区 */

/*******************************************************************
** 函数名:     dqbuf
** 函数描述:从缓冲区取出一个缓冲帧,删除队列中的缓冲帧并传给buf
********************************************************************/
static void dqbuf()
{
	int ret;
	
	memset(&buf, 0, sizeof(buf));
	
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	
	ret = ioctl(video_fd, VIDIOC_DQBUF, &buf);
	
	if (ret < 0) {
	    printf("VIDIOC_DQBUF failed (%d)\n", ret);
	}
}

使用:
取出的数据的格式和所使用的摄像头有很大的关系。我有两个usb摄像头,一个摄像头的视频格式是YUYV的格式,一个是mjpeg的格式,获取的数据格式是不同的。
1、如果获取的是YUYV格式的数据要转换成jpg或者bmp需要另外处理,也可以直接保存成.yuv的格式,使用YUVPlayer这个软件即可查看。
2、如果获取的是mjpeg格式的数据直接保存为.jpg即可。
如果要对jpeg的数据进行处理的话就需要使用到libjpeg这个库。可以查看imx6平台交叉编译libjpegimx6平台交叉编译libjpeg-turbo

#define BUFFER_COUNT 1                             /* 缓存区数量 */

typedef struct {
	void *start;                                   /* 缓冲区的起始地址 */
	unsigned long length;                          /* 缓冲区的长度 */
} app_buffer_t;                /* 应用层缓冲区映射 */

static struct v4l2_buffer buf;                     /* 帧缓冲区 */
static app_buffer_t app_buffer[BUFFER_COUNT];      /* 应用层缓冲区映射 */

/*******************************************************************
** 函数描述:   储存图片
********************************************************************/
void v4l2_store_image()
{
	int i;
	FILE *fp = NULL;
	
	for(i = 0; i < 10; i++){
		qbuf();     /* 放入帧缓冲区 */
		dqbuf();    /* 取出帧缓冲区 */
		/* 以下部分为使用数据,简化了代码 */
		/* 由于初始化时已经将驱动的内存缓冲区映射到app_buffer中,所以可以直接取出 */
		/* buf.index在取出时会被自动赋值 */
		/* app_buffer[buf.index].start为数据起始位置的指针,app_buffer[buf.index].length为数据的长度 */
		fp = fopen("pic.yuv", "wb");
		
		if (NULL != fp) {
			fwrite(app_buffer[buf.index].start, app_buffer[buf.index].length, 1, fp);
		}
		
		fclose(fp);
		sleep(1);
	}
}
发布了62 篇原创文章 · 获赞 106 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/lang523493505/article/details/82886218