2323#define CAPTURE_HEIGHT 480
2424#define OUTPUT_WIDTH 240 // Resize to 240x240
2525#define OUTPUT_HEIGHT 240
26+ #define NUM_BUFFERS 2 // Use 2 buffers for robust streaming
2627
2728// Webcam object type
2829typedef struct _webcam_obj_t {
2930 mp_obj_base_t base ;
3031 int fd ; // File descriptor for the webcam
31- void * buffer ; // Memory-mapped buffer
32- size_t buffer_length ; // Length of the buffer
32+ struct {
33+ void * start ; // Memory-mapped buffer
34+ size_t length ; // Buffer length
35+ } buffers [NUM_BUFFERS ]; // Array of buffers
36+ size_t num_buffers ; // Number of allocated buffers
3337 bool streaming ; // Streaming state
3438} webcam_obj_t ;
3539
@@ -72,13 +76,27 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) {
7276 struct v4l2_buffer buf = {0 };
7377 buf .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
7478 buf .memory = V4L2_MEMORY_MMAP ;
75- buf .index = 0 ;
7679
77- // Queue the buffer
78- WEBCAM_DEBUG_PRINT ("webcam: Queuing buffer (index=%d)\n" , buf .index );
79- if (ioctl (self -> fd , VIDIOC_QBUF , & buf ) < 0 ) {
80- WEBCAM_DEBUG_PRINT ("webcam: Failed to queue buffer for capture (errno=%d)\n" , errno );
81- mp_raise_OSError (errno );
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+ }
82100 }
83101
84102 // Start streaming if not already started
@@ -92,16 +110,16 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) {
92110 self -> streaming = true;
93111 }
94112
95- // Dequeue the buffer (capture frame)
96- memset (& buf , 0 , sizeof (buf )); // Clear buffer for dequeue
113+ // Dequeue a buffer (capture frame)
114+ memset (& buf , 0 , sizeof (buf ));
97115 buf .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
98116 buf .memory = V4L2_MEMORY_MMAP ;
99- buf .index = 0 ;
100117 WEBCAM_DEBUG_PRINT ("webcam: Dequeuing buffer\n" );
101118 if (ioctl (self -> fd , VIDIOC_DQBUF , & buf ) < 0 ) {
102119 WEBCAM_DEBUG_PRINT ("webcam: Failed to dequeue captured frame (errno=%d)\n" , errno );
103120 mp_raise_OSError (errno );
104121 }
122+ size_t buf_index = buf .index ;
105123
106124 // Convert YUYV to grayscale (640x480)
107125 size_t capture_size = CAPTURE_WIDTH * CAPTURE_HEIGHT ;
@@ -110,7 +128,7 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) {
110128 WEBCAM_DEBUG_PRINT ("webcam: Failed to allocate memory for grayscale buffer (%zu bytes)\n" , capture_size );
111129 mp_raise_OSError (ENOMEM );
112130 }
113- yuyv_to_grayscale ((uint8_t * )self -> buffer , grayscale_buf , CAPTURE_WIDTH , CAPTURE_HEIGHT );
131+ yuyv_to_grayscale ((uint8_t * )self -> buffers [ buf_index ]. start , grayscale_buf , CAPTURE_WIDTH , CAPTURE_HEIGHT );
114132
115133 // Resize to 240x240
116134 size_t output_size = OUTPUT_WIDTH * OUTPUT_HEIGHT ;
@@ -129,14 +147,14 @@ static mp_obj_t webcam_capture_grayscale(mp_obj_t self_in) {
129147 // Clean up
130148 free (resized_buf );
131149
132- // Re-queue the buffer for the next capture
133- memset (& buf , 0 , sizeof (buf )); // Clear buffer for re-queue
150+ // Re-queue the buffer
151+ memset (& buf , 0 , sizeof (buf ));
134152 buf .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
135153 buf .memory = V4L2_MEMORY_MMAP ;
136- buf .index = 0 ;
137- WEBCAM_DEBUG_PRINT ("webcam: Re-queuing buffer (index=%d )\n" , buf . index );
154+ buf .index = buf_index ;
155+ WEBCAM_DEBUG_PRINT ("webcam: Re-queuing buffer (index=%zu )\n" , buf_index );
138156 if (ioctl (self -> fd , VIDIOC_QBUF , & buf ) < 0 ) {
139- WEBCAM_DEBUG_PRINT ("webcam: Failed to re-queue buffer after capture (errno=%d)\n" , errno );
157+ WEBCAM_DEBUG_PRINT ("webcam: Failed to re-queue buffer after capture (index=%zu, errno=%d)\n" , buf_index , errno );
140158 mp_raise_OSError (errno );
141159 }
142160
@@ -159,13 +177,16 @@ static mp_obj_t webcam_deinit(mp_obj_t self_in) {
159177 self -> streaming = false;
160178 }
161179
162- // Unmap buffer
163- if (self -> buffer != NULL && self -> buffer != MAP_FAILED ) {
164- WEBCAM_DEBUG_PRINT ("webcam: Unmapping buffer\n" );
165- munmap (self -> buffer , self -> buffer_length );
166- self -> buffer = NULL ;
167- self -> buffer_length = 0 ;
180+ // Unmap buffers
181+ for (size_t i = 0 ; i < self -> num_buffers ; i ++ ) {
182+ if (self -> buffers [i ].start != NULL && self -> buffers [i ].start != MAP_FAILED ) {
183+ WEBCAM_DEBUG_PRINT ("webcam: Unmapping buffer %zu\n" , i );
184+ munmap (self -> buffers [i ].start , self -> buffers [i ].length );
185+ self -> buffers [i ].start = NULL ;
186+ self -> buffers [i ].length = 0 ;
187+ }
168188 }
189+ self -> num_buffers = 0 ;
169190
170191 // Close device
171192 if (self -> fd >= 0 ) {
@@ -183,9 +204,12 @@ static mp_obj_t webcam_init(void) {
183204 webcam_obj_t * self = m_new_obj (webcam_obj_t );
184205 self -> base .type = & webcam_type ;
185206 self -> fd = -1 ;
186- self -> buffer = NULL ;
187- self -> buffer_length = 0 ;
207+ self -> num_buffers = 0 ;
188208 self -> streaming = false;
209+ for (size_t i = 0 ; i < NUM_BUFFERS ; i ++ ) {
210+ self -> buffers [i ].start = NULL ;
211+ self -> buffers [i ].length = 0 ;
212+ }
189213
190214 // Open the webcam device
191215 WEBCAM_DEBUG_PRINT ("webcam: Opening device %s\n" , VIDEO_DEVICE );
@@ -209,37 +233,50 @@ static mp_obj_t webcam_init(void) {
209233 mp_raise_OSError (errno );
210234 }
211235
212- // Request one buffer
236+ // Request buffers
213237 struct v4l2_requestbuffers req = {0 };
214- req .count = 1 ;
238+ req .count = NUM_BUFFERS ;
215239 req .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
216240 req .memory = V4L2_MEMORY_MMAP ;
217- WEBCAM_DEBUG_PRINT ("webcam: Requesting one memory-mapped buffer \n" );
241+ WEBCAM_DEBUG_PRINT ("webcam: Requesting %d memory-mapped buffers \n" , NUM_BUFFERS );
218242 if (ioctl (self -> fd , VIDIOC_REQBUFS , & req ) < 0 ) {
219- WEBCAM_DEBUG_PRINT ("webcam: Failed to request memory-mapped buffer (errno=%d)\n" , errno );
220- close (self -> fd );
221- mp_raise_OSError (errno );
222- }
223-
224- // Query and map the buffer
225- struct v4l2_buffer buf = {0 };
226- buf .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
227- buf .memory = V4L2_MEMORY_MMAP ;
228- buf .index = 0 ;
229- WEBCAM_DEBUG_PRINT ("webcam: Querying buffer properties\n" );
230- if (ioctl (self -> fd , VIDIOC_QUERYBUF , & buf ) < 0 ) {
231- WEBCAM_DEBUG_PRINT ("webcam: Failed to query buffer properties (errno=%d)\n" , errno );
243+ WEBCAM_DEBUG_PRINT ("webcam: Failed to request memory-mapped buffers (errno=%d)\n" , errno );
232244 close (self -> fd );
233245 mp_raise_OSError (errno );
234246 }
247+ self -> num_buffers = req .count ;
248+
249+ // Query and map buffers
250+ for (size_t i = 0 ; i < self -> num_buffers ; i ++ ) {
251+ struct v4l2_buffer buf = {0 };
252+ buf .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
253+ buf .memory = V4L2_MEMORY_MMAP ;
254+ buf .index = i ;
255+ WEBCAM_DEBUG_PRINT ("webcam: Querying buffer %zu properties\n" , i );
256+ if (ioctl (self -> fd , VIDIOC_QUERYBUF , & buf ) < 0 ) {
257+ WEBCAM_DEBUG_PRINT ("webcam: Failed to query buffer %zu properties (errno=%d)\n" , i , errno );
258+ for (size_t j = 0 ; j < i ; j ++ ) {
259+ if (self -> buffers [j ].start != NULL ) {
260+ munmap (self -> buffers [j ].start , self -> buffers [j ].length );
261+ }
262+ }
263+ close (self -> fd );
264+ mp_raise_OSError (errno );
265+ }
235266
236- self -> buffer_length = buf .length ;
237- WEBCAM_DEBUG_PRINT ("webcam: Mapping buffer of length %zu\n" , self -> buffer_length );
238- self -> buffer = mmap (NULL , buf .length , PROT_READ | PROT_WRITE , MAP_SHARED , self -> fd , buf .m .offset );
239- if (self -> buffer == MAP_FAILED ) {
240- WEBCAM_DEBUG_PRINT ("webcam: Failed to map buffer memory (errno=%d)\n" , errno );
241- close (self -> fd );
242- mp_raise_OSError (errno );
267+ self -> buffers [i ].length = buf .length ;
268+ WEBCAM_DEBUG_PRINT ("webcam: Mapping buffer %zu of length %zu\n" , i , buf .length );
269+ self -> buffers [i ].start = mmap (NULL , buf .length , PROT_READ | PROT_WRITE , MAP_SHARED , self -> fd , buf .m .offset );
270+ if (self -> buffers [i ].start == MAP_FAILED ) {
271+ WEBCAM_DEBUG_PRINT ("webcam: Failed to map buffer %zu memory (errno=%d)\n" , i , errno );
272+ for (size_t j = 0 ; j < i ; j ++ ) {
273+ if (self -> buffers [j ].start != NULL ) {
274+ munmap (self -> buffers [j ].start , self -> buffers [j ].length );
275+ }
276+ }
277+ close (self -> fd );
278+ mp_raise_OSError (errno );
279+ }
243280 }
244281
245282 // Create a tuple to hold the webcam object and method references
0 commit comments