FFmpeg,如何插入SEI自定義數據
一、什么是SEI?
SEI(Supplemental Enhancement Information,補充增強信息)是H.264/H.265視頻編碼標準中的一種元數據載體,它允許在視頻流中嵌入額外的信息,如時間戳、人臉框、設備信息等增強信息。
與視頻幀數據不同,SEI信息不是解碼必需的,但可以用于增強播放體驗或傳遞輔助信息。
二、AVPacket中插入SEI
在ffmpeg中,H264裸流的數據,一個 AVPacket 可能包含多個 NALU,它們之間通過起始碼 0x00 00 00 01 或 0x00 00 01 分隔。
一個AVPacket中的關鍵幀數據包含SPS、PPS、IDR,而SEI的插入位置在PPS之后,IDR之前,如下圖所示。
三、如何構建一個SEI NALU
1、SEI NALU格式
SEI NALU由起始碼(0x00 00 00 01)、nal head(0x06)、payload type(0x05)、UVLC編碼字節數、rbsp_data組成。其中rbsp_data由uuid(16字節)、payload、結束標記(0x80)組成,具體格式如下圖所示。
說明 | 示例值或長度 | |
Start Code | 起始碼(Annex-B 格式) | 0x00000001 |
NAL Header | NAL 類型為 SEI | 0x06 |
SEI Payload Type | 固定為 5,表示未注冊用戶數據 | 0x05 |
SEI Payload Size | 整個 payload 長度(字節) | 如 0x2F(47) |
UUID | 16 字節,唯一標識數據類型 | 如 dc45e9bd... |
Payload Content | 自定義數據內容(如字符串) | 如 "hello" |
RBSP Trailing Bits | 固定結尾對齊字節 | 0x80 |
2、UVLC編碼字節數
UVLC(Unsigned Variable Length Coding)是H.264/H.265標準中用于編碼SEI的payload_size和payload_type的壓縮算法,其核心特點是:
- 無符號整數編碼:僅處理非負整數
- 前綴碼結構:通過0xFF標記實現變長
- 自描述性:解碼器無需預知長度即可解析
若值 < 0xFF:用1字節直接表示
? ? value = 100 → 0x64
若值 ≥ 0xFF:
- 第1字節固定為0xFF
- 剩余值遞歸編碼(value -= 255)
value = 300 → 0xFF 0x2D (300 = 255 + 45)
value = 550 → 0xFF 0xFF 0x28 (550 = 255 + 255 + 40)
四、參考信息
1、NALU類型
NALU 的類型,共 32 種(0-31),常見類型如下表:
NALU 類型 | 說明 | |
0 | 未指定 | 保留,不使用 |
1 | 非 IDR 圖像的片(Slice) | P 幀或 B 幀的 Slice 數據 |
2 | 數據分區 A | 用于分片編碼,存放重要的運動信息 |
3 | 數據分區 B | 存放次要的運動信息 |
4 | 數據分區 C | 存放殘差數據 |
5 | IDR 圖像的片(Slice) | 立即刷新圖像(關鍵幀)的 Slice 數據,解碼時需清空參考幀緩沖區 |
6 | SEI(補充增強信息) | 包含額外信息(如時間戳、用戶數據),不影響基本解碼 |
7 | SPS(序列參數集) | 包含視頻序列的全局參數(如分辨率、profile 等) |
8 | PPS(圖像參數集) | 包含圖像級參數(如量化參數、熵編碼方式) |
9 | 訪問單元分隔符 | 標記視頻幀的開始 |
10 | 序列結束符 | 標記視頻序列的結束 |
11 | 流結束符 | 標記整個碼流的結束 |
12 | 填充數據 | 用于增加碼流長度(如測試場景) |
13-23 | 保留 | 用于 H.264 的擴展功能 |
24-31 | 未指定 | 通常用于 RTP 等網絡協議的封裝 |
2. RBSP(Raw Byte Sequence Payload)
RBSP 是 NALU 的負載數據,包含 VCL 層的壓縮信息(如 Slice 數據、參數集內容)。它由SODB(String of Data Bits) 經過處理后得到:
SODB:VCL 層輸出的原始比特流(如預測殘差、運動矢量等);
RBSP:在 SODB 末尾添加停止位(1 個 "1" 比特后跟若干 "0" 比特),使其字節對齊,形成 RBSP。
3、關鍵 NALU 類型詳解
3.1 SPS(序列參數集,nal_unit_type=7)
SPS 是視頻序列的全局配置,包含影響整個序列的參數,解析時需優先處理。常見參數如下:
// SPS參數示例(部分關鍵參數)
profile_idc???????????????? // 編碼profile(如Baseline=66,Main=77,High=100)
level_idc?????????????????? // 編碼level(如3.0=30,3.1=31)
seq_parameter_set_id??????? // SPS的ID(用于關聯PPS)
chroma_format_idc?????????? // 色度格式(如1=4:2:0,2=4:2:2,3=4:4:4)
bit_depth_luma_minus8?????? // 亮度位深度(通常為8)
bit_depth_chroma_minus8???? // 色度位深度(通常為8)
log2_max_frame_num_minus4?? // 最大幀號的對數(用于計算幀號范圍)
pic_order_cnt_type????????? // 圖像順序計數類型(0-2,控制POC的計算方式)
max_num_ref_frames????????? // 最大參考幀數量
pic_width_in_mbs_minus1???? // 視頻寬度(以宏塊為單位,實際寬度=(值+1)*16)
pic_height_in_map_units_minus1 // 視頻高度(以宏塊為單位)
frame_mbs_only_flag???????? // 是否僅幀編碼(0=支持幀/場混合,1=僅幀)
3.2 PPS(圖像參數集,nal_unit_type=8)
PPS 定義單幅圖像的參數,依賴于 SPS,常見參數如下:
// PPS參數示例(部分關鍵參數)
pic_parameter_set_id??????? // PPS的ID
seq_parameter_set_id??????? // 關聯的SPS的ID
entropy_coding_mode_flag??? // 熵編碼方式(0=CAVLC,1=CABAC)
num_ref_idx_l0_default_active_minus1 // 默認的前向參考幀列表長度
num_ref_idx_l1_default_active_minus1 // 默認的后向參考幀列表長度
weighted_pred_flag????????? // 是否使用加權預測(對P幀)
weighted_bipred_idc???????? // 雙向預測加權模式(0-2)
pic_init_qp_minus26???????? // 初始量化參數(QP=值+26)
deblocking_filter_control_present_flag // 是否存在去塊濾波控制信息
3.3. IDR Slice(即時解碼刷新,nal_unit_type=5)
IDR Slice 是一種特殊的 I Slice,屬于關鍵幀:
解碼 IDR Slice 時,解碼器會清空所有參考幀緩沖區,確保后續幀的解碼不依賴之前的錯誤幀,從而終止錯誤傳播。
IDR Slice 必須包含完整的幀內預測信息,不依賴其他幀。
3.4. 非 IDR Slice(nal_unit_type=1)
包括 P Slice 和 B Slice:
P Slice:依賴前向參考幀(已解碼的 I/P 幀)進行預測;
B Slice:依賴雙向參考幀(前向和后向的 I/P 幀)進行預測,壓縮效率更高。
3.5. SEI(補充增強信息,nal_unit_type=6)
攜帶與解碼無關的輔助信息,常見類型:
時間戳信息(如 NTP 時間);
用戶數據(如字幕、水印);
場景切換標記;
碼流統計信息。
具體實現方式參見:
https://gitee.com/hanshuang741852/mem-push-streamhttps://gitee.com/hanshuang741852/mem-push-stream