前言
錄像功能是監控系統中最重要的功能之一,除了本文的功能實作外,還需要你自己考慮合适的存儲政策:存儲大小、時間段、存儲盤符等。
注意
本系列文章限于學習交流,注重過程,由于涉及公司,是以不提供源代碼下載下傳,非常抱歉!!但是請大家放心,核心、實作以及其他能夠貼出來的代碼我都會貼出來,并且争取盡所能的回答留言裡的每一個問題,感謝大家關注,歡迎交流 :)
系列
推薦文章
正文
一、抓圖
這個功能沒有在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。
補充:
1. 錄像的時候務必考慮單錄像檔案的大小以及磁盤空間不夠的問題,最好還能考慮下分時段監控等。
2. 注意儲存檔案頭的變量fileheader,如果分檔案連續儲存的話有可能出現第一個檔案能播放,後面的都不能播放了,可能是檔案頭變量的資料類型問題,你可以換byte[] -> intptr儲存試試看。
3. 自帶的示例裡面有播放器極其源碼,打開播放器,直接将.264檔案拖拽到裡面就可以播放了;如果報錯那麼說明你的錄像有問題!!
結束
雖然代碼都給出來了,但是裡面整個過程還是需要了解的,一定要配合vc++自帶的例子進行調試編寫。
轉載:http://www.cnblogs.com/over140/archive/2009/03/16/1410262.html