关于对H264码流的PS的封装的相关代码实现

简介: 1、写在开始之前:            最近因为新工作要维护别人留下的GB模块代码,先熟悉了流程,然后也试着封装了下ps流,结果也能通过测试正常预览了,当然,其中开发读文档的头疼,预览花屏,卡帧的事情都有遇到,当时慢慢的看文档,整理逻辑,也就都顺利解决了,下面把大致的一些流程代码贴出来分享下。

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)的大致流程,

然后再一一罗列出各个部件的封装接口

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /*** 
  2.  *@remark:  音视频数据的打包成ps流,并封装成rtp 
  3.  *@param :  pData      [in] 需要发送的音视频数据 
  4.  *          nFrameLen  [in] 发送数据的长度 
  5.  *          pPacker    [in] 数据包的一些信息,包括时间戳,rtp数据buff,发送的socket相关信息 
  6.  *          stream_type[in] 数据类型 0 视频 1 音频 
  7.  *@return:  0 success others failed 
  8. */  
  9.   
  10. int gb28181_streampackageForH264(char *pData, int nFrameLen, Data_Info_s* pPacker, int stream_type)  
  11. {  
  12.     char szTempPacketHead[256];  
  13.     int  nSizePos = 0;  
  14.     int  nSize = 0;       
  15.     char *pBuff = NULL;  
  16.     memset(szTempPacketHead, 0, 256);  
  17.     // 1 package for ps header   
  18.     gb28181_make_ps_header(szTempPacketHead + nSizePos, pPacker->s64CurPts);  
  19.     nSizePos += PS_HDR_LEN;   
  20.     //2 system header   
  21.     if( pPacker->IFrame == 1 )  
  22.     {  
  23.         // 如果是I帧的话,则添加系统头  
  24.         gb28181_make_sys_header(szTempPacketHead + nSizePos);  
  25.         nSizePos += SYS_HDR_LEN;  
  26.         //这个地方我是不管是I帧还是p帧都加上了map的,貌似只是I帧加也没有问题  
  27. //      gb28181_make_psm_header(szTempPacketHead + nSizePos);  
  28. //      nSizePos += PSM_HDR_LEN;  
  29.   
  30.     }  
  31.     // psm头 (也是map)  
  32.     gb28181_make_psm_header(szTempPacketHead + nSizePos);  
  33.     nSizePos += PSM_HDR_LEN;  
  34.   
  35.     //加上rtp发送出去,这样的话,后面的数据就只要分片分包就只有加上pes头和rtp头了  
  36.     if(gb28181_send_rtp_pack(szTempPacketHead, nSizePos, 0, pPacker) != 0 )  
  37.         return -1;    
  38.   
  39.     // 这里向后移动是为了方便拷贝pes头  
  40.     //这里是为了减少后面音视频裸数据的大量拷贝浪费空间,所以这里就向后移动,在实际处理的时候,要注意地址是否越界以及覆盖等问题  
  41.     pBuff = pData - PES_HDR_LEN;  
  42.     while(nFrameLen > 0)  
  43.     {  
  44.         //每次帧的长度不要超过short类型,过了就得分片进循环行发送  
  45.         nSize = (nFrameLen > PS_PES_PAYLOAD_SIZE) ? PS_PES_PAYLOAD_SIZE : nFrameLen;  
  46.         // 添加pes头  
  47.         gb28181_make_pes_header(pBuff, stream_type ? 0xC0:0xE0, nSize, (pPacker->s64CurPts / 100), (pPacker->s64CurPts/300));  
  48.   
  49.         //最后在添加rtp头并发送数据  
  50.         if( gb28181_send_rtp_pack(pBuff, nSize + PES_HDR_LEN, ((nSize == nFrameLen)?1:0), pPacker) != 0 )  
  51.         {  
  52.             printf("gb28181_send_pack failed!\n");  
  53.             return -1;  
  54.         }  
  55.         //分片后每次发送的数据移动指针操作  
  56.         nFrameLen -= nSize;  
  57.         //这里也只移动nSize,因为在while向后移动的pes头长度,正好重新填充pes头数据  
  58.         pBuff     += nSize;  
  59.           
  60.     }  
  61.     return 0;  
  62. }  

 

上面列出来了整个打包发包的过程,接下来一个一个接口的看

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /*** 
  2.  *@remark:   ps头的封装,里面的具体数据的填写已经占位,可以参考标准 
  3.  *@param :   pData  [in] 填充ps头数据的地址 
  4.  *           s64Src [in] 时间戳 
  5.  *@return:   0 success, others failed 
  6. */  
  7. int gb28181_make_ps_header(char *pData, unsigned long long s64Scr)  
  8. {  
  9.     unsigned long long lScrExt = (s64Scr) % 100;      
  10.     s64Scr = s64Scr / 100;  
  11.     // 这里除以100是由于sdp协议返回的video的频率是90000,帧率是25帧/s,所以每次递增的量是3600,  
  12.     // 所以实际你应该根据你自己编码里的时间戳来处理以保证时间戳的增量为3600即可,  
  13.     //如果这里不对的话,就可能导致卡顿现象了  
  14.     bits_buffer_s   bitsBuffer;  
  15.     bitsBuffer.i_size = PS_HDR_LEN;   
  16.     bitsBuffer.i_data = 0;  
  17.     bitsBuffer.i_mask = 0x80; // 二进制:10000000 这里是为了后面对一个字节的每一位进行操作,避免大小端夸字节字序错乱  
  18.     bitsBuffer.p_data = (unsigned char *)(pData);  
  19.     memset(bitsBuffer.p_data, 0, PS_HDR_LEN);  
  20.     bits_write(&bitsBuffer, 32, 0x000001BA);            /*start codes*/  
  21.     bits_write(&bitsBuffer, 2,  1);                     /*marker bits '01b'*/  
  22.     bits_write(&bitsBuffer, 3,  (s64Scr>>30)&0x07);     /*System clock [32..30]*/  
  23.     bits_write(&bitsBuffer, 1,  1);                     /*marker bit*/  
  24.     bits_write(&bitsBuffer, 15, (s64Scr>>15)&0x7FFF);   /*System clock [29..15]*/  
  25.     bits_write(&bitsBuffer, 1,  1);                     /*marker bit*/  
  26.     bits_write(&bitsBuffer, 15, s64Scr&0x7fff);         /*System clock [29..15]*/  
  27.     bits_write(&bitsBuffer, 1,  1);                     /*marker bit*/  
  28.     bits_write(&bitsBuffer, 9,  lScrExt&0x01ff);        /*System clock [14..0]*/  
  29.     bits_write(&bitsBuffer, 1,  1);                     /*marker bit*/  
  30.     bits_write(&bitsBuffer, 22, (255)&0x3fffff);        /*bit rate(n units of 50 bytes per second.)*/  
  31.     bits_write(&bitsBuffer, 2,  3);                     /*marker bits '11'*/  
  32.     bits_write(&bitsBuffer, 5,  0x1f);                  /*reserved(reserved for future use)*/  
  33.     bits_write(&bitsBuffer, 3,  0);                     /*stuffing length*/  
  34.     return 0;  
  35. }  
