日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

unity接入海康網(wǎng)絡(luò)攝像頭的多種實現(xiàn)方法

 鴻蛟家平 2018-08-07

此次項目需求是接入海康威視的網(wǎng)絡(luò)智能攝像頭實現(xiàn)實時監(jiān)控。網(wǎng)上搜羅一番,也有挺多例子的,但是大多數(shù)都是通過官方提供的rstp協(xié)議地址實現(xiàn),為了自己記憶,在下打算在這里記錄一下,不需要的可以跳過這里哈(這里采用一個叫UMP的插件,當(dāng)然還有其他Vlc for unity,openCV等都可以實現(xiàn)的)

首先貼一下??档膔tsp協(xié)議地址:

rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream
說明:
username: 用戶名。例如admin。
password: 密碼。例如12345。
ip: 為設(shè)備IP。例如 192.0.0.64。
port: 端口號默認(rèn)為554,若為默認(rèn)可不填寫。
codec:有h264、MPEG-4、mpeg4這幾種。
channel: 通道號,起始為1。例如通道1,則為ch1。
subtype: 碼流類型,主碼流為main,輔碼流為sub。


例如,請求??禂z像機通道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 地址我就不貼了,搜一下會有的哈。導(dǎo)入后是這樣的


然后隨便選一個場景填入你的rtsp地址



然后這是運行的效果



但是本人通過測試后發(fā)現(xiàn)延遲~額。。。有一丟丟小高,于是繼續(xù)搜羅,發(fā)現(xiàn)海康的SDK里的函數(shù)是可以回調(diào)獲取視頻流的數(shù)據(jù)的,還提供了一個播放庫的SDK,同過此SDK的方法可以將標(biāo)準(zhǔn)的視頻碼流轉(zhuǎn)換為YV12的格式,相信研究過視頻流的對這個格式都不陌生吧,沒錯,小白我準(zhǔn)備在unity中將這個格式的數(shù)據(jù)實時生成一幀一幀的textur2d,然后,上步驟

首先下載海康最新的SDK包

地址:http://www.hikvision.com/cn/download_more_570.html

記住播放庫的也一起下來哦、

前一部分的實現(xiàn)代碼其實官方已經(jīng)提供了各種各樣的案例 C#和C++的都有,但是看過代碼的同學(xué)應(yīng)該都會知道,在這寫Demo中想要播放視頻都有一個必不可少的參數(shù)就是窗口的句柄Handle,就是一個IntPtr類型的參數(shù),但是untiy中。As you konw ,哪里來的句柄啊,UI都是畫出來的嘛,整個unity才是一個窗口,但是SDK中也說了可以給這個參數(shù)傳空然后給一個回調(diào)函數(shù)來獲取這些視頻數(shù)據(jù),



記住這個回調(diào)函數(shù)里是用來啟用Play_M4播放庫解碼的(有點長,還是不貼圖片貼代碼號了,不要噴我亂(* ̄︶ ̄))



/// <summary>
/// 獲取數(shù)據(jù)流回調(diào)函數(shù)
/// </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)
{
//下面數(shù)據(jù)處理建議使用委托的方式
//MyDebugInfo AlarmInfo = new MyDebugInfo(DebugInfo);
switch (dwDataType)
{
case CHCNetSDK.NET_DVR_SYSHEAD:     // sys head
if (dwBufSize > 0)
{
if (m_lPort >= 0)
{
return; //同一路碼流不需要多次調(diào)用開流接口
}
//debugInfo +=  ("系統(tǒng)頭數(shù)據(jù):" + pBuffer + "數(shù)據(jù)長度:" + 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;
}


//設(shè)置流播放模式 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);
}


//打開碼流,送入頭數(shù)據(jù) 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;
}




//設(shè)置顯示緩沖區(qū)個數(shù) 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);
}


//設(shè)置顯示模式 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);
}


//設(shè)置解碼回調(diào)函數(shù),獲取解碼后音視頻原始數(shù)據(jù) 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++)
{
//送入碼流數(shù)據(jù)進(jìn)行解碼 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)
{
//送入其他數(shù)據(jù) 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;
}
}


然后在播放庫代碼中在加一個回調(diào)來自己處理數(shù)據(jù)

看圖



這里的數(shù)據(jù)都是別人官方的播放庫函數(shù)處理好的每一幀的數(shù)據(jù),連幀號都有,可以說已經(jīng)非常方便了,我們只需要將每一幀的數(shù)據(jù)拿來然后轉(zhuǎn)換為我們需要的Texutrue2D數(shù)據(jù),然后一幀一幀的顯示到我們的UI上就可以了,那么重點來了,之前說過播放庫拿出來的數(shù)據(jù)是YV12的格式,而我們需要的Texture2D是rgb(YUV)格式的,這些都是圖片數(shù)據(jù)的存儲格式,我實在太懶了,給你們貼個地址,不懂得有興趣的可以看看O(∩_∩)O  https://www.cnblogs.com/samaritan/p/YUV.html

這里轉(zhuǎn)換的話,由于數(shù)據(jù)量有點大 一幀1280*720的YV12數(shù)據(jù)大概有2764800,如果自己寫硬生生的在數(shù)據(jù)里復(fù)制替換啥的,額,本人試了一下,不到10幀,感覺成GIF圖了都,所以網(wǎng)上又搜羅一下(有事沒事就搜羅搜羅),看到其他博友測試的五六種方法,有直接硬轉(zhuǎn)的(通過查表法優(yōu)化過的,其實也沒優(yōu)化多少),然后看到效率比較高的兩種,用opencv和ffmpeg實現(xiàn)的,opencv和ffmpeg都試了下,最后選擇了ffmpeg,因為ffmpeg的算法效率最好(別人說的,別人說測試了的)

這里如果要用ffmpeg的格式轉(zhuǎn)換算法需要兩個庫avutil-55.dll和swscale-4.dll

然后呢當(dāng)然就是調(diào)用這兩個庫,寫一個轉(zhuǎn)換方法導(dǎo)出可以供untiy調(diào)用的Dll了

下面貼出Dll里的轉(zhuǎn)換函數(shù)代碼,比較粗糙,大家不要介意.



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函數(shù)主要做一些初始化,c++里面大家知道的申請內(nèi)存啊什么的 


然后是轉(zhuǎn)化函數(shù)

///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,"轉(zhuǎn)換成功!");
return true;
break;
 }
default:{
//printf("Not Support Output Pixel Format.\n");
break;
}
}
//strcpy(rst,"轉(zhuǎn)換失??!");
return false;
}

然后unity里的調(diào)用 


然后調(diào)用此函數(shù)將之前回調(diào)函數(shù)里存起來的YV12數(shù)據(jù) 轉(zhuǎn)換為rgb數(shù)據(jù)



然后,額,創(chuàng)建Texture2D?,還是貼一下吧,雖然代碼很丑。。




最后貼一下運行效果吧(里面其他的功能都很簡單,就不多做介紹)

試一下GIF圖o(* ̄︶ ̄*)o



共同學(xué)習(xí),歡迎留言評論以及給我更好的建議或者實現(xiàn)方案,謝謝大家!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約