refactor(main): 优化摄像头视频流启动逻辑 - 只启动有ROI配置的摄像头
问题: - 旧逻辑启动所有数据库中的摄像头视频流(7个) - 实际只有3个摄像头有ROI配置 - 浪费带宽和计算资源 解决方案: 1. 新增 _get_camera_ids_with_roi() 方法 - 从ROI配置中提取有配置的摄像头ID集合 - 返回去重后的摄像头ID set 2. 新增 _get_camera_config_by_id() 方法 - 根据ID获取摄像头配置对象 - 提高代码复用性和可维护性 3. 重构 _load_cameras() 方法 - 只启动有ROI配置的摄像头 - 添加详细的成功/失败统计 - 改进日志信息,便于排查问题 4. 重构 _reload_cameras() 方法 - 配置更新时只添加有ROI且未启动的摄像头 - 使用集合运算提高性能 - 统一错误处理逻辑 5. 新增 _cleanup_cameras_without_roi() 方法 - 清理已启动但没有ROI的摄像头流 - 当用户删除所有ROI时自动停止视频流 - 节省系统资源 6. 更新配置回调逻辑 - 先清理无ROI的摄像头 - 再添加新增的有ROI的摄像头 - 保证视频流与ROI配置同步 优势: - 资源利用率提升:只启动必要的视频流 - 代码模块化:提取公共逻辑,提高复用性 - 可维护性强:清晰的注释和文档字符串 - 日志完善:详细的统计信息便于监控 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
186
main.py
186
main.py
@@ -98,7 +98,9 @@ class EdgeInferenceService:
|
||||
def _on_config_update(topic, data):
|
||||
if self._algorithm_manager:
|
||||
self._algorithm_manager.reload_all_algorithms()
|
||||
# 配置更新后动态加载新摄像头流
|
||||
# 配置更新后清理无ROI的摄像头流
|
||||
self._cleanup_cameras_without_roi()
|
||||
# 配置更新后动态加载新摄像头流(有ROI的)
|
||||
self._reload_cameras()
|
||||
self._config_manager.register_callback("config_update", _on_config_update)
|
||||
self._logger.info("配置管理器初始化成功")
|
||||
@@ -264,11 +266,73 @@ class EdgeInferenceService:
|
||||
|
||||
self._logger.info("所有组件初始化完成")
|
||||
|
||||
def _load_cameras(self):
|
||||
"""加载摄像头配置"""
|
||||
cameras = self._config_manager.get_cameras()
|
||||
def _get_camera_ids_with_roi(self) -> set:
|
||||
"""获取有ROI配置的摄像头ID集合
|
||||
|
||||
Returns:
|
||||
set: 有ROI配置的摄像头ID集合
|
||||
"""
|
||||
try:
|
||||
all_rois = self._config_manager.get_roi_configs(force_refresh=True)
|
||||
camera_ids = {roi.camera_id for roi in all_rois if roi.camera_id}
|
||||
return camera_ids
|
||||
except Exception as e:
|
||||
self._logger.error(f"获取ROI配置失败: {e}")
|
||||
return set()
|
||||
|
||||
def _get_camera_config_by_id(self, camera_id: str):
|
||||
"""根据摄像头ID获取配置
|
||||
|
||||
Args:
|
||||
camera_id: 摄像头ID
|
||||
|
||||
Returns:
|
||||
CameraInfoModel or None: 摄像头配置对象,未找到返回None
|
||||
"""
|
||||
try:
|
||||
cameras = self._config_manager.get_cameras()
|
||||
for camera in cameras:
|
||||
if camera.camera_id == camera_id:
|
||||
return camera
|
||||
return None
|
||||
except Exception as e:
|
||||
self._logger.error(f"获取摄像头配置失败 {camera_id}: {e}")
|
||||
return None
|
||||
|
||||
def _load_cameras(self):
|
||||
"""加载摄像头配置 - 只启动有ROI配置的摄像头
|
||||
|
||||
逻辑:
|
||||
1. 获取所有ROI配置,提取关联的摄像头ID
|
||||
2. 只为有ROI配置的摄像头启动视频流
|
||||
3. 避免启动无用的视频流,节省资源
|
||||
"""
|
||||
# 获取有ROI配置的摄像头ID
|
||||
camera_ids_with_roi = self._get_camera_ids_with_roi()
|
||||
|
||||
if not camera_ids_with_roi:
|
||||
self._logger.warning("未找到任何ROI配置,不启动视频流")
|
||||
return
|
||||
|
||||
self._logger.info(f"检测到 {len(camera_ids_with_roi)} 个摄像头有ROI配置")
|
||||
|
||||
# 只启动有ROI的摄像头
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for camera_id in camera_ids_with_roi:
|
||||
camera = self._get_camera_config_by_id(camera_id)
|
||||
|
||||
if not camera:
|
||||
self._logger.warning(f"摄像头 {camera_id} 有ROI但未找到配置,跳过")
|
||||
failed_count += 1
|
||||
continue
|
||||
|
||||
if not camera.rtsp_url:
|
||||
self._logger.warning(f"摄像头 {camera_id} 缺少 rtsp_url,跳过")
|
||||
failed_count += 1
|
||||
continue
|
||||
|
||||
for camera in cameras:
|
||||
try:
|
||||
self._stream_manager.add_stream(
|
||||
camera_id=camera.camera_id,
|
||||
@@ -277,23 +341,64 @@ class EdgeInferenceService:
|
||||
on_frame_callback=self._create_frame_callback(camera.camera_id)
|
||||
)
|
||||
self._logger.info(f"已添加摄像头: {camera.camera_id}")
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
self._logger.error(f"添加摄像头失败 {camera.camera_id}: {e}")
|
||||
failed_count += 1
|
||||
|
||||
self._logger.info(
|
||||
f"摄像头加载完成 - 成功: {success_count}, 失败: {failed_count}, "
|
||||
f"总计: {len(camera_ids_with_roi)}"
|
||||
)
|
||||
|
||||
def _reload_cameras(self):
|
||||
"""配置更新后动态加载新摄像头(不重复添加已有的)"""
|
||||
"""配置更新后动态加载新摄像头 - 只添加有ROI配置且未启动的摄像头
|
||||
|
||||
逻辑:
|
||||
1. 获取当前有ROI配置的摄像头ID
|
||||
2. 过滤出尚未启动的摄像头
|
||||
3. 动态添加并启动新摄像头流
|
||||
"""
|
||||
if not self._stream_manager or not self._config_manager:
|
||||
return
|
||||
|
||||
try:
|
||||
cameras = self._config_manager.get_cameras(force_refresh=True)
|
||||
existing = set(self._stream_manager._streams.keys())
|
||||
added = 0
|
||||
for camera in cameras:
|
||||
if camera.camera_id in existing:
|
||||
# 获取有ROI配置的摄像头ID
|
||||
camera_ids_with_roi = self._get_camera_ids_with_roi()
|
||||
|
||||
if not camera_ids_with_roi:
|
||||
self._logger.debug("配置更新后未找到有ROI配置的摄像头")
|
||||
return
|
||||
|
||||
# 获取已启动的摄像头
|
||||
existing_streams = set(self._stream_manager._streams.keys())
|
||||
|
||||
# 找出需要新增的摄像头(有ROI但未启动)
|
||||
new_camera_ids = camera_ids_with_roi - existing_streams
|
||||
|
||||
if not new_camera_ids:
|
||||
self._logger.debug("配置更新后无需新增摄像头流")
|
||||
return
|
||||
|
||||
self._logger.info(f"检测到 {len(new_camera_ids)} 个新摄像头需要启动")
|
||||
|
||||
# 动态添加新摄像头
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for camera_id in new_camera_ids:
|
||||
camera = self._get_camera_config_by_id(camera_id)
|
||||
|
||||
if not camera:
|
||||
self._logger.warning(f"摄像头 {camera_id} 有ROI但未找到配置,跳过")
|
||||
failed_count += 1
|
||||
continue
|
||||
|
||||
if not camera.rtsp_url:
|
||||
self._logger.warning(f"摄像头 {camera.camera_id} 无 rtsp_url,跳过")
|
||||
self._logger.warning(f"摄像头 {camera_id} 缺少 rtsp_url,跳过")
|
||||
failed_count += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
self._stream_manager.add_stream(
|
||||
camera_id=camera.camera_id,
|
||||
@@ -301,16 +406,65 @@ class EdgeInferenceService:
|
||||
target_fps=self._settings.video_stream.default_fps,
|
||||
on_frame_callback=self._create_frame_callback(camera.camera_id)
|
||||
)
|
||||
# 立即启动新添加的流
|
||||
self._stream_manager._streams[camera.camera_id].start()
|
||||
added += 1
|
||||
success_count += 1
|
||||
self._logger.info(f"动态添加并启动摄像头: {camera.camera_id}")
|
||||
except Exception as e:
|
||||
self._logger.error(f"动态添加摄像头失败 {camera.camera_id}: {e}")
|
||||
if added > 0:
|
||||
self._logger.info(f"配置更新后新增 {added} 个摄像头流")
|
||||
failed_count += 1
|
||||
|
||||
if success_count > 0:
|
||||
self._logger.info(
|
||||
f"配置更新后新增摄像头完成 - 成功: {success_count}, 失败: {failed_count}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self._logger.error(f"动态加载摄像头失败: {e}")
|
||||
|
||||
|
||||
def _cleanup_cameras_without_roi(self):
|
||||
"""清理没有ROI配置的摄像头视频流
|
||||
|
||||
逻辑:
|
||||
1. 获取当前有ROI配置的摄像头ID
|
||||
2. 找出已启动但没有ROI的摄像头
|
||||
3. 停止并移除这些摄像头的视频流,节省资源
|
||||
"""
|
||||
if not self._stream_manager or not self._config_manager:
|
||||
return
|
||||
|
||||
try:
|
||||
# 获取有ROI配置的摄像头ID
|
||||
camera_ids_with_roi = self._get_camera_ids_with_roi()
|
||||
|
||||
# 获取已启动的摄像头
|
||||
running_streams = set(self._stream_manager._streams.keys())
|
||||
|
||||
# 找出需要停止的摄像头(已启动但没有ROI)
|
||||
cameras_to_remove = running_streams - camera_ids_with_roi
|
||||
|
||||
if not cameras_to_remove:
|
||||
self._logger.debug("无需清理摄像头视频流")
|
||||
return
|
||||
|
||||
self._logger.info(f"检测到 {len(cameras_to_remove)} 个摄像头无ROI配置,需要停止")
|
||||
|
||||
# 停止并移除这些摄像头的视频流
|
||||
removed_count = 0
|
||||
for camera_id in cameras_to_remove:
|
||||
try:
|
||||
self._stream_manager.remove_stream(camera_id)
|
||||
removed_count += 1
|
||||
self._logger.info(f"已停止并移除摄像头流: {camera_id} (无ROI配置)")
|
||||
except Exception as e:
|
||||
self._logger.error(f"移除摄像头流失败 {camera_id}: {e}")
|
||||
|
||||
if removed_count > 0:
|
||||
self._logger.info(f"清理完成 - 已移除 {removed_count} 个无ROI的摄像头流")
|
||||
|
||||
except Exception as e:
|
||||
self._logger.error(f"清理摄像头流失败: {e}")
|
||||
|
||||
def _create_frame_callback(self, camera_id: str):
|
||||
"""创建帧处理回调"""
|
||||
def callback(frame):
|
||||
|
||||
Reference in New Issue
Block a user