[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /*** 
  2.  *@remark:   sys头的封装,里面的具体数据的填写已经占位,可以参考标准 
  3.  *@param :   pData  [in] 填充ps头数据的地址 
  4.  *@return:   0 success, others failed 
  5. */  
  6. int gb28181_make_sys_header(char *pData)  
  7. {  
  8.       
  9.     bits_buffer_s   bitsBuffer;  
  10.     bitsBuffer.i_size = SYS_HDR_LEN;  
  11.     bitsBuffer.i_data = 0;  
  12.     bitsBuffer.i_mask = 0x80;  
  13.     bitsBuffer.p_data = (unsigned char *)(pData);  
  14.     memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);  
  15.     /*system header*/  
  16.     bits_write( &bitsBuffer, 32, 0x000001BB);   /*start code*/  
  17.     bits_write( &bitsBuffer, 16, SYS_HDR_LEN-6);/*header_length 表示次字节后面的长度,后面的相关头也是次意思*/  
  18.     bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/  
  19.     bits_write( &bitsBuffer, 22, 50000);        /*rate_bound*/  
  20.     bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/  
  21.     bits_write( &bitsBuffer, 6,  1);            /*audio_bound*/  
  22.     bits_write( &bitsBuffer, 1,  0);            /*fixed_flag */  
  23.     bits_write( &bitsBuffer, 1,  1);            /*CSPS_flag */  
  24.     bits_write( &bitsBuffer, 1,  1);            /*system_audio_lock_flag*/  
  25.     bits_write( &bitsBuffer, 1,  1);            /*system_video_lock_flag*/  
  26.     bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/  
  27.     bits_write( &bitsBuffer, 5,  1);            /*video_bound*/  
  28.     bits_write( &bitsBuffer, 1,  0);            /*dif from mpeg1*/  
  29.     bits_write( &bitsBuffer, 7,  0x7F);         /*reserver*/  
  30.     /*audio stream bound*/  
  31.     bits_write( &bitsBuffer, 8,  0xC0);         /*stream_id*/  
  32.     bits_write( &bitsBuffer, 2,  3);            /*marker_bit */  
  33.     bits_write( &bitsBuffer, 1,  0);            /*PSTD_buffer_bound_scale*/  
  34.     bits_write( &bitsBuffer, 13, 512);          /*PSTD_buffer_size_bound*/  
  35.     /*video stream bound*/  
  36.     bits_write( &bitsBuffer, 8,  0xE0);         /*stream_id*/  
  37.     bits_write( &bitsBuffer, 2,  3);            /*marker_bit */  
  38.     bits_write( &bitsBuffer, 1,  1);            /*PSTD_buffer_bound_scale*/  
  39.     bits_write( &bitsBuffer, 13, 2048);         /*PSTD_buffer_size_bound*/  
  40.     return 0;  
  41. }  
