這幾天整理了一下辦公電腦裏的開發工程,刪除了一些測試工程,也發現了之前做的有些比較還可以的測試Demo。
代碼工程上傳至鏈接:https://pan.baidu.com/s/13-rTaeVLtcX0KzHy2RT5Ow 提取碼:g0u2
我這裏用的海康威視的SDK是比較老了,因爲這個Demo是2017年做的。(注:我只是將海康威視的C#編寫的視頻監控集成到Unity3D中使用(我Unity用的5.5版本),使用的是CH-HCNetSDK(Windows64)V5.2.3.3_build20160623_2版本下載地址:鏈接:https://pan.baidu.com/s/1aMX-bM3h00ogvD53YhER6w
提取碼:o5y5 )
大家可以去海康威視下載最新的SDK,下載地址https://www.hikvision.com/cn/download_more_570.html#prettyPhoto
下載你需要的版本。
SDK下載完成後,你在Unity新建一個工程,按照下載SDK文件中找到Demo示例,找C#開發,裏面有需要的功能C# demo,打開你需要的Demo,裏面有一個說明,按照這個說明,找到.dll庫文件,導入Unity的Plugins文件中。
其次,找到CHCNetSDK.cs文件和PlayCtrl.cs文件,去掉C#編輯界面的內容重新集成SDK主要功能。
比如:
CHCNetSDK.cs
PlayCtrl.cs
再次,創建一個集成功能腳本工具,實現視頻監控功能。我粘主要功能代碼
private uint iLastErr = 0;
private int m_lUserID = -1;
private bool m_bInitSDK = false;
private bool m_bRecord = false;
private int m_lRealHandle = -1;
private string str;
private int m_lPort = -1;
Texture2D img;
private string move_url;
private FileInfo file;
private byte[] buffer_byte;
Texture2D tex;
private int buffer_count = 0;
private int num = 0;
private PlayCtrl.DECCBFUN m_fDisplayFun = null;
byte[] byteBuf;
byte[] outYuv;
//初始化
void Init()
{
tex = new Texture2D(2560, 1440, TextureFormat.ATC_RGBA8, false);
m_bInitSDK = CHCNetSDK.NET_DVR_Init();
if (!m_bInitSDK)
Debug.Log("NET_DVR_Init error!");
else
CHCNetSDK.NET_DVR_SetLogToFile(3, "D:\\SdkLog\\", true);
// Create a texture. Texture size does not matter, since
// LoadImage will replace with with incoming image size.
Texture2D texe= new Texture2D(2, 2);
// A small 64x64 Unity logo encoded into a PNG.
byte[] nn = new byte[] { 0, 78, 3, 56, 128 };
byte[] pngBytes = new byte[] {
0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52,
0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x08,0x00,0x00,0x00,0x00,0x8F,0x02,0x2E,
0x02,0x00,0x00,0x01,0x57,0x49,0x44,0x41,0x54,0x78,0x01,0xA5,0x57,0xD1,0xAD,0xC4,
0x30,0x08,0x83,0x81,0x32,0x4A,0x66,0xC9,0x36,0x99,0x85,0x45,0xBC,0x4E,0x74,0xBD,
0x8F,0x9E,0x5B,0xD4,0xE8,0xF1,0x6A,0x7F,0xDD,0x29,0xB2,0x55,0x0C,0x24,0x60,0xEB,
0x0D,0x30,0xE7,0xF9,0xF3,0x85,0x40,0x74,0x3F,0xF0,0x52,0x00,0xC3,0x0F,0xBC,0x14,
0xC0,0xF4,0x0B,0xF0,0x3F,0x01,0x44,0xF3,0x3B,0x3A,0x05,0x8A,0x41,0x67,0x14,0x05,
0x18,0x74,0x06,0x4A,0x02,0xBE,0x47,0x54,0x04,0x86,0xEF,0xD1,0x0A,0x02,0xF0,0x84,
0xD9,0x9D,0x28,0x08,0xDC,0x9C,0x1F,0x48,0x21,0xE1,0x4F,0x01,0xDC,0xC9,0x07,0xC2,
0x2F,0x98,0x49,0x60,0xE7,0x60,0xC7,0xCE,0xD3,0x9D,0x00,0x22,0x02,0x07,0xFA,0x41,
0x8E,0x27,0x4F,0x31,0x37,0x02,0xF9,0xC3,0xF1,0x7C,0xD2,0x16,0x2E,0xE7,0xB6,0xE5,
0xB7,0x9D,0xA7,0xBF,0x50,0x06,0x05,0x4A,0x7C,0xD0,0x3B,0x4A,0x2D,0x2B,0xF3,0x97,
0x93,0x35,0x77,0x02,0xB8,0x3A,0x9C,0x30,0x2F,0x81,0x83,0xD5,0x6C,0x55,0xFE,0xBA,
0x7D,0x19,0x5B,0xDA,0xAA,0xFC,0xCE,0x0F,0xE0,0xBF,0x53,0xA0,0xC0,0x07,0x8D,0xFF,
0x82,0x89,0xB4,0x1A,0x7F,0xE5,0xA3,0x5F,0x46,0xAC,0xC6,0x0F,0xBA,0x96,0x1C,0xB1,
0x12,0x7F,0xE5,0x33,0x26,0xD2,0x4A,0xFC,0x41,0x07,0xB3,0x09,0x56,0xE1,0xE3,0xA1,
0xB8,0xCE,0x3C,0x5A,0x81,0xBF,0xDA,0x43,0x73,0x75,0xA6,0x71,0xDB,0x7F,0x0F,0x29,
0x24,0x82,0x95,0x08,0xAF,0x21,0xC9,0x9E,0xBD,0x50,0xE6,0x47,0x12,0x38,0xEF,0x03,
0x78,0x11,0x2B,0x61,0xB4,0xA5,0x0B,0xE8,0x21,0xE8,0x26,0xEA,0x69,0xAC,0x17,0x12,
0x0F,0x73,0x21,0x29,0xA5,0x2C,0x37,0x93,0xDE,0xCE,0xFA,0x85,0xA2,0x5F,0x69,0xFA,
0xA5,0xAA,0x5F,0xEB,0xFA,0xC3,0xA2,0x3F,0x6D,0xFA,0xE3,0xAA,0x3F,0xEF,0xFA,0x80,
0xA1,0x8F,0x38,0x04,0xE2,0x8B,0xD7,0x43,0x96,0x3E,0xE6,0xE9,0x83,0x26,0xE1,0xC2,
0xA8,0x2B,0x0C,0xDB,0xC2,0xB8,0x2F,0x2C,0x1C,0xC2,0xCA,0x23,0x2D,0x5D,0xFA,0xDA,
0xA7,0x2F,0x9E,0xFA,0xEA,0xAB,0x2F,0xDF,0xF2,0xFA,0xFF,0x01,0x1A,0x18,0x53,0x83,
0xC1,0x4E,0x14,0x1B,0x00,0x00,0x00,0x00,0x49,0x45,0x4E,0x44,0xAE,0x42,0x60,0x82,};
// Load data into the texture.
texe.LoadImage(pngBytes);
//數據流
public void RealDataCallBack(Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser)
{
Debug.Log("dwDataType " + dwDataType + "pBuffer " + pBuffer + "dwBufSize " + dwBufSize);
//byte[] bte = new byte[(int)dwBufSize];
switch (dwDataType)
{
case CHCNetSDK.NET_DVR_SYSHEAD:
if (dwBufSize > 0)
{
//Marshal.Copy(pBuffer, bte, 0, (int)dwBufSize);
//for (int i = 0; i < bte.Length; i++)
//{
// buffer_byte[num + i] = bte[i];
//}
//num += bte.Length;
if (m_lPort >= 0)
{
return; //同一路碼流不需要多次調用開流接口
}
//獲取播放句柄 Get the port to play
if (!PlayCtrl.PlayM4_GetPort(ref m_lPort))
{
iLastErr = PlayCtrl.PlayM4_GetLastError(m_lPort);
str = "PlayM4_GetPort failed, error code= " + iLastErr;
Debug.Log(str);
break;
}
//設置流播放模式 Set the stream mode: real-time stream mode
if (!PlayCtrl.PlayM4_SetStreamOpenMode(m_lPort, PlayCtrl.STREAME_REALTIME))
{
iLastErr = PlayCtrl.PlayM4_GetLastError(m_lPort);
str = "Set STREAME_REALTIME mode failed, error code= " + iLastErr;
Debug.Log(str);
}
//打開碼流,送入頭數據 Open stream
if (!PlayCtrl.PlayM4_OpenStream(m_lPort, pBuffer, dwBufSize, 2 * 1024 * 1024))
{
iLastErr = PlayCtrl.PlayM4_GetLastError(m_lPort);
str = "PlayM4_OpenStream failed, error code= " + iLastErr;
Debug.Log(str);
break;
}
//設置顯示緩衝區個數 Set the display buffer number
if (!PlayCtrl.PlayM4_SetDisplayBuf(m_lPort, 15))
{
iLastErr = PlayCtrl.PlayM4_GetLastError(m_lPort);
str = "PlayM4_SetDisplayBuf failed, error code= " + iLastErr;
Debug.Log(str);
}
//設置顯示模式 Set the display mode
if (!PlayCtrl.PlayM4_SetOverlayMode(m_lPort, 0, 0/* COLORREF(0)*/)) //play off screen
{
iLastErr = PlayCtrl.PlayM4_GetLastError(m_lPort);
str = "PlayM4_SetOverlayMode failed, error code= " + iLastErr;
Debug.Log(str);
}
//設置解碼回調函數,獲取解碼後音視頻原始數據 Set callback function of decoded data
m_fDisplayFun = new PlayCtrl.DECCBFUN(DecCallbackFUN);
//if (!PlayCtrl.PlayM4_SetDecCallBackEx(m_lPort, m_fDisplayFun, IntPtr.Zero, 0))
//{
// Debug.Log("PlayM4_SetDisplayCallBack fail");
//}
PlayCtrl.PlayM4_SetDecCallBack(m_lPort , DecCallbackFUN);
////開始解碼 Start to play
if (!PlayCtrl.PlayM4_Play(m_lPort,IntPtr.Zero))
{
iLastErr = PlayCtrl.PlayM4_GetLastError(m_lPort);
str = "PlayM4_Play failed, error code= " + iLastErr;
Debug.Log(str);
break;
}
}
break;
case CHCNetSDK.NET_DVR_STREAMDATA:
//Debug.Log("pBuffer" + pBuffer);
if (dwBufSize > 0 && m_lPort != -1)
{
//Marshal.Copy(pBuffer, bte, 0, (int)dwBufSize);
//for (int i = 0; i < bte.Length; i++)
//{
// buffer_byte[num + i] = bte[i];
// //Debug.Log("byteeeee " + buffer_byte[i]);
//}
//num += bte.Length;
//buffer_count = num;
//img.LoadImage(buffer_byte);
//RawImg.GetComponent<RawImage>().texture = byteArrayToImage;
//Creat(byt);
for (int i = 0; i < 999; i++)
{
//送入碼流數據進行解碼 Input the stream data to decode
if (!PlayCtrl.PlayM4_InputData(m_lPort, pBuffer, dwBufSize))
{
iLastErr = PlayCtrl.PlayM4_GetLastError(m_lPort);
str = "PlayM4_InputData failed, error code= " + iLastErr;
Thread.Sleep(2);
}
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))
{
iLastErr = PlayCtrl.PlayM4_GetLastError(m_lPort);
str = "PlayM4_InputData failed, error code= " + iLastErr;
Thread.Sleep(2);
}
else
{
break;
}
}
}
break;
}
}
//回調視頻
private void DecCallbackFUN(int nPort, IntPtr pBuf, int nSize, ref PlayCtrl.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
{
if (pFrameInfo.nType == 3) //#define T_YV12 3
{
FileStream fs = null;
BinaryWriter bw = null;
try
{
fs = new FileStream("DecodedVideo.yuv", FileMode.Append);
bw = new BinaryWriter(fs);
Marshal.Copy(pBuf, byteBuf, 0, byteBuf.Length);
Yv12ToYUV(byteBuf, pFrameInfo.nWidth, pFrameInfo.nHeight, 7680);
//for (int i = 0; i < byteBuf.Length; i++)
//{
// Debug.Log("byteBuf SSSSSS" + byteBuf[i]);
//}
//tex.LoadRawTextureData(byteBuf);
//tex.Apply();
//RawImg.GetComponent<RawImage>().texture = tex;
//Creat(byteBuf);
}
catch (System.Exception ex)
{
Debug.Log(ex.ToString());
}
finally
{
bw.Close();
fs.Close();
}
}
}
//視頻流轉換
private void Yv12ToYUV(byte[] inYv12 ,int width,int height,int widthStep)
{
int col;
int row;
int tmp;
int idx;
int Y, U, V;
for (row = 0; row < height; row++)
{
idx = row * widthStep;
int rowptr = row * width;
for (col = 0; col < width; col++)
{
tmp = (row / 2) * (width / 2) + (col / 2);
Y = ( int) inYv12[row * width + col];
U = ( int) inYv12[width * height + width * height / 4 + tmp];
V = ( int) inYv12[width * height + tmp];
outYuv[idx + col * 3] = (byte)Y;
outYuv[idx + col * 3 + 1] = (byte)U;
outYuv[idx + col * 3 + 2] = (byte)V;
}
}
}
最後,創建自己的View層腳本,即可實現海康威視的SDK集成Unity3D 實現監控。親測可以,但是對視頻解碼這塊不太懂,出來的畫面有點色彩不太對,流暢度還可以。
注意:視頻色彩可在Yv12ToYUV和DecCallbackFUN方法中修改。