资讯动态

Unity WebGL 播放海康监控流:AVProVideo 实战 Demo 与避坑指南

发布时间:2026/10/16 3:20:59 来源:尧图企业网站定制
简介这份资源面向需要在Unity WebGL环境下接入海康监控设备实时画面的开发者核心解决浏览器端播放RTSP/RTMP流并规避AVProVideo水印的问题。作者重写了Http请求模块并附带无水印版AVProVideo的WebGL发布设置指导配置文件只需手动修改Http请求配表中的字段值即可正常发布WebGL运行本地部署采用nginx适合具备一定Unity与C#基础的流媒体开发人员。压缩包共701个文件约80.17MB包含99个cs脚本、30个asset资源、28个so配置、26个dll动态库、25个shader着色器以及aar、jar、mat、mp4、png等素材与依赖覆盖播放器内核、渲染与平台适配层。目前已有1213人学习下载。通过该demo可掌握WebGL下视频流请求改写、AVProVideo配置与发布流程并参考nginx部署思路完成本地联调快速搭建可运行的监控预览原型。1. 从一次监控画面接入翻车说起这个 Demo 到底解决了什么问题去年帮一个做智慧园区的团队看现场他们用 Unity 做了一套三维可视化大屏模型、灯光、粒子都调得挺漂亮结果卡在最后一步——海康摄像头的实时画面怎么都贴不到 WebGL 页面的模型上。VideoPlayer 组件在编辑器里跑得好好的一发布到 WebGL 就黑屏控制台报的错还都是些看不懂的编解码问题。折腾了三天最后换了个思路才跑通。这个 Demo 源码干的就是这件事在 Unity WebGL 平台下用 AVProVideo 插件播放海康设备监控的实时画面。它不是一个完整的业务系统而是一个最小可运行的技术验证工程——把「海康 RTSP 流 → 转码 → AVProVideo 播放 → WebGL 渲染」这条链路完整跑通让你能直接拿过去改。适合正在做安防可视化、数字孪生大屏、工业监控面板的 Unity 开发者尤其是被 WebGL 视频播放坑过的人。源码里包含场景文件、播放控制脚本、材质配置和 WebGL 模板拿到手改个流地址就能看到画面。2. 技术选型拆解为什么是 AVProVideo 而不是原生 VideoPlayer2.1 WebGL 平台视频播放的硬约束Unity 原生 VideoPlayer 在 WebGL 下的限制非常多。它依赖浏览器的 HTML5 video 标签而浏览器对视频编码的支持是固定的——H.264 基本没问题但海康设备默认输出的 RTSP 流用的是 H.265 或者私有封装格式浏览器根本不认。更麻烦的是VideoPlayer 在 WebGL 下不支持直接播放 RTSP 协议你没法把rtsp://admin:password192.168.1.64:554/Streaming/Channels/101这样的地址直接丢给它。AVProVideo 的思路不一样。它在 WebGL 平台下走的是 HTML5 video 元素 MSEMedia Source Extensions的路线但做了大量兼容层封装。它支持 HLS.m3u8和 DASH 这类浏览器友好的流格式也支持直接指定视频 URL。关键点在于AVProVideo 帮你处理了 WebGL 下视频纹理的上传和渲染你不需要自己写 WebGL 的 video texture 绑定逻辑。注意AVProVideo 在 WebGL 下依然不能直接播 RTSP必须先把 RTSP 转成 HLS 或 WebRTC 等浏览器可播的格式。这个 Demo 的架构里转码环节是独立于 Unity 工程之外的。2.2 海康设备流的接入方式对比海康摄像头常见的取流方式有三种在这个 Demo 的场景下各有取舍取流方式协议延迟WebGL 兼容性适用场景RTSP 直取RTSP over TCP低200-500ms不支持本地客户端、桌面端HLS 切片HTTP m3u8高3-10s原生支持WebGL、对延迟不敏感WebRTC 推流WebRTC极低200ms需额外信令实时性要求高的 WebGL这个 Demo 采用的是 HLS 方案。原因很直接AVProVideo 在 WebGL 下对 HLS 的支持最成熟不需要引入额外的 WebRTC 信令服务器部署成本最低。代价就是延迟偏高大概在 3 到 5 秒左右。如果你的场景是实时操控这个延迟可能不够如果只是监控预览完全够用。2.3 AVProVideo 的核心 API 与播放控制Demo 里控制播放的核心脚本挂在场景中的 MediaPlayer 对象上。下面这段代码是播放控制逻辑的简化版保留了关键调用using RenderHeads.Media.AVProVideo; using UnityEngine; public class HikvisionStreamPlayer : MonoBehaviour { // AVProVideo 的 MediaPlayer 组件引用 public MediaPlayer mediaPlayer; // 海康流转换后的 HLS 地址 public string streamUrl http://192.168.1.100:8080/live/stream.m3u8; void Start() { if (mediaPlayer null) { Debug.LogError(MediaPlayer 未赋值); return; } // 打开媒体第二个参数是自动播放 mediaPlayer.OpenMedia(new MediaPath(streamUrl, MediaPathType.AbsolutePathOrURL), true); } void Update() { // 每帧检查播放状态处理缓冲和错误 if (mediaPlayer.Info ! null) { if (mediaPlayer.Info.GetPlaybackState() MediaPlayer.PlaybackState.Playing) { // 正常播放中可以在这里做画面亮度、对比度调节 } else if (mediaPlayer.Info.GetPlaybackState() MediaPlayer.PlaybackState.Buffering) { // 缓冲中UI 上可以显示加载提示 } } } // 切换摄像头流地址 public void SwitchStream(string newUrl) { streamUrl newUrl; mediaPlayer.OpenMedia(new MediaPath(streamUrl, MediaPathType.AbsolutePathOrURL), true); } }这段代码的逻辑很直白OpenMedia负责打开流地址第二个参数true表示打开后自动播放。Update里通过mediaPlayer.Info.GetPlaybackState()拿到当前播放状态你可以根据状态做 UI 反馈。SwitchStream是给多摄像头切换用的直接换 URL 重新打开就行。参数说明MediaPath的第二个参数MediaPathType.AbsolutePathOrURL表示传入的是完整 URL不是相对路径。如果你把流地址放在 StreamingAssets 下要用RelativeToStreamingAssetsFolder。OpenMedia的第二个参数是autoPlayWebGL 下浏览器有自动播放策略限制可能需要用户先点一下页面才能触发播放。3. 工程结构拆解从场景到 WebGL 模板的完整链路3.1 目录结构与关键文件说明这个 Demo 的工程结构不复杂但每个文件都有明确职责。下面是我整理的核心目录树WebGLHikvisionDemo/ ├── Assets/ │ ├── Scenes/ │ │ └── MainScene.unity # 主场景包含摄像机、平面、UI │ ├── Scripts/ │ │ ├── HikvisionStreamPlayer.cs # 播放控制核心脚本 │ │ └── StreamSwitcher.cs # 多路流切换逻辑 │ ├── Materials/ │ │ └── VideoMaterial.mat # 用于显示视频的材质 │ ├── Plugins/ │ │ └── AVProVideo/ # AVProVideo 插件目录 │ └── WebGLTemplates/ │ └── CustomTemplate/ │ └── index.html # WebGL 页面模板 ├── ProjectSettings/ │ └── ProjectSettings.asset # 项目设置含 WebGL 配置 └── Packages/ └── manifest.json # 包依赖清单MainScene.unity里有一个 Quad 或 Plane 对象挂载了VideoMaterial材质上绑定了 AVProVideo 的ApplyToMaterial组件。摄像机正对这个平面UI 层有播放/暂停按钮和流地址输入框。StreamSwitcher.cs负责在多个摄像头之间切换逻辑就是换 URL 重新OpenMedia。3.2 场景搭建与材质绑定步骤如果你要自己从头搭这个场景按下面几步走第一步在 Hierarchy 里创建一个 3D Object → Quad调整 Scale 为 (16, 9, 1)这样比例接近监控画面的 16:9。第二步在 Project 窗口右键创建 Material命名为VideoMaterialShader 选Unlit/Texture。把这个材质拖到 Quad 上。第三步选中 Quad在 Inspector 里 Add Component → AVProVideo → Apply To Material。把VideoMaterial拖到Material字段把场景里的MediaPlayer拖到Media Player字段。第四步在场景里创建空物体命名为MediaPlayerAdd Component → AVProVideo → Media Player。在Media Player组件的Global Settings里把Video Location设为URLPath填你的 HLS 地址。第五步把HikvisionStreamPlayer.cs挂到任意物体上把MediaPlayer拖到脚本的mediaPlayer字段。提示WebGL 平台下MediaPlayer的Platform Options里要勾选WebGL相关的选项特别是Use WebGL Video和Auto Rotate这类设置具体取决于 AVProVideo 的版本。3.3 WebGL 发布配置与模板修改WebGL 发布有几个关键设置漏一个就可能黑屏在File → Build Settings里切换到 WebGL 平台点击Player Settings。在Other Settings里Color Space建议设为GammaLinear在某些浏览器下会导致视频颜色发灰。Api Compatibility Level设为.NET Standard 2.1或.NET 4.x取决于 AVProVideo 版本要求。在Publishing Settings里Compression Format选Disabled或Gzip。如果选Brotli某些服务器不支持会导致加载失败。Decompression Fallback建议勾上兼容性更好。WebGL 模板的index.html里需要确保 video 元素能被正确创建。AVProVideo 会在运行时动态插入 video 标签你不需要手动写。但如果你用了自定义模板要保证body里有足够的空间并且没有 CSS 把 video 隐藏了。!-- WebGLTemplates/CustomTemplate/index.html 关键片段 -- canvas idunity-canvas width960 height600/canvas script // Unity 加载器配置 var canvas document.querySelector(#unity-canvas); var buildUrl Build; var loaderUrl buildUrl /WebGLHikvisionDemo.loader.js; var config { dataUrl: buildUrl /WebGLHikvisionDemo.data, frameworkUrl: buildUrl /WebGLHikvisionDemo.framework.js, codeUrl: buildUrl /WebGLHikvisionDemo.wasm, streamingAssetsUrl: StreamingAssets, companyName: DefaultCompany, productName: WebGLHikvisionDemo, productVersion: 0.1 }; // 创建 Unity 实例 var script document.createElement(script); script.src loaderUrl; script.onload () { createUnityInstance(canvas, config, (progress) { // 加载进度回调 }).then((unityInstance) { // 加载完成 }); }; document.body.appendChild(script); /script这段 HTML 是 Unity WebGL 的标准加载模板关键点是streamingAssetsUrl指向StreamingAssets目录AVProVideo 的配置文件如果放在这里需要确保路径正确。createUnityInstance返回的unityInstance可以用来和 Unity 侧通信比如发送流地址切换指令。4. 避坑与排查WebGL 视频播放的五个血泪教训4.1 黑屏但控制台无报错现象页面加载完成Unity 场景正常渲染但视频平面是纯黑控制台没有明显错误。原因最常见的是浏览器自动播放策略拦截。Chrome 要求用户有交互行为点击、触摸后才能播放带声音的视频。AVProVideo 默认可能带音频轨道被浏览器拦截了。解决在MediaPlayer的Audio设置里把Volume设为 0或者勾选Mute。然后在页面上加一个「点击播放」的按钮用户点击后再调用OpenMedia。另一个可能是 HLS 地址跨域检查服务器是否返回了Access-Control-Allow-Origin: *。4.2 画面卡顿、延迟越来越大现象刚开始播放正常几分钟后延迟累积到十几秒画面一卡一卡的。原因HLS 切片方案本身延迟就高如果转码服务器的切片时长设得太大比如 10 秒一片延迟会非常明显。另外WebGL 下视频解码走的是浏览器如果分辨率太高4K浏览器解码压力大也会卡。解决把 HLS 切片时长调到 1 到 2 秒GOP 对齐。分辨率降到 1080p 或 720p。在 AVProVideo 的MediaPlayer里开启Buffer优化适当增大缓冲区。如果还是卡考虑换 WebRTC 方案。4.3 发布后提示「Uncaught RuntimeError: memory access out of bounds」现象WebGL 构建加载到一半报内存越界错误页面崩溃。原因Unity WebGL 默认内存池可能不够用AVProVideo 在解码视频时需要额外内存。特别是你同时播放多路流的时候。解决在Player Settings → Publishing Settings里把Memory Size从默认的 256MB 调到 512MB 或 1024MB。注意不要超过浏览器限制Chrome 单标签页大概能用到 2GB 左右。4.4 流地址在编辑器里能播WebGL 里不能播现象Unity 编辑器里填同样的 HLS 地址播放正常发布到 WebGL 后一直缓冲或直接报错。原因编辑器下 AVProVideo 走的是原生解码路径WebGL 下走的是浏览器解码路径两者支持的编码格式不一样。编辑器能解的 H.265浏览器不一定能解。解决确保转码服务器输出的是 H.264 编码的 HLS。用ffprobe检查流信息确认Video Codec是h264。如果是hevc需要重新转码。4.5 多路流切换时画面残留或崩溃现象从摄像头 A 切到摄像头 B画面没有更新或者直接卡死。原因OpenMedia是异步操作前一路流还没完全关闭就打开新流导致资源竞争。解决在SwitchStream里加一个延迟或状态检查等mediaPlayer.Info.GetPlaybackState()变成Stopped或Error后再打开新流。或者先调用mediaPlayer.CloseMedia()等一帧后再OpenMedia。public void SwitchStreamSafe(string newUrl) { StartCoroutine(SwitchRoutine(newUrl)); } private IEnumerator SwitchRoutine(string newUrl) { // 先关闭当前流 mediaPlayer.CloseMedia(); // 等待一帧确保资源释放 yield return new WaitForEndOfFrame(); // 再打开新流 mediaPlayer.OpenMedia(new MediaPath(newUrl, MediaPathType.AbsolutePathOrURL), true); }这段协程的逻辑是先CloseMedia释放当前解码资源等一帧让 AVProVideo 内部状态机完成清理再打开新流。不加这个等待快速切换时很容易触发空引用或内存越界。5. 二次开发改造从 Demo 到可交付项目的三个关键改动5.1 把流地址配置化别硬编码Demo 里流地址是写死在脚本里的实际项目肯定不能这样。我一般会做一个StreamConfig的 ScriptableObject把摄像头列表、名称、URL 都配进去运行时动态加载。using UnityEngine; [CreateAssetMenu(fileName StreamConfig, menuName Hikvision/StreamConfig)] public class StreamConfig : ScriptableObject { [System.Serializable] public class CameraStream { public string cameraName; // 摄像头名称 public string streamUrl; // HLS 流地址 public bool isMainStream; // 是否主码流 } public CameraStream[] streams; }在 Unity 里右键Create → Hikvision → StreamConfig创建配置文件把摄像头信息填进去。运行时用Resources.LoadStreamConfig(StreamConfig)加载或者通过 Addressables 远程加载。这样改流地址不用重新打包 WebGL改配置文件就行。5.2 加一层播放状态回调别在 Update 里轮询Demo 里用Update每帧检查播放状态实际项目里这样写性能不好而且逻辑分散。AVProVideo 提供了事件回调用事件驱动更干净。void OnEnable() { mediaPlayer.Events.AddListener(OnMediaPlayerEvent); } void OnDisable() { mediaPlayer.Events.RemoveListener(OnMediaPlayerEvent); } private void OnMediaPlayerEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode) { switch (et) { case MediaPlayerEvent.EventType.ReadyToPlay: // 流已就绪可以开始播放 break; case MediaPlayerEvent.EventType.Started: // 播放开始 break; case MediaPlayerEvent.EventType.FirstFrameReady: // 第一帧渲染完成可以隐藏加载动画 break; case MediaPlayerEvent.EventType.Error: // 播放出错根据 errorCode 做重试或提示 Debug.LogError($播放错误: {errorCode}); break; } }MediaPlayerEvent.EventType覆盖了从打开到播放到出错的完整生命周期。FirstFrameReady特别有用你可以用它来隐藏 loading 动画用户体验会好很多。Error事件里可以根据errorCode决定是重试还是提示用户检查网络。5.3 多路流同屏的性能取舍如果你要做 4 路或 9 路同屏WebGL 下性能压力很大。我的经验是超过 4 路就别用 HLS 了延迟和 CPU 都扛不住。4 路以内把分辨率压到 720p帧率限制在 15fps浏览器还能跑。另一个技巧是用RenderTexture做画面池化。不要每个摄像头都创建一个MediaPlayer而是用 2 到 3 个MediaPlayer轮询播放切换时只换 URL。这样内存占用可控但切换会有短暂黑屏。// 画面池化示例两个 MediaPlayer 轮询 public MediaPlayer[] playerPool; private int currentIndex 0; public void PlayNext(string url) { var player playerPool[currentIndex]; player.OpenMedia(new MediaPath(url, MediaPathType.AbsolutePathOrURL), true); currentIndex (currentIndex 1) % playerPool.Length; }这个方案适合轮播场景比如大屏上每隔 10 秒切换一个摄像头。如果是固定 4 宫格还是得用 4 个MediaPlayer但要把分辨率和帧率压下来。5.4 一个验证技巧用浏览器开发者工具看视频元素WebGL 发布后按 F12 打开开发者工具在 Elements 面板里搜video。AVProVideo 会在 DOM 里插入 video 标签你可以直接看到它的src、currentTime、readyState等属性。如果readyState一直是 0说明流没加载成功如果是 4说明可以播放。这个技巧比看 Unity 控制台快得多我每次排查播放问题都先看这里。从那以后我每次做 WebGL 视频接入都强制走一遍「先看 video 元素状态再看 Unity 日志」的流程能省掉一半的排查时间。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