Refactor API route setup and enhance modularity

- Consolidated API route definitions by registering routes from separate modules for better organization and maintainability.
- Removed redundant route definitions from the APIServer class, improving code clarity.
- Updated camera monitoring and recording modules to utilize a shared context manager for suppressing camera SDK errors, enhancing error handling.
- Adjusted timeout settings in camera operations for improved reliability during frame capture.
- Enhanced logging and error handling across camera operations to facilitate better debugging and monitoring.
This commit is contained in:
salirezav
2025-11-01 15:53:01 -04:00
parent 1a8aa8a027
commit f1a9cb0c1e
17 changed files with 2199 additions and 681 deletions

View File

@@ -12,7 +12,6 @@ import time
import logging
import cv2
import numpy as np
import contextlib
import subprocess
from typing import Optional, Dict, Any, Generator
from datetime import datetime
@@ -26,30 +25,19 @@ from ..core.config import CameraConfig
from ..core.state_manager import StateManager
from ..core.events import EventSystem
from .sdk_config import ensure_sdk_initialized
@contextlib.contextmanager
def suppress_camera_errors():
"""Context manager to temporarily suppress camera SDK error output"""
# Save original file descriptors
original_stderr = os.dup(2)
original_stdout = os.dup(1)
try:
# Redirect stderr and stdout to devnull
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, 2) # stderr
os.dup2(devnull, 1) # stdout (in case SDK uses stdout)
os.close(devnull)
yield
finally:
# Restore original file descriptors
os.dup2(original_stderr, 2)
os.dup2(original_stdout, 1)
os.close(original_stderr)
os.close(original_stdout)
from .utils import suppress_camera_errors
from .constants import (
MJPEG_QUEUE_MAXSIZE,
RTSP_QUEUE_MAXSIZE,
RECORDING_QUEUE_MAXSIZE,
PREVIEW_FPS,
RTSP_FPS,
PREVIEW_JPEG_QUALITY,
CAMERA_GET_BUFFER_TIMEOUT,
CAMERA_TEST_CAPTURE_TIMEOUT,
STREAMING_LOOP_SLEEP,
BRIEF_PAUSE_SLEEP,
)
class CameraStreamer:
@@ -78,17 +66,17 @@ class CameraStreamer:
self._rtsp_thread: Optional[threading.Thread] = None
self._stop_streaming_event = threading.Event()
self._stop_rtsp_event = threading.Event()
self._frame_queue = queue.Queue(maxsize=5) # Buffer for latest frames (for MJPEG streaming)
self._rtsp_frame_queue = queue.Queue(maxsize=10) # Buffer for RTSP frames (larger buffer for smoother streaming)
self._recording_frame_queue = queue.Queue(maxsize=30) # Buffer for recording frames (shared with recorder)
self._frame_queue = queue.Queue(maxsize=MJPEG_QUEUE_MAXSIZE) # Buffer for latest frames (for MJPEG streaming)
self._rtsp_frame_queue = queue.Queue(maxsize=RTSP_QUEUE_MAXSIZE) # Buffer for RTSP frames (larger buffer for smoother streaming)
self._recording_frame_queue = queue.Queue(maxsize=RECORDING_QUEUE_MAXSIZE) # Buffer for recording frames (shared with recorder)
self._lock = threading.RLock()
# Stream settings (optimized for preview)
self.preview_fps = 10.0 # Lower FPS for preview to reduce load
self.preview_quality = 70 # JPEG quality for streaming
self.preview_fps = PREVIEW_FPS # Lower FPS for preview to reduce load
self.preview_quality = PREVIEW_JPEG_QUALITY # JPEG quality for streaming
# RTSP settings
self.rtsp_fps = 15.0 # RTSP FPS (can be higher than MJPEG preview)
self.rtsp_fps = RTSP_FPS # RTSP FPS (can be higher than MJPEG preview)
# Use MEDIAMTX_HOST env var if set, otherwise default to localhost
# Note: If API uses network_mode: host, MediaMTX container ports are exposed to host
# So localhost should work, but MediaMTX must be accessible on that port
@@ -254,7 +242,7 @@ class CameraStreamer:
if frame_bytes:
yield (b"--frame\r\n" b"Content-Type: image/jpeg\r\n\r\n" + frame_bytes + b"\r\n")
else:
time.sleep(0.1) # Wait a bit if no frame available
time.sleep(STREAMING_LOOP_SLEEP) # Wait a bit if no frame available
def _initialize_camera(self) -> bool:
"""Initialize camera for streaming (separate from recording)"""
@@ -366,11 +354,11 @@ class CameraStreamer:
try:
# If using shared camera, skip capture - recorder will populate queues
if self._using_shared_camera:
time.sleep(0.1) # Just wait, recorder populates queues
time.sleep(STREAMING_LOOP_SLEEP) # Just wait, recorder populates queues
continue
# Capture frame with timeout
pRawData, FrameHead = mvsdk.CameraGetImageBuffer(self.hCamera, 200) # 200ms timeout
pRawData, FrameHead = mvsdk.CameraGetImageBuffer(self.hCamera, CAMERA_GET_BUFFER_TIMEOUT)
# Process frame
mvsdk.CameraImageProcess(self.hCamera, pRawData, self.frame_buffer, FrameHead)
@@ -431,7 +419,7 @@ class CameraStreamer:
except Exception as e:
if not self._stop_streaming_event.is_set():
self.logger.error(f"Error in streaming loop: {e}")
time.sleep(0.1) # Brief pause before retrying
time.sleep(BRIEF_PAUSE_SLEEP) # Brief pause before retrying
except Exception as e:
self.logger.error(f"Fatal error in streaming loop: {e}")