天天看點

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

前言

     錄像功能是監控系統中最重要的功能之一,除了本文的功能實作外,還需要你自己考慮合适的存儲政策:存儲大小、時間段、存儲盤符等。

注意

     本系列文章限于學習交流,注重過程,由于涉及公司,是以不提供源代碼下載下傳,非常抱歉!!但是請大家放心,核心、實作以及其他能夠貼出來的代碼我都會貼出來,并且争取盡所能的回答留言裡的每一個問題,感謝大家關注,歡迎交流 :)

系列

推薦文章

正文

     一、抓圖

          這個功能沒有在vc++伺服器端找到對應的代碼,但是google到了一段csdn求助的代碼: 

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

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# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

          c#:

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

            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");

            }

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

          注意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()

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

        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;

        }

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

               streamdirectreadcallback

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

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;

}

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

               chkvisiondlg::onstop()

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

                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--;

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

               代碼說明:

                    1.     從startcap和stopcap的按鈕事件可以看得出主要實作寫檔案頭和檔案尾的功能,注意_write函數。

                    2.     而上一章我們講到了回調函數streamdirectreadcallback,主要是将資料寫到記憶體中,從代碼能看出回調中是邊寫記憶體邊寫檔案的代碼,而且輸出就是.264檔案。由于回調從啟動開始(允許被用戶端通路),就一直不停的在調用這個回調,根據斷點調試可以看得出當frametype == pktsysheader時表示的就是檔案頭,并且隻執行一次,這樣在點選startcap按鈕時就直接将這個儲存的檔案頭的資料寫入檔案了,用ue打開.264的檔案可以發現前幾個字元總是以4hkh開頭的檔案。

                    3.     注意gfilehandle是一個檔案指針數組,檔案被打開後回調中就一直往這個檔案指針寫資料!!

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

        //用于存放頭檔案

        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();

C# 視訊監控系列(9):伺服器端——資料捕獲(抓圖 + 錄像)

          代碼說明:

               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

繼續閱讀