Unity WebGL 接入海康监控:AVProVideo 与 RTSP 转 HLS 实战

发布时间:2026/10/16 12:28:29
Unity WebGL 接入海康监控:AVProVideo 与 RTSP 转 HLS 实战 简介这份资源面向Unity开发者和安防监控集成人员提供在WebGL平台下基于AVProVideo播放海康设备实时监控画面的完整Demo源码解决浏览器端无法直接播放RTSP/RTMP流、需借助转码与播放器插件的问题。包内共701个文件涵盖99个C#脚本、25个Shader、30个Asset、28个ScriptableObject配置、26个DLL及14个AAR等另有mp4示例视频、png贴图与json配置压缩包约80.17MB。作者重写了Http请求模块并附带AVProVideo无水印版本的WebGL发布设置指导配置文件只需手动修改Http请求配表中的字段值即可发布运行本地部署采用nginx。目前已有1213人学习下载。读者可获取可直接运行的工程结构、请求模块改造思路、WebGL发布配置参考以及播放器与海康设备对接的排错经验适合具备一定Unity与C#基础、需要快速验证监控流Web端播放方案的开发者。1. 从一次监控画面接入翻车说起这套 WebGL 方案到底解决什么去年帮某实验室做一个设备状态看板需求很朴素在浏览器里实时看到几路监控画面叠加设备运行数据。第一版我图省事直接用 Unity 的 VideoPlayer 加 RTSP 地址编辑器里跑得好好的一打包 WebGL 就傻眼——浏览器根本不认 RTSP 协议画面全黑控制台一堆解码报错。后来换成 AVProVideo 做播放层配合服务端转码把海康设备的流转成 WebGL 能吃的格式才把这条路走通。这套 demo 源码要解决的就是「Unity WebGL 环境下稳定播放海康设备监控实时画面」这个具体问题适合正在做安防看板、设备监控大屏、数字孪生场景的毕设或课程设计同学也适合需要快速验证 WebGL 视频接入可行性的从业者。它不追求大而全而是把「海康流怎么进浏览器」这条链路讲透、跑通。2. 需求拆解与技术选型为什么是 AVProVideo 而不是原生 VideoPlayer2.1 先把「实时画面」这个需求翻译成技术约束很多人一上来就问「用什么插件」其实应该先问「浏览器允许我做什么」。WebGL 跑在浏览器沙箱里视频这块有三条硬约束第一浏览器原生只认 HTTP/HTTPS 上的渐进式下载或 HLS/DASH 这类分片流RTSP、RTMP 这类实时流协议它不直接支持第二WebGL 的渲染管线拿不到系统级解码器的帧数据所有画面必须走浏览器自己的 video 元素或 WebCodecs 解码后再上传纹理第三跨域和自动播放策略会卡住一部分加载方式。把这三条套到「海康设备监控实时画面」上需求就具体了海康设备吐出来的是 RTSP 主码流或子码流浏览器吃不下中间必须有一个转码/转封装环节把 RTSP 转成 HLS 或 WebRTC 这类浏览器能消费的形态。同时 Unity 侧要有一个能把视频帧稳定喂给 WebGL 纹理的播放组件。这就是整套方案的两个核心模块服务端流转换 客户端播放渲染。2.2 AVProVideo 在 WebGL 下的真实定位AVProVideo 是 Unity 生态里比较成熟的视频播放插件桌面端支持一大堆格式和硬件解码。但到了 WebGL 平台它的能力边界会收窄——底层其实是桥接到浏览器的 video 元素或 MediaSource所以它能播什么取决于浏览器能播什么。这一点必须先说清楚否则容易产生「装了插件就万能」的误解。它在这套方案里的价值主要有三点一是统一的 API播放、暂停、跳转、取纹理这些操作在编辑器和 WebGL 下写法一致减少平台分支二是它处理了视频帧到 Unity 纹理的映射省掉自己写 WebGL 纹理上传的活三是它带了一些事件回调方便做播放状态监控和错误处理。换句话说它解决的是「客户端播放层」的问题不解决「协议转换」的问题。协议转换得靠服务端。2.3 服务端转码选型HLS 还是 WebRTC这是整个方案里最关键的选型决策直接决定延迟和实现复杂度。我一般会按延迟要求来分方案典型延迟实现复杂度适用场景RTSP 转 HLS310 秒低状态看板、非实时监控RTSP 转 WebRTC0.52 秒高需要实时操作的场景RTSP 转 HTTP-FLV13 秒中兼顾延迟与兼容性HLS 的好处是浏览器原生支持、CDN 友好、实现简单缺点是延迟高切片越小延迟越低但开销越大。WebRTC 延迟最低但要处理信令、STUN/TURN对毕设来说工作量偏大。这套 demo 走的是 HLS 路线因为监控看板这类场景对秒级延迟不敏感稳定和省事更重要。如果你的场景要求实时操作可以把服务端换成 WebRTC 网关客户端 AVProVideo 的接入方式基本不变。提示选型时先确认延迟容忍度再决定转码方案。不要为了追求低延迟把毕设做成一个信令服务器项目。3. 总体架构设计三层结构与数据流向3.1 三层职责划分整套方案按「设备层 → 服务层 → 客户端层」三层来组织每层职责单一方便单独调试。设备层就是海康摄像头或 NVR通过 RTSP 对外提供视频流通常有主码流和子码流两路主码流清晰度高码率大子码流适合多路预览。服务层负责把 RTSP 拉过来转封装成 HLS对外暴露一个 HTTP 地址。客户端层是 Unity WebGL 应用用 AVProVideo 加载 HLS 地址把画面渲染到 UI 或 3D 物体上同时叠加业务数据。这样分层的好处是设备层出问题查 RTSP 地址和网络服务层出问题查转码进程和切片客户端出问题查播放器配置和跨域。定位问题时不会一锅粥。3.2 数据流向与关键节点数据从摄像头出来经过服务层转码到客户端播放中间有几个关键节点需要重点关注。第一个节点是 RTSP 拉流这里最容易因为鉴权、网络、码流格式出问题。第二个节点是转封装HLS 切片时长和 GOP 对齐会影响起播速度和卡顿。第三个节点是 HTTP 分发跨域头CORS没配好浏览器会直接拒绝加载。第四个节点是客户端解码WebGL 下解码能力受浏览器限制码率过高会掉帧。把这四个节点画成一条链路每个节点都有对应的排查手段这就是后面避坑章节的依据。3.3 客户端模块划分客户端这边我一般拆成四个模块播放控制模块、纹理渲染模块、状态监控模块、业务叠加模块。播放控制模块封装 AVProVideo 的加载、播放、暂停、重连逻辑纹理渲染模块负责把视频纹理贴到 RawImage 或材质上状态监控模块监听播放事件处理卡顿和错误业务叠加模块把设备数据画在画面上层。模块之间通过事件或接口通信不要互相直接引用。这样换播放器或换渲染方式时改动范围可控。4. 服务端转码落地把 RTSP 变成浏览器能吃的流4.1 转码命令与参数说明服务端用 FFmpeg 做转封装是最省事的做法。核心命令如下# 将海康 RTSP 流转换为 HLS供 WebGL 客户端播放 ffmpeg -rtsp_transport tcp \ -i rtsp://用户名:密码设备IP:554/Streaming/Channels/101 \ -c:v copy \ -c:a aac \ -f hls \ -hls_time 2 \ -hls_list_size 3 \ -hls_flags delete_segments \ -hls_segment_filename /var/www/hls/stream_%03d.ts \ /var/www/hls/stream.m3u8逐段解释-rtsp_transport tcp强制用 TCP 拉流UDP 在丢包网络下容易花屏-i后面是海康的 RTSP 地址通道号 101 一般代表主码流102 是子码流-c:v copy表示视频不重新编码直接复制前提是源流已经是 H.264这样 CPU 占用极低-c:a aac音频转成 AAC因为 HLS 对音频编码有要求-hls_time 2每个切片 2 秒切片越短延迟越低但请求越频繁-hls_list_size 3播放列表保留 3 个切片-hls_flags delete_segments自动删除旧切片避免磁盘写满。如果源流不是 H.264-c:v copy会失败这时要改成-c:v libx264 -preset veryfast -tune zerolatency重新编码代价是 CPU 占用上升。4.2 多路流的管理方式一个看板往往要同时看多路画面每路起一个 FFmpeg 进程是最直接的做法但要管理好进程生命周期。我一般写一个简单的守护脚本用进程名或配置文件来跟踪每路流的状态进程挂了自动重启。#!/bin/bash # 简单的多路流守护脚本每 10 秒检查一次进程状态 STREAMS(cam01 cam02 cam03) for cam in ${STREAMS[]}; do if ! pgrep -f hls/${cam}.m3u8 /dev/null; then echo 重启流: $cam nohup ffmpeg -rtsp_transport tcp \ -i rtsp://用户名:密码设备IP:554/Streaming/Channels/101 \ -c:v copy -c:a aac -f hls -hls_time 2 \ -hls_list_size 3 -hls_flags delete_segments \ /var/www/hls/${cam}.m3u8 /dev/null 21 fi done这个脚本只做最基础的存活检查生产环境建议换成 systemd 或 supervisor 来管理能拿到更完整的日志和重启策略。毕设场景下这个脚本够用但要在文档里说明它的局限。4.3 HTTP 服务与跨域配置HLS 切片要通过 HTTP 暴露出去用 Nginx 托管静态文件是最常见的做法。关键配置是 CORS 头WebGL 应用和视频流往往不同源没有正确的跨域头浏览器会拦截。server { listen 80; server_name localhost; location /hls/ { # 允许 Unity WebGL 页面跨域加载视频切片 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET, OPTIONS; add_header Access-Control-Allow-Headers Range; types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /var/www; } }Access-Control-Allow-Origin *在开发阶段方便上线要改成具体域名。types块确保 m3u8 和 ts 文件返回正确的 MIME 类型类型不对某些浏览器会拒绝播放。如果客户端要拖动进度或做 Range 请求Access-Control-Allow-Headers里要带上 Range。5. 客户端播放实现AVProVideo 接入与画面渲染5.1 播放器初始化与加载流程客户端这边AVProVideo 的核心组件是 MediaPlayer加载 HLS 地址的流程和加载普通视频差不多但 WebGL 下要注意加载是异步的不能假设调用完就能立刻拿到纹理。using RenderHeads.Media.AVProVideo; using UnityEngine; using UnityEngine.UI; public class MonitorPlayer : MonoBehaviour { public MediaPlayer mediaPlayer; // 在 Inspector 中赋值 public RawImage display; // 用于显示视频画面的 UI public string hlsUrl; // HLS 播放地址 void Start() { // 监听播放事件便于处理加载完成和错误 mediaPlayer.Events.AddListener(OnVideoEvent); // 打开 HLS 地址第二个参数 autoPlay 设为 true mediaPlayer.OpenMedia(new MediaPath(hlsUrl, MediaPathType.AbsolutePathOrURL), true); } void Update() { // 每帧把视频纹理赋给 RawImageWebGL 下纹理可能延迟就绪 if (mediaPlayer ! null mediaPlayer.TextureProducer ! null) { var texture mediaPlayer.TextureProducer.GetTexture(); if (texture ! null display.texture ! texture) { display.texture texture; } } } void OnVideoEvent(MediaPlayer mp, MediaPlayerEvent.EventType type, ErrorCode code) { switch (type) { case MediaPlayerEvent.EventType.ReadyToPlay: Debug.Log(视频就绪开始播放); break; case MediaPlayerEvent.EventType.Error: Debug.LogError($播放错误: {code}); // 这里可以触发重连逻辑 break; } } }这段代码的关键点OpenMedia是异步的加载完成前TextureProducer可能为空所以 Update 里要做空判断纹理赋值前比较一下是否变化避免每帧重复赋值造成不必要的开销事件回调里处理错误为后面的重连逻辑留接口。MediaPathType.AbsolutePathOrURL表示传入的是完整 URL不要用相对路径。5.2 画面渲染到 UI 与 3D 物体渲染到 UI 用 RawImage 最简单把视频纹理直接赋给display.texture即可。如果要渲染到 3D 物体表面比如做一个虚拟屏幕就把纹理赋给材质的_MainTex。// 渲染到 3D 物体表面 public Renderer screenRenderer; void Update() { if (mediaPlayer ! null mediaPlayer.TextureProducer ! null) { var texture mediaPlayer.TextureProducer.GetTexture(); if (texture ! null) { screenRenderer.material.mainTexture texture; } } }WebGL 下纹理是每帧更新的材质赋值本身开销不大但要注意如果场景里有多个物体共用材质会互相影响需要给每个物体单独实例化材质。另外视频纹理的宽高比和物体宽高比不一致会拉伸通常的做法是根据视频分辨率动态调整物体的缩放或 UI 的 RectTransform。5.3 播放状态监控与自动重连监控画面最怕的是流断了没人知道。客户端要监听错误事件配合定时器做重连。我一般会记录最近一次成功播放的时间超过阈值没更新就触发重连。private float lastFrameTime; private const float ReconnectThreshold 5f; // 5 秒无更新则重连 void Update() { if (mediaPlayer ! null mediaPlayer.Control ! null mediaPlayer.Control.IsPlaying()) { lastFrameTime Time.time; // 播放正常时刷新时间戳 } else if (Time.time - lastFrameTime ReconnectThreshold) { Debug.LogWarning(画面超时尝试重连); mediaPlayer.CloseMedia(); mediaPlayer.OpenMedia(new MediaPath(hlsUrl, MediaPathType.AbsolutePathOrURL), true); lastFrameTime Time.time; // 重置避免频繁重连 } }重连逻辑要加节流否则流一直不通会疯狂重连。ReconnectThreshold根据切片时长调整HLS 切片 2 秒的话5 秒阈值比较合理。重连后要重置时间戳给播放器留出加载时间。6. 避坑与常见问题排查那些让我熬夜的坑6.1 画面全黑但无报错现象WebGL 页面加载正常播放器也显示就绪但画面就是黑的。原因通常是视频纹理还没就绪就赋给了显示组件或者 HLS 切片 MIME 类型不对导致浏览器静默拒绝。解决在 Update 里做纹理空判断和变化判断确认 Nginx 返回的 Content-Type 是application/vnd.apple.mpegurl和video/mp2t用浏览器开发者工具的 Network 面板看切片请求是否 200。6.2 播放几秒后卡住现象起播正常几秒后画面定格过一会又跳一下。原因多半是 HLS 切片时长和 GOP 不对齐或者服务端转码跟不上。解决确认源流 GOP 长度-hls_time设成 GOP 时长的整数倍如果是-c:v copy模式切片边界只能落在关键帧上切片时长不会严格等于设定值这是正常的但要在播放器侧容忍这种抖动。6.3 跨域报错导致加载失败现象控制台出现 CORS 相关错误视频请求被拦截。原因Unity WebGL 页面和视频流不同源Nginx 没配跨域头或者配了但没覆盖 OPTIONS 预检请求。解决按第 4.3 节的配置加Access-Control-Allow-Origin并确保 OPTIONS 请求返回 204 且带同样的头。开发阶段可以用同源代理绕开但上线必须配好。6.4 多路播放时帧率骤降现象单路流畅开到四路以上整体卡顿。原因WebGL 下每路视频都要解码和上传纹理浏览器解码器数量有限码率过高会拖垮渲染。解决多路预览时改用子码流降低分辨率和码率限制同屏播放路数超出部分用截图或轮播替代纹理更新频率可以适当降低不必每帧都传。6.5 移动端浏览器兼容问题现象桌面浏览器正常手机浏览器黑屏或无法播放。原因移动端浏览器对自动播放和 HLS 支持差异大部分浏览器要求用户手势后才能播放部分不支持 MediaSource。解决加一个「点击播放」的交互入口不要依赖自动播放测试目标浏览器是否支持 HLS不支持的话考虑降级到 MP4 点播或提示用户。7. 验收与答辩怎么证明这套方案真的跑通了验收这件事我踩过的最大坑是「演示时好好的答辩老师一问细节就露馅」。所以验收不能只看画面出没出来要按链路逐段验证每一段都有可复现的证据。第一步验设备层用 FFplay 或 VLC 直接拉 RTSP 地址确认设备本身能出流排除设备侧问题。命令是ffplay -rtsp_transport tcp rtsp://...能出画面说明地址、鉴权、网络都没问题。这一步的截图留档答辩时能证明问题不在设备。第二步验服务层单独访问 HLS 的 m3u8 地址确认切片在持续生成。用curl看播放列表内容或者用浏览器直接打开 m3u8能播放说明转码和分发正常。同时用top看 FFmpeg 进程的 CPU 占用-c:v copy模式下应该很低如果很高说明在重新编码要检查源流编码格式。第三步验客户端在 Unity 编辑器里先跑通再打包 WebGL 跑通。编辑器能播 WebGL 不能播问题基本在跨域或浏览器解码能力两边都不能播问题在播放器配置或地址。这一步要记录起播时间、卡顿次数、重连次数作为性能指标。第四步验异常处理手动断掉一路流看客户端是否在预期时间内重连手动停掉 FFmpeg看守护脚本是否拉起。异常处理是答辩加分项很多同学只演示正常流程一问断流就答不上来。验收项验证方法合格标准设备出流FFplay 拉 RTSP画面正常无花屏转码正常访问 m3u8切片持续更新可播放客户端播放WebGL 页面起播小于 5 秒画面流畅断流重连手动断流10 秒内自动恢复多路并发同时开 4 路帧率不低于 24fps答辩时我一般会准备一个「故障演示」环节主动断一路流让老师看到系统能自愈。这比只放正常画面有说服力得多。另外把架构图、数据流向图、关键参数表打印出来老师问什么都能指到图上不会慌。从那以后我每次做视频接入类项目都强制走一遍「设备→服务→客户端→异常」四段验收每段留证据不靠感觉判断。希望这套结构和这份 demo 源码能帮到你少走我当年那条弯路。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询