[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /*** 
  2.  *@remark:   psm头的封装,里面的具体数据的填写已经占位,可以参考标准 
  3.  *@param :   pData  [in] 填充ps头数据的地址 
  4.  *@return:   0 success, others failed 
  5. */  
  6. int gb28181_make_psm_header(char *pData)  
  7. {  
  8.       
  9.     bits_buffer_s   bitsBuffer;  
  10.     bitsBuffer.i_size = PSM_HDR_LEN;   
  11.     bitsBuffer.i_data = 0;  
  12.     bitsBuffer.i_mask = 0x80;  
  13.     bitsBuffer.p_data = (unsigned char *)(pData);  
  14.     memset(bitsBuffer.p_data, 0, PS_SYS_MAP_SIZE);  
  15.     bits_write(&bitsBuffer, 24,0x000001);   /*start code*/  
  16.     bits_write(&bitsBuffer, 8, 0xBC);       /*map stream id*/  
  17.     bits_write(&bitsBuffer, 16,18);         /*program stream map length*/   
  18.     bits_write(&bitsBuffer, 1, 1);          /*current next indicator */  
  19.     bits_write(&bitsBuffer, 2, 3);          /*reserved*/  
  20.     bits_write(&bitsBuffer, 5, 0);          /*program stream map version*/  
  21.     bits_write(&bitsBuffer, 7, 0x7F);       /*reserved */  
  22.     bits_write(&bitsBuffer, 1, 1);          /*marker bit */  
  23.     bits_write(&bitsBuffer, 16,0);          /*programe stream info length*/  
  24.     bits_write(&bitsBuffer, 16, 8);         /*elementary stream map length  is*/  
  25.     /*audio*/  
  26.     bits_write(&bitsBuffer, 8, 0x90);       /*stream_type*/  
  27.     bits_write(&bitsBuffer, 8, 0xC0);       /*elementary_stream_id*/  
  28.     bits_write(&bitsBuffer, 16, 0);         /*elementary_stream_info_length is*/  
  29.     /*video*/  
  30.     bits_write(&bitsBuffer, 8, 0x1B);       /*stream_type*/  
  31.     bits_write(&bitsBuffer, 8, 0xE0);       /*elementary_stream_id*/  
  32.     bits_write(&bitsBuffer, 16, 0);         /*elementary_stream_info_length */  
  33.     /*crc (2e b9 0f 3d)*/  
  34.     bits_write(&bitsBuffer, 8, 0x45);       /*crc (24~31) bits*/  
  35.     bits_write(&bitsBuffer, 8, 0xBD);       /*crc (16~23) bits*/  
  36.     bits_write(&bitsBuffer, 8, 0xDC);       /*crc (8~15) bits*/  
  37.     bits_write(&bitsBuffer, 8, 0xF4);       /*crc (0~7) bits*/  
  38.     return 0;  
  39. }  
[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /*** 
  2.  *@remark:   pes头的封装,里面的具体数据的填写已经占位,可以参考标准 
  3.  *@param :   pData      [in] 填充ps头数据的地址 
  4.  *           stream_id  [in] 码流类型 
  5.  *           paylaod_len[in] 负载长度 
  6.  *           pts        [in] 时间戳 
  7.  *           dts        [in] 
  8.  *@return:   0 success, others failed 
  9. */  
  10. int gb28181_make_pes_header(char *pData, int stream_id, int payload_len, unsigned long long pts, unsigned long long dts)  
  11. {  
  12.       
  13.     bits_buffer_s   bitsBuffer;  
  14.     bitsBuffer.i_size = PES_HDR_LEN;  
  15.     bitsBuffer.i_data = 0;  
  16.     bitsBuffer.i_mask = 0x80;  
  17.     bitsBuffer.p_data = (unsigned char *)(pData);  
  18.     memset(bitsBuffer.p_data, 0, PES_HDR_LEN);  
  19.     /*system header*/  
  20.     bits_write( &bitsBuffer, 24,0x000001);  /*start code*/  
  21.     bits_write( &bitsBuffer, 8, (stream_id));   /*streamID*/  
  22.     bits_write( &bitsBuffer, 16,(payload_len)+13);  /*packet_len*/ //指出pes分组中数据长度和该字节后的长度和  
  23.     bits_write( &bitsBuffer, 2, 2 );        /*'10'*/  
  24.     bits_write( &bitsBuffer, 2, 0 );        /*scrambling_control*/  
  25.     bits_write( &bitsBuffer, 1, 0 );        /*priority*/  
  26.     bits_write( &bitsBuffer, 1, 0 );        /*data_alignment_indicator*/  
  27.     bits_write( &bitsBuffer, 1, 0 );        /*copyright*/  
  28.     bits_write( &bitsBuffer, 1, 0 );        /*original_or_copy*/  
  29.     bits_write( &bitsBuffer, 1, 1 );        /*PTS_flag*/  
  30.     bits_write( &bitsBuffer, 1, 1 );        /*DTS_flag*/  
  31.     bits_write( &bitsBuffer, 1, 0 );        /*ESCR_flag*/  
  32.     bits_write( &bitsBuffer, 1, 0 );        /*ES_rate_flag*/  
  33.     bits_write( &bitsBuffer, 1, 0 );        /*DSM_trick_mode_flag*/  
  34.     bits_write( &bitsBuffer, 1, 0 );        /*additional_copy_info_flag*/  
  35.     bits_write( &bitsBuffer, 1, 0 );        /*PES_CRC_flag*/  
  36.     bits_write( &bitsBuffer, 1, 0 );        /*PES_extension_flag*/  
  37.     bits_write( &bitsBuffer, 8, 10);        /*header_data_length*/   
  38.     // 指出包含在 PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前  
  39.     //的字节指出了有无可选字段。  
  40.       
  41.     /*PTS,DTS*/   
  42.     bits_write( &bitsBuffer, 4, 3 );                    /*'0011'*/  
  43.     bits_write( &bitsBuffer, 3, ((pts)>>30)&0x07 );     /*PTS[32..30]*/  
  44.     bits_write( &bitsBuffer, 1, 1 );  
  45.     bits_write( &bitsBuffer, 15,((pts)>>15)&0x7FFF);    /*PTS[29..15]*/  
  46.     bits_write( &bitsBuffer, 1, 1 );  
  47.     bits_write( &bitsBuffer, 15,(pts)&0x7FFF);          /*PTS[14..0]*/  
  48.     bits_write( &bitsBuffer, 1, 1 );  
  49.     bits_write( &bitsBuffer, 4, 1 );                    /*'0001'*/  
  50.     bits_write( &bitsBuffer, 3, ((dts)>>30)&0x07 );     /*DTS[32..30]*/  
  51.     bits_write( &bitsBuffer, 1, 1 );  
  52.     bits_write( &bitsBuffer, 15,((dts)>>15)&0x7FFF);    /*DTS[29..15]*/  
  53.     bits_write( &bitsBuffer, 1, 1 );  
  54.     bits_write( &bitsBuffer, 15,(dts)&0x7FFF);          /*DTS[14..0]*/  
  55.     bits_write( &bitsBuffer, 1, 1 );  
  56.     return 0;  
  57. }  
[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /*** 
  2.  *@remark:   rtp头的打包,并循环发送数据 
  3.  *@param :   pData      [in] 发送的数据地址 
  4.  *           nDatalen   [in] 发送数据的长度 
  5.  *           mark_flag  [in] mark标志位 
  6.  *           curpts     [in] 时间戳 
  7.  *           pPacker    [in] 数据包的基本信息 
  8.  *@return:   0 success, others failed 
  9. */  
  10.   
  11. int gb28181_send_rtp_pack(char *databuff, int nDataLen, int mark_flag, Data_Info_s* pPacker)  
  12. {  
  13.     int nRes = 0;  
  14.     int nPlayLoadLen = 0;  
  15.     int nSendSize    = 0;  
  16.     char szRtpHdr[RTP_HDR_LEN];  
  17.     memset(szRtpHdr, 0, RTP_HDR_LEN);  
  18.       
  19.     if(nDataLen + RTP_HDR_LEN <= RTP_MAX_PACKET_BUFF)// 1460 pPacker指针本来有一个1460大小的buffer数据缓存  
  20.     {  
  21.         // 一帧数据发送完后,给mark标志位置1  
  22.         gb28181_make_rtp_header(szRtpHdr, ((mark_flag == 1 )? 1 : 0 ), ++pPacker->u16CSeq, (pPacker->s64CurPts /300), pPacker->u32Ssrc);  
  23.         memcpy(pPacker->szBuff, szRtpHdr, RTP_HDR_LEN);  
  24.         memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nDataLen);  
  25.         nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);  
  26.         if (nRes != (RTP_HDR_LEN + nDataLen))  
  27.         {  
  28.             printf(" udp send error !\n");  
  29.             return -1;  
  30.         }  
  31.           
  32.     }  
  33.     else   
  34.     {  
  35.         nPlayLoadLen = RTP_MAX_PACKET_BUFF - RTP_HDR_LEN; // 每次只能发送的数据长度 除去rtp头  
  36.         gb28181_make_rtp_header(pPacker->szBuff, 0, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);  
  37.         memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nPlayLoadLen);  
  38.                 nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);  
  39.         if (nRes != (RTP_HDR_LEN + nPlayLoadLen))  
  40.         {  
  41.             printf(" udp send error !\n");  
  42.             return -1;  
  43.         }  
  44.           
  45.         nDataLen -= nPlayLoadLen;  
  46.         // databuff += (nPlayLoadLen - RTP_HDR_LEN);  
  47.         databuff += nPlayLoadLen; // 表明前面到数据已经发送出去        
  48.         databuff -= RTP_HDR_LEN; // 用来存放rtp头  
  49.         while(nDataLen > 0)  
  50.         {  
  51.             if(nDataLen <= nPlayLoadLen)  
  52.             {  
  53.                 //一帧数据发送完,置mark标志位  
  54.                 gb28181_make_rtp_header(databuff, mark_flag, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);  
  55.                 nSendSize = nDataLen;  
  56.             }  
  57.             else   
  58.             {  
  59.                 gb28181_make_rtp_header(databuff, 0, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);  
  60.                 nSendSize = nPlayLoadLen;  
  61.             }  
  62.                         nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);  
  63.             if (nRes != (RTP_HDR_LEN + nSendSize))  
  64.             {  
  65.                 printf(" udp send error !\n");  
  66.                 return -1;  
  67.             }  
  68.             nDataLen -= nSendSize;  
  69.             databuff += nSendSize;   
  70.             //因为buffer指针已经向后移动一次rtp头长度后,  
  71.             //所以每次循环发送rtp包时,只要向前移动裸数据到长度即可,这是buffer指针实际指向到位置是  
  72.             //databuff向后重复的rtp长度的裸数据到位置上   
  73.   
  74.         }  
  75.   
  76.     }  
  77.     return 0;  
  78. }  

