眾所周知,海康的攝像頭sdk較為完善,但是對於新手來說還是有點麻煩。
今天寫一篇隨筆給大家展示下怎么控制海康攝像頭的雲台(前提是有ptz雲台設備)
1、sdk准備


本文基於C#的frame來開發一個ptz控制的demo,上圖是海康sdk提供的動態庫cs文件,通過dllimport來提取封裝的函數。主要使用NET_DVR_PTZControlWithSpeed_Other來控制雲台。

其中虛線框的流程是可選部分,不會影響其他流程和模塊的功能使用。按實現功能的不同可以分成十 個模塊,實現每個模塊的功能時初始化 SDK、用戶注冊設備、注銷設備和釋放 SDK 資源這 4 個流程是必不 可少的,解碼器功能模塊和行為分析功能模塊是針對解碼器和智能設備的,在該文檔里我們不做描述。
用戶注冊設備(NET_DVR_Login_V30):實現用戶的注冊功能,注冊成功后,返回的用戶 ID 作為其他功 能操作的唯一標識,SDK 允許最大注冊用戶數為 512 個。網絡攝像機和網絡球機允許有 16 個注冊用戶 名,而且同時最多允許 128 個用戶注冊。
1.1用戶注冊及鏈接攝像頭設備
private bool m_bInitSDK = false;
private bool m_bRecord = false;
private uint iLastErr = 0;
private Int32 m_lUserID = -1;
private Int32 m_lRealHandle = -1;
private string str1;
private string str2;
private Int32 i = 0;
private Int32 m_lTree = 0;
private string str;
private long iSelIndex = 0;
private uint dwAChanTotalNum = 0;
private uint dwDChanTotalNum = 0;
private Int32 m_lPort = -1;
private IntPtr m_ptrRealHandle;
private int[] iIPDevID = new int[96];
private int[] iChannelNum = new int[96];
private CHCNetSDK.REALDATACALLBACK RealData = null;
public CHCNetSDK.NET_DVR_DEVICEINFO_V30 DeviceInfo;
public CHCNetSDK.NET_DVR_IPPARACFG_V40 m_struIpParaCfgV40;
public CHCNetSDK.NET_DVR_STREAM_MODE m_struStreamMode;
public CHCNetSDK.NET_DVR_IPCHANINFO m_struChanInfo;
public CHCNetSDK.NET_DVR_PU_STREAM_URL m_struStreamURL;
public CHCNetSDK.NET_DVR_IPCHANINFO_V40 m_struChanInfoV40;
public delegate void MyDebugInfo(string str);
CHCNetSDK.NET_DVR_PREVIEWINFO lpPreviewInfo = new CHCNetSDK.NET_DVR_PREVIEWINFO();
public MainForm()
{
InitializeComponent();
m_bInitSDK = CHCNetSDK.NET_DVR_Init();
if (m_bInitSDK == false)
{
MessageBox.Show("NET_DVR_Init error!");
return;
}
else
{
//保存SDK日志 To save the SDK log
CHCNetSDK.NET_DVR_SetLogToFile(3, @"./SdkLog", true);
//comboBoxView.SelectedIndex = 0;
//for (int i = 0; i < 64; i++)
//{
// iIPDevID[i] = -1;
// iChannelNum[i] = -1;
//}
}
}
public void ListIPChannel(Int32 iChanNo, byte byOnline, int byIPID)
{
str1 = String.Format("IPCamera {0}", iChanNo);
m_lTree++;
if (byIPID == 0)
{
str2 = "X"; //通道空閑,沒有添加前端設備 the channel is idle
}
else
{
if (byOnline == 0)
{
str2 = "offline"; //通道不在線 the channel is off-line
}
else
str2 = "online"; //通道在線 The channel is on-line
}
//listViewIPChannel.Items.Add(new ListViewItem(new string[] { str1, str2 }));//將通道添加到列表中 add the channel to the list
}
public void ListAnalogChannel(Int32 iChanNo, byte byEnable)
{
str1 = String.Format("Camera {0}", iChanNo);
m_lTree++;
if (byEnable == 0)
{
str2 = "Disabled"; //通道已被禁用 This channel has been disabled
}
else
{
str2 = "Enabled"; //通道處於啟用狀態 This channel has been enabled
}
//listViewIPChannel.Items.Add(new ListViewItem(new string[] { str1, str2 }));//將通道添加到列表中 add the channel to the list
}
public void InfoIPChannel()
{
uint dwSize = (uint)Marshal.SizeOf(m_struIpParaCfgV40);
IntPtr ptrIpParaCfgV40 = Marshal.AllocHGlobal((Int32)dwSize);
Marshal.StructureToPtr(m_struIpParaCfgV40, ptrIpParaCfgV40, false);
uint dwReturn = 0;
int iGroupNo = 0; //該Demo僅獲取第一組64個通道,如果設備IP通道大於64路,需要按組號0~i多次調用NET_DVR_GET_IPPARACFG_V40獲取
if (!CHCNetSDK.NET_DVR_GetDVRConfig(m_lUserID, CHCNetSDK.NET_DVR_GET_IPPARACFG_V40, iGroupNo, ptrIpParaCfgV40, dwSize, ref dwReturn))
{
iLastErr = CHCNetSDK.NET_DVR_GetLastError();
str = "NET_DVR_GET_IPPARACFG_V40 failed, error code= " + iLastErr;
//獲取IP資源配置信息失敗,輸出錯誤號 Failed to get configuration of IP channels and output the error code
}
else
{
m_struIpParaCfgV40 = (CHCNetSDK.NET_DVR_IPPARACFG_V40)Marshal.PtrToStructure(ptrIpParaCfgV40, typeof(CHCNetSDK.NET_DVR_IPPARACFG_V40));
for (i = 0; i < dwAChanTotalNum; i++)
{
ListAnalogChannel(i + 1, m_struIpParaCfgV40.byAnalogChanEnable[i]);
iChannelNum[i] = i + (int)DeviceInfo.byStartChan;
}
byte byStreamType = 0;
uint iDChanNum = 64;
if (dwDChanTotalNum < 64)
{
iDChanNum = dwDChanTotalNum; //如果設備IP通道小於64路,按實際路數獲取
}
for (i = 0; i < iDChanNum; i++)
{
iChannelNum[i + dwAChanTotalNum] = i + (int)m_struIpParaCfgV40.dwStartDChan;
byStreamType = m_struIpParaCfgV40.struStreamMode[i].byGetStreamType;
dwSize = (uint)Marshal.SizeOf(m_struIpParaCfgV40.struStreamMode[i].uGetStream);
switch (byStreamType)
{
//目前NVR僅支持直接從設備取流 NVR supports only the mode: get stream from device directly
case 0:
IntPtr ptrChanInfo = Marshal.AllocHGlobal((Int32)dwSize);
Marshal.StructureToPtr(m_struIpParaCfgV40.struStreamMode[i].uGetStream, ptrChanInfo, false);
m_struChanInfo = (CHCNetSDK.NET_DVR_IPCHANINFO)Marshal.PtrToStructure(ptrChanInfo, typeof(CHCNetSDK.NET_DVR_IPCHANINFO));
//列出IP通道 List the IP channel
ListIPChannel(i + 1, m_struChanInfo.byEnable, m_struChanInfo.byIPID);
iIPDevID[i] = m_struChanInfo.byIPID + m_struChanInfo.byIPIDHigh * 256 - iGroupNo * 64 - 1;
Marshal.FreeHGlobal(ptrChanInfo);
break;
case 4:
IntPtr ptrStreamURL = Marshal.AllocHGlobal((Int32)dwSize);
Marshal.StructureToPtr(m_struIpParaCfgV40.struStreamMode[i].uGetStream, ptrStreamURL, false);
m_struStreamURL = (CHCNetSDK.NET_DVR_PU_STREAM_URL)Marshal.PtrToStructure(ptrStreamURL, typeof(CHCNetSDK.NET_DVR_PU_STREAM_URL));
//列出IP通道 List the IP channel
ListIPChannel(i + 1, m_struStreamURL.byEnable, m_struStreamURL.wIPID);
iIPDevID[i] = m_struStreamURL.wIPID - iGroupNo * 64 - 1;
Marshal.FreeHGlobal(ptrStreamURL);
break;
case 6:
IntPtr ptrChanInfoV40 = Marshal.AllocHGlobal((Int32)dwSize);
Marshal.StructureToPtr(m_struIpParaCfgV40.struStreamMode[i].uGetStream, ptrChanInfoV40, false);
m_struChanInfoV40 = (CHCNetSDK.NET_DVR_IPCHANINFO_V40)Marshal.PtrToStructure(ptrChanInfoV40, typeof(CHCNetSDK.NET_DVR_IPCHANINFO_V40));
//列出IP通道 List the IP channel
ListIPChannel(i + 1, m_struChanInfoV40.byEnable, m_struChanInfoV40.wIPID);
iIPDevID[i] = m_struChanInfoV40.wIPID - iGroupNo * 64 - 1;
Marshal.FreeHGlobal(ptrChanInfoV40);
break;
default:
break;
}
}
}
Marshal.FreeHGlobal(ptrIpParaCfgV40);
}
上面展示的是設備注冊鏈接時要申請的變量;接下來開始鏈接,這里用一個button去實現用戶連接的功能:
private void button1_Click(object sender, EventArgs e)
{
if (m_lUserID < 0)
{
string DVRIPAddress = tb_IP.Text; //設備IP地址或者域名 Device IP
Int16 DVRPortNumber = Int16.Parse(tb_Port.Text);//設備服務端口號 Device Port
string DVRUserName = tb_ID.Text;//設備登錄用戶名 User name to login
string DVRPassword = tb_Pwd.Text;//設備登錄密碼 Password to login
//登錄設備 Login the device
m_lUserID = CHCNetSDK.NET_DVR_Login_V30(DVRIPAddress, DVRPortNumber, DVRUserName, DVRPassword, ref DeviceInfo);
if (m_lUserID < 0)
{
iLastErr = CHCNetSDK.NET_DVR_GetLastError();
str = "NET_DVR_Login_V30 failed, error code= " + iLastErr; //登錄失敗,輸出錯誤號 Failed to login and output the error code
return;
}
else
{
//登錄成功
button1.Text = "斷開";
dwAChanTotalNum = (uint)DeviceInfo.byChanNum;
dwDChanTotalNum = (uint)DeviceInfo.byIPChanNum + 256 * (uint)DeviceInfo.byHighDChanNum;
if (dwDChanTotalNum > 0)
{
InfoIPChannel();
}
else
{
for (i = 0; i < dwAChanTotalNum; i++)
{
ListAnalogChannel(i + 1, 1);
iChannelNum[i] = i + (int)DeviceInfo.byStartChan;
}
//comboBoxView.SelectedItem = 1;
// MessageBox.Show("This device has no IP channel!");
}
}
}
else
{
//注銷登錄 Logout the device
if (m_lRealHandle >= 0)
{
m_lUserID = -1;
button1.Text = "連接相機";
return;
}
if (!CHCNetSDK.NET_DVR_Logout(m_lUserID))
{
iLastErr = CHCNetSDK.NET_DVR_GetLastError();
str = "NET_DVR_Logout failed, error code= " + iLastErr;
return;
}
//listViewIPChannel.Items.Clear();//清空通道列表 Clean up the channel list
m_lUserID = -1;
button1.Text = "連接相機";
}
if (m_lRealHandle < 0)
{
//lpPreviewInfo.hPlayWnd = RealPlayWnd.Handle;//預覽窗口 live view window
lpPreviewInfo.lChannel = iChannelNum[(int)iSelIndex];//預覽的設備通道 the device channel number
lpPreviewInfo.dwStreamType = 0;//碼流類型:0-主碼流,1-子碼流,2-碼流3,3-碼流4,以此類推
lpPreviewInfo.dwLinkMode = 0;//連接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
lpPreviewInfo.bBlocked = true; //0- 非阻塞取流,1- 阻塞取流
lpPreviewInfo.dwDisplayBufNum = 15; //播放庫顯示緩沖區最大幀數
IntPtr pUser = IntPtr.Zero;//用戶數據 user data
//if (comboBoxView.SelectedIndex == 0)
//{
//打開預覽 Start live view
m_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V40(m_lUserID, ref lpPreviewInfo, null/*RealData*/, pUser);
//}
//else
//{
// lpPreviewInfo.hPlayWnd = IntPtr.Zero;//預覽窗口 live view window
// m_ptrRealHandle = RealPlayWnd.Handle;
// RealData = new CHCNetSDK.REALDATACALLBACK(RealDataCallBack);//預覽實時流回調函數 real-time stream callback function
// m_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V40(m_lUserID, ref lpPreviewInfo, RealData, pUser);
//}
if (m_lRealHandle < 0)
{
iLastErr = CHCNetSDK.NET_DVR_GetLastError();
str = "NET_DVR_RealPlay_V40 failed, error code= " + iLastErr; //預覽失敗,輸出錯誤號 failed to start live view, and output the error code.
return;
}
else
{
////預覽成功
//DebugInfo("NET_DVR_RealPlay_V40 succ!");
//btnPreview.Text = "Stop View";
MessageBox.Show("連接成功!");
}
}
return;
}
到這里沒報錯的話就說明連接上了
1.2 控制雲台
上一步時連接設備,注意:我們本次要做的時不預覽畫面單獨控制雲台!
需要用到:CHCNetSDK.NET_DVR_PTZControlWithSpeed_Other(m_lRealHandle, lpPreviewInfo.lChannel, 21, 0, 15);
解釋:

CHCNetSDK.NET_DVR_PTZControlWithSpeed_Other(m_lRealHandle, lpPreviewInfo.lChannel, 21, 0, 15);//示例
雲台控制指令如圖:

大家有需要可以試一下,練練手!
