LINUX打开UVC相机代码
2024-03-28
152
1
- 打开 /dev/video0 设备。
- 查询设备的基本能力,并打印驱动程序、设备名称等信息。
- 设置视频格式为 640x480 分辨率,YUYV 格式,交错扫描。
- 请求 4 个内存缓冲区用于视频数据。
- 将这些缓冲区映射到用户空间。
- 将缓冲区放入视频队列。
- 开始数据流。
- 在需要时,可以添加视频处理或保存的代码。
- 停止数据流。
- 解除缓冲区的映射。
- 关闭设备。
在实际应用中,你可能需要根据相机的具体规格和要求调整这些设置,比如分辨率、像素格式、缓冲区数量等。此外,你还可以添加更多的 V4L2 ioctl 请求来配置其他功能,如设置帧率、曝光、白平衡等。
确保在使用 mmap() 映射内存时小心处理错误和释放资源,以避免内存泄漏或其他问题。
另外,代码中大量使用的是ioctl函数,其不同的控制码对应不同的功能。这些功能码在UVC驱动中对应的回调函数详见:UVC驱动向V4L2提供的回调接口v4l2_ioctl_ops https://www.usbzh.com/article/detail-1343.html
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#define CAMERA_DEVICE "/dev/video0"
#define OUTPUT_FILE "output.raw"
int main() {
int fd;
struct v4l2_capability cap;
struct v4l2_fmtdesc fmt_desc;
struct v4l2_frmsizeenum frmsize;
struct v4l2_format fmt;
struct v4l2_requestbuffers reqbuf;
enum v4l2_buf_type type;
// 打开设备
fd = open(CAMERA_DEVICE, O_RDWR);
if (fd == -1) {
perror("Failed to open device");
return EXIT_FAILURE;
}
// 查询设备能力
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
perror("Failed to query device capabilities");
close(fd);
return EXIT_FAILURE;
}
printf("Driver: %s\n", cap.driver);
printf("Card: %s\n", cap.card);
printf("Bus: %s\n", cap.bus_info);
// 打印支持的格式和分辨率
printf("Supported formats:\n");
fmt_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for (fmt_desc.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmt_desc) == 0; fmt_desc.index++) {
printf("- Format: %s\n", fmt_desc.description);
// 获取该格式支持的分辨率
frmsize.pixel_format = fmt_desc.pixelformat;
for (frmsize.index = 0; ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0; frmsize.index++) {
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
printf(" Resolution: %dx%d\n", frmsize.discrete.width, frmsize.discrete.height);
}
}
}
// 设置视频格式(这里选择第一个支持的格式和分辨率)
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // 这里选择 YUYV 格式
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
perror("Failed to set video format");
close(fd);
return EXIT_FAILURE;
}
// 请求内存缓冲区
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 4;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
perror("Failed to request buffers");
close(fd);
return EXIT_FAILURE;
}
// 将缓冲区映射到用户空间
struct v4l2_buffer buf;
void *buffer_start[reqbuf.count];
for (unsigned int i = 0; i < reqbuf.count; i++) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
perror("Failed to query buffer");
close(fd);
return EXIT_FAILURE;
}
buffer_start[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (buffer_start[i] == MAP_FAILED) {
perror("Failed to map buffer");
close(fd);
return EXIT_FAILURE;
}
}
// 将缓冲区放入队列
for (unsigned int i = 0; i < reqbuf.count; i++) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
perror("Failed to queue buffer");
close(fd);
return EXIT_FAILURE;
}
}
// 开始数据流
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
perror("Failed to start streaming");
close(fd);
return EXIT_FAILURE;
}
// 打开输出文件
FILE *output_file = fopen(OUTPUT_FILE, "wb");
if (!output_file) {
perror("Failed to open output file");
close(fd);
return EXIT_FAILURE;
}
// 获取并存储视频数据
for (unsigned int i = 0; i < 100; i++) { // 为简化示例,这里仅获取 100 帧数据
// 从队列中取出缓冲区
if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
perror("Failed to dequeue buffer");
close(fd);
fclose(output_file);
return EXIT_FAILURE;
}
// 写入数据到文件
fwrite(buffer_start[buf.index], buf.length, 1, output_file);
// 将缓冲区重新放入队列
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
perror("Failed to queue buffer");
close(fd);
fclose(output_file);
return EXIT_FAILURE;
}
}
// 停止数据流
if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
perror("Failed to stop streaming");
close(fd);
fclose(output_file);
return EXIT_FAILURE;
}
// 关闭输出文件
fclose(output_file);
// 解除映射
for (unsigned int i = 0; i < reqbuf.count; i++) {
if (munmap(buffer_start[i], buf.length) == -1) {
perror("Failed to unmap buffer");
close(fd);
return EXIT_FAILURE;
}
}
// 关闭设备
close(fd);
return EXIT_SUCCESS;
}