Vue3 + ZLMRTCClient 实现 WebRTC 低延迟播放(ZLMediaKit)
2026/7/23 14:13:44 网站建设 项目流程

基于 ZLMRTCClient 实现 WebRTC 低延迟视频播放(Vue3 实战)

前言

在安防、低空监管、工业监控等场景里,页面往往需要秒开、低延迟的实时画面。传统 FLV/HLS 延迟通常在几秒甚至十几秒,而WebRTC可以把端到端延迟压到亚秒级。

ZLMediaKit(简称 ZLM)提供了 HTTP 接口做 WebRTC 信令交换,官方配套的浏览器 SDK 就是ZLMRTCClient。本文基于 Vue3,说明如何用ZLMRTCClient.Endpoint实现一个可复用的 WebRTC 播放器。


一、整体架构:ZLM + WebRTC 播放链路

播放端并不直接“拉 RTSP”,而是走 WebRTC:

摄像头 / 推流端 ──► ZLMediaKit(转 WebRTC) │ │ HTTP 交换 SDP(Offer / Answer) ▼ 浏览器 ZLMRTCClient.Endpoint │ ▼ <video> 渲染画面

关键点只有两个:

  1. 信令:浏览器把本地 SDP Offer POST 到 ZLM 的 WebRTC API,换回 Answer。
  2. 媒体:通过 ICE/DTLS/SRTP 建立 PeerConnection,把远端MediaStream挂到<video>

ZLM 的播放地址一般类似:

http://{host}:{port}/index/api/webrtc?app=live&stream=test&type=play
参数含义
app应用名(如live
stream流 ID(如test
type=play播放(拉流);推流则用push

这个 URL 就是 SDK 里的zlmsdpUrl


二、引入 ZLMRTCClient

官方 SDK 常以 UMD 形式挂到全局,例如放到public/ZLMRTCClient.js,在index.html引入:

<scriptsrc="/ZLMRTCClient.js"></script>

页面里可直接使用全局变量ZLMRTCClientEndpointEvents)。

也可用 npm / ES Module,本文按全局脚本方式,和多数 ZLM Demo 一致。

模板里放一个<video>

<videoref="videoRef"style="width:100%;height:auto;"muted></video>

muted很重要:浏览器自动播放策略下,静音更容易play()成功


三、核心 API:ZLMRTCClient.Endpoint

播放器本质就是创建一个Endpoint

constplayer=ref(null)constvideoRef=ref(null)constinitVideo=(url)=>{closePlayer()constvideoDom=videoRef.value player.value=newZLMRTCClient.Endpoint({element:videoDom,// 绑定的 <video>debug:false,// 调试日志zlmsdpUrl:url,// ZLM WebRTC 信令地址simulecast:false,// 是否 Simulcast(多码率)useCamera:false,// 是否采集本地摄像头audioEnable:false,// 是否收/发音频videoEnable:true,// 是否收/发视频recvOnly:true,// 仅接收(纯播放必开)usedatachannel:false,// 是否使用 DataChannel// resolution: { w: 600, h: 340 }, // 可选:期望分辨率})// ... 事件监听见下一节}

参数说明(纯播放场景)

配置项推荐值说明
element<video>节点SDK 会把远端流挂到该元素
zlmsdpUrlZLM webrtc APIOffer/Answer 交换入口
recvOnlytrue只看不推,监控预览标准配置
useCamerafalse播放端不采本地摄像头
audioEnable按需只要画面可关,减少权限与策略干扰
videoEnabletrue必须开
simulecastfalse一般单路流关闭即可(注意官方拼写)
usedatachannelfalse不做自定义消息时关闭
debug开发true排障时看 ICE/SDP 日志

纯播放记住一句:recvOnly: true+useCamera: 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)})// 采集本地流失败(useCamera=true 时)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)})// DataChannel(usedatachannel=true 时)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)})

事件优先级(排查顺序)

  1. WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED→ 先查zlmsdpUrl、流是否在线、跨域/HTTP。
  2. WEBRTC_ICE_CANDIDATE_ERROR/connectionState === 'failed'→ 查 ICE、端口、NAT、TURN。
  3. WEBRTC_ON_REMOTE_STREAMS有了但黑屏 → 查play()、静音策略、srcObject是否挂上。

五、Vue3 弹窗预览完整流程

设备列表点「视频预览」时:打开 Dialog →nextTick等 DOM 就绪 →initVideo(url)

import{ref,nextTick}from'vue'constvisible=ref(false)constvideoRef=ref(null)constplayer=ref(null)functionopenDialog(row){if(!row)returnvisible.value=truenextTick(()=>{// 实际项目里用接口返回的 webrtc 地址initVideo('http://192.168.1.120:8001/index/api/webrtc?app=live&stream=test&type=play')})}constclosePlayer=()=>{if(player.value){// 关闭底层 RTCPeerConnection,避免泄漏player.value?.pc?.close()player.value=null}}functionhandleClosed(){closePlayer()// 重置业务状态...}

要点:

  • 必须nextTick:Dialog 未渲染完时videoRef可能是null
  • closePlayer再新建:切换流或重复打开,避免多个 PeerConnection。
  • 弹窗关闭务必释放:否则后台还在收流、占带宽。

六、资源释放与封装建议

最小关闭方式:

constclosePlayer=()=>{if(player.value){player.value?.pc?.close()player.value=null}}

更稳妥一点可以:

functionstop(){if(player){// 若 SDK 提供 close/destroy,优先用官方方法player.close?.()||player.destroy?.()||player.pc?.close()player=null}if(video.value){video.value.srcObject=nullvideo.value.load()}}

多页面复用时,建议抽成组件(如WebRTCPlayer):

  • props:zlmsdpUrlmutedaudioEnable
  • emit:connected/failed/statechange/closed
  • onMounted建连,onUnmounted销毁,watch(url)换流重连

业务页只关心传地址,不必每次抄一遍事件。


七、常见问题与踩坑

1. 有远端流但画面不播

浏览器限制未交互自动播放。处理:

  • <video muted playsinline autoplay>
  • play()失败时再强制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


八、最小可运行示例(精简版)

<videoid="video"mutedautoplayplaysinlinestyle="width:640px;background:#000"></video><scriptsrc="/ZLMRTCClient.js"></script><script>constvideo=document.getElementById('video')consturl='http://127.0.0.1:80/index/api/webrtc?app=live&stream=test&type=play'constendpoint=newZLMRTCClient.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 播放,闭环其实很短:

  1. 引入 SDK,准备<video>
  2. new Endpoint({ zlmsdpUrl, recvOnly: true, ... })
  3. 监听WEBRTC_ON_REMOTE_STREAMSplay()
  4. 监听信令失败 / ICE / connectionState 做排障
  5. 离开页面或关弹窗时关闭 PeerConnection

适合监控预览、设备联调、低延迟大屏。若还要兼容旧浏览器或弱网,可再做WebRTC 优先、FLV/HLS 降级的双通道方案。


参考

  • ZLMediaKit
  • MDN: RTCPeerConnection.connectionState
  • ZLMRTCClient 官方 Demo(随 ZLM www 目录或官方仓库)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询