fix: RTSP Connection Issues
All checks were successful
Build Worker Base and Application Images / check-base-changes (push) Successful in 11s
Build Worker Base and Application Images / build-base (push) Has been skipped
Build Worker Base and Application Images / build-docker (push) Successful in 2m56s
Build Worker Base and Application Images / deploy-stack (push) Successful in 10s

This commit is contained in:
ziesorx 2025-09-25 03:12:27 +07:00
parent f467cb005d
commit 2f3c2b08cb
2 changed files with 43 additions and 13 deletions

View file

@ -38,8 +38,8 @@ class RTSPReader:
# Frame processing parameters
self.frame_interval = 1.0 / self.expected_fps # ~167ms for 6fps
self.error_recovery_delay = 2.0
self.max_consecutive_errors = 10
self.error_recovery_delay = 5.0 # Increased from 2.0 for stability
self.max_consecutive_errors = 30 # Increased from 10 to handle network jitter
self.stream_timeout = 30.0
def set_frame_callback(self, callback: Callable[[str, np.ndarray], None]):
@ -107,9 +107,15 @@ class RTSPReader:
consecutive_errors = 0
time.sleep(self.error_recovery_delay)
else:
# Skip corrupted frame and continue
logger.debug(f"Camera {self.camera_id}: Frame read failed (error {consecutive_errors})")
time.sleep(0.1)
# Skip corrupted frame and continue with exponential backoff
if consecutive_errors <= 5:
logger.debug(f"Camera {self.camera_id}: Frame read failed (error {consecutive_errors})")
elif consecutive_errors % 10 == 0: # Log every 10th error after 5
logger.warning(f"Camera {self.camera_id}: Continuing frame read failures (error {consecutive_errors})")
# Exponential backoff with cap at 1 second
sleep_time = min(0.1 * (1.5 ** min(consecutive_errors, 10)), 1.0)
time.sleep(sleep_time)
continue
# Validate frame dimensions
@ -169,7 +175,18 @@ class RTSPReader:
logger.info(f"Initializing capture for camera {self.camera_id}")
# Create capture with FFMPEG backend
# Create capture with FFMPEG backend and TCP transport for reliability
# Use TCP instead of UDP to prevent packet loss
rtsp_url_tcp = self.rtsp_url.replace('rtsp://', 'rtsp://')
if '?' in rtsp_url_tcp:
rtsp_url_tcp += '&tcp'
else:
rtsp_url_tcp += '?tcp'
# Alternative: Set environment variable for RTSP transport
import os
os.environ['OPENCV_FFMPEG_CAPTURE_OPTIONS'] = 'rtsp_transport;tcp'
self.cap = cv2.VideoCapture(self.rtsp_url, cv2.CAP_FFMPEG)
if not self.cap.isOpened():
@ -181,8 +198,9 @@ class RTSPReader:
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.expected_height)
self.cap.set(cv2.CAP_PROP_FPS, self.expected_fps)
# Set small buffer to reduce latency and avoid accumulating corrupted frames
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
# Set moderate buffer to handle network jitter while avoiding excessive latency
# Buffer of 3 frames provides resilience without major delay
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 3)
# Set FFMPEG options for better H.264 handling
self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'H264'))
@ -208,13 +226,23 @@ class RTSPReader:
return False
def _reinitialize_capture(self):
"""Reinitialize capture after errors."""
"""Reinitialize capture after errors with retry logic."""
logger.info(f"Reinitializing capture for camera {self.camera_id}")
if self.cap:
self.cap.release()
self.cap = None
time.sleep(1.0)
self._initialize_capture()
# Longer delay before reconnection to avoid rapid reconnect loops
time.sleep(3.0)
# Retry initialization up to 3 times
for attempt in range(3):
if self._initialize_capture():
logger.info(f"Successfully reinitialized camera {self.camera_id} on attempt {attempt + 1}")
break
else:
logger.warning(f"Failed to reinitialize camera {self.camera_id} on attempt {attempt + 1}")
time.sleep(2.0)
def _is_frame_corrupted(self, frame: np.ndarray) -> bool:
"""Check if frame is corrupted (all black, all white, or excessive noise)."""