國標GB/T28181視頻流媒體網頁無插件直播——註冊、心跳和註銷協議說明介紹

EasyGBS國標(GB28181)流媒體服務軟件提供用戶管理及Web可視化頁面管理; 提供設備狀態管理,可實時查看設備是否掉線等信息; 實時流媒體處理,PS(TS)轉ES; 設備狀態監測、雲臺控制、錄像檢索、回放; 提供RTSP、RTMP、HTTP-FLV、HLS等多種協議流輸出; 對外提供服務器獲取狀態、信息,控制等HTTP API接口。c++

EasyGBS2.5D架構圖.png

今天咱們就來簡單介紹下注冊、心跳和註銷這幾個協議。註冊和註銷很好理解,就是Expires: 0的時候爲註銷,心跳也很簡單就是一個簡單的無應答message。安全

下面將註冊的流程圖簡單摘出來一下:服務器

6.jpg

根據流程圖仍是比較容易讀懂,下面粘貼一下抓包信息:session

REGISTER sip:34020000002000000001@192.168.1.81:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI
To: <sip:64000000002000000001@192.168.1.93:5060>
Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93
CSeq: 5 REGISTER
Contact: <sip:64000000002000000001@192.168.1.93:5060>
Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="bd2e4df9e3d9b280", uri="sip:64000000002000000001@192.168.1.93:5060", response="9c8411f2b96c5aef55eb136ba3f34655", algorithm=MD5
Max-Forwards: 70
User-Agent: iVMS 1.0
Expires: 200
Content-Length: 0

SIP/2.0 401 Unauthorized
To: <sip:64000000002000000001@192.168.1.93:5060>;tag=67239569_53173353_7ce4590f-587d-4d44-9690-96498367c675
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME;received=192.168.1.93
CSeq: 5 REGISTER
Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI
WWW-Authenticate: Digest realm="3402000000",nonce="31ada55697307236"
Content-Length: 0

REGISTER sip:34020000002000000001@192.168.1.81:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-3068be5-lnl05y0C
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI
To: <sip:64000000002000000001@192.168.1.93:5060>
Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93
CSeq: 6 REGISTER
Contact: <sip:64000000002000000001@192.168.1.93:5060>
Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="31ada55697307236", uri="sip:64000000002000000001@192.168.1.93:5060", response="3526a5a5dd91a45e19d1e6a65704457f", algorithm=MD5
Max-Forwards: 70
User-Agent: iVMS 1.0
Expires: 200
Content-Length: 0

SIP/2.0 200 OK
To: <sip:64000000002000000001@192.168.1.93:5060>;tag=86156704_53173353_d7fb589f-4357-4fae-ae32-47c38d81fff1
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-3068be5-lnl05y0C;received=192.168.1.93
CSeq: 6 REGISTER
Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI
Contact: <sip:64000000002000000001@192.168.1.93:5060>
Expires: 200
Date: 2017-05-19T18:09:17.033
Content-Length:

其中註冊要注意的是,註冊的時候有個回覆時間格式須要進行上下級和設備之間校時功能:架構

心跳:

MESSAGE sip:34020000002000000001@192.168.1.81:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-2a2b21b-nddru7fy
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=UL61Qycy
To: <sip:34020000002000000001@192.168.1.81:5060>
Call-ID: 5be796e4-b5ab-592e-cc13-64f34ea57528@192.168.1.93
CSeq: 35 MESSAGE
Contact: <sip:192.168.1.93:5060>
Content-Type: Application/MANSCDP+xml
Max-Forwards: 70
User-Agent: iVMS 1.0
Content-Length:   151

<?xml version="1.0"?>
<Notify>
<CmdType>Keepalive</CmdType>
<SN>26</SN>
<DeviceID>64000000002000000001</DeviceID>
<Status>OK</Status>
</Notify>
SIP/2.0 200 OK
To: <sip:34020000002000000001@192.168.1.81:5060>;tag=69112542_53173353_89bb4bc2-7f74-42a8-a102-6b6490b6daa2
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-2a2b21b-nddru7fy;received=192.168.1.93
CSeq: 35 MESSAGE
Call-ID: 5be796e4-b5ab-592e-cc13-64f34ea57528@192.168.1.93
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=UL61Qycy
Content-Length: 0

