Web 播放 RTSP(Webrtc方案)
需求背景
项目需要在 web 上播放海康摄像头,这个在各种智慧类应用上应该是常见需求,在前几年的时候也有这个需求,那时候通过旧版本谷歌浏览器可以直接播放 rtmp 解决了问题;现在谷歌浏览器已经不再支持,项目上又有这个需求,打算好好处理下。大概需求内容项目有多个车,每个车有多个摄像头,web对这些摄像头进行管理呈现。
Web 播放视频流方案
web 播放视频流有多种方案有 HLS、flv、webrtc 等等,我们这里选用 webrtc 方案,webrtc 播放视频是网页原生支持的音视频通信技术,对比其他其他方案有低时延等优点
Webrtc 实践
- 海康摄像头推送 rtsp 流,web 播放 webrtc,搜索 rtsp 转 webrtc 发现开源项目 https://github.com/deepch/RTSPtoWebRTC,golang 编写,参照项目说明,项目跑起来很简单,代码仓库拉下来运行就行了,
RTSPtoWebRTC项目自带 http 代理了简单 web 页面 - 按照项目说明将 config 修改为本地海康摄像头地址,重新运行验证,摄像头就播放出来了

![[图片]](https://img-blog.csdnimg.cn/direct/8a1a7bdd325145bea5996f661055eeaa.png)
- 自身项目为前后端分离,开始迁移该项目的 webrtc 代码到自己的项目中,编写一个 Webrtc 组件
exportfunctionWebRtcPlayer(props:{ suuid:string, url:string}){let ref =useRef<HTMLVideoElement>();useEffect(()=>{let{ suuid, url }= props;let stream =newMediaStream();const pc =newRTCPeerConnection();
pc.onnegotiationneeded=async()=>{let offer =await pc.createOffer();await pc.setLocalDescription(offer);let formData =newFormData();
formData.append('suuid', suuid);
formData.append('data',btoa(pc.localDescription.sdp));fetch(`${APP_CONFIG.rtc}/stream/receiver/`+ suuid,{
method:"POST",
body: formData
}).then(resp =>{return resp.text()}).then(data =>{try{
pc.setRemoteDescription(newRTCSessionDescription({
type:'answer',
sdp:atob(data)}))}catch(e){console.warn(e);}})};
pc.ontrack=function(event){
stream.addTrack(event.track);
ref.current.srcObject = stream;log(event.streams.length +' track is delivered')}
pc.oniceconnectionstatechange= e =>log(pc.iceConnectionState)letlog= msg =>{// document.getElementById('div').innerHTML += msg + '<br>'}//新增加的http接口let formData =newFormData();
formData.append('suuid', suuid);
formData.append('url', url);fetch(`${APP_CONFIG.rtc}/codec`,{
method:"POST",
body: formData
}).then(resp => resp.json()).then(data =>{(data asany[]).forEach(el =>{
pc.addTransceiver(el.Type,{'direction':'sendrecv'})})
pc.setRemoteDescription(newRTCSessionDescription({
type:'answer',
sdp:atob(data)}))}).catch(e =>{console.warn(e);})//demo中使用的接口// fetch("http://127.0.0.1:8083/stream/codec/" + suuid)// .then(resp => resp.json())// .then(data => {// (data as any[]).forEach(el => {// pc.addTransceiver(el.Type, {// 'direction': 'sendrecv'// })// })// pc.setRemoteDescription(new RTCSessionDescription({// type: 'answer',// sdp: atob(data)// }))// }).catch(e => {// console.warn(e);// })},[])return<video ref={ref} style={{ width:"600px"}} autoPlay muted controls />}
其中
fetch(${APP_CONFIG.rtc}/stream/receiver/ + suuid
和
fetch(http://127.0.0.1:8083/stream/codec/ + suuid)
是项目 demo 中使用到的接口,项目跑起,可以在项目里看到摄像头,初步完成。
- 观察项目代码,发现:1. config 配置了 rtsp 流地址信息,
on_demand即是否是未请求也处理 rtsp2. http 接口用于信令交换(/stream/codec/用于查询配置返回 codec 信息,/stream/receiver/用于发起一个协程go RTSPWorkerLoop) - 我的项目是内网环境且 rtsp 需要预先设置好改动就要重启有点麻烦,于是做出修改:1. config 修改配置
on_demand为 false,网页未请求则不处理 rtsp,满足大量 rtsp 配置选播放2. 去掉 ice 配置,ice 配置是一个公网环境用的,内网没有3. 新增 http 接口动态传入设备的摄像头配置,传递参数 key 和 rtsp 地址,如果 key 在配置里不存在则写入 config,其他和/stream/codec/代码一致
完成修改后运行项目播放 ok,断网播放 ok,本地服务器 docker 部署再次测试发现没法播放,连接断了
WritePacket WebRTC Client Offline
,
- 搜索问题,发现项目 issue:https://github.com/deepch/RTSPtoWebRTC/issues/183,修改 docker 配置 network:"host"解决问题,再次测试播放 ok

- 项目部署 RTSPtoWebRTC 能播通信链路 ok,基本可以在项目上使用,当然项目 RTSPtoWebRTC 本身是 demo 型,一些细节未处理,比如
/stream/codec/接口内部在coGe(suuid string) []av.CodecData方法里直接 100 次循环,但是并未完成视频的等待,导致在后续流程 sps()会报错index out of range,这个可以修改等待代码解决
版权归原作者 干饭杂记 所有, 如有侵权,请联系我们删除。