天天看點

嵌入式 視訊編碼(H264)

這幾天在編寫視訊錄制子產品,是以,閑暇之餘,又粗粗的整理了一下,主要是api,以備不時之用   

嵌入式 視訊編碼(H264)

    攝像頭擷取的模拟信号通過經晶片處理(我們使用的是cx25825),将模拟信号轉成數字信号,産生标準的itu 656 yuv格式的數字信号以幀為機關送到編碼卡上的dsp和記憶體中。分别供視訊實時預覽、移動偵測處理以及編碼等使用。其中編碼的作用是将編碼卡記憶體中的yuv資料送到h264編碼器中,進過h.264編碼産生壓縮好的碼流,送到主機記憶體中,供錄像或網絡傳輸使用。編碼子產品完成各個協定編碼,協調 md、vpp

相關子產品的管理、同步和控制,配合軟體排程和硬體共同完成視訊編碼相關功能。

一、重要概念

主次碼流

    主次碼流是指硬體邏輯單元啟動一次同時産生的 2 路碼流,即 1 路主碼流和

1 路次碼流。主碼流和次碼流可以為不同的編碼協定,但其寬高比例都必須滿足 1:1、1:2 或 1:4,次碼流不能單獨存在(必須和 1 路主碼流在同一個通道組中) 。

雙碼流

    雙碼流是指硬體邏輯單元啟動 2 次分時産生的 2 個碼流,即 2 路主碼流。雙碼流可以為不同的編碼協定,雙碼流之間的大小比例沒有限制關系。

通道組

通道組是指晶片能夠同時處理的編碼通道的集合,相當于一個容器。一個通道組最多可同時包含 1 路主碼流(h.264/mjpeg) 、1路次碼流(h.264/mjpeg) ,或者僅包含 1 路 jpeg抓拍(即 jpeg抓拍時,不允許包含任何其他通道) ,或者 1 路mpeg4 編碼通道。

h264

    h.264 的功能分為兩層:視訊編碼層(vcl,

videocoding layer)和網絡提取層(nal, networkabstraction layer)。vcl資料即編碼處理的輸出,它表示被壓縮編碼後的視訊數序列。在vcl資料傳輸或存儲之前,這些編碼的vcl資料,先被映射或封裝進 nal單元中。每個nal單元包括一個原始位元組序列負荷(rbsp,

raw byte sequencepayload)、一組對應于視訊編碼的 nal 頭資訊。rbsp 的基本結構是:在原始編碼資料的後面填加了結尾比特。一個bit“1”若幹比特“0”,以便位元組對齊。h.264的編碼視訊序列包括一系列的nal單元,每個 nal單元包含一個rbsp。編碼片(包括資料分割片 idr片)和序列rbsp結束符被定義為vcl

nal單元,其餘為 nal 單元。典型的 rbsp 單元序列如圖 2 所示。每個單元都按獨立的 nal 單元傳送。單元的資訊頭(一個位元組)定義了rbsp 單元的類型,nal單元的其餘部分為 rbsp 資料。

二、相關結構

1.定義編碼通道屬性結構體:

typedef structhivenc_chn_attr_s

{

 payload_type_e entype; //編碼協定類型

 hi_void  *pvalue;           //編碼屬性指針

}venc_chn_attr_s;

2.定義 h.264編碼屬性結構體:

typedef structhivenc_attr_h264_s

    hi_u32 u32priority;           //通道優先級。 目前未使用,取值不限。

    hi_u32 u32picwidth;         //編碼圖像寬度。 取值範圍:[160, 2048],以像素為機關。

靜态屬性。

    hi_u32 u32picheight;        //編碼圖像高度。 取值範圍:[112, 1536],以像素為機關。靜态屬性。

    hi_u32 u32viframerate;   //vi 輸入的幀率(原始幀率)。 取值範圍: p制:(0, 25],以幀為機關。  n制:(0, 30],以幀為機關。靜态屬性。

    hi_bool bmainstream;      //主次碼流辨別。 取值範圍:{hi_true, hi_false}。 hi_true:主碼流。 hi_false:次碼流。 靜态屬性。

    hi_bool bvifield;             //輸入圖像的幀場标志。 取值範圍:{hi_true, hi_false}。 hi_true:場。 

hi_false:幀。 靜态屬性。目前未使用。

    hi_bool bfield;                //幀場編碼模式。 取值範圍:{hi_true, hi_false}。

hi_true:場編碼。 hi_false:幀編碼。 靜态屬性。

    //推薦值:一幅yuv420編碼圖像大小。以編碼 d1 圖像為例,推薦值為 704×576×1.5 byte。 最小值:一幅 yuv420編碼圖像大小的的

1/2。 最大值:無限制,但是會消耗更多的記憶體。

    hi_u32 u32bufsize;               // 碼流 buffer大小。 取值範圍:[min,

max],以 byte 為機關。靜态屬性。  

    hi_bool bbyframe;               //幀/包模式擷取碼流。 取值範圍:{hi_true, hi_false}。

hi_true:按幀擷取。  hi_false:按包擷取。靜态屬性。  

    hi_u32 u32targetframerate;// 目标幀率。取值範圍: p制:[1/16, 25],n制:[1/16, 30],以幀/秒為機關。整數:高 16bit 為0。 分數:高 16bit 為分母,低

16bit 為分子。 動态屬性。   

    hi_u32 u32gop;                   //i幀間隔。

取值範圍:[0, 1000],以幀為機關。 動态屬性。

    hi_u32 u32maxdelay;          // 最大延遲。取值範圍:最大延遲,以幀為機關。目前未使用。  動态屬性。

    rc_mode_e enrcmode;       //碼率控制模式。 取值範圍:[0, 3]。 0:vbr 模式。 1:cbr 模式。 2:abr 模式。 3:fixqp。 動态屬性。

    hi_u32 u32bitrate;              //cbr/abr 模式,表示平均碼率。 vbr

模式,表示最大碼率。 fixqp 模式,該值無效。 取值範圍:[1, 20000],機關 kbps。動态屬性。

    hi_u32 u32piclevel;           //圖像等級,僅 vbr/cbr模式下有效。 vbr 模式下,表示圖像的品質等級。 取值範圍:[0, 5],值越小,圖像品質越好。

    hi_s32 s32qpi;                 //i幀 qp。fixqp 模式下有效。

取值範圍:[10, 50]。

    hi_s32 s32qpp;                //p幀 qp。fixqp 模式下有效。

    hi_s32 s32minutes;           //碼率統計時段。abr 模式下有效。

abr,即碼率短時間波動,長時間平穩。長時間碼率的統計,以此時間為準。 

}venc_attr_h264_s;

3.定義編碼的數字水印的結構體:

#define   dwm_key_len 8       //密鑰字元的最大個數

#define   dwm_char_len 16   //水印字元個數

typedef structhivenc_wm_attr_s

 hi_u8 au8key[dwm_key_len];  //數字水印的密鑰字元串。最多 8 個字元,不滿 8 個字元填充 0。

 hi_u8 au8user[dwm_char_len];  //數字水印使用者字元。個數最多不超過 dwm_char_len。

}venc_wm_attr_s;

4.定義編碼通道的狀态結構體:

typedef structhivenc_chn_stat_s

 hi_bool bregistered;               //注冊到通道組标志:取值範圍:{hi_true, hi_false}。

 hi_u32 u32leftpics;                  //待編碼的圖像數。

 hi_u32 u32leftstreambytes;      //碼流 buffer剩餘的 byte數。

 hi_u32 u32curpacks;               //目前幀的碼流包個數。

}venc_chn_stat_s;

5.定義幀碼流類型結構體:  

typedef structhivenc_stream_s

 venc_pack_s *pstpack;  //幀碼流包結構。

 hi_u32 u32packcount;    //一幀碼流的所有包的個數。

 hi_u32 u32seq;             //碼流序列号。 按幀擷取幀序号;按包擷取包序号。

}venc_stream_s;

6.定義幀碼流包結構體:

typedef structhivenc_pack_s

 hi_u32   u32phyaddr[2];  //碼流包首位址。

 hi_u8   *pu8addr[2];      //碼流包實體位址。

 hi_u32   u32len[2];        //碼流包長度。

 venc_data_type_u datatype; //碼流類型。

 hi_u64   u64pts; //時間戳。機關:us。

 hi_bool   bfieldend;    //場結束辨別。 取值範圍: hi_true:該碼流包是該場的最後一個包。 hi_false:該碼流包不是該場的最後一個包。

 hi_bool   bframeend; //幀結束辨別。 取值範圍: hi_true:該碼流包是該幀的最後一個包。 hi_false:該碼流包不是該場的最後一個包。

}venc_pack_s;

7.定義碼流結果類型

typedef unionhivenc_data_type_u

 h264e_nalu_type_e enh264etype;  //h.264 碼流包類型

 jpege_pack_type_e enjpegetype;

 mpeg4e_pack_type_e enmpeg4etype;

}venc_data_type_u;

8.定義 jpeg碼流的 pack類型

typedef enumhijpege_pack_type_e

  jpege_pack_ecs = 5, //ecs類型

  jpege_pack_app = 6,

  jpege_pack_vdo = 7,

  jpege_pack_pic = 8,

 jpege_pack_butt

} jpege_pack_type_e;

9.定義 mpeg4碼流的pack類型

typedef enumhimpeg4e_pack_type_e

  mpeg4e_pack_vo  = 1, //vo類型

  mpeg4e_pack_vos = 2,

  mpeg4e_pack_vol = 3,

  mpeg4e_pack_vop = 4,

  mpeg4e_pack_slice = 5

} mpeg4e_pack_type_e;

10.定義 h.264碼流 nalu類型

typedef enumhih264e_nalu_type_e

 h264e_nalu_pslice = 1, //pslice 類型

 h264e_nalu_islice = 5,

 h264e_nalu_sei = 6,

 h264e_nalu_sps = 7,

 h264e_nalu_pps = 8,

 h264e_nalu_butt

} h264e_nalu_type_e;

11.定義 h.264編碼的 nalu大小設定結構體

typedef structhivenc_attr_h264_nalu_s

    hi_bool bnalusplitenable;   //是否打開 nalu劃分,hi_ture:打開。

    hi_u32 u32nalusize;  //nalu劃分使能的情況下指定 nalu的大小,以位元組為機關,在關閉使能的情況下,此參數無效。 必須滿足 128 <=u32nalusize <= 圖象大小(包括色度)。

} venc_attr_h264_nalu_s;

12.定義 h.264編碼的碼率控制模式

typedef enum hirc_mode_e

   rc_mode_vbr = 0,    //可變碼率模式。該模式下,碼率波動大,圖像品質穩定

   rc_mode_cbr,          //恒定碼率模式。該模式下,碼率始終保持平穩。

   rc_mode_abr,          //平均碼率模式。該模式下,碼率長時間平穩,短時間内波動。

   rc_mode_fixqp,       //固定 qp模式。該模式下,使用固定的 qp分别編碼 i幀和 p幀。

   rc_mode_butt, 

} rc_mode_e;

三、api 參考

    視訊編碼功能實際包含 venc(視訊編碼)和 group(通道組管理)兩個重要的部分,主要提供視訊編碼通道組的建立和銷毀、通道組 group 與視訊輸入通道的綁定和解綁定、視訊編碼通道的建立和銷毀、注冊和反注冊到通道組、開啟和停止接收圖像、設定和擷取編碼通道屬性、擷取和釋放碼流、設定和擷取數字水印屬性、啟用和禁用數字水印、視訊編碼通道屬性的設定和查詢等功能。

1.建立/銷毀編碼通道組

hi_s32 hi_mpi_venc_creategroup(venc_grp vegroup); 

hi_s32 hi_mpi_venc_destroygroup(venc_grp vegroup); 

    a.本文檔中含有通道組号的接口的通道組号的取值範圍為[0, venc_max_grp_num),否則傳回 hi_err_venc_invalid_chnid。

    b.編碼通道組是指晶片能夠同時處理的編碼通道的集合,一個通道組最多可同時包含 1 路主碼流(h.264/mjpeg)和一路次碼流(h.264/mjpeg) ,或者包含 1 路jpeg抓拍,或者僅包含 1 路 mpeg4通道。

   c.如果指定的通道組已經存在,則傳回錯誤碼 hi_err_venc_exist。 

   d.銷毀通道組時,必須保證通道組為空,即沒有任何通道在通道組中注冊,否則會傳回錯誤碼 hi_err_venc_not_perm。

   e.銷毀并不存在的通道組,傳回錯誤碼 hi_err_venc_unexist。

2.綁定/解綁定 vi 到通道組

hi_s32 hi_mpi_venc_bindinput(venc_grp vegroup, vi_dev videvid, vi_chn vichn);

hi_s32 hi_mpi_venc_unbindinput(venc_grp vegroup);

   a.綁定并不存在的通道組,則傳回錯誤碼 hi_err_venc_unexist。

   b.如果 vi 裝置或者 vi 通道超出範圍,則傳回 hi_err_venc_invalid_devid或者 hi_err_venc_invalid_chnid。 

   c.此接口并不判斷 vi 的狀态,videvid 和 vichn 可以對應實際的 vi 裝置,也可以對應虛拟的 vi 裝置,對應虛拟的 vi 裝置主要用于使用者手動發送圖像編碼, hi_mpi_venc_sendframe 會對此作   出詳細的說明。

   d.如果通道組已經綁定了某個 vi 通道,則傳回錯誤碼hi_err_venc_not_perm。 

   e.一個通道組隻能綁定一個 vi 通道,但一個 vi 通道可以被多個通道組綁定。

   f.在編碼過程中,可以動态解綁定和綁定 vi,達到編碼不同圖像源的目的。

   g.解綁定并不存在的通道組,傳回錯誤碼 hi_err_venc_unexist。

   h.解綁定之後,vi 通道如果滿足條件,可以再綁定到其他任意通道組。

   i.可以重複解綁定,傳回 hi_success。

3.建立/銷毀編碼通道

hi_s32 hi_mpi_venc_createchn(venc_chn vechn, const venc_chn_attr_s *pstattr, const venc_wm_attr_s *pstwm);

hi_s32 hi_mpi_venc_destroychn(venc_chn vechn); 

   a.hi3520 支援對主次碼流進行編碼。在建立編碼通道的時候必須指定該通道是主碼流還是次碼流。 

   b.在建立編碼通道的時候,編碼通道屬性除需要輸入各個協定的特有的編碼屬性之外,一般還需要輸入主次碼流(mpeg4 編碼協定無此屬性) 、編碼協定、編碼的幀場模式、輸入圖像的幀場模式、擷取碼流的方式(按幀還是按包擷取碼流)、編碼圖像大小屬性,這些屬性受表 6-1限制,并且這些屬性都為靜态屬性,不允許動态設定。 

   c.若輸入圖像大于大碼流的寬高,但相差 16 像素以内(含 16像素) ,則大碼流編碼圖像通過輸入圖像做切邊得到。 

   d.若輸入圖像小于大碼流的寬高,會丢棄這些圖像,而不會對其放大進行編碼。該出錯資訊會在 log 中顯示。 

   e.推薦的大碼流編碼寬高為:2048×1536(3m 像素) 、1280×1024(1.3m 像素) 、1920×1080(1080p) 、1280×720(720p)、704×576、704×480、352×288、352×240。 

   f.對于 h.264主碼流,編碼圖像大小不為 d1 時,其編碼方式推薦使用幀編碼。

   g.當參數 pstwm為空時,表示該編碼通道不需要使用水印,否則認為需要使用數字水印。如果建立成功,數字水印預設使能。目前隻有 h.264 編碼的大碼流可以設定數字水印,其他的情況設定數字水印時均傳回錯誤碼hi_err_venc_not_support。

   h.銷毀并不存在的通道,傳回錯誤碼 hi_err_venc_unexist。

   i.銷毀前必須保證通道已經從通道組反注冊,否則傳回錯誤碼hi_err_venc_not_perm。 