註銷:

REGISTER sip:34020000002000000001@192.168.1.81:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI
To: <sip:64000000002000000001@192.168.1.93:5060>
Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93
CSeq: 5 REGISTER
Contact: <sip:64000000002000000001@192.168.1.93:5060>
Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="bd2e4df9e3d9b280", uri="sip:64000000002000000001@192.168.1.93:5060", response="9c8411f2b96c5aef55eb136ba3f34655", algorithm=MD5
Max-Forwards: 70
User-Agent: iVMS 1.0
Expires: 0
Content-Length: 0

SIP/2.0 401 Unauthorized
To: <sip:64000000002000000001@192.168.1.93:5060>;tag=67239569_53173353_7ce4590f-587d-4d44-9690-96498367c675
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME;received=192.168.1.93
CSeq: 5 REGISTER
Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI
WWW-Authenticate: Digest realm="3402000000",nonce="31ada55697307236"
Content-Length: 0

REGISTER sip:34020000002000000001@192.168.1.81:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-3068be5-lnl05y0C
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI
To: <sip:64000000002000000001@192.168.1.93:5060>
Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93
CSeq: 6 REGISTER
Contact: <sip:64000000002000000001@192.168.1.93:5060>
Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="31ada55697307236", uri="sip:64000000002000000001@192.168.1.93:5060", response="3526a5a5dd91a45e19d1e6a65704457f", algorithm=MD5
Max-Forwards: 70
User-Agent: iVMS 1.0
Expires: 0
Content-Length: 0

SIP/2.0 200 OK
To: <sip:64000000002000000001@192.168.1.93:5060>;tag=86156704_53173353_d7fb589f-4357-4fae-ae32-47c38d81fff1
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-3068be5-lnl05y0C;received=192.168.1.93
CSeq: 6 REGISTER
Call-ID: b4359b71-eb2f-b6f4-2fce-8fd47e5888ff@192.168.1.93
From: <sip:64000000002000000001@192.168.1.93:5060>;tag=pck2i7pI
Contact: <sip:64000000002000000001@192.168.1.93:5060>
Expires: 0
Date: 2017-05-19T18:09:17.033
Content-Length: 0

下面粘貼一下部分註冊的代碼,因爲使用c++寫的代碼,全部只能裁剪一部分出來:dom

#include <stdlib.h>
#include <stdio.h>
#include "eXosip2/eXosip.h"
#include "osipparser2/osip_uri.h"
#include "osipparser2/osip_message.h"
#include "eXosip2.h"
 
//初始化 監聽端口
int Init(int listenport)
{
    int iRet = eXosip_init();
    if (iRet != OSIP_SUCCESS)
    {
        //DBGPrint(M_SipUA, ERROR_LEVEL, "eXosip2 fail!");
        //return -1;
        printf("eXosip2 fail! \n");
        exit(1);
    }
    iRet = eXosip_listen_addr(IPPROTO_UDP, NULL, listenport, AF_INET, 0);
    if (iRet != OSIP_SUCCESS)
    {
        eXosip_quit();
        //DBGPrint(M_SipUA, ERROR_LEVEL, "eXosip2 could not initialize transport layer!");
        //return -1;
        DBGPrint(M_SipUA, BREAK_LEVEL, "eXosip2 could not initialize transport layer! \n");
        exit(1);
    }
 
    DBGPrint(M_SipUA, BREAK_LEVEL, "%s: SipSvr Listen Port:%d Sucess!!!!", `ls_function` , listenport);
    return 0;    
}
 