还有一个很重要的宏定义,之所以把它定义成宏,是因为会频繁调用,其功能是循环将一个字节的8位按位一个一个的压入数据,防止出现夸字节的导致字序出错问题具体实现如下,其实是挪用了vlc源码中的实现过来的,这也是读源码的一个好处,能很好的利用里面比较高级而又方便的功能代码模块

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. #define PS_HDR_LEN  14  
  2. #define SYS_HDR_LEN 18  
  3. #define PSM_HDR_LEN 24  
  4. #define PES_HDR_LEN 19  
  5. #define RTP_HDR_LEN 12  
  6. /*** 
  7.  *@remark:  讲传入的数据按地位一个一个的压入数据 
  8.  *@param :  buffer   [in]  压入数据的buffer 
  9.  *          count    [in]  需要压入数据占的位数 
  10.  *          bits     [in]  压入的数值 
  11.  */  
  12. #define bits_write(buffer, count, bits)\  
  13. {\  
  14.     bits_buffer_s *p_buffer = (buffer);\  
  15.     int i_count = (count);\  
  16.     uint64_t i_bits = (bits);\  
  17.     while( i_count > 0 )\  
  18.     {\  
  19.         i_count--;\  
  20.         if( ( i_bits >> i_count )&0x01 )\  
  21.         {\  
  22.             p_buffer->p_data[p_buffer->i_data] |= p_buffer->i_mask;\  
  23.         }\  
  24.         else\  
  25.         {\  
  26.             p_buffer->p_data[p_buffer->i_data] &= ~p_buffer->i_mask;\  
  27.         }\  
  28.         p_buffer->i_mask >>= 1;         /*操作完一个字节第一位后,操作第二位*/\  
  29.         if( p_buffer->i_mask == 0 )     /*循环完一个字节的8位后,重新开始下一位*/\  
  30.         {\  
  31.             p_buffer->i_data++;\  
  32.             p_buffer->i_mask = 0x80;\  
  33.         }\  
  34.     }\  
  35. }  

