一、抓圖
這個功能沒有在VC++伺服器端找到對應的代碼,但是GOOGLE到了一段CSDN求助的代碼:
int ret=GetJpegImage(aa,bb,cc,dd);
if(ret==0)
{
CString str;
str.Format("ch%02d_%s.jpg",iLastSelect,csStartTime);
FILE *pFile=fopen(str.GetBuffer(0),"wb");//Buffer應該是個緩沖區
if(pFile)
fwrite(bb,cc,1,pFile); //存儲圖像
fclose(pFile);
C#:
byte[] imageBuf = new byte[704*576*2];
int size = 704*576*2;
HikVisionSDK.GetJpegImage(ChannelHandle, imageBuf, out size, 100);
using (MemoryStream ms = new MemoryStream(imageBuf))
{
Image image = Image.FromStream(ms, true);
image.Save("C:\\1.jpg");
}
注意GetJpegImage的參數說明!!并且請注意,由于這個示例,發現前面的(GetJpegImage/GetOriginalImage)API錯誤了,請你及時更新!!
public static extern int GetOriginalImage(IntPtr hChannelHandle, byte[] ImageBuf, out int Size);
public static extern int GetJpegImage(IntPtr hChannelHandle, byte[] ImageBuf, out int Size, uint nQuality);
儲存為bmp的方法請自行嘗試,應該是差不多的: )
二、錄像
關于錄像的檔案總共有三個部分,分别是檔案頭、資料流和檔案尾,這裡先給出代碼,然後再進行說明。
VC++:
CHKVisionDlg::OnStart()
for(int i = 0; i < GetTotalDSPs(); i++){
m_bDspPreset[i]=TRUE;
if(m_bDspPreset[i]){
char fileName[256];
sprintf(fileName, "d:\\stream%d_%d.264", i, gFileNum++/GetTotalDSPs());
gFileHandle[i] = _open(fileName, _O_CREAT | _O_BINARY | _O_WRONLY| _O_TRUNC, _S_IREAD | _S_IWRITE);
if(gFileHandle[i] == -1){
TRACE("channel %d file open error\n,i");
return;
}
gChannelFrames[i] = 0;
gChannelTotalLength[i] = 0;
gChannelFramesLost[i] = 0;
gChannelOverflow[i] = 0;
gCurrentFileLen[i] = 0;
_write(gFileHandle[i], FileHeader[i], FileHeaderLen);
// could not be start again untill stopped first
//m_bDspPreset[i] = FALSE;
gCaptureStartedNum++;
// let the threads have chance to run
//Sleep(500);
}else
gFileHandle[i] = -1;
}
StreamDirectReadCallback
int __cdecl StreamDirectReadCallback(ULONG channelNum,void *DataBuf,DWORD Length,int frameType,void *context)
{
//CHKVisionDlg * lpDlg = (CHKVisionDlg*)context;
//return lpDlg->ProcCallBack(channelNum, DataBuf, Length, frameType);
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
//_write(gFileHandle[channelNum], DataBuf, Length);
return 0;
}
CHKVisionDlg::OnStop()
ASSERT(gFileHandle[i] != -1);
// StopVideoCapture(ChannelHandle[i]);
//lseek(gFileHandle[i], 0, SEEK_SET);
//FRAMES_STATISTICS fs;
//GetFramesStatistics(ChannelHandle[i], &fs);
//ULONG frames = fs.AudioFrames + fs.VideoFrames;
//TRACE("channel %i has %x frames written\n", i, frames);
#define END_CODE 0x00000002
ULONG endCode = END_CODE;
_write(gFileHandle[i], &endCode, sizeof(ULONG));
_close(gFileHandle[i]);
///add v34
if (bEncodeCifAndQcif[i])
_close(gFileHandleQcif[i]);
gCaptureStartedNum--;
代碼說明:
1. 從StartCap和StopCap的按鈕事件可以看得出主要實作寫檔案頭和檔案尾的功能,注意_write函數。
2. 而上一章我們講到了回調函數StreamDirectReadCallback,主要是将資料寫到記憶體中,從代碼能看出回調中是邊寫記憶體邊寫檔案的代碼,而且輸出就是.264檔案。由于回調從啟動開始(允許被用戶端通路),就一直不停的在調用這個回調,根據斷點調試可以看得出當frameType == PktSysHeader時表示的就是檔案頭,并且隻執行一次,這樣在點選StartCap按鈕時就直接将這個儲存的檔案頭的資料寫入檔案了,用UE打開.264的檔案可以發現前幾個字元總是以4HKH開頭的檔案。
3. 注意gFileHandle是一個檔案指針數組,檔案被打開後回調中就一直往這個檔案指針寫資料!!
//用于存放頭檔案
byte[] FileHeader;
//檔案頭長度
int FileHeaderLen;
//是否開始捕獲檔案 0 未啟用 1 啟用
volatile int CaptureState;
/// <summary>
/// 開始錄像
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
//寫入頭檔案
using (FileStream fs = new FileStream("C:\\hik.264", FileMode.Create))
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(FileHeader);
bw.Flush();
bw.Close();
CaptureState = 1;
uint endCode = 0x00000002;
/// 停止錄像
private void btnStop_Click(object sender, EventArgs e)
CaptureState = 0;
using (FileStream fs = new FileStream("C:\\hik.264", FileMode.Append))
bw.Write(endCode);
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;
int status = 0;
if (frameType > 0)
if (frameType == FrameType_t.PktSysHeader)
{
FileHeader = new byte[Length];
Marshal.Copy(DataBuf, FileHeader, 0, Length);
FileHeaderLen = Length;
if (frameType == FrameType_t.PktIFrames || frameType == FrameType_t.PktSubIFrames)
status = 1;
else
status = 0;
if (frameType == FrameType_t.PktMotionDetection || frameType == FrameType_t.PktOrigImage)
return 0;
if (Length == 0)
//TRACE("no data ?\n");
return 0;
if (frameType == FrameType_t.PktAudioFrames)
WriterVideoCapture(Length, DataBuf);
//寫檔案
// _write(gFileHandleQcif[channelNum],DataBuf,Length);
//HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 1);
// _write(gFileHandle[channelNum], DataBuf, Length);
HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);
else if (frameType == FrameType_t.PktSubIFrames || frameType == FrameType_t.PktSubPFrames || frameType == FrameType_t.PktSubBBPFrames || frameType == FrameType_t.PktSubSysHeader)
HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 1);
else
return 0;
/// 将資料流寫入視訊檔案
/// <param name="length"></param>
/// <param name="dataBuf"></param>
private void WriterVideoCapture(int length, IntPtr dataBuf)
if (CaptureState == 1)
using (FileStream fs = new FileStream("C:\\hik.264", FileMode.Append))
BinaryWriter bw = new BinaryWriter(fs);
byte[] byteBuf = new byte[length];
Marshal.Copy(dataBuf, byteBuf, 0, length);
bw.Write(byteBuf);
bw.Flush();
bw.Close();
代碼說明:
1. 回調函數STREAM_DIRECT_READ_CALLBACK1是在上篇文章的基礎上修改的,也主要是參照的VC++的源代碼改寫的。
2. CaptureState變量主要用于STREAM_DIRECT_READ_CALLBACK1中控制是否寫檔案。
3. btnStart_Click與btnStop_Click分别代表界面上的開始錄像和停止錄像按鈕。
4. 注意寫檔案的方式,開始錄像用FileMode.Create,持續寫入用FileMode.Append。