fix: camera api endpoint
This commit is contained in:
parent
83aaf95f59
commit
519e073f7f
5 changed files with 69 additions and 68 deletions
|
@ -377,6 +377,8 @@ class WebSocketHandler:
|
|||
camera_id = subscription_id.split(';')[-1]
|
||||
model_id = payload['modelId']
|
||||
|
||||
logger.info(f"[SUBSCRIPTION_MAPPING] subscription_id='{subscription_id}' → camera_id='{camera_id}'")
|
||||
|
||||
# Get tracking integration for this model
|
||||
tracking_integration = tracking_integrations.get(model_id)
|
||||
|
||||
|
|
|
@ -46,13 +46,7 @@ class FrameBuffer:
|
|||
|
||||
frame_data = self._frames[camera_id]
|
||||
|
||||
# Check if frame is too old
|
||||
age = time.time() - frame_data['timestamp']
|
||||
if age > self.max_age_seconds:
|
||||
logger.debug(f"Frame for camera {camera_id} is {age:.1f}s old, discarding")
|
||||
del self._frames[camera_id]
|
||||
return None
|
||||
|
||||
# Return frame regardless of age - frames persist until replaced
|
||||
return frame_data['frame'].copy()
|
||||
|
||||
def get_frame_info(self, camera_id: str) -> Optional[Dict[str, Any]]:
|
||||
|
@ -64,10 +58,7 @@ class FrameBuffer:
|
|||
frame_data = self._frames[camera_id]
|
||||
age = time.time() - frame_data['timestamp']
|
||||
|
||||
if age > self.max_age_seconds:
|
||||
del self._frames[camera_id]
|
||||
return None
|
||||
|
||||
# Return frame info regardless of age - frames persist until replaced
|
||||
return {
|
||||
'timestamp': frame_data['timestamp'],
|
||||
'age': age,
|
||||
|
@ -95,24 +86,10 @@ class FrameBuffer:
|
|||
logger.debug(f"Cleared all frames ({count} cameras)")
|
||||
|
||||
def get_camera_list(self) -> list:
|
||||
"""Get list of cameras with valid frames."""
|
||||
"""Get list of cameras with frames - all frames persist until replaced."""
|
||||
with self._lock:
|
||||
current_time = time.time()
|
||||
valid_cameras = []
|
||||
expired_cameras = []
|
||||
|
||||
for camera_id, frame_data in self._frames.items():
|
||||
age = current_time - frame_data['timestamp']
|
||||
if age <= self.max_age_seconds:
|
||||
valid_cameras.append(camera_id)
|
||||
else:
|
||||
expired_cameras.append(camera_id)
|
||||
|
||||
# Clean up expired frames
|
||||
for camera_id in expired_cameras:
|
||||
del self._frames[camera_id]
|
||||
|
||||
return valid_cameras
|
||||
# Return all cameras that have frames - no age-based filtering
|
||||
return list(self._frames.keys())
|
||||
|
||||
def get_stats(self) -> Dict[str, Any]:
|
||||
"""Get buffer statistics."""
|
||||
|
@ -120,8 +97,8 @@ class FrameBuffer:
|
|||
current_time = time.time()
|
||||
stats = {
|
||||
'total_cameras': len(self._frames),
|
||||
'valid_cameras': 0,
|
||||
'expired_cameras': 0,
|
||||
'recent_cameras': 0,
|
||||
'stale_cameras': 0,
|
||||
'total_memory_mb': 0,
|
||||
'cameras': {}
|
||||
}
|
||||
|
@ -130,16 +107,17 @@ class FrameBuffer:
|
|||
age = current_time - frame_data['timestamp']
|
||||
size_mb = frame_data.get('size_mb', 0)
|
||||
|
||||
# All frames are valid/available, but categorize by freshness for monitoring
|
||||
if age <= self.max_age_seconds:
|
||||
stats['valid_cameras'] += 1
|
||||
stats['recent_cameras'] += 1
|
||||
else:
|
||||
stats['expired_cameras'] += 1
|
||||
stats['stale_cameras'] += 1
|
||||
|
||||
stats['total_memory_mb'] += size_mb
|
||||
|
||||
stats['cameras'][camera_id] = {
|
||||
'age': age,
|
||||
'valid': age <= self.max_age_seconds,
|
||||
'recent': age <= self.max_age_seconds, # Recent but all frames available
|
||||
'shape': frame_data['shape'],
|
||||
'dtype': frame_data['dtype'],
|
||||
'size_mb': size_mb
|
||||
|
|
|
@ -130,6 +130,7 @@ class StreamManager:
|
|||
try:
|
||||
if stream_config.rtsp_url:
|
||||
# RTSP stream using FFmpeg subprocess with CUDA acceleration
|
||||
logger.info(f"[STREAM_START] Starting FFmpeg RTSP stream for camera_id='{camera_id}' URL={stream_config.rtsp_url}")
|
||||
reader = FFmpegRTSPReader(
|
||||
camera_id=camera_id,
|
||||
rtsp_url=stream_config.rtsp_url,
|
||||
|
@ -138,10 +139,11 @@ class StreamManager:
|
|||
reader.set_frame_callback(self._frame_callback)
|
||||
reader.start()
|
||||
self._streams[camera_id] = reader
|
||||
logger.info(f"Started FFmpeg RTSP stream for camera {camera_id}")
|
||||
logger.info(f"[STREAM_START] ✅ Started FFmpeg RTSP stream for camera_id='{camera_id}'")
|
||||
|
||||
elif stream_config.snapshot_url:
|
||||
# HTTP snapshot stream
|
||||
logger.info(f"[STREAM_START] Starting HTTP snapshot stream for camera_id='{camera_id}' URL={stream_config.snapshot_url}")
|
||||
reader = HTTPSnapshotReader(
|
||||
camera_id=camera_id,
|
||||
snapshot_url=stream_config.snapshot_url,
|
||||
|
@ -151,7 +153,7 @@ class StreamManager:
|
|||
reader.set_frame_callback(self._frame_callback)
|
||||
reader.start()
|
||||
self._streams[camera_id] = reader
|
||||
logger.info(f"Started HTTP snapshot stream for camera {camera_id}")
|
||||
logger.info(f"[STREAM_START] ✅ Started HTTP snapshot stream for camera_id='{camera_id}'")
|
||||
|
||||
else:
|
||||
logger.error(f"No valid URL provided for camera {camera_id}")
|
||||
|
@ -169,8 +171,9 @@ class StreamManager:
|
|||
try:
|
||||
self._streams[camera_id].stop()
|
||||
del self._streams[camera_id]
|
||||
shared_cache_buffer.clear_camera(camera_id)
|
||||
logger.info(f"Stopped stream for camera {camera_id}")
|
||||
# DON'T clear frames - they should persist until replaced
|
||||
# shared_cache_buffer.clear_camera(camera_id) # REMOVED - frames should persist
|
||||
logger.info(f"Stopped stream for camera {camera_id} (frames preserved in buffer)")
|
||||
except Exception as e:
|
||||
logger.error(f"Error stopping stream for camera {camera_id}: {e}")
|
||||
|
||||
|
@ -179,6 +182,11 @@ class StreamManager:
|
|||
try:
|
||||
# Store frame in shared buffer
|
||||
shared_cache_buffer.put_frame(camera_id, frame)
|
||||
logger.info(f"[FRAME_CALLBACK] Stored frame for camera_id='{camera_id}' in shared_cache_buffer, shape={frame.shape}")
|
||||
|
||||
# Log current buffer state
|
||||
available_cameras = shared_cache_buffer.frame_buffer.get_camera_list()
|
||||
logger.info(f"[FRAME_CALLBACK] Buffer now contains {len(available_cameras)} cameras: {available_cameras}")
|
||||
|
||||
# Process tracking for subscriptions with tracking integration
|
||||
self._process_tracking_for_camera(camera_id, frame)
|
||||
|
|
|
@ -101,14 +101,14 @@ class FFmpegRTSPReader:
|
|||
# This ensures each file is complete when written
|
||||
camera_id_safe = self.camera_id.replace(' ', '_')
|
||||
self.frame_prefix = f"camera_{camera_id_safe}"
|
||||
# Using strftime pattern with microseconds for unique filenames
|
||||
self.frame_pattern = f"{self.frame_dir}/{self.frame_prefix}_%Y%m%d_%H%M%S_%f.ppm"
|
||||
# Using strftime pattern with seconds for unique filenames (avoid %f which may not work)
|
||||
self.frame_pattern = f"{self.frame_dir}/{self.frame_prefix}_%Y%m%d_%H%M%S.ppm"
|
||||
|
||||
cmd = [
|
||||
'ffmpeg',
|
||||
# DO NOT REMOVE
|
||||
'-hwaccel', 'cuda',
|
||||
'-hwaccel_device', '0',
|
||||
# '-hwaccel', 'cuda',
|
||||
# '-hwaccel_device', '0',
|
||||
'-rtsp_transport', 'tcp',
|
||||
'-i', self.rtsp_url,
|
||||
'-f', 'image2',
|
||||
|
@ -201,14 +201,17 @@ class FFmpegRTSPReader:
|
|||
# Sort by filename (which includes timestamp) and get the latest
|
||||
frame_files.sort()
|
||||
latest_frame = frame_files[-1]
|
||||
logger.debug(f"Camera {self.camera_id}: Found {len(frame_files)} frames, processing latest: {latest_frame}")
|
||||
|
||||
# Read the latest frame (it's complete since FFmpeg wrote it atomically)
|
||||
frame = cv2.imread(latest_frame)
|
||||
|
||||
if frame is not None and frame.shape == (self.height, self.width, 3):
|
||||
# Call frame callback directly
|
||||
if frame is not None:
|
||||
logger.debug(f"Camera {self.camera_id}: Successfully read frame {frame.shape} from {latest_frame}")
|
||||
# Accept any frame dimensions initially for debugging
|
||||
if self.frame_callback:
|
||||
self.frame_callback(self.camera_id, frame)
|
||||
logger.debug(f"Camera {self.camera_id}: Called frame callback")
|
||||
|
||||
frame_count += 1
|
||||
|
||||
|
@ -217,6 +220,8 @@ class FFmpegRTSPReader:
|
|||
if current_time - last_log_time >= 30:
|
||||
logger.info(f"Camera {self.camera_id}: {frame_count} frames processed")
|
||||
last_log_time = current_time
|
||||
else:
|
||||
logger.warning(f"Camera {self.camera_id}: Failed to read frame from {latest_frame}")
|
||||
|
||||
# Clean up old frame files to prevent disk filling
|
||||
# Keep only the latest 5 frames
|
||||
|
@ -226,6 +231,8 @@ class FFmpegRTSPReader:
|
|||
os.remove(old_file)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
logger.warning(f"Camera {self.camera_id}: No frame files found in {self.frame_dir} with pattern {self.frame_prefix}*.ppm")
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Camera {self.camera_id}: Error reading frames: {e}")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue