ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ESP32 ONVIF服务端开发:让NVR顺利添加你的摄像头

ESP32 ONVIF服务端开发:让NVR顺利添加你的摄像头 做过 ESP32 摄像头的人应该都有个不太愉快的阶段RTSP 推流、Web 预览、MQTT 告警都跑通了但一拿到海康或大华的 NVR 添加界面输入 IP 和密码设备就是搜不到或者搜到了却提示“协议不匹配”“添加失败”。问题的根源往往不在视频流而在 ONVIF 这一层。ONVIF 是摄像机与 NVR 之间的“握手协议”想让普通网络录像机把 ESP32 当成标准摄像头绕不开这套规范。开源社区里有叫 onvif-c 的组件能用 plain C 在 ESP-IDF 环境下把 ONVIF 服务端跑起来。这篇文章就是我自己的完整参考手册从创建 ESP-IDF 工程、引入 onvif-c 组件到配置设备信息、实现鉴权与 RTSP 流最终让 NVR 把 ESP32 相机正常添加进来。如果你正要做一个低成本 IP Camera、家庭监控、或者只是想搞明白“NVR 添加摄像头时背后到底发生了什么”这篇文章应该能帮你省掉至少两周的试错时间。下面的内容按我实际推进的顺序来写踩过的坑都会单独标出来。1. 先想清楚ONVIF 服务端到底要做什么1.1 ONVIF 不是单个接口而是一组 SOAP/WS 服务ONVIF 的全称是 Open Network Video Interface Forum它定义的是网络摄像机、NVR、客户端之间互相通信的接口。实际抓包会发现ONVIF 的本质是 SOAP/XML 消息传输层跑 HTTP设备发现则跑 UDP 组播。协议里最核心的几块是 Device Management、Media、PTZ、Events 这些服务。一个能被 NVR 添加的 ESP32 相机至少要正确实现 Device 服务和 Media 服务的关键操作比如 GetDeviceInformation、GetCapabilities、GetProfiles、GetStreamUri。很多初学者以为“添加摄像头”就是提供一个 RTSP 地址这是最大的误区。NVR 在添加设备时的流程通常是先问这设备是谁、有哪些媒体 Profile、能提供什么格式的视频流、RTSP 地址是什么。这一整套问答都走 ONVIF。换句话说RTSP 是给你视频的ONVIF 是给 NVR 做“介绍信”和“菜单”的两个缺一不可。1.2 为什么 onvif-c 要用 plain C 写还塞进 ESP-IDF 组件机制ESP-IDF 本身支持 C那为什么还要找一个 plain C 实现的 ONVIF 组件我自己最初也用 C 写过一版后来发现两个问题一是 C 的 RTTI、异常等特性在嵌入式交叉编译环境下容易引入额外开销二是很多做嵌入式底层的人更习惯 C 的回调风格代码拿到 ESP32、ESP8266、甚至一些国产 RISC-V 芯片上都能快速移植。onvif-c 这类组件的价值就是把 SOAP 解析、XML 生成、WS-Security 鉴权这些脏活封装好对外暴露一组 C 回调接口让使用者只关心“设备信息是什么”“视频流地址是什么”。把 onvif-c 做成 ESP-IDF 组件还有一个很实际的好处ESP-IDF 的组件机制可以自动处理依赖关系idf.py 会把 components 目录下的东西一起编译不需要你手动维护 Makefile也不用自己写 CMake。只要满足 lwIP、mbedTLS 这些基础依赖整个组件就能和主工程一起构建。这个设计对嵌入式项目非常友好升级芯片平台时协议层代码基本不用动。1.3 从 NVR 视角看一遍添加流程你才知道该实现什么我调这个功能时反复在脑子里模拟 NVR 的视角最后整理成下面这条链路NVR 在局域网内发送 WS-Discovery 的 Probe 消息目的地址是组播 239.255.255.250:3702询问“局域网里有没有 ONVIF 设备”。ESP32 收到 Probe 后回一个 ProbeMatch里面带上设备 UUID、类型、作用域、XAddrs设备服务地址。NVR 拿到 XAddrs 后用 HTTP POST 向/onvif/device_service发 GetCapabilities、GetDeviceInformation确认这个设备的基本能力。NVR 再向/onvif/media_service发 GetProfiles拿到视频 Profile 列表和编码类型。NVR 调 GetStreamUri拿到 RTSP 地址。NVR 去连接 RTSP 地址做 DESCRIBE、SETUP、PLAY然后开始拉流。每一步都对应一组具体报文。如果中间任何一步返回错误、超时或者报文格式不规范NVR 就会显示“设备不存在”“添加失败”或“视频不可用”。所以后面讲实现时我都是按这条链路逐段检查的。2. 工程搭建与组件接入从零到编译通过2.1 先选好 ESP-IDF 版本别用太老的onvif-c 依赖 lwIP 的 BSD socket 接口和 mbedTLS 的哈希算法这些在 ESP-IDF 4.x 和 5.x 都可以工作但我建议直接上 5.1 以上的稳定版。原因有两个一是 5.x 里 esp_netif 和 lwIP 的配置更规范二是如果你还要用 esp32-camera 驱动新版本对 OV2640/OV5640 的支持更完整。我现在用的就是 ESP-IDF 5.1.2配合 ESP32-CAM 模组。这里不是非要用最新版但最好别停在 4.0 以前的版本否则组件代码里的日志宏和网络接口可能都要改。安装完 IDF 后确认一下工具链正常idf.py --version如果像我一样在 Windows 上开发又觉得编译慢可以把源目录放到本地 SSD临时目录指向内存盘编译速度能快不少。这个细节看起来不起眼但反复改代码时会明显影响心情。2.2 创建工程并引入 onvif-c 组件建议从空工程开始避免被模板里多余的 demo 代码干扰。我通常会先创建一个干净的工程idf.py create-project esp32_onvif_camera cd esp32_onvif_camera然后把 onvif-c 源码放到 components 目录下。组件不一定要走官方组件注册中心直接放在本地工程里反而更容易调试因为你会经常想进组件里打日志、看 SOAP 报文。目录结构最终大概是esp32_onvif_camera/ ├── main/ │ ├── CMakeLists.txt │ ├── idf_component.yml │ └── app_main.c └── components/ └── onvif-c/ ├── CMakeLists.txt ├── include/ └── src/主程序的 CMakeLists 里只需要保留idf_component_register(SRCS app_main.c)这种最基本的内容。组件这边只要它有完整的 CMakeLists 和头文件idf.py 构建时会自动发现不需要手动去改主程序的依赖列表。不过我想提醒一点如果你的摄像头驱动也要加进来务必在main/idf_component.yml里声明依赖比如dependencies: espressif/esp32-camera: ^2.0.0这样 CMake 才能保证编译顺序正确不然容易出现“摄像头驱动头文件找不到”的问题。2.3 sdkconfig 里必须配好的几个关键项工程能编译只是第一步真正让 NVR 能添加sdkconfig 里有几个选项必须提前想清楚网络接口Wi-Fi 或以太网至少要有一个打开。用 ESP32-CAM 的话通常就是 Wi-Fi配置成 STA 模式连到路由器。SNTP 时间同步ONVIF 鉴权的 Digest 计算依赖时间和 NonceESP32 上电后 RTC 时间是错的NVR 发的请求就会验证失败。建议在idf.py menuconfig里打开 SNTP并配置好 NTP 服务器地址。lwIP 的 socket 数量ONVIF、RTSP、摄像头驱动的 TCP 连接会同时存在默认的 socket 数可能不够我把CONFIG_LWIP_MAX_SOCKETS调到了 10 左右。mbedTLS如果只想跑 ONVIF over HTTP 和摘要鉴权不需要开完整的 TLS但 SHA1 算法模块要保留。如果之后想支持 HTTPS 的 ONVIF 端点再把 TLS 打开。我不建议第一步就上 HTTPS排障难度会大很多。内存和 PSRAM如果接了 OV2640用 JPEG 输出大概率需要外部 PSRAM。在 menuconfig 里确认CONFIG_SPIRAM已启用这直接决定摄像头缓冲区能开多大。2.4 第一次编译以及运行时的资源预期配置完就可以编译了idf.py build idf.py -p COM3 flash monitor第一次编译通过后不要急着去连 NVR先在日志里确认三件事IP 地址是否正确、SNTP 是否已经同步、摄像头初始化是否成功。我碰到过很多次“NVR 搜不到”的问题最后发现是 Wi-Fi 根本没连上代码一直在重启循环里。内存方面要有个心理预期。ONVIF 的 SOAP 响应报文并不算小GetCapabilities 这种消息动不动就几百字节甚至上 KB再加上 XML 解析缓冲整个协议栈会吃掉几十 KB 的 DRAM。ESP32 有 200 多 KB 可用 DRAM如果摄像头驱动再占一些剩余空间可能很紧张。我的经验是启动后 Heap 剩余至少保持在 50 KB 以上才比较稳低于 30 KB 的时候就容易出现 HTTP 响应被截断、RTSP 连接异常中断这种诡异问题。3. 协议核心与关键实现3.1 WS-Discovery让 NVR 在局域网里找到你WS-Discovery 是 ONVIF 设备发现的底层机制本质是一个运行在 UDP 3702 端口上的 SOAP 服务。NVR 会向239.255.255.250:3702发 Probe消息里携带要查找的设备类型和 Scope。你的 ESP32 设备需要创建一个 UDP socket加入这个组播组监听 3702然后解析收到的 Probe并回一个 ProbeMatch。ProbeMatch 里最容易被忽略但最致命的是 XAddrs 字段。XAddrs 是 NVR 接下来访问你设备的地址必须填写 NVR 能从网络实际访问到的 IP 和端口。之前我图省事直接写了http://127.0.0.1:80/onvif/device_service结果 NVR 当然死活找不到后续服务。正确的做法是在设备拿到 IP 后动态生成 XAddrs比如http://192.168.1.100:80/onvif/device_service。另外WS-Discovery 的标准响应里还会带 EndpointReference 和 Types。EndpointReference 通常是一个设备 UUID最好固化到 NVRAM 或通过设备 MAC 生成不要每次开机都变。如果 UUID 一直变NVR 会把旧的设备记录当成“离线”重新添加又变成一台新设备体验非常差。3.2 鉴权WS-Security UsernameToken DigestONVIF 设备默认都有用户名和密码。NVR 添加时会在 SOAP Header 里带一个 UsernameToken里面包含 Username、Password可能是 PasswordDigest、Nonce 和 Created。设备端要做的是解析这四个字段然后校验 Digest 是否正确。Digest 的计算规则是Digest Base64( SHA1( Nonce Created Password ) )需要注意这里的是字节拼接不是字符串加号。Nonce 来自请求里的 Base64 字段要先解码成原始字节Created 是 ISO 8601 格式的时间字符串比如2024-05-01T12:00:00ZPassword 是纯文本密码。我之前一直以为是先拼接字符串再哈希结果算出来始终和 NVR 对不上。后来逐字节调试才发现Nonce 必须原始字节参与 SHA1不能把 Base64 文本直接拼进去。示意代码如下bool check_digest(const char *nonce_b64, const char *created, const char *digest_b64, const char *password) { uint8_t nonce[32]; size_t nonce_len base64_decode(nonce_b64, nonce, sizeof(nonce)); mbedtls_sha1_context ctx; unsigned char digest[20]; mbedtls_sha1_init(ctx); mbedtls_sha1_starts_ret(ctx); mbedtls_sha1_update_ret(ctx, nonce, nonce_len); mbedtls_sha1_update_ret(ctx, (const unsigned char *)created, strlen(created)); mbedtls_sha1_update_ret(ctx, (const unsigned char *)password, strlen(password)); mbedtls_sha1_finish_ret(ctx, digest); mbedtls_sha1_free(ctx); char computed_b64[40] {0}; base64_encode(computed_b64, digest, sizeof(digest)); return strcmp(computed_b64, digest_b64) 0; }如果 SNTP 没有同步请求里的 Created 时间可能和你设备当前时间差很多。严格实现里可以检查时间窗口但很多组件默认不查只做 Digest 比对。为了减少麻烦我建议在调试阶段先不启用时间窗口检查等联调通过后再加。3.3 服务端点Device、Media、PTZ 的分工ONVIF 设备对外提供的不是同一个根路径而是多个 Web 服务端点。最常用的是这三个/onvif/device_service/onvif/media_service/onvif/ptz_service不同端点处理不同命名空间下的 SOAP Action。Device 服务负责设备信息、能力、时间、网络设置Media 服务负责视频 Profile、码流地址、截图地址PTZ 服务负责云台控制。NVR 添加设备时会先访问 device_service 的 GetCapabilities根据返回的能力列表再决定后面访问哪个端点。有些组件把所有端点统一到一个路径上也能跑通因为很多 NVR 只看 HTTP Body 里的 SOAP Action不太在意 URL 路径。但为了最大兼容我建议还是按标准分成三个端点。每个端点内部是一个 XML 解析器加一个 Action 分发器。onvif-c 组件一般就是这样设计的你需要做的只是给每个 Action 注册对应的回调函数。3.4 RTSP/RTPNVR 添加后靠什么取流ONVIF 只负责“介绍”视频流真正传输视频的是 RTSP/RTP。NVR 从 GetStreamUri 拿到一个地址比如rtsp://192.168.1.100:554/stream1然后就发起标准 RTSP 会话。ESP32 这边要有一个独立的 RTSP 服务端至少实现这些方法OPTIONS询问服务端支持哪些方法。DESCRIBE返回 SDP 描述告诉客户端视频编码格式。SETUP建立 RTP 传输通道。PLAY开始推流。TEARDOWN结束会话。SDP 描述特别重要。如果你的视频源是 MJPEGSDP 里会写mvideo 0 RTP/AVP 26payload type 是 26。如果是 H.264payload type 一般写成 96同时要带sprop-parameter-sets之类的参数。NVR 很多时候是先看 Profile 里的编码格式再根据 SDP 决定是否接受这个流。如果 SDP 写得不规范NVR 会把连接挂起表现就是“能添加成功但画面一直黑屏”。RTP 打包又是另一个话题。MJPEG 需要按 RFC 2435 切片H.264 需要按 RTP 封包格式切 NAL 单元。对 ESP32 来说MJPEG 打包比较容易因为 JPEG 数据天然就有分段边界。我在 demo 里直接复用了摄像头驱动的 JPEG buffer加上 RTP 头就推进 socket 里NVR 如果支持 MJPEG 就能直接显示。4. 实操让 NVR 能添加你的 ESP32 相机4.1 先把现实问题讲清楚编码格式怎么选标题里说“写出能被 NVR 添加的 ESP32 相机”这里必须诚实一点添加成功和稳定拉流是两个层次的问题。很多 NVR 默认期望 H.264/H.265 码流而 ESP32 芯片没有硬件 H.264 编码器靠纯软件编码 H.264 在低分辨率低帧率下勉强能跑但复杂度和功耗都很难看。相比之下OV2640 摄像头直接输出 JPEG配合 MJPEG 封装成 RTSP 流是最省资源的方案。但问题是并不是每台 NVR 都支持 MJPEG。我在家用海康 NVR 上试的时候ONVIF 添加可以成功但拉 MJPEG 流时部分型号不支持。如果目标只是“能被添加”MJPEG 完全够如果目标是“能在 NVR 上实时预览”最好先确认一下你手里那台 NVR 是否支持 Profile M 或 MJPEG 取流。还有一个折中方案就是预先转一段低分辨率 H.264 视频存到 flash 或 SD 卡循环播放期间再叠加 OSD 信息这样 NVR 拉到的就是标准的 H.264 流兼容性最好代价是它不是真正的实时画面。4.2 组织设备信息与媒体 Profile要让 NVR 觉得你不是“野路子”设备设备信息和 Profile 要组织得像模像样。GetDeviceInformation 返回的内容包括厂商、型号、固件版本、序列号。这些东西不需要完全真实但序列号最好同 UUID 一样基于 MAC 地址固定生成不要每次重启都变。我的做法是管理员创建 ESP32 摄像头时将 NVR 的录像机界面当作标准 ONVIF 设备。设备名称写“ESP32-CAM-DEMO”型号留空或填 ESP32-CAM制造商可以写“Espressif”序列号使用 MAC 的十六进制去掉冒号后 12 位。这样的话即使你以后重置设备NVR 重新扫描也能以同一身份识别它。Media 服务的 GetProfiles 返回一个 Profile 列表每个 Profile 要有唯一的 Token、名称、视频编码配置、分辨率、帧率。我通常会定义两个 Profile一个主码流分辨率 640x480帧率 10fps编码 MJPEG一个子码流分辨率 320x240帧率 5fps。NVR 在添加的时候可能会遍历所有 Profile也可能只选第一个所以至少要保证第一个 Profile 是可用的。GetStreamUri 要根据客户端请求的 Profile Token 返回对应的 RTSP 地址比如rtsp://192.168.1.100:554/stream1。4.3 回调函数的触点和数据流用 onvif-c 这类组件你的工作重心会从“解析 SOAP”转移到“填回调”。我画过一张很简单的数据流图帮助自己理解摄像头驱动捕获 JPEG 帧放进一个环形缓冲区RTSP 服务线程从缓冲区取帧按 RTP 封包发到网络ONVIF 服务线程处理 SOAP 请求从设备信息表和 Profile 表中取数据组装响应。这三条线程之间用队列或互斥锁同步避免 NVR 正在拉流时你修改 Profile 导致数据不一致。回调函数长得像这样static onvif_device_ops_t ops { .get_device_information my_get_device_information, .get_capabilities my_get_capabilities, .get_profiles my_get_profiles, .get_stream_uri my_get_stream_uri, .check_user my_check_user, }; void app_main(void) { onvif_init(ops); // 初始化摄像头、RTSP、网络 }my_get_stream_uri 这个回调尤其重要。它要读取当前设备的 IP 地址动态拼出 RTSP URL而不是写死一个 127.0.0.1。我早期就吃过写死 IP 的亏路由器 DHCP 一换地址NVR 就拉不到流。4.4 联调先用工具测试再上真实 NVR我第一次直接被真实 NVR 折磨了两天后来才学会先用手头的工具把 ONVIF 服务端验一遍。推荐顺序是这样的先用 ONVIF Device ManagerODM在 Windows 上扫描局域网看能不能发现设备。ODM 的功能比真实 NVR 更全面能直接列出设备能力、Profile、RTSP 地址。用 VLC 打开 RTSP 地址确认视频流能播放。这能剥离掉 ONVIF 层的问题单独验证 RTSP/RTP。ODM 和 VLC 都通过后再回到 NVR 的添加界面选“手动添加”输入 IP、用户名、密码协议选 ONVIF。为什么坚持先 ODM 后 NVR因为 NVR 的日志很不友好失败原因经常只显示一个“设备不存在”而 ODM 会把每个 SOAP 请求的响应都列出来能快速定位是鉴权失败、GetCapabilities 失败还是媒体配置不匹配。这个顺序帮我过滤掉一大半低级问题。5. 常见问题与排查实录5.1 问题速查表这一节直接做成速查表方便你联调时对照现象可能原因处理办法NVR/ODM 搜不到设备WS-Discovery 没监听 3702或组播没加入抓包确认是否有 Probe 到达检查 socket 是否绑定了正确的地址能搜到但添加失败XAddrs 填了回环地址或端口不通检查 XAddrs 是否为设备实际 IP确认 HTTP 端口能访问一直提示用户名密码错误Digest 拼接顺序不对或时间未同步先同步 SNTP然后逐字节核对 Nonce、Created、Password 的哈希输入能添加但画面黑屏SDP 编码格式与 NVR 不匹配用 VLC 先验证 RTSP确认 SDP 里的 payload type 和 Video Codec 一致RTSP 连接建立后被断开服务端没有周期性发送 RTP或超时处理不对在 PLAY 后立即开始推流并定期发送 RTCP 或保持 socket 活跃系统运行一段时间后崩溃内存泄漏或任务栈不够提高 ONVIF/RTSP 任务栈大小检查每个回调内部的动态分配释放这张表基本覆盖了我遇到过 90% 的问题。其中第一类“搜不到”我曾以为是组件代码问题后来发现是自己的 socket 只监听了单播地址没有加入组播组。这个坑极其经典值得多说一句。5.2 用 Wireshark 抓包定位失败点联调 ONVIF 一定要学会抓包。直接看协议栈日志是一回事抓包能让你看到 NVR 到底次什么给你回复了什么比空猜高效得多。抓包时的过滤规则我固定用这几条发现阶段udp.port 3702ONVIF SOAPhttp ip.addr ESP32_IPRTSP/RTPrtsp || tcp.port 554看包顺序时先确认几个关键点ProbeMatch 是否在收到 Probe 后很快返回XAddrs 是不是可访问地址GetCapabilities 的响应状态码是不是 200GetStreamUri 返回的 RTSP URL 能不能被同一网段的主机 ping 通RTSP DESCRIBE 的响应里 SDP 是否完整。只要这几步都正常NVR 添加基本就成了一半。抓包还有一个额外好处就是可以翻出真实 NVR 发出的 Digest 明文结构拿它当测试用例。我把海康添加失败时的请求报文保存成 pcap然后解析里面的 Nonce、Created、Digest对比自己设备算出的结果很快就发现了 SHA1 拼接顺序的错误。没有抓包我可能还在瞎改配置。5.3 雷区与经验第一时间同步是所有鉴权问题的根源。我每次换一块新开发板都会先确认 SNTP 是否已经同步成功日志里出现当前时间从 1970 年开始跳变时任何 Digest 校验都会失败。实测下来ESP32 的 SNTP 在路由器开启 NTP 转发时一般 10 秒内能同步成功如果总是失败检查防火墙和网络隔离。第二Wi-Fi 信号差的时候RTSP 的 RTP 包会疯狂丢包。NVR 会认为视频流异常反复重连最终表现为“设备在线但录像断断续续”。这在 ESP32 上特别明显因为 2.4GHz 频段干扰太多。如果条件允许我会优先插一个 USB 转以太网模块测试先把网络质量因素排除掉再回到 Wi-Fi 上做优化。第三ONVIF 的 XML 响应里有一堆命名空间手写的时候少写一个 xmlns 前缀轻则 NVR 忽略字段重则直接解析失败。这就是为什么我不建议自己从零写 SOAP 封装用 onvif-c 组件能省掉大量这种心智负担。但你也别完全依赖组件适当打开它的日志开关把发出的 XML 抓出来人工检查一遍有时能找到文档没写清楚的细节。6. 再往后怎么从能跑 demo 变成稳定方案6.1 视频编码与带宽的现实约束NVR 能添加只是万里长征第一步。真正要长期用得把视频流质量、内存占用、功耗一起优化。ESP32-CAM 的 OV2640 在 VGA 分辨率下JPEG 单帧大小通常在 30 KB 到 80 KB 之间10fps 就相当于每秒 300 KB 到 800 KB 的数据量换算成带宽大约是 3 Mbps 到 7 Mbps。这在 Wi-Fi 下还能接受但如果同时开 ONVIF 的多个 Profile或者 NVR 做双码流录像带宽就会吃紧。可以考虑把分辨率降到 320x240帧率降到 5fps画质优先换成低压缩率这种设置下马赛克会比较严重但作为演示完全够。如果之后想做更高画质就得认真考虑软件编码 H.264或者外接专门的编码芯片例如用某些带硬件编码的 SoC 做视频处理ESP32 只做主控和 ONVIF 协议这会大幅提高复杂度但也不是不能做。6.2 稳定性看门狗、重连与固件更新实验室里跑通不代表挂到弱电箱边上也能稳定运行三个月。我遇到过几次比较典型的稳定性问题内存碎片SOAP 响应动态串接字符串时频繁 malloc/free运行几天后堆空间碎片化。现在我会在每次请求处理结束后主动记录堆剩余量低于阈值就重启设备。任务栈溢出ONVIF 偶尔会处理很大的 XML栈设小了直接崩。我给 ONVIF 任务分配了 8192 字节而不是默认的 4096。Wi-Fi 掉线路由器重启后ESP32 的自动重连逻辑不完善设备离线。这个要在网络事件回调里加 EV_STA_DISCONNECTED 的重连处理。RTSP 连接泄漏NVR 每次添加设备失败或超时可能会留下半开的 TCP 连接长期累积抓满 socket。每次 TEARDOWN 后要关闭监听 client socketPLAY 状态超时也要主动断开。固件升级方面ONVIF 组件本身不负责 OTA但 ESP-IDF 自带的 OTA 可以和它共存。我可以把新版固件放到局域网某个 HTTP 服务上设备端定时检查更新更新后保留原有 UUID 和用户名密码这样 NVR 的配置不用重新添加。很多人忽略这一点导致升级固件后 NVR 还要重新配网很麻烦。6.3 可以再扩展的能力ONVIF 还有不少加分项在 demo 稳定之后可以逐步加事件服务实现 Motion 事件配合 NVR 的移动侦测录像。ESP32 端可以用摄像头帧差法粗判画面变化然后在 ONVIF 事件服务里上报。PTZ 服务如果电机驱动接上云台可以实现上下左右控制。我之前加了个最简单的相对移动控制NVR 界面的云台按钮就能直接用。多码流同时输出主码流和子码流NVR 可以一个用于预览一个用于录像对带宽控制有帮助。HTTPS 访问把 ONVIF 服务端点套上 TLS用户名密码不以明文方式传输安全性提升一个档次。这些功能都要在内存预算内做增量。尤其事件服务SOAP 长连接会占不少资源最好一个拉流连接释放后再跑事件订阅不然 ESP32 很容易被压垮。最后说点个人体会。onvif-c 组件其实把你从 SOAP/XML 的地狱里拉了出来但它也只解决到“能被 NVR 添加”这一步为止真正让它成为可用设备的往往是后面那些脏活把时钟调准、把内存盯住、把 RTSP 服务器和摄像头驱动绑紧。调试过程中我踩过最大的两个坑一个是把时间同步和鉴权放在最后做结果怎么算密码都对不上另一个是让 XAddrs 依赖写死的 IP结果 DHCP 一换地址 NVR 立刻找不到。做这类嵌入式网络外设我的建议是先让网络、时钟、存储这三个底层组件稳定跑起来再去折腾上层协议否则每层都可能是隐藏的定时炸弹。
返回列表