news 2026/10/21 1:47:02

ESP32 ONVIF相机开发实战:从NVR搜不到设备到成功出流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESP32 ONVIF相机开发实战:从NVR搜不到设备到成功出流

1. 从零理解 onvif-c 组件到底解决了什么问题

如果你手头有一块 ESP32 系列模组,想把它做成一台能被主流 NVR(网络录像机)直接搜索、添加、出流的网络摄像机,那么你迟早会撞上 ONVIF 这堵墙。ONVIF 是一套围绕网络视频设备互操作性的行业规范,它规定了设备发现、能力协商、媒体配置、RTSP 地址获取、PTZ 控制等一系列交互流程。问题在于,官方规范文档动辄几百页,SOAP、WSDL、XML Schema 层层嵌套,用纯 C 从零实现一套可用的服务端,工作量足以劝退绝大多数嵌入式开发者。

onvif-c这个组件就是冲着这个痛点来的。它是一套基于 ESP-IDF 的纯 C 实现,把 ONVIF 服务端最核心的几块能力——设备发现(WS-Discovery)、设备管理(Device Management)、媒体配置(Media)、实时流地址下发(RTSP URI)——封装成可以直接编译进固件的组件。你不需要引入 gSOAP 这类重量级代码生成工具,也不需要啃完整个 ONVIF 规范,只要按它的接口把设备信息、媒体配置、流地址填进去,就能让一台 ESP32 相机在 NVR 的"自动搜索"列表里冒出来。

这篇文章面向三类人:第一类是有 ESP32 开发基础、想快速做出可被 NVR 识别的 IP 相机原型的嵌入式工程师;第二类是已经跑通了 RTSP 推流、但卡在"NVR 搜不到设备"这一步的开发者;第三类是想理解 ONVIF 服务端最小可用集合、为后续自研打基础的技术负责人。我会从工程搭建讲起,一路讲到 NVR 成功添加设备并出流,中间把每个关键决策背后的原因、容易踩的坑、以及实测中的经验都摊开说清楚。

需要先明确一个边界:onvif-c提供的是 ONVIF 服务端的"骨架和关键器官",不是一整套开箱即用的完整相机固件。视频采集、H.264 编码、RTSP 服务端这些仍然需要你自己用 ESP-IDF 的其他组件(比如 esp32-camera、硬件编码器、或者第三方 RTSP 库)来补齐。ONVIF 在这里扮演的角色,是让 NVR"知道有这么一台设备、它有什么能力、该去哪里拉流",而真正把视频流送出去的是 RTSP 那一层。理解这个分工,后面很多设计选择就顺理成章了。

2. 工程搭建:把 onvif-c 塞进 ESP-IDF 项目的正确姿势

2.1 组件获取与目录结构规划

ESP-IDF 的组件机制决定了onvif-c的接入方式。最稳妥的做法是把它作为一个独立组件放进项目的components/目录下,而不是直接改 IDF 自带的组件树。原因很简单:IDF 升级时自带组件会被覆盖,而项目级components/目录下的内容是你自己的,升级 IDF 不会动它。

典型的目录结构长这样:

my_onvif_cam/ ├── CMakeLists.txt ├── main/ │ ├── CMakeLists.txt │ └── main.c ├── components/ │ └── onvif-c/ │ ├── CMakeLists.txt │ ├── include/ │ │ └── onvif_c.h │ └── src/ │ ├── onvif_device.c │ ├── onvif_media.c │ └── onvif_discovery.c └── sdkconfig

main/CMakeLists.txt里通过REQUIRES onvif-c声明依赖,这样编译时 IDF 的构建系统会自动把组件头文件路径和源文件纳入进来。这里有个细节:如果你的onvif-c还依赖esp_http_server、mdns、lwip这些 IDF 自带组件,要在onvif-c/CMakeLists.txt的REQUIRES里一并写上,否则链接阶段会报找不到符号。

2.2 关键编译配置项:别让默认值坑了你

menuconfig里有几个配置直接决定 ONVIF 能不能正常工作,我按重要性排序说。

LWIP 的 SO_REUSEADDR 和最大 socket 数。ONVIF 服务端要同时监听 HTTP(通常是 80 或 8000 端口)和 WS-Discovery 的 UDP 组播(239.255.255.250:3702),再加上 RTSP 的 554 端口,socket 占用比普通 IoT 设备高。默认的CONFIG_LWIP_MAX_SOCKETS是 10,跑单路相机够用,但如果你还想加个 Web 配置页面、或者做多路流,建议提到 16。位置在Component config → LWIP → Max number of open sockets。

