https://blog.csdn.net/ichenwin/article/details/100086930
PS封裝格式:GB28181協議RTP傳輸
在安防行業,有個協議是無論如何都要適配的,因為公安監控網絡用的就是它,它就是:GB28181。而這份協議主要由海康制定,所以除了海康其他廠商想要適配都會少許有點兒麻煩。題主前東家便是海康,這里稍微分析下該協議幾個容易搞混的細節,記錄在此,方便以后自己查閱,也方便諸位。
1. GB28181要求的RTP流格式
首先,我們來看看I幀的PS流格式,這里需要注意的是SPS、PPS之前要加上PES頭部。如下圖所示,其中綠色部分就是我們拿到的H.264裸流數據,須將它拆分成三段並在前面加上PES頭部。這一點在GB28181標准中沒有細說,需要通過分析海康IPC流才能看出。
一般情況下IDR幀很大,超過了RTP的負載長度限制(1400字節),所以上面這一個I幀要拆分成若干包RTP分多次發送。第一包的結構如上圖所示,第二包以后RTP的結構就簡單多了,它是這樣的:
上面提到的是I幀的情況,相比它,P/B幀的幀格式真是太簡單了,因為它既沒有SYS、PSM,也沒有SPS、PPS:
P/B幀大小一般不超過1400字節,如果超過1400字節,也需分成多包RTP數據進行傳輸,超出1400部分的第二包RTP結構:
2. 頭部信息
首先是RTP包頭信息,它一般長度為12個字節
#define RTP_HDR_LEN 12
static int gb28181_make_rtp_header(char *pData, int marker_flag, unsigned short cseq, long long curpts, unsigned int ssrc)
{
bits_buffer_s bitsBuffer;
if (pData == NULL)
return -1;
bitsBuffer.i_size = RTP_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, RTP_HDR_SIZE);
bits_write(&bitsBuffer, 2, RTP_VERSION); /* rtp version 版本號,固定為2 */
bits_write(&bitsBuffer, 1, 0); /* rtp padding */
bits_write(&bitsBuffer, 1, 0); /* rtp extension */
bits_write(&bitsBuffer, 4, 0); /* rtp CSRC count */
bits_write(&bitsBuffer, 1, (marker_flag)); /* rtp marker 結束標志位,一幀圖像的最后一包RTP置1*/
bits_write(&bitsBuffer, 7, 96); /* rtp payload type,96代表PS*/
bits_write(&bitsBuffer, 16, (cseq)); /* rtp sequence */
bits_write(&bitsBuffer, 32, (curpts)); /* rtp timestamp */
bits_write(&bitsBuffer, 32, (ssrc)); /* rtp SSRC */
return 0;
}
然后是PSH頭部,它占了14個字節:
#define PS_HDR_LEN 14
static int gb28181_make_ps_header(char *pData, unsigned long long s64Scr)
{
unsigned long long lScrExt = (s64Scr) % 100;
s64Scr = s64Scr / 100;
bits_buffer_s bitsBuffer;
bitsBuffer.i_size = PS_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, PS_HDR_LEN);
bits_write(&bitsBuffer, 32, 0x000001BA); /*start codes 起始碼*/
bits_write(&bitsBuffer, 2, 1); /*marker bits '01b'*/
bits_write(&bitsBuffer, 3, (s64Scr>>30)&0x07); /*System clock [32..30]*/
bits_write(&bitsBuffer, 1, 1); /*marker bit*/
bits_write(&bitsBuffer, 15, (s64Scr>>15)&0x7FFF); /*System clock [29..15]*/
bits_write(&bitsBuffer, 1, 1); /*marker bit*/
bits_write(&bitsBuffer, 15, s64Scr & 0x7fff); /*System clock [14..0]*/
bits_write(&bitsBuffer, 1, 1); /*marker bit*/
bits_write(&bitsBuffer, 9, 0); /*SCR extension*/
bits_write(&bitsBuffer, 1, 1); /*marker bit*/
bits_write(&bitsBuffer, 22, (255)&0x3fffff); /*bit rate(n units of 50 bytes per second.)*/
bits_write(&bitsBuffer, 2, 3); /*marker bits '11'*/
bits_write(&bitsBuffer, 5, 0x1f); /*reserved(reserved for future use)*/
bits_write(&bitsBuffer, 3, 0); /*stuffing length*/
return 0;
}
后面是SYS,它包含了流類型信息,比如音頻還是視頻、視頻編碼格式
#define SYS_HDR_LEN 18
static int gb28181_make_sys_header(char *pData)
{
bits_buffer_s bitsBuffer;
bitsBuffer.i_size = SYS_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);
/*system header*/
bits_write( &bitsBuffer, 32, 0x000001BB); /*start code*/
bits_write( &bitsBuffer, 16, SYS_HDR_LEN-6); /* 減6,是因為start code加上length這兩位,占了6個字節*/
bits_write( &bitsBuffer, 1, 1); /*marker_bit*/
bits_write( &bitsBuffer, 22, 50000); /*rate_bound*/
bits_write( &bitsBuffer, 1, 1); /*marker_bit*/
bits_write( &bitsBuffer, 6, 1); /*audio_bound*/
bits_write( &bitsBuffer, 1, 0); /*fixed_flag */
bits_write( &bitsBuffer, 1, 1); /*CSPS_flag */
bits_write( &bitsBuffer, 1, 1); /*system_audio_lock_flag*/
bits_write( &bitsBuffer, 1, 1); /*system_video_lock_flag*/
bits_write( &bitsBuffer, 1, 1); /*marker_bit*/
bits_write( &bitsBuffer, 5, 1); /*video_bound*/
bits_write( &bitsBuffer, 1, 0); /*dif from mpeg1*/
bits_write( &bitsBuffer, 7, 0x7F); /*reserver*/
/*audio stream bound*/
bits_write( &bitsBuffer, 8, 0xC0); /*stream_id 音頻的流id*/
bits_write( &bitsBuffer, 2, 3); /*marker_bit */
bits_write( &bitsBuffer, 1, 0); /*PSTD_buffer_bound_scale*/
bits_write( &bitsBuffer, 13, 512); /*PSTD_buffer_size_bound*/
/*video stream bound*/
bits_write( &bitsBuffer, 8, 0xE0); /*stream_id 視頻的流id*/
bits_write( &bitsBuffer, 2, 3); /*marker_bit */
bits_write( &bitsBuffer, 1, 1); /*PSTD_buffer_bound_scale*/
bits_write( &bitsBuffer, 13, 2048); /*PSTD_buffer_size_bound*/
return 0;
}
接下來是PSM,這里面記錄了媒體信息,比如音視頻的編碼格式:
static int gb28181_make_psm_header(char *pData)
{
bits_buffer_s bitsBuffer;
bitsBuffer.i_size = PSM_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, PSM_HDR_LEN);//24Bytes
bits_write(&bitsBuffer, 24,0x000001); /*start code*/
bits_write(&bitsBuffer, 8, 0xBC); /*map stream id*/
bits_write(&bitsBuffer, 16,18); /*program stream map length*/
bits_write(&bitsBuffer, 1, 1); /*current next indicator */
bits_write(&bitsBuffer, 2, 3); /*reserved*/
bits_write(&bitsBuffer, 5, 0); /*program stream map version*/
bits_write(&bitsBuffer, 7, 0x7F); /*reserved */
bits_write(&bitsBuffer, 1, 1); /*marker bit */
bits_write(&bitsBuffer, 16,0); /*programe stream info length*/
bits_write(&bitsBuffer, 16, 8); /*elementary stream map length is*/
/*audio*/
bits_write(&bitsBuffer, 8, 0x90); /*stream_type 音頻編碼格式G711*/
bits_write(&bitsBuffer, 8, 0xC0); /*elementary_stream_id*/
bits_write(&bitsBuffer, 16, 0); /*elementary_stream_info_length is*/
/*video*/
bits_write(&bitsBuffer, 8, 0x1B); /*stream_type 視頻編碼格式H.264*/
bits_write(&bitsBuffer, 8, 0xE0); /*elementary_stream_id*/
bits_write(&bitsBuffer, 16, 0); /*elementary_stream_info_length */
/*crc (2e b9 0f 3d)*/
bits_write(&bitsBuffer, 8, 0x45); /*crc (24~31) bits*/
bits_write(&bitsBuffer, 8, 0xBD); /*crc (16~23) bits*/
bits_write(&bitsBuffer, 8, 0xDC); /*crc (8~15) bits*/
bits_write(&bitsBuffer, 8, 0xF4); /*crc (0~7) bits*/
return 0;
}
最后是PES頭部,它記錄了幀的時間戳,DTS可以不填,如果填寫要和PTS保持一致,且同一幀數據的PTS要也要一樣(即SPS、PPS、IDR的PES要一致):
#define PES_HDR_LEN 19
static int gb28181_make_pes_header(char *pData, int stream_id, int payload_len, unsigned long long pts, unsigned long long dts)
{
bits_buffer_s bitsBuffer;
bitsBuffer.i_size = PES_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, PES_HDR_LEN);
/*system header*/
bits_write( &bitsBuffer, 24,0x000001); /*start code*/
bits_write( &bitsBuffer, 8, (stream_id)); /*streamID*/
bits_write( &bitsBuffer, 16,(payload_len)+13); /*packet_len pes剩余頭部以及后面的es長度之和,比如SPS長度+13*/
bits_write( &bitsBuffer, 2, 2 ); /*'10'*/
bits_write( &bitsBuffer, 2, 0 ); /*scrambling_control*/
bits_write( &bitsBuffer, 1, 1 ); /*priority*/
bits_write( &bitsBuffer, 1, 1 ); /*data_alignment_indicator*/
bits_write( &bitsBuffer, 1, 0 ); /*copyright*/
bits_write( &bitsBuffer, 1, 0 ); /*original_or_copy*/
bits_write( &bitsBuffer, 1, 1 ); /*PTS_flag 是否有PTS*/
bits_write( &bitsBuffer, 1, 1 ); /*DTS_flag 是否有DTS信息*/
bits_write( &bitsBuffer, 1, 0 ); /*ESCR_flag*/
bits_write( &bitsBuffer, 1, 0 ); /*ES_rate_flag*/
bits_write( &bitsBuffer, 1, 0 ); /*DSM_trick_mode_flag*/
bits_write( &bitsBuffer, 1, 0 ); /*additional_copy_info_flag*/
bits_write( &bitsBuffer, 1, 0 ); /*PES_CRC_flag*/
bits_write( &bitsBuffer, 1, 0 ); /*PES_extension_flag*/
bits_write( &bitsBuffer, 8, 10); /*header_data_length*/
/*PTS,DTS*/
bits_write( &bitsBuffer, 4, 3 ); /*'0011'*/
bits_write( &bitsBuffer, 3, ((pts)>>30)&0x07 ); /*PTS[32..30]*/
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 15,((pts)>>15)&0x7FFF); /*PTS[29..15]*/
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 15,(pts)&0x7FFF); /*PTS[14..0]*/
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 4, 1 ); /*'0001'*/
bits_write( &bitsBuffer, 3, ((dts)>>30)&0x07 ); /*DTS[32..30]*/
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 15,((dts)>>15)&0x7FFF); /*DTS[29..15]*/
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 15,(dts)&0x7FFF); /*DTS[14..0]*/
bits_write( &bitsBuffer, 1, 1 );
return 0;
}
關於時間戳的比特位結構,下圖表示的比較清晰: