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

關於對H264碼流的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)的大致流程,

然後再一一羅列出各個部件的封裝介面

  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 longlong s64Scr)  
  8. {  
  9.     unsigned longlong 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. 相關推薦

    關於H264PS封裝相關程式碼實現

    1、寫在開始之前:            最近因為新工作要維護別人留下的GB模組程式碼,先熟悉了流程,然後也試著封裝了下ps流,結果也能通過測試正常預覽了,當然,其中開發讀文件的頭疼,預覽花屏,卡幀的事情都有遇到,當時慢慢的看文件,整理邏輯,也就都順利解決了,下

    關於H264PS封裝相關實現

    真心 clip gef 但是 占用 udp 大致 結果 方法 轉自:http://www.cnblogs.com/lidabo/p/6604988.html 1、寫在開始之前: 最近因為新工作要維護別人留下的GB模塊代碼,先熟悉了流程,然後也試著封裝

    關於H264的TS的封裝相關程式碼實現

    1 寫在開始之前             在前段時間有分享一個H264封裝ps流到相關文章的,這次和大家分享下將H264封裝成TS流到相關實現,其實也是工作工作需要。依照上篇一樣,分段說明每個資料頭的封裝情況,當然,一樣也會加上rtp頭,方便以後的這方面到需求,如果開發不

    關於H264的TS的封裝相關實現

    有效 當前 完成 read ble tco and mark comm 轉自:http://www.cnblogs.com/lidabo/p/6604998.html 1 寫在開始之前 在前段時間有分享一個H264封裝ps流到相關文章的,這次和

    Java利用JNI呼叫FFMpegh264進行解碼

    前期配置工作: 使用JNI呼叫: java端: package com.recon.action; public class Decode { public native String loadfile(String s); //

    RTP協議全解析(H264PS

    寫在前面:RTP的解析,網上找了很多資料,但是都不全,所以我力圖整理出一個比較全面的解析, 其中借鑑了很多文章,我都列在了文章最後,在此表示感謝。 網際網路的發展離不開大家的無私奉獻,我決定從我做起,希望大家支援。 1、RTP Header解析         

    實現rtp H264的組幀

    rtp打包h264,包含了三種類型的包:  一個rtp包攜帶了一幀資料(single)  多個rtp包攜帶了一幀資料(FU-A)  一個rtp包攜帶了多幀資料(STAP-A) 在實際應用中絕大部分採用的是前兩種方式,對方式1常見的是對nalu的sps,pps進行打包

    媒體開發: RTP協議全解析(H264PS

    1、RTP Header解析                                                                                                                                      

    RTP協議全解析(H264以及PS荷載)

    寫在前面:RTP的解析,網上找了很多資料,但是都不全,所以我力圖整理出一個比較全面的解析,其中借鑑了很多文章,我都列在了文章最後,在此表示感謝。網際網路的發展離不開大家的無私奉獻,我決定從我做起,希望大家支援。1、RTP Header解析                  

    FFMPEG 實時解碼網路H264,RTP封裝

    初學FFMPEG和H264,解碼視訊流時遇到了很多麻煩,記錄一下研究成果。 我使用的FFMPEG 2.5.2版本,使用av_parser_parse2重組影象幀時遇到了一下麻煩! 下面是主要程式碼: RTP頭定義, typedef struct { /**/

    H264中SPS PPS詳解<轉>

    擴展 vlc 地址 逗號 部分 級別 軟件 第一個 bottom 轉載地址:https://zhuanlan.zhihu.com/p/27896239 1 SPS和PPS從何處而來? 2 SPS和PPS中的每個參數起什麽作用? 3 如何解析SDP中

    FFmpeg In Android - H264解碼/OpenGL ES渲染

    主要思路是FFmpeg解碼H264得到一張yuv420p圖片後,傳遞給opengl es在著色器內部做圖片轉換yuv->rgb,然後通過紋理貼圖的方式渲染出來.這種方式的效率更高.核心程式碼如下: #include "common.h" #include "gl_util.h"

    FFmpeg In Android - H264解碼/儲存Yuv

    本節例子原始碼_NativeH264Android,修改自ffmpeg原始碼目錄/doc/examples/decode_video.c H264的碼流結構 H.264原始碼流(又稱為“裸流”)是由一個一個的NALU組成的,包括I幀,B幀,P幀等等,他們的結構如下圖所示: 其中每個

    H264中SPS PPS詳解

    轉載地址:https://zhuanlan.zhihu.com/p/27896239 1 SPS和PPS從何處而來? 2 SPS和PPS中的每個引數起什麼作用? 3 如何解析SDP中包含的H.264的SPS和PPS串? 1 客戶端抓包 在做客戶端視訊解碼時,一

    gstreamer將H264轉為avi視訊檔案示例

    是時候記錄怎麼使用gstreamer庫將h264碼流轉為avi、mp4、flv等視訊檔案了。 下圖是本片示例視訊合成的流程圖,其中H264 採集與佇列實現部分程式碼未貼上。 總體思想是,“視訊合成主執行緒”根據視訊資料通道建立gstreamer視訊合成pipe

    H264打包成RTP包

    H264碼流打包成RTP包的程式碼如下:#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <string.h> #incl

    h264rtp打包(一)

      一幀image編碼完的資料儲存在h264buffer中,編碼後的h264碼流的大小為nH264Size      因為對於NALU,並不是一幀對應一個NALU,而是對於SLICE而言,一個slice就封裝層一個nal,所以一幀可以有多個slice,即一幀有多個nal。

    H264編碼器11( H.264 探索 第二部分 H264格式)

    來自:https://segmentfault.com/a/1190000006698552 表1中描述了所有可能的資料包型別。 Type Definition 0 Undefined

    H264編碼器8( H264打包分析(精華))

    來自:https://www.cnblogs.com/lidabo/p/4602422.html H264碼流打包分析 SODB 資料位元串-->最原始的編碼資料 RBSP 原始位元組序列載荷-->在SODB的後面填加了結尾位元(RBSP trailing bits 一個bit“1”)若

    Wireshark提取RTP包中的H264

    1-- Dump RTP h.264 payload to raw h.264 file (*.264) 2-- According to RFC3984 to dissector H264 payload of RTP to NALU, and write it 3--