# motion_recorder_cpp **Repository Path**: mz8023yt/motion_recorder_cpp ## Basic Information - **Project Name**: motion_recorder_cpp - **Description**: 【运动检测】基于 ubuntu 和 hik 摄像头实现的运动检测程序 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-24 - **Last Updated**: 2026-05-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 运动录像程序 这是一个运行在 Ubuntu 上的 C++ 海康摄像头 RTSP 运动检测录像程序。 ## 功能 - 支持多路海康 RTSP 摄像头。 - 每个摄像头对应一个 `CameraPipeline`。 - 每个模块都是独立对象,并且拥有独立处理线程。 - 支持 RTSP 解封装,输出 H.264 压缩包。 - 支持 H.264 解码,输出 NV12 图像帧。 - 使用 OpenCV 对 NV12 灰度图做运动检测。 - 录像时复用缓存的 H.264 压缩包,不重新编码。 - 默认输出 MPEG-TS 文件,也支持输出裸 H.264 文件。 - 每类配置使用独立 JSON 文件。 - 使用 Makefile 构建。 ## Ubuntu 依赖 ```bash sudo apt update sudo apt install pkg-config sudo apt install nlohmann-json3-dev sudo apt install ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev sudo apt install libopencv-dev ``` ## 编译 ```bash make ``` ## 运行 修改 `config/cameras.json`,填入真实 RTSP 地址,并将对应摄像头的 `enabled` 设置为 `true`。 ```bash ./motion_recorder config ``` 使用 `Ctrl+C` 停止程序。 ## 架构 ```text StreamManager -> CameraPipeline 1 -> CameraPipeline 2 -> CameraPipeline N CameraPipeline Demuxer RTSP 拉流和 H.264 解封装 Cache 预录缓存和数据分发 Decoder H.264 解码到 NV12 MotionDetect 基于 OpenCV 的运动检测 Event 运动开始和结束事件控制 Recorder MPEG-TS 或裸 H.264 片段写入 ``` ```mermaid flowchart LR StreamManager["StreamManager"] -->|"创建 Pipeline"| Pipeline["CameraPipeline"] subgraph PipelineGraph ["CameraPipeline"] Demuxer["Demuxer"] --> Cache["Cache"] Cache --> Decoder["Decoder"] Decoder --> MotionDetect["MotionDetect"] MotionDetect --> Event["Event"] Event --> Recorder["Recorder"] Cache --> Recorder Cache -.-> Event end Recorder --> SegmentFile["录像片段文件"] ``` 每个 Pipeline 子模块都有独立的头文件和实现文件: ```text include/demuxer.hpp src/demuxer.cpp include/cache.hpp src/cache.cpp include/decoder.hpp src/decoder.cpp include/motion_detect.hpp src/motion_detect.cpp include/event.hpp src/event.cpp include/recorder.hpp src/recorder.cpp ``` ### 模块说明 `StreamManager` 加载摄像头配置,为每个启用的摄像头创建一个 `CameraPipeline`。每个 Pipeline 相互独立,所以某一路摄像头重连或异常不会影响其他摄像头。 `CameraPipeline` 负责组合一路摄像头的所有模块。它创建模块之间的队列,启动各模块线程,停止时按顺序关闭并回收线程。它本身不做解封装、解码、检测或录像。 `Demuxer` 使用 FFmpeg 拉取 RTSP 流,找到 H.264 视频流,在需要时将码流转换为 Annex-B 格式,并输出 H.264 压缩包。它还会附带 PTS、DTS、duration、关键帧标记、time base、宽高、SPS/PPS extradata 等元信息。连接或读取失败后,会等待 `reconnect_interval_ms` 再重连。 `Cache` 接收 `Demuxer` 输出的 H.264 压缩包,并分发到两条路径:一条送给 `Decoder` 做运动检测,另一条送给 `Recorder` 在录像开启时写入文件。 Cache 按时间缓存,不按帧数缓存。它至少保留 `pre_record_seconds`,并额外保留 10 秒关键帧回溯窗口。例如 `pre_record_seconds` 为 `1` 时,Cache 内部大约保留 11 秒数据;运动开始时先选中目标 1 秒预录窗口,再向前回退到最近的关键帧开始写入。这样做是因为 H.264 片段最好从关键帧开始,否则前面的 P/B 帧可能无法解码。 `Decoder` 消费 H.264 压缩包,并解码成 NV12 图像帧。如果 FFmpeg 输出的是其他像素格式,则使用 `swscale` 转换成 NV12。对于已废弃的 full-range YUVJ 格式,会先归一化再转换,避免 `swscaler` 刷屏警告。检测链路允许丢弃旧包,因为运动检测应该尽量接近实时。 `MotionDetect` 使用 OpenCV 做运动检测。它不是每帧都检测,而是按 `detect_interval_ms` 节流,默认 200ms 检测一次,也就是约 5fps。每次检测时,先将 NV12 转成灰度图,这里直接使用 NV12 的 Y 平面作为灰度图来源;然后缩放到 `resize_width x resize_height`,默认 480x270;接着做 CLAHE 均衡,降低光照不均对检测的影响;然后使用 MOG2 背景建模提取前景;最后先做闭运算再做开运算去噪。去噪后的前景像素比例大于 `foreground_ratio_threshold` 时,认为当前帧有运动。 `Event` 将逐帧运动检测结果转换为录像开始和结束命令。`trigger_frames` 表示连续多少帧检测到运动后才开始录像。`post_record_seconds` 表示最后一次检测到运动后继续录像的时长。`clear_frames` 只在 `post_record_seconds` 设置为 `0` 时作为按帧数结束的备用条件。 `Recorder` 接收 `Event` 的开始和结束命令,同时接收 `Cache` 分发过来的 H.264 压缩包。录像开始时先写入预录包,然后继续写入实时包,直到收到停止命令。它不重新编码视频。默认将 H.264 包封装为 MPEG-TS 文件;如果 `format` 设置为 `h264`,则写入裸 Annex-B H.264 文件。 ## 配置文件 - `config/app.json`:全局配置。 - `config/cameras.json`:摄像头列表和 RTSP 地址。 - `config/demuxer.json`:RTSP 和重连参数。 - `config/decoder.json`:解码队列和实时丢弃策略。 - `config/motion.json`:OpenCV 运动检测参数。 - `config/record.json`:预录、尾录和输出参数。 `post_record_seconds` 是最后一次检测到运动后的主要尾录时长。 `clear_frames` 只在 `post_record_seconds` 设置为 `0` 时作为按帧数结束的备用条件。 预录会以第一次检测到运动的帧为基准向前截取。Cache 会额外保留关键帧回溯窗口,保证保存片段可以从可解码的 H.264 关键帧开始。 `debug_packets` 控制是否打印每一个实际写入录像片段的 H.264 packet。开启后会打印 `source`、`index`、`pts`、`dts`、`duration`、`key`、`size`、`arrival_ms` 和写入返回值,便于排查预录包和实时包是否重复或乱序。 `watchdog_enabled` 控制是否启用进程级 Watchdog。各模块处理到数据时会上报心跳,Watchdog 每 `watchdog_check_interval_ms` 检查一次。如果某个模块超过 `watchdog_timeout_ms` 没有心跳,程序会记录错误并 `exit(1)`,建议配合 systemd 的 `Restart=on-failure` 使用。 ## 输出 录像文件保存路径: ```text recordings//_.ts ``` 程序会直接将 H.264 压缩包写入 MPEG-TS 容器,不会对解码后的 NV12 图像重新编码。 如果需要输出裸 Annex-B H.264 文件,可以将 `config/record.json` 中的 `format` 设置为 `h264`。 ## systemd 开机自启动 部署约定: ```text 程序路径:/usr/local/bin/motion_recorder 配置目录:/etc/motion_detect 录像目录:/home/avs/Videos/motion_detect 运行用户:avs ``` 先编译并安装可执行文件和 JSON 配置文件: ```bash make sudo make install ``` `make install` 默认执行以下安装动作: ```text 可执行文件:motion_recorder -> /usr/local/bin/motion_recorder JSON 配置:config/*.json -> /etc/motion_detect/ ``` 然后创建录像目录: ```bash sudo mkdir -p /home/avs/Videos/motion_detect sudo chown -R avs:avs /home/avs/Videos/motion_detect ``` 修改 `/etc/motion_detect/record.json`,将 `output_dir` 设置为录像目录: ```json { "format": "mpegts", "output_dir": "/home/avs/Videos/motion_detect", "pre_record_seconds": 5, "post_record_seconds": 5, "max_segment_seconds": 300, "packet_queue_size": 1024, "debug_packets": false } ``` 创建 systemd 服务文件: ```bash sudo nano /etc/systemd/system/motion_detect.service ``` 写入以下内容: ```ini [Unit] Description=Motion Detect Recorder After=network-online.target Wants=network-online.target [Service] Type=simple User=avs Group=avs WorkingDirectory=/home/avs ExecStart=/usr/local/bin/motion_recorder /etc/motion_detect Restart=on-failure RestartSec=3 [Install] WantedBy=multi-user.target ``` 启动并设置开机自启动: ```bash sudo systemctl daemon-reload sudo systemctl enable motion_detect.service sudo systemctl start motion_detect.service ``` 查看运行状态和日志: ```bash sudo systemctl status motion_detect.service journalctl -u motion_detect.service -f ``` 停止或重启服务: ```bash sudo systemctl stop motion_detect.service sudo systemctl restart motion_detect.service ``` 如果程序异常退出,或者 Watchdog 检测到模块卡死后主动 `exit(1)`,systemd 会根据 `Restart=on-failure` 自动重启服务。