unity接入海康网络摄像头的多种实现方法
此次项目需求是接入海康威视的网络智能摄像头实现实时监控。网上搜罗一番,也有挺多例子的,但是大多数都是通过官方提供的rstp协议地址实现,为了自己记忆,在下打算在这里记录一下,不需要的可以跳过这里哈(这里采用一个叫UMP的插件,当然还有其他Vlc for unity,openCV等都可以实现的)
首先贴一下海康的rtsp协议地址:
rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream
说明:
username: 用户名。例如admin。
password: 密码。例如12345。
ip: 为设备IP。例如 192.0.0.64。
port: 端口号默认为554,若为默认可不填写。
codec:有h264、MPEG-4、mpeg4这几种。
channel: 通道号,起始为1。例如通道1,则为ch1。
subtype: 码流类型,主码流为main,辅码流为sub。
例如,请求海康摄像机通道1的主码流,Url如下
主码流:
rtsp://admin:12345@192.0.0.64:554/h264/ch1/main/av_stream
rtsp://admin:12345@192.0.0.64:554/MPEG-4/ch1/main/av_stream
子码流:
rtsp://admin:12345@192.0.0.64/mpeg4/ch1/sub/av_stream
rtsp://admin:12345@192.0.0.64/h264/ch1/sub/av_stream
然后插件的名字叫UMP 地址我就不贴了,搜一下会有的哈。导入后是这样的
然后随便选一个场景填入你的rtsp地址
然后这是运行的效果
但是本人通过测试后发现延迟~额。。。有一丢丢小高,于是继续搜罗,发现海康的SDK里的函数是可以回调获取视频流的数据的,还提供了一个播放库的SDK,同过此SDK的方法可以将标准的视频码流转换为YV12的格式,相信研究过视频流的对这个格式都不陌生吧,没错,小白我准备在unity中将这个格式的数据实时生成一帧一帧的textur2d,然后,上步骤
首先下载海康最新的SDK包
地址:http://www.hikvision.com/cn/download_more_570.html
记住播放库的也一起下来哦、
前一部分的实现代码其实官方已经提供了各种各样的案例 C#和C++的都有,但是看过代码的同学应该都会知道,在这写Demo中想要播放视频都有一个必不可少的参数就是窗口的句柄Handle,就是一个IntPtr类型的参数,但是untiy中。As you konw ,哪里来的句柄啊,UI都是画出来的嘛,整个unity才是一个窗口,但是SDK中也说了可以给这个参数传空然后给一个回调函数来获取这些视频数据,
记住这个回调函数里是用来启用Play_M4播放库解码的(有点长,还是不贴图片贴代码号了,不要喷我乱(* ̄︶ ̄))
/// <summary>
/// 获取数据流回调函数
/// </summary>
/// <param name="lRealHandle">L real handle.</param>
/// <param name="dwDataType">Dw data type.</param>
/// <param name="pBuffer">P buffer.</param>
/// <param name="dwBufSize">Dw buffer size.</param>
/// <param name="pUser">P user.</param>
public void RealDataCallBack(Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser)
{
//下面数据处理建议使用委托的方式
//MyDebugInfo AlarmInfo = new MyDebugInfo(DebugInfo);
switch (dwDataType)
{
case CHCNetSDK.NET_DVR_SYSHEAD: // sys head
if (dwBufSize > 0)
{
if (m_lPort >= 0)
{
return; //同一路码流不需要多次调用开流接口
}
//debugInfo += ("系统头数据:" + pBuffer + "数据长度:" + dwBufSize);
//获取播放句柄 Get the port to play
if (!PlayCtrl.PlayM4_GetPort(ref m_lPort))
{
error_num = PlayCtrl.PlayM4_GetLastError(m_lPort);
debugInfo += ("PlayM4_GetPort failed, error code= " + error_num);
break;
}
//设置流播放模式 Set the stream mode: real-time stream mode
if (!PlayCtrl.PlayM4_SetStreamOpenMode(m_lPort, PlayCtrl.STREAME_REALTIME))
{
error_num = PlayCtrl.PlayM4_GetLastError(m_lPort);
debugInfo += ("Set STREAME_REALTIME mode failed, error code= " + error_num);
}
//打开码流,送入头数据 Open stream
if (!PlayCtrl.PlayM4_OpenStream(m_lPort, pBuffer, dwBufSize, 1024*1024))
{
error_num = PlayCtrl.PlayM4_GetLastError(m_lPort);
debugInfo += ("PlayM4_OpenStream failed, error code= " + error_num);
break;
}
//设置显示缓冲区个数 Set the display buffer number
if (!PlayCtrl.PlayM4_SetDisplayBuf(m_lPort, 15))
{
error_num = PlayCtrl.PlayM4_GetLastError(m_lPort);
debugInfo += ("PlayM4_SetDisplayBuf failed, error code= " + error_num);
}
//设置显示模式 Set the display mode
if (!PlayCtrl.PlayM4_SetOverlayMode(m_lPort, 0, 0)) //play off screen
{
error_num = PlayCtrl.PlayM4_GetLastError(m_lPort);
debugInfo += ("PlayM4_SetOverlayMode failed, error code= " + error_num);
}
//设置解码回调函数,获取解码后音视频原始数据 Set callback function of decoded data
m_fDisplayFun = new PlayCtrl.DECCBFUN(DecCallbackFUN);
if (!PlayCtrl.PlayM4_SetDecCallBack(m_lPort, m_fDisplayFun))
{
debugInfo += ("PlayM4_SetDisplayCallBack fail");
}
//开始解码 Start to play
if (!PlayCtrl.PlayM4_Play(m_lPort, IntPtr.Zero))
{
error_num = PlayCtrl.PlayM4_GetLastError(m_lPort);
debugInfo += ("PlayM4_Play failed, error code= " + error_num);
break;
}
}
break;
case CHCNetSDK.NET_DVR_STREAMDATA: // video stream data
if (dwBufSize > 0 && m_lPort != -1)
{
for (int i = 0; i < 999; i++)
{
//送入码流数据进行解码 Input the stream data to decode
if (!PlayCtrl.PlayM4_InputData(m_lPort, pBuffer, dwBufSize))
{
error_num = PlayCtrl.PlayM4_GetLastError(m_lPort);
debugInfo += ("PlayM4_InputData failed, error code= " + error_num);
Thread.Sleep(10);
}
else
{
break;
}
}
}
break;
default:
if (dwBufSize > 0 && m_lPort != -1)
{
//送入其他数据 Input the other data
for (int i = 0; i < 999; i++)
{
if (!PlayCtrl.PlayM4_InputData(m_lPort, pBuffer, dwBufSize))
{
error_num = PlayCtrl.PlayM4_GetLastError(m_lPort);
debugInfo += ("PlayM4_InputData failed, error code= " + error_num);
Thread.Sleep(10);
}
else
{
break;
}
}
}
break;
}
}
然后在播放库代码中在加一个回调来自己处理数据
看图
这里的数据都是别人官方的播放库函数处理好的每一帧的数据,连帧号都有,可以说已经非常方便了,我们只需要将每一帧的数据拿来然后转换为我们需要的Texutrue2D数据,然后一帧一帧的显示到我们的UI上就可以了,那么重点来了,之前说过播放库拿出来的数据是YV12的格式,而我们需要的Texture2D是rgb(YUV)格式的,这些都是图片数据的存储格式,我实在太懒了,给你们贴个地址,不懂得有兴趣的可以看看O(∩_∩)O https://www.cnblogs.com/samaritan/p/YUV.html
这里转换的话,由于数据量有点大 一帧1280*720的YV12数据大概有2764800,如果自己写硬生生的在数据里复制替换啥的,额,本人试了一下,不到10帧,感觉成GIF图了都,所以网上又搜罗一下(有事没事就搜罗搜罗),看到其他博友测试的五六种方法,有直接硬转的(通过查表法优化过的,其实也没优化多少),然后看到效率比较高的两种,用opencv和ffmpeg实现的,opencv和ffmpeg都试了下,最后选择了ffmpeg,因为ffmpeg的算法效率最好(别人说的,别人说测试了的)
这里如果要用ffmpeg的格式转换算法需要两个库avutil-55.dll和swscale-4.dll
然后呢当然就是调用这两个库,写一个转换方法导出可以供untiy调用的Dll了
下面贴出Dll里的转换函数代码,比较粗糙,大家不要介意.
AVPixelFormat SRC_pixfmt;
AVPixelFormat DST_pixfmt;
AVPicture SRC_frameinfo;
AVPicture DST_frameinfo;
struct SwsContext *img_convert_ctx;
TransformResolution *TP;
FFMPEG_FOR_UNITY_API bool StartConvert_Updated(bool start_or_end,int src_width,int src_height,int dst_width,int dst_height,int src_type,int dst_type)
{
if(start_or_end)
{
SRC_pixfmt = (AVPixelFormat)src_type;//0
DST_pixfmt = (AVPixelFormat)dst_type;//2
int ret=0;
TP = new TransformResolution();
TP->SRC_WIDTH = src_width;
TP->SRC_HEIGHT = src_height;
TP->DST_WIDTH = dst_width;
TP->DST_HEIGHT = dst_height;
ret= av_image_alloc(SRC_frameinfo.data, SRC_frameinfo.linesize,src_width, src_height, SRC_pixfmt, 1);
if (ret< 0) {
//printf( "Could not allocate source image\n");
//strcpy(rst,"Could not allocate source image\n");
return false;
}
ret = av_image_alloc(DST_frameinfo.data, DST_frameinfo.linesize,dst_width, dst_height, DST_pixfmt, 1);
if (ret< 0) {
//printf( "Could not allocate destination image\n");
//strcpy(rst,"Could not allocate destination image\n");
return false;
}
//Init Method 1
img_convert_ctx =sws_alloc_context();
//Show AVOption
//av_opt_show2(img_convert_ctx,stdout,AV_OPT_FLAG_VIDEO_PARAM,NULL);
//Set Value
av_opt_set_int(img_convert_ctx,"sws_flags",SWS_BICUBIC|SWS_PRINT_INFO,NULL);
av_opt_set_int(img_convert_ctx,"srcw",src_width,NULL);
av_opt_set_int(img_convert_ctx,"srch",src_height,NULL);
av_opt_set_int(img_convert_ctx,"src_format",SRC_pixfmt,NULL);
//'0' for MPEG (Y:0-235);'1' for JPEG (Y:0-255)
av_opt_set_int(img_convert_ctx,"src_range",1,NULL);
av_opt_set_int(img_convert_ctx,"dstw",dst_width,NULL);
av_opt_set_int(img_convert_ctx,"dsth",dst_height,NULL);
av_opt_set_int(img_convert_ctx,"dst_format",DST_pixfmt,NULL);
av_opt_set_int(img_convert_ctx,"dst_range",1,NULL);
sws_init_context(img_convert_ctx,NULL,NULL);
return true;
}
else
{
sws_freeContext(img_convert_ctx);
av_freep(&SRC_frameinfo);
av_freep(&DST_frameinfo);
delete TP;
return true;
}
}
StartConvert_Update函数主要做一些初始化,c++里面大家知道的申请内存啊什么的
然后是转化函数
///update
FFMPEG_FOR_UNITY_API bool YV12toRgb_Updated(uint8_t* pDst, uint8_t* pSrc)
{
if(!pDst)
{
//strcpy(rst,"pDst is null");
return false;
}
if(!TP)
{
return false;
}
switch(SRC_pixfmt){
case AV_PIX_FMT_GRAY8:{
memcpy(SRC_frameinfo.data[0],pSrc,TP->SRC_WIDTH*TP->SRC_HEIGHT);
break;
}
case AV_PIX_FMT_YUV420P:{
memcpy(SRC_frameinfo.data[0],pSrc,TP->SRC_WIDTH*TP->SRC_HEIGHT); //Y
memcpy(SRC_frameinfo.data[1],pSrc+TP->SRC_WIDTH*TP->SRC_HEIGHT*5/4,TP->SRC_WIDTH*TP->SRC_HEIGHT/4); //V
memcpy(SRC_frameinfo.data[2],pSrc+TP->SRC_WIDTH*TP->SRC_HEIGHT,TP->SRC_WIDTH*TP->SRC_HEIGHT/4); //U
break;
}
case AV_PIX_FMT_YUV422P:{
memcpy(SRC_frameinfo.data[0],pSrc,TP->SRC_WIDTH*TP->SRC_HEIGHT); //Y
memcpy(SRC_frameinfo.data[1],pSrc+TP->SRC_WIDTH*TP->SRC_HEIGHT,TP->SRC_WIDTH*TP->SRC_HEIGHT/2); //U
memcpy(SRC_frameinfo.data[2],pSrc+TP->SRC_WIDTH*TP->SRC_HEIGHT*3/2,TP->SRC_WIDTH*TP->SRC_HEIGHT/2); //V
break;
}
case AV_PIX_FMT_YUV444P:{
memcpy(SRC_frameinfo.data[0],pSrc,TP->SRC_WIDTH*TP->SRC_HEIGHT); //Y
memcpy(SRC_frameinfo.data[1],pSrc+TP->SRC_WIDTH*TP->SRC_HEIGHT,TP->SRC_WIDTH*TP->SRC_HEIGHT); //U
memcpy(SRC_frameinfo.data[2],pSrc+TP->SRC_WIDTH*TP->SRC_HEIGHT*2,TP->SRC_WIDTH*TP->SRC_HEIGHT); //V
break;
}
case AV_PIX_FMT_YUYV422:{
memcpy(SRC_frameinfo.data[0],pSrc,TP->SRC_WIDTH*TP->SRC_HEIGHT*2); //Packed
break;
}
case AV_PIX_FMT_RGB24:{
memcpy(SRC_frameinfo.data[0],pSrc,TP->SRC_WIDTH*TP->SRC_HEIGHT*3); //Packed
break;
}
default:{
//printf("Not Support Input Pixel Format.\n");
break;
}
}
sws_scale(img_convert_ctx, SRC_frameinfo.data, SRC_frameinfo.linesize, 0, TP->SRC_HEIGHT, DST_frameinfo.data, DST_frameinfo.linesize);
switch(DST_pixfmt){
case AV_PIX_FMT_GRAY8:{
memcpy(pDst,DST_frameinfo.data[0],TP->DST_WIDTH*TP->DST_HEIGHT);
break;
}
case AV_PIX_FMT_YUV420P:{
memcpy(pDst,DST_frameinfo.data[0],TP->DST_WIDTH*TP->DST_HEIGHT); //Y
memcpy(pDst,DST_frameinfo.data[1],TP->DST_WIDTH*TP->DST_HEIGHT/4); //U
memcpy(pDst,DST_frameinfo.data[2],TP->DST_WIDTH*TP->DST_HEIGHT/4); //V
break;
}
case AV_PIX_FMT_YUV422P:{
memcpy(pDst,DST_frameinfo.data[0],TP->DST_WIDTH*TP->DST_HEIGHT); //Y
memcpy(pDst,DST_frameinfo.data[1],TP->DST_WIDTH*TP->DST_HEIGHT/2); //U
memcpy(pDst,DST_frameinfo.data[2],TP->DST_WIDTH*TP->DST_HEIGHT/2); //V
break;
}
case AV_PIX_FMT_YUV444P:{
memcpy(pDst,DST_frameinfo.data[0],TP->DST_WIDTH*TP->DST_HEIGHT); //Y
memcpy(pDst,DST_frameinfo.data[1],TP->DST_WIDTH*TP->DST_HEIGHT); //U
memcpy(pDst,DST_frameinfo.data[2],TP->DST_WIDTH*TP->DST_HEIGHT); //V
break;
}
case AV_PIX_FMT_YUYV422:{
memcpy(pDst,DST_frameinfo.data[0],TP->DST_WIDTH*TP->DST_HEIGHT*2);//Packed
break;
}
case AV_PIX_FMT_RGB24:{
//fwrite(dstFrameInfo.data[0],1,dst_w*dst_h*3,dst_file);
memcpy(pDst,DST_frameinfo.data[0],TP->DST_WIDTH*TP->DST_HEIGHT*3);//Packed
// strcpy(rst,"转换成功!");
return true;
break;
}
default:{
//printf("Not Support Output Pixel Format.\n");
break;
}
}
//strcpy(rst,"转换失败!");
return false;
}
然后unity里的调用
然后调用此函数将之前回调函数里存起来的YV12数据 转换为rgb数据
然后,额,创建Texture2D?,还是贴一下吧,虽然代码很丑。。
最后贴一下运行效果吧(里面其他的功能都很简单,就不多做介绍)
试一下GIF图o(* ̄︶ ̄*)o
共同学习,欢迎留言评论以及给我更好的建议或者实现方案,谢谢大家!
unity接入海康网络摄像头的多种实现方法相关推荐
- Unity接入海康网络摄像头SDK
Unity接入海康网络摄像头SDK 写这篇文章是因为采用https://blog.csdn.net/qq_33214503/article/details/80091247 的方法,导致unity无法 ...
- Unity接入海康网络摄像头(测试无延迟)
海康威视官网下载最新版本设备网络SDK:http://www.hikvision.com/Cn/download_more_401.html 下载好SDK后,将需要的DLL导入到Unity的Plugi ...
- Unity使用UMP插件实现接入海康网络摄像头方法
一.下载UMP插件并且导入到Unity项目中 我这里下载了UMP插件,并且进行了处理,制作了对应播放本地文件夹和海康网络摄像头内容的Unity预设"RemoteVideoControl&qu ...
- unity 调用 海康 网络摄像头 免费下载
微信号:ITComputerGraph 更多精彩内容,关注公众号<IT木子李> csdn积分规则导致资源下载次数越多,需要的积分越高,本着奉献精神,博主特把所有资源放到微信公众号,欢迎大家 ...
- 海康网络摄像头忘记密码_不要忘记网络性能
海康网络摄像头忘记密码 Amazon released a famous statement in the late 2000s stating that every hundred millisec ...
- 【硬件配置教程】海康NVR硬盘录像机接入海康RTSP摄像头操作步骤
由于目前市面上RTMP推流摄像头较少,因此TSINGSEE青犀视频和海康合作研发的RTMP推流摄像头一上线就有很多需求.RTMP推流摄像头能够直接将视频流推送到阿里云.腾讯云等平台,便捷可靠. 同样, ...
- 【硬件设备】海康NVR硬盘录像机接入海康RTSP摄像头操作步骤
由于目前市面上RTMP推流摄像头较少,因此TSINGSEE青犀视频和海康合作研发的RTMP推流摄像头一上线就有很多需求.RTMP推流摄像头能够直接将视频流推送到阿里云.腾讯云等平台,便捷可靠. 同样, ...
- 海康网络摄像头实时视频预览(流媒体转码推流 red5,nginx-rtmp,ffmpeg)
海康网络摄像头实时视频预览(流媒体转码推流 red5,nginx-rtmp,ffmpeg) 实现思路 获取摄像头rtsp流→流媒体拉流转码推流成rtmp流(网页具备flash可播放)→根据rtmp流地 ...
- [Rtsp]海康网络摄像头基于RTSP协议的windows平台监控
[Rtsp]海康网络摄像头基于RTSP协议的windows平台监控 基于RTSP协议的windows平台监控. 1. 基于RTSP协议的windows平台监控. 1.1 选取海康网络摄像头(支持RT ...
最新文章
- 分布式CAP中情侣的纠缠故事,真是剪不断 理还乱!
- stopping NetworkManager daemon failed
- C mysql批量写入_使用JDBC在MySQL数据库中快速批量插入数据
- 【渝粤教育】21秋期末考试市场调查与预测10243k2
- java监听剪贴板_在java中实现windows剪贴板监视
- oFono学习笔记——GATChat(2):发送AT命令
- JSP+Servlet+C3P0+Mysql实现的azhuo商城
- 100以内偶数的个数_【数学杂谈】如何记住1000以内的所有质数?
- 数据结构第三篇——线性表的链式存储之单链表
- 基于Python-turtle库绘制皮卡丘、多边形、星空
- 家庭记账本开发进度4
- 【045】HTML初学
- pdf覆盖图片 Java_Java 添加、替换、删除PDF中的图片
- matlab 黑白格子
- onNewIntent与singleTask
- CTF中出现的各种字符/密码总结
- 暗刷流量,刷点击,手机访问的一些小见解
- NYOJ:458-小光棍数
- “三网融合”的过去、现在与未来
- np.ceil的用法
热门文章
- 陈天桥雒芊芊伉俪助阵盛大 “爱心传递”慈善拍卖,捐出丰子恺爱女画作
- 清华版五年级计算机下册教案,清华大学出版社小学五年级信息技术教案新版
- [下载]三维卫星云图显示系统V2.0
- Unity下的UDP客户端
- 每天一练——爱因斯坦出了一道这样的数学题
- 【大话三国】揭秘蜀汉五虎将的真相
- 【各版本通吃】【2023/05/14更新】通过网易云音乐分享链接找到分享用户主页
- 学习钢材物流相关知识后的体会
- 单片机STC8H入门
- QY-GD17型FMS柔性生产制造操作实训系统教学设备,FMS柔性生产系统实训,FMS制造系统操作教学设备