HTTP 服务器最大 URI 处理长度。ONVIF 的 SOAP 请求体有时候不小,尤其是GetServices和GetProfiles的响应。CONFIG_HTTPD_MAX_REQ_HDR_LEN默认 512 字节,遇到某些 NVR 发的长 header 会直接 431。实测把它提到 1024 更稳。另外CONFIG_HTTPD_MAX_URI_LEN也要留意,ONVIF 的 endpoint 路径可能比较长。

组播支持。WS-Discovery 依赖 UDP 组播,必须确保CONFIG_LWIP_IPV4打开,并且CONFIG_LWIP_UDP相关选项正常。如果你用的是某些精简版 IDF 配置,组播可能被裁掉了,编译能过但运行时收不到 Probe 消息,这个坑很隐蔽。

任务栈大小。ONVIF 的 XML 解析和 SOAP 响应拼装比较吃栈,尤其是用了递归下降的 XML parser 时。给 ONVIF 服务任务分配至少 4096 字节栈,实测 8192 更保险。栈溢出在 ESP32 上表现为莫名其妙的 Guru Meditation,排查起来很费时间。

2.3 初始化顺序:为什么不能随便调

onvif-c的初始化有严格的先后依赖,顺序错了会出现"服务起来了但 NVR 搜不到"或者"搜到了但拿不到流地址"这类问题。正确的顺序是:

  1. 网络就绪:先连上 WiFi 或以太网,拿到 IP。ONVIF 的所有服务都绑定在具体 IP 上,网络没起来就初始化等于白搭。
  2. RTSP 服务先起:ONVIF 的GetStreamUri返回的是一个 RTSP URL,NVR 拿到后会立刻去连。如果 RTSP 服务还没监听,NVR 会认为设备不可用。所以 RTSP 要先于 ONVIF 初始化。
  3. 媒体配置注册:把视频编码参数(分辨率、帧率、码率、编码格式)注册进onvif-c,这些是GetProfiles和GetVideoEncoderConfiguration的数据来源。
  4. ONVIF 服务启动:最后启动 HTTP 服务端和 WS-Discovery 响应线程。

我见过有人把 ONVIF 初始化放在 WiFi 事件回调里,结果 WiFi 重连时重复初始化导致端口被占用。正确做法是用一个状态机管理,只在首次拿到 IP 时初始化一次,后续重连只更新 IP 绑定。

3. WS-Discovery:让 NVR 在列表里看见你的设备

3.1 组播探测与单播响应的完整链路

NVR 搜索设备的过程,本质是一问一答。NVR 往组播地址239.255.255.250的3702端口发一个Probe消息,消息里带着它想找的设备类型(比如NetworkVideoTransmitter)。局域网内所有支持 WS-Discovery 的设备都会收到这个组播包,然后各自判断自己是否符合条件,符合的就用单播方式回一个ProbeMatch给 NVR 的源地址和源端口。

这里有个关键点:响应必须走单播,不能也往组播地址回。原因在于组播响应会导致所有设备互相收到对方的回复,网络里设备一多就是广播风暴。onvif-c内部已经处理了这一点,它从收到的 Probe 包里提取发送方的 IP 和端口,构造单播响应。但如果你自己改代码,千万别图省事往组播回。

ProbeMatch里必须包含几个关键字段,缺一个 NVR 就可能不认:

字段作用常见错误
EndpointReference设备唯一标识,通常是 urn:uuid:xxx每次重启都变,导致 NVR 重复添加
Types设备类型,相机填 NetworkVideoTransmitter填错成别的类型,NVR 过滤掉
Scopes设备能力范围描述缺失导致 NVR 无法分类
XAddrsONVIF 服务入口 URLIP 写错或端口不对,后续请求全失败

3.2 EndpointReference 的稳定性:一个容易被忽视的细节

EndpointReference(EPR)是 NVR 识别"这是不是同一台设备"的依据。如果你的 EPR 每次重启都变,NVR 会认为局域网里不断有新设备出现,设备列表越堆越多,用户体验极差。

正确做法是基于 ESP32 的 MAC 地址生成一个固定的 UUID。ESP32 的 MAC 地址在出厂时就烧录好了,全球唯一且不变。你可以取 MAC 的 6 个字节,按 UUID 格式拼成urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx。onvif-c通常提供了设置 EPR 的接口,初始化时传入一次即可。实测下来,用 MAC 派生的 EPR 在主流 NVR 上都能稳定识别,重启后不会重复添加。

