- 配置
- 获取一帧
/* 初始化 V4L2:打开设备、设置 YUYV 格式、申请 mmap 缓冲区、开始流 */
int camera_init(const char *dev, int w, int h)
{
struct v4l2_format fmt = {0};
struct v4l2_requestbuffers req = {0};
struct v4l2_buffer vbuf = {0};
enum v4l2_buf_type type;
cam_fd = open(dev, O_RDWR);
if (cam_fd < 0) { perror("open"); return -1; }
/* 协商 YUYV 格式 */
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = w;
fmt.fmt.pix.height = h;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(cam_fd, VIDIOC_S_FMT, &fmt) < 0) {
perror("VIDIOC_S_FMT"); close(cam_fd); return -1;
}
img_w = fmt.fmt.pix.width;
img_h = fmt.fmt.pix.height;
printf("camera: %dx%d YUYV\n", img_w, img_h);
/* 申请 V4L2 循环缓冲区(内存由驱动管理) */
req.count = BUF_NUM;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam_fd, VIDIOC_REQBUFS, &req) < 0) {
perror("VIDIOC_REQBUFS"); close(cam_fd); return -1;
}
for (int i = 0; i < BUF_NUM; i++) {
memset(&vbuf, 0, sizeof(vbuf));
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vbuf.memory = V4L2_MEMORY_MMAP;
vbuf.index = i;
if (ioctl(cam_fd, VIDIOC_QUERYBUF, &vbuf) < 0) {
perror("VIDIOC_QUERYBUF"); close(cam_fd); return -1;
}
buffers[i].length = vbuf.length;
buffers[i].start = mmap(NULL, vbuf.length, PROT_READ | PROT_WRITE,
MAP_SHARED, cam_fd, vbuf.m.offset);
if (buffers[i].start == MAP_FAILED) {
perror("mmap"); close(cam_fd); return -1;
}
}
/* 所有缓冲入队,准备采集 */
for (int i = 0; i < BUF_NUM; i++) {
memset(&vbuf, 0, sizeof(vbuf));
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vbuf.memory = V4L2_MEMORY_MMAP;
vbuf.index = i;
ioctl(cam_fd, VIDIOC_QBUF, &vbuf);
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(cam_fd, VIDIOC_STREAMON, &type) < 0) {
perror("VIDIOC_STREAMON"); close(cam_fd); return -1;
}
return 0;
}
/*
* 捕获一帧:DQBUF 取一帧 → 直接转换到 rgb_buf → QBUF 归还
* 不经过中间缓冲,mmap 数据直转 ARGB8888
*/
int camera_capture_frame(uint8_t *rgb_buf, int *size)
{
struct v4l2_buffer vbuf = {0};
fd_set fds;
struct timeval tv;
FD_ZERO(&fds);
FD_SET(cam_fd, &fds);
tv.tv_sec = 0;
tv.tv_usec = 200000;
int ret = select(cam_fd + 1, &fds, NULL, NULL, &tv);
if (ret <= 0) return -1;
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vbuf.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam_fd, VIDIOC_DQBUF, &vbuf) < 0) return -1;
/* 从 mmap 缓冲直接转换到输出缓冲,节省一次 memcpy */
yuyv_to_argb8888(buffers[vbuf.index].start, rgb_buf, img_w, img_h);
ioctl(cam_fd, VIDIOC_QBUF, &vbuf);
if (size) *size = img_w * img_h * 4;
return 0;
}
/*
* YUYV → ARGB8888 转换(BT.601 近似)
* 输入 4 字节 = 2 像素 (Y0 U Y1 V)
* 输出每像素 4 字节 (B G R A)
*/
static void yuyv_to_argb8888(const uint8_t *src, uint8_t *dst, int w, int h)
{
for (int i = 0; i < w * h / 2; i++) {
int y0 = src[0], u = src[1] - 128, y1 = src[2], v = src[3] - 128;
src += 4;
int r = (298 * y0 + 409 * v + 128) >> 8;
int g = (298 * y0 - 100 * u - 208 * v + 128) >> 8;
int b = (298 * y0 + 516 * u + 128) >> 8;
dst[0] = (uint8_t)(b < 0 ? 0 : (b > 255 ? 255 : b));
dst[1] = (uint8_t)(g < 0 ? 0 : (g > 255 ? 255 : g));
dst[2] = (uint8_t)(r < 0 ? 0 : (r > 255 ? 255 : r));
dst[3] = 0xFF;
dst += 4;
r = (298 * y1 + 409 * v + 128) >> 8;
g = (298 * y1 - 100 * u - 208 * v + 128) >> 8;
b = (298 * y1 + 516 * u + 128) >> 8;
dst[0] = (uint8_t)(b < 0 ? 0 : (b > 255 ? 255 : b));
dst[1] = (uint8_t)(g < 0 ? 0 : (g > 255 ? 255 : g));
dst[2] = (uint8_t)(r < 0 ? 0 : (r > 255 ? 255 : r));
dst[3] = 0xFF;
dst += 4;
}
}