Skip to content

Commit f505936

Browse files
webcam: add deinit workaround
1 parent 78aa4af commit f505936

File tree

1 file changed

+102
-11
lines changed

1 file changed

+102
-11
lines changed

c_mpos/src/webcam.c

Lines changed: 102 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
#define CAPTURE_HEIGHT 480
2424
#define OUTPUT_WIDTH 240 // Resize to 240x240
2525
#define OUTPUT_HEIGHT 240
26-
#define NUM_BUFFERS 5 // Use 2 buffers, as it worked for two captures
26+
#define NUM_BUFFERS 5 // Use 5 buffers, as it achieved 5 captures
2727
#define QUEUE_RETRIES 5 // Number of retry attempts for queueing
28-
#define QUEUE_RETRY_DELAY_US 100000 // 50ms delay between retries
28+
#define QUEUE_RETRY_DELAY_US 100000 // 100ms delay between retries
2929

3030
// Webcam object type
3131
typedef struct _webcam_obj_t {
@@ -70,6 +70,48 @@ static void resize_640x480_to_240x240(uint8_t *src, uint8_t *dst) {
7070
}
7171
}
7272

73+
// Reset streaming state
74+
static void webcam_reset_streaming(webcam_obj_t *self) {
75+
if (self->streaming) {
76+
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
77+
WEBCAM_DEBUG_PRINT("webcam: Stopping video streaming for reset\n");
78+
if (ioctl(self->fd, VIDIOC_STREAMOFF, &type) < 0) {
79+
WEBCAM_DEBUG_PRINT("webcam: Failed to stop video streaming (errno=%d)\n", errno);
80+
}
81+
self->streaming = false;
82+
}
83+
84+
// Re-queue all buffers
85+
for (size_t i = 0; i < self->num_buffers; i++) {
86+
struct v4l2_buffer buf = {0};
87+
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
88+
buf.memory = V4L2_MEMORY_MMAP;
89+
buf.index = i;
90+
91+
// Query buffer state
92+
WEBCAM_DEBUG_PRINT("webcam: Querying buffer state during reset (index=%zu)\n", i);
93+
if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) {
94+
WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state (index=%zu, errno=%d)\n", i, errno);
95+
continue;
96+
}
97+
98+
// Queue buffer
99+
WEBCAM_DEBUG_PRINT("webcam: Re-queuing buffer during reset (index=%zu)\n", i);
100+
if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) {
101+
WEBCAM_DEBUG_PRINT("webcam: Failed to re-queue buffer during reset (index=%zu, errno=%d)\n", i, errno);
102+
}
103+
}
104+
105+
// Restart streaming
106+
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
107+
WEBCAM_DEBUG_PRINT("webcam: Restarting video streaming\n");
108+
if (ioctl(self->fd, VIDIOC_STREAMON, &type) < 0) {
109+
WEBCAM_DEBUG_PRINT("webcam: Failed to restart video streaming (errno=%d)\n", errno);
110+
} else {
111+
self->streaming = true;
112+
}
113+
}
114+
73115
// Capture a grayscale image
74116
static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) {
75117
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) {
84126
buf.memory = V4L2_MEMORY_MMAP;
85127
buf.index = i;
86128

129+
// Query buffer state
130+
WEBCAM_DEBUG_PRINT("webcam: Querying buffer state (index=%zu, attempt=%d)\n", i, attempt + 1);
131+
if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) {
132+
WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state (index=%zu, errno=%d)\n", i, errno);
133+
continue;
134+
}
135+
87136
WEBCAM_DEBUG_PRINT("webcam: Attempting to queue buffer (index=%zu, attempt=%d)\n", i, attempt + 1);
88137
if (ioctl(self->fd, VIDIOC_QBUF, &buf) == 0) {
89138
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) {
94143
}
95144
if (!queued && attempt < QUEUE_RETRIES - 1) {
96145
WEBCAM_DEBUG_PRINT("webcam: No buffers available, retrying after delay\n");
97-
usleep(QUEUE_RETRY_DELAY_US); // Wait 50ms before retrying
146+
usleep(QUEUE_RETRY_DELAY_US); // Wait 100ms
147+
}
148+
}
149+
150+
if (!queued) {
151+
WEBCAM_DEBUG_PRINT("webcam: No buffers available after %d retries, resetting streaming\n", QUEUE_RETRIES);
152+
webcam_reset_streaming(self);
153+
// Retry queuing one more time after reset
154+
for (size_t i = 0; i < self->num_buffers; i++) {
155+
memset(&buf, 0, sizeof(buf));
156+
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
157+
buf.memory = V4L2_MEMORY_MMAP;
158+
buf.index = i;
159+
160+
WEBCAM_DEBUG_PRINT("webcam: Querying buffer state after reset (index=%zu)\n", i);
161+
if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) {
162+
WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state after reset (index=%zu, errno=%d)\n", i, errno);
163+
continue;
164+
}
165+
166+
WEBCAM_DEBUG_PRINT("webcam: Attempting to queue buffer after reset (index=%zu)\n", i);
167+
if (ioctl(self->fd, VIDIOC_QBUF, &buf) == 0) {
168+
WEBCAM_DEBUG_PRINT("webcam: Successfully queued buffer after reset (index=%zu)\n", i);
169+
queued = true;
170+
break;
171+
}
172+
WEBCAM_DEBUG_PRINT("webcam: Failed to queue buffer after reset (index=%zu, errno=%d)\n", i, errno);
98173
}
99174
}
100175

