三頁搞定GB2818/SIP/RTP、PS封裝

GB2818集成了sip通訊、RTP封裝及PS流封裝。初涉者不瞭解整體框架,如果每一項去啃讀,每項有幾百頁的標準文檔,啃完估計該吐血了。實際上雖然GB28181裏用了3個項目,但每個單元基本都是固定的,用法比較簡單。

一、關於SIP:GB28181裏只是簡單用了開源的eXosip2和osip2,有興趣可以下載編繹,或直接使用別人編繹好和.h、.lib、.dll,初始化也蠻簡單

1> 初始化

osip_trace_initialize(OSIP_INFO1, NULL);

long result = eXosip_init();

if (result != OSIP_SUCCESS)

       return -1;

result = eXosip_listen_addr(IPPROTO_UDP, NULL, m_iMySipPort, AF_INET, 0);

if (result != OSIP_SUCCESS)

       return -2;

result = eXosip_listen_addr(IPPROTO_TCP, NULL, m_iMySipPort, AF_INET, 0);

if (result != OSIP_SUCCESS)

       return -3;

2>SIP消息處理線程

eXosip_event_t *pSipEvent;

while(m_bActiveSipMsgThread)

{

       pSipEvent = eXosip_event_wait(0, 50);

       eXosip_lock();

       eXosip_default_action(pSipEvent);

       eXosip_automatic_refresh();

       eXosip_automatic_action();

       eXosip_unlock();

       if(pSipEvent==NULL)

              continue;

       switch(pSipEvent->type)

       {

              case EXOSIP_REGISTRATION_NEW://有註冊進來

                     break;

              case EXOSIP_REGISTRATION_REFRESHED:

                     break;

              case EXOSIP_REGISTRATION_TERMINATED:

                     break;

              case EXOSIP_CALL_NOANSWER:

                     break;

              case EXOSIP_CALL_ANSWERED://請求視頻流回覆成功

                     break;

              case EXOSIP_CALL_CANCELLED:

                     break;

              case EXOSIP_CALL_TIMEOUT:

                     break;

              case EXOSIP_CALL_CLOSED:

                     break;

              case EXOSIP_CALL_MESSAGE_ANSWERED:

                     break;

              case EXOSIP_MESSAGE_NEW://下級平臺保活、設備查詢回覆

                     break;

              case EXOSIP_MESSAGE_ANSWERED://查詢

                     break;

              case EXOSIP_REGISTRATION_FAILURE:

                     break;

              case EXOSIP_REGISTRATION_SUCCESS:

                     break;

              default:

                     break;

       }

       eXosip_event_free(pSipEvent);//釋放

}

3>關於sdp

sSDP.Format("v=0\r\n"                      

            "o=%s 0 0 IN IP4 %s\r\n"        //owner/creator and session identifier

            "s=%s\r\n"                  //session name

            "c=IN IP4 %s\r\n"//connection information

            "t=%d %d\r\n"                   //time the session is active

            "m=video %d RTP/AVP 96 98\r\n"  //media name and transport address

            "a=recvonly\r\n"                //zero or more session attribute lines

            "a=rtpmap:96 PS/90000\r\n"      //

            "a=rtpmap:98 H264/90000\r\n"

            "y=%d%sd\r\n",               //SSRC Y字段:爲十進制整數字符串,表示SSRC值。格式如下:D ddddd dddd(第一位爲歷史或實時媒體,2-5爲SipID的中間(-8),4位StreamID

二、關於RTP

RTP定義本身很複雜,但在GB28181裏很簡單,基本就是固定的12個字節

每一幀視頻或音頻數據,先封裝成ps流,再封裝成RTP包,這個處理是gb28181裏最爲複雜的部分。

RTP

struct RTPHeader

{

    uint8_t csrccount:4;

    uint8_t extension:1;

    uint8_t padding:1;

    uint8_t version:2;

    uint8_t payloadtype:7;

    uint8_t marker:1;

    uint16_t sequencenumber;

    uint32_t timestamp;

    uint32_t ssrc;

};

細看比較複雜,其實就是一個12字節的頭,後面是av數據。需要注意以下幾個標識

Marker:如果爲1,表明該幀已經結束,爲0表示是連接的音視頻數據

Sequencenumber:RTP包順序,比如一幀K幀,200K,順序可能是0-199,最後一個包Marker位爲1。

Ssrc:爲流標識,實際可以多個流往一個端口上發,通過此位標識。

Payloadlength:爲該包的長度,如果是前面的包,此值通常爲1024,最後一個長度爲總長除1024的餘數

Payloadoffset:通常爲12,rtp頭信息。

Timestamp:這個值並非每幀的時間戳,但是一個音頻或視頻包此項是相同的。

三、關於PS流封裝

若干個PS包會組成一個AV包(Marker標識一幀結束),以00、00、01在個字節固定開頭,至少需要6個字節,根據第4個字節判斷是音頻幀還是視頻幀

0xBA :I幀(關鍵幀),後面還跟有8字節的ps pack header信息,即ps pack header信息長度爲14字節。

0xBB: // ps system header <18字節>

0xBC:// ps map header <30字節>

0xC0:// 音頻頭

0xE0: //視頻頭 <19字節>

最後根據各字節解析出音視頻包的實際長度。比如一個I幀爲64400,則後面的64400/1024=63個包全是該I幀數據。音頻幀要簡單一些,沒有ps header及map header.

四、注意事項

l          使用多線程接受UDP數據,如果用單線程,需要使用map去檢索。

l          實際UDP包傳輸時如果網絡情況不太好,它的到達順序是不固定的,即有可能先發的包後收到,這個處理起來非常麻煩。如果使用鏈表排序又會降低系統性能。因此需要做好內存管理。

轉載地址:http://blog.sina.com.cn/s/blog_5375c7f90102x4bp.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章