基于V4L2的C++语言YUYV格式图像捕获问题求助
I've dug into your code and the error you're seeing (libv4l2: error dequeuing buf: Resource temporarily unavailable), and there are a few key issues that are causing your YUYV capture to fail. Let's break down the problems and fix them step by step:
Core Issues Identified
- Broken
selectLoop Logic: You're using an assignment (errno = EINTR) instead of a comparison (errno == EINTR) in your retry condition, which breaks error handling. - Unrecycled Buffers: After dequeuing a buffer, you don't requeue it back to the capture pipeline, which quickly exhausts available buffers.
- Unverified Format Configuration: You assume your 1920x1080 YUYV request is accepted, but the camera might be silently adjusting parameters (or failing entirely).
- Incorrect Image Saving: You're writing raw YUYV data directly to a PPM file (which expects RGB), but this isn't the immediate cause of your runtime error.
Step-by-Step Fixes
1. Fix the select Loop Error
This is the most critical fix—your current loop corrupts errno and fails to handle non-interrupt errors correctly. Replace your select loop with this:
do { FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 2; tv.tv_usec = 0; r = select(fd + 1, &fds, NULL, NULL, &tv); } while (r == -1 && errno == EINTR); // Use comparison, not assignment
2. Requeue Buffers After Dequeuing
Every time you pull a buffer from the pipeline with VIDIOC_DQBUF, you must send it back with VIDIOC_QBUF to keep the capture process running. Here's how to update your capture logic:
// Capture first frame do { FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 2; tv.tv_usec = 0; r = select(fd + 1, &fds, NULL, NULL, &tv); } while (r == -1 && errno == EINTR); if (r == -1) { perror("select"); exit(EXIT_FAILURE); } clear_memmory(&(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; xioctl(fd, VIDIOC_DQBUF, &buf); printf("Buff index: %i\n", buf.index); // Save YUYV data (raw format, not PPM) sprintf(out_name, "image%03d.yuv", pic_count); fout = fopen(out_name, "w"); if (!fout) { perror("Cannot open image"); exit(EXIT_FAILURE); } fwrite(buffers[buf.index].start, buf.bytesused, 1, fout); fclose(fout); pic_count++; // Requeue the buffer for future captures xioctl(fd, VIDIOC_QBUF, &buf); // Repeat for second frame (with same select and requeue steps) do { FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 2; tv.tv_usec = 0; r = select(fd + 1, &fds, NULL, NULL, &tv); } while (r == -1 && errno == EINTR); if (r == -1) { perror("select"); exit(EXIT_FAILURE); } clear_memmory(&(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; xioctl(fd, VIDIOC_DQBUF, &buf); printf("Buff index: %i\n", buf.index); sprintf(out_name, "image%03d.yuv", pic_count); fout = fopen(out_name, "w"); if (!fout) { perror("Cannot open image"); exit(EXIT_FAILURE); } fwrite(buffers[buf.index].start, buf.bytesused, 1, fout); fclose(fout); pic_count++; xioctl(fd, VIDIOC_QBUF, &buf);
3. Verify Format Configuration
After setting the format, print the actual parameters returned by the camera to confirm it accepted your YUYV request:
xioctl(fd, VIDIOC_S_FMT, &format); printf("Device initialized with actual params:\n"); printf("Width: %d, Height: %d\n", format.fmt.pix.width, format.fmt.pix.height); printf("Pixel format: %s (0x%x)\n", format.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV ? "YUYV" : "Unknown", format.fmt.pix.pixelformat); printf("Expected buffer size: %d bytes\n", format.fmt.pix.sizeimage);
If the returned format doesn't match your request, check supported resolutions with:
v4l2-ctl --list-framesizes=YUYV v4l2-ctl --list-formats-ext
4. Convert YUYV to RGB for PPM (Optional)
To view your captured images, you'll need to convert YUYV to RGB24 before saving to PPM. Add this conversion function:
void yuyv_to_rgb24(const unsigned char* yuyv, unsigned char* rgb, int width, int height) { int i, j; for (i = 0; i < height; i++) { for (j = 0; j < width; j += 2) { unsigned char y0 = yuyv[(i * width + j) * 2]; unsigned char u = yuyv[(i * width + j) * 2 + 1]; unsigned char y1 = yuyv[(i * width + j + 1) * 2]; unsigned char v = yuyv[(i * width + j + 1) * 2 + 1]; // YUV to RGB conversion formula int r0 = y0 + 1.370705 * (v - 128); int g0 = y0 - 0.698001 * (v - 128) - 0.337633 * (u - 128); int b0 = y0 + 1.732446 * (u - 128); int r1 = y1 + 1.370705 * (v - 128); int g1 = y1 - 0.698001 * (v - 128) - 0.337633 * (u - 128); int b1 = y1 + 1.732446 * (u - 128); // Clamp values to 0-255 rgb[(i * width + j) * 3] = (r0 < 0 ? 0 : (r0 > 255 ? 255 : r0)); rgb[(i * width + j) * 3 + 1] = (g0 < 0 ? 0 : (g0 > 255 ? 255 : g0)); rgb[(i * width + j) * 3 + 2] = (b0 < 0 ? 0 : (b0 > 255 ? 255 : b0)); rgb[(i * width + j + 1) * 3] = (r1 < 0 ? 0 : (r1 > 255 ? 255 : r1)); rgb[(i * width + j + 1) * 3 + 1] = (g1 < 0 ? 0 : (g1 > 255 ? 255 : g1)); rgb[(i * width + j + 1) * 3 + 2] = (b1 < 0 ? 0 : (b1 > 255 ? 255 : b1)); } } }
Then use it when saving:
unsigned char* rgb_data = (unsigned char*)malloc(width * height * 3); yuyv_to_rgb24((unsigned char*)buffers[buf.index].start, rgb_data, width, height); sprintf(out_name, "image%03d.ppm", pic_count); fout = fopen(out_name, "w"); if (!fout) { perror("Cannot open image"); exit(EXIT_FAILURE); } fprintf(fout, "P6\n%d %d 255\n", width, height); fwrite(rgb_data, width * height * 3, 1, fout); fclose(fout); free(rgb_data);
Final Notes
The buffer recycling and select loop fixes should resolve your immediate "resource unavailable" error. If you still have issues, confirm your camera supports the exact resolution/format combination you're requesting using the v4l2-ctl commands above.
内容的提问来源于stack exchange,提问作者mcf1lmnfs

