|
6 | 6 | #include <linux/videodev2.h> |
7 | 7 | #include <sys/mman.h> |
8 | 8 | #include <string.h> |
| 9 | +#include <poll.h> |
9 | 10 |
|
10 | 11 | #include "py/obj.h" |
11 | 12 | #include "py/runtime.h" |
|
23 | 24 | #define CAPTURE_HEIGHT 480 |
24 | 25 | #define OUTPUT_WIDTH 240 // Resize to 240x240 |
25 | 26 | #define OUTPUT_HEIGHT 240 |
26 | | -#define NUM_BUFFERS 2 // Use 2 buffers for robust streaming |
| 27 | +#define NUM_BUFFERS 4 // Use 4 buffers for continuous streaming |
27 | 28 |
|
28 | 29 | // Webcam object type |
29 | 30 | typedef struct _webcam_obj_t { |
@@ -72,46 +73,16 @@ static void resize_640x480_to_240x240(uint8_t *src, uint8_t *dst) { |
72 | 73 | static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) { |
73 | 74 | webcam_obj_t *self = MP_OBJ_TO_PTR(self_in); |
74 | 75 |
|
75 | | - // Initialize buffer structure |
76 | | - struct v4l2_buffer buf = {0}; |
77 | | - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
78 | | - buf.memory = V4L2_MEMORY_MMAP; |
79 | | - |
80 | | - // Find an available buffer |
81 | | - for (size_t i = 0; i < self->num_buffers; i++) { |
82 | | - buf.index = i; |
83 | | - |
84 | | - // Query buffer state to ensure it’s not already queued |
85 | | - WEBCAM_DEBUG_PRINT("webcam: Querying buffer state (index=%zu)\n", i); |
86 | | - if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) { |
87 | | - WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state (index=%zu, errno=%d)\n", i, errno); |
88 | | - mp_raise_OSError(errno); |
89 | | - } |
90 | | - |
91 | | - // Queue the buffer |
92 | | - WEBCAM_DEBUG_PRINT("webcam: Queuing buffer (index=%zu)\n", i); |
93 | | - if (ioctl(self->fd, VIDIOC_QBUF, &buf) == 0) { |
94 | | - break; // Successfully queued |
95 | | - } |
96 | | - WEBCAM_DEBUG_PRINT("webcam: Failed to queue buffer (index=%zu, errno=%d)\n", i, errno); |
97 | | - if (i == self->num_buffers - 1) { |
98 | | - mp_raise_OSError(errno); // No buffers available |
99 | | - } |
100 | | - } |
101 | | - |
102 | | - // Start streaming if not already started |
103 | | - if (!self->streaming) { |
104 | | - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
105 | | - WEBCAM_DEBUG_PRINT("webcam: Starting video streaming\n"); |
106 | | - if (ioctl(self->fd, VIDIOC_STREAMON, &type) < 0) { |
107 | | - WEBCAM_DEBUG_PRINT("webcam: Failed to start video streaming (errno=%d)\n", errno); |
108 | | - mp_raise_OSError(errno); |
109 | | - } |
110 | | - self->streaming = true; |
| 76 | + // Poll for available buffer |
| 77 | + struct pollfd pfd = { .fd = self->fd, .events = POLLIN }; |
| 78 | + WEBCAM_DEBUG_PRINT("webcam: Polling for available buffer\n"); |
| 79 | + if (poll(&pfd, 1, 1000) <= 0) { // 1-second timeout |
| 80 | + WEBCAM_DEBUG_PRINT("webcam: Poll timeout or error (errno=%d)\n", errno); |
| 81 | + mp_raise_OSError(ETIMEDOUT); |
111 | 82 | } |
112 | 83 |
|
113 | 84 | // Dequeue a buffer (capture frame) |
114 | | - memset(&buf, 0, sizeof(buf)); |
| 85 | + struct v4l2_buffer buf = {0}; |
115 | 86 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
116 | 87 | buf.memory = V4L2_MEMORY_MMAP; |
117 | 88 | WEBCAM_DEBUG_PRINT("webcam: Dequeuing buffer\n"); |
@@ -154,7 +125,7 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) { |
154 | 125 | buf.index = buf_index; |
155 | 126 | WEBCAM_DEBUG_PRINT("webcam: Re-queuing buffer (index=%zu)\n", buf_index); |
156 | 127 | if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { |
157 | | - WEBCAM_DEBUG_PRINT("webcam: Failed to re-queue buffer after capture (index=%zu, errno=%d)\n", buf_index, errno); |
| 128 | + WEBCAM_DEBUG_PRINT("webcam: Failed to re-queue buffer (index=%zu, errno=%d)\n", buf_index, errno); |
158 | 129 | mp_raise_OSError(errno); |
159 | 130 | } |
160 | 131 |
|
@@ -213,7 +184,7 @@ static mp_obj_t webcam_init(void) { |
213 | 184 |
|
214 | 185 | // Open the webcam device |
215 | 186 | WEBCAM_DEBUG_PRINT("webcam: Opening device %s\n", VIDEO_DEVICE); |
216 | | - self->fd = open(VIDEO_DEVICE, O_RDWR); |
| 187 | + self->fd = open(VIDEO_DEVICE, O_RDWR | O_NONBLOCK); // Non-blocking for polling |
217 | 188 | if (self->fd < 0) { |
218 | 189 | WEBCAM_DEBUG_PRINT("webcam: Failed to open device %s (errno=%d)\n", VIDEO_DEVICE, errno); |
219 | 190 | mp_raise_OSError(errno); |
@@ -245,6 +216,11 @@ static mp_obj_t webcam_init(void) { |
245 | 216 | mp_raise_OSError(errno); |
246 | 217 | } |
247 | 218 | self->num_buffers = req.count; |
| 219 | + if (self->num_buffers < 2) { |
| 220 | + WEBCAM_DEBUG_PRINT("webcam: Insufficient buffers allocated (%zu)\n", self->num_buffers); |
| 221 | + close(self->fd); |
| 222 | + mp_raise_OSError(ENOMEM); |
| 223 | + } |
248 | 224 |
|
249 | 225 | // Query and map buffers |
250 | 226 | for (size_t i = 0; i < self->num_buffers; i++) { |
@@ -277,6 +253,23 @@ static mp_obj_t webcam_init(void) { |
277 | 253 | close(self->fd); |
278 | 254 | mp_raise_OSError(errno); |
279 | 255 | } |
| 256 | + |
| 257 | + // Queue the buffer upfront |
| 258 | + memset(&buf, 0, sizeof(buf)); |
| 259 | + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 260 | + buf.memory = V4L2_MEMORY_MMAP; |
| 261 | + buf.index = i; |
| 262 | + WEBCAM_DEBUG_PRINT("webcam: Initial queuing of buffer %zu\n", i); |
| 263 | + if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { |
| 264 | + WEBCAM_DEBUG_PRINT("webcam: Failed to queue buffer %zu initially (errno=%d)\n", i, errno); |
| 265 | + for (size_t j = 0; j <= i; j++) { |
| 266 | + if (self->buffers[j].start != NULL) { |
| 267 | + munmap(self->buffers[j].start, self->buffers[j].length); |
| 268 | + } |
| 269 | + } |
| 270 | + close(self->fd); |
| 271 | + mp_raise_OSError(errno); |
| 272 | + } |
280 | 273 | } |
281 | 274 |
|
282 | 275 | // Create a tuple to hold the webcam object and method references |
|
0 commit comments