(2016-08-23 18:27:20)
转载▼
标签: it互联网gb28181siprtp |
分类: 技术 |
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包传输时如果网络情况不太好,它的到达顺序是不固定的,即有可能先发的包后收到,这个处理起来非常麻烦。如果使用链表排序又会降低系统性能。因此需要做好内存管理。