【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A 4、使用v4l2调用usb摄像头拍照 c语言
[复制链接]
我买的官方的MIPI CSI摄像头,但现在没有提供支持系统下载了,还好有备选usb摄像头,只不过是100万像素的usb摄像头,拍的照片一点也不清楚,但用于学习还是够了的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>//O_RDWR宏
#include <unistd.h>//open close函数
#include <linux/videodev2.h>//v4l2头文件
#include <sys/mman.h>//mmap函数
#include <sys/ioctl.h>//ioctl函数
#define video_width 640
#define video_height 480
char *userbuff[4]; /* 存放摄像头设备内核缓冲区映射后的用户内存地址 */
int userbuff_length[4]; /* 保存映射后的数据长度,释放缓存时要用 */
int video_fd;
//打开 & 设置相机
int v4l2_open()
{
int ret;
/* 1.打开摄像头设备 /dev/video0 */
video_fd = open("/dev/video0", O_RDWR);
if(video_fd < 0){
perror("打开摄像头设备失败");
return -1;
}
/* 3.获取摄像头的能力 (VIDIOC_QUERYCAP:是否支持视频采集、内存映射等) */
struct v4l2_capability capability;
if(0 == ioctl(video_fd, VIDIOC_QUERYCAP, &capability)){
if((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0){
perror("该摄像头设备不支持视频采集!");
close(video_fd);
return -2;
}
if((capability.capabilities & V4L2_MEMORY_MMAP) == 0){
perror("该摄像头设备不支持mmap内存映射!");
close(video_fd);
return -3;
}
}
/* 4.枚举摄像头支持的格式 (VIDIOC_ENUM_FMT:MJPG、YUYV等)
列举出每种格式下支持的分辨率 (VIDIOC_ENUM_FRAMESIZES) */
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //设置视频采集设备类型
int i = 0;
while(1){
fmtdesc.index = i++;
// 获取支持格式
if(0 == ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc)){
printf("支持格式:%s, %c%c%c%c\n", fmtdesc.description,
fmtdesc.pixelformat & 0xff,
fmtdesc.pixelformat >> 8 & 0xff,
fmtdesc.pixelformat >> 16 & 0xff,
fmtdesc.pixelformat >> 24 & 0xff);
// 列出该格式下支持的分辨率 VIDIOC_ENUM_FRAMESIZES & 默认帧率 VIDIOC_G_PARM
// 1.默认帧率
struct v4l2_streamparm streamparm;
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(0 == ioctl(video_fd, VIDIOC_G_PARM, &streamparm))
printf("该格式默认帧率 %d fps\n", streamparm.parm.capture.timeperframe.denominator);
// 2.循环列出支持的分辨率
struct v4l2_frmsizeenum frmsizeenum;
frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmsizeenum.pixel_format = fmtdesc.pixelformat; //设置成对应的格式
int j = 0;
printf("支持的分辨率有:\n");
while(1){
frmsizeenum.index = j++;
if(0 == ioctl(video_fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum))
printf("%d x %d\n", frmsizeenum.discrete.width, frmsizeenum.discrete.height);
else break;
}
printf("\n");
}else break;
}
/* 5.设置摄像头类型为捕获、设置分辨率、视频采集格式 (VIDIOC_S_FMT) */
struct v4l2_format format;
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* 视频采集 */
format.fmt.pix.width = video_width; /* 宽 */
format.fmt.pix.height = video_height; /* 高 */
format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; /* 设置输出类型:MJPG */
if(0 > ioctl(video_fd, VIDIOC_S_FMT, &format)){
perror("设置摄像头参数失败!");
close(video_fd);
return -4;
}
/* 6.向内核申请内存 (VIDIOC_REQBUFS:个数、映射方式为mmap)
将申请到的缓存加入内核队列 (VIDIOC_QBUF)
将内核内存映射到用户空间 (mmap) */
struct v4l2_requestbuffers requestbuffers;
requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
requestbuffers.count = 4; //申请缓存个数
requestbuffers.memory = V4L2_MEMORY_MMAP; //申请为物理连续的内存空间
if(0 == ioctl(video_fd, VIDIOC_REQBUFS, &requestbuffers)){
/* 申请到内存后 */
for(int i = 0; i < requestbuffers.count; i++){
/* 将申请到的缓存加入内核队列 (VIDIOC_QBUF) */
struct v4l2_buffer buffer;
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.index = i;
buffer.memory = V4L2_MEMORY_MMAP;
ret = ioctl(video_fd, VIDIOC_QBUF, &buffer);
if(ret < 0){
perror("查询内核空间失败");
close(video_fd);
return -6;
}
else if(0 == ret){
/* 加入内核队列成功后,将内存映射到用户空间 (mmap) */
userbuff[i] = (char *)mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, video_fd, buffer.m.offset);
userbuff_length[i] = buffer.length;//保存映射长度用于后期释放
}
}
}
else{
perror("申请内存失败!");
close(video_fd);
return -5;
}
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(0 > ioctl(video_fd, VIDIOC_STREAMON, &type)){
perror("打开视频流失败!");
return -7;
}
return 0;
}
/* 拍照 */
void v4l2_take()
{
char str[] = "./photo.jpg";
int ret;
/* 采集图片数据 */
//定义结构体变量,用于获取内核队列数据
struct v4l2_buffer buffer;
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* 从内核中捕获好的输出队列中取出一个 */
ret = ioctl(video_fd, VIDIOC_DQBUF, &buffer);
if(ret < 0){
perror("捕获图片失败!");
}
else if(0 == ret){
//保存到本地
int fd = open(str, O_RDWR | O_CREAT, 0777); //打开并创建一个新文件
write(fd, userbuff[buffer.index], buffer.bytesused);
printf("文件地址:%s\n", str);
close(fd);
}
/* 将使用后的缓冲区放回到内核的输入队列中 (VIDIOC_QBUF) */
ret = ioctl(video_fd, VIDIOC_QBUF, &buffer);
if(ret < 0){
perror("返回队列失败!");
}
}
//关闭
int v4l2_close()
{
/* 8.停止采集,关闭视频流 (VIDIOC_STREAMOFF)
关闭摄像头设备 & 关闭LCD设备 */
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(0 == ioctl(video_fd, VIDIOC_STREAMOFF, &type)){
/* 9.释放映射 */
for(int i = 0; i < 4; i++)
munmap(userbuff[i], userbuff_length[i]);
close(video_fd);
printf("关闭相机成功!\n");
return 0;
}
return -1;
}
int main(int argc, char* argv[]){
v4l2_open();
v4l2_take();
v4l2_close();
return 0;
}
执行结果:
打开photo.jpg
|