拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Unity WebGL集成海康监控流:AVPro Video + HLS转码 + XCharts数据叠加实战

1. 项目概述当Unity WebGL遇上海康监控流最近在做一个工业数字孪生的项目客户要求在Web端的三维场景里实时查看部署在现场的海康威视监控画面。这个需求听起来挺直接不就是播个视频流嘛但真动起手来发现坑是一个接一个。核心挑战在于Unity的WebGL平台限制很多而海康监控通常提供的是基于HTTP-FLV或RTSP的流这些协议在浏览器环境里基本没法直接用。最终我们确定的方案是Unity WebGL AVPro Video插件 海康M3U8转码流 XCharts图表叠加。简单说就是在Unity里用AVPro Video播放经过转码的M3U8流同时用XCharts在视频画面上叠加一些实时数据图表比如设备温度、转速等。这个组合拳解决了几个关键问题让Unity WebGL能播监控视频、让海康流能在浏览器里跑、还能实现数据与视频画面的融合展示。整个过程涉及流媒体服务搭建、Unity插件配置、跨域问题处理、性能优化等一系列实操细节。如果你也在做类似的事情比如智慧园区、安防监控、远程巡检的Web三维可视化那这篇从踩坑到填坑的实录应该能帮你省下不少时间。2. 技术选型与架构设计思路为什么是这几个技术点的组合这背后是一连串的妥协与最优解。2.1 为什么是Unity WebGL项目本身是一个复杂的3D数字孪生场景包含了大量的设备模型、动画和交互逻辑。用Three.js或Babylon.js从头开发在有限的时间和团队技术栈背景下成本太高。Unity强大的编辑器、成熟的资源管线、以及我们团队已有的Unity开发经验使得它成为快速构建复杂3D Web应用的首选。WebGL作为Unity的发布目标能让用户通过浏览器直接访问无需安装任何客户端便捷性无可替代。2.2 为什么是AVPro VideoUnity自带的Video Player组件在WebGL平台功能孱弱支持的格式有限对直播流的支持几乎为零性能也不尽如人意。AVPro Video是Unity社区公认最强大的视频播放插件尤其在WebGL平台它通过一系列“黑科技”实现了更好的兼容性和性能。底层渲染优化它使用更底层的纹理更新方式减少CPU到GPU的数据拷贝在播放高分辨率视频时帧率更稳定。格式支持广泛对HLSM3U8协议有原生支持这是我们方案能成立的基础。API丰富提供了精确的播放控制、事件回调、多实例管理等功能方便我们实现播放、暂停、抓图等业务逻辑。2.3 为什么需要M3U8转码这是整个链路中最关键的一环。海康威视的摄像机或NVR网络硬盘录像机默认输出的流媒体协议通常是RTSP或HTTP-FLV。RTSP传统监控协议延迟低但需要专门的客户端或播放库支持现代浏览器已不再原生支持。HTTP-FLV基于HTTP长连接的流延迟也较低但同样需要浏览器端有Flash或特定的JavaScript播放器如flv.js支持兼容性和未来性堪忧。而HLSHTTP Live Streaming协议其播放列表文件就是.m3u8是苹果公司提出的标准现在已被所有现代浏览器原生支持。它的原理是将视频流切成一系列小的TS文件片段通过HTTP下载播放。虽然会引入一定的延迟通常10-30秒但对于非实时指挥、以查看和记录为主的监控场景是完全可接受的。因此我们需要一个流媒体服务器充当“翻译官”的角色将海康的RTSP/FLV流实时转封装或转码为HLSM3U8流。这样Unity WebGL中的AVPro Video就能像播放普通网络视频一样播放这个M3U8地址。2.4 为什么引入XCharts客户的需求不仅仅是看视频还要在视频画面上看到叠加的实时数据。例如在某个设备监控画面上浮动显示其当前的温度、压力、开关状态曲线图。Unity的UI系统UGUI虽然强大但制作动态、美观的图表比较繁琐。XCharts是一个功能强大且易于集成的Unity图表插件它支持折线图、柱状图、饼图等多种类型并且可以轻松地以Canvas渲染方式叠加在3D场景或UI层上。我们将XCharts的图表渲染到一个Render Texture上然后作为材质赋给一个位于视频前方的半透明UI面板就实现了数据图表与监控视频的融合展示。整体架构流程图文字描述海康摄像机输出原始视频流RTSP。流媒体服务器如SRS、Nginx-rtmp-module拉取RTSP流并转封装为HLS流生成.m3u8索引文件和.ts分片文件。用户浏览器访问Unity WebGL构建的页面。Unity WebGL应用中的AVPro Video组件向流媒体服务器请求index.m3u8文件。AVPro Video根据m3u8文件列表持续请求并播放.ts分片文件。Unity同时从后端数据接口获取设备的实时数据。XCharts插件根据这些数据动态生成图表并渲染叠加到视频画面上方。3. 核心模块实现与配置详解3.1 流媒体服务器搭建与配置这是整个系统的“咽喉要道”。我们选择使用SRSSimple RTMP Server因为它轻量、高效且对HLS的支持非常成熟。部署步骤环境准备准备一台Linux服务器Ubuntu 20.04 LTS具备公网IP或内网可访问的IP。安装SRSgit clone https://github.com/ossrs/srs.git cd srs/trunk ./configure --with-hls --with-ssl --with-ffmpeg make sudo make install关键编译选项--with-hls确保HLS功能被启用。配置SRS编辑配置文件/usr/local/srs/conf/srs.conf核心配置如下listen 1935; max_connections 1000; daemon on; srs_log_tank file; srs_log_file ./objs/srs.log; http_server { enabled on; listen 8080; dir ./objs/nginx/html; } vhost __defaultVhost__ { hls { enabled on; hls_path ./objs/nginx/html/hls; # HLS文件输出目录 hls_fragment 5; # 每个TS分片时长秒 hls_window 30; # HLS播放列表窗口长度秒 hls_cleanup on; # 自动清理过期分片 hls_dispose 0; hls_m3u8_file [app]/[stream].m3u8; hls_ts_file [app]/[stream]-[seq].ts; } }这个配置开启了HLS并指定了文件输出路径。http_server用于提供HTTP服务让浏览器能访问到生成的m3u8和ts文件。启动SRSsudo /usr/local/srs/objs/srs -c /usr/local/srs/conf/srs.conf推流测试使用FFmpeg或OBS将一路测试视频推送到SRS。ffmpeg -re -i test.mp4 -c copy -f flv rtmp://你的服务器IP:1935/live/stream1如果成功你可以在服务器目录./objs/nginx/html/hls/live下看到生成的stream1.m3u8和一系列.ts文件。通过浏览器访问http://你的服务器IP:8080/hls/live/stream1.m3u8应该能下载到m3u8文件。对接海康设备海康设备通常支持RTSP协议。你需要在海康摄像机的Web管理界面找到RTSP地址格式通常为rtsp://[username]:[password][ip]:[port]/h264/ch1/main/av_stream。 然后使用FFmpeg将海康的RTSP流拉取并推送到SRSffmpeg -rtsp_transport tcp -i rtsp://admin:你的密码摄像头IP:554/h264/ch1/main/av_stream -c copy -f flv rtmp://你的SRS服务器IP:1935/live/camera1这样海康的RTSP流就被“搬运”到了SRS并自动生成了HLS流。Unity端只需访问http://你的SRS服务器IP:8080/hls/live/camera1.m3u8即可。注意直接使用-c copy是转封装延迟低CPU消耗小但要求海康输出的编码格式通常是H.264能被HLS兼容。如果遇到播放问题可以尝试转码-c:v libx264 -preset ultrafast -c:a aac但会显著增加服务器CPU负载。3.2 Unity中AVPro Video的配置与使用导入与基础设置从Asset Store导入AVPro Video。在播放视频的GameObject上添加Media Player组件和Display uGUI组件用于在UI上显示。关键属性配置Media Player / Source选择Path或URL。我们使用URL填入上一步得到的M3U8地址例如http://192.168.1.100:8080/hls/live/camera1.m3u8。Media Player / Platform Options / WebGLVideo API: 选择WebGL。Use Texture Mipmaps: 通常关闭以节省内存。Use Low-Latency Mode: 如果对延迟有要求可以开启但可能影响稳定性。Display uGUI指定一个RawImage组件作为视频渲染的目标。播放控制脚本编写一个简单的控制脚本挂载到同一GameObject上。using RenderHeads.Media.AVProVideo; using UnityEngine; using UnityEngine.UI; public class HikvisionStreamPlayer : MonoBehaviour { public MediaPlayer mediaPlayer; public Button playButton; public Button pauseButton; public Text statusText; void Start() { if (mediaPlayer null) mediaPlayer GetComponentMediaPlayer(); // 监听事件 mediaPlayer.Events.AddListener(OnMediaPlayerEvent); // 初始状态 UpdateUI(); } void OnMediaPlayerEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode) { switch (et) { case MediaPlayerEvent.EventType.Started: statusText.text “播放中”; break; case MediaPlayerEvent.EventType.FirstFrameReady: statusText.text “第一帧就绪”; break; case MediaPlayerEvent.EventType.FinishedPlaying: statusText.text “播放结束”; break; case MediaPlayerEvent.EventType.Error: statusText.text “错误: ” errorCode.ToString(); Debug.LogError(“AVProVideo Error: ” errorCode); break; } UpdateUI(); } public void PlayStream() { if (!mediaPlayer.Control.IsPlaying()) { mediaPlayer.Control.Play(); } } public void PauseStream() { if (mediaPlayer.Control.IsPlaying()) { mediaPlayer.Control.Pause(); } } void UpdateUI() { playButton.interactable !mediaPlayer.Control.IsPlaying(); pauseButton.interactable mediaPlayer.Control.IsPlaying(); } }处理自动播放策略现代浏览器如Chrome禁止音频自动播放。如果监控流包含音频需要在用户首次交互如点击一个“启动监控”按钮后再调用mediaPlayer.Control.Play()。可以在按钮的OnClick事件中绑定PlayStream方法。3.3 XCharts图表叠加实现目标在视频画面上方创建一个半透明的图表层。创建图表Canvas在UI Canvas下创建一个新的Panel命名为“ChartOverlay”。设置其锚点为全屏拉伸Image组件的颜色为半透明如Alpha0或完全透明。添加图表在“ChartOverlay”下创建一个空GameObject添加LineChart组件以折线图为例。配置图表通过脚本或编辑器配置图表的基本样式主题、坐标轴、提示框等。关键是要设置图表的Rect Transform确定其在屏幕上的位置和大小例如固定在右上角。动态更新图表数据假设我们有一个后台WebSocket或HTTP接口能定时推送设备数据。using System.Collections; using UnityEngine; using XCharts; public class DataChartUpdater : MonoBehaviour { public LineChart lineChart; public string deviceId; public float updateInterval 2.0f; // 更新间隔 private Serie serie; private float timeCounter 0; private int maxDataPoints 50; // 图表上最多显示的数据点 void Start() { if (lineChart null) lineChart GetComponentLineChart(); // 获取或创建一条数据线 serie lineChart.EnsureSerie(“Temperature”, SerieType.Line); // 模拟或从网络获取初始数据 StartCoroutine(FetchDataPeriodically()); } IEnumerator FetchDataPeriodically() { while (true) { yield return new WaitForSeconds(updateInterval); // 1. 模拟或实际调用API获取数据 // float newTemperature Random.Range(20f, 40f); float newTemperature await GetTemperatureFromAPI(deviceId); // 2. 更新图表数据 serie.AddData(newTemperature); // 限制数据点数量保持图表清晰 if (serie.dataCount maxDataPoints) { serie.RemoveData(0); } // 3. 刷新图表 lineChart.RefreshChart(); } } async Taskfloat GetTemperatureFromAPI(string id) { // 这里实现实际的网络请求逻辑例如使用UnityWebRequest // 返回解析后的温度值 // 注意WebGL中需注意跨域问题后端需配置CORS return 25.0f; // 示例返回值 } }层级管理确保“ChartOverlay”的Canvas Sorting Order比视频显示的Canvas更高这样图表才能渲染在视频上方。3.4 WebGL构建与部署的关键设置在Unity Editor中运行正常不代表WebGL构建后也能正常。以下几个设置至关重要Player Settings / Resolution and PresentationRun In Background: 勾选。否则浏览器标签页失焦后视频和图表更新会停止。WebGL Template: 可以选择一个更简洁的模板或自定义模板以去除Unity Logo和加载进度条样式。Player Settings / Publishing SettingsCompression Format: 选择Brotli。它比Gzip压缩率更高能显著减少构建后文件的下载大小。Decompression Fallback: 勾选。确保在不支持Brotli的旧浏览器上也能回滚到Gzip。Enable Exceptions: 建议选择Full Without Stacktrace。在开发阶段可以选择Full以便调试发布时改为None以提升性能。处理跨域问题CORS这是WebGL访问外部视频流最常见的“拦路虎”。浏览器出于安全考虑禁止从http://yourgame.com加载http://your-stream-server.com的资源除非流服务器明确允许。解决方案必须在流媒体服务器SRS/Nginx上配置CORS头。以Nginx为例如果SRS前面用Nginx做反向代理location /hls/ { add_header Access-Control-Allow-Origin *; # 生产环境应替换为具体的域名如 http://yourgame.com add_header Access-Control-Allow-Methods GET, OPTIONS; add_header Access-Control-Allow-Headers Range; # 处理预检请求 if ($request_method OPTIONS) { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET, OPTIONS; add_header Access-Control-Allow-Headers Range; add_header Access-Control-Max-Age 1728000; add_header Content-Type text/plain charsetUTF-8; add_header Content-Length 0; return 204; } # ... 其他配置如alias指向SRS的hls目录 }配置后浏览器在请求M3U8和TS文件时服务器会返回允许跨域的头部WebGL应用才能成功加载视频流。构建与部署使用Unity进行WebGL构建会生成一个包含index.html、.js和.data等文件的文件夹。将这个文件夹整个上传到你的Web服务器如Nginx、Apache即可。确保你的Web服务器也正确配置了.m3u8和.ts文件的MIME类型。4. 性能优化与内存管理在WebGL环境下性能和内存是稀缺资源必须精打细算。视频流参数优化分辨率与码率不要盲目追求1080p或4K。根据实际显示区域的大小选择合适的分辨率。例如在网页中一个800x600的窗口播放720p1280x720的流已经足够清晰码率可以控制在1-2 Mbps能大幅减轻网络和解码压力。关键帧间隔GOP在转码时如果使用转码可以适当减小GOP大小如2秒这能降低切换流或随机seek时的延迟但会轻微增加文件体积。对于监控流2-4秒是比较平衡的选择。AVPro Video设置优化Buffer Size在Media Player的WebGL平台选项里可以调整Buffer Size。较小的缓冲区如0.5秒能降低延迟但网络波动时更容易卡顿较大的缓冲区如2秒更平滑但延迟增高。根据网络状况调整。禁用非必要功能如果不需要音频确保视频流本身不包含音频轨或者在AVPro Video中关闭音频播放可以节省处理资源。及时销毁当不再需要播放某个监控流时务必调用mediaPlayer.Control.Stop()和mediaPlayer.CloseMedia()并销毁MediaPlayer GameObject以释放其占用的纹理内存和网络连接。XCharts优化数据量限制如上文代码所示限制图表上显示的最大数据点数量如最近50个点。历史数据可以通过其他方式查看实时图表只展示最新趋势。刷新频率不要每帧都刷新图表。根据数据变化的频率以合理的间隔如1-5秒更新一次数据并刷新图表即可。可以使用Coroutine或InvokeRepeating。简化图表样式减少不必要的视觉元素如网格线密度、数据点标记符号等。复杂的样式会增加Canvas的绘制开销。Unity WebGL通用优化减少Draw Call合并UI元素使用图集。确保视频显示区域和图表区域的UI层级不要过于复杂。内存预警WebGL应用的内存有硬性限制通常为256MB或512MB取决于浏览器和系统。在Unity Profiler开发时和浏览器开发者工具的内存面板中密切关注总内存使用量。纹理视频纹理是大头和JavaScript堆内存是主要监控对象。代码分包如果应用很大考虑使用Unity的Asset Bundle或Addressables系统实现按需加载减少初始加载时间。5. 常见问题与故障排查实录在实际开发和部署中我遇到了不少问题这里把典型的几个和解决方法记录下来。问题1视频能加载但一直黑屏控制台没有报错。排查首先检查浏览器控制台的网络Network标签页确认.m3u8和.ts文件是否被成功请求并返回了200状态码。如果文件能正常下载问题可能出在解码或渲染环节。解决检查CORS这是最常见的原因。确保流服务器正确返回了Access-Control-Allow-Origin头。可以在Network标签里查看响应头。检查M3U8内容打开下载的.m3u8文件查看里面的#EXT-X-STREAM-INF或直接列出的.ts文件路径是否正确是否是相对路径或完整的可访问URL。检查视频编码AVPro Video WebGL对H.264编码的Baseline或Main Profile支持最好。确保海康摄像头输出或转码后的流是H.264编码。可以用FFmpeg命令检查ffmpeg -i input.ts查看输出信息中的Video: h264。尝试转码如果之前是-c copy转封装尝试改用-c:v libx264 -preset ultrafast进行转码排除编码兼容性问题。问题2播放延迟非常大超过1分钟。排查HLS本身有延迟但过大延迟通常与切片配置有关。解决调整HLS分片参数在SRS配置中减少hls_fragment例如从10秒改为2秒和hls_window例如从60秒改为10秒。这会让分片更小、列表更短从而降低端到端延迟。但注意过小的分片会增加服务器和客户端的请求开销。启用低延迟模式在AVPro Video的WebGL设置中勾选Use Low-Latency Mode。这个模式会尝试更积极地请求最新的分片。检查网络确保流媒体服务器与Unity WebGL客户端之间的网络通畅没有严重的带宽瓶颈或丢包。问题3在Chrome中运行正常但在某些浏览器如奇安信可信浏览器中报错“A WebGL context could not be created”。排查这是浏览器或系统环境对WebGL支持不完整或受限。解决启用WebGL在浏览器设置中搜索“硬件加速”并确保其开启。在浏览器地址栏输入chrome://flags或edge://flags搜索“WebGL”确保相关选项是“Enabled”。更新显卡驱动过旧或损坏的显卡驱动可能导致WebGL初始化失败。浏览器兼容性一些基于安全考虑的国产浏览器可能默认禁用或限制了WebGL。尝试使用主流的Chrome、Edge或Firefox最新版进行测试和作为兼容基准。对于必须使用的特定浏览器可能需要引导用户手动开启相关功能。问题4播放一段时间后内存持续增长最终页面崩溃。排查这是典型的内存泄漏。解决检查AVPro Video实例确保在切换视频源或关闭页面时正确调用CloseMedia()和销毁组件。AVPro Video在播放过程中会持续缓存视频数据。检查XCharts确保在销毁图表组件或父级GameObject时图表数据被清空。XCharts内部可能会缓存顶点数据。使用浏览器开发者工具利用Memory面板定期拍摄堆快照Heap Snapshot对比分析内存增长的对象类型定位泄漏源。重点关注MediaPlayer、Texture2D、ArrayBuffer等对象是否未被释放。问题5图表叠加导致整体帧率FPS下降明显。排查Canvas的频繁重绘是性能杀手。解决降低图表刷新率将数据更新和图表刷新的频率从每秒多次降低到每2-5秒一次。简化图表关闭阴影、渐变色等特效使用简单的线条和颜色。分离渲染如果条件允许可以考虑将图表渲染到一个离屏的、分辨率更低的Render Texture上然后显示这个纹理而不是让XCharts直接在主Canvas上高频重绘。使用性能分析工具在Unity Editor中或使用浏览器的Performance面板进行性能分析找到确切的耗时函数进行针对性优化。这个项目从技术选型到最终稳定运行是一个典型的工程化问题解决过程。没有银弹每一个环节都需要根据实际情况进行调整和优化。最深的体会是在WebGL这种受限环境下做实时流媒体应用“简单可靠”远比“技术先进”重要。优先选择广泛支持的标准如HLS在关键链路上如流媒体服务器做好监控和日志在客户端Unity做好异常处理和降级预案比如视频加载失败时显示占位图和错误信息才能保证最终的用户体验。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门