转自:http://www.cnblogs.com/lidabo/p/6604988.html
1、写在开始之前:
最近因为新工作要维护别人留下的GB模块代码,先熟悉了流程,然后也试着封装了下ps流,结果也能通过测试正常预览了,当然,其中开发读文档的头疼,预览花屏,卡帧的事情都有遇到,当时慢慢的看文档,整理逻辑,也就都顺利解决了,下面把大致的一些流程代码贴出来分享下。既然是对接国标,自然少不了通读它的标准文档和相关的RFC文档了!具体的我就不说了,可以用百度google下的。
注意:因为是GB要求ps封装后再加上rtp头的格式来的, 所以下面代码中我也加上了rtp头,如果不需要的话,直接屏蔽代码中的rtp即可。
2、封装的重点
当我们从读缓冲区中取得一帧音视频数据的时候,封装时其实每一帧数据有且只有一个ps头和psm头,如果是I帧的话,就还多一个system头,一个或者多个pes头和rtp头,
像如果帧数据过长的话,就得进行分片,每片都会包含一个pes头,rtp负载最好长度1460,所以会进行再分包操作!所以每一个包数据至少一个rtp+databuf,每一片数据,至少有个rtp+pes+databuf,每一帧数据至少有rtp+ps+psm+pes+databuf(关键帧的话:多一个system头)
3、具体的各个封装的代码实现
首先给去一个整体的封装rtp->ps->sys->psm->pes(如果只要ps的话,则为ps->sys->psm->pes)的大致流程,
然后再一一罗列出各个部件的封装接口
-
- 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;
- }
上面列出来了整个打包发包的过程,接下来一个一个接口的看
- 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;
- }
4、封装相关心得
博主已经分步验证过rtp或者ps又或者rtp+ps封装都能正常预览。其实这个封装真心没什么理论知识说的,看看标准都知道了,只要仔细看标准一步一步的向下走,分析各个封装的各个字段,就没有什么问题了,当然在实际中也有很多小问题苦恼我很久,但是无不是因为标准没注意或者理解有误。现在我也只是简单的把自己实现的代码大致贴出来分享了,希望相关开发的少走一些弯路而已。有问题或者有更好的方法,大家可以相互交流。