@@ -25,6 +25,7 @@ static const mp_obj_type_t webcam_type;
2525typedef struct _webcam_obj_t {
2626 mp_obj_base_t base ;
2727 int fd ;
28+ char device [64 ]; // Device path (e.g., "/dev/video0")
2829 void * buffers [NUM_BUFFERS ];
2930 size_t buffer_length ;
3031 int frame_count ;
@@ -147,8 +148,11 @@ static void save_raw_rgb565(const char *filename, uint16_t *data, int width, int
147148 fclose (fp );
148149}
149150
150- static int init_webcam (webcam_obj_t * self , const char * device ) {
151- //WEBCAM_DEBUG_PRINT("webcam.c: init_webcam\n");
151+ static int init_webcam (webcam_obj_t * self , const char * device , int width , int height ) {
152+ // Store device path for later use (e.g., reconfigure)
153+ strncpy (self -> device , device , sizeof (self -> device ) - 1 );
154+ self -> device [sizeof (self -> device ) - 1 ] = '\0' ;
155+
152156 self -> fd = open (device , O_RDWR );
153157 if (self -> fd < 0 ) {
154158 WEBCAM_DEBUG_PRINT ("Cannot open device: %s\n" , strerror (errno ));
@@ -157,8 +161,8 @@ static int init_webcam(webcam_obj_t *self, const char *device) {
157161
158162 struct v4l2_format fmt = {0 };
159163 fmt .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
160- fmt .fmt .pix .width = WIDTH ;
161- fmt .fmt .pix .height = HEIGHT ;
164+ fmt .fmt .pix .width = width ;
165+ fmt .fmt .pix .height = height ;
162166 fmt .fmt .pix .pixelformat = V4L2_PIX_FMT_YUYV ;
163167 fmt .fmt .pix .field = V4L2_FIELD_ANY ;
164168 if (ioctl (self -> fd , VIDIOC_S_FMT , & fmt ) < 0 ) {
@@ -167,6 +171,10 @@ static int init_webcam(webcam_obj_t *self, const char *device) {
167171 return - errno ;
168172 }
169173
174+ // Store actual format (driver may adjust dimensions)
175+ width = fmt .fmt .pix .width ;
176+ height = fmt .fmt .pix .height ;
177+
170178 struct v4l2_requestbuffers req = {0 };
171179 req .count = NUM_BUFFERS ;
172180 req .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
@@ -215,9 +223,9 @@ static int init_webcam(webcam_obj_t *self, const char *device) {
215223
216224 self -> frame_count = 0 ;
217225
218- // Store the input dimensions from V4L2 format
219- self -> input_width = WIDTH ;
220- self -> input_height = HEIGHT ;
226+ // Store the input dimensions (actual values from V4L2, may be adjusted by driver)
227+ self -> input_width = width ;
228+ self -> input_height = height ;
221229
222230 // Initialize output dimensions with defaults if not already set
223231 if (self -> output_width == 0 ) self -> output_width = OUTPUT_WIDTH ;
@@ -323,13 +331,25 @@ static mp_obj_t capture_frame(mp_obj_t self_in, mp_obj_t format) {
323331 }
324332}
325333
326- static mp_obj_t webcam_init (size_t n_args , const mp_obj_t * args ) {
327- mp_arg_check_num (n_args , 0 , 0 , 1 , false);
334+ static mp_obj_t webcam_init (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
335+ enum { ARG_device , ARG_width , ARG_height };
336+ static const mp_arg_t allowed_args [] = {
337+ { MP_QSTR_device , MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
338+ { MP_QSTR_width , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = WIDTH } },
339+ { MP_QSTR_height , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = HEIGHT } },
340+ };
341+
342+ mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
343+ mp_arg_parse_all (n_args , pos_args , kw_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
344+
328345 const char * device = "/dev/video0" ;
329- if (n_args == 1 ) {
330- device = mp_obj_str_get_str (args [0 ] );
346+ if (args [ ARG_device ]. u_obj != MP_OBJ_NULL ) {
347+ device = mp_obj_str_get_str (args [ARG_device ]. u_obj );
331348 }
332349
350+ int width = args [ARG_width ].u_int ;
351+ int height = args [ARG_height ].u_int ;
352+
333353 webcam_obj_t * self = m_new_obj (webcam_obj_t );
334354 self -> base .type = & webcam_type ;
335355 self -> fd = -1 ;
@@ -340,14 +360,14 @@ static mp_obj_t webcam_init(size_t n_args, const mp_obj_t *args) {
340360 self -> output_width = 0 ; // Will use default OUTPUT_WIDTH in init_webcam
341361 self -> output_height = 0 ; // Will use default OUTPUT_HEIGHT in init_webcam
342362
343- int res = init_webcam (self , device );
363+ int res = init_webcam (self , device , width , height );
344364 if (res < 0 ) {
345365 mp_raise_OSError (- res );
346366 }
347367
348368 return MP_OBJ_FROM_PTR (self );
349369}
350- MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (webcam_init_obj , 0 , 1 , webcam_init );
370+ MP_DEFINE_CONST_FUN_OBJ_KW (webcam_init_obj , 0 , webcam_init );
351371
352372static mp_obj_t webcam_deinit (mp_obj_t self_in ) {
353373 webcam_obj_t * self = MP_OBJ_TO_PTR (self_in );
@@ -373,11 +393,10 @@ MP_DEFINE_CONST_FUN_OBJ_2(webcam_capture_frame_obj, webcam_capture_frame);
373393
374394static mp_obj_t webcam_reconfigure (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
375395 /*
376- * Reconfigure webcam resolution.
396+ * Reconfigure webcam resolution by reinitializing .
377397 *
378- * Supports changing both INPUT resolution (V4L2 capture format) and
379- * OUTPUT resolution (conversion buffers). If input resolution changes,
380- * this will stop streaming, reconfigure V4L2, and restart streaming.
398+ * This elegantly reuses deinit_webcam() and init_webcam() instead of
399+ * duplicating V4L2 setup code.
381400 *
382401 * Parameters:
383402 * input_width, input_height: V4L2 capture resolution (optional)
@@ -412,141 +431,40 @@ static mp_obj_t webcam_reconfigure(size_t n_args, const mp_obj_t *pos_args, mp_m
412431 if (new_output_height == 0 ) new_output_height = self -> output_height ;
413432
414433 // Validate dimensions
415- if (new_input_width <= 0 || new_input_height <= 0 || new_input_width > 1920 || new_input_height > 1920 ) {
434+ if (new_input_width <= 0 || new_input_height <= 0 || new_input_width > 3840 || new_input_height > 2160 ) {
416435 mp_raise_ValueError (MP_ERROR_TEXT ("Invalid input dimensions" ));
417436 }
418- if (new_output_width <= 0 || new_output_height <= 0 || new_output_width > 1920 || new_output_height > 1920 ) {
437+ if (new_output_width <= 0 || new_output_height <= 0 || new_output_width > 3840 || new_output_height > 2160 ) {
419438 mp_raise_ValueError (MP_ERROR_TEXT ("Invalid output dimensions" ));
420439 }
421440
422- bool input_changed = (new_input_width != self -> input_width || new_input_height != self -> input_height );
423- bool output_changed = (new_output_width != self -> output_width || new_output_height != self -> output_height );
424-
425- // If input resolution changed, need to reconfigure V4L2
426- if (input_changed ) {
427- WEBCAM_DEBUG_PRINT ("Reconfiguring V4L2: %dx%d -> %dx%d\n" ,
428- self -> input_width , self -> input_height ,
429- new_input_width , new_input_height );
430-
431- // 1. Stop streaming
432- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
433- if (ioctl (self -> fd , VIDIOC_STREAMOFF , & type ) < 0 ) {
434- WEBCAM_DEBUG_PRINT ("STREAMOFF failed: %s\n" , strerror (errno ));
435- mp_raise_OSError (errno );
436- }
437-
438- // 2. Unmap old buffers
439- for (int i = 0 ; i < NUM_BUFFERS ; i ++ ) {
440- if (self -> buffers [i ] != MAP_FAILED && self -> buffers [i ] != NULL ) {
441- munmap (self -> buffers [i ], self -> buffer_length );
442- self -> buffers [i ] = MAP_FAILED ;
443- }
444- }
445-
446- // 3. Set new V4L2 format
447- struct v4l2_format fmt = {0 };
448- fmt .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
449- fmt .fmt .pix .width = new_input_width ;
450- fmt .fmt .pix .height = new_input_height ;
451- fmt .fmt .pix .pixelformat = V4L2_PIX_FMT_YUYV ;
452- fmt .fmt .pix .field = V4L2_FIELD_ANY ;
453-
454- if (ioctl (self -> fd , VIDIOC_S_FMT , & fmt ) < 0 ) {
455- WEBCAM_DEBUG_PRINT ("S_FMT failed: %s\n" , strerror (errno ));
456- mp_raise_OSError (errno );
457- }
458-
459- // Verify format was set (driver may adjust dimensions)
460- if (fmt .fmt .pix .width != new_input_width || fmt .fmt .pix .height != new_input_height ) {
461- WEBCAM_DEBUG_PRINT ("Warning: Driver adjusted format to %dx%d\n" ,
462- fmt .fmt .pix .width , fmt .fmt .pix .height );
463- new_input_width = fmt .fmt .pix .width ;
464- new_input_height = fmt .fmt .pix .height ;
465- }
466-
467- // 4. Request new buffers
468- struct v4l2_requestbuffers req = {0 };
469- req .count = NUM_BUFFERS ;
470- req .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
471- req .memory = V4L2_MEMORY_MMAP ;
472-
473- if (ioctl (self -> fd , VIDIOC_REQBUFS , & req ) < 0 ) {
474- WEBCAM_DEBUG_PRINT ("REQBUFS failed: %s\n" , strerror (errno ));
475- mp_raise_OSError (errno );
476- }
477-
478- // 5. Map new buffers
479- for (int i = 0 ; i < NUM_BUFFERS ; i ++ ) {
480- struct v4l2_buffer buf = {0 };
481- buf .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
482- buf .memory = V4L2_MEMORY_MMAP ;
483- buf .index = i ;
484-
485- if (ioctl (self -> fd , VIDIOC_QUERYBUF , & buf ) < 0 ) {
486- WEBCAM_DEBUG_PRINT ("QUERYBUF failed: %s\n" , strerror (errno ));
487- mp_raise_OSError (errno );
488- }
489-
490- self -> buffer_length = buf .length ;
491- self -> buffers [i ] = mmap (NULL , buf .length , PROT_READ | PROT_WRITE ,
492- MAP_SHARED , self -> fd , buf .m .offset );
493-
494- if (self -> buffers [i ] == MAP_FAILED ) {
495- WEBCAM_DEBUG_PRINT ("mmap failed: %s\n" , strerror (errno ));
496- mp_raise_OSError (errno );
497- }
498- }
499-
500- // 6. Queue buffers
501- for (int i = 0 ; i < NUM_BUFFERS ; i ++ ) {
502- struct v4l2_buffer buf = {0 };
503- buf .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
504- buf .memory = V4L2_MEMORY_MMAP ;
505- buf .index = i ;
506-
507- if (ioctl (self -> fd , VIDIOC_QBUF , & buf ) < 0 ) {
508- WEBCAM_DEBUG_PRINT ("QBUF failed: %s\n" , strerror (errno ));
509- mp_raise_OSError (errno );
510- }
511- }
512-
513- // 7. Restart streaming
514- if (ioctl (self -> fd , VIDIOC_STREAMON , & type ) < 0 ) {
515- WEBCAM_DEBUG_PRINT ("STREAMON failed: %s\n" , strerror (errno ));
516- mp_raise_OSError (errno );
517- }
518-
519- // Update stored input dimensions
520- self -> input_width = new_input_width ;
521- self -> input_height = new_input_height ;
441+ // Check if anything changed
442+ if (new_input_width == self -> input_width &&
443+ new_input_height == self -> input_height &&
444+ new_output_width == self -> output_width &&
445+ new_output_height == self -> output_height ) {
446+ return mp_const_none ; // Nothing to do
522447 }
523448
524- // If output resolution changed (or input changed which may affect output), reallocate output buffers
525- if (output_changed || input_changed ) {
526- // Free old buffers
527- free (self -> gray_buffer );
528- free (self -> rgb565_buffer );
449+ WEBCAM_DEBUG_PRINT ("Reconfiguring webcam: %dx%d -> %dx%d (input), %dx%d -> %dx%d (output)\n" ,
450+ self -> input_width , self -> input_height , new_input_width , new_input_height ,
451+ self -> output_width , self -> output_height , new_output_width , new_output_height );
529452
530- // Update dimensions
531- self -> output_width = new_output_width ;
532- self -> output_height = new_output_height ;
453+ // Remember device path before deinit (which closes fd)
454+ char device [ 64 ] ;
455+ strncpy ( device , self -> device , sizeof ( device )) ;
533456
534- // Allocate new buffers
535- self -> gray_buffer = ( unsigned char * ) malloc ( self -> output_width * self -> output_height * sizeof ( unsigned char )) ;
536- self -> rgb565_buffer = ( uint16_t * ) malloc ( self -> output_width * self -> output_height * sizeof ( uint16_t )) ;
457+ // Set desired output dimensions before reinit
458+ self -> output_width = new_output_width ;
459+ self -> output_height = new_output_height ;
537460
538- if (!self -> gray_buffer || !self -> rgb565_buffer ) {
539- free (self -> gray_buffer );
540- free (self -> rgb565_buffer );
541- self -> gray_buffer = NULL ;
542- self -> rgb565_buffer = NULL ;
543- mp_raise_OSError (MP_ENOMEM );
544- }
545- }
461+ // Clean shutdown and reinitialize with new input dimensions
462+ deinit_webcam (self );
463+ int res = init_webcam (self , device , new_input_width , new_input_height );
546464
547- WEBCAM_DEBUG_PRINT ( "Webcam reconfigured: input %dx%d, output %dx%d\n" ,
548- self -> input_width , self -> input_height ,
549- self -> output_width , self -> output_height );
465+ if ( res < 0 ) {
466+ mp_raise_OSError ( - res );
467+ }
550468
551469 return mp_const_none ;
552470}
0 commit comments