天天看点

C# 视频监控系列(8):服务器端——预览和可被客户端连接

前言

     在客户端相关的文章还没有写出来的时候,服务器端已经差不多了,没有很及时的把文章一篇接一篇的写是有理由的——有些功能我项目中暂时没有加入,只是对照api知道有这个功能,边写文章边做例子,这样一来发现有些api封装的不对,所以把这系列的文章写的速度都放慢了,以求尽量每一篇文章都正确。当然还是免不了找借口说太忙,现在在写播放器部分的代码,进展目前看来还顺利: )

注意

     本系列文章限于学习交流,注重过程,由于涉及公司,所以不提供源代码下载,非常抱歉!!但是请大家放心,核心、实现以及其他能够贴出来的代码我都会贴出来,并且争取尽所能的回答留言里的每一个问题,感谢大家关注,欢迎交流 :)

系列

推荐文章

正文

     一、vc++ demo里关于这两个功能的实现和分析

          基本上每段代码都可以从oninitdialog这个方法开始分析

          1.1.     vc++ code:

               hikvisiondlg.cpp 的oninitdialog方法中的关键代码

C# 视频监控系列(8):服务器端——预览和可被客户端连接

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

C# 视频监控系列(8):服务器端——预览和可被客户端连接

               hikvisiondlg.cpp 的streamdirectreadcallback方法

C# 视频监控系列(8):服务器端——预览和可被客户端连接

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;

}

C# 视频监控系列(8):服务器端——预览和可被客户端连接

               videowin.cpp的onpaint方法               

startvideopreview(&dc);

               videowin.cpp的startvideopreview方法

C# 视频监控系列(8):服务器端——预览和可被客户端连接

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

C# 视频监控系列(8):服务器端——预览和可被客户端连接

          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:

C# 视频监控系列(8):服务器端——预览和可被客户端连接

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

C# 视频监控系列(8):服务器端——预览和可被客户端连接

          代码说明:

               1.     仅仅实现服务器端的预览代码并不多,这也是在vc++ demo中不断注释代码、在已经成功完成大部分功能的基础上才试出来的,可见预览和服务器启动是相对独立的。

               2.     form2_move是窗体移动时执行的,在vc++的也是在窗体移动中进行了同样处理,否则你一移动窗体会出现难看的一幕呢 : )

               3.     startvideopreview的参数rect *rect 直接使用rectangle结构体即可。

               4.     panel1是窗体是的一个面板panel。

     三、让客户端连接并预览

C# 视频监控系列(8):服务器端——预览和可被客户端连接

        //将委托声明为成员变量!!

        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;

C# 视频监控系列(8):服务器端——预览和可被客户端连接

               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%!!

修改记录

     1.     2009-3-30

          将stream_direct_read_callback声明为成员变量,发现不声明成成员变量在vs里面调试可以运行通过(有时候),但是直接运行exe文件会报内存出错!!

结束

     这篇文章在我研究的时候花了将近1个多星期,主要症状就是能预览,客户端死活都看不到画面,能连接!!甚至找了vc++牛人(不会c#)帮忙分析了都没能出来,不过倒是帮我弄得能调试源代码了,也是在无意中从头到尾整理代码的时候出来的(得到上司提醒整理代码),极度兴奋!!

转载:http://www.cnblogs.com/over140/archive/2009/03/11/1397378.html

继续阅读