Skip to content

Commit b488887

Browse files
webcam: repeatable captures
1 parent 4b768b1 commit b488887

File tree

1 file changed

+99
-47
lines changed

1 file changed

+99
-47
lines changed

c_mpos/src/webcam.c

Lines changed: 99 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@
2424
#define OUTPUT_WIDTH 240 // Resize to 240x240
2525
#define OUTPUT_HEIGHT 240
2626

27-
// Structure to hold webcam state
28-
typedef struct {
27+
// Webcam object type
28+
typedef struct _webcam_obj_t {
29+
mp_obj_base_t base;
2930
int fd; // File descriptor for the webcam
3031
void *buffer; // Memory-mapped buffer
3132
size_t buffer_length; // Length of the buffer
32-
} webcam_t;
33+
bool streaming; // Streaming state
34+
} webcam_obj_t;
35+
36+
const mp_obj_type_t webcam_type;
3337

3438
// Convert YUYV to grayscale (extract Y component)
3539
static void yuyv_to_grayscale(uint8_t *src, uint8_t *dst, size_t width, size_t height) {
@@ -59,83 +63,96 @@ static void resize_640x480_to_240x240(uint8_t *src, uint8_t *dst) {
5963
}
6064
}
6165

62-
// Function to capture a grayscale image
63-
static mp_obj_t webcam_capture_grayscale(void) {
64-
webcam_t cam = { .fd = -1, .buffer = NULL, .buffer_length = 0 };
65-
struct v4l2_format fmt = {0};
66-
struct v4l2_buffer buf = {0};
67-
struct v4l2_requestbuffers req = {0};
66+
// Initialize the webcam
67+
static mp_obj_t webcam_init(void) {
68+
webcam_obj_t *self = m_new_obj(webcam_obj_t);
69+
self->base.type = &webcam_type;
70+
self->fd = -1;
71+
self->buffer = NULL;
72+
self->buffer_length = 0;
73+
self->streaming = false;
6874

6975
// Open the webcam device
70-
cam.fd = open(VIDEO_DEVICE, O_RDWR);
71-
if (cam.fd < 0) {
76+
self->fd = open(VIDEO_DEVICE, O_RDWR);
77+
if (self->fd < 0) {
7278
WEBCAM_DEBUG_PRINT("webcam: Failed to open device %s\n", VIDEO_DEVICE);
7379
mp_raise_OSError(errno);
7480
}
7581

7682
// Set format to YUYV at 640x480
83+
struct v4l2_format fmt = {0};
7784
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
7885
fmt.fmt.pix.width = CAPTURE_WIDTH;
7986
fmt.fmt.pix.height = CAPTURE_HEIGHT;
8087
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
8188
fmt.fmt.pix.field = V4L2_FIELD_ANY;
82-
if (ioctl(cam.fd, VIDIOC_S_FMT, &fmt) < 0) {
89+
if (ioctl(self->fd, VIDIOC_S_FMT, &fmt) < 0) {
8390
WEBCAM_DEBUG_PRINT("webcam: Failed to set YUYV format at %dx%d\n", CAPTURE_WIDTH, CAPTURE_HEIGHT);
84-
close(cam.fd);
91+
close(self->fd);
8592
mp_raise_OSError(errno);
8693
}
8794

8895
// Request one buffer
96+
struct v4l2_requestbuffers req = {0};
8997
req.count = 1;
9098
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
9199
req.memory = V4L2_MEMORY_MMAP;
92-
if (ioctl(cam.fd, VIDIOC_REQBUFS, &req) < 0) {
100+
if (ioctl(self->fd, VIDIOC_REQBUFS, &req) < 0) {
93101
WEBCAM_DEBUG_PRINT("webcam: Failed to request memory-mapped buffer\n");
94-
close(cam.fd);
102+
close(self->fd);
95103
mp_raise_OSError(errno);
96104
}
97105

98106
// Query and map the buffer
107+
struct v4l2_buffer buf = {0};
99108
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
100109
buf.memory = V4L2_MEMORY_MMAP;
101110
buf.index = 0;
102-
if (ioctl(cam.fd, VIDIOC_QUERYBUF, &buf) < 0) {
111+
if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) {
103112
WEBCAM_DEBUG_PRINT("webcam: Failed to query buffer properties\n");
104-
close(cam.fd);
113+
close(self->fd);
105114
mp_raise_OSError(errno);
106115
}
107116

108-
cam.buffer_length = buf.length;
109-
cam.buffer = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, cam.fd, buf.m.offset);
110-
if (cam.buffer == MAP_FAILED) {
117+
self->buffer_length = buf.length;
118+
self->buffer = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd, buf.m.offset);
119+
if (self->buffer == MAP_FAILED) {
111120
WEBCAM_DEBUG_PRINT("webcam: Failed to map buffer memory\n");
112-
close(cam.fd);
121+
close(self->fd);
113122
mp_raise_OSError(errno);
114123
}
115124

125+
return MP_OBJ_FROM_PTR(self);
126+
}
127+
MP_DEFINE_CONST_FUN_OBJ_0(webcam_init_obj, webcam_init);
128+
129+
// Capture a grayscale image
130+
static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) {
131+
webcam_obj_t *self = MP_OBJ_TO_PTR(self_in);
132+
116133
// Queue the buffer
117-
if (ioctl(cam.fd, VIDIOC_QBUF, &buf) < 0) {
134+
struct v4l2_buffer buf = {0};
135+
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
136+
buf.memory = V4L2_MEMORY_MMAP;
137+
buf.index = 0;
138+
if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) {
118139
WEBCAM_DEBUG_PRINT("webcam: Failed to queue buffer for capture\n");
119-
munmap(cam.buffer, cam.buffer_length);
120-
close(cam.fd);
121140
mp_raise_OSError(errno);
122141
}
123142

124-
// Start streaming
125-
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
126-
if (ioctl(cam.fd, VIDIOC_STREAMON, &type) < 0) {
127-
WEBCAM_DEBUG_PRINT("webcam: Failed to start video streaming\n");
128-
munmap(cam.buffer, cam.buffer_length);
129-
close(cam.fd);
130-
mp_raise_OSError(errno);
143+
// Start streaming if not already started
144+
if (!self->streaming) {
145+
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
146+
if (ioctl(self->fd, VIDIOC_STREAMON, &type) < 0) {
147+
WEBCAM_DEBUG_PRINT("webcam: Failed to start video streaming\n");
148+
mp_raise_OSError(errno);
149+
}
150+
self->streaming = true;
131151
}
132152

133153
// Dequeue the buffer (capture frame)
134-
if (ioctl(cam.fd, VIDIOC_DQBUF, &buf) < 0) {
154+
if (ioctl(self->fd, VIDIOC_DQBUF, &buf) < 0) {
135155
WEBCAM_DEBUG_PRINT("webcam: Failed to dequeue captured frame\n");
136-
ioctl(cam.fd, VIDIOC_STREAMOFF, &type);
137-
munmap(cam.buffer, cam.buffer_length);
138-
close(cam.fd);
139156
mp_raise_OSError(errno);
140157
}
141158

@@ -144,22 +161,16 @@ static mp_obj_t webcam_capture_grayscale(void) {
144161
uint8_t *grayscale_buf = (uint8_t *)malloc(capture_size);
145162
if (!grayscale_buf) {
146163
WEBCAM_DEBUG_PRINT("webcam: Failed to allocate memory for grayscale buffer (%zu bytes)\n", capture_size);
147-
ioctl(cam.fd, VIDIOC_STREAMOFF, &type);
148-
munmap(cam.buffer, cam.buffer_length);
149-
close(cam.fd);
150164
mp_raise_OSError(ENOMEM);
151165
}
152-
yuyv_to_grayscale((uint8_t *)cam.buffer, grayscale_buf, CAPTURE_WIDTH, CAPTURE_HEIGHT);
166+
yuyv_to_grayscale((uint8_t *)self->buffer, grayscale_buf, CAPTURE_WIDTH, CAPTURE_HEIGHT);
153167

154168
// Resize to 240x240
155169
size_t output_size = OUTPUT_WIDTH * OUTPUT_HEIGHT;
156170
uint8_t *resized_buf = (uint8_t *)malloc(output_size);
157171
if (!resized_buf) {
158172
WEBCAM_DEBUG_PRINT("webcam: Failed to allocate memory for resized buffer (%zu bytes)\n", output_size);
159173
free(grayscale_buf);
160-
ioctl(cam.fd, VIDIOC_STREAMOFF, &type);
161-
munmap(cam.buffer, cam.buffer_length);
162-
close(cam.fd);
163174
mp_raise_OSError(ENOMEM);
164175
}
165176
resize_640x480_to_240x240(grayscale_buf, resized_buf);
@@ -170,18 +181,59 @@ static mp_obj_t webcam_capture_grayscale(void) {
170181

171182
// Clean up
172183
free(resized_buf);
173-
ioctl(cam.fd, VIDIOC_STREAMOFF, &type);
174-
munmap(cam.buffer, cam.buffer_length);
175-
close(cam.fd);
176184

177185
return result;
178186
}
179-
MP_DEFINE_CONST_FUN_OBJ_0(webcam_capture_grayscale_obj, webcam_capture_grayscale);
187+
MP_DEFINE_CONST_FUN_OBJ_1(webcam_capture_grayscale_obj, webcam_capture_grayscale);
188+
189+
// Deinitialize the webcam
190+
static mp_obj_t webcam_deinit(mp_obj_t self_in) {
191+
webcam_obj_t *self = MP_OBJ_TO_PTR(self_in);
192+
193+
// Stop streaming if active
194+
if (self->streaming) {
195+
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
196+
if (ioctl(self->fd, VIDIOC_STREAMOFF, &type) < 0) {
197+
WEBCAM_DEBUG_PRINT("webcam: Failed to stop video streaming\n");
198+
mp_raise_OSError(errno);
199+
}
200+
self->streaming = false;
201+
}
202+
203+
// Unmap buffer
204+
if (self->buffer != NULL && self->buffer != MAP_FAILED) {
205+
munmap(self->buffer, self->buffer_length);
206+
self->buffer = NULL;
207+
self->buffer_length = 0;
208+
}
209+
210+
// Close device
211+
if (self->fd >= 0) {
212+
close(self->fd);
213+
self->fd = -1;
214+
}
215+
216+
return mp_const_none;
217+
}
218+
MP_DEFINE_CONST_FUN_OBJ_1(webcam_deinit_obj, webcam_deinit);
219+
220+
// Webcam type definition
221+
static const mp_rom_map_elem_t webcam_locals_dict_table[] = {
222+
{ MP_ROM_QSTR(MP_QSTR_capture_grayscale), MP_ROM_PTR(&webcam_capture_grayscale_obj) },
223+
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&webcam_deinit_obj) },
224+
};
225+
static MP_DEFINE_CONST_DICT(webcam_locals_dict, webcam_locals_dict_table);
226+
227+
const mp_obj_type_t webcam_type = {
228+
{ &mp_type_type },
229+
.name = MP_QSTR_webcam,
230+
.locals_dict = (mp_obj_dict_t *)&webcam_locals_dict,
231+
};
180232

181233
// Module definition
182234
static const mp_rom_map_elem_t webcam_module_globals_table[] = {
183235
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_webcam) },
184-
{ MP_ROM_QSTR(MP_QSTR_capture_grayscale), MP_ROM_PTR(&webcam_capture_grayscale_obj) },
236+
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&webcam_init_obj) },
185237
};
186238
static MP_DEFINE_CONST_DICT(webcam_module_globals, webcam_module_globals_table);
187239

0 commit comments

Comments
 (0)