這幾天在編寫視頻錄制模塊,所以,閑暇之余,又粗粗的整理了一下,主要是API,以備不時之用
攝像頭獲取的模擬信號通過經芯片處理(我們使用的是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 模式下有效。 取值范圍:[10, 50]。
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);
A.如果通道未創建,則返回 HI_ERR_VENC_UNEXIST。
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