1 写在开始之前

在前段时间有分享一个H264封装ps流到相关文章的,这次和大家分享下将H264封装成TS流到相关实现,其实也是工作工作需要。依照上篇一样,分段说明每个数据头的封装情况,当然,一样也会加上rtp头,方便以后的这方面到需求,如果开发不需要的话,可   以自行屏蔽掉,当然需要主要buffer指针的移动情况

2 封装的各个头到规则要点
             整个封装过程也是和ps类似,但是最大到区别在于TS流到数据长度都是固定188大小来传输的,而PS流则是可变包结构,正因为两者在结构上到差异,导致了它们在传输误码上的不同抵抗力.TS流由于采用了固定长度的数据包,当传输误码破坏了某一个TS包的同步信息时,接收端可在固定的位置检测到下一个TS包到同步信息,从而恢复同步,避免数据丢失,PS流则长度可变到数据包,当某一个ps包同步信息丢失时,接收端就无法进行信息同步,也就无法确认下一步到同步信息,导致了严重到信息丢失。因此在环境恶劣的情况下, 传输码丢失比较严重,一般都采用TS流来进行避免,当网络环境比较稳定,传输误码概率小,这个时候采用PS流进行传送。
          关于TS流需要了解下节目映射表(PAT:Program Associate Table)以及节目映射表(PMT:Program Map Table),当发送到数据为视频数据关键帧的时候,需要在包头中添加PAT和PMT 
 
  具体结构体如下
  封装组成:(PAT +PMT) + TS + PES + H264 + (TS + H264 + TS + H264 ....)
  数据长度:PES包的长度=188字节-TS包头长度(4字节)-适应域长度(PES长度或者0)

注意:

a    每次数据定长包188个字节,如果不足的则用1填充,这里填充时值每一个bit位都填充,memset就是最好选择。
   b    因为我个人习惯,在封装到时候当为关键帧的时候,我直接丢了PAM+PMT+TS+PES 然后填充满188个字节,这样做提醒大家          是错误到,完全错误的,PES后必须跟H264数据。

c    PES能表示的数据长度只有short, 两个字节,所以当数据长度超过的话,则需要考虑多个PES头

