一、VC++ Demo裡關于這兩個功能的實作和分析
基本上每段代碼都可以從OnInitDialog這個方法開始分析
1.1. VC++ Code:
HikVisionDlg.cpp 的OnInitDialog方法中的關鍵代碼
for(i = 0; i < GetTotalDSPs(); i++)
{
ChannelHandle[i] = ChannelOpen(i);
if (ChannelHandle[i]<0)
{
AfxMessageBox("channel open error > 0");
}
else if (ChannelHandle[i] ==(HANDLE) 0xffff)
AfxMessageBox("channel open error 0xffff");
gChannelTotalLength[i] = 0;
nowstate[i]=0;
if (servertype == DIALTYPE)
SetIBPMode(ChannelHandle[i],211,2,1,8);
SetDefaultQuant(ChannelHandle[i],18,18,23);
SetStreamType(ChannelHandle[i],STREAM_TYPE_VIDEO);
else
SetIBPMode(ChannelHandle[i],100,2,1,25);
SetDefaultQuant(ChannelHandle[i],15,15,20);
}
if (servertype == DIALTYPE)
for(i = 0; i < GetTotalDSPs(); i++)
SetEncoderPictureFormat(ChannelHandle[i], ENC_QCIF_FORMAT);
else
if ( i==0 )
{
//when initiated,set the first channel as 4CIF encode,others as CIF
SetEncoderPictureFormat(ChannelHandle[0], ENC_4CIF_FORMAT);
bEncodeCifAndQcif[0] = FALSE;
}
else
SetEncoderPictureFormat(ChannelHandle[i], ENC_CIF_FORMAT);
// int id = IDC_CHECK2;
// for(i = 0; i < MAX_CHANNELS; i++){
// GetDlgItem(id + i)->EnableWindow(FALSE);
// }
RegisterStreamDirectReadCallback(::StreamDirectReadCallback,this);
RegisterMessageNotifyHandle(m_hWnd, MsgDataReady);
MP4_ServerSetMessage(WM_MYCOMMAND,this->m_hWnd);
gCapImages = 0;
SetOverlayColorKey(gBackgroundColor);
gTimer = SetTimer(1, 1000, 0);
SetTimer(2,2000,0);
SetTimer(5,5000,0);
for (i=0;i<MAX_CHANNELS;i++)
gCurrentFileLen[i] = 0;
SERVER_VIDEOINFO videoinfo;
g_nChannelTotal = GetTotalDSPs();
for( i=0 ; i < g_nChannelTotal; i++ )
if(i == 0)
MP4_ServerSetBufNum(i,90);
MP4_ServerSetBufNum(i,80);
videoinfo.m_datatype[i] = DIALING;
videoinfo.m_datatype[i] = NORMAL;
videoinfo.m_datatype[0] = SMALLPIC;
videoinfo.m_channum = g_nChannelTotal;
videoinfo.m_waittime = 2;
MP4_ServerSetStart(StartCap);
MP4_ServerSetStop(StopCap);
MP4_ServerSetIBPMode(SetIBP);
MP4_ServerSetCapIFrame(MakeIFrame);
MP4_ServerSetTTL(64);
MP4_ServerSetNetPort(5050,6050);
MP4_ServerCheckIP(CheckIP);
MP4_ServerCheckPassword(checkpassword);
//set the max connector of channel 0
MP4_ServerMaxUser(0,24);
//如果想不使用預設方式進行多點傳播,
//可以調用下面的函數設定自己的多點傳播資訊
//詳細資訊請參考SDK文檔
// MP4_ServerCastGroup(TRUE,0,"228.0.0.132",9988);
if (!MP4_ServerStart(&videoinfo))
MessageBox("error","error",MB_OK);
HikVisionDlg.cpp 的StreamDirectReadCallback方法
int __cdecl StreamDirectReadCallback(ULONG channelNum,void *DataBuf,DWORD Length,int frameType,void *context)
{
int i,status=0;
CString ctip;
int nframetype =0;
// if cap images we need clean the queue here
// if (!bCapture)
// return 0;
// no errors
if(frameType > 0) {
if(frameType == PktSysHeader){
// store the file header
memcpy(FileHeader[channelNum], DataBuf, Length);
FileHeaderLen = Length;
TRACE("channel %d get the file header !\n",channelNum);
}
if(frameType == PktIFrames || frameType ==PktSubIFrames){
status = 1;
else{
status = 0;
if(frameType == PktMotionDetection){
// m_VideoWin.DrawVect(channelNum, (char *)DataBuf, Length);
return 0;
if(frameType == PktOrigImage){
}
if(Length == 0){
TRACE("no data ?\n");
return 0;
// if(frameType == PktIFrames){
// int iii=1;
// }
ULONG currentTime = timeGetTime();
gChannelTotalLength[channelNum] += Length;
gCurrentFileLen[channelNum] += Length;
if(currentTime > StartTime+1000){
CString str,str2;
str.Format("%d", (gChannelTotalLength[dcurrentwin] *8/(currentTime - StartTime)));
for(i=0;i<g_nChannelTotal;i++)
gChannelTotalLength[i] = 0;
StartTime= currentTime;
CHKVisionDlg *pMain = (CHKVisionDlg *)AfxGetMainWnd();
pMain->GetDlgItem(IDC_BPS)->SetWindowText((LPCTSTR)str);
// if (m_sframe && channelNum ==0)
// {
// if((frameType == PktSFrames && nframetype ==4 )||(frameType == PktSysHeader))
// {
// MP4_ServerWriteData(channelNum,(unsigned char *)DataBuf, Length,frameType,status);
// }
// MP4_ServerWriteData(channelNum,(unsigned char *)DataBuf, Length,frameType,status);
if(frameType ==PktAudioFrames)
_write(gFileHandleQcif[channelNum],DataBuf,Length);
MP4_ServerWriteDataEx(channelNum,(unsigned char *)DataBuf, Length,frameType,status,1);
_write(gFileHandle[channelNum], DataBuf, Length);
MP4_ServerWriteDataEx(channelNum,(unsigned char *)DataBuf, Length,frameType,status,0);
}else if (frameType ==PktSubIFrames || frameType ==PktSubPFrames || frameType == PktSubBBPFrames || frameType == PktSubSysHeader)
MP4_ServerWriteDataEx(channelNum,(unsigned char *)DataBuf, Length,frameType,status,1);
}else
return 0;
}
VideoWin.cpp的OnPaint方法
StartVideoPreview(&dc);
VideoWin.cpp的StartVideoPreview方法
for(int i = 0; i < GetTotalDSPs(); i++){
StopVideoPreview(ChannelHandle[i]);
RECT previewWnd;
GetClientRect(&previewWnd);
//CDC *pDC = GetDlgItem(IDC_VIDEOWIN)->GetDC();
CBrush tempBrush(RGB(10, 10, 10));
CBrush *oldBrush = dc->SelectObject(&tempBrush);
dc->Rectangle(&previewWnd);
dc->SelectObject(oldBrush);
int rectWidth = previewWnd.right - previewWnd.left;
int rectHeight = previewWnd.bottom - previewWnd.top;
int numRects = GetTotalDSPs();
ZeroMemory(rectList, sizeof(rectList));
numRects = CacRects(GetTotalDSPs());
for(i = 0; i < GetTotalDSPs(); i++){
if(bDdrawMode)
::StartVideoPreview(ChannelHandle[i], m_hWnd, &rectList[i], FALSE, vdfRGB16, 25);
else
::StartVideoPreview(ChannelHandle[i], m_hWnd, &rectList[i], FALSE, vdfYUV422Planar, 25);
1.2. 代碼分析
1. 從OnInitDialog中并參照《DS-4000HC、HCS、HC+、HF、HS、MD卡的Windows程式設計指南V4.3》的[API調用順序](pdf 21頁)以及對應的注釋能看得出基本上是做闆卡的初始化,伺服器的初始化等。
2. StreamDirectReadCallback回調函數主要是通過MP4_ServerWriteDataEx将資料寫入記憶體(文檔注釋:往發送緩存寫資料。)和用_write寫檔案做存儲視訊錄像。
3. 預覽的代碼是在OnPaint事件調用的。
二、伺服器端預覽
C# Code:
#region 變量
IntPtr ChannelHandle;
#endregion
#region 窗體事件
private void Form2_Load(object sender, EventArgs e)
//設定系統預設的視訊制式
HikVisionSDK.SetDefaultVideoStandard(VideoStandard_t.StandardNTSC);
//初始化闆卡
if (HikVisionSDK.InitDSPs() < 0)
MessageBox.Show("初始化DSPs失敗!!");
return;
if (HikVisionSDK.GetTotalDSPs() == 0)
MessageBox.Show("沒有可用的通道!!您是否已經啟動伺服器端?");
//打開通道
ChannelHandle = HikVisionSDK.ChannelOpen(0);
//設定編碼幀結構、幀率
HikVisionSDK.SetIBPMode(ChannelHandle, 100, 2, 1, 25);
//設定編碼圖像品質
HikVisionSDK.SetDefaultQuant(ChannelHandle, 15, 15, 20);
//視訊預覽
StartVideoPreview();
/// <summary>
/// 視訊預覽
/// </summary>
private void StartVideoPreview()
Rectangle rect = panel1.ClientRectangle;
HikVisionSDK.StartVideoPreview(ChannelHandle, panel1.Handle, ref rect, false, (int)TypeVideoFormat.vdfRGB16, 25);
/// 窗體移動
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form2_Move(object sender, EventArgs e)
HikVisionSDK.StopVideoPreview(ChannelHandle);
代碼說明:
1. 僅僅實作伺服器端的預覽代碼并不多,這也是在VC++ Demo中不斷注釋代碼、在已經成功完成大部分功能的基礎上才試出來的,可見預覽和伺服器啟動是相對獨立的。
2. Form2_Move是窗體移動時執行的,在VC++的也是在窗體移動中進行了同樣處理,否則你一移動窗體會出現難看的一幕呢 : )
3. StartVideoPreview的參數RECT *rect 直接使用Rectangle結構體即可。
4. panel1是窗體是的一個面闆Panel。
三、讓用戶端連接配接并預覽
//将委托聲明為成員變量!!
STREAM_DIRECT_READ_CALLBACK sdrc;
/// 預覽并用戶端連接配接
private void PreviewAndClientConnect()
sdrc = new STREAM_DIRECT_READ_CALLBACK(STREAM_DIRECT_READ_CALLBACK1);
//[必須]注冊編碼圖像資料流直接讀取回調函數
HikVisionSDK.RegisterStreamDirectReadCallback(sdrc, this.Handle);
//[必須]啟動服務端
HikServer.MP4_ServerSetStart(new StartCap(StartCap));
//HikServer.MP4_ServerSetStop(sc);
//HikServer.MP4_ServerSetIBPMode(new SetIBP(SetIBP));
//[必須]設定回調,重新生成一個I幀
HikServer.MP4_ServerSetCapIFrame(new MakeIFrame(MakeIFrame));
//HikServer.MP4_ServerSetTTL(64);
//HikServer.MP4_ServerSetNetPort(5050, 6050);
PSERVER_VIDEOINFO videoInfo = new PSERVER_VIDEOINFO();
//初始化
videoInfo.m_datatype = new byte[64];
//設定發送緩沖區大小
HikServer.MP4_ServerSetBufNum((ushort)0, (ushort)90);
videoInfo.m_datatype[0] = (byte)ChannelDataType.SMALLPIC;
videoInfo.m_channum = (byte)1;
videoInfo.m_waittime = 5;
//設定每個通道的最大使用者數量
//HikServer.MP4_ServerMaxUser(0, 24);
if (HikServer.MP4_ServerStart(ref videoInfo) == 0)
MessageBox.Show("服務端啟動錯誤!!");
//開啟視訊預覽
#region 回調函數
public void StartCap(int port)
HikVisionSDK.StartVideoCapture(ChannelHandle);
public void MakeIFrame(ulong port)
HikVisionSDK.CaptureIFrame(ChannelHandle);
public int STREAM_DIRECT_READ_CALLBACK1(int channelNum, IntPtr DataBuf, int Length, FrameType_t frameType, IntPtr context)
int status = 0;
HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);
return 0;
1. 将Form2_Load中最後一行代碼StartVideoPreview替換成PreviewAndClientConnect調用即可。
2. 調用注釋前面帶了"[必須]"的方法是必須調用的,而被我的注釋掉的方法參照源代碼可以加也可以不加,因為他是有預設設定的。
3. MakeIFrame這個回調函數是用戶端連接配接伺服器的關鍵,如果沒有執行這個回調用戶端将不能夠連接配接并顯示畫面!
4. STREAM_DIRECT_READ_CALLBACK1回調函數在VC++代碼說明裡面已經說明了,因為本章不寫視訊存儲,是以把其他代碼都注釋掉了,隻管往記憶體寫資料就行了。
注意
1. StartVideoPreview的參數用結構體RECT會報錯,直接使用Rectangle結構體即可。
2. 使用GetDspCount總是隻傳回可用的Dsp數量,而用GetTotalDSPs可以擷取所有的Dsp數量。
3. 再強調一遍,雖然我這裡沒有把委托執行個體化成 成員變量,也能調試通過,但是強烈建議您把這些都寫成 成員變量然後在窗體初始化時初始化!
4. 本文是後續伺服器端文章的基礎,務必細心調試,我敢說如果本文的功能你達到了——你的伺服器端可以說完成了60%!!