项目需要在 web 上播放海康摄像头,这个在各种智慧类应用上应该是常见需求,在前几年的时候也有这个需求,那时候通过旧版本谷歌浏览器可以直接播放 rtmp 解决了问题;现在谷歌浏览器已经不再支持,项目上又有这个需求,打算好好处理下。大概需求内容项目有多个车,每个车有多个摄像头,web对这些摄像头进行管理呈现。
web 播放视频流有多种方案有 HLS、flv、webrtc 等等,我们这里选用 webrtc 方案,webrtc 播放视频是网页原生支持的音视频通信技术,对比其他其他方案有低时延等优点
海康摄像头推送 rtsp 流,web 播放 webrtc,搜索 rtsp 转 webrtc 发现开源项目 https://github.com/deepch/RTSPtoWebRTC,golang 编写,参照项目说明,项目跑起来很简单,代码仓库拉下来运行就行了,RTSPtoWebRTC 项目自带 http 代理了简单 web 页面
按照项目说明将 config 修改为本地海康摄像头地址,重新运行验证,摄像头就播放出来了
![[图片]](https://img.pic99.top/ittdroid/202411/4a2d016dab872c7.png)
自身项目为前后端分离,开始迁移该项目的 webrtc 代码到自己的项目中,编写一个 Webrtc 组件
export function WebRtcPlayer(props: { suuid: string, url: string }) { let ref = useRef(); useEffect(() => { let { suuid, url } = props; let stream = new MediaStream(); const pc = new RTCPeerConnection(); pc.onnegotiationneeded = async () => { let offer = await pc.createOffer(); await pc.setLocalDescription(offer); let formData = new FormData(); 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(new RTCSessionDescription({ 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) let log = msg => { // document.getElementById('div').innerHTML += msg + '
' } //新增加的http接口 let formData = new FormData(); formData.append('suuid', suuid); formData.append('url', url); fetch(`${APP_CONFIG.rtc}/codec`, { method: "POST", body: formData }) .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); }) //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 其中 fetch(${APP_CONFIG.rtc}/stream/receiver/ + suuid 和 fetch(http://127.0.0.1:8083/stream/codec/ + suuid) 是项目 demo 中使用到的接口,项目跑起,可以在项目里看到摄像头,初步完成。
观察项目代码,发现:
on_demand 即是否是未请求也处理 rtsp/stream/codec/ 用于查询配置返回 codec 信息,/stream/receiver/ 用于发起一个协程 go RTSPWorkerLoop)我的项目是内网环境且 rtsp 需要预先设置好改动就要重启有点麻烦,于是做出修改:
on_demand 为 false,网页未请求则不处理 rtsp,满足大量 rtsp 配置选播放/stream/codec/ 代码一致完成修改后运行项目播放 ok,断网播放 ok,本地服务器 docker 部署再次测试发现没法播放,连接断了 WritePacket WebRTC Client Offline,

/stream/codec/ 接口内部在 coGe(suuid string) []av.CodecData 方法里直接 100 次循环,但是并未完成视频的等待,导致在后续流程 sps()会报错 index out of range,这个可以修改等待代码解决
上一篇:前端 Vite 项目使用 vite-plugin-dts 打包输出.d.ts文件,分析处理踩坑:Cannot find module ‘vue‘. Did you mean to set ...