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

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

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

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

  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. }
  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. bits_buffer_s   bitsBuffer;
  9. bitsBuffer.i_size = SYS_HDR_LEN;
  10. bitsBuffer.i_data = 0;
  11. bitsBuffer.i_mask = 0x80;
  12. bitsBuffer.p_data = (unsigned char *)(pData);
  13. memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);
  14. /*system header*/
  15. bits_write( &bitsBuffer, 32, 0x000001BB);   /*start code*/
  16. bits_write( &bitsBuffer, 16, SYS_HDR_LEN-6);/*header_length 表示次字节后面的长度,后面的相关头也是次意思*/
  17. bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/
  18. bits_write( &bitsBuffer, 22, 50000);        /*rate_bound*/
  19. bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/
  20. bits_write( &bitsBuffer, 6,  1);            /*audio_bound*/
  21. bits_write( &bitsBuffer, 1,  0);            /*fixed_flag */
  22. bits_write( &bitsBuffer, 1,  1);            /*CSPS_flag */
  23. bits_write( &bitsBuffer, 1,  1);            /*system_audio_lock_flag*/
  24. bits_write( &bitsBuffer, 1,  1);            /*system_video_lock_flag*/
  25. bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/
  26. bits_write( &bitsBuffer, 5,  1);            /*video_bound*/
  27. bits_write( &bitsBuffer, 1,  0);            /*dif from mpeg1*/
  28. bits_write( &bitsBuffer, 7,  0x7F);         /*reserver*/
  29. /*audio stream bound*/
  30. bits_write( &bitsBuffer, 8,  0xC0);         /*stream_id*/
  31. bits_write( &bitsBuffer, 2,  3);            /*marker_bit */
  32. bits_write( &bitsBuffer, 1,  0);            /*PSTD_buffer_bound_scale*/
  33. bits_write( &bitsBuffer, 13, 512);          /*PSTD_buffer_size_bound*/
  34. /*video stream bound*/
  35. bits_write( &bitsBuffer, 8,  0xE0);         /*stream_id*/
  36. bits_write( &bitsBuffer, 2,  3);            /*marker_bit */
  37. bits_write( &bitsBuffer, 1,  1);            /*PSTD_buffer_bound_scale*/
  38. bits_write( &bitsBuffer, 13, 2048);         /*PSTD_buffer_size_bound*/
  39. return 0;
  40. }
  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. bits_buffer_s   bitsBuffer;
  9. bitsBuffer.i_size = PSM_HDR_LEN;
  10. bitsBuffer.i_data = 0;
  11. bitsBuffer.i_mask = 0x80;
  12. bitsBuffer.p_data = (unsigned char *)(pData);
  13. memset(bitsBuffer.p_data, 0, PS_SYS_MAP_SIZE);
  14. bits_write(&bitsBuffer, 24,0x000001);   /*start code*/
  15. bits_write(&bitsBuffer, 8, 0xBC);       /*map stream id*/
  16. bits_write(&bitsBuffer, 16,18);         /*program stream map length*/
  17. bits_write(&bitsBuffer, 1, 1);          /*current next indicator */
  18. bits_write(&bitsBuffer, 2, 3);          /*reserved*/
  19. bits_write(&bitsBuffer, 5, 0);          /*program stream map version*/
  20. bits_write(&bitsBuffer, 7, 0x7F);       /*reserved */
  21. bits_write(&bitsBuffer, 1, 1);          /*marker bit */
  22. bits_write(&bitsBuffer, 16,0);          /*programe stream info length*/
  23. bits_write(&bitsBuffer, 16, 8);         /*elementary stream map length  is*/
  24. /*audio*/
  25. bits_write(&bitsBuffer, 8, 0x90);       /*stream_type*/
  26. bits_write(&bitsBuffer, 8, 0xC0);       /*elementary_stream_id*/
  27. bits_write(&bitsBuffer, 16, 0);         /*elementary_stream_info_length is*/
  28. /*video*/
  29. bits_write(&bitsBuffer, 8, 0x1B);       /*stream_type*/
  30. bits_write(&bitsBuffer, 8, 0xE0);       /*elementary_stream_id*/
  31. bits_write(&bitsBuffer, 16, 0);         /*elementary_stream_info_length */
  32. /*crc (2e b9 0f 3d)*/
  33. bits_write(&bitsBuffer, 8, 0x45);       /*crc (24~31) bits*/
  34. bits_write(&bitsBuffer, 8, 0xBD);       /*crc (16~23) bits*/
  35. bits_write(&bitsBuffer, 8, 0xDC);       /*crc (8~15) bits*/
  36. bits_write(&bitsBuffer, 8, 0xF4);       /*crc (0~7) bits*/
  37. return 0;
  38. }
  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. bits_buffer_s   bitsBuffer;
  13. bitsBuffer.i_size = PES_HDR_LEN;
  14. bitsBuffer.i_data = 0;
  15. bitsBuffer.i_mask = 0x80;
  16. bitsBuffer.p_data = (unsigned char *)(pData);
  17. memset(bitsBuffer.p_data, 0, PES_HDR_LEN);
  18. /*system header*/
  19. bits_write( &bitsBuffer, 24,0x000001);  /*start code*/
  20. bits_write( &bitsBuffer, 8, (stream_id));   /*streamID*/
  21. bits_write( &bitsBuffer, 16,(payload_len)+13);  /*packet_len*/ //指出pes分组中数据长度和该字节后的长度和
  22. bits_write( &bitsBuffer, 2, 2 );        /*'10'*/
  23. bits_write( &bitsBuffer, 2, 0 );        /*scrambling_control*/
  24. bits_write( &bitsBuffer, 1, 0 );        /*priority*/
  25. bits_write( &bitsBuffer, 1, 0 );        /*data_alignment_indicator*/
  26. bits_write( &bitsBuffer, 1, 0 );        /*copyright*/
  27. bits_write( &bitsBuffer, 1, 0 );        /*original_or_copy*/
  28. bits_write( &bitsBuffer, 1, 1 );        /*PTS_flag*/
  29. bits_write( &bitsBuffer, 1, 1 );        /*DTS_flag*/
  30. bits_write( &bitsBuffer, 1, 0 );        /*ESCR_flag*/
  31. bits_write( &bitsBuffer, 1, 0 );        /*ES_rate_flag*/
  32. bits_write( &bitsBuffer, 1, 0 );        /*DSM_trick_mode_flag*/
  33. bits_write( &bitsBuffer, 1, 0 );        /*additional_copy_info_flag*/
  34. bits_write( &bitsBuffer, 1, 0 );        /*PES_CRC_flag*/
  35. bits_write( &bitsBuffer, 1, 0 );        /*PES_extension_flag*/
  36. bits_write( &bitsBuffer, 8, 10);        /*header_data_length*/
  37. // 指出包含在 PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前
  38. //的字节指出了有无可选字段。
  39. /*PTS,DTS*/
  40. bits_write( &bitsBuffer, 4, 3 );                    /*'0011'*/
  41. bits_write( &bitsBuffer, 3, ((pts)>>30)&0x07 );     /*PTS[32..30]*/
  42. bits_write( &bitsBuffer, 1, 1 );
  43. bits_write( &bitsBuffer, 15,((pts)>>15)&0x7FFF);    /*PTS[29..15]*/
  44. bits_write( &bitsBuffer, 1, 1 );
  45. bits_write( &bitsBuffer, 15,(pts)&0x7FFF);          /*PTS[14..0]*/
  46. bits_write( &bitsBuffer, 1, 1 );
  47. bits_write( &bitsBuffer, 4, 1 );                    /*'0001'*/
  48. bits_write( &bitsBuffer, 3, ((dts)>>30)&0x07 );     /*DTS[32..30]*/
  49. bits_write( &bitsBuffer, 1, 1 );
  50. bits_write( &bitsBuffer, 15,((dts)>>15)&0x7FFF);    /*DTS[29..15]*/
  51. bits_write( &bitsBuffer, 1, 1 );
  52. bits_write( &bitsBuffer, 15,(dts)&0x7FFF);          /*DTS[14..0]*/
  53. bits_write( &bitsBuffer, 1, 1 );
  54. return 0;
  55. }
  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. int gb28181_send_rtp_pack(char *databuff, int nDataLen, int mark_flag, Data_Info_s* pPacker)
  11. {
  12. int nRes = 0;
  13. int nPlayLoadLen = 0;
  14. int nSendSize    = 0;
  15. char szRtpHdr[RTP_HDR_LEN];
  16. memset(szRtpHdr, 0, RTP_HDR_LEN);
  17. if(nDataLen + RTP_HDR_LEN <= RTP_MAX_PACKET_BUFF)// 1460 pPacker指针本来有一个1460大小的buffer数据缓存
  18. {
  19. // 一帧数据发送完后,给mark标志位置1
  20. gb28181_make_rtp_header(szRtpHdr, ((mark_flag == 1 )? 1 : 0 ), ++pPacker->u16CSeq, (pPacker->s64CurPts /300), pPacker->u32Ssrc);
  21. memcpy(pPacker->szBuff, szRtpHdr, RTP_HDR_LEN);
  22. memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nDataLen);
  23. nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);
  24. if (nRes != (RTP_HDR_LEN + nDataLen))
  25. {
  26. printf(" udp send error !\n");
  27. return -1;
  28. }
  29. }
  30. else
  31. {
  32. nPlayLoadLen = RTP_MAX_PACKET_BUFF - RTP_HDR_LEN; // 每次只能发送的数据长度 除去rtp头
  33. gb28181_make_rtp_header(pPacker->szBuff, 0, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);
  34. memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nPlayLoadLen);
  35. nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);
  36. if (nRes != (RTP_HDR_LEN + nPlayLoadLen))
  37. {
  38. printf(" udp send error !\n");
  39. return -1;
  40. }
  41. nDataLen -= nPlayLoadLen;
  42. // databuff += (nPlayLoadLen - RTP_HDR_LEN);
  43. databuff += nPlayLoadLen; // 表明前面到数据已经发送出去
  44. databuff -= RTP_HDR_LEN; // 用来存放rtp头
  45. while(nDataLen > 0)
  46. {
  47. if(nDataLen <= nPlayLoadLen)
  48. {
  49. //一帧数据发送完,置mark标志位
  50. gb28181_make_rtp_header(databuff, mark_flag, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);
  51. nSendSize = nDataLen;
  52. }
  53. else
  54. {
  55. gb28181_make_rtp_header(databuff, 0, ++pPacker->u16CSeq, (pPacker->s64CurPts/100), pPacker->u32Ssrc);
  56. nSendSize = nPlayLoadLen;
  57. }
  58. nRet = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize, pPacker);
  59. if (nRes != (RTP_HDR_LEN + nSendSize))
  60. {
  61. printf(" udp send error !\n");
  62. return -1;
  63. }
  64. nDataLen -= nSendSize;
  65. databuff += nSendSize;
  66. //因为buffer指针已经向后移动一次rtp头长度后,
  67. //所以每次循环发送rtp包时,只要向前移动裸数据到长度即可,这是buffer指针实际指向到位置是
  68. //databuff向后重复的rtp长度的裸数据到位置上
  69. }
  70. }
  71. return 0;
  72. }

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

  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头的长度了!

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