3.3 Scopes 配置:决定 NVR 怎么归类你的设备

Scopes 是一组用空格分隔的 URI,描述设备的位置、类型、名称等信息。NVR 用这些信息做设备分类和显示。常见的 Scope 格式包括:

  • onvif://www.onvif.org/type/video_encoder:表明这是视频编码设备
  • onvif://www.onvif.org/Profile/Streaming:支持的 ONVIF Profile
  • onvif://www.onvif.org/name/MyCam:设备显示名称
  • onvif://www.onvif.org/location/office:设备位置

这里有个实操经验:设备名称尽量用 ASCII 字符,别用中文。有些 NVR 对 Scope 里的非 ASCII 字符处理有问题,显示成乱码甚至直接忽略整个 Scope。如果你确实需要中文名称,放在 ONVIF 的GetDeviceInformation响应里,那个字段的编码兼容性更好。

3.4 探测响应的时机与去重

WS-Discovery 规范里有个MessageID和RelatesTo机制,用来匹配请求和响应。onvif-c在构造 ProbeMatch 时会把收到的 Probe 的 MessageID 填进 RelatesTo,这样 NVR 能确认这个响应是针对自己那次探测的。

另一个容易忽略的是响应延迟。规范建议设备在收到 Probe 后随机延迟一小段时间(0 到 500ms)再响应,避免所有设备同时回复造成碰撞。onvif-c内部一般会做一个小的随机延迟。如果你发现 NVR 搜索时设备时有时无,可以检查一下这个延迟是否被改成了 0,多设备环境下 0 延迟很容易丢包。

4. SOAP 服务端:Device 与 Media 两大能力集的实现要点

4.1 HTTP 服务端的路由设计

ONVIF 的所有请求都走 HTTP POST,路径通常是/onvif/device_service和/onvif/media_service。onvif-c基于esp_http_server注册了两个 URI handler,分别处理 Device 和 Media 的 SOAP 请求。

这里有个设计选择值得说:为什么用两个独立路径而不是一个?因为 ONVIF 规范允许设备把不同能力集暴露在不同 endpoint 上,NVR 通过GetServices或GetCapabilities拿到各个服务的地址。分开路径的好处是职责清晰,调试时看日志一眼就知道是哪个服务出的问题。坏处是要维护两套 handler,但对嵌入式场景来说这点开销可以接受。

handler 内部的处理流程是固定的:读取 HTTP body → 解析 SOAP Envelope → 提取 Body 里的操作名 → 分发到对应的处理函数 → 拼装 SOAP 响应 → 返回 200。onvif-c把 XML 解析和拼装封装好了,你主要关心的是每个操作返回什么数据。

4.2 Device 服务:NVR 最先问的几个问题

NVR 添加设备时,会按顺序问几个问题,onvif-c必须都能答上来:

GetDeviceInformation:返回厂商、型号、固件版本、序列号、硬件 ID。这些信息显示在 NVR 的设备列表里。厂商和型号建议填真实可读的字符串,别填默认值,否则 NVR 列表里一堆"Unknown Device"很难区分。序列号用 MAC 派生,保证唯一。

GetCapabilities:返回设备支持的能力分类,包括 Device、Media、PTZ、Imaging 等。如果你没实现 PTZ,就不要在响应里声明 PTZ 能力,否则 NVR 会显示云台控制按钮,用户点了没反应,体验很差。这一点很多人踩坑:为了"看起来功能全"把能力都声明上,结果 NVR 发来一堆没实现的请求,设备要么不响应要么报错。

GetServices:返回服务列表和各自的 endpoint 地址。这个响应里的 XAddr 必须和实际监听的地址一致。我见过有人写死了192.168.1.100,换到别的网段后 NVR 拿到的地址连不上,设备添加失败。正确做法是运行时获取当前 IP 动态拼装。

GetSystemDateAndTime:返回设备时间。有些 NVR 在添加设备时会先校时,如果这个请求失败,后续流程可能中断。即使你的设备没有 RTC,也要返回一个合理的时间(比如从 SNTP 同步,或者返回一个固定值),别直接报错。

4.3 Media 服务:Profile 与 StreamUri 的构造逻辑

Media 服务是 NVR 拉流的核心。NVR 会先调GetProfiles拿到媒体配置列表,然后对选中的 Profile 调GetStreamUri拿 RTSP 地址。

