尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Unity WebGL集成海康摄像头:AVProVideo与流媒体代理实战

Unity WebGL集成海康摄像头:AVProVideo与流媒体代理实战 1. 项目概述与核心挑战最近在做一个智慧工厂的数字孪生项目客户要求在3D场景里实时看到车间关键工位的监控画面。设备用的是海康威视的摄像头而我们的孪生平台是基于Unity WebGL构建的。这个需求听起来简单不就是播个视频流嘛但真动起手来坑是一个接一个。最核心的问题在于Unity WebGL运行在浏览器的沙箱环境里它不能像桌面应用那样直接访问本地网络设备或调用系统API。海康摄像头提供的通常是RTSP或RTMP流这些协议在WebGL里根本不被原生支持。更头疼的是当你试图从Unity WebGL去请求摄像头所在的流媒体服务器时十有八九会撞上浏览器的跨域资源共享CORS策略这堵墙直接给你报个Access-Control-Allow-Origin错误画面一片黑。所以这个项目的核心命题就变成了如何在Unity WebGL环境中安全、稳定、低延迟地播放位于另一个域名或IP下的海康摄像头视频流经过一番折腾和选型我们最终敲定了以AVProVideo插件为核心搭配一个轻量级的流媒体转发与跨域代理服务的技术方案。这条路走通了效果还不错延迟能控制在1-2秒完全满足监控级需求。下面我就把整个实战过程包括技术选型的思考、具体的操作步骤、以及踩过的那些坑毫无保留地分享出来。2. 技术选型与方案设计思路2.1 为什么是AVProVideo面对WebGL播放视频的需求Unity开发者通常有几个选择原生的VideoPlayer组件、Unity WebRTC或者第三方插件如AVProVideo。原生VideoPlayer在WebGL平台它严重依赖浏览器自身的HTML5video标签能力。这意味着它只能播放浏览器原生支持的格式如MP4、WebM对于海康摄像头普遍输出的RTSP/RTMP/HLS流基本无能为力除非在服务器端先进行转码这又引入了额外的复杂性和延迟。Unity WebRTC这是一个强大的实时通信方案理论上可以实现超低延迟。但它架构复杂需要信令服务器、STUN/TURN服务器并且与海康摄像头的标准流媒体协议对接需要大量的定制开发工作对于“单纯播放监控流”这个需求来说杀鸡用牛刀了。AVProVideo这是我们的最终选择。它本质上是一个功能强大的本地插件集合针对不同平台Windows, macOS, iOS, Android做了深度优化。关键在于它的**“Windows/macOS等平台使用原生插件高效解码而在WebGL平台则自动回退到基于HTML5 Video的策略”**。这个“回退”机制正是我们需要的。我们不需要它在WebGL里做硬解码而是利用它统一的API和强大的兼容性让我们可以专注于解决“如何让HTML5 Video能播海康流”这个问题。简单说AVProVideo为我们提供了一个稳定、跨平台的视频播放API接口把平台差异的复杂性封装了起来。我们在WebGL上要做的就是为它准备好一个浏览器能播的、且解决了跨域问题的视频流地址。2.2 整体架构设计基于以上分析我们设计了如下架构[海康摄像头] --(RTSP流)-- [流媒体服务器/NVR] --(HTTP-FLV/HLS流)-- [跨域代理服务器] --(可跨域访问的流)-- [Unity WebGL (AVProVideo)]源端海康摄像头产生RTSP流。转码与封装使用流媒体服务器如SRS、Nginx-rtmp-module、或海康自带的iVMS-5200平台将RTSP流转码并封装为Web端友好的协议。HLS (m3u8ts切片) 和 HTTP-FLV是两种主流选择。HLS兼容性极好但延迟通常较高10秒以上HTTP-FLV延迟可以做到1-3秒更符合实时监控需求。跨域代理这是关键一环。流媒体服务器和Unity WebGL应用往往不在同一个域名下。我们需要一个简单的代理服务器它作为中间层从流媒体服务器拉流并给响应头加上Access-Control-Allow-Origin: *等CORS头部从而让Unity WebGL可以直接请求这个代理地址而不会跨域。客户端播放在Unity中使用AVProVideo组件将代理服务器提供的流地址如http://proxy-server/live/stream.flv赋值给它即可播放。这个架构清晰地将问题分解流媒体服务器解决协议兼容性问题代理服务器解决跨域安全问题AVProVideo解决Unity内的播放与控制问题。3. 核心组件部署与配置详解3.1 流媒体服务器搭建以SRS为例我们选择了SRSSimple RTMP Server因为它轻量、开源对HTTP-FLV支持非常好。安装Ubuntu为例:git clone -b 4.0release https://gitee.com/ossrs/srs.git cd srs/trunk ./configure make关键配置 (conf/http.flv.live.conf):listen 1935; max_connections 1000; daemon off; srs_log_tank console; http_server { enabled on; listen 8080; # HTTP服务端口 dir ./objs/nginx/html; } vhost __defaultVhost__ { http_remux { enabled on; mount [vhost]/[app]/[stream].flv; # 定义HTTP-FLV的访问路径 hstrs on; } }这个配置让SRS在1935端口接收RTMP推流很多工具可以将RTSP转RTMP同时在8080端口提供HTTP服务并将流以FLV格式通过HTTP方式分发出来。启动与推流:./objs/srs -c conf/http.flv.live.conf假设摄像头RTSP地址是rtsp://admin:password192.168.1.100:554/h264/ch1/main/av_stream你需要使用ffmpeg或OBS等工具将其转为RTMP推送到SRS。ffmpeg -rtsp_transport tcp -i rtsp://admin:password192.168.1.100:554/h264/ch1/main/av_stream -c copy -f flv rtmp://localhost:1935/live/stream1成功后你就可以通过http://你的服务器IP:8080/live/stream1.flv来访问这个FLV流了。用VLC等播放器测试一下能播就成功了一半。注意直接暴露RTSP流地址给ffmpeg可能会遇到海康摄像头的流格式问题。如果遇到Unable to find a suitable output format for rtmp://...错误可以尝试指定具体的编码格式例如-vcodec copy -acodec aac。更稳妥的方法是使用海康SDK或专门的RTSP转RTMP网关工具。3.2 跨域代理服务器实现流媒体服务器在A域名或IP你的WebGL应用部署在B域名。浏览器会阻止这种跨域请求。我们需要一个代理。使用Node.js Express实现一个简单的CORS代理初始化项目:mkdir stream-proxy cd stream-proxy npm init -y npm install express http-proxy-middleware cors创建server.js:const express require(express); const { createProxyMiddleware } require(http-proxy-middleware); const cors require(cors); const app express(); const PORT 3000; // 代理服务器端口 // 启用CORS允许所有来源生产环境应指定具体域名 app.use(cors()); // 定义代理规则 const flvProxy createProxyMiddleware({ target: http://你的流媒体服务器IP:8080, // 指向SRS服务器 changeOrigin: true, onProxyRes: function (proxyRes, req, res) { // 关键确保响应头包含CORS允许信息 proxyRes.headers[Access-Control-Allow-Origin] *; proxyRes.headers[Access-Control-Allow-Methods] GET, OPTIONS; proxyRes.headers[Access-Control-Allow-Headers] Origin, X-Requested-With, Content-Type, Accept; // 对于FLV流确保Content-Type正确防止浏览器MIME类型错误 if (req.url.endsWith(.flv)) { proxyRes.headers[Content-Type] video/x-flv; } }, // 可选日志 // logger: console }); // 将所有以 /proxy/ 开头的请求转发到流媒体服务器 app.use(/proxy, flvProxy); // 健康检查端点 app.get(/health, (req, res) { res.send(Proxy server is running); }); app.listen(PORT, () { console.log(CORS proxy server running at http://localhost:${PORT}); console.log(Example FLV stream: http://localhost:${PORT}/proxy/live/stream1.flv); });运行代理:node server.js现在你的Unity WebGL应用应该去请求http://代理服务器IP:3000/proxy/live/stream1.flv而不是直接请求流媒体服务器。这个代理地址是允许跨域访问的。实操心得在生产环境中千万不要用Access-Control-Allow-Origin: *。应该用app.use(cors({ origin: https://你的WebGL站点域名 }))来精确控制。另外这个代理服务器非常简易没有认证、限流、负载均衡。如果流量大或安全性要求高可以考虑用Nginx来做反向代理和CORS配置性能更好配置也更灵活。4. Unity项目集成与AVProVideo配置4.1 AVProVideo导入与基本设置导入插件从Asset Store购买并导入AVProVideo。导入后检查Tools/AVPro Video/Run Auto Configure让插件自动根据你的Unity版本和平台进行一些基础配置。创建播放器在场景中创建一个UI RawImage然后为其添加AVPro Video组件。或者你也可以直接从GameObject - AVPro Video - Media Player菜单创建一个完整的播放器预制体。关键组件说明Media Player核心组件负责加载、解码、播放控制。Display uGui将视频画面渲染到指定的uGUI RawImage上。Audio Output处理音频输出监控流通常无音频可忽略。4.2 WebGL平台专项配置这是最容易出错的地方。Player Settings:Resolution and Presentation: 取消勾选Run In Background对于监控看板通常需要一直运行。Publishing Settings:Compression Format: 建议选择Brotli以获得更小的包体和更快的加载速度。Data Caching: 启用提升重复访问体验。AVProVideo针对WebGL的设置:在Media Player组件的Platform Options中找到WebGL选项卡。Video API: 选择HTML5 Video。这是必须的WebGL平台只能用它。Use Texture Mipmaps: 通常关闭除非你的视频纹理需要缩小的LOD。Allow Background Playback: 勾选。确保浏览器标签页非激活时视频也能继续播放对于监控场景很重要。4.3 编写播放控制脚本创建一个C#脚本挂载到播放器对象上。using UnityEngine; using RenderHeads.Media.AVProVideo; public class HikvisionStreamPlayer : MonoBehaviour { public MediaPlayer mediaPlayer; public string streamUrl http://你的代理服务器:3000/proxy/live/stream1.flv; void Start() { if (mediaPlayer null) { mediaPlayer GetComponentMediaPlayer(); } if (mediaPlayer ! null) { // 监听事件便于调试 mediaPlayer.Events.AddListener(OnMediaPlayerEvent); // 设置播放源 // 注意WebGL上AVProVideo会将这个URL直接传递给HTML5 Video元素 mediaPlayer.OpenMedia(MediaPathType.AbsolutePathOrURL, streamUrl, true); } else { Debug.LogError(MediaPlayer component not found!); } } void OnMediaPlayerEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode) { switch (et) { case MediaPlayerEvent.EventType.Started: Debug.Log(视频开始播放); break; case MediaPlayerEvent.EventType.FirstFrameReady: Debug.Log(第一帧准备就绪); break; case MediaPlayerEvent.EventType.FinishedPlaying: Debug.Log(视频播放结束); // 如果是直播流通常不会结束除非出错。可以在这里尝试重连。 break; case MediaPlayerEvent.EventType.Error: Debug.LogError($播放错误: {errorCode}); // 处理错误例如网络断开、流地址错误等 break; } } // 提供给UI按钮的方法 public void PlayStream(string url) { if (!string.IsNullOrEmpty(url)) { streamUrl url; } mediaPlayer.OpenMedia(MediaPathType.AbsolutePathOrURL, streamUrl, true); } public void StopStream() { mediaPlayer.Stop(); } void OnDestroy() { if (mediaPlayer ! null) { mediaPlayer.Events.RemoveListener(OnMediaPlayerEvent); mediaPlayer.Stop(); } } }脚本要点MediaPathType.AbsolutePathOrURL因为我们的流是一个完整的HTTP URL。OpenMedia的第三个参数autoPlay设为true打开后自动播放。事件监听OnMediaPlayerEvent至关重要它是你了解播放状态加载中、开始、错误的唯一途径。4.4 处理WebGL特有的播放问题在WebGL平台视频播放受浏览器控制权限更高。需要注意自动播放策略现代浏览器Chrome, Safari等都有严格的自动播放策略。通常要求视频必须是静音的muted或者用户之前与页面有过交互如点击才能自动播放。解决方案在Start()中打开视频前可以先尝试将mediaPlayer.Control.MuteAudio(true)。或者设计一个“点击以启动监控”的按钮在用户的点击事件回调里再调用PlayStream。格式支持虽然我们用了HTTP-FLV但浏览器原生并不支持.flv。这里AVProVideo和流媒体服务器做了“欺骗”。SRS服务器在分发FLV流时响应的Content-Type是video/x-flv而AVProVideo在WebGL模式下内部可能是通过MediaSource ExtensionsMSE来喂流给video标签的MSE可以支持FLV封装。如果遇到播放失败检查代理服务器是否正确设置了Content-Type头见前面Node.js代理代码。5. 部署、优化与问题排查实录5.1 完整部署流程服务器端在拥有公网IP或内网可达的服务器上部署SRS或同类流媒体服务。确保摄像头RTSP流能稳定推送到SRS并通过HTTP-FLV地址能正常播放。在同一台或另一台服务器上部署Node.js跨域代理服务。配置防火墙开放SRS的1935RTMP、8080HTTP端口以及Node.js代理服务的端口如3000。重要生产环境务必为代理服务配置HTTPS。因为WebGL应用如果部署在HTTPS站点它只能请求HTTPS资源。可以使用Let‘s Encrypt免费证书。Unity端在脚本中将streamUrl更新为你的代理服务器HTTPS地址例如https://proxy.yourdomain.com/proxy/live/stream1.flv。进行WebGL构建File - Build Settings选择WebGL平台点击Build。将构建生成的Build文件夹和TemplateData文件夹上传到你的Web服务器如Nginx, Apache。Web服务器配置确保你的Web服务器能正确托管Unity WebGL文件通常需要配置正确的MIME类型例如.data为application/octet-stream.wasm为application/wasm。如果你的WebGL应用也涉及跨域请求其他API同样需要在Web服务器配置CORS头部。5.2 性能优化建议降低延迟协议选择HTTP-FLV延迟1-3s通常低于HLS10s。优先使用FLV。SRS配置调整SRS的gop_cache和queue_size参数降低缓存。但注意缓存太小可能导致卡顿。网络链路确保流媒体服务器、代理服务器、用户浏览器之间的网络延迟尽可能低。尽量让它们处于同一个地域或内网。降低带宽与负载视频参数在摄像头端或ffmpeg转码时降低视频码率、分辨率和帧率。例如从1080P 25fps降至720P 15fps带宽消耗会大幅下降。CDN如果用户分布广可以考虑使用支持FLV的CDN来分发视频流减轻源站压力。按需播放在数字孪生场景中可以设计为只有当用户视角靠近某个监控点位时才加载和播放对应的视频流离开时则停止。5.3 常见问题与排查技巧下面是一个快速排查表涵盖了从流源头到Unity播放的整个链条问题现象可能原因排查步骤Unity WebGL中视频黑屏无画面无错误1. 自动播放策略阻止2. 流地址错误或无法访问3. CORS问题未解决1. 打开浏览器开发者工具F12- Console查看是否有自动播放策略的警告。尝试在代码中先静音或通过用户交互触发播放。2. 在浏览器新标签页直接输入代理后的流地址如https://proxy.../stream.flv用VLC或PotPlayer测试能否播放。如果不能问题在代理或上游。3. 在开发者工具 - Network标签页找到对视频流的请求查看Response Headers是否有Access-Control-Allow-Origin: *或你的域名。没有就是代理服务器配置问题。控制台报错Cross-Origin Request Blocked跨域请求被浏览器拦截确认代理服务器的响应头包含正确的CORS头部。确保代理服务器本身能正常访问上游流。视频能播放但延迟极高10秒1. 使用了HLS协议2. 流媒体服务器或网络缓存过大1. 确认流地址是.flv而非.m3u8。如果是HLS考虑切换为HTTP-FLV。2. 检查SRS配置尝试减小gop_cache等缓存参数。播放几秒后卡住或断开1. 网络不稳定2. 流本身中断摄像头离线、推流工具停止3. 浏览器标签页休眠1. 检查网络。2. 在服务器上查看ffmpeg推流进程是否还在运行。查看SRS日志。3. 在Unity Player Settings和AVProVideo设置中确保允许后台播放。检查浏览器是否设置了“休眠不活动的标签页以节省电量”。画面绿屏或花屏视频编码格式或封装与浏览器/AVProVideo不兼容1. 尝试在ffmpeg推流时指定明确的编码格式如-vcodec libx264 -acodec aac。2. 尝试将视频编码统一为H.264 Baseline/Main Profile这是兼容性最广的格式。3. 用专业工具如ffprobe分析原始RTSP流和SRS输出的FLV流的编码信息是否一致。MediaPlayer报告ErrorCode.LoadFailed加载失败通常是网络或地址问题1. 检查streamUrl字符串是否正确是否有拼写错误。2. 检查代理服务器是否在运行端口是否开放。3. 在浏览器中直接访问该URL看是否返回数据可能是一个播放器或文件下载。WebGL构建后视频组件不显示或报JS错误1. AVProVideo的WebGL插件未正确包含在构建中2. Unity版本与AVProVideo版本不兼容1. 检查构建日志确认AVProVideo的相关文件通常在Assets/Plugins/WebGL下被包含。2. 查阅AVProVideo官方文档确认其支持的Unity版本。尝试创建一个最简场景只放AVProVideo播放器进行测试构建。一个关键的调试技巧充分利用浏览器开发者工具。Console看错误和警告Network看视频流请求的状态码、响应头、数据接收是否持续。Media面板Chrome可以查看视频缓冲、解码状态。这些信息是定位WebGL端问题最直接的依据。6. 进阶考量与扩展方向当基础播放搞定后可以考虑以下方向来提升项目的专业度和用户体验多摄像头管理与切换数字孪生场景往往有几十上百个摄像头。可以设计一个数据驱动系统为每个监控点位在3D空间中放置一个“视频播放器”预制体根据用户视角或选择动态加载和卸载流避免同时播放所有流造成的性能灾难。使用对象池管理MediaPlayer实例。流媒体服务器高可用单点SRS有风险。可以考虑SRS集群或者采用更企业级的方案如ZLMediaKit、Monibuca等它们对大规模并发和集群化支持更好。安全性加固流地址鉴权不要在代理服务器上暴露无鉴权的流地址。可以在代理逻辑里加入Token验证Unity端在请求流时携带由业务后端颁发的临时Token。HTTPS全链路HTTPS包括摄像头RTSPover TLS、流媒体服务器、代理服务器、WebGL站点。限制访问IP在流媒体服务器和代理服务器防火墙层面限制只允许业务服务器或特定IP段访问。状态监控与告警在代理服务器或业务后端增加监控检测每个视频流是否正常。当流中断时可以在3D场景中将对应的监控图标变红并触发告警通知。录制与回放实时监控往往需要联动录像回放。可以在流媒体服务器端开启录制功能将FLV流同时写入文件。然后在Unity中设计一个时间轴界面通过请求不同的录制文件片段地址来实现回放。AVProVideo也支持播放本地文件或网络视频文件可以复用。这条路走下来你会发现Unity WebGL播放海康摄像头直播流难点不在于Unity本身而在于流媒体服务链路的搭建和Web安全策略的规避。AVProVideo扮演了一个优秀的“桥梁”让我们能用一套接近原生的代码去控制视频而把协议转换和跨域这些“脏活累活”交给服务器端去处理。这种前后端分离、各司其职的架构也使得系统更清晰、更易于维护和扩展。希望这篇详尽的实战记录能帮你绕过我踩过的那些坑顺利地把实时视频流整合到你的数字孪生项目中。
返回列表