3 各个部件到头的伪代码实现

  1. /*
  2. *@remark: 整体发送数据的抽象逻辑处理函数接口
  3. */
  4. int rtsp_RTPPackage( RTP_SESSION_S *pRtpSender, int nFrameLen, StreamType_E enStreamType)
  5. {
  6. int nRet = 0;
  7. int bVideo = 1 ;
  8. int nSendDataOff = 0;
  9. int nSendSize    = 0;
  10. int nPayLoadSize = 0;
  11. int nHasSend     = 0;
  12. int IFrameFlag   = 0;
  13. char TSFrameHdr[1024];
  14. int nHead = 0;
  15. memset(TSFrameHdr, 0, 1024);
  16. memset(pRtpSender->stRtpPack, 0, RTP_MAX_PACKET_BUFF);
  17. bVideo = ((enStreamType == VIDEO_STREAM ) ? 1 : 0);
  18. // @remark: 判断数据是否为I帧,如果为I帧的话,加上PAT和PMT
  19. if( (bVideo == 1) && pRtpSender->stAvData.u8IFrame == 1)
  20. {
  21. if((nRet = mk_ts_pat_packet(TSFrameHdr +nSendDataOff,
  22. pRtpSender->hHdlTs)) <= 0)
  23. {
  24. DBG_INFO(" mk_ts_pat_packet failed!\n");
  25. return -1;
  26. }
  27. // @remark: 每次添加一个头的时候,必须注意指针到偏移量
  28. nSendDataOff += nRet;
  29. if((nRet = mk_ts_pmt_packet(TSFrameHdr + nSendDataOff,
  30. pRtpSender->hHdlTs)) <= 0)
  31. {
  32. DBG_INFO(" mk_ts_pmt_packet failed!\n");
  33. return -1;
  34. }
  35. nSendDataOff += nRet;
  36. }
  37. // @remark: 添加PS头,需要注意ps里也有一个计数的字段
  38. if((nRet = mk_ts_packet(TSFrameHdr + nSendDataOff, pRtpSender->hHdlTs,
  39. 1, bVideo, pRtpSender->stAvData.u8IFrame, pRtpSender->stAvData.u64TimeStamp)) <= 0 )
  40. {
  41. DBG_INFO(" mk_ts_packet failed!\n");
  42. return -1;
  43. }
  44. nSendDataOff  += nRet;
  45. //此字段是用来计算ts长度,因为ts包是固定188字节长度
  46. nHead = nRet;
  47. // @remark: 添加PES头,后面就必须接H264数据了,不能通过1来填充
  48. if((nRet = mk_pes_packet(TSFrameHdr + nSendDataOff, bVideo, nFrameLen, 1,
  49. pRtpSender->stAvData.u64TimeStamp, pRtpSender->stAvData.u64TimeStamp)) <= 0 )
  50. {
  51. DBG_INFO(" mk_pes_packet failed!\n");
  52. return -1;
  53. }
  54. nSendDataOff += nRet;
  55. nHead += nRet;
  56. // @remark: 如果第一次发送的数据长度大于剩余长度,则先发送ts包剩余长度的数据
  57. if( nFrameLen > (TS_LOAD_LEN - nHead))
  58. {
  59. memcpy(TSFrameHdr + nSendDataOff,  pRtpSender->stAvData.data, TS_LOAD_LEN - nHead);
  60. nSendDataOff += (TS_LOAD_LEN - nHead);
  61. nHasSend      = (TS_LOAD_LEN - nHead);
  62. if( rtsp_send_rtppack(TSFrameHdr, &nSendDataOff, pRtpSender->stAvData.u64TimeStamp, 0, (pRtpSender->stAvData.u8IFrame?1:0), bVideo, 1, pRtpSender) != 0 )
  63. {
  64. DBG_INFO(" rtsp_send_pack failed!\n");
  65. return -1;
  66. }
  67. }
  68. // @remark: 如果第一次发送数据长度小于ts头剩余长度,则,发送数据帧长度,剩余没有188长度的用1填充
  69. else
  70. {
  71. memcpy(TSFrameHdr + nSendDataOff,  pRtpSender->stAvData.data, nFrameLen);
  72. nSendDataOff += nFrameLen;
  73. nHasSend      = nFrameLen;
  74. memset(TSFrameHdr +nSendDataOff, 0xFF, (TS_LOAD_LEN-nHead - nFrameLen));
  75. nSendDataOff += (TS_LOAD_LEN -nHead- nFrameLen);
  76. if( rtsp_send_rtppack(TSFrameHdr, &nSendDataOff, pRtpSender->stAvData.u64TimeStamp, 1, (pRtpSender->stAvData.u8IFrame?1:0), bVideo, 1, pRtpSender) != 0 )
  77. {
  78. DBG_INFO(" rtsp_send_rtppack failed!\n");
  79. return -1;
  80. }
  81. }
  82. // 对应的数据便宜长度,因为我处理的时候时固定1460到rtp包发送数据,所以这里会处理偏移,方便添加rtp头
  83. nPayLoadSize = RTP_MAX_PACKET_BUFF - 4 - RTP_HDR_LEN -  (4+6) * 7; // 减去rtp头,ts头 ,一个rtp包最多7个ts包
  84. nFrameLen -= (TS_LOAD_LEN - nHead);
  85. // @remark: 第二次发送数据了,此时发送数据时候,就需要外再添加ps头了
  86. while(nFrameLen > 0 )
  87. {
  88. nSendSize = (nFrameLen > nPayLoadSize) ? nPayLoadSize : nFrameLen;
  89. if( rtsp_send_rtppack(pRtpSender->stAvData.data + nHasSend, &nSendSize, pRtpSender->stAvData.u64TimeStamp,
  90. ((nSendSize == nFrameLen) ? 1 : 0),  IFrameFlag, bVideo, 0, pRtpSender) != 0 )
  91. {
  92. DBG_INFO(" rtsp_send_rtppack failed!\n");
  93. return -1;
  94. }
  95. nFrameLen -= nSendSize;
  96. nHasSend  += nSendSize;
  97. memset(pRtpSender->stRtpPack, 0, RTP_MAX_PACKET_BUFF);
  98. IFrameFlag = 0;
  99. }
  100. return 0;
  101. }
  1. /*
  2. *@remark : 添加pat头
  3. */
  4. int mk_ts_pat_packet(char *buf, int handle)
  5. {
  6. int nOffset = 0;
  7. int nRet = 0;
  8. if (!buf)
  9. {
  10. return 0;
  11. }
  12. if (0 >= (nRet = ts_header(buf, handle, TS_TYPE_PAT, 1)))
  13. {
  14. return 0;
  15. }
  16. nOffset += nRet;
  17. if (0 >= (nRet = ts_pointer_field(buf + nOffset)))
  18. {
  19. return 0;
  20. }
  21. nOffset += nRet;
  22. if (0 >= (nRet = ts_pat_header(buf + nOffset)))
  23. {
  24. return 0;
  25. }
  26. nOffset += nRet;
  27. // 每一个pat都会当成一个ts包来处理,所以每次剩余部分用1来充填完
  28. memset(buf + nOffset, 0xFF, TS_PACKET_SIZE - nOffset);
  29. return TS_PACKET_SIZE;
  30. }
  31. int ts_pat_header(char *buf)
  32. {
  33. BITS_BUFFER_S bits;
  34. if (!buf)
  35. {
  36. return 0;
  37. }
  38. bits_initwrite(&bits, 32, (unsigned char *)buf);
  39. bits_write(&bits, 8, 0x00);             // table id, 固定为0x00
  40. bits_write(&bits, 1, 1);                // section syntax indicator, 固定为1
  41. bits_write(&bits, 1, 0);                // zero, 0
  42. bits_write(&bits, 2, 0x03);             // reserved1, 固定为0x03
  43. bits_write(&bits, 12, 0x0D);            // section length, 表示这个字节后面有用的字节数, 包括CRC32
  44. bits_write(&bits, 16, 0x0001);          // transport stream id, 用来区别其他的TS流
  45. bits_write(&bits, 2, 0x03);             // reserved2, 固定为0x03
  46. bits_write(&bits, 5, 0x00);             // version number, 范围0-31
  47. bits_write(&bits, 1, 1);                // current next indicator, 0 下一个表有效, 1当前传送的PAT表可以使用
  48. bits_write(&bits, 8, 0x00);             // section number, PAT可能分为多段传输,第一段为00
  49. bits_write(&bits, 8, 0x00);             // last section number
  50. bits_write(&bits, 16, 0x0001);          // program number
  51. bits_write(&bits, 3, 0x07);             // reserved3和pmt_pid是一组,共有几个频道由program number指示
  52. bits_write(&bits, 13, TS_PID_PMT);      // pmt of pid in ts head
  53. bits_write(&bits, 8, 0x9F);             // CRC_32 先暂时写死
  54. bits_write(&bits, 8, 0xC7);
  55. bits_write(&bits, 8, 0x62);
  56. bits_write(&bits, 8, 0x58);
  57. bits_align(&bits);
  58. return bits.i_data;
  59. }
  1. /*
  2. *@remaark: 添加PMT头
  3. */
  4. int mk_ts_pmt_packet(char *buf, int handle)
  5. {
  6. int nOffset = 0;
  7. int nRet = 0;
  8. if (!buf)
  9. {
  10. return 0;
  11. }
  12. if (0 >= (nRet = ts_header(buf, handle, TS_TYPE_PMT, 1)))
  13. {
  14. return 0;
  15. }
  16. nOffset += nRet;
  17. if (0 >= (nRet = ts_pointer_field(buf + nOffset)))
  18. {
  19. return 0;
  20. }
  21. nOffset += nRet;
  22. if (0 >= (nRet = ts_pmt_header(buf + nOffset)))
  23. {
  24. return 0;
  25. }
  26. nOffset += nRet;
  27. // 每一个pmt都会当成一个ts包来处理,所以每次剩余部分用1来充填完
  28. memset(buf + nOffset, 0xFF, TS_PACKET_SIZE - nOffset);
  29. return TS_PACKET_SIZE;
  30. }
  31. int ts_pmt_header(char *buf)
  32. {
  33. BITS_BUFFER_S bits;
  34. if (!buf)
  35. {
  36. return 0;
  37. }
  38. bits_initwrite(&bits, 32, (unsigned char *)buf);
  39. bits_write(&bits, 8, 0x02);             // table id, 固定为0x02
  40. bits_write(&bits, 1, 1);                // section syntax indicator, 固定为1
  41. bits_write(&bits, 1, 0);                // zero, 0
  42. bits_write(&bits, 2, 0x03);             // reserved1, 固定为0x03
  43. bits_write(&bits, 12, 0x1C);            // section length, 表示这个字节后面有用的字节数, 包括CRC32
  44. bits_write(&bits, 16, 0x0001);          // program number, 表示当前的PMT关联到的频道号码
  45. bits_write(&bits, 2, 0x03);             // reserved2, 固定为0x03
  46. bits_write(&bits, 5, 0x00);             // version number, 范围0-31
  47. bits_write(&bits, 1, 1);                // current next indicator, 0 下一个表有效, 1当前传送的PAT表可以使用
  48. bits_write(&bits, 8, 0x00);             // section number, PAT可能分为多段传输,第一段为00
  49. bits_write(&bits, 8, 0x00);             // last section number
  50. bits_write(&bits, 3, 0x07);             // reserved3, 固定为0x07
  51. bits_write(&bits, 13, TS_PID_VIDEO);    // pcr of pid in ts head, 如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF
  52. bits_write(&bits, 4, 0x0F);             // reserved4, 固定为0x0F
  53. bits_write(&bits, 12, 0x00);            // program info length, 前两位bit为00
  54. bits_write(&bits, 8, TS_PMT_STREAMTYPE_H264_VIDEO);     // stream type, 标志是Video还是Audio还是其他数据
  55. bits_write(&bits, 3, 0x07);             // reserved, 固定为0x07
  56. bits_write(&bits, 13, TS_PID_VIDEO);    // elementary of pid in ts head
  57. bits_write(&bits, 4, 0x0F);             // reserved, 固定为0x0F
  58. bits_write(&bits, 12, 0x00);            // elementary stream info length, 前两位bit为00
  59. bits_write(&bits, 8, TS_PMT_STREAMTYPE_11172_AUDIO);        // stream type, 标志是Video还是Audio还是其他数据
  60. bits_write(&bits, 3, 0x07);             // reserved, 固定为0x07
  61. bits_write(&bits, 13, TS_PID_AUDIO);    // elementary of pid in ts head
  62. bits_write(&bits, 4, 0x0F);             // reserved, 固定为0x0F
  63. bits_write(&bits, 12, 0x00);            // elementary stream info length, 前两位bit为00
  64. bits_write(&bits, 8, 0xA4);             // stream type, 标志是Video还是Audio还是其他数据
  65. bits_write(&bits, 3, 0x07);             // reserved, 固定为0x07
  66. bits_write(&bits, 13, 0x00A4);          // elementary of pid in ts head
  67. bits_write(&bits, 4, 0x0F);             // reserved, 固定为0x0F
  68. bits_write(&bits, 12, 0x00);            // elementary stream info length, 前两位bit为00
  69. bits_write(&bits, 8, 0x34);             //CRC_32    先暂时写死
  70. bits_write(&bits, 8, 0x12);
  71. bits_write(&bits, 8, 0xA3);
  72. bits_write(&bits, 8, 0x72);
  73. bits_align(&bits);
  74. return bits.i_data;
  75. }
  1. /*
  2. *@remark: ts头的封装
  3. */
  4. int mk_ts_packet(char *buf, int handle, int bStart, int bVideo, int bIFrame, unsigned long long timestamp)
  5. {
  6. int nOffset = 0;
  7. int nRet = 0;
  8. if (!buf)
  9. {
  10. return 0;
  11. }
  12. if (0 >= (nRet = ts_header(buf, handle, bVideo ? TS_TYPE_VIDEO : TS_TYPE_AUDIO, bStart)))
  13. {
  14. return 0;
  15. }
  16. nOffset += nRet;
  17. if (0 >= (nRet = ts_adaptation_field(buf + nOffset, bStart, bVideo && (bIFrame), timestamp)))
  18. {
  19. return 0;
  20. }
  21. nOffset += nRet;
  22. return nOffset;
  23. }
  24. /* *@remark: ts头相关封装
  25. *  PSI 包括了PAT、PMT、NIT、CAT
  26. *  PSI--Program Specific Information, PAT--program association table, PMT--program map table
  27. *  NIT--network information table, CAT--Conditional Access Table
  28. *  一个网络中可以有多个TS流(用PAT中的ts_id区分)
  29. *  一个TS流中可以有多个频道(用PAT中的pnumber、pmt_pid区分)
  30. *  一个频道中可以有多个PES流(用PMT中的mpt_stream区分)
  31. */
  32. int ts_header(char *buf, int handle, TS_TYPE_E type, int bStart)
  33. {
  34. BITS_BUFFER_S bits;
  35. TS_MNG_S *pMng = (TS_MNG_S *)handle;
  36. if (!buf || !handle || TS_TYPE_BEGIN >= type || TS_TYPE_END <= type)
  37. {
  38. return 0;
  39. }
  40. bits_initwrite(&bits, 32, (unsigned char *)buf);
  41. bits_write(&bits, 8, 0x47);         // sync_byte, 固定为0x47,表示后面的是一个TS分组
  42. // payload unit start indicator根据TS packet究竟是包含PES packet还是包含PSI data而设置不同值
  43. // 1. 若包含的是PES packet header, 设为1,  如果是PES packet余下内容, 则设为0
  44. // 2. 若包含的是PSI data, 设为1, 则payload的第一个byte将是point_field, 0则表示payload中没有point_field
  45. // 3. 若此TS packet为null packet, 此flag设为0
  46. bits_write(&bits, 1, 0);            // transport error indicator
  47. bits_write(&bits, 1, bStart);       // payload unit start indicator
  48. bits_write(&bits, 1, 0);            // transport priority, 1表示高优先级
  49. if (TS_TYPE_PAT == type)
  50. {
  51. bits_write(&bits, 13, 0x00);    // pid, 0x00 PAT, 0x01 CAT
  52. }
  53. else if (TS_TYPE_PMT == type)
  54. {
  55. bits_write(&bits, 13, TS_PID_PMT);
  56. }
  57. else if (TS_TYPE_VIDEO == type)
  58. {
  59. bits_write(&bits, 13, TS_PID_VIDEO);
  60. }
  61. else if (TS_TYPE_AUDIO == type)
  62. {
  63. bits_write(&bits, 13, TS_PID_AUDIO);
  64. }
  65. bits_write(&bits, 2, 0);            // transport scrambling control, 传输加扰控制
  66. if (TS_TYPE_PAT == type || TS_TYPE_PMT == type)
  67. {
  68. // continuity counter, 是具有同一PID值的TS包之间的连续计数值
  69. // 当分组的adaption_field_control字段为00话10时,该字段不递增
  70. bits_write(&bits, 2, 0x01);     // adaptation field control, 00 forbid, 01 have payload, 10 have adaptation, 11 have payload and adaptation
  71. bits_write(&bits, 4, pMng->nPatCounter); // continuity counter, 0~15
  72. if (TS_TYPE_PAT != type)
  73. {
  74. pMng->nPatCounter++;
  75. pMng->nPatCounter &= 0x0F;
  76. }
  77. }
  78. else
  79. {
  80. bits_write(&bits, 2, 0x03);     // 第一位表示有无调整字段,第二位表示有无有效负载
  81. bits_write(&bits, 4, pMng->nContinuityCounter);
  82. pMng->nContinuityCounter++;
  83. pMng->nContinuityCounter &= 0x0F;
  84. }
  85. bits_align(&bits);
  86. return bits.i_data;
  87. }
  1. /*
  2. *remark:添加pes头
  3. */
  4. int mk_pes_packet(char *buf, int bVideo, int length, int bDtsEn, unsigned long long pts, unsigned long long dts)
  5. {
  6. PES_HEAD_S pesHead;
  7. PES_OPTION_S pesOption;
  8. PES_PTS_S pesPts;
  9. PES_PTS_S pesDts;
  10. if (!buf)
  11. {
  12. return 0;
  13. }
  14. if( bVideo == 1)
  15. {
  16. // 视频的采样频率为90kHZ,则增量为3600
  17. pts = pts * 9 / 100;    //  90000Hz
  18. dts = dts * 9 / 100;    //  90000Hz
  19. }
  20. else
  21. {
  22. // 音频的话,则需要按照8000HZ来计算增量[需要的话]
  23. pts = pts * 8 / 1000;   // 8000Hz
  24. dts = dts * 8 / 1000;   // 8000Hz
  25. }
  26. memset(&pesHead, 0, sizeof(pesHead));
  27. memset(&pesOption, 0, sizeof(pesOption));
  28. memset(&pesPts, 0, sizeof(pesPts));
  29. memset(&pesDts, 0, sizeof(pesDts));
  30. pesHead.startcode = htonl(0x000001) >> 8;
  31. pesHead.stream_id = bVideo ? 0xE0 : 0xC0;
  32. if (PES_MAX_SIZE < length)
  33. {
  34. pesHead.pack_len = 0;
  35. }
  36. else
  37. {
  38. pesHead.pack_len = htons(length + sizeof(pesOption) + sizeof(pesPts) + (bDtsEn ? sizeof(pesDts) : 0));
  39. }
  40. pesOption.fixed = 0x02;
  41. pesOption.pts_dts = bDtsEn ? 0x03 : 0x02;
  42. pesOption.head_len = sizeof(pesPts) + (bDtsEn ? sizeof(pesDts) : 0);
  43. pesPts.fixed2 = pesPts.fixed3 = pesPts.fixed4 = 0x01;
  44. pesPts.fixed1 = bDtsEn ? 0x03 : 0x02;
  45. pesPts.ts1 = (pts >> 30) & 0x07;
  46. pesPts.ts2 = (pts >> 22) & 0xFF;
  47. pesPts.ts3 = (pts >> 15) & 0x7F;
  48. pesPts.ts4 = (pts >> 7) & 0xFF;
  49. pesPts.ts5 = pts & 0x7F;
  50. pesDts.fixed1 = pesDts.fixed2 = pesDts.fixed3 = pesDts.fixed4 = 0x01;
  51. pesDts.ts1 = (dts >> 30) & 0x07;
  52. pesDts.ts2 = (dts >> 22) & 0xFF;
  53. pesDts.ts3 = (dts >> 15) & 0x7F;
  54. pesDts.ts4 = (dts >> 7) & 0xFF;
  55. pesDts.ts5 = dts & 0x7F;
  56. char *head = buf;
  57. memcpy(head, &pesHead, sizeof(pesHead));
  58. head += sizeof(pesHead);
  59. memcpy(head, &pesOption, sizeof(pesOption));
  60. head += sizeof(pesOption);
  61. memcpy(head, &pesPts, sizeof(pesPts));
  62. head += sizeof(pesPts);
  63. if (bDtsEn)
  64. {
  65. memcpy(head, &pesDts, sizeof(pesDts));
  66. head += sizeof(pesPts);
  67. }
  68. return (head - buf);
  69. }
  1. /*
  2. *@remark: 最后封装rtp头并发送最终封装好到完整的数据包
  3. */
  4. int rtsp_send_rtppack(char *Databuf, int *datalen, unsigned long curtimestamp, int mark_flag, int IFrameFlag, int bVideo, int nFrameStart, RTP_SESSION_S *pRtpSender)
  5. {
  6. int nHasSend     = 0;
  7. int nRet         = 0;
  8. int nTsHeadNum   = 0;
  9. int nHadDataLen  = 0;
  10. int nTcpSendLen  = 0;
  11. static unsigned short cSeqnum;
  12. // @remark:表示为数据的第一次发送,所以不需要额外再添加ts头
  13. if( nFrameStart == 1 )
  14. {
  15. nRet = mk_rtp_packet(pRtpSender->stRtpPack + nHasSend, mark_flag, IFrameFlag, bVideo, ++cSeqnum, (curtimestamp * 9/100));
  16. nHasSend += nRet;
  17. memcpy(pRtpSender->stRtpPack + nHasSend, Databuf, *datalen);
  18. nHasSend += *datalen;
  19. }
  20. else  // 不是第一次发送此帧数据的话,则需要添加封装新的ts包,并添加ts头
  21. {
  22. // rtp+ rtp_ext + ts  +data
  23. nRet = mk_rtp_packet(pRtpSender->stRtpPack + nHasSend, mark_flag, IFrameFlag, bVideo, ++cSeqnum, (curtimestamp * 9/100));
  24. nHasSend += nRet;
  25. while(*datalen > 0 && nTsHeadNum < 7)
  26. {
  27. nRet = mk_ts_packet(pRtpSender->stRtpPack + nHasSend , pRtpSender->hHdlTs, 0, bVideo, (IFrameFlag > 0 ? 1:0), curtimestamp);
  28. nHasSend += nRet;
  29. if(*datalen < (TS_LOAD_LEN- nRet))
  30. {
  31. memcpy(pRtpSender->stRtpPack + nHasSend, Databuf + nHadDataLen, *datalen);
  32. nHasSend    += *datalen;
  33. nHadDataLen += *datalen;
  34. //不够Ts188用1补充
  35. memset(pRtpSender->stRtpPack + nHasSend, 0xFF, TS_LOAD_LEN- nRet - (*datalen));
  36. nHasSend += (TS_LOAD_LEN - nRet - *datalen);
  37. }
  38. else
  39. {
  40. memcpy(pRtpSender->stRtpPack + nHasSend, Databuf + nHadDataLen, TS_LOAD_LEN - nRet);
  41. nHasSend    += (TS_LOAD_LEN - nRet);
  42. *datalen    -= (TS_LOAD_LEN - nRet);
  43. nHadDataLen += (TS_LOAD_LEN - nRet);
  44. }
  45. nTsHeadNum ++;
  46. }
  47. *datalen = nHadDataLen; //实际发送裸数据到长度
  48. }
  49. if(pRtpSender->RtspsockFd <= 0 )
  50. {
  51. DBG_INFO("send rtp packet socket error\n");
  52. return -1;
  53. }
  54. nTcpSendLen = hi_tcp_noblock_send(pRtpSender->RtspsockFd, pRtpSender->stRtpPack, nHasSend, NULL,1500);
  55. if(nTcpSendLen != nHasSend )
  56. {
  57. DBG_INFO("send rtp packet failed:%s\n",strerror(errno));
  58. return -1;
  59. }
  60. return 0;
  61. }
  1. /*
  2. *remark: 上面用到的一些宏定义和一些关于字节操作的函数,很多一些开源到视频处理的库都能看到,
  3. 为了方便也都将贴出来分享,当然也可以参考下vlc里面的源码
  4. */
  5. /*@remark: 常量定义 */
  6. #define TS_PID_PMT      (0x62)
  7. #define TS_PID_VIDEO    (0x65)
  8. #define TS_PID_AUDIO    (0x84)
  9. #define TS_PMT_STREAMTYPE_11172_AUDIO   (0x03)
  10. #define TS_PMT_STREAMTYPE_13818_AUDIO   (0x04)
  11. #define TS_PMT_STREAMTYPE_AAC_AUDIO     (0x0F)
  12. #define TS_PMT_STREAMTYPE_H264_VIDEO    (0x1B)
  13. /* @remark: 结构体定义 */
  14. typedef struct
  15. {
  16. int i_size;             // p_data字节数
  17. int i_data;             // 当前操作字节的位置
  18. unsigned char i_mask;   // 当前操作位的掩码
  19. unsigned char *p_data;  // bits buffer
  20. } BITS_BUFFER_S;
  21. typedef struct
  22. {
  23. unsigned int startcode      : 24;   // 固定为00 00 01
  24. unsigned int stream_id      : 8;    // 0xC0-0xDF audio stream, 0xE0-0xEF video stream, 0xBD Private stream 1, 0xBE Padding stream, 0xBF Private stream 2
  25. unsigned short pack_len;            // PES packet length
  26. } __attribute__ ((packed)) PES_HEAD_S;
  27. typedef struct
  28. {
  29. #if (BYTE_ORDER == LITTLE_ENDIAN)
  30. unsigned char original      : 1;    // original or copy, 原版或拷贝
  31. unsigned char copyright     : 1;    // copyright flag
  32. unsigned char align         : 1;    // data alignment indicator, 数据定位指示符
  33. unsigned char priority      : 1;    // PES priority
  34. unsigned char scramb        : 2;    // PES Scrambling control, 加扰控制
  35. unsigned char fixed         : 2;    // 固定为10
  36. unsigned char exten         : 1;    // PES extension flag
  37. unsigned char crc           : 1;    // PES CRC flag
  38. unsigned char acopy         : 1;    // additional copy info flag
  39. unsigned char trick         : 1;    // DSM(Digital Storage Media) trick mode flag
  40. unsigned char rate          : 1;    // ES rate flag, ES流速率标志
  41. unsigned char escr          : 1;    // ESCR(Elementary Stream Clock Reference) flag, ES流时钟基准标志
  42. unsigned char pts_dts       : 2;    // PTS DTS flags, 00 no PTS and DTS, 01 forbid, 10 have PTS, 11 have PTS and DTS
  43. #elif (BYTE_ORDER == BIG_ENDIAN)
  44. unsigned char fixed         : 2;    // 固定为10
  45. unsigned char scramb        : 2;    // PES Scrambling control, 加扰控制
  46. unsigned char priority      : 1;    // PES priority
  47. unsigned char align         : 1;    // data alignment indicator, 数据定位指示符
  48. unsigned char copyright     : 1;    // copyright flag
  49. unsigned char original      : 1;    // original or copy, 原版或拷贝
  50. unsigned char pts_dts       : 2;    // PTS DTS flags, 00 no PTS and DTS, 01 forbid, 10 have PTS, 11 have PTS and DTS
  51. unsigned char escr          : 1;    // ESCR(Elementary Stream Clock Reference) flag, ES流时钟基准标志
  52. unsigned char rate          : 1;    // ES rate flag, ES流速率标志
  53. unsigned char trick         : 1;    // DSM(Digital Storage Media) trick mode flag
  54. unsigned char acopy         : 1;    // additional copy info flag
  55. unsigned char crc           : 1;    // PES CRC flag
  56. unsigned char exten         : 1;    // PES extension flag
  57. #endif
  58. unsigned char head_len;             // PES header data length
  59. } __attribute__ ((packed)) PES_OPTION_S;
  60. typedef struct
  61. {// ts total 33 bits
  62. #if (BYTE_ORDER == LITTLE_ENDIAN)
  63. unsigned char fixed2        : 1;    // 固定为1
  64. unsigned char ts1           : 3;    // bit30-32
  65. unsigned char fixed1        : 4;    // DTS为0x01, PTS为0x02, PTS+DTS则PTS为0x03
  66. unsigned char ts2;                  // bit22-29
  67. unsigned char fixed3        : 1;    // 固定为1
  68. unsigned char ts3           : 7;    // bit15-21
  69. unsigned char ts4;                  // bit7-14
  70. unsigned char fixed4        : 1;    // 固定为1
  71. unsigned char ts5           : 7;    // bit0-6
  72. #elif (BYTE_ORDER == BIG_ENDIAN)
  73. unsigned char fixed1        : 4;    // DTS为0x01, PTS为0x02, PTS+DTS则PTS为0x03
  74. unsigned char ts1           : 3;    // bit30-32
  75. unsigned char fixed2        : 1;    // 固定为1
  76. unsigned char ts2;                  // bit22-29
  77. unsigned char ts3           : 7;    // bit15-21
  78. unsigned char fixed3        : 1;    // 固定为1
  79. unsigned char ts4;                  // bit7-14
  80. unsigned char ts5           : 7;    // bit0-6
  81. unsigned char fixed4        : 1;    // 固定为1
  82. #endif
  83. } __attribute__ ((packed)) PES_PTS_S;
  84. /* remark:接口函数定义 */
  85. int bits_initwrite(BITS_BUFFER_S *p_buffer, int i_size, unsigned char *p_data)
  86. {
  87. if (!p_data)
  88. {
  89. return -1;
  90. }
  91. p_buffer->i_size = i_size;
  92. p_buffer->i_data = 0;
  93. p_buffer->i_mask = 0x80;
  94. p_buffer->p_data = p_data;
  95. p_buffer->p_data[0] = 0;
  96. return 0;
  97. }
  98. void bits_align(BITS_BUFFER_S *p_buffer)
  99. {
  100. if (p_buffer->i_mask != 0x80 && p_buffer->i_data < p_buffer->i_size)
  101. {
  102. p_buffer->i_mask = 0x80;
  103. p_buffer->i_data++;
  104. p_buffer->p_data[p_buffer->i_data] = 0x00;
  105. }
  106. }
  107. inline void bits_write(BITS_BUFFER_S *p_buffer, int i_count, unsigned long i_bits)
  108. {
  109. while (i_count > 0)
  110. {
  111. i_count--;
  112. if ((i_bits >> i_count ) & 0x01)
  113. {
  114. p_buffer->p_data[p_buffer->i_data] |= p_buffer->i_mask;
  115. }
  116. else
  117. {
  118. p_buffer->p_data[p_buffer->i_data] &= ~p_buffer->i_mask;
  119. }
  120. p_buffer->i_mask >>= 1;
  121. if (p_buffer->i_mask == 0)
  122. {
  123. p_buffer->i_data++;
  124. p_buffer->i_mask = 0x80;
  125. }
  126. }
  127. }
  128. int bits_initread(BITS_BUFFER_S *p_buffer, int i_size, unsigned char *p_data)
  129. {
  130. if (!p_data)
  131. {
  132. return -1;
  133. }
  134. p_buffer->i_size = i_size;
  135. p_buffer->i_data = 0;
  136. p_buffer->i_mask = 0x80;
  137. p_buffer->p_data = p_data;
  138. return 0;
  139. }
  140. inline int bits_read(BITS_BUFFER_S *p_buffer, int i_count, unsigned long *i_bits)
  141. {
  142. if (!i_bits)
  143. {
  144. return -1;
  145. }
  146. *i_bits = 0;
  147. while (i_count > 0)
  148. {
  149. i_count--;
  150. if (p_buffer->p_data[p_buffer->i_data] & p_buffer->i_mask)
  151. {
  152. *i_bits |= 0x01;
  153. }
  154. if (i_count)
  155. {
  156. *i_bits = *i_bits << 1;
  157. }
  158. p_buffer->i_mask >>= 1;
  159. if(p_buffer->i_mask == 0)
  160. {
  161. p_buffer->i_data++;
  162. p_buffer->i_mask = 0x80;
  163. }
  164. }
  165. return 0;
  166. }

