一、寫在開始以前: 緩存
最近由於新工做要維護別人留下的GB模塊代碼,先熟悉了流程,而後也試着封裝了下ps流,結果也能經過測試正常預覽了,固然,其中開發讀文檔的頭疼,預覽花屏,卡幀的事情都有遇到,當時慢慢的看文檔,整理邏輯,也就都順利解決了,下面把大體的一些流程代碼貼出來分享下。既然是對接國標,天然少不了通讀它的標準文檔和相關的RFC文檔了!具體的我就不說了,能夠用百度google下的。併發
注意:由於是GB要求ps封裝後再加上rtp頭的格式來的, 因此下面代碼中我也加上了rtp頭,若是不須要的話,直接屏蔽代碼中的rtp便可。socket
二、封裝的重點ide
當咱們從讀緩衝區中取得一幀音視頻數據的時候,封裝時其實每一幀數據有且只有一個ps頭和psm頭,若是是I幀的話,就還多一個system頭,一個或者多個pes頭和rtp頭,函數
像若是幀數據過長的話,就得進行分片,每片都會包含一個pes頭,rtp負載最好長度1460,因此會進行再分包操做!因此每個包數據至少一個rtp+databuf,每一片數據,至少有個rtp+pes+databuf,每一幀數據至少有rtp+ps+psm+pes+databuf(關鍵幀的話:多一個system頭)測試
三、具體的各個封裝的代碼實現ui
首先給去一個總體的封裝rtp->ps->sys->psm->pes(若是隻要ps的話,則爲ps->sys->psm->pes)的大體流程,google
而後再一一羅列出各個部件的封裝接口編碼
-
- 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);
-
- gb28181_make_ps_header(szTempPacketHead + nSizePos, pPacker->s64CurPts);
- nSizePos += PS_HDR_LEN;
-
- if( pPacker->IFrame == 1 )
- {
-
- gb28181_make_sys_header(szTempPacketHead + nSizePos);
- nSizePos += SYS_HDR_LEN;
-
-
- }
-
- gb28181_make_psm_header(szTempPacketHead + nSizePos);
- nSizePos += PSM_HDR_LEN;
-
-
- if(gb28181_send_rtp_pack(szTempPacketHead, nSizePos, 0, pPacker) != 0 )
- return -1;
-
-
-
- pBuff = pData - PES_HDR_LEN;
- while(nFrameLen > 0)
- {
-
- nSize = (nFrameLen > PS_PES_PAYLOAD_SIZE) ? PS_PES_PAYLOAD_SIZE : nFrameLen;
-
- gb28181_make_pes_header(pBuff, stream_type ? 0xC0:0xE0, nSize, (pPacker->s64CurPts / 100), (pPacker->s64CurPts/300));
-
-
- 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;
-
- pBuff += nSize;
-
- }
- return 0;
- }
上面列出來了整個打包發包的過程,接下來一個一個接口的看spa
- 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);
- bits_write(&bitsBuffer, 2, 1);
- bits_write(&bitsBuffer, 3, (s64Scr>>30)&0x07);
- bits_write(&bitsBuffer, 1, 1);
- bits_write(&bitsBuffer, 15, (s64Scr>>15)&0x7FFF);
- bits_write(&bitsBuffer, 1, 1);
- bits_write(&bitsBuffer, 15, s64Scr&0x7fff);
- bits_write(&bitsBuffer, 1, 1);
- bits_write(&bitsBuffer, 9, lScrExt&0x01ff);
- bits_write(&bitsBuffer, 1, 1);
- bits_write(&bitsBuffer, 22, (255)&0x3fffff);
- bits_write(&bitsBuffer, 2, 3);
- bits_write(&bitsBuffer, 5, 0x1f);
- bits_write(&bitsBuffer, 3, 0);
- return 0;
- }
- 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);
-
- bits_write( &bitsBuffer, 32, 0x000001BB);
- bits_write( &bitsBuffer, 16, SYS_HDR_LEN-6);
- bits_write( &bitsBuffer, 1, 1);
- bits_write( &bitsBuffer, 22, 50000);
- bits_write( &bitsBuffer, 1, 1);
- bits_write( &bitsBuffer, 6, 1);
- bits_write( &bitsBuffer, 1, 0);
- bits_write( &bitsBuffer, 1, 1);
- bits_write( &bitsBuffer, 1, 1);
- bits_write( &bitsBuffer, 1, 1);
- bits_write( &bitsBuffer, 1, 1);
- bits_write( &bitsBuffer, 5, 1);
- bits_write( &bitsBuffer, 1, 0);
- bits_write( &bitsBuffer, 7, 0x7F);
-
- bits_write( &bitsBuffer, 8, 0xC0);
- bits_write( &bitsBuffer, 2, 3);
- bits_write( &bitsBuffer, 1, 0);
- bits_write( &bitsBuffer, 13, 512);
-
- bits_write( &bitsBuffer, 8, 0xE0);
- bits_write( &bitsBuffer, 2, 3);
- bits_write( &bitsBuffer, 1, 1);
- bits_write( &bitsBuffer, 13, 2048);
- return 0;
- }
- 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, PS_SYS_MAP_SIZE);
- bits_write(&bitsBuffer, 24,0x000001);
- bits_write(&bitsBuffer, 8, 0xBC);
- bits_write(&bitsBuffer, 16,18);
- bits_write(&bitsBuffer, 1, 1);
- bits_write(&bitsBuffer, 2, 3);
- bits_write(&bitsBuffer, 5, 0);
- bits_write(&bitsBuffer, 7, 0x7F);
- bits_write(&bitsBuffer, 1, 1);
- bits_write(&bitsBuffer, 16,0);
- bits_write(&bitsBuffer, 16, 8);
-
- bits_write(&bitsBuffer, 8, 0x90);
- bits_write(&bitsBuffer, 8, 0xC0);
- bits_write(&bitsBuffer, 16, 0);
-
- bits_write(&bitsBuffer, 8, 0x1B);
- bits_write(&bitsBuffer, 8, 0xE0);
- bits_write(&bitsBuffer, 16, 0);
-
- bits_write(&bitsBuffer, 8, 0x45);
- bits_write(&bitsBuffer, 8, 0xBD);
- bits_write(&bitsBuffer, 8, 0xDC);
- bits_write(&bitsBuffer, 8, 0xF4);
- return 0;
- }
- 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);
-
- bits_write( &bitsBuffer, 24,0x000001);
- bits_write( &bitsBuffer, 8, (stream_id));
- bits_write( &bitsBuffer, 16,(payload_len)+13);
- bits_write( &bitsBuffer, 2, 2 );
- bits_write( &bitsBuffer, 2, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 1 );
- bits_write( &bitsBuffer, 1, 1 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 1, 0 );
- bits_write( &bitsBuffer, 8, 10);
-
-
-
-
- bits_write( &bitsBuffer, 4, 3 );
- bits_write( &bitsBuffer, 3, ((pts)>>30)&0x07 );
- bits_write( &bitsBuffer, 1, 1 );
- bits_write( &bitsBuffer, 15,((pts)>>15)&0x7FFF);
- bits_write( &bitsBuffer, 1, 1 );
- bits_write( &bitsBuffer, 15,(pts)&0x7FFF);
- bits_write( &bitsBuffer, 1, 1 );
- bits_write( &bitsBuffer, 4, 1 );
- bits_write( &bitsBuffer, 3, ((dts)>>30)&0x07 );
- bits_write( &bitsBuffer, 1, 1 );
- bits_write( &bitsBuffer, 15,((dts)>>15)&0x7FFF);
- bits_write( &bitsBuffer, 1, 1 );
- bits_write( &bitsBuffer, 15,(dts)&0x7FFF);
- bits_write( &bitsBuffer, 1, 1 );
- return 0;
- }
-
- int gb28181_send_rtp_pack(char *databuff, int nDataLen, int mark_flag, Data_Info_s* pPacker)
- {
- int nRes = 0;
- int nPlayLoadLen = 0;
- int nSendSize = 0;
- char szRtpHdr[RTP_HDR_LEN];
- memset(szRtpHdr, 0, RTP_HDR_LEN);
-
- if(nDataLen + RTP_HDR_LEN <= RTP_MAX_PACKET_BUFF)
- {
-
- gb28181_make_rtp_header(szRtpHdr, ((mark_flag == 1 )? 1 : 0 ), ++pPacker->u16CSeq, (pPacker->s64CurPts /300), pPacker->u32Ssrc);
- memcpy(pPacker->szBuff, szRtpHdr, RTP_HDR_LEN);
- memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nDataLen);
- nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);
- if (nRes != (RTP_HDR_LEN + nDataLen))
- {
- printf(" udp send error !\n");
- return -1;
- }
-
- }
- else
- {
- nPlayLoadLen = RTP_MAX_PACKET_BUFF - RTP_HDR_LEN;
- gb28181_make_rtp_header(pPacker->szBuff, 0, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);
- memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nPlayLoadLen);
- nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);
- if (nRes != (RTP_HDR_LEN + nPlayLoadLen))
- {
- printf(" udp send error !\n");
- return -1;
- }
-
- nDataLen -= nPlayLoadLen;
-
- databuff += nPlayLoadLen;
- databuff -= RTP_HDR_LEN;
- while(nDataLen > 0)
- {
- if(nDataLen <= nPlayLoadLen)
- {
-
- gb28181_make_rtp_header(databuff, mark_flag, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);
- nSendSize = nDataLen;
- }
- else
- {
- gb28181_make_rtp_header(databuff, 0, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);
- nSendSize = nPlayLoadLen;
- }
- nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);
- if (nRes != (RTP_HDR_LEN + nSendSize))
- {
- printf(" udp send error !\n");
- return -1;
- }
- nDataLen -= nSendSize;
- databuff += nSendSize;
-
-
-
-
- }
-
- }
- return 0;
- }
還有一個很重要的宏定義,之因此把它定義成宏,是由於會頻繁調用,其功能是循環將一個字節的8位按位一個一個的壓入數據,防止出現誇字節的致使字序出錯問題具體實現以下,實際上是挪用了vlc源碼中的實現過來的,這也是讀源碼的一個好處,能很好的利用裏面比較高級而又方便的功能代碼模塊
- #define PS_HDR_LEN 14
- #define SYS_HDR_LEN 18
- #define PSM_HDR_LEN 24
- #define PES_HDR_LEN 19
- #define RTP_HDR_LEN 12
- #define bits_write(buffer, count, bits)\
- {\
- bits_buffer_s *p_buffer = (buffer);\
- int i_count = (count);\
- uint64_t i_bits = (bits);\
- while( i_count > 0 )\
- {\
- i_count--;\
- if( ( i_bits >> i_count )&0x01 )\
- {\
- p_buffer->p_data[p_buffer->i_data] |= p_buffer->i_mask;\
- }\
- else\
- {\
- p_buffer->p_data[p_buffer->i_data] &= ~p_buffer->i_mask;\
- }\
- p_buffer->i_mask >>= 1;
- if( p_buffer->i_mask == 0 )
- {\
- p_buffer->i_data++;\
- p_buffer->i_mask = 0x80;\
- }\
- }\
- }
上面忘記貼出rtp封裝頭了,此次補充,若是在實際不須要rtp的話,能夠直接在gb28181_send_rtp_pack函數接口中屏蔽gb28181_make_rtp_header函數接口便可,固然須要注意一點問題,就是對應的buffer指針的位置就不須要移動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);
- bits_write(&bitsBuffer, 1, 0);
- bits_write(&bitsBuffer, 1, 0);
- bits_write(&bitsBuffer, 4, 0);
- bits_write(&bitsBuffer, 1, (marker_flag));
- bits_write(&bitsBuffer, 7, 96);
- bits_write(&bitsBuffer, 16, (cseq));
- bits_write(&bitsBuffer, 32, (curpts));
- bits_write(&bitsBuffer, 32, (ssrc));
- return 0;
- }
四、封裝相關心得
博主已經分步驗證過rtp或者ps又或者rtp+ps封裝都能正常預覽。其實這個封裝真心沒什麼理論知識說的,看看標準都知道了,只要仔細看標準一步一步的向下走,分析各個封裝的各個字段,就沒有什麼問題了,固然在實際中也有不少小問題苦惱我好久,可是無不是由於標準沒注意或者理解有誤。如今我也只是簡單的把本身實現的代碼大體貼出來分享了,但願相關開發的少走一些彎路而已。有問題或者有更好的方法,你們能夠相互交流。