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

资讯详情

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

Unity WebGL数字孪生项目实战:海康摄像头RTSP流实时播放与跨域解决方案

Unity WebGL数字孪生项目实战:海康摄像头RTSP流实时播放与跨域解决方案 1. 项目概述当数字孪生遇上WebGL视频流最近在做一个智慧工厂的数字孪生项目客户要求在WebGL平台上实时展示厂区里几十个海康威视摄像头的监控画面。这个需求听起来很常规但真动起手来坑是一个接一个。Unity的WebGL平台对网络请求有严格的跨域限制而海康摄像头默认的RTSP流在浏览器里根本无法直接播放。一开始尝试用Unity自带的Video Player发现它在WebGL上对主流流媒体协议的支持几乎为零。经过一番折腾和选型最终用AVProVideo插件配合一个轻量的流媒体服务器中转方案才算把这条路走通。这篇文章我就把整个实战过程从插件选型、环境搭建、核心配置到最头疼的跨域问题解决方案毫无保留地梳理一遍。如果你也在做类似的事情比如用Unity做数字孪生、智慧园区、安防监控的Web端展示需要接入海康、大华等厂商的摄像头那这篇内容应该能帮你省下不少摸索的时间。2. 核心方案选型与思路拆解2.1 为什么是AVProVideo面对在Unity WebGL中播放网络视频流的需求我们首先得明白平台限制。WebGL本质上是将C#代码编译成WebAssembly在浏览器中运行其网络行为受浏览器安全策略同源策略的严格约束。Unity内置的Video Player组件在桌面端和移动端表现尚可但其在WebGL后端主要依赖浏览器的HTML5video标签。问题在于大多数安防摄像头输出的RTSP、RTMP等流媒体协议并非HTML5标准原生支持。直接播放RTSP流在绝大多数现代浏览器中都是行不通的。这时就需要专业的视频插件。市面上主流的有AVPro Video和Unity Render StreamingURS。URS更偏向于低延迟的交互式流媒体比如云游戏、远程桌面架构相对较重需要搭配独立的信令服务器和流媒体服务器如WebRTC。对于数字孪生中“观看”监控视频这种场景对延迟的要求是秒级可接受但需要高稳定性、多路同时播放并且最好能复用现有的摄像头网络流。AVPro Video的优势就凸显出来了。首先它针对不同平台提供了强大的后端支持。在WebGL平台它可以使用其“Media Foundation”后端虽然名字叫这个但在WebGL上它巧妙地进行了转换或者更常用的“HTML5 Audio/Video”后端。后者本质上是在Unity中创建了一个HTML5视频元素的包装器但AVPro Video为其增加了大量的控制接口、事件回调以及性能优化。更重要的是AVPro Video对“渐进式下载”如直接链接到.mp4文件和“自适应流”如HLS、MPEG-DASH提供了良好的支持。这为我们设计解决方案提供了关键思路将摄像头不可直接播放的RTSP流转换为WebGL可接受的HLS或MPEG-DASH流。注意直接让AVPro Video在WebGL上播放RTSP地址是无效的。所有方案的核心第一步都是进行流媒体协议的转换。2.2 整体架构设计推流、转流与播放基于以上分析一个可行的技术架构浮出水面。整个数据流需要经历三个环节采集与推流端海康威视摄像头。它通过ONVIF或SDK被配置将原始的RTSP流推送出来。例如一个典型的RTSP地址格式为rtsp://admin:password192.168.1.100:554/h264/ch1/main/av_stream。流媒体转换与分发服务器关键枢纽这是解决跨域和协议转换的核心。我们需要一个服务器程序它需要完成两件大事协议转换接收来自摄像头的RTSP流并将其实时转封装或转码为Web友好的流媒体格式如HLS.m3u8 .ts切片或MPEG-DASH.mpd .m4s切片。FFmpeg是完成这项任务的不二之选。HTTP服务与跨域处理作为一个HTTP服务器对外提供转换后的HLS/DASH流地址。同时必须在HTTP响应头中正确配置CORS跨源资源共享策略允许来自Unity WebGL构建后所部署的域名进行访问。Unity WebGL客户端使用AVPro Video插件创建一个MediaPlayer对象。将流媒体服务器提供的HLS/DASH流地址例如http://你的服务器地址:端口/live/camera1.m3u8赋值给MediaPlayer。配置AVPro Video使用“HTML5”后端并将其渲染到UGUI的RawImage或一个自定义的材质上集成到数字孪生场景的监控屏幕模型中。这个架构清晰地将问题分解。Unity和AVPro Video只负责播放“标准、安全”的HTTP流而所有“脏活累活”协议转换、跨域支持都交给了中间的流媒体服务器。接下来我们就深入每个环节的实操细节。3. 实战环境搭建与核心配置3.1 流媒体服务器搭建Nginx FFmpeg方案我们选择Nginx nginx-rtmp-module或更现代的nginx-http-flv-module搭配FFmpeg作为服务器方案。它轻量、高效且CORS配置方便。步骤1准备带有FFmpeg的服务器你可以使用一台Linux云服务器如Ubuntu 20.04或者在本机Windows/Mac上用于开发测试。确保系统已安装FFmpeg。# Ubuntu 示例 sudo apt update sudo apt install ffmpeg步骤2编译安装Nginx与RTMP/HTTP-FLV模块这里以nginx-http-flv-module为例它支持RTMP、HTTP-FLV和HLS功能更全面。# 安装依赖 sudo apt install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev openssl libssl-dev # 下载Nginx和模块源码 wget http://nginx.org/download/nginx-1.22.1.tar.gz tar -zxvf nginx-1.22.1.tar.gz git clone https://github.com/winshining/nginx-http-flv-module.git # 编译安装 cd nginx-1.22.1 ./configure --add-module../nginx-http-flv-module --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module make sudo make install步骤3配置Nginx关键编辑Nginx配置文件通常位于/usr/local/nginx/conf/nginx.conf。在http块内添加以下配置http { ... # 其他原有配置 server { listen 8080; # HTTP服务端口 server_name localhost; # 核心添加CORS头部允许所有来源生产环境应指定具体域名 add_header Access-Control-Allow-Origin * always; add_header Access-Control-Allow-Methods GET, OPTIONS always; add_header Access-Control-Allow-Headers Range, Content-Type always; # 非常重要处理浏览器预检请求 if ($request_method OPTIONS) { return 204; } # HLS流访问位置 location /hls { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /tmp; # HLS切片文件存放目录 add_header Cache-Control no-cache; # 禁用缓存确保实时性 } # 可选HTTP-FLV流访问位置延迟比HLS低 location /live { flv_live on; chunked_transfer_encoding on; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true; } } } # RTMP推流入口配置在http块外 rtmp { server { listen 1935; # 标准RTMP端口 chunk_size 4096; application live { live on; record off; # 将推送的RTMP流转推为HLS exec_pull ffmpeg -i rtsp://admin:password摄像头IP:554/流路径 -c:v copy -c:a aac -f flv rtmp://localhost:1935/live/camera1; # 或者使用exec_static在启动时拉流 exec_static ffmpeg -i rtsp://admin:password摄像头IP:554/流路径 -c copy -f flv -flvflags no_duration_filesize rtmp://localhost:1935/live/camera1; } } }这个配置做了几件事在8080端口开启了HTTP服务配置了/hls目录用于访问HLS文件最重要的是通过add_header指令设置了CORS头部允许任何来源*的GET请求这对于WebGL访问至关重要。OPTIONS请求返回204是为了正确处理浏览器的预检请求。步骤4启动服务与推流测试启动Nginx后我们需要让FFmpeg工作起来。上述配置中的exec_static指令会在Nginx启动时自动执行FFmpeg命令拉取RTSP流并推送到本地的RTMP服务。你也可以手动在命令行执行ffmpeg -rtsp_transport tcp -i rtsp://admin:password192.168.1.100:554/Streaming/Channels/101 -c:v copy -c:a aac -f flv rtmp://localhost:1935/live/camera1参数解释-rtsp_transport tcp强制使用TCP模式拉取RTSP流比UDP更稳定尤其适合网络复杂的场景。-c:v copy -c:a aac视频流直接复制不重新编码节省CPU音频流转码为AAC格式FLV容器需要。-f flv指定输出格式为FLV。如果一切正常访问http://你的服务器IP:8080/hls/camera1.m3u8应该能下载到一个HLS的索引文件。用VLC等播放器打开这个URL也能正常播放说明流媒体服务器端工作正常。3.2 Unity与AVPro Video基础配置步骤1导入AVPro Video插件从Asset Store购买并导入AVPro Video到你的Unity项目建议使用较新版本如2.x。导入后检查Window - AVPro Video - About确认版本。步骤2创建播放器与UI在场景中创建一个UI RawImage用于显示视频。在RawImage所在的GameObject上添加Media Player组件。创建一个新的Render To Texture资产Assets窗口右键创建将其拖拽到Media Player组件的Output字段。将这个Render To Texture资产拖拽到RawImage的Texture字段上。在Media Player组件的Location属性选择Path或Absolute Path并将流媒体服务器生成的HLS地址填入。例如http://192.168.1.50:8080/hls/camera1.m3u8。步骤3关键播放器设置Auto Start勾选后场景运行自动播放。Platform Options-Windows/Mac/Linux-Video API选择Media Foundation桌面端或DirectShow。Platform Options-WebGL-Video API这是重点必须选择HTML5。AVPro Video在WebGL平台通过此后端调用浏览器能力。Loop根据监控视频需求设置是否循环。Audio Output如果流包含音频且需要播放选择Audio。步骤4WebGL播放器设置Unity Build Settings在File - Build Settings中选择WebGL平台点击Player Settings。Resolution and Presentation取消勾选Run In Background避免标签页切换后视频卡顿。Publishing SettingsCompression Format选择Disabled可以加快构建和加载速度但包体会变大。Data Caching根据需求启用。最关键的一步在WebGL Template下拉框中不要使用默认的Default。选择AVProVideo插件提供的模板通常名为AVProVideo或Minimal。这个模板已经内置了正确处理视频播放和CORS请求的必要JavaScript代码。这是避免“跨域”问题在客户端侧的最后一道保险。配置好后可以先在编辑器模式下测试使用桌面端API确保播放逻辑正确。然后进行WebGL构建并部署到与流媒体服务器不同域名或端口的Web服务器上以真实测试跨域场景。4. 跨域问题深度剖析与解决方案即使按照上述步骤操作在浏览器中打开部署的WebGL页面视频很可能仍然无法播放浏览器控制台会报错“Access to XMLHttpRequest at ‘http://... .m3u8‘ from origin ‘http://...‘ has been blocked by CORS policy”。这就是令人头疼的跨域问题。4.1 理解CORS与WebGL的网络请求CORS是一种安全机制。当你的WebGL页面部署在https://your-app.com而视频流来自http://your-stream-server:8080浏览器会认为这是“跨源”请求。对于涉及“凭证”如某些自定义头或非简单请求如带有Range头的视频分段请求浏览器会先发送一个OPTIONS方法的“预检”请求到服务器询问是否允许。AVPro Video在WebGL后端播放HLS流时其底层JavaScript会使用XMLHttpRequest或Fetch API去请求.m3u8索引文件和.ts视频切片文件。为了支持视频跳转和缓冲这些请求会携带Range头部用于指定获取文件的某一部分。带有Range头的请求属于“非简单请求”会触发预检。4.2 服务器端解决方案治本解决方案完全在流媒体服务器端。Nginx的配置已在3.1节给出这里再强调和解释几个关键点Access-Control-Allow-Origin必须设置为包含Unity WebGL页面来源的域名或者像示例中使用*通配符允许任何来源。在生产环境中强烈建议指定确切域名而非*以提高安全性。例如add_header Access-Control-Allow-Origin https://your-app.com;Access-Control-Allow-Methods必须包含GET和OPTIONS。因为浏览器会先用OPTIONS方法发起预检请求。Access-Control-Allow-Headers必须包含Range。这是允许浏览器发送范围请求的关键。通常还需要包含Content-Type。处理OPTIONS预检请求当请求方法是OPTIONS时直接返回204 No Content状态码并附上上述CORS头部。这告诉浏览器“预检通过你可以发送真正的GET请求了。”# 专门处理OPTIONS预检请求 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, Content-Type; add_header Access-Control-Max-Age 1728000; # 缓存预检结果20天 return 204; }Access-Control-Expose-Headers可选但重要如果服务器在响应中返回了一些自定义头部而客户端AVPro Video的JS代码需要读取它们则必须在此列出。对于视频播放通常不需要。配置修改后务必重启Nginx (sudo nginx -s reload)。然后你可以在浏览器中直接访问你的.m3u8文件地址并打开开发者工具的“网络”选项卡查看请求和响应头确认CORS头部是否正确返回。4.3 客户端与开发调试技巧使用AVPro Video的WebGL模板如前所述这是必须的。插件自带的模板包含了处理CORS和视频元素生命周期的优化代码。调试时禁用浏览器CORS仅限开发在本地开发测试时可以临时使用浏览器插件如CORS Unblock或启动浏览器时添加参数如Chrome的--disable-web-security --user-data-dir/tmp来禁用CORS检查。这仅用于快速定位问题是服务器配置问题还是客户端问题绝非解决方案。查看AVPro Video日志在Media Player组件上勾选Enable Debug Logging构建WebGL时在浏览器控制台会看到AVPro Video的详细日志有助于判断是加载失败、解码失败还是其他问题。确保流地址可公开访问你的流媒体服务器地址必须能被最终用户的浏览器访问到。本地局域网IP如192.168.x.x在互联网上无法访问需要使用公网IP并配置端口转发或者使用云服务器。5. 性能优化与多路播放实践当数字孪生场景中需要同时播放多个摄像头画面时性能成为挑战。5.1 服务器端优化FFmpeg参数调优-c:v copy尽可能使用视频流复制避免实时转码这是节省服务器CPU资源最重要的参数。-rtsp_transport tcp网络不稳定时使用TCP模式。-bufsize和-maxrate限制码率避免带宽波动。例如-b:v 2000k -maxrate 2000k -bufsize 4000k。HLS切片参数在Nginx配置中可以调整HLS切片时长和数量。更短的切片如2秒能降低延迟但会增加请求频率。在nginx-rtmp-module中可用hls_fragment、hls_playlist_length等指令控制。使用HTTP-FLV替代HLS降低延迟HLS通常有10-30秒的延迟对于实时监控可能偏高。HTTP-FLV协议延迟可以做到3-5秒。在Nginx配置中启用HTTP-FLVAVPro Video同样支持播放.flv地址。只需将Media Player的路径指向http://服务器:8080/live?applivestreamcamera1即可。5.2 Unity客户端优化控制同时播放的路数不要一次性激活场景中所有的摄像头Media Player。可以采用“视锥裁剪”或根据用户与监控屏幕的距离来动态开启/关闭播放器。监听OnMediaPlayerEvent在播放完成或出错时释放资源。降低分辨率与码率如果摄像头支持多码流在FFmpeg拉流时可以指定获取子码流通常是较低分辨率的流。例如海康摄像头主码流是ch1/main/av_stream子码流可能是ch1/sub/av_stream。在Unity端播放低分辨率流可以显著降低GPU解码和渲染压力。AVPro Video组件优化非当前聚焦的播放器可以设置MediaPlayer.Pause()或MediaPlayer.Stop()。将Playback Rate设置为1.0非必要不调速。如果UI不需要交互可以将接收视频纹理的RawImage的Raycast Target取消勾选小幅提升性能。使用Texture Mipmaps对于渲染到3D模型屏幕上的视频如果模型可能远离相机可以开启视频纹理的Mipmaps生成在Render To Texture资产上设置有助于提升远处画面的渲染性能和质量。6. 常见问题排查与实战心得6.1 问题速查表问题现象可能原因排查步骤与解决方案WebGL页面黑屏控制台报CORS错误服务器未正确配置CORS头部1. 检查Nginx配置中add_header指令是否正确添加且生效。2. 浏览器直接访问.m3u8地址查看响应头是否有Access-Control-Allow-Origin等。3. 检查Nginx是否重启。能加载但视频卡在第一帧流媒体格式或编码不被浏览器支持1. 用VLC播放器测试同一个HLS地址确认流本身正常。2. 检查FFmpeg转码参数确保视频编码为H.264音频为AAC这是HTML5视频最广泛的兼容格式。3. 尝试使用HTTP-FLV流。播放几秒后中断网络不稳定或服务器推流中断1. 查看FFmpeg推流进程是否还在运行有无报错。2. 检查摄像头网络连接和RTSP服务是否稳定。3. 在FFmpeg命令中添加-re按原始帧率读取和-stimeout设置RTSP超时如-stimeout 5000000单位微秒。音频正常视频绿屏或花屏视频解码失败可能是编码问题或GPU解码异常1. 确认AVPro Video在WebGL平台使用了HTML5后端。2. 尝试在Unity中降低播放器的分辨率缩放。3. 检查摄像头输出是否是标准的H.264 Baseline/Main Profile。多路播放时性能急剧下降浏览器解码或Unity渲染压力过大1. 实施5.2节的客户端优化策略动态管理播放器。2. 在服务器端使用子码流。3. 考虑使用画中画Picture-in-Picture或分页显示减少同屏播放路数。移动端浏览器无法播放移动浏览器对自动播放策略更严格1. 确保视频元素有muted属性可在AVPro Video初始化代码中设置。2. 播放必须由用户手势如点击按钮触发不能Auto Start。6.2 实战心得与避坑指南测试先行分步验证不要等所有摄像头都接入、所有功能都开发完再测试。先搞定一个摄像头的完整链路摄像头RTSP流 - FFmpeg转推 - Nginx分发HLS - 浏览器直接访问.m3u8能播 - Unity WebGL单路播放成功。这个闭环打通了再扩展数量。重视日志FFmpeg、Nginx、浏览器控制台、AVPro Video Debug Log这四个地方的日志是排查问题的生命线。养成出错第一时间看日志的习惯。网络环境模拟开发环境localhost一切正常部署到公网可能就出问题。尽早将流媒体服务器部署到有公网IP的环境进行测试排查防火墙、安全组、端口映射等问题。关于HTTPS如果你的WebGL页面部署在HTTPS站点那么视频流地址也必须使用HTTPS或WS/WSS否则浏览器会因为混合内容Mixed Content策略而阻止加载。你需要为Nginx配置SSL证书。备选方案如果自建流媒体服务器运维成本太高可以考虑使用云服务商提供的“直播转码”或“RTSP转WebRTC/HLS”服务。一些物联网视频平台也提供API可以直接获取适合Web播放的HLS地址省去了服务器运维的麻烦但可能会有费用和定制化程度的考量。AVPro Video的许可确保你的AVPro Video插件许可适用于WebGL平台通常是的并且在构建最终发布版本时正确处理了许可证信息。整个流程走下来核心思想就是“桥梁”架构。UnityAVPro Video作为强大的客户端渲染引擎负责呈现而中间件NginxFFmpeg作为协议转换与跨域中介负责适配。把这两者的边界和职责厘清各自配置到位海康摄像头在Unity数字孪生的WebGL项目中流畅播放就不再是难题了。
返回列表