上面忘记贴出rtp封装头了,这次补充,如果在实际不需要rtp的话,可以直接在gb28181_send_rtp_pack函数接口中屏蔽gb28181_make_rtp_header函数接口即可,当然需要注意一点问题,就是对应的buffer指针的位置就不需要移动rtp头的长度了!

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. int gb28181_make_rtp_header(char *pData, int marker_flag, unsigned short cseq, long long curpts, unsigned int ssrc)  
  2. {  
  3.     bits_buffer_s   bitsBuffer;  
  4.     if (pData == NULL)  
  5.         return -1;  
  6.     bitsBuffer.i_size = RTP_HDR_LEN;  
  7.     bitsBuffer.i_data = 0;  
  8.     bitsBuffer.i_mask = 0x80;  
  9.     bitsBuffer.p_data = (unsigned char *)(pData);  
  10.     memset(bitsBuffer.p_data, 0, RTP_HDR_SIZE);  
  11.     bits_write(&bitsBuffer, 2, RTP_VERSION);    /* rtp version  */  
  12.     bits_write(&bitsBuffer, 1, 0);              /* rtp padding  */  
  13.     bits_write(&bitsBuffer, 1, 0);              /* rtp extension    */  
  14.     bits_write(&bitsBuffer, 4, 0);              /* rtp CSRC count */  
  15.     bits_write(&bitsBuffer, 1, (marker_flag));          /* rtp marker   */  
  16.     bits_write(&bitsBuffer, 7, 96);         /* rtp payload type*/  
  17.     bits_write(&bitsBuffer, 16, (cseq));            /* rtp sequence      */  
  18.     bits_write(&bitsBuffer, 32, (curpts));      /* rtp timestamp     */  
  19.     bits_write(&bitsBuffer, 32, (ssrc));        /* rtp SSRC      */  
  20.     return 0;  
  21. }  

 

