1. 程式人生 > >關於對H264碼流的TS的封裝的相關代碼實現

關於對H264碼流的TS的封裝的相關代碼實現

有效 當前 完成 read ble tco and mark comm

轉自:http://www.cnblogs.com/lidabo/p/6604998.html

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 各個部件到頭的偽代碼實現

[cpp] view plain copy
  1. /*
  2. [email protected]
/* */: 整體發送數據的抽象邏輯處理函數接口
  • */
  • int rtsp_RTPPackage( RTP_SESSION_S *pRtpSender, int nFrameLen, StreamType_E enStreamType)
  • {
  • int nRet = 0;
  • int bVideo = 1 ;
  • int nSendDataOff = 0;
  • int nSendSize = 0;
  • int nPayLoadSize = 0;
  • int nHasSend = 0;
  • int IFrameFlag = 0;
  • char TSFrameHdr[1024];
  • int nHead = 0;
  • memset(TSFrameHdr, 0, 1024);
  • memset(pRtpSender->stRtpPack, 0, RTP_MAX_PACKET_BUFF);
  • bVideo = ((enStreamType == VIDEO_STREAM ) ? 1 : 0);
  • // @remark: 判斷數據是否為I幀,如果為I幀的話,加上PAT和PMT
  • if( (bVideo == 1) && pRtpSender->stAvData.u8IFrame == 1)
  • {
  • if((nRet = mk_ts_pat_packet(TSFrameHdr +nSendDataOff,
  • pRtpSender->hHdlTs)) <= 0)
  • {
  • DBG_INFO(" mk_ts_pat_packet failed!\n");
  • return -1;
  • }
  • // @remark: 每次添加一個頭的時候,必須註意指針到偏移量
  • nSendDataOff += nRet;
  • if((nRet = mk_ts_pmt_packet(TSFrameHdr + nSendDataOff,
  • pRtpSender->hHdlTs)) <= 0)
  • {
  • DBG_INFO(" mk_ts_pmt_packet failed!\n");
  • return -1;
  • }
  • nSendDataOff += nRet;
  • }
  • // @remark: 添加PS頭,需要註意ps裏也有一個計數的字段
  • if((nRet = mk_ts_packet(TSFrameHdr + nSendDataOff, pRtpSender->hHdlTs,
  • 1, bVideo, pRtpSender->stAvData.u8IFrame, pRtpSender->stAvData.u64TimeStamp)) <= 0 )
  • {
  • DBG_INFO(" mk_ts_packet failed!\n");
  • return -1;
  • }
  • nSendDataOff += nRet;
  • //此字段是用來計算ts長度,因為ts包是固定188字節長度
  • nHead = nRet;
  • // @remark: 添加PES頭,後面就必須接H264數據了,不能通過1來填充
  • if((nRet = mk_pes_packet(TSFrameHdr + nSendDataOff, bVideo, nFrameLen, 1,
  • pRtpSender->stAvData.u64TimeStamp, pRtpSender->stAvData.u64TimeStamp)) <= 0 )
  • {
  • DBG_INFO(" mk_pes_packet failed!\n");
  • return -1;
  • }
  • nSendDataOff += nRet;
  • nHead += nRet;
  • // @remark: 如果第一次發送的數據長度大於剩余長度,則先發送ts包剩余長度的數據
  • if( nFrameLen > (TS_LOAD_LEN - nHead))
  • {
  • memcpy(TSFrameHdr + nSendDataOff, pRtpSender->stAvData.data, TS_LOAD_LEN - nHead);
  • nSendDataOff += (TS_LOAD_LEN - nHead);
  • nHasSend = (TS_LOAD_LEN - nHead);
  • if( rtsp_send_rtppack(TSFrameHdr, &nSendDataOff, pRtpSender->stAvData.u64TimeStamp, 0, (pRtpSender->stAvData.u8IFrame?1:0), bVideo, 1, pRtpSender) != 0 )
  • {
  • DBG_INFO(" rtsp_send_pack failed!\n");
  • return -1;
  • }
  • }
  • // @remark: 如果第一次發送數據長度小於ts頭剩余長度,則,發送數據幀長度,剩余沒有188長度的用1填充
  • else
  • {
  • memcpy(TSFrameHdr + nSendDataOff, pRtpSender->stAvData.data, nFrameLen);
  • nSendDataOff += nFrameLen;
  • nHasSend = nFrameLen;
  • memset(TSFrameHdr +nSendDataOff, 0xFF, (TS_LOAD_LEN-nHead - nFrameLen));
  • nSendDataOff += (TS_LOAD_LEN -nHead- nFrameLen);
  • if( rtsp_send_rtppack(TSFrameHdr, &nSendDataOff, pRtpSender->stAvData.u64TimeStamp, 1, (pRtpSender->stAvData.u8IFrame?1:0), bVideo, 1, pRtpSender) != 0 )
  • {
  • DBG_INFO(" rtsp_send_rtppack failed!\n");
  • return -1;
  • }
  • }
  • // 對應的數據便宜長度,因為我處理的時候時固定1460到rtp包發送數據,所以這裏會處理偏移,方便添加rtp頭
  • nPayLoadSize = RTP_MAX_PACKET_BUFF - 4 - RTP_HDR_LEN - (4+6) * 7; // 減去rtp頭,ts頭 ,一個rtp包最多7個ts包
  • nFrameLen -= (TS_LOAD_LEN - nHead);
  • // @remark: 第二次發送數據了,此時發送數據時候,就需要外再添加ps頭了
  • while(nFrameLen > 0 )
  • {
  • nSendSize = (nFrameLen > nPayLoadSize) ? nPayLoadSize : nFrameLen;
  • if( rtsp_send_rtppack(pRtpSender->stAvData.data + nHasSend, &nSendSize, pRtpSender->stAvData.u64TimeStamp,
  • ((nSendSize == nFrameLen) ? 1 : 0), IFrameFlag, bVideo, 0, pRtpSender) != 0 )
  • {
  • DBG_INFO(" rtsp_send_rtppack failed!\n");
  • return -1;
  • }
  • nFrameLen -= nSendSize;
  • nHasSend += nSendSize;
  • memset(pRtpSender->stRtpPack, 0, RTP_MAX_PACKET_BUFF);
  • IFrameFlag = 0;
  • }
  • return 0;
  • }
  • [cpp] view plain copy
    1. /*
    2. [email protected] : 添加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. }

    [cpp] view plain copy
    1. /*
    2. [email protected]: 添加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. }



    [cpp] view plain copy
    1. /*
    2. [email protected]: 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. /* [email protected]: 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. }



    [cpp] view plain copy
    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. }



    [cpp] view plain copy
    1. /*
    2. [email protected]: 最後封裝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. }



    [cpp] view plain copy
    1. /*
    2. *remark: 上面用到的一些宏定義和一些關於字節操作的函數,很多一些開源到視頻處理的庫都能看到,
    3. 為了方便也都將貼出來分享,當然也可以參考下vlc裏面的源碼
    4. */
    5. [email protected]: 常量定義 */
    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的封裝的相關代碼實現