关于对H264码流的PS的封装的相关代码实现的更多相关文章

  1. 关于对H264码流的TS的封装的相关代码实现

    1 写在开始之前 在前段时间有分享一个H264封装ps流到相关文章的,这次和大家分享下将H264封装成TS流到相关实现,其实也是工作工作需要.依照上篇一样,分段说明每个数据头的封装情况,当然,一样也会 ...

  2. RTP协议全解析(H264码流和PS流)(转)

    源: RTP协议全解析(H264码流和PS流)

  3. RTP协议全解析(H264码流和PS流)

    转自:http://blog.csdn.net/chen495810242/article/details/39207305 写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个 ...

  4. (转)RTP协议全解(H264码流和PS流)

    写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析, 其中借鉴了很多文章,我都列在了文章最后,在此表示感谢. 互联网的发展离不开大家的无私奉献,我决定从我做起,希 ...

  5. RTP协议全解(H264码流和PS流)

    写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析, 其中借鉴了很多文章,我都列在了文章最后,在此表示感谢. 互联网的发展离不开大家的无私奉献,我决定从我做起,希 ...

  6. H264码流打包分析(精华)

    H264码流打包分析 SODB 数据比特串-->最原始的编码数据 RBSP 原始字节序列载荷-->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit“1”)若 ...

  7. 从H264码流中获取视频宽高 (SPS帧) 升级篇

    之前写过 <从H264码流中获取视频宽高 (SPS帧)> . 但发现很多局限性,而且有时解出来是错误的. 所以重新去研究了. 用了 官方提供的代码库来解析. 花了点时间,从代码库里单独把解 ...

  8. 从H264码流中获取视频宽高 (SPS帧)

    获取.h264视频宽高的方法 花了2个通宵终于搞定.(后面附上完整代码) http://write.blog.csdn.net/postedit/7852406 图像的高和宽在H264的SPS帧中.在 ...

  9. H264码流解析及NALU

    ffmpeg 从mp4上提取H264的nalu http://blog.csdn.net/gavinr/article/details/7183499 639     /* bitstream fil ...

