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
				
			
		
		
	
	
				
					
				
			
		
			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:
		
							parent
							
								
									f467cb005d
								
							
						
					
					
						commit
						2f3c2b08cb
					
				
					 2 changed files with 43 additions and 13 deletions
				
			
		| 
						 | 
				
			
			@ -2,6 +2,8 @@
 | 
			
		|||
  "poll_interval_ms": 100,
 | 
			
		||||
  "max_streams": 20,
 | 
			
		||||
  "target_fps": 2,
 | 
			
		||||
  "reconnect_interval_sec": 5,
 | 
			
		||||
  "max_retries": -1
 | 
			
		||||
  "reconnect_interval_sec": 10,
 | 
			
		||||
  "max_retries": -1,
 | 
			
		||||
  "rtsp_buffer_size": 3,
 | 
			
		||||
  "rtsp_tcp_transport": true
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)."""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue