ESP32 纯 C 实现 ONVIF 协议栈:让 NVR 直接发现并预览摄像头

发布时间:2026/10/21 10:29:11
ESP32 纯 C 实现 ONVIF 协议栈:让 NVR 直接发现并预览摄像头 1. 为什么要在 ESP32 上折腾 ONVIF 相机先说清楚这个项目到底在干什么。ONVIF 是一套网络视频设备之间的通信标准简单理解就是让摄像头、录像机、管理平台之间能互相认出来并对话的通用语言。你手上有一块 ESP32接上摄像头模组想让它变成一个能被 NVR网络录像机直接搜索、添加、出画面的网络摄像机——这中间缺的那块拼图就是 ONVIF 协议栈的实现。onvif-c这个组件就是干这件事的它是一个纯 C 写的 ESP-IDF 组件不依赖 C、不依赖庞大的第三方框架直接跑在 ESP32 上把 ONVIF 的设备发现WS-Discovery、设备管理Device Management、媒体服务Media Service、RTSP 推流这几块核心能力补齐。做完之后的效果是NVR 在局域网里点搜索设备你的 ESP32 相机能出现在列表里点添加能拿到它的 RTSP 地址点开预览能看到实时画面。适合谁看这篇三类人。第一类是做嵌入式视频产品的开发者手上有 ESP32 加摄像头模组的硬件需要快速验证 ONVIF 接入能力。第二类是想学 ONVIF 协议但被官方文档劝退的人通过一个能跑起来的最小实现去理解协议到底在传什么。第三类是做智能家居、安防小设备的独立开发者想用低成本方案做出能被主流 NVR 识别的相机。我踩过的坑先摆在这ONVIF 最坑的地方不是协议本身复杂而是各家 NVR 对标准的实现程度参差不齐。有的 NVR 只认 WS-Discovery 的特定字段有的对 SOAP 响应的命名空间极其挑剔有的要求 RTSP 的 SDP 描述必须包含特定属性。所以这个组件的价值不在于实现了协议而在于实现了能被真实 NVR 接受的协议子集。下面我把从建工程到出画面的完整路径拆开讲。2. 整体架构设计与方案选型思路2.1 为什么选纯 C 而不是 C 封装ESP-IDF 本身是 C 为主的框架虽然支持 C但在资源受限的 ESP32 上C 的异常处理、RTTI、模板实例化都会带来额外的 flash 和 RAM 开销。ONVIF 的 SOAP 消息处理本质上是字符串拼接和解析用 C 写反而更直接。onvif-c选择纯 C 的另一个原因是可移植性同一套协议逻辑理论上可以搬到其他 RTOS 或者裸机环境只要把网络和 socket 层抽象出来就行。从内存角度看一个典型的 ONVIF 交互流程涉及UDP 组播接收探测请求、解析 XML、构造 SOAP 响应、TCP 上处理 HTTP POST 的 SOAP 请求、RTSP 会话管理。这些如果用 C 的字符串类和容器动态分配会非常频繁在 ESP32 这种只有几百 KB 可用堆的环境里容易碎片化。纯 C 配合静态缓冲区和固定大小的栈上数组能把内存行为控制得很确定。2.2 组件分层从 socket 到 SOAP 到业务整个组件的分层我建议这样理解从上到下业务层设备信息、媒体配置、能力集这些是 ONVIF 语义层面的东西SOAP 层把业务数据包装成符合 ONVIF WSDL 定义的 XML 信封HTTP/RTSP 层SOAP 走 HTTP POST视频流走 RTSP传输层TCP 用于 HTTP 和 RTSPUDP 组播用于 WS-Discovery网络抽象层对接 ESP-IDF 的 lwIP socket API这样分层的好处是当某个 NVR 对 SOAP 响应有特殊要求时你只需要改 SOAP 层的模板不用动业务逻辑。我实测下来把 SOAP 响应做成可配置的模板字符串是最省事的做法比用 XML 库动态生成要快得多也更容易针对不同 NVR 做微调。2.3 WS-Discovery 是整个接入的敲门砖NVR 添加设备的第一步永远是发现。ONVIF 用的是 WS-Discovery 协议NVR 往 UDP 组播地址239.255.255.250的3702端口发一个Probe消息设备收到后单播回一个ProbeMatch。这个 ProbeMatch 里包含了设备的服务地址XAddrNVR 拿到之后才会去访问设备管理服务。这里有个关键点ProbeMatch 里的 XAddr 必须是 NVR 能访问到的地址。如果你的 ESP32 有多个网卡或者地址填错了 NVR 就连不上。我一般是在收到 Probe 的时候从 socket 的源地址反推应该回复哪个 IP而不是硬编码。这个细节后面实操部分会展开。2.4 RTSP 推流方案的选择ONVIF 的媒体服务只负责告诉 NVR 去哪里取流真正的视频流是走 RTSP 的。ESP32 上做 RTSP 服务端有几种思路自己实现 RTSP 的 OPTIONS/DESCRIBE/SETUP/PLAY 交互或者用现成的组件。onvif-c的定位是 ONVIF 协议部分RTSP 通常配合一个独立的 RTSP 服务组件使用。我选的是自己实现精简 RTSP 服务端因为标准 RTSP 服务端组件往往带着一堆用不上的特性而 NVR 实际用到的就是那几个方法。精简实现的好处是可控出问题的时候知道去哪找。RTSP 的 SDP 描述里sps和pps这两个 H.264 参数集必须正确否则 NVR 解不出画面——这是最常见的能连上但黑屏的原因。3. 核心细节解析与实操要点3.1 工程目录结构与组件引入一个能跑的最小工程目录结构建议这样组织onvif_cam/ ├── CMakeLists.txt ├── sdkconfig.defaults ├── main/ │ ├── CMakeLists.txt │ └── main.c └── components/ └── onvif-c/ ├── CMakeLists.txt ├── include/ │ └── onvif.h └── src/ ├── onvif_discovery.c ├── onvif_device.c ├── onvif_media.c └── onvif_soap.cmain/CMakeLists.txt里要声明依赖idf_component_register( SRCS main.c INCLUDE_DIRS . REQUIRES onvif-c nvs_flash esp_netif )sdkconfig.defaults里几个必须调的参数CONFIG_LWIP_SO_RCVBUF8192 CONFIG_LWIP_MAX_SOCKETS16 CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM16 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM32注意CONFIG_LWIP_MAX_SOCKETS默认值偏小ONVIF 同时要开 UDP 组播 socket、HTTP 监听 socket、RTSP 监听 socket再加上每个 NVR 连接会占用 socket默认值很容易不够用表现为发现能收到但回复发不出去。3.2 WS-Discovery 的组播接收实现组播接收的核心是加入组播组并绑定端口。关键代码逻辑int sock socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); struct sockaddr_in addr { .sin_family AF_INET, .sin_port htons(3702), .sin_addr.s_addr htonl(INADDR_ANY), }; bind(sock, (struct sockaddr *)addr, sizeof(addr)); struct ip_mreq mreq; mreq.imr_multiaddr.s_addr inet_addr(239.255.255.250); mreq.imr_interface.s_addr htonl(INADDR_ANY); setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, mreq, sizeof(mreq));收到 Probe 后解析出MessageID构造 ProbeMatch 响应。这里有个必须注意的细节ProbeMatch 的RelatesTo字段要填 Probe 里的MessageIDNVR 靠这个字段匹配请求和响应。填错了 NVR 会忽略你的响应表现为设备不出现。响应里的 XAddr 构造逻辑// 从 recvfrom 拿到的源地址反推本机 IP struct sockaddr_in from; socklen_t fromlen sizeof(from); recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)from, fromlen); // 用 getsockname 或者遍历 netif 拿到本机对应 IP我实测下来最稳的做法是遍历esp_netif拿到当前 STA 接口的 IP而不是从源地址反推因为有些网络环境下源地址可能是网关地址。3.3 SOAP 响应的命名空间陷阱ONVIF 的 SOAP 消息对命名空间极其敏感。一个典型的 GetDeviceInformation 响应?xml version1.0 encodingUTF-8? SOAP-ENV:Envelope xmlns:SOAP-ENVhttp://www.w3.org/2003/05/soap-envelope xmlns:tdshttp://www.onvif.org/ver10/device/wsdl SOAP-ENV:Body tds:GetDeviceInformationResponse tds:ManufacturerMyCam/tds:Manufacturer tds:ModelESP32-CAM/tds:Model tds:FirmwareVersion1.0.0/tds:FirmwareVersion tds:SerialNumberSN123456/tds:SerialNumber tds:HardwareIdHW-01/tds:HardwareId /tds:GetDeviceInformationResponse /SOAP-ENV:Body /SOAP-ENV:Envelope命名空间前缀tds、SOAP-ENV本身可以任意命名但URI 必须完全正确。不同服务的命名空间不同服务命名空间 URIDevicehttp://www.onvif.org/ver10/device/wsdlMediahttp://www.onvif.org/ver10/media/wsdlEventshttp://www.onvif.org/ver10/events/wsdlPTZhttp://www.onvif.org/ver20/ptz/wsdl踩坑记录某品牌 NVR 在解析响应时如果 Body 里直接是响应元素而没有正确的外层 Envelope 命名空间会直接报设备不支持。我一开始图省事把 Envelope 的命名空间写成了http://schemas.xmlsoap.org/soap/envelope/SOAP 1.1结果那台 NVR 死活不认。ONVIF 要求的是 SOAP 1.2命名空间必须是http://www.w3.org/2003/05/soap-envelope。3.4 能力集Capabilities的声明NVR 在添加设备后会先调GetCapabilities问设备支持哪些能力。这个响应决定了 NVR 后续会调哪些接口。如果你声明了 Media 能力NVR 就会去调GetProfiles如果声明了 EventsNVR 就会去订阅事件。一个精简但够用的能力集响应tds:GetCapabilitiesResponse tds:Capabilities tt:Device tt:XAddrhttp://192.168.1.100/onvif/device_service/tt:XAddr /tt:Device tt:Media tt:XAddrhttp://192.168.1.100/onvif/media_service/tt:XAddr tt:StreamingCapabilities tt:RTPMulticastfalse/tt:RTPMulticast tt:RTP_TCPtrue/tt:RTP_TCP tt:RTP_RTSP_TCPtrue/tt:RTP_RTSP_TCP /tt:StreamingCapabilities /tt:Media /tds:Capabilities /tds:GetCapabilitiesResponseRTP_RTSP_TCP这个字段很关键。很多 NVR 默认走 UDP 收 RTP但 ESP32 上做 UDP 推流容易丢包走 RTSP over TCP 更稳。声明支持 TCP 之后NVR 在 SETUP 阶段就会用RTP/AVP/TCP传输。3.5 媒体配置与 Profile 构造GetProfiles返回的 Profile 是 NVR 取流的核心依据。一个 Profile 包含视频编码配置、分辨率、帧率、码率以及对应的 RTSP 地址。精简实现里通常只提供一个主码流 Profiletrt:GetProfilesResponse trt:Profiles tokenProfile_1 fixedtrue tt:NamemainStream/tt:Name tt:VideoSourceConfiguration tokenVSC_1 tt:NameVideoSource/tt:Name tt:UseCount1/tt:UseCount tt:SourceTokenVS_1/tt:SourceToken tt:Bounds x0 y0 width1280 height720/ /tt:VideoSourceConfiguration tt:VideoEncoderConfiguration tokenVEC_1 tt:NameH264/tt:Name tt:UseCount1/tt:UseCount tt:EncodingH264/tt:Encoding tt:Resolution tt:Width1280/tt:Width tt:Height720/tt:Height /tt:Resolution tt:RateControl tt:FrameRateLimit15/tt:FrameRateLimit tt:BitrateLimit1024/tt:BitrateLimit /tt:RateControl /tt:VideoEncoderConfiguration /trt:Profiles /trt:GetProfilesResponsetoken字段是 NVR 后续调GetStreamUri时要传回来的标识必须和 Profile 里的 token 一致。我见过有人把 token 写成随机字符串结果 NVR 调 GetStreamUri 时传回来的 token 对不上直接报错。4. 完整实操流程与关键环节实现4.1 从零建工程到编译通过第一步用 ESP-IDF 的模板建工程idf.py create-project onvif_cam cd onvif_cam第二步把onvif-c组件放到components/目录下。如果是从仓库拉取注意检查组件的CMakeLists.txt是否正确声明了依赖idf_component_register( SRCS src/onvif_discovery.c src/onvif_device.c src/onvif_media.c src/onvif_soap.c INCLUDE_DIRS include REQUIRES lwip esp_netif )第三步配置 WiFi。在main.c里初始化 NVS、netif、event loop然后连上 AP。这一步是标准流程但有个细节必须在拿到 IP 之后再启动 ONVIF 服务否则 XAddr 里的 IP 是空的。我一般是在IP_EVENT_STA_GOT_IP事件回调里启动 ONVIF 任务。第四步编译烧录idf.py set-target esp32 idf.py build idf.py -p /dev/ttyUSB0 flash monitor4.2 启动顺序与任务划分ONVIF 服务启动后内部至少要跑三个任务Discovery 任务阻塞在 UDP 组播 socket 的 recvfrom 上收到 Probe 就回复HTTP 任务监听 80 端口处理 SOAP 请求RTSP 任务监听 554 端口处理 RTSP 会话任务优先级建议RTSP 最高视频流不能卡HTTP 次之Discovery 最低。栈大小方面HTTP 任务因为要解析 XML栈给到 8KB 比较稳RTSP 任务 6KBDiscovery 4KB 够用。xTaskCreate(rtsp_task, rtsp, 6144, NULL, 6, NULL); xTaskCreate(http_task, http, 8192, NULL, 5, NULL); xTaskCreate(disc_task, disc, 4096, NULL, 4, NULL);实操心得ESP32 的默认任务栈是 3584 字节如果你在 HTTP 任务里用了递归解析或者大的局部数组很容易栈溢出。表现是设备随机重启日志里能看到***ERROR*** A stack overflow in task http。遇到这个先把栈加大再说。4.3 HTTP SOAP 请求的处理流程NVR 发来的 SOAP 请求是 HTTP POSTContent-Type 是application/soapxml。处理流程读取 HTTP 请求头拿到Content-Length按长度读取 body从 body 里提取 SOAP Action在 HTTP 头或者 body 的 Body 第一个子元素名根据 Action 分发到对应的处理函数构造响应带上正确的 Content-Type 和 Content-Length 返回提取 Action 的时候我建议优先从 HTTP 头的SOAPAction字段读读不到再从 body 里解析。因为有些 NVR 不发 SOAPAction 头有些发了但格式不规范。char *action http_get_header(req, SOAPAction); if (!action) { action soap_extract_action(body); }分发逻辑用一张表最清晰typedef struct { const char *action; esp_err_t (*handler)(const char *req, char *resp, size_t resp_size); } soap_handler_t; static const soap_handler_t handlers[] { {GetDeviceInformation, handle_get_device_info}, {GetCapabilities, handle_get_capabilities}, {GetProfiles, handle_get_profiles}, {GetStreamUri, handle_get_stream_uri}, {GetSystemDateAndTime, handle_get_datetime}, };4.4 GetSystemDateAndTime 为什么必须实现这个接口看起来无关紧要但很多 NVR 在添加设备时会先调它如果返回错误或者超时NVR 可能直接放弃添加。更坑的是有些 NVR 会拿设备时间去做鉴权校验时间差太大就拒绝。响应里要包含 UTC 时间和本地时区tds:GetSystemDateAndTimeResponse tds:SystemDateAndTime tt:DateTimeTypeManual/tt:DateTimeType tt:DaylightSavingsfalse/tt:DaylightSavings tt:TimeZone tt:TZUTC08:00/tt:TZ /tt:TimeZone tt:UTCDateTime tt:Time tt:Hour10/tt:Hour tt:Minute30/tt:Minute tt:Second0/tt:Second /tt:Time tt:Date tt:Year2024/tt:Year tt:Month1/tt:Month tt:Day15/tt:Day /tt:Date /tt:UTCDateTime /tds:SystemDateAndTime /tds:GetSystemDateAndTimeResponse时间从 ESP32 的 RTC 或者 SNTP 同步拿。如果没联网同步时间至少给一个合理的默认值别给 1970 年。4.5 RTSP 会话与 SDP 构造NVR 拿到 StreamUri 后会发起 RTSP 交互。完整流程OPTIONS→ 返回支持的方法DESCRIBE→ 返回 SDP 描述SETUP→ 建立传输返回 Session IDPLAY→ 开始推流TEARDOWN→ 结束SDP 描述是黑屏问题的重灾区。一个正确的 H.264 SDPv0 o- 0 0 IN IP4 192.168.1.100 sESP32 Camera cIN IP4 0.0.0.0 t0 0 mvideo 0 RTP/AVP 96 artpmap:96 H264/90000 afmtp:96 packetization-mode1;profile-level-id42C01E;sprop-parameter-setsZ0LAHtoCgPaEAAAAwAQAAAMDKPFCqg,aM4PyA acontrol:trackID0sprop-parameter-sets就是 SPS 和 PPS 的 Base64 编码逗号分隔。这两个值必须来自你实际编码器输出的参数集不能随便填。我见过有人从网上抄了一段 SDP结果 NVR 能连上但画面花屏就是因为 SPS/PPS 和实际码流不匹配。关键技巧SPS/PPS 在编码器初始化后就能拿到把它缓存起来每次 DESCRIBE 时动态拼进 SDP。如果你的编码器是硬件编码参数集可能在第一次编码输出时才产生那就等第一帧编码完成后再允许 RTSP 连接。4.6 RTP 打包与发送H.264 的 RTP 打包有两种模式单 NAL 单元模式和分片模式FU-A。小于 MTU 的 NAL 用单 NAL 模式大于 MTU 的用 FU-A 分片。单 NAL 模式很简单RTP 头后面直接跟 NAL 数据。FU-A 模式要把 NAL 头拆开第一个分片的 FU indicator 和 FU header 有特殊标志位// FU-A 起始分片 fu_indicator (nal[0] 0xE0) | 28; fu_header (nal[0] 0x1F) | 0x80; // Start bit // 中间分片 fu_header nal[0] 0x1F; // 结束分片 fu_header (nal[0] 0x1F) | 0x40; // End bitMTU 一般按 1400 字节算留出 RTP 头和 UDP/IP 头的空间。分片大小算错会导致 NVR 收到的数据拼不起来表现为画面撕裂或者直接黑屏。RTP 时间戳的步进H.264 的时钟频率是 90000Hz如果帧率是 15fps每帧时间戳增加90000/15 6000。这个值算错会导致 NVR 播放速度异常。5. 常见问题与排查技巧实录5.1 NVR 搜索不到设备这是最高频的问题。排查顺序排查项检查方法常见原因组播是否收到在 recvfrom 后打日志防火墙拦截、组播组没加入ProbeMatch 是否发出在 sendto 后打日志socket 错误、目标地址错MessageID 是否匹配对比 Probe 和 ProbeMatchRelatesTo 字段填错XAddr 是否可达用浏览器访问 XAddrIP 填错、端口不对命名空间是否正确抓包看响应 XMLSOAP 版本用错我遇到最多的是组播没收到。ESP32 的 WiFi 在某些路由器下默认不开组播转发需要在路由器设置里打开 IGMP Snooping 或者组播转发。另外如果 ESP32 连的是 5G 频段而 NVR 在 2.4G跨频段组播可能不通建议都放同一个频段。5.2 能添加但预览黑屏黑屏问题按这个顺序查GetStreamUri 返回的地址对不对用 VLC 直接打开这个 RTSP 地址如果 VLC 能播说明是 NVR 的问题如果 VLC 也黑屏说明是设备端问题。SDP 里的 SPS/PPS 对不对把 SDP 里的 sprop-parameter-sets 解码出来和实际码流的参数集对比。RTP 打包对不对抓包看 RTP 序列号是否连续时间戳步进是否正常。传输模式对不对如果 NVR 走 UDP 收流但网络丢包严重改成 TCP 试试。独家技巧在设备端加一个RTP 统计日志每发送 100 个包打印一次序列号和时间戳。如果序列号跳变说明打包逻辑有问题如果时间戳不按预期步进说明时间戳计算错了。这个日志在排查黑屏时比抓包还快。5.3 设备随机重启ESP32 随机重启八成是内存或者栈的问题。排查方法看串口日志有没有Stack overflow或者Heap corruption用esp_get_free_heap_size()定期打印剩余堆用uxTaskGetStackHighWaterMark()检查各任务栈余量ONVIF 场景下内存泄漏的高发点是 SOAP 响应的字符串拼接。如果用malloc拼字符串一定要确保每条路径都free。我建议用固定大小的静态缓冲区虽然浪费一点内存但绝对不会泄漏。5.4 不同 NVR 兼容性差异这是最让人头疼的部分。我整理了一张兼容性对照表NVR 类型特殊要求应对方法A 类要求 GetCapabilities 返回 Events声明空 Events 能力B 类SOAP 响应必须带 wsa:To 头在响应里补上C 类RTSP 必须支持 TCP 传输声明 RTP_RTSP_TCPD 类要求设备时间与 NVR 同步实现 SNTP 同步E 类对 XML 缩进敏感响应不要带多余空白应对兼容性差异的通用思路是把 SOAP 响应做成可配置的。在代码里留一个兼容模式开关针对不同 NVR 切换响应模板。虽然不够优雅但实用。5.5 性能优化让画面更流畅ESP32 的性能有限要跑 ONVIF 加 RTSP 加编码必须做取舍分辨率720p 是甜点1080p 在 ESP32 上编码压力大容易掉帧帧率15fps 足够安防用25fps 对 ESP32 来说偏吃力码率1Mbps 左右太高会撑爆 WiFi 带宽编码优先用硬件编码软件编码会占满 CPU如果发现画面卡顿先降分辨率再降帧率最后降码率。我实测 720p15fps1Mbps 在 ESP32-S3 上跑得比较稳。6. 从能用到好用的几个进阶方向6.1 多码流与 Profile 扩展基础实现只提供一个主码流但很多 NVR 期望看到主码流和子码流两个 Profile。子码流通常是低分辨率低码率用于多画面预览。扩展方法是在 GetProfiles 响应里返回两个 ProfileGetStreamUri 根据传入的 token 返回不同的 RTSP 地址。子码流的实现有两种一是编码器同时输出两路码流ESP32 硬件编码器支持二是用同一路码流但通过 RTSP 地址区分。前者画质好但吃资源后者省资源但两个流内容一样。我一般先用后者快速验证再根据需求升级。6.2 事件与移动侦测ONVIF 的 Events 服务允许设备主动上报事件比如移动侦测。实现方式是 NVR 先调Subscribe建立一个事件订阅设备通过一个 HTTP 长连接或者 SOAP 通知把事件推给 NVR。ESP32 上做移动侦测可以用简单的帧差法比较相邻两帧的像素差异超过阈值就触发事件。这个计算量不大但要注意别在 RTSP 推流任务里做单独开一个低优先级任务处理。6.3 OTA 与配置持久化设备部署之后固件升级和配置保存是刚需。ESP-IDF 自带 OTA 框架ONVIF 这边可以通过SystemReboot和SetSystemFactoryDefault接口配合。配置持久化用 NVS把设备名、序列号、网络配置这些存起来重启后能恢复。注意OTA 升级过程中要暂停 RTSP 推流否则升级完重启NVR 那边会显示流中断。优雅的做法是先通过 ONVIF 通知 NVR 设备即将重启再执行升级。6.4 安全加固ONVIF 支持 WS-UsernameToken 鉴权。基础实现可以先不做鉴权但生产环境必须加。鉴权逻辑是在 SOAP 头里校验UsernameToken包含用户名、密码摘要、随机数和时间戳。摘要算法是Base64(SHA1(nonce timestamp password))。ESP32 上有 mbedTLSSHA1 和 Base64 都有现成实现。鉴权失败要返回标准的 SOAP FaultNVR 收到后会提示用户重新输入密码。7. 我踩过的那些坑和最后的经验做这个项目最大的体会是ONVIF 的难点不在协议本身而在兼容性。标准文档写得很清楚但真实 NVR 各有各的脾气。我的建议是先拿一台主流 NVR 跑通基本流程再逐步适配其他型号。每适配一台就把差异点记下来做成配置项。另一个体会是日志要打够。ONVIF 交互涉及的环节多出问题的时候如果没有详细的日志排查起来像大海捞针。我在每个 SOAP 请求和响应的关键字段上都加了日志虽然串口输出多了点但排查效率提升明显。最后分享一个小技巧用 VLC 作为标准客户端来验证设备端实现。VLC 对 ONVIF 和 RTSP 的支持比较标准如果 VLC 能正常发现和播放说明设备端基本没问题剩下的就是 NVR 的兼容性适配。反过来如果 VLC 都播不了那就别怀疑 NVR 了先把自己的实现查清楚。这个组件后续还可以往 PTZ 控制、音频对讲、多路视频这些方向扩展但那是另一个话题了。把基础的发现、添加、出画面这条链路跑通已经能覆盖大部分安防场景的需求。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询