4、封装相关心得

        博主已经分步验证过rtp或者ps又或者rtp+ps封装都能正常预览。其实这个封装真心没什么理论知识说的,看看标准都知道了,只要仔细看标准一步一步的向下走,分析各个封装的各个字段,就没有什么问题了,当然在实际中也有很多小问题苦恼我很久,但是无不是因为标准没注意或者理解有误。现在我也只是简单的把自己实现的代码大致贴出来分享了,希望相关开发的少走一些弯路而已。有问题或者有更好的方法,大家可以相互交流。

目录
相关文章
|
7月前
mpeg-ps GB28181 ps封装
mpeg-ps GB28181 ps封装
160 0
代码例题(1-3)(含进阶版)及讲解:1.求两个数二进制中不同位的个数 2.打印整数的二进制的奇数和偶数位 3.交换两个变量,不创建临时变量 (ps最后一道为已知代码求值题)
代码例题(1-3)(含进阶版)及讲解:1.求两个数二进制中不同位的个数 2.打印整数的二进制的奇数和偶数位 3.交换两个变量,不创建临时变量 (ps最后一道为已知代码求值题)
98 0
|
程序员 计算机视觉 Python
一行代码搞定的事还用个锤子的 PS 啊
在我们的日常生活和工作中有不少场景需要简单处下理图片,很多人都是依赖 PS、美图秀秀等各种图像处理工具,殊不知在你打开软件的一瞬间 Python 就已经将图片处理完了。听起来是不是很神奇,正所谓是 Python 在手,啥也不愁。
170 0
一行代码搞定的事还用个锤子的 PS 啊
|
程序员 计算机视觉 Python
一行代码搞定的事还用个锤子的 PS 啊
在我们的日常生活和工作中有不少场景需要简单处下理图片,很多人都是依赖 PS、美图秀秀等各种图像处理工具,殊不知在你打开软件的一瞬间 Python 就已经将图片处理完了。听起来是不是很神奇,正所谓是 Python 在手,啥也不愁。 Python 届处理图片最强的库是 PIL(Python Image Library),但由于该库只支持 2.x 版本,于是有一批乐于助人的程序员在此基础上做了扩展,出了一个兼容 3.x 的版本也就是 Pillow,因此,我们今天要用的库就是 Pillow。
197 0
一行代码搞定的事还用个锤子的 PS 啊
|
前端开发 安全 Unix
破解索尼PS4系列:用户代码执行(二)
本文讲的是破解索尼PS4系列:用户代码执行(二),本文会重点讲解如何在WebKit进程中进行代码执行。
1980 0
|
Web App开发 前端开发 Unix
索尼PS4破解之用户层代码执行(Part2)
原文:Hacking the PS4, part 2 译者:鸢尾 来源:索尼PS4破解之用户层代码执行 自从我之前写的那篇有关PS4安全发布一来,我又有了一些新的发现。
1226 0
H264 NALU 使用PS封装 RTP发送
最近由于项目平台需求,要将H264 NALU封装为PS再用RTP发送,PS封装按照ISO DEC-13818-1标准。一个PS包包含PS Header, PES Header, PS system header, PS system map等。
2168 0
|
编解码
RTP协议全解析(H264码流和PS流)
写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析, 其中借鉴了很多文章,我都列在了文章最后,在此表示感谢。 互联网的发展离不开大家的无私奉献,我决定从我做起,希望大家支持。
1861 0
|
7月前
|
Linux
百度搜索:蓝易云【Linux系统ps命令:查看正在运行的进程】
通过这些简洁的ps命令用法,你可以方便地查看Linux系统中正在运行的进程信息。
90 1
|
7月前
|
存储 监控 Linux
【Shell 命令集合 系统管理 】⭐⭐⭐Linux 查看当前正在运行的进程信息 ps命令 使用指南
【Shell 命令集合 系统管理 】⭐⭐⭐Linux 查看当前正在运行的进程信息 ps命令 使用指南
133 0
下一篇
DataWorks