diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index d06b8418..a380c6c0 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -23,9 +23,9 @@ #define CAPTURE_HEIGHT 480 #define OUTPUT_WIDTH 240 // Resize to 240x240 #define OUTPUT_HEIGHT 240 -#define NUM_BUFFERS 5 // Use 2 buffers, as it worked for two captures +#define NUM_BUFFERS 5 // Use 5 buffers, as it achieved 5 captures #define QUEUE_RETRIES 5 // Number of retry attempts for queueing -#define QUEUE_RETRY_DELAY_US 100000 // 50ms delay between retries +#define QUEUE_RETRY_DELAY_US 100000 // 100ms delay between retries // Webcam object type typedef struct _webcam_obj_t { @@ -70,6 +70,48 @@ static void resize_640x480_to_240x240(uint8_t *src, uint8_t *dst) { } } +// Reset streaming state +static void webcam_reset_streaming(webcam_obj_t *self) { + if (self->streaming) { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + WEBCAM_DEBUG_PRINT("webcam: Stopping video streaming for reset\n"); + if (ioctl(self->fd, VIDIOC_STREAMOFF, &type) < 0) { + WEBCAM_DEBUG_PRINT("webcam: Failed to stop video streaming (errno=%d)\n", errno); + } + self->streaming = false; + } + + // Re-queue all buffers + for (size_t i = 0; i < self->num_buffers; i++) { + struct v4l2_buffer buf = {0}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + // Query buffer state + WEBCAM_DEBUG_PRINT("webcam: Querying buffer state during reset (index=%zu)\n", i); + if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) { + WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state (index=%zu, errno=%d)\n", i, errno); + continue; + } + + // Queue buffer + WEBCAM_DEBUG_PRINT("webcam: Re-queuing buffer during reset (index=%zu)\n", i); + if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { + WEBCAM_DEBUG_PRINT("webcam: Failed to re-queue buffer during reset (index=%zu, errno=%d)\n", i, errno); + } + } + + // Restart streaming + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + WEBCAM_DEBUG_PRINT("webcam: Restarting video streaming\n"); + if (ioctl(self->fd, VIDIOC_STREAMON, &type) < 0) { + WEBCAM_DEBUG_PRINT("webcam: Failed to restart video streaming (errno=%d)\n", errno); + } else { + self->streaming = true; + } +} + // Capture a grayscale image static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) { webcam_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -84,6 +126,13 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) { buf.memory = V4L2_MEMORY_MMAP; buf.index = i; + // Query buffer state + WEBCAM_DEBUG_PRINT("webcam: Querying buffer state (index=%zu, attempt=%d)\n", i, attempt + 1); + if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) { + WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state (index=%zu, errno=%d)\n", i, errno); + continue; + } + WEBCAM_DEBUG_PRINT("webcam: Attempting to queue buffer (index=%zu, attempt=%d)\n", i, attempt + 1); if (ioctl(self->fd, VIDIOC_QBUF, &buf) == 0) { WEBCAM_DEBUG_PRINT("webcam: Successfully queued buffer (index=%zu)\n", i); @@ -94,12 +143,38 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) { } if (!queued && attempt < QUEUE_RETRIES - 1) { WEBCAM_DEBUG_PRINT("webcam: No buffers available, retrying after delay\n"); - usleep(QUEUE_RETRY_DELAY_US); // Wait 50ms before retrying + usleep(QUEUE_RETRY_DELAY_US); // Wait 100ms } } if (!queued) { - WEBCAM_DEBUG_PRINT("webcam: No buffers available after %d retries\n", QUEUE_RETRIES); + WEBCAM_DEBUG_PRINT("webcam: No buffers available after %d retries, resetting streaming\n", QUEUE_RETRIES); + webcam_reset_streaming(self); + // Retry queuing one more time after reset + for (size_t i = 0; i < self->num_buffers; i++) { + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + WEBCAM_DEBUG_PRINT("webcam: Querying buffer state after reset (index=%zu)\n", i); + if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) { + WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state after reset (index=%zu, errno=%d)\n", i, errno); + continue; + } + + WEBCAM_DEBUG_PRINT("webcam: Attempting to queue buffer after reset (index=%zu)\n", i); + if (ioctl(self->fd, VIDIOC_QBUF, &buf) == 0) { + WEBCAM_DEBUG_PRINT("webcam: Successfully queued buffer after reset (index=%zu)\n", i); + queued = true; + break; + } + WEBCAM_DEBUG_PRINT("webcam: Failed to queue buffer after reset (index=%zu, errno=%d)\n", i, errno); + } + } + + if (!queued) { + WEBCAM_DEBUG_PRINT("webcam: No buffers available even after reset\n"); mp_raise_OSError(EAGAIN); } @@ -156,6 +231,10 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) { buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = buf_index; + WEBCAM_DEBUG_PRINT("webcam: Querying buffer state before re-queue (index=%zu)\n", buf_index); + if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) { + WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state before re-queue (index=%zu, errno=%d)\n", buf_index, errno); + } WEBCAM_DEBUG_PRINT("webcam: Re-queuing buffer (index=%zu)\n", buf_index); if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { WEBCAM_DEBUG_PRINT("webcam: Failed to re-queue buffer (index=%zu, errno=%d)\n", buf_index, errno); @@ -286,14 +365,26 @@ static mp_obj_t webcam_init(void) { close(self->fd); mp_raise_OSError(errno); } + + // Queue buffer upfront + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + WEBCAM_DEBUG_PRINT("webcam: Initial queuing of buffer %zu\n", i); + if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { + WEBCAM_DEBUG_PRINT("webcam: Failed to queue buffer %zu initially (errno=%d)\n", i, errno); + for (size_t j = 0; j <= i; j++) { + if (self->buffers[j].start != NULL) { + munmap(self->buffers[j].start, self->buffers[j].length); + } + } + close(self->fd); + mp_raise_OSError(errno); + } } - // Create a tuple to hold the webcam object and method references - mp_obj_t tuple[3]; - tuple[0] = MP_OBJ_FROM_PTR(self); - tuple[1] = MP_OBJ_FROM_PTR(&webcam_capture_grayscale_obj); - tuple[2] = MP_OBJ_FROM_PTR(&webcam_deinit_obj); - return mp_obj_new_tuple(3, tuple); + return mp_obj_new_tuple(3, (mp_obj_t[]){MP_OBJ_FROM_PTR(self), MP_OBJ_FROM_PTR(&webcam_capture_grayscale_obj), MP_OBJ_FROM_PTR(&webcam_deinit_obj)}); } MP_DEFINE_CONST_FUN_OBJ_0(webcam_init_obj, webcam_init); @@ -302,7 +393,7 @@ static const mp_rom_map_elem_t webcam_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_webcam) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&webcam_init_obj) }, { MP_ROM_QSTR(MP_QSTR_capture_grayscale), MP_ROM_PTR(&webcam_capture_grayscale_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&webcam_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR/&webcam_deinit_obj) }, }; static MP_DEFINE_CONST_DICT(webcam_module_globals, webcam_module_globals_table);