101176
if (!queued) {
102-
WEBCAM_DEBUG_PRINT("webcam: No buffers available after %d retries\n", QUEUE_RETRIES);
177+
WEBCAM_DEBUG_PRINT("webcam: No buffers available even after reset\n");
103178
mp_raise_OSError(EAGAIN);
104179
}
105180

@@ -156,6 +231,10 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) {
156231
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
157232
buf.memory = V4L2_MEMORY_MMAP;
158233
buf.index = buf_index;
234+
WEBCAM_DEBUG_PRINT("webcam: Querying buffer state before re-queue (index=%zu)\n", buf_index);
235+
if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) {
236+
WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer state before re-queue (index=%zu, errno=%d)\n", buf_index, errno);
237+
}
159238
WEBCAM_DEBUG_PRINT("webcam: Re-queuing buffer (index=%zu)\n", buf_index);
160239
if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) {
161240
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) {
286365
close(self->fd);
287366
mp_raise_OSError(errno);
288367
}
368+
369+
// Queue buffer upfront
370+
memset(&buf, 0, sizeof(buf));
371+
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
372+
buf.memory = V4L2_MEMORY_MMAP;
373+
buf.index = i;
374+
WEBCAM_DEBUG_PRINT("webcam: Initial queuing of buffer %zu\n", i);
375+
if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) {
376+
WEBCAM_DEBUG_PRINT("webcam: Failed to queue buffer %zu initially (errno=%d)\n", i, errno);
377+
for (size_t j = 0; j <= i; j++) {
378+
if (self->buffers[j].start != NULL) {
379+
munmap(self->buffers[j].start, self->buffers[j].length);
380+
}
381+
}
382+
close(self->fd);
383+
mp_raise_OSError(errno);
384+
}
289385
}
290386

291-
// Create a tuple to hold the webcam object and method references
292-
mp_obj_t tuple[3];
293-
tuple[0] = MP_OBJ_FROM_PTR(self);
294-
tuple[1] = MP_OBJ_FROM_PTR(&webcam_capture_grayscale_obj);
295-
tuple[2] = MP_OBJ_FROM_PTR(&webcam_deinit_obj);
296-
return mp_obj_new_tuple(3, tuple);
387+
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)});
297388
}
298389
MP_DEFINE_CONST_FUN_OBJ_0(webcam_init_obj, webcam_init);
299390

@@ -302,7 +393,7 @@ static const mp_rom_map_elem_t webcam_module_globals_table[] = {
302393
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_webcam) },
303394
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&webcam_init_obj) },
304395
{ MP_ROM_QSTR(MP_QSTR_capture_grayscale), MP_ROM_PTR(&webcam_capture_grayscale_obj) },
305-
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&webcam_deinit_obj) },
396+
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR/&webcam_deinit_obj) },
306397
};
307398
static MP_DEFINE_CONST_DICT(webcam_module_globals, webcam_module_globals_table);
308399

0 commit comments

Comments
 (0)