)
基于 ZLMRTCClient 实现 WebRTC 低延迟视频播放Vue3 实战前言在安防、低空监管、工业监控等场景里页面往往需要秒开、低延迟的实时画面。传统 FLV/HLS 延迟通常在几秒甚至十几秒而WebRTC可以把端到端延迟压到亚秒级。ZLMediaKit简称 ZLM提供了 HTTP 接口做 WebRTC 信令交换官方配套的浏览器 SDK 就是ZLMRTCClient。本文基于 Vue3说明如何用ZLMRTCClient.Endpoint实现一个可复用的 WebRTC 播放器。一、整体架构ZLM WebRTC 播放链路播放端并不直接“拉 RTSP”而是走 WebRTC摄像头 / 推流端 ──► ZLMediaKit转 WebRTC │ │ HTTP 交换 SDPOffer / Answer ▼ 浏览器 ZLMRTCClient.Endpoint │ ▼ video 渲染画面关键点只有两个信令浏览器把本地 SDP Offer POST 到 ZLM 的 WebRTC API换回 Answer。媒体通过 ICE/DTLS/SRTP 建立 PeerConnection把远端MediaStream挂到video。ZLM 的播放地址一般类似http://{host}:{port}/index/api/webrtc?applivestreamtesttypeplay参数含义app应用名如livestream流 ID如testtypeplay播放拉流推流则用push这个 URL 就是 SDK 里的zlmsdpUrl。二、引入 ZLMRTCClient官方 SDK 常以 UMD 形式挂到全局例如放到public/ZLMRTCClient.js在index.html引入scriptsrc/ZLMRTCClient.js/script页面里可直接使用全局变量ZLMRTCClientEndpoint、Events。也可用 npm / ES Module本文按全局脚本方式和多数 ZLM Demo 一致。模板里放一个videovideorefvideoRefstylewidth:100%;height:auto;muted/videomuted很重要浏览器自动播放策略下静音更容易play()成功。三、核心 APIZLMRTCClient.Endpoint播放器本质就是创建一个Endpointconstplayerref(null)constvideoRefref(null)constinitVideo(url){closePlayer()constvideoDomvideoRef.value player.valuenewZLMRTCClient.Endpoint({element:videoDom,// 绑定的 videodebug:false,// 调试日志zlmsdpUrl:url,// ZLM WebRTC 信令地址simulecast:false,// 是否 Simulcast多码率useCamera:false,// 是否采集本地摄像头audioEnable:false,// 是否收/发音频videoEnable:true,// 是否收/发视频recvOnly:true,// 仅接收纯播放必开usedatachannel:false,// 是否使用 DataChannel// resolution: { w: 600, h: 340 }, // 可选期望分辨率})// ... 事件监听见下一节}参数说明纯播放场景配置项推荐值说明elementvideo节点SDK 会把远端流挂到该元素zlmsdpUrlZLM webrtc APIOffer/Answer 交换入口recvOnlytrue只看不推监控预览标准配置useCamerafalse播放端不采本地摄像头audioEnable按需只要画面可关减少权限与策略干扰videoEnabletrue必须开simulecastfalse一般单路流关闭即可注意官方拼写usedatachannelfalse不做自定义消息时关闭debug开发true排障时看 ICE/SDP 日志纯播放记住一句recvOnly: trueuseCamera: false。四、事件监听从“连上”到“能播”Endpoint通过on(Event, handler)订阅生命周期// ICE 候选失败网络/防火墙/NAT 常见player.value.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e){console.log(ICE 协商出错,e)})// 拿到远端流 —— 可以播了player.value.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e){console.log(播放成功,e.streams)videoDom.addEventListener(canplay,(){videoDom.play()})})// SDP Offer/Answer 交换失败流不存在、地址错、服务挂player.value.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e){console.log(offer answer 交换失败,e)})// 本地流推流场景更有用纯播放通常可忽略player.value.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s){console.log(获取到了本地流,s)})// 采集本地流失败useCameratrue 时player.value.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED,(){console.log(获取本地流失败)})// PeerConnection 状态new / connecting / connected / disconnected / failed / closedplayer.value.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE,(state){console.log(当前状态,state)})// DataChannelusedatachanneltrue 时player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN,(event){console.log(datachannel 打开,event)})player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG,(event){console.log(datachannel 消息,event.data)})player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR,(event){console.log(datachannel 错误,event)})player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE,(event){console.log(datachannel 关闭,event)})事件优先级排查顺序WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED→ 先查zlmsdpUrl、流是否在线、跨域/HTTP。WEBRTC_ICE_CANDIDATE_ERROR/connectionState failed→ 查 ICE、端口、NAT、TURN。WEBRTC_ON_REMOTE_STREAMS有了但黑屏 → 查play()、静音策略、srcObject是否挂上。五、Vue3 弹窗预览完整流程设备列表点「视频预览」时打开 Dialog →nextTick等 DOM 就绪 →initVideo(url)。import{ref,nextTick}fromvueconstvisibleref(false)constvideoRefref(null)constplayerref(null)functionopenDialog(row){if(!row)returnvisible.valuetruenextTick((){// 实际项目里用接口返回的 webrtc 地址initVideo(http://192.168.1.120:8001/index/api/webrtc?applivestreamtesttypeplay)})}constclosePlayer(){if(player.value){// 关闭底层 RTCPeerConnection避免泄漏player.value?.pc?.close()player.valuenull}}functionhandleClosed(){closePlayer()// 重置业务状态...}要点必须nextTickDialog 未渲染完时videoRef可能是null。先closePlayer再新建切换流或重复打开避免多个 PeerConnection。弹窗关闭务必释放否则后台还在收流、占带宽。六、资源释放与封装建议最小关闭方式constclosePlayer(){if(player.value){player.value?.pc?.close()player.valuenull}}更稳妥一点可以functionstop(){if(player){// 若 SDK 提供 close/destroy优先用官方方法player.close?.()||player.destroy?.()||player.pc?.close()playernull}if(video.value){video.value.srcObjectnullvideo.value.load()}}多页面复用时建议抽成组件如WebRTCPlayerpropszlmsdpUrl、muted、audioEnable…emitconnected/failed/statechange/closedonMounted建连onUnmounted销毁watch(url)换流重连业务页只关心传地址不必每次抄一遍事件。七、常见问题与踩坑1. 有远端流但画面不播浏览器限制未交互自动播放。处理video muted playsinline autoplayplay()失败时再强制muted true重试2. Offer/Answer 失败核对app/stream是否与推流一致ZLM 是否已有该流可用 ZLM API / 控制台看HTTP/HTTPS 混用、证书、跨域3. ICE failed内网调试常通公网/跨网段要配TURN。看WEBRTC_ON_CONNECTION_STATE_CHANGE是否落到failed/disconnected。4. 拼写注意部分版本配置是simulecast少了一个l事件名是WEBRTC_OFFER_ANWSER_EXCHANGE_FAILEDANSWER写成了ANWSER。以你引入的ZLMRTCClient.js为准不要“纠正”官方拼写。5. HTTPS 与安全上下文生产环境页面尽量 HTTPS部分浏览器对非安全上下文限制 WebRTC/媒体能力。6. 监听泄漏canplay若每次addEventListener不移除重复打开会叠多个回调。可用{ once: true }或先removeEventListener。八、最小可运行示例精简版videoidvideomutedautoplayplaysinlinestylewidth:640px;background:#000/videoscriptsrc/ZLMRTCClient.js/scriptscriptconstvideodocument.getElementById(video)consturlhttp://127.0.0.1:80/index/api/webrtc?applivestreamtesttypeplayconstendpointnewZLMRTCClient.Endpoint({element:video,debug:true,zlmsdpUrl:url,useCamera:false,audioEnable:false,videoEnable:true,recvOnly:true,})endpoint.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(){video.play().catch(console.warn)})endpoint.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e){console.error(信令失败,e)})endpoint.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE,(state){console.log(PC state:,state)})/script先确认 ZLM 上该流在线再用浏览器打开此页。九、总结用 ZLMRTCClient 做 WebRTC 播放闭环其实很短引入 SDK准备videonew Endpoint({ zlmsdpUrl, recvOnly: true, ... })监听WEBRTC_ON_REMOTE_STREAMS再play()监听信令失败 / ICE / connectionState 做排障离开页面或关弹窗时关闭 PeerConnection适合监控预览、设备联调、低延迟大屏。若还要兼容旧浏览器或弱网可再做WebRTC 优先、FLV/HLS 降级的双通道方案。参考ZLMediaKitMDN: RTCPeerConnection.connectionStateZLMRTCClient 官方 Demo随 ZLM www 目录或官方仓库