Profile 的构造:一个 Profile 包含视频编码配置(VideoEncoderConfiguration)、音频配置(AudioEncoderConfiguration,可选)、PTZ 配置(可选)。视频编码配置里最关键的是分辨率、帧率、码率、编码格式(H.264/H.265)、GOP 长度。这些值必须和你实际 RTSP 推流的参数一致,否则 NVR 按 Profile 里的参数去解码,画面会花屏或者卡顿。

onvif-c一般提供一个注册 Profile 的接口,你把参数填进去,它负责在GetProfiles时拼装成规范的 XML。这里有个细节:Profile 的 token 要稳定,NVR 会用 token 来引用 Profile。如果你每次重启 token 都变,NVR 保存的配置就失效了。

StreamUri 的构造:GetStreamUri返回的 RTSP URL 格式通常是rtsp://<ip>:554/<path>。IP 要动态获取,path 要和你 RTSP 服务端注册的路径一致。如果你的 RTSP 服务需要认证,URL 里可以带用户名密码,格式是rtsp://user:pass@ip:554/path。但要注意,有些 NVR 对 URL 里的特殊字符处理不好,密码里如果有@或:会解析错误,建议密码用纯字母数字。

4.4 SOAP 响应的命名空间与格式陷阱

ONVIF 的 SOAP 响应对命名空间极其敏感。每个元素都要带正确的 namespace,少了或者错了,NVR 的 XML parser 直接报错,表现为"设备添加失败"但没有任何有用提示。

onvif-c内部处理了命名空间拼装,但如果你自己扩展操作,要特别注意几点:Device 服务的命名空间是http://www.onvif.org/ver10/device/wsdl,Media 是http://www.onvif.org/ver10/media/wsdl,SOAP Envelope 是http://www.w3.org/2003/05/soap-envelope。响应里的元素前缀(比如tds:、trt:)要和请求里的一致,虽然规范上前缀名不重要,但有些 NVR 实现就是按前缀匹配的,实测中遇到过只认tds:不认其他前缀的情况。

另一个坑是空元素的写法。ONVIF 里有些字段是可选的,不需要返回时,要么整个元素不出现,要么写成自闭合标签<foo/>。千万别写成<foo></foo>带空内容,某些严格的 NVR 会认为这是非法值。

5. 联调实录:NVR 搜不到、添加失败、出流异常的排查链路

5.1 搜不到设备:从抓包开始逐层排除

NVR 搜索列表里没有你的设备,这是最常见的问题。排查要自下而上,别一上来就怀疑 ONVIF 代码。

第一步,确认设备真的发出了 ProbeMatch。在 ESP32 侧打开日志,看有没有收到 Probe 并发出响应。如果日志里连 Probe 都没收到,说明组播没通。检查 ESP32 是否加入了组播组,有些网络环境(比如某些企业 WiFi)会屏蔽组播,这时候 WS-Discovery 直接废掉,只能改用固定 IP 手动添加。

第二步,确认 ProbeMatch 发对了地址。如果 ESP32 日志显示发了响应,但 NVR 没收到,用抓包工具在 NVR 侧看。重点看响应的目标 IP 和端口是不是 NVR 的源地址和源端口。我遇到过一种情况:ESP32 收到的 Probe 源端口是 3702,但 NVR 实际监听响应的是另一个临时端口,如果响应发到 3702 就丢了。正确做法是从收到的 UDP 包的源地址和源端口直接回复,不要假设端口。

第三步,确认 ProbeMatch 内容合法。如果 NVR 收到了响应但不显示设备,多半是内容问题。重点检查 Types 字段是否是NetworkVideoTransmitter,XAddrs 是否是可达的 URL。可以用一个 ONVIF 测试工具模拟 NVR 发 Probe,看你的响应能不能被解析。

5.2 添加失败:认证与能力协商的坑

设备能被搜到,但添加时失败,问题通常出在认证或能力协商阶段。

认证问题:ONVIF 支持 WS-Security 的 UsernameToken 认证。如果你的设备声明需要认证,NVR 会在后续请求里带上认证头。onvif-c需要正确解析这个头并校验。常见错误是时间戳校验太严格,NVR 和设备时间差超过允许范围就拒绝。建议把时间戳容差放宽到 5 分钟,或者干脆在联调阶段先关闭认证,确认流程通了再打开。

能力协商问题:NVR 调GetCapabilities后,会根据返回的能力决定后续调哪些接口。如果你声明了某个能力但没实现对应接口,NVR 调用时收到错误响应,可能直接放弃添加。所以能力声明要"量力而行",实现了什么就声明什么。

GetServices 地址问题:前面提过,XAddr 必须是运行时可用的地址。有个隐蔽的坑:如果 ESP32 同时有 WiFi 和以太网两个接口,GetServices返回的地址可能是 NVR 不可达的那个。解决办法是绑定到收到请求的那个接口的 IP,esp_http_server的 handler 里可以拿到本地地址信息。

5.3 出流异常:Profile 参数与实际流不匹配

设备添加成功,但预览画面花屏、卡顿或者黑屏,问题多半在 Profile 参数和实际 RTSP 流不一致。

分辨率不匹配:Profile 里写 1080p,实际推流是 720p,NVR 按 1080p 解码,画面会错位。解决办法是让 Profile 参数从实际的编码器配置读取,别硬编码。

帧率与码率:Profile 里的帧率和码率是给 NVR 做参考的,但如果和实际差太多,NVR 的缓冲策略会出问题。比如 Profile 写 30fps 实际只有 10fps,NVR 可能认为丢帧严重而频繁请求关键帧。

GOP 长度:GOP 太长会导致 NVR 起播慢,因为要等下一个 I 帧。建议 GOP 设为帧率的 1 到 2 倍,比如 25fps 对应 GOP 25 到 50。

RTSP 路径不一致:GetStreamUri返回的 path 和 RTSP 服务端注册的 path 必须完全一致,包括大小写。我见过 path 里多个斜杠或者大小写不匹配导致 404 的情况。

5.4 多 NVR 并发访问:资源竞争与连接数限制

一台设备可能同时被多个 NVR 或客户端访问。ESP32 的资源有限,并发连接数一多就容易出问题。

socket 耗尽:每个 RTSP 连接占一个 socket,ONVIF 的 HTTP 请求也占 socket。如果CONFIG_LWIP_MAX_SOCKETS设得太小,新连接会被拒绝。建议根据预期并发数留足余量,单路相机至少留 16 个。

RTSP 会话管理:多个客户端拉同一路流时,理想做法是共享同一个编码流,而不是每个连接独立编码。这需要 RTSP 服务端支持流复用。如果每个连接独立编码,ESP32 的 CPU 根本扛不住。onvif-c不管 RTSP 层,这部分要你自己在 RTSP 服务端实现。

内存碎片:长时间运行后,频繁的 malloc/free 会导致堆碎片。ONVIF 的 XML 解析和响应拼装会动态分配内存,建议用内存池或者静态缓冲区,减少碎片。实测中,用静态缓冲区后连续运行一周的稳定性明显好于动态分配。

6. 从能用到好用:稳定性与兼容性的进阶经验

6.1 心跳与保活:让 NVR 知道你还活着

NVR 添加设备后,会定期发请求确认设备在线。如果设备长时间不响应,NVR 会标记为离线。onvif-c要确保这些保活请求都能及时响应。

常见的保活请求是GetSystemDateAndTime或者GetDeviceInformation。这些请求处理很快,一般不会超时。但如果你在 ONVIF 任务里做了耗时操作(比如同步读传感器),可能阻塞响应。建议把 ONVIF 服务放在独立任务里,优先级适中,别被其他任务饿死。

另外,WS-Discovery 的 Hello 和 Bye 消息也影响 NVR 对设备状态的判断。设备启动时发 Hello,关机时发 Bye,NVR 能更快更新状态。onvif-c一般支持这两个消息,初始化后调一下发送 Hello,关机前发 Bye。实测中,发了 Hello 的设备在 NVR 里出现得更快。

6.2 兼容性矩阵:不同 NVR 的脾气

不同厂商的 NVR 对 ONVIF 的实现细节要求不一样,这是最让人头疼的地方。我整理了几个常见的行为差异:

NVR 行为表现应对
严格校验命名空间前缀前缀不对直接报错响应前缀与请求保持一致
要求 GetServices 返回所有服务缺服务则添加失败即使没实现也返回占位服务
对时间戳容差小时间差大则认证失败放宽容差或同步时间
探测响应超时短响应慢则搜不到减少响应延迟,别做耗时操作
要求特定 Profile token 格式token 不合法则忽略用规范推荐的 token 命名

应对兼容性的通用策略是:先满足规范的最小集合,再针对具体 NVR 做适配。别一上来就为某个 NVR 写特殊逻辑,那样代码会越来越乱。可以做一个配置项,针对不同 NVR 开关某些兼容行为。

6.3 日志与调试:把问题定位时间从小时降到分钟

ONVIF 联调最痛苦的是错误信息不明确。NVR 只说"添加失败",不说为什么。所以设备侧的日志至关重要。

建议在onvif-c里加分级日志:收到什么请求、解析出什么操作、返回什么响应,都打出来。日志级别可以运行时调整,联调时开 DEBUG,稳定后开 WARN。日志里要包含请求的 MessageID,方便和 NVR 侧抓包对应。

另一个技巧是把 SOAP 请求和响应完整打印出来。虽然日志量大,但联调阶段这是最快的定位手段。可以加一个开关,只在特定 IP 的请求时打印完整内容,避免日志刷屏。

6.4 资源占用优化:给视频编码留足 CPU

ONVIF 服务本身不重,但在 ESP32 这种资源紧张的平台上,任何额外开销都要计较。几个优化点:

XML 解析用流式而非 DOM:DOM 解析要把整个 XML 加载到内存建树,流式解析边读边处理,内存占用小得多。onvif-c如果用的是 DOM,可以考虑换成轻量级流式 parser。

响应缓冲区复用:每个请求都 malloc 一块响应缓冲区很浪费。可以预分配一块固定大小的缓冲区,所有响应复用它。注意加锁保护,因为可能有并发请求。

降低 ONVIF 任务优先级:ONVIF 的实时性要求不高,优先级设得比视频编码任务低,让编码器优先拿 CPU。实测中,把 ONVIF 任务优先级降到 5 以下,视频流的稳定性有明显提升。

6.5 安全加固:别让相机成为网络里的短板

ONVIF 设备如果裸奔,很容易被扫描和攻击。几个基本加固措施:

开启认证:至少对 Device 和 Media 服务开启 UsernameToken 认证。用户名密码别用默认值,初始化时强制要求设置。

限制访问来源:如果 NVR 的 IP 固定,可以在 ONVIF 服务端做 IP 白名单,只响应白名单内的请求。这能挡住大部分扫描。

关闭不必要的服务:没实现的 ONVIF 操作直接返回错误,别返回空响应。空响应可能被利用来做信息探测。

固件更新机制:ONVIF 有固件更新相关的接口,如果你不实现,确保这些接口返回明确的"不支持",别留后门。

7. 写在最后:一些踩坑后的个人体会

做 ESP32 上的 ONVIF 相机,最深的体会是"规范是死的,NVR 是活的"。ONVIF 规范写得再清楚,落到具体 NVR 上总有意想不到的行为。我的建议是,联调阶段一定要用真实的 NVR 测,别只用 ONVIF 测试工具。测试工具按规范来,真实 NVR 各有各的脾气,工具过了不代表 NVR 能过。

另一个体会是,onvif-c这类组件的价值在于帮你跨过"从零实现"的门槛,但它不是银弹。RTSP 层、视频编码层的问题,它管不了。所以遇到出流异常,先确认 RTSP 单独能不能拉流,再怀疑 ONVIF。分层排查,能省很多时间。

最后说个具体的:ESP32 的内存是真的紧张。ONVIF 的 XML 处理、RTSP 的缓冲、视频编码的帧缓冲,三者抢内存是常态。我的做法是给视频编码留足 PSRAM,ONVIF 和 RTSP 尽量用内部 RAM,并且严格控制动态分配。这个分配策略定下来之后,连续运行几天的稳定性比之前好太多。如果你也在做类似的东西,建议早点把内存规划清楚,别等到跑起来才发现到处 OOM。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 1:46:05

RTOS任务核心解读:状态、调度、栈与优先级实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 1:45:11

5 分钟省掉 3 小时手搓:戴森球计划工厂蓝图仓库使用指南

5 分钟省掉 3 小时手搓&#xff1a;戴森球计划工厂蓝图仓库使用指南 【免费下载链接】FactoryBluePrints 游戏戴森球计划的**工厂**蓝图仓库 项目地址: https://gitcode.com/GitHub_Trending/fa/FactoryBluePrints FactoryBluePrints 是《戴森球计划》工厂蓝图仓库&…

作者头像 李华
网站建设 2026/10/21 1:42:39

Android权限革命:Shizuku技术终极指南与完整教程

Android权限革命&#xff1a;Shizuku技术终极指南与完整教程 在Android生态系统中&#xff0c;Shizuku技术正在掀起一场真正的权限革命&#xff01;&#x1f3af; 这项创新技术允许普通应用程序在非root设备上直接使用系统API&#xff0c;通过ADB获得提升的权限&#xff0c;彻底…

作者头像 李华