5 写在最后
   看过我上一篇的关于ps封装的可能会注意的,关于压字节的处理,两篇博文到处理方式有些差异。关于我这个我简单说两点
   第一次是这个ts的处理里面封装是另外一个同事实现的,我因为用到所以拿来使用,但是上次调用封装都是自己完成。第二个就是
   ps和ts的处理方式不一样。一个定长,一个不定长。所以这样处理,也挺好的,我也有点懒,所以没有改过来。

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

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

    1.写在开始之前: 最近因为新工作要维护别人留下的GB模块代码,先熟悉了流程,然后也试着封装了下ps流,结果也能通过测试正常预览了,当然,其中开发读文档的头疼,预览花屏,卡帧的事情都有遇到,当时慢慢的 ...

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

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

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

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

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

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

  5. H264码流解析及NALU

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

  6. hisi出的H264码流结构

    hisi出的H264码流结构: IDR帧结构如下: 开始码 + nalu + I帧    +    开始码 + nalu + SPS    +     开始码 + nalu + PPS    +    ...

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

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

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

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

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

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

随机推荐

  1. jquery基础研究学习【效果】

    ---------------------------------分割线-------------------------------------- 2017年6月7日18:16:35Query 效果 ...

  2. Intersection(计算几何)

    Intersection Time Limit: 4000/4000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java/Others)To ...

  3. 九度OJ 1341:艾薇儿的演唱会 (最短路)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:528 解决:241 题目描述: 艾薇儿今天来到了中国,她计划两天后在哈尔滨举行一场个人的演唱会.由于出现了紧急情况,演唱会的举办方要求艾薇儿 ...

  4. 让Xcode支持高版本系统设备真机测试

    最新支持11.2 (15C107) Xcode只可以支持iPhone手机对应iOS系统以下的真机测试.一般想要支持最新的iPhone手机系统,有两个方法. 第一.就需要更新Xcode,这一个方法有一个 ...

  5. Surpassing Human-Level Face Verification Performance on LFW with GaussianFace

    Face verification remains a challenging problem in very complex conditions with large variations suc ...

  6. 【python】-- IO多路复用(select、poll、epoll)介绍及实现

    IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...

  7. ZOJ - 3537 Cake (凸包+区间DP+最优三角剖分)

    Description You want to hold a party. Here's a polygon-shaped cake on the table. You'd like to cut t ...

  8. PHP 提高PHP性能的编码技巧以及性能优化

    0.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这 么做,它是 一种可以把多个字符串当作参数的“函数”(译注:PHP ...

  9. cocos2d-x CCControl控件

    感谢点评与关注.欢迎转载与分享.勤奋努力,持之以恒! CCControlSlider 滑动条 void HelloWorld::myInit10() { CCSize size = CCDirecto ...

  10. anaconda + opencv3

    直接运行 pip install opencv-python 或者 pip install opencv-contrib-python 参照如下网页 https://blog.csdn.net/sin ...