4.注冊/反注冊編碼通道到通道組

hi_s32 hi_mpi_venc_registerchn(venc_grp vegroup,venc_chn vechn); 

hi_s32 hi_mpi_venc_unregisterchn(venc_chn vechn); 

   a.注冊并不存在的通道,傳回錯誤碼 hi_err_venc_unexist。

   b.注冊通道到不存在的通道組,傳回錯誤碼 hi_err_venc_unexist。 

   c.同一個編碼通道隻能注冊到一個通道組,如果該通道已經注冊到某個通道組,則傳回 hi_err_venc_not_perm。 

   d.主次碼流注冊的時候需要判定以下限制關系: 

      −  主碼流要先于次碼流注冊,否則傳回 hi_err_venc_not_perm。 

      −  如果編碼通道已經注冊,則在反注冊前不能再進行注冊,否則傳回hi_err_venc_not_perm。

   e.md通道注冊必須在編碼通道注冊成功之後進行,否則傳回hi_err_venc_not_perm。 

   f.同組的主次碼流若為 1:1的關系,則編碼方式必須同為幀編碼,否則傳回hi_err_venc_not_perm。 

   g.同組的主次碼流寬高必須符合如下限制:d/s – d = r(主次碼流寬或高分别為 d和 d,s 為1、2 或者4,r 為0 到 16)。 

   h.如果通道未注冊,則傳回錯誤碼 hi_err_venc_not_perm。

   i.如果編碼通道未停止接收圖像編碼(hi_mpi_venc_stoprecvpic 可停止接收) ,則傳回錯誤碼 hi_err_venc_not_perm。 

   j.反注冊後會将編碼通道複位,如果使用者還在使用未及時釋放的碼流 buffer,将不能保證此 buffer 資料的正确性。使用者可以使用 hi_mpi_venc_query接口來查詢狀态,确認自己所有的操作都完成之後再反注冊通道。

5.開啟/停止編碼通道接收輸入圖像

hi_s32 hi_mpi_venc_startrecvpic(venc_chn vechn); 

hi_s32 hi_mpi_venc_stoprecvpic(venc_chn vechn); 

   a.如果通道未建立,則傳回 hi_err_venc_unexist。 

   b.如果通道沒有注冊到通道組,則傳回 hi_err_venc_not_perm。

   c.此接口不判斷目前是否已經開啟接收,直接将狀态設定為開啟接收。 

   d.此接口用于開啟編碼通道接收圖像來編碼,請注意它和綁定通道組的差別。

   e.開始接收輸入是針對通道的,隻有開啟接收之後編碼器才開始接收圖像編碼。

   f.此接口并不判斷目前是否停止接收,直接将狀态設定為停止接收。 

   g.此接口用于編碼通道停止接收圖像來編碼,在編碼通道反注冊前必須停止接收圖像。 

   h.調用此接口僅停止接收原始資料編碼,碼流 buffer并不會被清除。

6.擷取編碼通道對應的裝置檔案句柄

hi_s32 hi_mpi_venc_getfd(venc_chn vechn);

         a. 使用者可以擷取檔案句柄實作多通道 select 擷取視訊幀資料

7.查詢編碼通道狀态

hi_s32 hi_mpi_venc_query(venc_chn vechn, venc_chn_stat_s *pststat);

   b.此接口用于查詢此函數調用時刻的編碼器狀态,pststat 包含三個主要的資訊: 

      −  在編碼通道狀态結構體中,u32leftpics表示待編碼的幀個數。 

    在反注冊通道前,可以通過查詢是否還有圖像待編碼來決定反注冊時機,防反注冊時将可能需要編碼的幀清理出去。 

      −  在編碼通道狀态結構體中,u32leftstreambytes表示碼流 buffer 中剩餘的 by數目。 

    在反注冊通道前,可以通過查詢是否還有碼流沒有被處理來決定反注冊時機防止反注冊時将可能需要的碼流清理出去。 

      −  在編碼通道狀态結構體中,u32curpacks 表示目前幀的碼流包個數。 

    在按包擷取時目前幀可能不是一個完整幀(被取走一部分) ,按幀擷取時表示目前一個完整幀的包個數(如果沒有一幀資料則為 0)。使用者在需要按幀擷取碼流時,需要查詢一個完整幀的包個數,在這種情況下,通常可以在 select 成功之後執行 query操作,此時 u32curpacks是目前完整幀中包的個數。

