简介:这份资源面向需要在Unity WebGL平台接入海康设备监控实时画面的开发者,基于AVProVideo(无水印版本)实现视频流播放,并重写了Http请求模块,解决WebGL环境下监控视频无法正常拉流与播放的问题。包内包含完整的Unity工程源码与发布配置,只需手动修改Http请求配表中的字段值,即可发布WebGL并正常运行,本地部署采用nginx,适合具备一定Unity与C#基础的音视频开发人员参考。资源共701个文件,涵盖99个cs脚本、30个asset资源、28个so配置、26个dll动态库、25个shader着色器、14个aar与6个jar安卓依赖、12个mp4示例视频及若干json、txt说明文件,压缩包约80.17MB,目录结构完整。目前已有1213人学习下载,读者可获取AVProVideo在WebGL下的发布设置指导、Http请求模块改写思路、海康设备取流对接方案以及可运行的demo工程,便于快速验证与二次开发。
1. 当海康监控流撞上 WebGL:这套 AVProVideo 播放 Demo 到底能跑通什么
去年有个做智慧园区可视化大屏的哥们找我,说他们用 Unity 发布 WebGL 之后,海康摄像头的 RTSP 流死活播不出来,浏览器控制台一片红,问我有没有现成的路子。这个问题其实很典型:Unity 在 WebGL 平台下没有原生视频解码能力,而海康设备吐出来的又是 RTSP 流,浏览器本身也不认这个协议。你要在网页端把监控画面跑起来,中间必须有人做协议转换和渲染适配。
这套 Demo 源码解决的就是这个链路问题。它的核心思路是用 AVProVideo 这个 Unity 插件作为播放器层,配合后端转流服务把海康设备的 RTSP 流转换成浏览器能吃的格式,然后在 WebGL 环境下完成渲染输出。整个包里有 C# 脚本、Shader、预制体、场景文件,拿到手改几个配置就能跑。适合谁?做安防可视化、工业监控大屏、数字孪生项目的 Unity 开发者,尤其是那些被 WebGL 视频播放坑过的兄弟。下面我把这套东西拆开讲,从架构到代码到踩坑,尽量让你少走弯路。
2. 拆解播放链路:从海康 RTSP 到 WebGL 画布的完整数据流
2.1 为什么不能直接在 WebGL 里播 RTSP
先说清楚底层限制。Unity WebGL 构建出来的产物跑在浏览器沙箱里,视频播放依赖的是 HTML5<video>标签或者 WebCodecs API,这两种方式都不支持 RTSP 协议。RTSP 是应用层协议,需要专门的客户端去协商和拉流,浏览器原生不具备这个能力。
另一个限制是解码。海康摄像头的 RTSP 流通常是 H.264 或 H.265 编码,WebGL 环境下 Unity 没法直接调用系统解码器。AVProVideo 在桌面平台可以走 DirectShow 或者 Media Foundation,但在 WebGL 平台它走的是浏览器原生视频播放通道,也就是说,你喂给它的必须是一个浏览器能识别的 URL。
所以整条链路必须是这样的:海康设备输出 RTSP 流,中间有一个转流服务把 RTSP 转成 HLS 或者 WebRTC 或者 HTTP-FLV,然后 AVProVideo 在 WebGL 端加载这个转换后的地址,最终渲染到 Unity 的 Texture 上。这个 Demo 的架构就是按这个思路搭的。
2.2 转流服务的选型与配置
转流服务这块,常见做法是用 FFmpeg 做 RTSP 到 HLS 的转换,或者用某开源流媒体服务做 RTSP 到 WebRTC 的低延迟转发。Demo 里默认走的是 HLS 方案,因为配置简单、兼容性好,缺点是延迟偏高,大概 3 到 8 秒。如果你做的是实时监控,对延迟敏感,建议换成 WebRTC 方案,延迟能压到 500 毫秒以内。
FFmpeg 转 HLS 的命令大概长这样:
ffmpeg -rtsp_transport tcp \ -i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \ -c:v libx264 -preset ultrafast -tune zerolatency \ -c:a aac -f hls \ -hls_time 2 -hls_list_size 3 \ -hls_flags delete_segments \ /path/to/output/stream.m3u8这里几个参数值得说清楚。-rtsp_transport tcp强制走 TCP 拉流,UDP 在某些网络环境下丢包严重,画面会花屏。-preset ultrafast -tune zerolatency是让编码器尽量快,牺牲压缩率换延迟。-hls_time 2表示每个切片 2 秒,-hls_list_size 3表示播放列表里只保留 3 个切片,配合delete_segments自动清理旧文件,避免磁盘写满。
注意:海康设备的 RTSP 地址格式跟通道号有关,主码流一般是
Channels/101,子码流是Channels/102。WebGL 端建议用子码流,分辨率低、码率小,浏览器解码压力小。
2.3 AVProVideo 在 WebGL 端的初始化流程
转流服务跑起来之后,Unity 这边的工作就是让 AVProVideo 去加载 m3u8 地址。Demo 里核心的初始化脚本大概是这样:
using RenderHeads.Media.AVProVideo; using UnityEngine; public class HikvisionPlayer : MonoBehaviour { // 在 Inspector 里拖入 MediaPlayer 组件 public MediaPlayer mediaPlayer; // 转流服务输出的 m3u8 地址 private string streamUrl = "http://localhost:8080/stream.m3u8"; void Start() { // WebGL 平台必须用 URL 方式打开,不能用文件路径 mediaPlayer.OpenMedia(new MediaPath(streamUrl, MediaPathType.AbsolutePathOrURL), true); // 注册事件回调,处理播放状态变化 mediaPlayer.Events.AddListener(OnMediaPlayerEvent); } void OnMediaPlayerEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode) { switch (et) { case MediaPlayerEvent.EventType.ReadyToPlay: mp.Play(); break; case MediaPlayerEvent.EventType.FirstFrameReady: Debug.Log("首帧渲染完成,画面应该出来了"); break; case MediaPlayerEvent.EventType.Error: Debug.LogError($"播放出错: {errorCode}"); break; } } }这段代码的逻辑很直白:OpenMedia触发加载,ReadyToPlay事件到了就调Play(),FirstFrameReady说明画面已经渲染到 Texture 上了。关键点是MediaPathType.AbsolutePathOrURL,WebGL 平台下必须用这个类型,否则 AVProVideo 会当成本地文件路径去处理,直接报错。
参数方面,mediaPlayer需要在 Inspector 里配置好,Auto Open 和 Auto Play 建议关掉,用代码控制更灵活。另外mediaPlayer.m_VideoLocation在 WebGL 下会被忽略,不用管。
3. 把 Demo 跑起来:场景配置、参数调优与首帧验证
3.1 场景搭建与关键组件挂载
拿到源码包之后,先用 Unity Hub 打开项目。Demo 用的 Unity 版本建议在 2021 LTS 以上,低版本对 WebGL 视频播放的支持不太稳定。打开之后找到主场景,里面应该已经搭好了基本的 UI 和摄像机。
场景里的关键对象是一个挂着MediaPlayer组件的 GameObject,以及一个用来显示画面的 RawImage 或者 MeshRenderer。AVProVideo 的工作方式是把解码后的视频帧输出到一个 Texture 上,你需要把这个 Texture 赋给某个显示组件。Demo 里用的是 RawImage,在 Canvas 下面。
检查几个地方:MediaPlayer 组件的Video Location选URL,Path填你的转流地址;Auto Open和Auto Start都取消勾选;Platform Options下面 WebGL 相关的设置保持默认就行。然后确认 RawImage 的 Texture 字段已经绑定了 MediaPlayer 的输出。
3.2 转流地址的配置与网络连通性检查
在浏览器里先验证转流服务是否正常工作。打开 Chrome,直接访问http://localhost:8080/stream.m3u8,如果能看到一个文本文件内容,里面列了一堆.ts切片文件,说明 HLS 服务没问题。如果 404 或者连接被拒,先排查 FFmpeg 进程是否还在跑、端口是否被占用。
跨域问题也要注意。如果你的 Unity WebGL 页面和转流服务不在同一个域名和端口下,浏览器会拦截请求。解决办法是在转流服务那边加上 CORS 头,允许你的页面域名访问。用 Nginx 反代的话,加一行add_header Access-Control-Allow-Origin *;就行。
提示:本地开发时可以用
localhost,但部署到服务器之后一定要换成实际域名或者 IP,并且确保防火墙放行了对应端口。
3.3 首帧渲染验证与延迟测量
构建 WebGL 之后,用本地服务器打开页面。按 F12 打开控制台,看有没有报错。正常情况下,你应该能看到FirstFrameReady的日志输出,画面上也会出现监控画面。
延迟测量有个简单办法:拿手机秒表放在摄像头前面,对比 WebGL 页面里的画面和真实秒表的时间差。HLS 方案一般在 3 到 8 秒,WebRTC 方案能压到 1 秒以内。如果你发现延迟特别大,先检查hls_time是不是设太大了,改成 1 或者 2 试试。
如果画面一直黑屏,先看控制台有没有 CORS 报错,再看 AVProVideo 的日志输出。常见问题是 m3u8 地址写错了,或者转流服务输出的编码格式 AVProVideo 不支持。H.264 是肯定支持的,H.265 在部分浏览器上会翻车,建议统一转成 H.264。
4. 避坑指南:WebGL 视频播放的五个血泪教训
4.1 坑一:浏览器自动播放策略导致画面卡住
现象:页面加载后,控制台没有报错,但画面就是不动,点一下页面才开始播放。
原因:Chrome 等现代浏览器有自动播放限制,没有用户交互的情况下,带声音的视频不允许自动播放。AVProVideo 在 WebGL 下走的是浏览器视频通道,同样受这个策略约束。
解决:两个办法。一是把视频静音,mediaPlayer.Control.SetVolume(0f),静音的视频允许自动播放。二是加一个「点击播放」的按钮,让用户手动触发。Demo 里默认是静音模式,如果你需要声音,就得加交互。
4.2 坑二:HLS 切片累积导致内存暴涨
现象:页面跑了几个小时之后越来越卡,最后浏览器崩溃。
原因:HLS 播放过程中,浏览器会缓存已经下载的切片。如果hls_list_size设得太大,或者delete_segments没开,切片文件会一直堆积,内存和磁盘都扛不住。
解决:FFmpeg 命令里加上-hls_flags delete_segments,并且hls_list_size不要超过 5。另外在 Unity 端定期调用mediaPlayer.CloseMedia()再重新OpenMedia(),强制释放旧资源。我一般设个定时器,每两小时重连一次。
4.3 坑三:RTSP 拉流超时导致转流服务假死
现象:监控画面突然卡住不动,FFmpeg 进程还在,但不再输出新切片。
原因:海康设备在网络波动时会断开 RTSP 连接,FFmpeg 默认会一直等,不会自动重连。时间长了就假死了。
解决:FFmpeg 加上-stimeout参数设置超时时间,单位是微秒,比如-stimeout 5000000表示 5 秒超时。然后写个守护脚本,检测到 FFmpeg 进程退出或者输出目录超过一定时间没有新文件,就自动重启。用 systemd 或者 supervisor 都行。
4.4 坑四:WebGL 内存限制导致多路播放崩溃
现象:单路播放正常,加到四路以上页面就崩了。
原因:Unity WebGL 默认内存上限是 2GB 左右,每路视频解码和 Texture 都占内存,四路 1080p 基本就到顶了。
解决:降低单路分辨率,用子码流;减少同时播放的路数,或者做分页切换;在 Player Settings 里适当调大 WebGL Memory Size,但别超过 4GB,不然 32 位浏览器直接挂。实在要多路,考虑用 WebRTC 方案,内存占用比 HLS 低不少。
4.5 坑五:跨域配置遗漏导致请求被拦截
现象:本地测试一切正常,部署到服务器之后画面出不来,控制台报 CORS 错误。
原因:浏览器的同源策略限制,Unity WebGL 页面和转流服务不同源时,请求会被拦截。
解决:在转流服务的 HTTP 响应头里加上Access-Control-Allow-Origin,值设成你的页面域名或者*。如果用 Nginx,在 location 块里加add_header指令。如果用某开源流媒体服务,查一下它的 CORS 配置项,一般都有现成的开关。
5. 进阶技巧:多路分屏与自适应码率切换的实操
5.1 多路分屏的布局与性能平衡
实际项目里经常需要在一个页面上同时看多路监控。Demo 默认是单路,改成四路分屏不难,但性能要算清楚。我的做法是用一个 2x2 的 Grid Layout Group,每个格子放一个 RawImage,各自绑定独立的 MediaPlayer。
public class MultiCameraGrid : MonoBehaviour { public MediaPlayer[] players; // 四个 MediaPlayer 实例 public string[] streamUrls; // 四路转流地址 void Start() { for (int i = 0; i < players.Length; i++) { // 每路单独打开,互不干扰 players[i].OpenMedia(new MediaPath(streamUrls[i], MediaPathType.AbsolutePathOrURL), true); // 静音,避免多路声音叠加 players[i].Control.SetVolume(0f); } } }四路 720p 子码流在 WebGL 下基本能稳住,再往上就要考虑降帧或者按需加载了。我一般会加一个可见性检测,不在视口内的画面暂停播放,能省不少资源。
5.2 自适应码率切换的实现思路
HLS 本身支持多码率自适应,但 AVProVideo 在 WebGL 下不会自动切换,需要手动干预。思路是监听网络状况或者缓冲状态,动态切换 m3u8 地址。
IEnumerator AdaptiveBitrateCheck() { while (true) { // 每 10 秒检查一次缓冲状态 yield return new WaitForSeconds(10f); if (mediaPlayer.Control.IsBuffering()) { // 缓冲中,切到低码率流 mediaPlayer.OpenMedia(new MediaPath(lowBitrateUrl, MediaPathType.AbsolutePathOrURL), true); } else if (mediaPlayer.Control.GetCurrentTimeMs() > 0) { // 播放正常,保持当前码率 } } }这个逻辑比较粗糙,实际项目里可以结合GetBufferingProgress()的数值做更细的判断。另外切换码率时会有短暂黑屏,体验上要加个过渡动画遮一下。
5.3 验证清单与日常排查习惯
每次部署新环境,我都会走一遍这个清单:转流服务是否正常输出切片、m3u8 地址在浏览器里能否直接访问、CORS 头是否正确、Unity 构建的 WebGL 页面是否静音、首帧渲染日志是否出现、延迟是否在可接受范围。这套流程走下来,基本能覆盖 90% 的问题。
从那以后我每次接监控类项目,都会先把转流服务和 WebGL 播放分开验证,确认每一段链路都通了再联调。这样出问题的时候能快速定位是哪一层的锅,不用从头查起。希望帮到你。
本文还有配套的精品资源,点击获取