天天看点

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

继续阅读