8.擷取/釋放編碼的碼流

hi_s32 hi_mpi_venc_getstream(venc_chn vechn, venc_stream_s *pststream, hi_u32 u32blockflag);

hi_s32 hi_mpi_venc_releasestream(venc_chn vechn, venc_stream_s *pststream); 

   a.如果通道未建立,傳回錯誤碼 hi_err_venc_unexist。

   b.如果 pststream為空,傳回錯誤碼 hi_err_venc_null_ptr。

   c.支援阻塞或非阻塞兩種方式擷取。同時可支援 select/poll 系統調用,u32blockflag:hi_io_block(阻塞)  hi_io_noblock(非阻塞)。

      −  非阻塞擷取時,如果緩沖無資料,則傳回 hi_err_venc_buf_empty。 

      −  阻塞時,如果緩沖無資料,則會等待有資料時才傳回 hi_success。

   d.支援按包或按幀方式擷取碼流。如果按包擷取,則: 

      −  對于 h.264編碼協定,每次擷取的是一個 nal 單元。 

      −  對于 jpeg編碼協定(包括 jpeg抓拍和 mjpeg) ,每次擷取的是一個 ecs或圖像參數位流包。 

      −  對于 mpeg4編碼協定,每次擷取的是一幀一個包,是以按幀擷取或者按包擷取,結果相同。

   e.碼流結構體 venc_stream_s包含3 個部分: 

      −  碼流包資訊指針 

          pstpack 指向一組 venc_pack_s 的記憶體空間,該空間由調用者配置設定。如果是按包擷取,則此空間不小于 sizeof(venc_pack_s)的大小;如果按幀擷取,則此空間不小于 n × sizeof(venc_pack_s)的大小,其中 n代表目前幀之中的包的個數,可以在 select之後通過查詢接口獲得。 

      −  碼流包個數 u32packcount  

          在輸入時,此值指定 pstpack 中 venc_pack_s 的個數。按包擷取時,u32packcount 必須不小于1;按幀擷取時,包個數。在函數調用成功後,u32packcount 傳回實際填充 pstpack 的包的個數。 

      −  序列号 u32seq 

          按幀擷取時是幀序列号;按包擷取時為包序列号。 

   f.如果使用者長時間不擷取碼流,那麼碼流緩沖區就會滿。一個編碼通道如果發生碼流緩沖區滿,就會停止該編碼通道編碼,等有足夠的碼流緩沖可以用來編碼時,才開始繼續編碼,這種情況對于主次碼流編碼通道來說,互相不受影響。

   g.使用者應該及時擷取碼流,防止由于碼流 buffer阻塞導緻編碼器停止工作。 

   h.如果通道未建立,則傳回錯誤碼 hi_err_venc_unexist。 

   i.如果 pststream為空,則傳回錯誤碼 hi_err_venc_null_ptr。 

   j.此接口應當和 hi_mpi_venc_getstream配對起來使用,使用者擷取碼流後必須及時釋放已經擷取的碼流緩存,否則可能會導緻碼流 buffer滿,影響編碼器編碼,并且使用者必須按先擷取先釋放的順序釋放已經擷取的碼流緩存。 

   k.在編碼通道反注冊以後,所有未釋放的碼流包均無效,不能再使用或者釋放這部分無效的碼流緩存。

   l.釋放無效的碼流會傳回 hi_err_venc_illegal_param。 

附:編碼過程為1-2-3-4-5-6-7-8

嵌入式 視訊編碼(H264)

繼續閱讀