/*******************************************************************************
* Function: 
*    Digest MD5 authorization validate process.
*******************************************************************************/
//MD5鑑權
bool MD5AuthValidate(char *pMethod, osip_authorization_t *pAuthEcho)
{
    if (NULL == pAuthEcho)
    {
        DBGPrint(M_SipUA, ERROR_LEVEL, "pAuthField is null pointer!");
        return false;
    }
 
    char *pAlgorithm  = NULL;
    char *pUsername   = NULL;
    char *pRealm      = NULL;
    char *pPasswd     = NULL;
    char *pNonce      = NULL;
    char *pNonceCount = NULL;
    char *pCNonce     = NULL;
    char *pQop        = NULL;
    char *pCMethod     = NULL;
    char *pUri        = NULL;
    char sNullAlg[]   = "";  //"MD5";
    char sNullQop[]   = "";  //"auth";
    char SessionKey[MD5_SESSION_KEY_LEN+1] = {0};
    char Response[MD5_RESPONSE_LEN] = {0};
    char *pResponse2  = NULL;
    
    if (NULL == pAuthEcho->algorithm)
        pAlgorithm = unquote(sNullAlg);
    else
        pAlgorithm = unquote(pAuthEcho->algorithm);
    
    pUsername  = unquote(pAuthEcho->username);
    pRealm     = unquote(pAuthEcho->realm);
    pPasswd    = unquote(CSBase::m_SipRegPasswd);
    pNonce     = unquote(pAuthEcho->nonce);
    pCNonce    = unquote(pAuthEcho->cnonce);
 
    if (NULL == pUsername)
    {
        DBGPrint(M_SipUA, ERROR_LEVEL, "%s Get username is null error!",__FUNCTION__);
        return false;
    }
    if(strcmp(pUsername, CSBase::m_SipRegUserName) != 0)
    {
    //    DBGPrint(M_SipUA, ERROR_LEVEL, "%s %s username error!",__FUNCTION__,pAuthEcho->username);
    //    return false;
    }
    
    //calculate session key
    DigestCalcHA1(pAlgorithm, pUsername, pRealm, pPasswd, pNonce, pCNonce, SessionKey);
    
    pNonceCount = unquote(pAuthEcho->nonce_count);
    
    if (NULL == pAuthEcho->message_qop)
        pQop = unquote(sNullQop);
    else
        pQop = unquote(pAuthEcho->message_qop);
    
    pCMethod = unquote(pMethod);
    pUri    = unquote(pAuthEcho->uri);
    
    //calculate response
    DigestCalcResponse(SessionKey, pNonce, pNonceCount, pCNonce, pQop, pCMethod, pUri, (char*)"", Response);
    
    pResponse2  = unquote(pAuthEcho->response);
    if (strcmp(Response, pResponse2) != 0)
    {
        DBGPrint(M_SipUA, ERROR_LEVEL, "Authorization failed <%s, %s>!", Response, pResponse2);
        return false;
    }
    
    return true;
}
 
//回覆沒有包體的響應
int SendRegisterAnswer(int Tid, int Code, bool bSetTime)
{
    osip_message_t * pMsgAnswer = NULL;
    int ret = eXosip_message_build_answer(Tid, Code,&pMsgAnswer);
    if(ret == OSIP_SUCCESS && pMsgAnswer != NULL )
    {
        if (bSetTime == true)
        {
            osip_header_t*  pHeader = NULL;
            char            TmpBuf[CLIP_BUFFER_SIZE+1];
            //initialize Date header
            int iRet = osip_header_init(&pHeader);
            if (OSIP_SUCCESS == iRet)
            {
                time_t now;
                struct timeval tv;
                //Get current time
                gettimeofday(&tv,NULL);
 
                now = tv.tv_sec;
    //            time(&now);
                //struct tm* pTmVal = localtime(&now);
                struct tm STm = {0};
                struct tm* pTmVal = localtime_r(&now, &STm);  //使用線程安全函數
                if (pTmVal != NULL)
                {
                    //系統啓用夏令時,須要減去一個小時。
                    if (1 == pTmVal->tm_isdst)
                    {
                        now -= 3600;
                        //pTmVal = localtime(&now);
                        pTmVal = localtime_r(&now, &STm);  //使用線程安全函數
                        if (NULL == pTmVal)
                            return -1;
                    }
                    
                    //2010-07-26 16:05:10
                    snprintf(TmpBuf, CLIP_BUFFER_SIZE, "%4d-%02d-%02dT%02d:%02d:%02d.%03d", pTmVal->tm_year+1900, pTmVal->tm_mon+1, pTmVal->tm_mday, pTmVal->tm_hour, \
                        pTmVal->tm_min, pTmVal->tm_sec,(int)(tv.tv_usec/1000));
                
                    //set Date field
                    osip_header_set_name(pHeader, osip_strdup("Date") );
                    osip_header_set_value(pHeader, osip_strdup(TmpBuf) );
                    osip_list_add(&pMsgAnswer->headers, pHeader, -1);
                }
            }
        }
        
        eXosip_message_send_answer(Tid, Code, pMsgAnswer);
    }
    else
    {
        DBGPrint(M_SipUA, ERROR_LEVEL, "%s:Build Message Answer Failed Tid<%d> Code<%d>!", `ls_function` , Tid, Code);
    }
    return 0;
}
 
