音视频开发---linux下V4L2拍照

 

目录

1. 流程说明

2. 代码说明

2.1 摄像头初始化

2.2 启动摄像头

2.3 拍照

2.4 停止摄像头

3. 参考链接


 

         V4L2(Video4linux2),是linux中关于视频设备的内核驱动。在Linux中,我们可以像访问普通文件一样对视频设备进行操作,V4L2也为我们在用户空间操作视频设备提供了一系列接口,使得应用程序可以使用统一的API操作不同的视频设备,极大简化了视频系统的开发和维护。本文基于一个拍照程序来介绍V4L2的使用。

1. 流程说明

        首先借用大神https://blog.csdn.net/eastmoon502136/article/details/8190262的一个流程图, 本人认为这个图还是很清晰的,就直接拿来了。

                  

      从流程图中也可以看出, 大部分操作都是借助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。

2. 代码说明

2.1 摄像头初始化

int cameral_init()
{
	// open camera device
	int ret;
	if( (video_fd = open(VIDEO_DEV_NAME,O_RDWR)) == -1){
		printf("open %s failed.\n", VIDEO_DEV_NAME);
		return -1;
	}

	// ioctl VIDIOC_QUERYCAP, 查询驱动功能信息, 通过VIDIOC_QUERYCAP也能判断当前是支持V4L还是V4L2
	struct v4l2_capability cap;
	if(ioctl(video_fd,VIDIOC_QUERYCAP,&cap)==-1){
		printf("VIDIOC_QUERYCAP failed.\n");
		return -1;
	}
	printf("ioctl VIDIOC_QUERYCAP:\n");
	printf("\tv4l2_capability.driver:%s\n", cap.driver);
	printf("\tv4l2_capability.card:%s\n", cap.card);
	printf("\tv4l2_capability.bus_info:%s\n", cap.bus_info);
	printf("\tv4l2_capability.version:%d\n", cap.version);
	printf("\tv4l2_capability.capabilities:%x\n", cap.capabilities);
	printf("\tv4l2_capability.reserved:%x %x %x %x\n", cap.reserved[0],cap.reserved[1],cap.reserved[2],cap.reserved[3]);
	printf("\n\n");


	

	//设置格式
	struct v4l2_format  format ; 
	format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; 
	format.fmt.pix.width  = WIDTH; 
	format.fmt.pix.height = HIGHT; 
	format.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;//可通过函数ioctl_VIDIOC_ENUM_FMT来获取摄像头支持的格式,我这里获取的是Motion-JPEG
 
	ret = ioctl(video_fd , VIDIOC_S_FMT , &format);
	if(ret != 0)
	{
		printf("VIDIOC_S_FMT fail");
		return -1 ; 
	}

	//获取当前格式, 从而可以判断设置操作是否成功.
	ioctl_VIDIOC_G_FMT();


	//申请buffer
	struct v4l2_requestbuffers  requestbuffer ; 
	requestbuffer.count = VIDEO_BUFFER_COUNT ; 
	requestbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; 
	requestbuffer.memory = V4L2_MEMORY_MMAP ; 
 
	ret = ioctl(video_fd , VIDIOC_REQBUFS , &requestbuffer);
	if(ret != 0)
	{
		printf("VIDIOC_REQBUFS fail\n");
		return -1  ;
	}
	printf("VIDIOC_REQBUFS  success.\n");
	

	struct v4l2_buffer buf; 
	int i;
	for(i = 0 ; i < VIDEO_BUFFER_COUNT ; i++)
	{
		buf.type =  V4L2_BUF_TYPE_VIDEO_CAPTURE ; 
		buf.memory = V4L2_MEMORY_MMAP ; 
		buf.index = i;
		//VIDIOC_QUERYBUF: 把VIDIOC_REQBUFS中分配的第i个数据缓存转换成物理地址
		if( (ret = ioctl(video_fd , VIDIOC_QUERYBUF , &buf)) != 0){
			printf("VIDIOC_QUERYBUF failed\n");
			return -1;
		}

		video_frame_length = buf.length;
		//将内核中的摄像头内存映射到用户空间,并返回用户空间的地址
		video_frame[i] = mmap(NULL,buf.length , PROT_READ | PROT_WRITE , MAP_SHARED , video_fd , buf.m.offset );
 

		//将缓冲区放入队列
		if( (ret = ioctl(video_fd , VIDIOC_QBUF , &buf)) != 0){
			printf("VIDIOC_REQBUFS failed\n");
		}


	}
	printf("camera init success\n");
}

 

2.2 启动摄像头

int cameral_start()
{
	//开启摄像头
	int ret ; 
	int on = 1 ; 
	ret = ioctl(video_fd , VIDIOC_STREAMON , &on);
	if(ret != 0)
	{
		printf("cameral start fail\n");
		return -1 ; 
	}
	return 0 ; 
}

2.3 拍照


void save_picture(char * frame, int frame_len)
{
	FILE *fp = fopen(PICTURE_FILE_NAME, "wb");
    if (fp < 0) {
        printf("open %s failed.\n",PICTURE_FILE_NAME);
        return ;
    }
	printf("open %s success.\n",PICTURE_FILE_NAME);
    fwrite(frame, 1, frame_len, fp);
    fclose(fp);
    printf("save %s success.\n",PICTURE_FILE_NAME); //%s, CAPTURE_FILE
}


int take_picture()
{
	int ret;
	struct v4l2_buffer  dqbuf,qbuf; 
	dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; 
	qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ; 
	dqbuf.memory = V4L2_MEMORY_MMAP ; 
	qbuf.memory = V4L2_MEMORY_MMAP ; 

    //从队列中取出一个缓冲区
	if( (ret = ioctl(video_fd , VIDIOC_DQBUF , &dqbuf)) != 0){
		printf("VIDIOC_DQBUF failed\n");
		return -1;
	}
    // 把数据保存成图片
	save_picture(video_frame[dqbuf.index], dqbuf.length);

    //把缓冲区还给队列
	qbuf.index = dqbuf.index;
	if( (ret = ioctl(video_fd , VIDIOC_QBUF , &qbuf)) != 0){
		printf("VIDIOC_QBUF failed\n");
		return -1;
	}
	return 0;
}

 

2.4 停止摄像头

int cameral_stop()
{
	//停止摄像头
	int ret ; 
	int off= 1 ; 
	ret = ioctl(video_fd , VIDIOC_STREAMOFF, &off);
	if(ret != 0)
	{
		printf("cameral stop fail\n");
		return -1 ; 
	}
	return 0 ;

}

编译: gcc main.c

3. 参考链接

https://blog.csdn.net/eastmoon502136/article/details/8190262

https://blog.csdn.net/morixinguan/article/details/51001713

代码下载:https://download.csdn.net/download/u011734326/11191689

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值