前两年接了个工厂监控大屏项目甲方点名要海康摄像头在网页上直接播放最好不装任何插件。一开始我觉得简单结果打开摄像头 Web 管理页面浏览器先提示“下载插件”装完又说只支持 IE换到 Chrome 直接白屏。折腾一圈才反应过来Web 无插件播放海康摄像头不是找一个播放器就能解决而是要从摄像头取流、协议转换、前端播放、安全认证整条链路一起考虑。这篇文章就把我在实际项目里走通的一套方案拆开讲清楚如何拿到海康摄像头的 RTSP 地址如何把它转成浏览器能直接播放的 HLS 或 WebRTC前端代码怎么写常见的坑为什么会出现、又怎么排查。不管你是刚接触安防集成的前端还是已经在用海康设备做平台的老手看完都能直接照着操作。1. 为什么“无插件”成了刚需1.1 浏览器把插件这条路堵死了海康摄像头的老 web 播放方案依赖的是一套控件底层是 NPAPI 或者 ActiveX专门给早期 IE 生态用的。Chrome 在 45 版本以后彻底移除了 NPAPI 支持Edge 改用 Chromium 内核后也不再碰 ActiveXFirefox 也是一路收紧。也就是说新操作系统上的现代浏览器里原来那套“下载插件—安装—重启浏览器—再登录”的播放路径已经走不通了。就算你的项目硬要在内网里保留一台 Windows Server 2012 和 IE 11也只能说自己给自己找麻烦。浏览器版本、系统位数、安全级别、插件签名任何一个环节变了插件就失效。甲方第一次用 Chrome 打开页面看到的永远是“控件未安装”这个体验放到任何交付现场都过不了验收。1.2 “无插件”本质是协议换道无插件播放的意思不是浏览器天生支持摄像头的 RTSP 流而是我们主动把 RTSP 转成浏览器标准能力能解码的协议。浏览器能直接处理的直播协议主要有三类HLS、WebRTC、HTTP-FLV再加一个纯 H.264 的 MP4 分片播放。海康摄像头原生输出 RTSP/ONVIF部分型号支持 GB28181修好这条路之前摄像头本身是不认识 HLS 的。所以结论很明确无插件播放方案的核心不是播放器而是一个“协议转换网关”。你不需要在用户浏览器里装海康插件但服务端通常要有一个程序把摄像头流拉进来再按需吐成 HLS 或者 WebRTC。这个前置条件想明白后面所有操作都好说。2. 开工前先摸清海康摄像头的脾气2.1 取流前必须确认的四件事第一摄像头 IP 是否可达。海康摄像头出厂一般走内网 DHCP或者用 SADP 工具手动改 IP。你得先确认媒体服务器和摄像头在同一网段中间如果有交换机还要考虑 PoE 供电和 VLAN 隔离。曾经遇到过一个现场摄像头用 PoE 交换机接得好好但服务器在另一个网段RTSP 端口 554 被防火墙拦了取流就是不通跟代码没关系。第二账号密码和权限。海康摄像头的 web 端有个“用户管理”菜单取流账号需要至少“预览”权限。很多项目交付时密码已经改过但测试时还在用 admin/12345导致一直鉴权失败。这个点很初级但踩的人真不少。第三编码格式。海康近几年设备默认可能是 H.265尤其是新出的 4G 摄像头和高清枪机。H.265 画质好、码率低但浏览器兼容性差Chrome 在 Windows 上默认不支持 HEVCHLS 播不了 H.265。后面我会给解决方案但前期最好统一把编码切成 H.264能少掉一半麻烦。第四主码流还是子码流。海康的主码流一般是 1080P 或 4K码率在高场景下可能到 4Mbps 以上适合回放和需要看清细节的时候用。子码流一般只有 D1/960P 甚至更低码率小适合页面九宫格预览和移动端弱网。Web 端监控大屏建议默认拉子码流用户点“高清”的时候再切主码流。2.2 RTSP 地址别拼错海康摄像头取流时RTSP 地址有统一的规律常见格式是rtsp://用户名:密码摄像头IP:554/Streaming/Channels/101 rtsp://用户名:密码摄像头IP:554/Streaming/Channels/102101 表示第 1 个通道的主码流102 表示第 1 个通道的子码流。如果是多通道录像机第二通道主码流就是 201子码流是 202以此类推。老一批海康设备也可能写成rtsp://用户名:密码摄像头IP:554/Streaming/Channels/1它代表的也是主码流。遇到这种老设备先用 VLC 验证一下是哪种格式再写进配置不要套模板硬来。密码里如果有特殊字符比如、:、#直接拼 RTSP 地址常常会解析错。实践中需要做一次 URL 编码比如写成%40冒号写成%3A。不信你试一次密码里带个#ffprobe 能直接把后面内容当注释处理画面怎么都拉不出来。2.3 用 VLC 和 ffprobe 先验证取流先别急着搭服务器先把原始流验证通了后面排查才有基准线。打开 VLC按CtrlN把 RTSP 地址填进去点播放能出画面就说明摄像头本身没问题。如果你习惯命令行也可以用 ffprobeffprobe -rtsp_transport tcp -i rtsp://admin:yourpassword192.168.1.64:554/Streaming/Channels/102能正常输出版本信息、编码格式和分辨率说明 RTSP 链路通畅。注意加了-rtsp_transport tcp强制走 TCP避免很多跨网段丢包导致花屏断流的情况。海康摄像头默认 RTSP 会优先用 UDPUDP 在跨路由、有防火墙的环境里非常容易丢包TCP 更稳。直到这一步完成摄像头侧的准备才算是到位了。3. 无插件播放的核心架构与选型3.1 为什么必须有一层协议转换把海康摄像头的 RTSP 流直接塞给video标签浏览器是不认的。HTML5 原生支持的视频格式是 WebM/MP4直播场景最接近的是 HLS 和 WebRTC。RTSP 是一套基于 RTP/RTCP 的实时传输协议浏览器既不实现它也不提供原生 socket 去解析。所以方案里必备一个“引流端”服务端主动连摄像头拿到 RTSP 流之后做协议封装再交给浏览器。这个流程在工程上叫“拉流—转协议—分发”。一个小项目可以用一块板子或者 Docker 容器跑一个园区级项目可以用集群跑原理都一样。3.2 几套主流方案怎么选我自己用下来的主流方案大概是下面几种方案适合规模延迟表现推荐场景ffmpeg Nginx/HLS1-5 路3-10 秒临时测试、低成本单点接入MediaMTX1-50 路HLS 2-5 秒WebRTC 可到 1 秒内中小项目快速交付ZLMediaKit WVP50 路以上HLS 2-5 秒WebRTC 1 秒内安防平台、GB28181 国标接入商业云平台视供应商而定1-3 秒不想自己维护服务端如果你的项目只是“几十个摄像头能看直播延迟别太离谱”MediaMTX 是最省事的。它是一个 Go 写的轻量媒体服务支持 RTSP 输入自动输出 HLS、WebRTC、RTMP、HTTP-FLV配置就是一个 YAML 文件Docker 容器一行命令就能跑。如果要做完整的安防平台有组织机构、用户权限、设备管理、录像回放、国标接入这些需求那就别用 MediaMTX 硬撑了。ZLMediaKit 负责流媒体能力WVP 负责业务和国标协议这套组合是现在国内安防项目里很常见的开源选型。3.3 延迟预期先想清楚你要什么样的“实时”提到实时监控甲方都会说“要低延迟”。但你得先问清楚到底是要“点开能看”还是要“对讲基本同步”。HLS 的延迟通常在 3 秒以上因为视频被切成一个个小分片播放器要先缓存几个分片再播好处是兼容性强Safari 原生支持不需要额外 JS。WebRTC 延迟能做到百毫秒到一秒内因为走的是 P2P 或 SRTP 数据通道但它需要信令配合前端代码也复杂一些。如果只是大屏轮巡、回放、巡检画面辅助HLS 完全够用。如果要做云台联动、语音对讲、机器人远程操控那必须上 WebRTC否则你转个云台镜头动完一秒半秒画面才跟上来体验会很难受。4. 实操落地RTSP 转 HLS/WebRTC 并嵌入网页4.1 用 MediaMTX 把海康 RTSP 变成网页能拉的地址假设你已经在服务器上装好 Docker并且摄像头 IP 是 192.168.1.64取流账号是 admin密码是 yourpassword。先创建一个mediamtx.ymlpaths: hik_main: source: rtsp://admin:yourpassword192.168.1.64:554/Streaming/Channels/101 sourceProtocol: tcp hik_sub: source: rtsp://admin:yourpassword192.168.1.64:554/Streaming/Channels/102 sourceProtocol: tcp然后用 Docker 启动docker run -d --name mediamtx \ -p 8554:8554 -p 1935:1935 \ -p 8888:8888 -p 8889:8889 \ -v $(pwd)/mediamtx.yml:/mediamtx.yml \ bluenviron/mediamtxMediaMTX 启动后会主动用配置里的 RTSP 地址去拉摄像头流然后对外提供多种协议输出。hik_main和hik_sub就是路径名你可以理解成房间号。浏览器端最常用的地址是这个http://服务器IP:8888/hik_sub/index.m3u8如果你的版本返回的不是这个地址可以打开http://服务器IP:8888/或者看容器日志官方会在日志里打印出每个 path 实际可访问的 HLS 和 WebRTC 地址。不同小版本之间略有差异用日志输出最保险。注意容器要和摄像头网络打通。启动后如果日志报connection refused先确认容器所在宿主机能不能直接 ping 通摄像头必要时用--network host模式跑。4.2 前端页面播放代码页面上用 hls.js 接流代码非常简洁。先引入库再实例化 Hls 对象把index.m3u8喂给video标签。!DOCTYPE html html langzh-CN head meta charsetUTF-8 / title海康摄像头无插件播放/title script srchttps://cdn.jsdelivr.net/npm/hls.js1/script /head body video idcamera1 controls autoplay muted/video script const video document.getElementById(camera1); const streamUrl http://192.168.1.10:8888/hik_sub/index.m3u8; if (Hls.isSupported()) { const hls new Hls({ liveDurationInfinity: true, maxLiveSyncPlaybackRate: 1.5 }); hls.loadSource(streamUrl); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () { video.play(); }); } else if (video.canPlayType(application/vnd.apple.mpegurl)) { video.src streamUrl; video.addEventListener(loadedmetadata, () { video.play(); }); } /script /body /html这段代码兼容两类情况主流浏览器用 hls.jsSafari 不识别 hls.js 时就自动走原生 HLS。自动播放带上muted属性是因为多数浏览器禁止无声播放去掉 muted 会被浏览器直接拦截。如果你用的是 ZLMediaKit地址样式可能变成http://服务器IP:80/live/hik_sub/hls.m3u8 http://服务器IP:80/live/hik_sub/hls.m3u8?tokenxxxx不同版本细节不同但前端播放逻辑不变核心就是拿到一个text/plain的 m3u8 地址交给 Hls 引擎。4.3 低延迟 WebRTC 怎么接如果要做低延迟就用 MediaMTX 输出的 WebRTC 地址。MediaMTX 支持 WHIP/WHEP 这类标准协议官方提供一个index页面可以直接测试你先在浏览器打开http://服务器IP:8889/验证路径能出画面再接入自己的业务。前端接 WebRTC 的代码和 HLS 完全不一样核心是RTCPeerConnection。大致流程是从后端或信令接口拿到 WebRTC 播放地址。创建一个RTCPeerConnection。通过addTransceiver声明只接收视频。拿到远端 SDP 后setRemoteDescription。在ontrack事件里把流挂到video的srcObject上。这套代码比 HLS 长不少而且不同服务器的信令格式不完全一样。我的建议是中小项目先把 HLS 跑通再去折腾 WebRTC如果方案已经选了 ZLMediaKit就优先考虑直接用它的 WebRTC 接口前端找到对应的 api 格式接入比自己手搓信令可靠得多。注意H.265 WebRTC 在浏览器端兼容性依然是个坑。生产环境尽量让摄像头输出 H.264否则你很可能要在 WebRTC 之前再加一层转码成本瞬间就起来了。4.4 4G 摄像头或录像机没有固定 IP 怎么办不是所有海康摄像头都允许服务器直接连过去。比如 4G 摄像头、部署在分公司内网环境没有公网 IP摄像头在 NAT 后面服务器主动拉 RTSP 常常拉不到。这种情况下要换思路不用拉流用推流。海康摄像头和录像机基本都支持 GB28181 国标协议让它主动往一个固定的 SIP 服务器注册注册成功后再把视频推给媒体的流媒体服务。开源生态里比较成熟的组合是 WVP ZLMediaKit。WVP 负责 GB28181 的设备注册、目录、历史录像ZLMediaKit 负责把 GB28181 的 RTP 流转成 HLS、WebRTC、RTMP前端拿到的仍然还是那些普通播放地址。配 GB28181 的时候海康摄像头 web 端一般会在“网络—高级设置—平台接入”里找到“GB28181”选项。你需要填上 SIP 服务器 ID、SIP 服务器地址、SIP 端口、设备编码、用户名、密码。平台那边填好后设备状态显示在线就能看到目录和实时流了。对后台用户来说这和不带 IP 的 4G 摄像头没区别前端还是通过 Web 页面播放只是底层从“服务器拉摄像头”变成了“摄像头推到服务器”。5. 常见问题与排查记录5.1 明明 RTSP 能播网页就是黑屏遇到黑屏我一般按三步来查。先看服务端日志。MediaMTX 或 ZLMediaKit 在拉流失败的时候会直接报错比如鉴权失败、TCP 连接超时、编码不支持。日志是最老实的信息源。再看生产环境端口。Docker 映射的 8888 端口有没有暴露给前端云服务器的安全组、本地防火墙是不是把端口拦了。我处理过一个现场摄像头和内网通但前端页面访问 8888 端口超时最后发现是云主机安全策略只放行了 80/443把 8888 漏了。最后用 ffprobe 看输出流编码。如果 HLS 服务一直在但 Chrome 播放黑屏且没有声音大概率是 H.265 视频流。Chrome 对 H.264 支持很成熟对 H.265 则要看硬件解码能力和操作系统。你在 Windows 上用 Chrome 拉 H.265 的 HLS基本就是黑屏或者只有声音没画面。碰到 H.265有两种做法一是到摄像头 web 端把视频编码改成 H.264二是用 ffmpeg 转码后再推到媒体服务器。ffmpeg -rtsp_transport tcp \ -i rtsp://admin:yourpassword192.168.1.64:554/Streaming/Channels/102 \ -c:v libx264 -preset veryfast -tune zerolatency \ -c:a aac -f flv rtmp://127.0.0.1:1935/hik_sub转码性能消耗不小几路还能扛几十路就得考虑 GPU 或者专门的转码节点了。5.2 延迟高怎么办先优化这些参数如果 HLS 播放延迟在 5 秒以上先看分片参数。把分片时长调小延迟会下降但服务器压力会上升。MediaMTX 里类似这些配置可以调整hlsSegmentDuration: 1s hlsPartDuration: 200ms如果还是不满意上 WebRTC。WebRTC 不走分片建立连接后直接基于 UDP 或 ICE 传输延迟通常能做到 1 秒内。对讲、云台控制这种交互型业务普通 HLS 是很难做到顺手体验的。还有一种情况是码流规格太高。摄像头主码流 4MbpsWeb 端只是 16 宫格中的一小块肯定卡。这时候页面默认应该拉子码流点“高清”再切主码流切换本质上就是把播放器的 src 换成一个新地址。5.3 安全不该只靠不公开 IP网页接入摄像头之后RTSP 地址就变成了工程资产如果直接写在页面里等于把摄像头账号密码展示给了所有能打开页面的人。很多暴露出来的风险不是服务器被攻破而是某个页面里有个rtsp://admin:passwordip的源码。我的习惯是不要让浏览器直接访问摄像头的 80/554 端口统一走媒体服务器。媒体服务器对外只暴露 HLS 或 WebRTC 端口再在前面套一层 Nginx做 HTTPS、域名、Basic Auth 或 OAuth 鉴权。后端在给前端签发 m3u8 地址前还可以做带有效期的 token。比如用 JWT 或 Redis 缓存一个短期 ticket前端拿着 ticket 去换取播放地址过期就失效。这样即使 somebody 抓包拿到了一个片段也无法长期复用。页面上的Cookie和会话凭证按最严格 HttpOnly Secure 配置能不开的接口千万不要开。5.4 老设备和新录像机兼容性怎么处理有些现场摄像头用了七八年海康新录像机可能识别不了老设备的私有协议或者老摄像头不能被新录像机一键添加。这时候用 SADP 先看设备 IP再确认有没有 ONVIF 开关手动添加时协议选“ONVIF”或者“HIKVISION”通常能解决大半问题。如果真碰上老摄像头连 ONVIF 都不完整那就把它当一个裸 RTSP 设备接入媒体服务器只要 554 端口能取流页面侧只管转发反而绕开了录像机的兼容性限制。我遇到过最极端的现场摄像头是十年前的型号VLC 能拉流NVR 死活添加不了最后就是用 MediaMTX 直接转发照样上了大屏。踩了这些坑之后我自己的习惯是生产环境一律默认给摄像头做一个独立网段媒体服务器只暴露必要端口摄像头编码能设 H.264 就设 H.264页面播放统一先走子码流 HLS交互要求高了再切 WebRTC。这套组合用了好几个月基本没有再收到过“看不了监控”的工单。