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)
3539static 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
182234static 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};
186238static MP_DEFINE_CONST_DICT (webcam_module_globals , webcam_module_globals_table ) ;
187239
0 commit comments