Run compose api as HOST_UID/GID with cache under /tmp, poll slice files for ready_event when ffmpeg stderr is silent, invoke batch via venv python, exclude logs from build context, and document Docker cache/VLC troubleshooting. Co-authored-by: Cursor <cursoragent@cursor.com>
7.9 KiB
Executable File
7.9 KiB
Executable File
视频双后端说明(RTSP / 海康 HCNetSDK)
目标容器
- 推荐:
Linux x86_64+ glibc(与当前python:3.13-slim-bookworm一致)。 - 不推荐:Alpine(musl)加载海康预编译
.so往往失败。 - 镜像已安装 ffmpeg 与 OpenCV 常用系统库,便于 RTSP 录像切片与预览。
RTSP 模式(默认)
- 配置
**camera_id→ RTSP URL** 映射:
**OR_SITE_CONFIG_JSON_FILE**(推荐):UTF-8 JSON 文件,仅支持站点对象:{"video_rtsp_urls":{...},"voice_or_room_bindings":[...]}。根级只允许这两个键;voice_or_room_bindings可为[]。见[app/resources/or_site_config.sample.json](../app/resources/or_site_config.sample.json)。服务每次解析映射时会重新读文件,便于联调覆盖video_rtsp_urls。**VIDEO_RTSP_URL_TEMPLATE**(可选):单模板字符串,可用{camera_id};在video_rtsp_urls未给出某路时使用。
- 调用
POST /client/surgeries/start时,camera_ids用于语音终端绑定;默认仅RTSP_PRIMARY_CAMERA_ID(or-cam-03) 参与 RTSP 录像与 batch 算法。 - 开录确认:主摄 RTSP 录像进程在超时内成功连接并写入首段数据后,才认为开录成功。
- RTSP 预热(可选):设置
RTSP_PREWARM_ENABLED=true后,API 进程启动即对站点 JSON 中 全部video_rtsp_urls机位后台 ffmpeg keep-alive 拉流;开录/停录时暂停/恢复对应机位,避免与录像双拉。日志含RTSP prewarm connected(connect_ms)与RTSP start_surgery timing(resolve_ms/spawn_ms/ready_ms/prewarm_was_warm)。
RTSP 录像切片(链路 1)
- FastAPI 进程内 ffmpeg 从 RTSP 拉流,默认每 120 秒(
RTSP_SEGMENT_DURATION_SEC)落盘一个 MP4 切片到logs/rtsp_segments/{surgery_id}/{camera_id}/。 - 每个完整切片通过
BatchAlgorithmService子进程调用algorithm_subprocesses/5.15/main.py(与链路 3 相同),解析result.tsv后写入活跃会话。 - 停录时 flush 尾切片并等待 batch 队列 drain(
RTSP_SLICE_BATCH_DRAIN_TIMEOUT_SEC)。 - 落盘切片默认 24 小时后自动删除(
RTSP_SEGMENT_TTL_HOURS;进程启动与后台定时 sweep)。 - 设置
RTSP_RECORD_ALL_CAMERAS=true可对请求中所有可解析 RTSP 的机位分别录像+跑 batch(多机位代码已预留)。 - 同一机位同时只允许一场手术录制(默认主摄
RTSP_PRIMARY_CAMERA_ID):另一场次开录同一 camera 时返回409/CAMERA_ALREADY_RECORDING;注册表在拉起 ffmpeg 前即占用机位,避免双路 RTSP 抢流导致录像周期性丢帧。 - 宿主机读切片:Compose 中
api使用HOST_UID/HOST_GID与宿主机对齐落盘权限;详见 Docker部署.md「RTSP 切片在宿主机无法用 VLC 打开」。
Docker 与 RTSP 地址
- 站点 JSON 中的局域网 IP(如
[or_site_config.sample.json](../app/resources/or_site_config.sample.json)的192.168.3.x):API 在默认 bridge 网络下出站流量经 宿主机 转发,只要宿主机能访问该网段,容器内一般可直接使用相同 URL,无需改成172.x等。 127.0.0.1/localhost:在容器内指向容器自身。若 RTSP 服务跑在宿主机或本机 MediaMTX,URL 应使用rtsp://host.docker.internal:<端口>/<路径>。backend/docker-compose.yml已为api服务配置extra_hosts: host.docker.internal:host-gateway(Linux 兼容;macOS/Windows Desktop 通常已内置该主机名)。- 传输协议:compose 默认设置环境变量
OPENCV_FFMPEG_CAPTURE_OPTIONS=rtsp_transport;tcp,使 ffmpeg/OpenCV 经 TCP 拉 RTSP,降低容器/NAT 下 UDP 丢包导致的首帧超时;可通过环境变量覆盖。
海康官方 SDK 模式(可选)
SDK 不作为构建期依赖:将厂商提供的 Linux x86_64 动态库挂载到容器内(例如 /opt/hikvision/lib/libhcnetsdk.so),并设置:
HIKVISION_SDK_ENABLED=trueHIKVISION_DEVICE_IP/HIKVISION_USER/HIKVISION_PASSWORD(以及可选端口)HIKVISION_PREVIEW_RTSP_TEMPLATE或HIKVISION_CAMERA_RTSP_URLS_JSON:登录成功后仍通过 RTSP 取帧送模型(与常见部署一致:SDK 负责设备会话,码流仍走 RTSP)。
行为概要:
- 进程内对
NET_DVR_Init使用引用计数;每路使用 SDK 的工作线程在登录后NET_DVR_Logout,线程结束时配对NET_DVR_Cleanup。 - 若
HIKVISION_SDK_FALLBACK_TO_RTSP=true(默认),在无法加载动态库、登录失败或未配置凭据时,自动回退到 OR 站点配置中的video_rtsp_urls或VIDEO_RTSP_URL_TEMPLATE拉流。
注意:NET_DVR_Login_V30 的设备信息结构体在不同 SDK 版本上可能存在差异;若登录异常,请优先使用 RTSP 回退或按厂商文档校对 ctypes 绑定。
推理与结果查询
链路 1:RTSP 切片 + batch(5.15/main.py)
- 开录后 FastAPI 用 ffmpeg 录像并按
RTSP_SEGMENT_DURATION_SEC切片;每段 MP4 经 batch 子进程推理,结果按 TSV 置信度写入会话。 - 候选耗材清单(
candidate_consumables):非空时仅清单内名称参与自动记账与待确认;缺省或[]时,用consumable_classifier_labels.yaml的 全部类名作为候选。 - 当 Top1 置信度 ≥
VIDEO_AUTO_CONFIRM_CONFIDENCE(默认 0.9)且标签在候选清单内时,自动写入source=video_batch明细;中间区间入待确认队列,由语音终端 TTS/确认。 - 客户端
GET /client/surgeries/{surgery_id}/pending-confirmation,确认后POST .../pending-confirmation/{id}/resolve等。
链路 3:离线 batch(POST /internal/demo/offline-batch)
- FastAPI 编排层
app/algo_host为 5.15 准备工作目录(config.yaml、白名单、商品表),子进程直调algorithm_subprocesses/5.15/main.py --config;解析result.tsv后入库。不在 API 进程内加载模型,不修改 5.15 源码。 - 可选标注视频由子进程
visualize_result_video.py生成;结果缓存键为sha256(video) + candidate_list(跨手术号复用)。 - 不启动实时会话、不触发语音终端。
通用
GET /client/surgeries/{surgery_id}/result仅在存在至少一条消耗明细时返回 200;无明细(已开录但尚未记账、已结束但零消耗、或尚无归档等)返回 503RESULT_NOT_READY。- 同类物品写入受
VIDEO_DETAIL_COOLDOWN_SEC节流。
Demo 联调台:HLS 浏览器预览
浏览器无法直接播放 RTSP。联调台通过 MediaMTX 将 RTSP 转为 HLS,由 FastAPI 反代 m3u8/ts 后由 hls.js 播放。
| 部署方式 | 行为 |
|---|---|
| Docker Compose(推荐) | 固定服务 mediamtx-hls(宿主机 127.0.0.1:18888,容器内 mediamtx-hls:8888);hls-preview-init 种子共享卷后 MediaMTX 直接读 /config/mediamtx.yml(官方镜像是 scratch,勿用 /bin/sh entrypoint);ensure 写共享 mediamtx.yml 并 docker restart orm-mediamtx-hls;DEMO_HLS_PREVIEW_UPSTREAM=http://mediamtx-hls:8888 |
| 本机 uvicorn | 未设 upstream 时按需 docker run 临时 MediaMTX(端口默认 127.0.0.1:18888) |
| 链路 | 行为 |
|---|---|
| 链路 1(真 RTSP) | POST /internal/demo/hls-preview/ensure 按 or_site_config 的 video_rtsp_urls 配置 MediaMTX 路径 |
- 播放地址:
GET /internal/demo/hls-preview/{camera_id}/index.m3u8(playlist 内 URL 重写为 API 路径)。 - Compose 需将 docker.sock 挂入
api(用于 reload mediamtx-hls)。
单帧 JPEG 预览仍保留:GET /internal/demo/rtsp-preview/{camera_id}/frame.jpg。
相关环境变量
详见 backend/.env.example 中「视频:RTSP + 切片 batch」、RTSP 预热、HLS 预览与可选海康 HCNetSDK 相关注释。