1. 程式人生 > >GB28181協議支援的H264的PS封裝實現

GB28181協議支援的H264的PS封裝實現

1、寫在前面:

最開始接觸H264的PS封裝的時候,參考的是:關於對H264碼流的PS的封裝的相關程式碼實現 , 確實是很有幫助,但完全參照這個實現,發現問題也很多,主要還是對MPEG213818的封裝協議理解不深產生,所以我們在參考程式碼實現時,還是需要對原理做深入細緻的分析,特別是封裝涉及到bit級別的配置,一個bit配錯了,可能就播放不了,所以記錄下,做個備份。

2、封裝需要基本瞭解的概念:

RTP:是流媒體實時傳輸協議,RTP頭有12個位元組

H264視訊幀:由NALU單元組成,其中I幀起始是00 00 00 01 65

                                                非I幀 00 00 00 01 41

                                                 SPS 00 00 00 01 67

                                                 PPS 00 00 00 01 68

根據上面參考文章的說法,I幀前面需要增加PS頭+System 頭+ System Map 頭+ PES 頭

非I幀前面增加PS 頭 + PES 頭

3、對比參考文章做的一些修改:

1、PS封裝頭的長度是可以變化的,不是固定長度

      參考文章中PS頭:

  1. #define PS_HDR_LEN  14
  2. #define SYS_HDR_LEN 18
  3. #define PSM_HDR_LEN 24
  4. #define PES_HDR_LEN 19

我們定義的長度如下:

#define PS_HDR_LEN  14
#define PSM_HDR_LEN 24
#define SYS_HDR_LEN 18
#define PES_HDR_LEN 14

2、關鍵的PTS和DTS是播放的關鍵因素,PTS:顯示時間戳,DTS:解碼時間戳

      PTS可以是一個相對值,以90KHZ取樣,25fps的視訊為例,每幀視訊的步長應該為3600

      另外:參考文章中PTS的計算和DTS的計算有問題,封裝後的視訊通過VLC播放時會一閃而過,修改後的函式如下:

static void Packet_PS_header(char* pDestBuf, int length, int currPts)
{
    unsigned long long lScrExt = 0;//(currPts) % 100;
    unsigned long s64Scr = currPts;//currPts / 100;

	bits_buffer_t bits;
	if ( NULL == pDestBuf)
	{
		return PS_Error_Param;
	}
	bits_initwrite( &bits, length, pDestBuf);
    bits_write(&bits, 32, 0x000001BA);            /*start codes*/
    bits_write(&bits, 2,  1);                     /*marker bits '01b'*/
    bits_write(&bits, 3,  (s64Scr>>30)&0x07);     /*System clock [32..30]*/
    bits_write(&bits, 1,  1);                     /*marker bit*/
    bits_write(&bits, 15, (s64Scr>>15)&0x7FFF);   /*System clock [29..15]*/
    bits_write(&bits, 1,  1);                     /*marker bit*/
    bits_write(&bits, 15, s64Scr&0x7fff);         /*System clock [29..15]*/
    bits_write(&bits, 1,  1);                     /*marker bit*/
    bits_write(&bits, 9,  lScrExt&0x01ff);        /*System clock [14..0]*/
    bits_write(&bits, 1,  1);                     /*marker bit*/
    bits_write(&bits, 22, (160001)&0x3fffff);        /*bit rate(n units of 50 bytes per second.)*/
    bits_write(&bits, 2,  3);                     /*marker bits '11'*/
    bits_write(&bits, 5,  0x1f);                  /*reserved(reserved for future use)*/
    bits_write(&bits, 3,  0);                     /*stuffing length*/
}

3、PES頭中,如果只包括PTS時間戳,則需要修改為下面程式碼:

修改的時候把DTS去掉了,然後配套修改了第8個位元組,但沒有檢查原來參考文章中設定的是同時包括PTS和DTS,所以需要關注修改:第七位元組的高兩位是PTS和DTS指示位,00表示無PTS無DTS,01禁止使用,10表示PES頭部欄位會附加PTS結構,11表示PTS和DTS都包括

static void gb28181_make_pes_header (unsigned char *dst , int32_t dstlen, int32_t data_length, int pts)
{
	short datalen = data_length + 8;
	bits_buffer_t bits;
	bits_initwrite( &bits, dstlen, dst);
	bits_write( &bits, 24, 0x000001 ); // header
	bits_write( &bits, 8, 0xe0 );

	bits_write( &bits, 16, datalen); //pes_packet_length : es len and the following pes len
	bits_write( &bits, 8, 0x8c ); //
	bits_write( &bits, 2, 0x02 ); //第七位元組的高兩位是PTS和DTS指示位,00表示無PTS無DTS,01禁止使用,10表示PES頭部欄位會附加PTS結構,11表示PTS和DTS都包括
	bits_write( &bits, 6, 0x00 ); //
	bits_write( &bits, 8, 0x05 ); //8
	//UINT64 i_scr = I_SCR(_iFrameIndextemp);

    bits_write( &bits, 4, 2 );                    /*'0010'*/
    bits_write( &bits, 3, ((pts)>>30)&0x07 );     /*PTS[32..30]*/
    bits_write( &bits, 1, 1 ); 
    bits_write( &bits, 15,((pts)>>15)&0x7FFF);    /*PTS[29..15]*/
    bits_write( &bits, 1, 1 );
    bits_write( &bits, 15,(pts)&0x7FFF);          /*PTS[14..0]*/
    bits_write( &bits, 1, 1 );
}

4、RTP的組包傳送上面,根據H264的RTP打包方式,有單NALU、FU-A、FU-B多種形式,根據適配需要調整。