首页 / 资讯中心 / 文章详情

Unity WebGL播放海康监控:AVProVideo方案与踩坑实录

Unity WebGL播放海康监控:AVProVideo方案与踩坑实录 ★ FEATURED ARTICLE
简介面向需要在Unity WebGL平台中集成海康摄像头实时画面的开发人员这份示例源码演示了基于AVProVideo插件播放监控视频流的完整方案。针对WebGL环境常见的跨域与兼容性问题作者重写了Http请求模块并附上AVProVideo无水印版本的WebGL发布设置指导配置文件手动修改配表字段即可正常运行。资源共701个文件压缩包约80.17MB包含C#脚本、Shader、Asset/Mat资源、jar/aar/so/dll等原生库以及mp4视频样例类型覆盖工程运行所需的主要模块其中30个asset与14个mat构成场景与材质基础C#脚本和Shader负责渲染交互Android与Java原生库辅助设备接入。已有1214人学习下载适合使用AVProVideo并需要绕过水印、快速落地WebGL监控展示的Unity开发者可作为排错参考与二次开发基础也方便参考作者对HTTP请求层的改动策略与WebGL发布配置思路。整体工程结构清晰便于按模块查找与复用。1. WebGL平台播放海康监控AVProVideo与摄像头解码的那点事在Unity里做WebGL端的海康监控画面接入看起来是个标准活儿真做起来却是一连串的坑。Unity的VideoPlayer在WebGL平台基本是半残状态直接拿来播RTSP流根本不现实而海康设备自带的Web控件在浏览器里更是直接失灵——这种情况下AVProVideo几乎是唯一能稳定走通的商业化方案。这份demo源码正是把AVProVideo和H5播放协议结合起来在Unity里实现海康设备实时画面的WebGL端播放。对于需要做三维数字孪生、大屏可视化或者远程设备监控的开发者来说这份资源解决的问题非常具体你不需要自己从零去啃解码库也不用对海康SDK做深度适配只要照着项目结构替换IP、用户名、密码就能把摄像头画面嵌进你的Unity场景里。2. 前置准备为什么是AVProVideo以及WebGL下的播放链路2.1 WebGL平台视频播放的现状和选型逻辑做过WebGL视频播放的人都有体会Unity自带的VideoPlayer在Android和iOS上还算能用但在WebGL平台上兼容性问题非常突出。浏览器的视频解码能力受制于浏览器自身的实现Unity的VideoPlayer没法直接读取RTSP流因为WebGL的底层是通过WebAssembly和WebGL接口来渲染的没有直接建立TCP/UDP连接的能力。所以你在WebGL上要么选择HLS要么选择HTTP-FLV要么就得借助第三方插件来处理。AVProVideo是目前Unity生态里支持WebGL最成熟的商业视频插件。它底层会根据平台自动切换播放内核在Windows上可能走DirectShow在Android上走MediaPlayer在WebGL上则走浏览器原生的Media Source Extensions和Video Element。这个特性决定了它能够播放经过转封装的流媒体协议而不是直接去解码裸的RTSP流。选择AVProVideo还有一个实际考量国产化项目里经常要对接海康、大华这些设备它们的RTSP流在局域网内非常流畅但要把RTSP带到浏览器里就必须做协议转换。常见的做法是用海康的WebSocket或者HTTP接口去拉流再通过WebRTC或者FLV方式在浏览器端播放。AVProVideo在这条链路上提供了相对稳定的解码和渲染能力而不是让你自己去写浏览器的MediaSource逻辑。2.2 demo里的整体架构和播放链路拆解这份demo的核心链路可以概括为四段海康设备 → 流媒体中间层 → 浏览器播放内核 → AVProVideo渲染显示。在Unity里部署的时候你需要搞清楚自己当前到底用的是哪条链路因为不同链路对应的入口参数完全不同。链路节点常见形态AVProVideo中的对应配置海康设备输出RTSP如rtsp://user:passip:554/Streaming/Channels/101不能直接填入AVProVideo流媒体中间层海康网关、流媒体服务器、WebSocket转换服务提供HTTP-FLV或HLS地址浏览器播放内核WebGL平台自动切换的VideoElementAVProVideo内部自动处理Unity渲染端AVProVideo组件挂载到UI或3D平面RenderTexture或直接显示在这个demo里开发者用的是通过海康的HTTP接口获取到播放地址再交给AVProVideo的MediaSource去播放的模式。如果你要播放的是海康的实时预览需要先调用海康的接口拿到鉴权后的播放URL这一步在demo中已经封装成了C#的工具类。你还需要注意一个关键点WebGL平台跨域资源共享问题。浏览器的安全策略会拦截跨域的流媒体请求如果流媒体服务器和你的WebGL页面不在同一个域下你必须在流媒体服务器上配置CORS头否则AVProVideo即使拿到了URL也无法播放。这个点demo里没有显式标注但在部署时几乎是必踩的坑。2.3 快速跑通demo的五个环境准备步骤先把Unity环境准备好最好是2021.3以上的LTS版本因为WebGL的构建工具链在旧版本上可能缺少一些必要依赖。AVProVideo需要你有一份正版授权demo里不会附带完整插件通常是需要你自己导入后把脚本引用接上。第一步用Unity Hub创建一个WebGL项目项目名称随意但注意路径里不要包含中文否则某些C#编译环节可能报奇怪错误。# 这里用命令行示意创建项目的逻辑实际上Unity项目通过Hub创建即可 # 项目名如 HikWebGLDemo路径如 D:/Work/HikWebGLDemo第二步把AVProVideo插件包导入到项目中。导入完成后检查Assets目录下是否出现RenderHeads.AVProVideo文件夹如果没有出现说明导入过程中被Unity的安全策略拦截了。第三步把demo里的核心脚本文件夹复制到自己的项目中主要是海康设备连接管理类和视频显示控制脚本。// 核心配置类示例 public class HikCameraConfig { public string deviceIp 192.168.1.64; // 摄像头IP public string userName admin; // 设备用户名 public string userPwd YourPassword123; // 设备密码 public string channelNo 101; // 通道号一般101表示主码流 }这个配置类是接入海康设备的第一步IP、用户名、密码三项必须改成实际环境值。通道号字段需要特别说明海康设备的101通道通常是主码流分辨率高但码率大102是子码流分辨率较低但更流畅。WebGL端如果网络条件一般建议先切到子码流测试等整个链路通了再切回主码流。第四步在场景中创建一个Canvas添加一个RawImage用于显示视频画面。demo里有一个预设好的Prefab可以直接拖拽使用省去了手动绑定纹理的步骤。第五步把海康连接脚本挂到场景中的空物体上然后绑定RawImage的引用运行场景。如果一切配置正确画面会在一到两秒内出现在RawImage上。3. 核心脚本解析从设备登录到画面显示的完整C#流程3.1 登录鉴权与RTSP地址拼接逻辑海康设备的接入不是直接把RTSP流扔给播放器就行正规流程是先用HTTP接口做登录鉴权拿到会话标识然后基于该会话生成临时的播放地址。这个流程和直接拼接rtsp://ip/user:passip:554这种平民方式是两种路径demo里使用的是前者可靠性更高。登录模块的核心方法是调用海康ISAPI接口进行摘要认证这一步需要处理随机数、时间戳的MD5加密。demo里已经用C#写好了一套哈希计算工具你不需要自己重新实现认证逻辑只需要知道它内部做了什么。private string BuildLoginDigest(string realm, string nonce, string method, string uri) { // 海康ISAPI摘要认证的关键步骤 string ha1 Md5(${_userName}:{realm}:{_userPwd}); string ha2 Md5(${method}:{uri}); string digest Md5(${ha1}:{nonce}:{ha2}); return digest; }这段代码看起来很短但它是整个登录链路的基石。realm和nonce是服务器返回的随机值不同设备、不同时刻都不一样。如果把海康设备当作黑盒子对待调试时看到401错误基本就是这里算错了。常见做法是先用Postman手动模拟一次ISAPI请求确认设备正常返回再用demo里的网络请求逻辑去比对。登录成功后脚本会解析设备返回的XML数据从中提取出媒体流的播放URL。这个URL不是RTSP而是基于HTTP的流媒体地址。在WebGL环境下AVProVideo能播放的正是这种HTTP流格式。3.2 播放地址的二次处理把鉴权参数拼接进URL拿到设备返回的原始播放地址后不能直接交给播放器因为这条地址里通常带有时效性的鉴权参数过期后需要重新获取。demo里对这一层做了封装把地址的刷新逻辑和AVProVideo的播放切换动作绑定在一起。public void StartPlay(string rawUrl) { // 对海康返回的URL做合法性检查 if (string.IsNullOrEmpty(rawUrl)) { Debug.LogError(播放地址为空请检查设备登录状态); return; } // AVProVideo的MediaSource需要显式指定当前URL _mediaPlayer.OpenMedia(new MediaPath(rawUrl, PathType.Absolute), true); _mediaPlayer.Play(); }这段代码直接调用了AVProVideo的OpenMedia接口。PathType.Absolute表示使用绝对路径不需要额外的解析逻辑。第二参数autoPlay设为true表示打开媒体后立即开始播放。这个设计在流媒体场景下是合理的因为监控画面本身就是要持续播放不需要人为干预。这里有个细节值得注意AVProVideo在WebGL上打开RTSP格式的地址会直接失败但demo里走的链路是海康设备返回的HTTP播放地址所以在内部不会触发这个限制。播放过程中如果摄像头掉线了海康设备会主动断开连接AVProVideo会触发一个Error事件。demo的处理方式是在这个事件里重新走一遍登录流程重新获取播放地址自动续播。这种自动重连的逻辑在实际项目中非常重要因为监控场景下摄像头重启、网络抖动都是常见现象。3.3 RenderTexture的绑定和显示逻辑视频画面播放出来后不能直接显示在屏幕上因为AVProVideo默认的渲染方式是输出到RenderTexture或者UI纹理。demo里采用了UI的RawImage绑定RenderTexture的方案这个方案的好处是可以在画面上叠加UI元素比如摄像头名称、时间戳、报警弹窗等。private void BindVideoToTexture() { if (_mediaPlayer ! null _rawImage ! null) { // 将MediaPlayer输出的纹理直接绑定到RawImage显示 _rawImage.texture _mediaPlayer.Texture; } }Texture属性在AVProVideo中是一个动态属性当视频源切换或者分辨率变化时它内部会重新分配纹理对象。这个细节要求你必须持续更新绑定关系如果只在Start里绑定一次切换视频源后纹理可能会失效。在WebGL平台上纹理的更新频率和本地平台有所区别。浏览器环境下视频帧是通过浏览器解码后传递到WebGL纹理中的这个过程有额外的拷贝开销。如果你的场景中同时显示多路摄像头画面要特别注意每路画面不能全分辨率渲染否则浏览器的GPU占用会非常高。demo里给了一个多路画面的排列参考把RawImage放进Grid Layout Group组件动态添加多个RawImage子对象然后为每个子对象对应一个MediaPlayer实例。这种做法在业务上是合理的因为不同摄像头的流地址和登录状态都是独立的。3.4 摄像头掉线后的自动重连与UI状态恢复监控项目里设备掉线再恢复是很常见的。海康设备在断网重连后原有的播放会话会失效需要重新走一遍登录和取流的流程。这个demo在断线重连处做了比较细致的处理。private void OnPlayerErrorEvent(MediaPlayer mp, MediaPlayerEvent.ErrorEventArgs args) { // 记录错误码用于区分掉线还是地址失效 _lastErrorCode args.Code; // 尝试重新获取播放地址并打开 StartCoroutine(nameof(ReconnectAndPlay)); } private IEnumerator ReconnectAndPlay() { yield return new WaitForSeconds(2f); // 等待2秒避开瞬断 yield return LoginToDevice(); yield return FetchStreamUrl(); StartPlay(_streamUrl); }错误处理的流程是两步先等待短暂时间避开网络抖动引起的误报如果设备恢复了就能继续播放如果设备真断了重新登录会失败此时会再次触发错误逻辑。为了避免无限重连把浏览器请求堆满demo里设置了一个重连次数上限超过5次后暂停自动重连改为在UI上显示离线提示。4. WebGL构建配置与发布部署把U3D工程变成可访问的网页4.1 构建参数的设置压缩格式、代码优化与内存配置Unity工程在编辑器里跑通后真正发布到WebGL还需要处理一组特定的参数。这些参数设置不当轻则画面卡顿重则画面完全黑屏。构建窗口里的压缩格式建议选择Brotli因为它的压缩率比Gzip高加载WebGL页面时浏览器能更快地拉完体积较大的wasm文件和数据文件。代码优化要设置为Release模式虽然Debug模式在浏览器端可以查看C#堆栈信息但体量和性能都非常糟糕不适合做正式部署。内存大小是一个隐藏问题WebGL的Unity运行时默认内存上限是256MB如果你的场景里加载了多路视频纹理这个内存往往会不够用。演示项目在构建时会把WebGL的内存大小调到512MB甚至1GB但内存增大后低配置电脑的浏览器可能无法初始化这么大的内存区域。{ name: webgl-build-params, memorySize: 536870912, compressionFormat: brotli, optimizationLevel: 2 }这段JSON是Unity构建时生成的配置示意实际参数需要在Project Settings的Player设置面板中调整。内存参数的数值单位是字节上文中设置的是512MB如果你同时播放四路1080p画面这个数值可能需要进一步提升。但务必要结合目标用户机器的浏览器环境调得过大可能导致页面白屏。4.2 部署时的MIME类型和跨域配置WebGL构建完成后生成一堆文件你需要把这堆文件放到Web服务器上才能访问。这一步如果放在Nginx上必须确认服务器正确处理wasm、js、data这三种文件类型的MIME头。server { listen 80; server_name your-web-domain.com; location /hik-demo/ { alias /var/www/hik-demo/; types { application/wasm wasm; application/javascript js; application/octet-stream data; } add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET, POST, OPTIONS; } }这里的关键是Access-Control-Allow-Origin这一段它解决的是上文提到的CORS问题。如果不加这个头当你的WebGL页面部署在a.com而流媒体地址指向b.com时浏览器的同源策略会阻止视频数据的加载。常见做法是临时先把跨域头设置为星号等调试通过后再收敛为具体的域名白名单。还有一个经常被忽略的坑是data文件的大小。Unity会把场景里的资源和部分脚本信息打包进data文件有些项目这个文件可能超过100MB。Nginx默认对于大文件的下载没有特殊限制但浏览器端如果启用了某些安全插件或者网络环境中有代理服务器拦截大文件加载到一半被切断会导致页面一直停在加载界面。4.3 局域网部署的特殊处理海康设备IP与Web页面IP的关系如果你的系统运行在企业内网中海康摄像头、流媒体服务器、Web页面服务器都在同一个局域网内网络设计要格外小心。浏览器所在的电脑必须能够同时访问Web页面服务器和摄像头所在的网段如果两者不在同一网段视频拉流依然会失败。我一般会建议把部署结构整理清楚后再去动代码不要在没有网络排查的情况下反复改代码。典型的内网部署结构如下组件内网IP示例端口海康摄像头192.168.1.64554/8000流媒体网关服务192.168.1.1008080Web页面服务器192.168.1.2008088这种结构下摄像头、网关、Web服务器都在同一网段浏览器访问Web页面获取视频地址再通过HTTP向网关拉流。整个过程不涉及跨网段的路由问题排查起来也相对容易。如果你需要跨网段访问务必要在网关上做好路由策略或者在防火墙上放开对应的端口。海康设备的8000端口是SDK通信用的RTSP的554端口是预览流传输用的HTTP的80端口是ISAPI接口用的这三个端口在你做网关映射时要同步放通。5. 常见问题排查A黑屏掉线等五个高频坑位的实战记录5.1 画面黑屏但日志无报错这个现象在WebGL播放海康时经常出现AVProVideo的播放状态显示是正常的但画面就是黑的。定位原因时会发现海康设备默认开启了视频加密或鉴权失败后仍然返回正常码流但实际是无效数据。解决方法是检查海康设备是否开启了视频加密功能关闭后重新拉流。还有一种情况是摄像头码流设置为H.265编码但浏览器端不支持解码。WebGL的AVProVideo在部分浏览器上对H.265的支持是有条件限制的解决方案是去海康设备端把编码格式改成H.264。5.2 播放几秒后画面卡死刷新页面才能恢复这个坑和AVProVideo的缓冲区设置有关。浏览器端的视频元素在遇到网络抖动时会等待缓冲区数据而暂停解码但Unity侧的画面渲染并不会同步暂停导致画面卡在最后一帧。此时等待一段时间如果数据恢复画面会自动继续如果长时间不恢复则表现为假死状态。解决思路是把AVProVideo的缓冲时长调低让它更快地触发错误重连逻辑而不是一直等数据。另外在流媒体网关那边开启TCP的keepalive参数让连接断开能更快被感知到加速重连过程。5.3 多路画面同时播放时浏览器卡顿严重在网页里同时播放四路以上720p视频对浏览器GPU的解码能力是一个考验。有些机器在播放两路时已经出现明显掉帧这个问题单靠软件优化很难彻底解决。常见的处理策略是降低子码流的分辨率或帧率。海康的子码流默认可能是640x48015fps这个分辨率做区域预览足够但如果你把它误解为主码流直接推给浏览器性能就会很差。另外多路播放时不要每个实例都输出全尺寸RenderTexture而是把Unity Canvas的缩放控制交给UGUI去处理避免在每个视频纹理上浪费显存。我这里记录一个有效做法把四路画面的RawImage分辨率统一控制在512x512以下并在UI上缩小显示区域这样对浏览器来说压力会小一半以上。5.4 重新播放时出現错乱多路画面串流这个现象比较隐蔽表现为A摄像头画面显示在B摄像头的窗口上。原因主要是播放器的实例复用逻辑没有做彻底的清理。在海康连接断开重连时AVProVideo内部如果复用了MediaPlayer对象而新打开的流尚未完成初始化上一路残留的纹理数据仍在渲染。解决方法是每次重连前先强制关闭旧播放器并等待状态切换完成而不是直接打开新的地址。代码如下这个顺序至关重要。private IEnumerator SafeRestartPlayer(string newUrl) { _mediaPlayer.Stop(); yield return null; // 等待一帧确保播放器完全停止 _mediaPlayer.OpenMedia(new MediaPath(newUrl, PathType.Absolute), true); }5.5 部署到服务器后画模糊但本地正常WebGL构建包部署到服务器后出现画面分辨率变低的现象往往不是代码问题而是你在Unity里使用了Quality Settings的默认级别。WebGL的默认质量级别在较低档位时纹理分辨率会被固定压缩视频纹理自然也会跟着变糊。在Project Settings里把WebGL的Quality Level调整为Ultra或者手动关闭纹理缩小相关的选项重新构建后画面就会恢复成原始清晰度。6. 一个进阶技巧用自定义UI叠加层给监控画面加时间戳与报警标记画面能稳定播放后下一个需求往往是在画面上叠加业务信息。最常见的需求是时间戳、摄像头名称、设备状态标签。这个demo里提供了一个轻量级的UI叠加方案不侵入AVProVideo的渲染管线只是利用Unity的ScreenSpace-Overlay模式叠加在RawImage之上。在Canvas下新建一个空对象挂上StatusOverlay组件。这个组件专门负责接收外部通信传入的报警信号然后在对应摄像头的RawImage上绘制一个红色闪烁边框。核心逻辑是一帧一帧地检测报警状态刷新边框颜色和透明度。public void SetAlarmState(string cameraId, bool isAlarm) { CameraItem item FindCameraItemById(cameraId); if (item ! null) { item.alarmBorder.enabled isAlarm; } } void Update() { // 报警边框闪烁控制每0.5秒切换一次透明度 if (Time.time - _lastBlink 0.5f) { _isBlinkOn !_isBlinkOn; _lastBlink Time.time; foreach (var item in _cameraItems) { if (item.alarmBorder.enabled) { item.alarmBorder.color _isBlinkOn ? _alarmColor : _transparentColor; } } } }这个叠加层的时间戳显示我习惯用系统时间每帧刷新而不是用协程因为协程在WebGL上可能受到帧率波动的影响导致时间跳动不均匀。每帧刷新在UI文本上会有微小的性能开销但监控场景下通常只有少量文本完全在可接受范围内。实际项目中这套UI叠加方案还能扩展到电子围栏检测、动检区域标记等更复杂的场景。因为AVProVideo输出的纹理是RawImage你完全可以把业务绘制的纹理再叠加一层上去形成多层画面合成而不仅仅局限于文本标签。多路摄像头统一管理还有一个值得养成的习惯把每个摄像头的IP、登录账号、摆放位置、通道编号都维护在一个配置表中运行时逐项读取生成对应的播放器实例。而不是每个摄像头单独写一个脚本挂载那样维护成本高且容易错。从那次把整个WebGL海康监控项目交付后我每次再做类似接入都会强制走一遍检查清单确认摄像头编码是H264、确认网关开启CORS、确认WebGL内存充足、确认子码流和主码流的选择符合页面用途。这套流程以前让我翻了太多车现在已经成为固定习惯。希望这份demo和这篇笔记能帮你少走同样的一段弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站