随机推荐

  1. Composer的Autoload源码实现1——启动与初始化

    前言 上一篇文章,我们讨论了 PHP 的自动加载原理.PHP 的命名空间.PHP 的 PSR0 与 PSR4 标准,有了这些知识,其实我们就可以按照 PSR4 标准写出可以自动加载的程序了.然而我们为 ...

  2. Node-Webkit - package.json - 参数设置

    必填: main :(string)APP的主入口,指定一个html文件,如:main:"index.htm". name :(string)APP的名称,必须具有唯一性. 例子: ...

  3. Spring MVC中的模型数据处理

    一.综述 Spring MVC 提供了以下途径来输出模型数据: 1.ModelAndView 当处理方法返回值类型为 ModelAndView时, 方法体即可通过该对象添加模型数据到请求域. 2.Ma ...

  4. Redis的主从同步手动执行故障切换

    1.准备三个redis配置文件,通过端口的区分,启动三个redis数据库实例,然后配置主从复制. # a6371.conf port 6371 daemonize yes pidfile /data/ ...

  5. 我的Android进阶之旅------>Android系统设置默认来电铃声、闹钟铃声、通知铃声

    首先了解Android系统本身提供的默认铃声文件,这些文件都放在  /system/media/audio  目录下. /system/media/audio/ringtones   系统来电铃声 / ...

  6. HDU 4772 Zhuge Liang&#39;s Password (简单模拟题)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4772 题面: Zhuge Liang's Password Time Limit: 2000/1000 ...

  7. Dubbo,ZooKeeper,Redis,FastDFS,ActiveMQ,Keepalived,Nginx,Hudson

    获取[下载地址]   QQ: 313596790   [免费支持更新] 三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体 [新录针对本系统的视频教程,手 ...

  8. springcloud zuul 使用zuulfilter 修改请求路径和响应头

    最近做项目有一个需求:一个网盘系统,文件存放在分布式文件系统中,之前的文件下载统一走的文件下载服务,现在需要在单文件下载的时候不需要走文件下载服务,而是直接访问文件系统上的路径,响应的时候修改响应头, ...

  9. scala与java之间的那些事

    scala与java之间的关系,我认为可以用一句话来开头:scala来源于java,但又高于java. scala的设计者Martin Odersky就是一个JAVA控,这位牛人设计了javac和编写 ...

  10. C#使用SQL语句时候的万用密码问题

    实际上万用密码就是因为SQL里面的语句--是注释,利用bug添加用户名和密码. 例如,用户名为 adada’ or 1=1-- 第一个种写法登录成功了 第二种写法登录失败(正确) 第三种写法登录失败( ...