@@ -123,6 +123,7 @@ def __init__(self, config: OpenCVCameraConfig):
123123 self .stop_event : Event | None = None
124124 self .frame_lock : Lock = Lock ()
125125 self .latest_frame : NDArray [Any ] | None = None
126+ self .cached_frame : NDArray [Any ] | None = None
126127 self .new_frame_event : Event = Event ()
127128
128129 self .rotation : int | None = get_cv2_rotation (config .rotation )
@@ -475,7 +476,7 @@ def _stop_read_thread(self) -> None:
475476 self .thread = None
476477 self .stop_event = None
477478
478- def async_read (self , timeout_ms : float = 200 ) -> NDArray [Any ]:
479+ def async_read (self , timeout_ms : float = 30 ) -> NDArray [Any ]:
479480 """
480481 Reads the latest available frame asynchronously.
481482
@@ -485,7 +486,7 @@ def async_read(self, timeout_ms: float = 200) -> NDArray[Any]:
485486
486487 Args:
487488 timeout_ms (float): Maximum time in milliseconds to wait for a frame
488- to become available. Defaults to 200ms (0.2 seconds ).
489+ to become available. Defaults to 33 ms (30 fps ).
489490
490491 Returns:
491492 np.ndarray: The latest captured frame as a NumPy array in the format
@@ -502,21 +503,32 @@ def async_read(self, timeout_ms: float = 200) -> NDArray[Any]:
502503 if self .thread is None or not self .thread .is_alive ():
503504 self ._start_read_thread ()
504505
506+ # Wait for the first recorded frame to be available
507+ if self .cached_frame is None :
508+ self .new_frame_event .wait ()
509+
505510 if not self .new_frame_event .wait (timeout = timeout_ms / 1000.0 ):
506511 thread_alive = self .thread is not None and self .thread .is_alive ()
507- raise TimeoutError (
508- f"Timed out waiting for frame from camera { self } after { timeout_ms } ms. "
509- f"Read thread alive: { thread_alive } ."
510- )
512+ if thread_alive :
513+ logger .warning (
514+ f"{ self } async_read timed out after { timeout_ms } ms but camera is still running."
515+ )
516+ else :
517+ raise TimeoutError (
518+ f"{ self } async_read timed out after { timeout_ms } ms: camera is not responding !"
519+ )
520+ return self .cached_frame
511521
512522 with self .frame_lock :
513523 frame = self .latest_frame
514524 self .new_frame_event .clear ()
515525
516526 if frame is None :
517527 raise RuntimeError (f"Internal error: Event set but no frame available for { self } ." )
528+ else :
529+ self .cached_frame = frame
518530
519- return frame
531+ return self . cached_frame
520532
521533 def disconnect (self ) -> None :
522534 """
0 commit comments