/*封包RTP*/
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 */
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 */
bits_write(&bitsBuffer, 7, 96); /* rtp payload type*/
bits_write(&bitsBuffer, 16, (cseq)); /* rtp sequence */
bits_write(&bitsBuffer, 32, (curpts)); /* rtp timestamp */
bits_write(&bitsBuffer, 32, (ssrc)); /* rtp SSRC */
return 0;
}
/*封裝ps頭包*/
int gb28181_make_ps_header(char *pData, unsigned long long s64Scr)
{
unsigned long long lScrExt = (s64Scr) % 100;
//s64Scr = s64Scr / 100;
// 這裏除以100是由於sdp協議返回的video的頻率是90000,幀率是25幀/s,所以每次遞增的量是3600,
// 所以實際你應該根據你自己編碼裏的時間戳來處理以保證時間戳的增量爲3600即可,
//如果這裏不對的話,就可能導致卡頓現象了
bits_buffer_s bitsBuffer;
bitsBuffer.i_size = PS_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80; // 二進制:10000000 這裏是爲了後面對一個字節的每一位進行操作,避免大小端誇字節字序錯亂
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, lScrExt&0x01ff); /*System clock [9..0]*/
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;
}
/***
*@remark: 音視頻數據的打包成ps流,並封裝成rtp
*@param : pData [in] 需要發送的音視頻數據
* nFrameLen [in] 發送數據的長度
* pPacker [in] 數據包的一些信息,包括時間戳,rtp數據buff,發送的socket相關信息
* stream_type[in] 數據類型 0 視頻 1 音頻
*@return: 0 success others failed
*/
int gb28181_streampackageForH264(char *pData, int nFrameLen, Data_Info_s* pPacker, int stream_type)
{
char szTempPacketHead[256];
int nSizePos = 0;
int nSize = 0;
char *pBuff = NULL;
memset(szTempPacketHead, 0, 256);
// 1 package for ps header
gb28181_make_ps_header(szTempPacketHead + nSizePos, pPacker->s64CurPts);
nSizePos += PS_HDR_LEN;
//2 system header
if( pPacker->IFrame == 1 )
{
HikOutFile <<"gb28181_streampackageForH264:如果是I幀的話,則添加系統頭:"<<"長度:"<<nFrameLen<<";I幀:"<<pPacker->IFrame<<";碼流類型:"<<stream_type<<"\n";
// 如果是I幀的話,則添加系統頭
gb28181_make_sys_header(szTempPacketHead + nSizePos);
nSizePos += SYS_HDR_LEN;
//這個地方我是不管是I幀還是p幀都加上了map的,貌似只是I幀加也沒有問題
gb28181_make_psm_header(szTempPacketHead + nSizePos);
nSizePos += PSM_HDR_LEN;
}
// psm頭 (也是map)
//gb28181_make_psm_header(szTempPacketHead + nSizePos);
//nSizePos += PSM_HDR_LEN;
//加上rtp發送出去,這樣的話,後面的數據就只要分片分包就只有加上pes頭和rtp頭了
if(gb28181_send_rtp_pack(szTempPacketHead, nSizePos, 0, pPacker) != 0 )
return -1;
// 這裏向後移動是爲了方便拷貝pes頭
//這裏是爲了減少後面音視頻裸數據的大量拷貝浪費空間,所以這裏就向後移動,在實際處理的時候,要注意地址是否越界以及覆蓋等問題
//pBuff = pData - PES_HDR_LEN;
pBuff = pData;
while(nFrameLen > 0)
{
HikOutFile <<"gb28181_streampackageForH264:分片進循環行發送:"<<"長度:"<<nSize<<";I幀:"<<pPacker->IFrame<<";碼流類型:"<<stream_type<<"\n";
//每次幀的長度不要超過short類型,過了就得分片進循環行發送
nSize = (nFrameLen > PS_PES_PAYLOAD_SIZE) ? PS_PES_PAYLOAD_SIZE : nFrameLen;
//cout<<"pPacker->s64CurPts="<<pPacker->s64CurPts<<endl;
// 添加pes頭
gb28181_make_pes_header(pBuff, stream_type ? 0xC0:0xE0, nSize, (pPacker->s64CurPts /*/ 100*/), (pPacker->s64CurPts/*/300*/));
//最後在添加rtp頭併發送數據
if( gb28181_send_rtp_pack(pBuff, nSize + PES_HDR_LEN, ((nSize == nFrameLen)?1:0), pPacker) != 0 )
{
printf("gb28181_send_pack failed!\n");
return -1;
}
//分片後每次發送的數據移動指針操作
nFrameLen -= nSize;
//這裏也只移動nSize,因爲在while向後移動的pes頭長度,正好重新填充pes頭數據
pBuff += nSize;
}
return 0;
}
本工程可在vs工程裏面直接添加編譯,直接可通過API生成GB28181所需碼流,如果有需要請用微信掃碼下圖Gitchat聯繫作者: