天天看點

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

繼續閱讀