ARTICLE DETAIL

资讯详情

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

网页端接入海康摄像头:RTSP转HLS与WebRTC实战指南

网页端接入海康摄像头:RTSP转HLS与WebRTC实战指南 搞网页端接入海康摄像头这件事这两年找我咨询的人不少。很多人拿着新装好的摄像头第一反应就是想把画面放到网页后台里实时预览结果卡在第一步浏览器打不开预览页要么提示安装插件要么黑屏转圈。老实说网页端接海康摄像头本身不复杂但它把几件事串在了一起——RTSP取流地址、编解码格式、浏览器播放限制、流媒体转发中间任何一个环节不匹配都会翻车。这篇文章我从实际做过的项目出发把网页端接入海康摄像头的完整链路拆开讲一遍。从取流地址怎么写、方案怎么选到FFmpeg转HLS、WebRTC低延迟接入再到夜视灵敏度调整这类真实场景里的问题都会覆盖到。适合刚接手监控平台开发的读者也适合给那些想自己把摄像头画面挂到网页上的朋友做个参考。1. 接入前先搞明白海康摄像头的画面是怎么“给”出来的1.1 取流地址RTSP是绕不开的核心海康摄像头默认提供RTSP协议取流这也是所有方案的地基。RTSP的全称是Real Time Streaming Protocol你可以把它理解成摄像头对外喊话的“视频出口”。网页端虽然不能直接吃RTSP但所有的接入方案最终都要先从这个地址把画面取出来。海康摄像头的RTSP地址有固定格式rtsp://用户名:密码摄像头IP:554/Streaming/Channels/101拆开来看用户名:密码建议单独建一个只读用户别直接用admin。我后面会专门说这个。摄像头IP设备在局域网里的地址默认一般是192.168.1.64这类。554RTSP默认端口海康没改过的话就是它。Streaming/Channels/101这个路径有讲究前一位数字是通道号后一位数字是码流类型。101表示第1个通道的主码流102表示第1个通道的子码流103是三码流。如果是硬盘录像机接多路摄像头第一路的取流地址就是601主码流、602子码流第二路是701、702以此类推。规律是通道号每加一路百位数字加1。主码流分辨率高一般用来录像或大屏显示子码流分辨率低适合网页端预览。我自己做网页接入时默认优先用子码流比如102因为网页端对实时性要求高对画质要求没那么苛刻子码流能明显降低带宽和转码压力。除了RTSP海康还支持HTTP方式取流。比较常用的是快照地址http://摄像头IP/ISAPI/streaming/channels/102/picture这个返回一张JPEG图片适合做缩略图或者定时抓拍。如果想通过HTTP直接拉视频流可以用ISAPI/streaming/channels/102/httpPreview这种接口但实际项目中用得不多因为兼容性和延迟都不如前面说的方案。我一般只把快照地址用来做封面视频流还是走RTSP。1.2 为什么浏览器不能直接播放RTSP很多第一次做这个项目的朋友都会问同一个问题VLC能直接打开RTSP为什么浏览器不行核心原因是协议和浏览器定位的冲突。RTSP本身是一种“控制协议”它负责协商播放、暂停、停止这些动作而实际的视频数据走的是RTP包通常还要占一组UDP端口。浏览器为了安全不允许网页任意申请UDP端口更不会内置一个完整的RTSP客户端。所以即便摄像头地址是通的浏览器也没办法直接播。早年有一些偏方比如装Web控件、ActiveX插件能让浏览器调用本机的播放器内核。海康官方以前也提供过这类网页插件在IE时代确实能用。后来Chrome和Edge都彻底停掉了NPAPI/ActiveX支持这条路基本断了。现在你打开摄像头网页后台如果还看到“请使用以下最新版本的浏览器打开”多半就是设备页面还在依赖老插件。所以网页端接海康摄像头的正确思路不是让浏览器直接连摄像头而是中间加一层“翻译官”先把RTSP拉过来转成浏览器能理解的格式再通过HTTP分发给网页播放。最常见的两种格式就是HLS和WebRTC这也是后面要重点展开的内容。2. 网页端播放方案怎么选插件、HLS还是WebRTC2.1 插件方案老路子能不用就别用先说已经过时的老方案。早些年海康的设备自带网页控件需要在浏览器里装一个ActiveX插件然后才能看到画面。那个年代还流行IE专用后台体验相当痛苦。现在的浏览器基本都不支持这类插件了除非你用的是Edge浏览器的IE模式在兼容性设置里把设备IP加进去才能勉强打开老后台。这个方法我在维护老项目时用过纯粹是为了进设备配置页改参数比如调画质、开夜视。如果是新项目、要在自己的网页里嵌画面千万别再想插件这条路维护成本太高用户访问还得装控件光这一步就能劝退一大半人。2.2 HLS转码方案简单稳定优先考虑HLS是Apple推的流媒体协议把视频切成一个个小切片文件通常是.ts再通过一个索引文件.m3u8来播放。浏览器端的video标签原生支持HLS或者在Chrome上用hls.js也能播放兼容性很好。用HLS接海康摄像头的经典链路是摄像头RTSP → FFmpeg拉流转封装 → 生成m3u8和ts切片 → Nginx托管 → 网页播放这套方案最大的优点是稳定、简单。FFmpeg是成熟的工具Nginx托管静态文件也很少出问题整个链路基本没有特别容易翻车的环节。缺点就是延迟会高一些切片本身会引入几秒的缓冲如果切片切到1秒、播放列表只留3到4个切片端到端延迟通常在3到6秒左右。这个延迟对大部分监控场景是能接受的。你想想看自家门口有没有人经过晚两三秒看到完全没影响。但如果你要做的项目是远程操作设备、语音对讲、无人机图传这种强交互场景4秒延迟就太难受了那得上WebRTC。2.3 WebRTC低延迟方案体验最好也最折腾WebRTC是浏览器原生的实时通信能力延迟能做到几百毫秒。最近几年很多商业监控平台都转向WebRTC体验确实好打开画面几乎是秒出。但它不像HLS那样一条FFmpeg命令就能搞定。你需要一个流媒体网关把RTSP流转成WebRTC可发布的格式还要处理STUN/TURN这类网络协商问题。局域网内会简单很多跨公网就要考虑NAT穿越复杂度立刻上来。工具方面常用的有MediaMTX、ZLMediaKit、SRS这几个。MediaMTX配置最简单适合个人项目和小规模部署ZLMediaKit功能全、性能好适合商用和并发量大的场景SRS在直播领域更知名但做WebRTC网关需要自己多配一些东西。2.4 我的选型建议我给一个能直接抄的结论只是自己看看、或者给客户做展示后台对延迟不敏感选HLS一天就能跑通。要做商业监控平台画面要秒开交互性强选WebRTC配ZLMediaKit。临时调试、验证摄像头是否正常直接VLC拉流最快。如果用户要的是“点开网页就有画面、最好还不用装插件”无脑HLS是稳的。另外现在也有人用FLV格式做低延迟播放比如JJEncode、jessibuca这类播放器走的也是HTTP-FLV链路。它在PC端的体验和WebRTC差不多实现成本比WebRTC低一些。不过FLV在移动端Safari支持不好如果你的用户主要是手机浏览器我会更推荐HLS或WebRTC。3. 实操FFmpegHLS让网页快速看到海康画面这一节用最直接的方式把一个海康摄像头的画面完整接到网页上。我默认你有一台服务器或者本地电脑能跑FFmpeg和Nginx摄像头和它在同一个内网里或者能通过IP直接访问到。3.1 设备端准备激活、建用户、开RTSP新买的海康摄像头第一次上电需要用海康官方的SADP工具搜索设备IP然后设置激活密码。这个工具在官网能下载到它会扫描网段里的所有海康设备显示IP和序列号。我建议在激活之后做两件事把摄像头IP固定住避免DHCP分配的IP变了导致取流地址失效。在“用户管理”里新建一个专用账号权限只给“远程预览”和“回放”密码用复杂一点的别和admin共用。后期网页端取流就用这个专用账号万一密码泄露了也不会把管理员权限暴露出去。然后到“网络设置→高级配置→集成协议”里确认RTSP开关是打开的。海康设备默认RTSP策略是开启的但有些定制固件会关掉需要手动打开。3.2 用FFprobe验证取流地址在服务器上装FFmpeg之后第一步不是直接转流而是先用ffprobe确认能拿到流。Ubuntu/Debian下安装sudo apt update sudo apt install ffmpeg然后探测ffprobe -rtsp_transport tcp -i rtsp://previewuser:yourpassword192.168.1.64:554/Streaming/Channels/102 -show_streams -show_format这里我故意加了-rtsp_transport tcp让FFmpeg用TCP方式拉RTSP。为什么默认的UDP模式在跨网段或者网络不稳时经常丢包、花屏TCP传输稳定得多代价是稍微高一丢丢延迟但监控场景完全可接受。看到Stream #0:0: Video: h264或者hevc类似信息说明取流成功。如果是hevc也就是H.265后面转HLS时特别要注意Chrome不支持原生H.265解码必须转成H.264不能直接copy。3.3 用FFmpeg把RTSP转成HLS确认取流没问题后就可以启动一个常驻的FFmpeg进程把摄像头流持续推进成HLS切片。我常用的一条命令ffmpeg -rtsp_transport tcp -i rtsp://previewuser:yourpassword192.168.1.64:554/Streaming/Channels/102 \ -c:v libx264 -preset veryfast -tune zerolatency -g 25 -sc_threshold 0 \ -b:v 800k -maxrate 800k -bufsize 1600k \ -c:a aac -b:a 64k -ar 44100 -ac 1 \ -f hls -hls_time 1 -hls_list_size 4 \ -hls_flags delete_segments \ -hls_segment_filename /var/www/html/hls/cam_%04d.ts /var/www/html/hls/live.m3u8逐个解释关键参数-c:v libx264把视频转成H.264。摄像头的子码流如果本来就是H.264理论上可以省掉转码用-c:v copy但我实测下来copy模式在首屏出画速度上不如主动转码稳定原因在于设备的GOP间隔往往和切片时间不匹配。所以这里索性直接转码。-preset veryfast -tune zerolatency转码速度和延迟优先。veryfast能降低CPU压力zerolatency尽量减少编码缓存延迟。-g 25强制每25帧一个关键帧。一般摄像头是25fps那每个关键帧间隔就是1秒。这个参数要和-hls_time 1配合才能保证每个切片都从关键帧开始否则播放器切到下一个切片时会等关键帧黑屏时间会变长。-b:v 800k子码流默认码率差不多是这个数。如果你的子码流本身是720p甚至1080p可以适当调到1M~1.5M画面会更清楚。-hls_time 1每个切片时长1秒延迟最低。如果CPU压力大或者想看稳定点可以改成2延迟会多1秒但切片的文件数会少一半。-hls_list_size 4播放列表里只保留最近4个切片。配合delete_segments旧的切片会被自动删除磁盘不会无限占用。跑起来后在/var/www/html/hls/目录下会不断生成live.m3u8和若干个以秒为单位的.ts切片文件。看到文件在滚动生成说明转流已经成功。3.4 Nginx托管HLS文件接下来要让网页能访问这些切片我用Nginx做HTTP文件托管。这一步不是必须的但你总得有个Web服务器把m3u8和ts文件发出去。把Nginx的站点配置里加上一段location /hls/ { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } alias /var/www/html/hls/; add_header Cache-Control no-cache; expires -1; }关键点在于types里必须声明.m3u8和.ts的MIME类型否则某些播放器会当成普通文本文件直接下载而不是播放。Cache-Control no-cache是为了防止播放器缓存旧切片导致画面一直停在几分钟前。重启Nginx后在浏览器访问http://你的IP/hls/live.m3u8如果浏览器直接能唤起播放器播起来说明后链路已经通了。3.5 前端用hls.js播放服务端就绪后前端代码其实非常短。我直接贴一个最小可用的示例!DOCTYPE html html langzh-CN head meta charsetUTF-8 script srchttps://cdn.jsdelivr.net/npm/hls.js1/script /head body video idvideo controls muted autoplay width800/video script const video document.getElementById(video); const source http://你的服务器IP/hls/live.m3u8; if (Hls.isSupported()) { const hls new Hls({ liveSyncDurationCount: 3, maxLiveSyncPlaybackRate: 1.5 }); hls.loadSource(source); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, function() { video.play(); }); } else if (video.canPlayType(application/vnd.apple.mpegurl)) { // Safari 原生支持 HLS video.src source; video.play(); } /script /body /html这段代码做了兼容处理Chrome、Edge、Firefox这类浏览器走hls.jsSafari走原生HLS。liveSyncDurationCount: 3控制播放器尽量追到直播边缘减少延迟maxLiveSyncPlaybackRate允许播放器用1.5倍速追赶时间轴这是直播场景的常见优化。我自己实测下来这套方案从摄像头画面到网页出画延迟大概在3到5秒。如果你把切片改成0.5秒一个可以压到2秒左右但Nginx的请求量会翻倍要注意服务器压力。4. 低延迟实战WebRTC接入方案如果HLS的3秒延迟在业务上过不了关比如要做门禁联动、实时对讲那就得升级到WebRTC。这一节讲能落地的WebRTC接入方式。4.1 选MediaMTX还是ZLMediaKitWebRTC接入的第一步是选网关。我个人经验是设备数量不超过10路、看内部小平台用MediaMTX。它就是一个简单的可执行文件加一个配置文件对RTSP转WebRTC做了很好的封装。要做几十路以上的并发、要集群、要鉴权、要按需拉流直接用ZLMediaKit。它的社区活跃度、API完整度都比MediaMTX好。代价是配置项多学习曲线陡一些。这里我用MediaMTX做演示因为它最能让你在半小时内看到WebRTC出来的画面。4.2 MediaMTX最小配置跑起来下载MediaMTX它以前的项目名是rtsp-simple-server解压后目录里有一个mediamtx.yml配置文件。里面核心配置是paths段用来声明每个流来源。我在配置里加了一个cam1路径paths: cam1: source: rtsp://previewuser:yourpassword192.168.1.64:554/Streaming/Channels/102配置好后运行MediaMTX它会自动拉取这个摄像头RTSP流并对外提供WebRTC发布能力。默认WebRTC端口是8889你可以直接访问http://服务器IP:8889/页面里有播放器示例。MediaMTX默认会同时启用多个输出协议包括HLS、WebRTC、RTSP转发。其中一个好处是它可以做到“按需拉流”当没有播放器在观看时它不会一直占着摄像头通道。对多路摄像头接入来说这个特性很关键能省很多带宽和资源。4.3 前端播放与浏览器兼容性MediaMTX自带测试页面用的是官方WebRTC播放器可以在自己的页面里用jessibuca这套播放器它支持WebRTC、H.264、H.265。一个最小示例script srchttps://jessibuca.com/pro/jessibuca.js/script div idplayer/div script const player new Jessibuca({ container: document.getElementById(player), videoBuffer: 0.2, isResize: false, useWCS: false }); player.play(webrtc://你的服务器IP:8889/cam1); /script需要提醒的是Chrome对WebRTC里H.265的解码支持并不好很多情况下需要靠jessibuca这类播放器的wasm软解CPU占用会偏高。如果摄像头是H.264建议硬件解码走通性能和画质都会好很多。这也是为什么很多商业方案宁可把摄像头设成H.264也不去折腾H.265。4.4 WebRTC方案的适用边界WebRTC不是万能的。跨公网、跨NAT时浏览器和摄像头之间可能建立不了直接连接需要部署TURN服务做中继转发这会增加服务器带宽成本。另外WebRTC网关本身对CPU、网络要求不低单机支撑的并发数远低于HLS那种纯静态文件分发模式。我实际项目的经验是如果延迟容忍度在2秒以上优先HLS只有延迟必须低于500毫秒、且网络条件可控时才考虑WebRTC。两者也可以混合部署默认用HLS遇到需要实时操作的功能再切WebRTC通道。5. 项目里的坑与排查实录5.1 “请使用最新版本的浏览器打开”到底在提示什么这个提示我几乎每年都能碰到。它并不是说你浏览器版本真的老而是摄像头后台页面还在尝试加载老式Web插件而浏览器禁用或不支持这个插件页面就弹了这句提示。处理方式分两种情况你只是想进设备配置页改参数用Edge浏览器的IE模式页面兼容性设置把摄像头IP加进去就能正常打开。Chrome就不要指望了它连IE模式都没有。你要在自己开发的网页里嵌摄像头画面那就绕过设备自带后台按前面几节的思路用RTSPHLS或者WebRTC自己拉流播放跟设备的页面提示无关。帮客户排查时还会遇到另一种情况同一个摄像头在内网电脑上能打开后台换一台电脑就打不开。这种一般是漏装了设备插件组件或者浏览器安全策略拦了ActiveX。不用纠结走RTSP拉流方案直接绕开。5.2 延迟高、首屏黑、花屏卡顿延迟高的常见原因有三个切片时间太长、播放列表太长、播放器没有追直播边缘。HLS方案里把-hls_time降到1、-hls_list_size控制在3到4前端再配上liveSyncDurationCount优化基本能压到3秒左右。首屏黑屏大概率是切片不从关键帧开始。检查FFmpeg命令行里的-g参数确保和帧率匹配也就是每1秒不少于1个关键帧。如果摄像头本身的GOP设置是50帧而切片是1秒一个播放器切到下一个ts切片时迟迟等不到关键帧就会一直黑着。花屏卡顿首先要检查是不是UDP拉流导致的丢包。把FFmpeg里的-rtsp_transport改成tcp能解决大部分花屏问题。紧接着查主码流和子码流的码率网页端播放如果强行拉4K主码流再经过转码性能弱一点的服务器直接CPU爆表画面自然卡。我的习惯是网页端一律走子码流102只有当用户主动点“高清”时才切主码流。5.3 4G摄像头晚上全彩模式灵敏度低怎么调这个问题的确头疼。4G摄像头如果晚上开启全彩模式经常出现移动侦测灵敏度下降人走到镜头前了都不报警或者反应慢半拍。我在项目里排查过几回原因集中在几个点上。第一全彩模式依赖白光补光夜间环境光暗运动目标与背景对比度低算法置信度下降。这时候去“事件管理→智能侦测→移动侦测”里把“灵敏度”拉到80以上有时能改善。第二侦测区域过大反而会让算法变“迟钝”。画面上如果有大面积树叶、车灯、光影变化在动算法会为了降低误报而调低敏感度。把侦测区域缩小到门口、过道这类核心位置只画一小块区域实测灵敏度明显提升。第三4G摄像头为了省流量很多型号默认会在夜间降低帧率帧率一下降移动侦测的连续性就会变差。在“编码参数→通道”里把夜间帧率从12fps提到20fps以上网络带宽允许的话再把子码流码率提上去灵敏度也会改善。要注意的是帧率提高后4G流量消耗会变大要权衡。第四如果设备支持“人形侦测”或“车辆侦测”这种智能分类建议开启并单独调高灵敏度。分类侦测比普通像素级侦测更智能弱光下的表现反而更好。有些老设备固件里的分类侦测算法比较保守升级固件到最新版本后会有改进。5.4 取流地址带特殊字符导致失败RTSP地址里的密码如果包含、:、/、?、#这些特殊字符直接拼接进URL会解析失败因为地址解析器会把它们当成协议分隔符。解决办法是对密码部分做URL编码。比如密码是pass123取流地址里要写成pass%40123。Python里可以用urllib.parse.quote处理前端就用encodeURIComponent。我在项目里遇到过密码里带中文和并存的情况不编码的话FFmpeg直接报401或者404排查了好久才意识到是特殊字符的锅。5.5 多路并发接入如何控制成本几十路摄像头同时转码对服务器是巨大的资源消耗。我踩过的最大的坑是每一路都用独立的FFmpeg进程去做H.264转码还没到20路一台8核16G的服务器CPU就满了。后来总结出几条经验网页端默认全部走子码流分辨率控制在720p以内码率压到1Mbps以内。主码流只保留给录像、回放或者用户主动点高清的时候用。使用按需拉流机制没有人观看的摄像头通道不应该一直拉流转码。HLS方案里可以写一个进程管理器检测到播放列表长时间没有请求就关掉对应的FFmpeg进程MediaMTX本身支持runOnDemand按需启动。转码参数不要追求高画质veryfast预设加tune zerolatency是最优解。别用medium或slow画质提升有限CPU开销翻倍。多路并发达到一定规模后可以考虑在服务器上做GPU硬编或者用ZLMediaKit这类性能更好的流媒体服务并配合集群部署。再提醒一句取流和播放链路都要加访问控制。不要让用户直接拿到RTSP地址去播放应该由后端统一拉流后通过HTTP接口把HLS或者WebRTC的地址分发给前端地址里带上临时有效的token。摄像头这个环节也要定期改取流账号的密码不要用默认密码。这个行业的惯例是倒卖摄像头地址和破解弱口令的黑色产业链一直存在作为开发者能做的就是别把裸露的取流地址交出去。最后再分享一个我自己的习惯不管用HLS还是WebRTC方案我都会在本地把整条链路在命令行里先跑通再写前端。先拿ffprobe确认取流成功再启动FFmpeg看切片文件生成最后才打开浏览器页面。这样每一层的问题都能被单独暴露而不是堆在一起最后在浏览器上什么都看不到还得一层一层往回查。这套排查顺序帮我省了太多时间也推荐你试试。
返回列表