尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Unity对接海康视频流:SDK封装、解码与纹理渲染实战

Unity对接海康视频流:SDK封装、解码与纹理渲染实战 有段时间我一直在折腾“Unity里显示海康摄像头画面”这件事。起因也简单手头一个数字孪生项目需要在三维场景里把几十路厂区监控画面叠到对应位置上领导开口就是一句“这个应该很简单的播放器而已”。这话听着轻松真做起来才明白Unity和海康视频流之间隔着的不是一层API而是编码、解码、线程、渲染管线一整套链路。先说明一下标题里那个“视屏”多半是输入法打错了正主是“视频”。这篇文章把我从方案调研到最终落地踩坑的过程记录下来目标读者是那些要在Unity里接海康摄像机、录像机或者机器视觉相机画面的开发者。文章会分别讲清楚设备类型怎么区分、方案怎么选、SDK怎么封装、数据怎么转成Unity能用的纹理最后附上一批高频问题的排查经验覆盖从“完全没头绪”到“能上线跑”的整个路径。1. 先理清需求Unity对接海康视频到底在对接什么1.1 设备类型决定技术路线先对号入座很多人拿到需求就搜“Unity 海康”然后看一堆文章越看越晕。原因很简单海康的产品线太宽了“海康视频”至少分三种完全不同的技术栈。第一种是网络摄像机IPC和网络录像机NVR这是最常见的情况。监控摄像头挂在交换机上给一个IP、端口、用户名密码就能取流。这种设备走的是海康设备网络SDKHCNetSDK出的是RTSP/RTMP这类标准流对接起来相对成熟网上能搜到的“海康摄像头取流地址”“海康设备网络SDK”基本都在这条线上。第二种是机器视觉相机比如海康机器人旗下的MV系列相机配合VisionMaster简称VM这个软件平台使用。热词里出现“海康VM软件”“海康VisionMaster下载安装”说的就是这一波。这类相机走的是GigE Vision或USB3.0接口输出的是原始图像数据而不是经过编码压缩的视频流。它的SDK是MVSMachine Vision Software和监控那条线的HCNetSDK完全是两码事。第三种是海康的安防综合管理平台也就是接入了大量设备之后提供的开放服务接口通常要配置appKey、appSecret通过平台API去查询设备列表、拉取实时预览地址再拿地址去播放。热词里“海康安防管理平台 有配置appkey、appsecret”说的就是这个场景本质上是把设备的取流能力包装成了平台级的服务接口。所以我建议拿到需求先问三个问题要接的是IPC/NVR还是CMOS工业相机有没有现成的安防平台做汇聚最终展示环境是Windows客户端还是WebGL网页这三个问题直接决定你后面的所有选型。1.2 三条路线对比插件、SDK还是RTSP确认了设备类型对接方案基本也就清晰了。以最常见的IPC/NVR设备为例Unity这边可以走三条路线。路线A官方Web插件。海康官方提供WebControl/WebPlugin控件网页里嵌一个控件就能看视频。如果你想在Unity里用常见做法是嵌一个WebView然后在WebView里打开海康的Web播放页。好处是开发量小坏处是延迟高、交互割裂、只能在Windows上用。这套控件走的是ActiveX那套老路子Chrome 45之后就砍掉了NPAPI插件支持必须用老IE内核或者其他专用浏览器才能跑而且画面是浮在Unity渲染结果上方的没法做到三维场景里贴图融合。项目要求不高的可以这么干但正经数字孪生项目我劝你别踩这个坑。路线B海康设备网络SDKHCNetSDK 自封装Unity原生插件。这是目前最主流的方案。海康提供C/C接口的动态库你把它封装成C#能调的接口P/Invoke然后在Unity里做登录、取流、解码、渲染。优点是可定制性极高、延迟低、能拿到像素数据直接在三维模型上贴图缺点是要处理DLL发布、回调线程、数据格式转换一堆细节门槛明显更高。路线CRTSP拉流方案。不依赖海康SDK直接按RTSP地址去拉视频流用FFmpeg或第三方播放组件解码然后转成Texture给Unity显示。现在市面上也有不少Unity插件可以直接播RTSP。好处是脱离海康SDK的绑定理论上什么摄像头都能接坏处是动态库体积大、授权问题烦、延迟优化全靠自己调而且很多现成播放器在Unity里的线程模型做得并不好容易卡主线程。三条路线我整理成一张表方便你对照着选方案开发成本延迟表现平台兼容三维融合推荐场景Web插件内嵌浏览器低高1s以上以Windows为主差画面浮在UI层演示型、原型验证HCNetSDK原生封装中高低可做到100ms以内Win/Linux可移植好像素数据可上贴图数字孪生、正式项目RTSP拉流FFmpeg/VLC中中看参数调优跨平台较好好多品牌设备混合场景我自己的选择是路线B因为项目要求把监控画面贴到三维楼宇模型的外立面、地下车库的平面图上还需要做多路轮询和云台控制Web控件那种“窗口叠窗口”的玩法根本满足不了。2. 核心细节拆解HCNetSDK方案里的关键知识点2.1 版本选择与动态库结构确定走SDK路线之后第一件事是去海康官网下载设备网络SDKWindows版本解压出来你会看到include、lib、dll等目录。热词里提到的“海康设备网络SDK v5.3.6.35”就是这类东西的具体版本号不同版本接口变化不大但老项目升级时要重新编译测试一遍。我用的是Windows x64环境的SDK核心动态库是HCNetSDK.dll另外还有一堆依赖库比如crypto、ssl、zlib那类发布游戏时不能只拷HCNetSDK.dll要连依赖一起放进去否则运行时各种莫名其妙报错。这个问题在后面的问题排查章节会细讲。这里还有个经验SDK包里会有Windows和Linux版本如果你的Unity项目还要发布到Linux服务器上做数字孪生展示记得下载对应Linux版本的库命名和接口基本一致但库文件后缀是.soC#封装的DllImport需要做平台判断。另外顺带提一下“海康guid文件在哪获取”这个热词。在海康的一些开放平台对接里GUID指的是设备或应用的唯一标识。如果是对接安防管理平台通常是在平台控制台创建应用时自动生成不需要你手动去设备上找如果是设备侧序列号那在IPC/NVR的网页管理界面、设备标签上都能看到也可以通过SDK的NET_DVR_GetDVRConfig接口读出来。2.2 登录、取流的基本接口链路HCNetSDK的使用流程可以理解成“注册-登录-取流-收数据”。先用NET_DVR_Init做初始化然后构造登录参数调NET_DVR_Login_V40登录设备登录成功会返回一个用户IDuint类型这个ID是所有后续操作的前提相当于你打开了设备的一扇门。接着是取流。海康SDK给了两种方式第一种是句柄预览。指定一个窗口句柄SDK内部解码并直接往窗口上画。这种方式代码最少但对Unity不友好因为Unity渲染在一个唯一的主窗口上你没法把一个三维物体上的“面”当成句柄给SDK强行用主窗口句柄视频画面会覆盖整个游戏视图根本没法叠加模型。第二种是回调取流。调用NET_DVR_RealPlay_V40时把预览窗口句柄设为空同时注册一个回调函数这样每一帧数据都会通过回调送出来。数据是什么注意这很关键——回调拿到的默认是PS流/H.264/H.265编码数据不是直接能显示的点阵。你要么把回调数据喂给海康播放库PlayCtrl.dll去解码拿位图要么喂给FFmpeg解码然后把解码后的YUV/RGB数据再转成Unity的Texture。很多人在这里翻车以为回调函数里拿到的Buffer直接SetPixels就能显示结果全屏雪花。实际上你把H.264裸流塞给Unity的纹理接口它压根不认必须先解码。2.3 解码链路PlayCtrl还是FFmpeg解码这一步我试过两条路各有利弊。用海康官方播放库PlayCtrl.dll的NET_DVR_PlayM4系列接口好处是和海康SDK配合度高打开码流后可以直接拿到YUV数据甚至可以注册回调拿解码后的RGB数据性能也不错。坏处是这套库相对封闭文档较少出问题不太好查。用FFmpeg解码好处是通用、可控、可以拿到各种格式的原始帧坏处是要自己维护解码上下文、处理PTS拼接代码量明显增加而且FFmpeg的动态库发布体积也不小。我最终选了PlayCtrl方案因为项目在Windows端运行稳定性和开发速度优先。具体做法是海康SDK回调里收到数据后通过NET_DVR_PlayM4_OpenStream创建播放通道把码流数据推进去再通过NET_DVR_PlayM4_SetDecCallBack注册一个解码回调在解码回调里拿到YV12或者其他格式的图像数据再交给Unity侧转纹理。这个过程里回调线程和Unity主线程是两个世界怎么把它们安全地接起来是后面第3章的重头戏。注意如果项目规模不大、只接一两路画面也可以绕开解码直接调用海康播放库的NET_DVR_PlayM4_GetBMP接口把当前帧拉成位图再转Texture但这种方式效率很低多路场景扛不住只适合做验证。3. 实操Unity端封装SDK并实时显示视频流3.1 项目结构与DLL放置先说工程结构。Unity项目里新建一个Plugins目录Windows x64平台下把HCNetSDK.dll、PlayCtrl.dll以及它们的依赖库放到Assets/Plugins/x86_64目录下。Unity发布时会自动把这些库拷贝到输出目录但有一个坑如果你的场景里没有任何脚本引用这些库Unity可能不会把它们一起打进去所以最好建一个Editor脚本或者在Player Settings里手动指定。然后就是C#封装。DllImport的写法类似这样[DllImport(HCNetSDK.dll)] public static extern bool NET_DVR_Init(); [DllImport(HCNetSDK.dll)] public static extern bool NET_DVR_Cleanup(); [DllImport(HCNetSDK.dll)] public static extern uint NET_DVR_Login_V40(ref NET_DVR_USER_LOGIN_INFO pLoginInfo, ref NET_DVR_DEVICEINFO_V40 lpDeviceInfo); [DllImport(HCNetSDK.dll)] public static extern bool NET_DVR_Logout(uint lUserID); [DllImport(HCNetSDK.dll)] public static extern uint NET_DVR_RealPlay_V40(ref NET_DVR_PREVIEWINFO pPreviewInfo, REALDATACALLBACK fRealDataCallBack_V30, IntPtr pUser);注意函数返回值的处理。海康SDK不少接口是返回bool或uint但失败原因需要通过NET_DVR_GetLastError()获取错误码可以从SDK头文件里查到。调试时建议把所有调用结果打日志否则后面出了问题都不知道是登录失败还是取流失败。还有一个细节结构体的字段对齐和内存布局非常关键尤其是登录信息里的IP地址、用户名密码这些字符串成员C#里要定义成固定长度的字节数组用MarshalAs特性标好否则传过去就是乱码。[StructLayout(LayoutKind.Sequential)] public struct NET_DVR_USER_LOGIN_INFO { [MarshalAs(UnmanagedType.ByValTStr, SizeConst 129)] public string sDeviceAddress; public byte byUseTransport; public ushort wPort; [MarshalAs(UnmanagedType.ByValTStr, SizeConst 64)] public string sUserName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst 64)] public string sPassword; // 后面还有写日志、回调等字段按头文件补全 }3.2 登录与实时预览的C#实现登录这块的结构体比较大定义起来有点啰嗦但必须字段对齐否则内存布局不对接口直接报错。关键代码大概是NET_DVR_USER_LOGIN_INFO loginInfo new NET_DVR_USER_LOGIN_INFO(); loginInfo.sDeviceAddress ipAddress; // 设备IP loginInfo.wPort 8000; // 海康设备默认端口 loginInfo.sUserName userName; loginInfo.sPassword password; NET_DVR_DEVICEINFO_V40 deviceInfo new NET_DVR_DEVICEINFO_V40(); uint userId NET_DVR_Login_V40(ref loginInfo, ref deviceInfo); if (userId 0) { Debug.LogError(登录失败错误码 NET_DVR_GetLastError()); return; }登录成功之后取预览。这里指定通道号和码流类型主码流清晰但带宽大子码流帧率高但清晰度低做多路轮询时建议用子码流NET_DVR_PREVIEWINFO previewInfo new NET_DVR_PREVIEWINFO(); previewInfo.lChannel channel; // 通道号IPC一般从1开始 previewInfo.dwStreamType 0; // 0-主码流1-子码流 previewInfo.dwLinkMode 0; // TCP方式跨网段更稳 previewInfo.hPlayWnd IntPtr.Zero; // 使用回调取流窗口句柄置空 uint playHandle NET_DVR_RealPlay_V40(ref previewInfo, OnRealDataCallback, IntPtr.Zero);回调函数签名必须是委托并且这个委托实例不能被GC回收所以最好用一个static或者类成员变量保存否则回调触发到一半委托被回收直接AccessViolation崩溃。这个问题我在测试阶段遇到过一旦出现根本没法排查只能在代码里强制hold住委托引用。3.3 回调数据到Unity纹理的完整通路监听回调之后数据就源源不断进来。注意回调执行线程仍然是海康SDK内部的线程不是Unity主线程。要安全地显示到Unity需要一套缓冲机制。我的做法是定义一个线程安全的帧队列private ConcurrentQueuebyte[] frameQueue new ConcurrentQueuebyte[](); private void OnRealDataCallback(uint lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr pUser) { // 假设这里已经经过播放库解码拿到的是图像数据 byte[] data new byte[dwBufSize]; Marshal.Copy(pBuffer, data, 0, (int)dwBufSize); frameQueue.Enqueue(data); }然后在Unity主线程的Update里消费队列更新纹理void Update() { if (frameQueue.TryDequeue(out byte[] data)) { texture2D.LoadRawTextureData(data); texture2D.Apply(); } }但这里要注意LoadRawTextureData要求数据格式跟纹理的格式完全匹配。如果你的解码结果是YV12Texture2D没法直接用需要先转成RGBA32。格式转换可以在C#里写也可以在解码侧直接设置为RGB24再转换。像素级for循环在4K分辨率下是灾难建议要么用Native插件在C侧转好要么用Compute Shader处理。另外回调频率可能比Unity渲染帧率高队列如果无限积压内存会爆。我的经验是队列里最多留两帧新帧进来把旧帧丢掉保证始终显示最新画面延迟也低。用ConcurrentQueue的话直接判断Count如果大于等于2就TryDequeue掉旧帧再Enqueue。有朋友问过“Unity为什么不能在回调里直接Update纹理”。核心原因是Unity的渲染API不是线程安全的纹理上传、GPU指令提交都必须发生在主线程和渲染线程的特定阶段。你在SDK回调线程里直接操作Texture2D轻则画面闪烁、卡顿重则直接崩溃。这种问题不一定会稳定复现但一旦跑到线上就是事故。所以线程调度这一步绝对不能省。3.4 WebGL与移动端的取舍如果你的目标是发布WebGL那原生DLL方案直接作废浏览器里跑不了HCNetSDK.dll。热词里出现“Unity 发布 webgl 使用 idbfs 写入失败”这类问题说明很多人在WebGL端折腾过文件读写这跟视频对接是两回事但侧面说明WebGL环境限制很多。WebGL的可行路线是服务端中转部署一个媒体网关把海康的RTSP流拉下来转成WebRTC或者HLSUnity客户端通过JS插件或WebSocket拿流播放。Unity WebGL对视频纹理的支持有限通常做法是用浏览器原生video标签铺在Canvas上或者用WebCodecs解码后写入纹理。这块工作量和复杂度都不低项目排期时一定要单独评估。移动端同理Android/iOS上没法直接用Windows的DLL需要找海康移动端的SDK或者走RTSP移动解码插件。说到底原生DLL方案只适合Windows/Linux桌面应用。4. 常见问题与排查经验速查4.1 黑屏、花屏、绿屏黑屏大概率是登录失败或者通道号不对。先看登录返回值再看通道号海康IPC一般从1开始但NVR的分通道号可能不是连续的最稳妥的办法是用NET_DVR_GetDVRConfig枚举通道信息或者直接去设备网页看通道号。花屏/绿屏常见于解码格式不匹配。比如解码回调给的是YV12你把纹理设置成了RGBA32或者纹理宽高和实际帧宽高不一致。解决方式很简单先在代码里把纹理的格式、宽高写死成实际分辨率确认显示正常后再考虑动态适配。还有一个容易忽略的设备并发限制。如果设备同时被多个客户端取流部分型号默认只允许4路主码流连接超过就会拒绝。项目里几十路摄像头轮询时记得联系设备管理员开“多路取流”或者改用子码流这也就是热词里“海康威视平台授权扩容”出现的场景。做项目之前最好把设备型号和授权路数问清楚免得后面扩容时被卡脖子。4.2 网络与设备参数问题拉流很卡先别怀疑代码用VLC直接打开RTSP地址验证一下网络质量rtsp://用户名:密码IP:554/Streaming/Channels/101注意这个地址里101代表第一通道主码流102是第一通道子码流201是第二通道主码流以此类推。如果VLC也卡基本就是带宽或者设备编码性能问题跟Unity没关系。另外跨网段取流时TCP方式比UDP稳但延迟略高局域网内建议UDP广域网建议TCP。这些参数在NET_DVR_PREVIEWINFO里都能配很多人默认不去改遇到问题才想起来。4.3 内存泄漏与多路视频多路摄像头同时取流时最容易踩的坑是每次切换通道都new Texture、new byte数组又不释放内存直线上升。建议做一个纹理池复用已经创建的Texture和缓冲数组切换通道只替换数据源尽量不要频繁申请。还有一个细节结束预览时一定要调NET_DVR_StopRealPlay退出时调NET_DVR_Logout和NET_DVR_Cleanup。漏了任何一个第二次登录时可能莫名其妙失败设备侧也会占用大量假连接。4.4 如果对接的是VisionMaster机器视觉相机最后把热词里的“海康VM”单独拿出来说。VisionMaster是海康机器人的机器视觉软件和前面讲的监控SDK不是一条线。如果你拿到的是MV系列相机要下载的是MVSMachine Vision Software客户端和对应SDK接口命名和HCNetSDK完全不同。在Unity里接MV相机思路类似用P/Invoke调MVS的库通过MV_CC_EnumDevices枚举设备MV_CC_StartGrabbing开始取流然后在回调里接收帧数据。帧数据默认是原始图像比如Mono8或者BGR8反而比H.264码流省了解码这一步直接转Texture就行。但它的网络配置、触发模式、缓存帧管理又是另外一套坑改天单独写一篇。4.5 高频问题速查表现象可能原因排查建议登录返回0IP/端口/账号密码错误NET_DVR_GetLastError取错误码对照头文件查登录成功但无画面通道号错误或无取流权限先查设备网页确认通道再用VLC验证取流画面花屏解码格式与纹理格式不匹配统一YUV/RGB格式确认宽高一致Unity崩溃回调线程操作Unity API所有纹理更新必须回到主线程Update画面卡顿带宽不足/码流过大切子码流调低分辨率或帧率发布后DLL找不到未配置Plugins或依赖缺失把HCNetSDK依赖库一起拷贝检查依赖项这篇文章写到这儿我把Unity对接海康视频从方案选型、SDK封装到数据链路、高频问题都过了一遍。说实话这类需求在数字孪生、智慧园区、工业监控项目里越来越多但技术选型上确实没有一个“万能模板”我在实际项目里最终选择HCNetSDKPlayCtrl自研插件也是权衡了平台限制、延迟要求、三维融合需求之后的结果。如果看完后你准备在自己项目里动手我的建议是先花半天做一个最小验证一台IPC、一个RTSP地址、一个能播RTSP的播放器确认设备本身链路没问题再往Unity里接能省下大量排查时间。后面有机会我再写写VisionMaster相机对接、WebGL服务端中转那些方向有什么具体问题也欢迎在评论区交流。
返回列表