//初始接收的註冊信息
int ProceRegister(const eXosip_event_t* pSipEvt)
{
    int iReturnCode = -1;
    int iRet = -1;
    osip_authorization_t *pWWWAuEcho = NULL;
    iRet = osip_message_get_authorization(pSipEvt->request, 0, &pWWWAuEcho);
    if (iRet != -1)
    {
        //鑑權, 鑑權成功回覆200OK
        if (MD5AuthValidate(pSipEvt->request->sip_method, pWWWAuEcho))
        {
            //鑑權成功,回覆200成功
            //添加用戶信息
            SendRegisterAnswer(pSipEvt->tid, SIP_OK, true);
        }
        else
        {
            //鑑權失敗,回覆403
            SendRegisterAnswer(pSipEvt->tid, SIP_FORBIDDEN, false);
        }
    }
    else
    {
        //回覆401 , 待認證
        osip_www_authenticate_t *pWWWAuth = NULL;
        iRet = osip_www_authenticate_init(&pWWWAuth);
        if (-1 == iRet)
        {
            DBGPrint(M_SipUA, ERROR_LEVEL, "Failed to init www-authenticate header");
            return -1;    
        }
        char RTag[RANDOM_TAG_LEN+1] = {0};
        char Nonce[DIGEST_NONCE_LEN+1] = {0};
        char TmpBuf[CLIP_BUFFER_SIZE/2+1] = {0};
        SIPGenerateNonce(Nonce, 0, GenerateRandomTag(RTag) );
        osip_www_authenticate_set_auth_type(pWWWAuth, osip_strdup("Digest") );
        memcpy(TmpBuf, CSBase::m_SipSvrPubID, 10);
        char realm[CLIP_BUFFER_SIZE];
        snprintf(realm, CLIP_BUFFER_SIZE/2, "\"%s\"", TmpBuf);
        osip_www_authenticate_set_realm(pWWWAuth, osip_strdup(realm) );
        snprintf(TmpBuf, CLIP_BUFFER_SIZE/2, "\"%s\"", Nonce);
        osip_www_authenticate_set_nonce(pWWWAuth, osip_strdup(TmpBuf) );
 
        char *pDest = NULL;
        osip_www_authenticate_to_str(pWWWAuth, &pDest);
 
        osip_message_t * pSRegister = NULL;
        iReturnCode = eXosip_message_build_answer(pSipEvt->tid,SIP_UNAUTHORIZED,&pSRegister);
        if ( iReturnCode == 0 && pSRegister != NULL )
        {
            osip_message_set_www_authenticate(pSRegister,pDest);
            osip_message_set_content_type(pSRegister,"Application/MANSCDP+xml");
            eXosip_message_send_answer(pSipEvt->tid,SIP_UNAUTHORIZED,pSRegister);
        }
 
        osip_www_authenticate_free(pWWWAuth);
        osip_free(pDest);        
    }
 
    return 0;
}
 
 
int ProceXsipEvt(eXosip_event_t* pSipEvt)
{
    //Check input parameter exception
    if (NULL == pSipEvt)
    {
        DBGPrint(M_SipUA, ERROR_LEVEL, "%s: pSipEvt is null pointer!", `ls_function` );
        return -1;
    }
    //Check eXosip event type
    switch (pSipEvt->type) 
    {
    case EXOSIP_MESSAGE_NEW: 
        if (MSG_IS_NOTIFY(pSipEvt->request)) 
        {    
            //ProceNotify(pSipEvt);
        }
        else if(MSG_IS_MESSAGE(pSipEvt->request))
        {
            //ProceMessage(pSipEvt);
        }
        else if(MSG_IS_REGISTER(pSipEvt->request))
        {
            ProceRegister(pSipEvt);
        }
        break;
    case EXOSIP_SUBSCRIPTION_ANSWERED:
    case EXOSIP_SUBSCRIPTION_REQUESTFAILURE:
        {
            //ProcSubScribleAnswer(pSipEvt);    
        }
        break;
    case EXOSIP_SUBSCRIPTION_NOTIFY:
        {
            //ProceNotify(pSipEvt);
        }
        break;
    case EXOSIP_CALL_PROCEEDING:
    case EXOSIP_CALL_RINGING:
        break;
        
    case EXOSIP_CALL_ANSWERED:
        {    
            eXosip_call_t* pJCall = NULL;
            if (OSIP_SUCCESS == eXosip_call_find(pSipEvt->cid, &pJCall) )
            {    
                //ProcCallAnswer(pSipEvt);
            }
        }
        break;
        
    case EXOSIP_CALL_NOANSWER:
    case EXOSIP_CALL_REDIRECTED:
    case EXOSIP_CALL_REQUESTFAILURE:
    case EXOSIP_CALL_SERVERFAILURE:
    case EXOSIP_CALL_GLOBALFAILURE:
    case EXOSIP_CALL_TIMEOUT:
        {
            if (pSipEvt->request == NULL || pSipEvt->response == NULL)
            {
                DBGPrint(M_SipUA, ERROR_LEVEL, "%s: Recv Sip Type<%d:%s> Error!", `ls_function` , pSipEvt->type, GetRecvSipType(pSipEvt->type));
                break;
            }
            DBGPrint(M_SipUA, ERROR_LEVEL, "%s: ******** receive device:<%s> error !response status code:<%d> username:<%s>!", `ls_function` , pSipEvt->request->to->url->username, pSipEvt->response->status_code, pSipEvt->request->req_uri->username );
            eXosip_call_t* pJCall = NULL;
            if (OSIP_SUCCESS == eXosip_call_find(pSipEvt->cid, &pJCall) )
            {
                //ProcCallAnswer(pSipEvt);
            }
        }
        break;
        
    case EXOSIP_CALL_MESSAGE_NEW:
        if (MSG_IS_BYE(pSipEvt->request) )
        {
            DBGPrint(M_SipUA, ERROR_LEVEL, "%s: ******** imcoming BYE for DevAor<%s>!", `ls_function` , CSBase::URIToAOR(pSipEvt->request->from->url) );
        }
        break;
        
    case EXOSIP_CALL_MESSAGE_ANSWERED:
        //BYE response
        break;
        
    case EXOSIP_CALL_CLOSED:
        break;
        
    case EXOSIP_CALL_RELEASED:
        break;
        
    case EXOSIP_MESSAGE_REQUESTFAILURE:
        break;
    case EXOSIP_MESSAGE_ANSWERED:
        break;
    default:
        DBGPrint(M_SipUA, ERROR_LEVEL, "%s: not handle <%d:%s> event type!", `ls_function` , pSipEvt->type, GetRecvSipType(pSipEvt->type));
        return -1;
    }
}
 
int main()
{
    //初始化exosip協議棧端口
    Init(5060);
    //等待接收sip數據
    while(1)
    {
        //---------------- eXosip running process ----------------//
        eXosip_execute();
        eXosip_event_t* pSipEvt = eXosip_event_wait(0, 50);
        while (pSipEvt != NULL)
        {
            eXosip_lock();
            ProceXsipEvt(pSipEvt);
            eXosip_default_action(pSipEvt);
            eXosip_unlock();
            eXosip_event_free(pSipEvt);
            pSipEvt = eXosip_event_wait(0, 50);
        }
        
        usleep(50000);    
    }
    
}

GB/T28181視頻流媒體解決方案播放效果可見下圖:
1.png函數

相關文章
相關標籤/搜索