音視頻學習(六十二):H264中的SEI

什么是SEI?

在 H.264 視頻編碼標準中,補充增強信息(Supplemental Enhancement Information,SEI) 是一種特殊的 NAL(網絡抽象層)單元。它不像序列參數集(SPS)或圖像參數集(PPS)那樣直接影響解碼過程,而是提供輔助性、非強制性的信息

SEI 可以視為視頻數據中的“便簽”或“元數據”,這些信息可以被解碼器或應用程序用來增強視頻體驗,但即使它們丟失了,解碼器仍然可以正常解碼和播放視頻。SEI 單元的引入,讓 H.264 碼流在傳輸視頻數據本身的同時,還能靈活地攜帶各種附加信息。

SEI的特點

SEI 的主要作用是為視頻流提供額外的信息:

  • 計時和同步: SEI 可以包含精確的計時信息,幫助解碼器同步音頻和視頻,確保播放流暢。例如,pic_timing SEI 消息可以提供每一幀的顯示時間。
  • 錯誤隱藏: 當視頻流在網絡傳輸中發生丟包時,SEI 消息可以提供錯誤隱藏所需的信息,幫助解碼器更好地處理損壞的圖像。例如,recovery_point SEI 消息可以指示一個可以安全恢復解碼的同步點。
  • 顯示與渲染: SEI 消息可以提供關于如何正確顯示視頻的信息。例如,它可以包含色彩空間、亮度范圍等元數據,確保視頻在不同設備上都能有正確的顯示效果。
  • 用戶自定義數據: SEI 單元可以攜帶用戶自定義的私有數據,這為開發者在視頻流中嵌入自己的信息提供了便利。例如,它可以用來攜帶版權信息、相機設置或者其他任何應用程序所需的數據。

SEI結構體

// H.264 SEI 消息結構
typedef struct H264SEI {int payloadType;     // SEI 類型 (0,1,5等)int payloadSize;     // SEI 數據大小(字節數)union {// (payloadType == 0) HRD 緩沖控制struct {int cpb_removal_delay;   // CPB 移除延時int dpb_output_delay;    // DPB 輸出延時} buffering_period;// (payloadType == 1) 圖像時間戳struct {int cpb_removal_delay;   // CPB 移除延時int dpb_output_delay;    // DPB 輸出延時int pic_struct;          // 圖像結構 (0=幀,1=頂場,2=底場,3=頂+底場, etc.)int clock_timestamp_flag;// 是否有時鐘時間戳int ct_type;             // 時鐘類型int nuit_field_based_flag;// 時間戳是否基于場int counting_type;       // 計數類型int full_timestamp_flag; // 是否輸出完整時間戳int seconds, minutes, hours; // 時間信息} pic_timing;// (payloadType == 5) 用戶自定義數據struct {uint8_t uuid[16];        // 唯一標識符 (UUID)std::vector<uint8_t> user_data; // 自定義數據} user_data_unregistered;// (payloadType == 6) 錯誤恢復點struct {int recovery_frame_cnt;  // 距離下一個可解碼幀的間隔} recovery_point;// 其他類型可繼續擴展...};
} H264SEI;

payloadType

payloadType名稱說明
0buffering_periodHRD 緩沖控制參數
1pic_timing圖像時間戳信息(CPB/DPB/顯示時間戳)
2pan_scan_rect平移掃描窗口
3filler_payload填充比特流(碼率控制)
4user_data_registered_itu_t_t35用戶數據(ITU-T T.35 標準注冊)
5user_data_unregistered用戶自定義數據(常用于水印、UUID 信息)
6recovery_point錯誤恢復點
9scene_info場景切換信息
45frame_packing_arrangement3D 視頻左右眼幀排列

參數說明

  • payloadType / payloadSize

    • payloadType:SEI 類型,決定后續解析方式。

    • payloadSize:SEI 消息的字節長度。

  • Buffering period(payloadType = 0)

    • cpb_removal_delay:表示解碼時間戳(DTS)相對于 CPB 的移除延時。

    • dpb_output_delay:表示顯示時間戳(PTS)相對于解碼的延時。

  • Picture timing(payloadType = 1)

    • pic_struct:指示圖像顯示方式

      • 0 = 幀
      • 1 = 頂場
      • 2 = 底場
      • 3 = 頂場+底場(逐行交錯)
    • clock_timestamp_flag:是否包含時鐘時間戳。

    • seconds/minutes/hours:可選的顯示時間信息。

  • User data unregistered(payloadType = 5)

    • uuid:16 字節的唯一標識符,區分不同廠商/應用。

    • user_data:實際攜帶的數據(比如字幕、元數據、OSD 等)。

  • Recovery point(payloadType = 6)

    • recovery_frame_cnt:表示從當前幀起,多少幀之后可恢復到無誤碼解碼。

示例(c++)

#include <cstdint>
#include <vector>
#include <string>
#include <cstring>
#include <iostream>// ========================
// H.264 SEI 結構體定義
// ========================
struct H264SEI {int payloadType;   // SEI 類型int payloadSize;   // 數據大小// 不同類型的 SEI 數據struct BufferingPeriod {int cpb_removal_delay;int dpb_output_delay;};struct PicTiming {int cpb_removal_delay;int dpb_output_delay;int pic_struct;bool clock_timestamp_flag;int ct_type;int nuit_field_based_flag;int counting_type;bool full_timestamp_flag;int seconds, minutes, hours;};struct UserDataUnregistered {uint8_t uuid[16];std::vector<uint8_t> user_data;};struct RecoveryPoint {int recovery_frame_cnt;};// union 方式存儲不同 payloadBufferingPeriod buffering_period;PicTiming pic_timing;UserDataUnregistered user_data_unregistered;RecoveryPoint recovery_point;
};// ========================
// 工具類:BitReader
// ========================
class BitReader {
public:BitReader(const uint8_t* data, int size) : data_(data), size_(size), bit_pos_(0) {}uint32_t read_bits(int n) {uint32_t val = 0;for (int i = 0; i < n; i++) {val <<= 1;val |= read_bit();}return val;}uint32_t read_bit() {if (bit_pos_ >= size_ * 8) return 0;uint32_t val = (data_[bit_pos_ / 8] >> (7 - (bit_pos_ % 8))) & 0x01;bit_pos_++;return val;}uint32_t read_ue() { // 無符號 Exp-Golombint zeros = 0;while (read_bit() == 0 && bit_pos_ < size_ * 8) zeros++;uint32_t value = (1 << zeros) - 1 + read_bits(zeros);return value;}int32_t read_se() { // 有符號 Exp-Golombuint32_t ueVal = read_ue();int32_t val = (ueVal & 1) ? (int32_t)((ueVal + 1) >> 1) : -(int32_t)(ueVal >> 1);return val;}private:const uint8_t* data_;int size_;int bit_pos_;
};// ========================
// H.264 SEI 解析器
// ========================
class H264SEIParser {
public:static std::vector<H264SEI> parse(const uint8_t* data, int size) {std::vector<H264SEI> seis;int offset = 0;while (offset < size) {// payloadTypeint payloadType = 0;while (offset < size && data[offset] == 0xFF) {payloadType += 255;offset++;}if (offset < size) payloadType += data[offset++];// payloadSizeint payloadSize = 0;while (offset < size && data[offset] == 0xFF) {payloadSize += 255;offset++;}if (offset < size) payloadSize += data[offset++];if (offset + payloadSize > size) break;H264SEI sei;sei.payloadType = payloadType;sei.payloadSize = payloadSize;const uint8_t* payload = data + offset;switch (payloadType) {case 0: // buffering_periodparse_buffering_period(payload, payloadSize, sei);break;case 1: // pic_timingparse_pic_timing(payload, payloadSize, sei);break;case 5: // user_data_unregisteredparse_user_data_unregistered(payload, payloadSize, sei);break;case 6: // recovery_pointparse_recovery_point(payload, payloadSize, sei);break;default:// 未實現的 SEI 類型break;}seis.push_back(sei);offset += payloadSize;}return seis;}private:static void parse_buffering_period(const uint8_t* payload, int size, H264SEI& sei) {BitReader br(payload, size);sei.buffering_period.cpb_removal_delay = br.read_ue();sei.buffering_period.dpb_output_delay = br.read_ue();}static void parse_pic_timing(const uint8_t* payload, int size, H264SEI& sei) {BitReader br(payload, size);sei.pic_timing.cpb_removal_delay = br.read_ue();sei.pic_timing.dpb_output_delay = br.read_ue();sei.pic_timing.pic_struct = br.read_bits(4);sei.pic_timing.clock_timestamp_flag = br.read_bit();if (sei.pic_timing.clock_timestamp_flag) {sei.pic_timing.ct_type = br.read_bits(2);sei.pic_timing.nuit_field_based_flag = br.read_bit();sei.pic_timing.counting_type = br.read_bits(5);sei.pic_timing.full_timestamp_flag = br.read_bit();sei.pic_timing.seconds = br.read_bits(6);sei.pic_timing.minutes = br.read_bits(6);sei.pic_timing.hours   = br.read_bits(5);}}static void parse_user_data_unregistered(const uint8_t* payload, int size, H264SEI& sei) {if (size < 16) return;memcpy(sei.user_data_unregistered.uuid, payload, 16);sei.user_data_unregistered.user_data.assign(payload + 16, payload + size);}static void parse_recovery_point(const uint8_t* payload, int size, H264SEI& sei) {BitReader br(payload, size);sei.recovery_point.recovery_frame_cnt = br.read_ue();}
};// ========================
// 示例 main()
// ========================
int main() {// 模擬一個 SEI NALU(例子:user_data_unregistered)uint8_t sei_nalu[] = {0x05, 0x20, // payloadType=5, payloadSize=32// UUID (16 bytes)0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF,0x00,// User Data (16 bytes)'T','E','S','T','_','S','E','I','_','D','A','T','A','!','!','!'};auto seis = H264SEIParser::parse(sei_nalu, sizeof(sei_nalu));for (auto& sei : seis) {std::cout << "SEI Type: " << sei.payloadType<< " Size: " << sei.payloadSize << std::endl;if (sei.payloadType == 5) {std::cout << "UUID: ";for (int i = 0; i < 16; i++) printf("%02X", sei.user_data_unregistered.uuid[i]);std::cout << std::endl;std::string str(sei.user_data_unregistered.user_data.begin(),sei.user_data_unregistered.user_data.end());std::cout << "User Data: " << str << std::endl;}}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920852.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920852.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920852.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

docker run 后報錯/bin/bash: /bin/bash: cannot execute binary file總結

以下方法來源于AI&#xff0c;個人僅驗證了第三條便成功執行 1. 鏡像與宿主機架構不匹配 比如&#xff1a; 你是 x86_64 的機器&#xff0c;但鏡像是 ARM64 的&#xff08;或反之&#xff09;。在 PC 上拉了樹莓派用的鏡像。查看鏡像架構 docker inspect <image_name> | …

【Redisson 加鎖源碼解析】

Redisson 源碼解析 —— 分布式鎖實現過程 在分布式系統中&#xff0c;分布式鎖 是非常常見的需求&#xff0c;用來保證多個節點之間的互斥操作。Redisson 是 Redis 的一個 Java 客戶端&#xff0c;它提供了對分布式鎖的良好封裝。本文將從源碼角度剖析 Redisson 的分布式鎖實現…

uni-app支持單多選、搜索、查詢、限制能否點擊組件

<template><view class="multi-select-container" :class="{ single-select: !multiple, no-search: !searchable }"><!-- 當組件被禁用時,直接顯示選中的內容 --><view class="disabled-display" v-if="disabled &a…

TFT屏幕:STM32硬件SPI+DMA+隊列自動傳輸

看了網上的很多的SPIDMA的代碼&#xff0c;感覺都有一些缺陷&#xff0c;就是基本都是需要有手動等待DMA完成的這個操作&#xff0c;我感覺這種等待操作在很大程度上浪費了時間&#xff0c;那么我加入的“隊列”就是一種將等待時間利用起來的方法。原本的SPIDMA的操作邏輯如下圖…

AI操作系統語言模型設計 之1 基于意識的Face-Gate-Window的共軛路徑的思維-認知-情感嵌套模型

摘要&#xff08;AI生成&#xff09;本文提出了一種創新的AI操作系統語言模型設計框架&#xff0c;將人類意識活動的分層結構映射到人工智能系統中。該模型包含三個嵌套層次&#xff1a;理性思維層&#xff08;Face層&#xff09;&#xff1a;采用雙面膠隱喻&#xff08;A/B面&…

瘋狂星期四文案網第57天運營日記

網站運營第57天&#xff0c;點擊觀站&#xff1a; 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 今日訪問量 今日搜索引擎收錄情況

SQLark:一款面向信創應用開發者的數據庫開發和管理工具

SQLark 是一款面向信創應用開發者的數據庫開發和管理工具&#xff0c;用于快速查詢、創建和管理不同類型的數據庫系統&#xff0c;現已支持達夢、Oracle、MySQL、PostgreSQL 數據庫。 SQLark 提供了對多種數據庫的連接支持&#xff0c;實現跨平臺數據庫管理的無縫切換&#xff…

BigDecimal——解決Java浮點數值精度問題:快速入門與使用

在Java開發中&#xff0c;涉及金額計算、科學計數或需要高精度數值處理時&#xff0c;你是否遇到過這樣的困惑&#xff1f;用double計算0.1加0.2&#xff0c;結果竟不是0.3&#xff1b;用float存儲商品價格&#xff0c;小數點后兩位莫名多出幾位亂碼&#xff1b;甚至在金融系統…

wpf之WrapPanel

前言 WrapPanel類似winform中的FlowLayoutPanel&#xff0c;采用流式布局。 1、Orientation 該屬性指定WrapPanel中子空間布局的方向&#xff0c;有水平和垂直方向兩種 1&#xff09;Horizontal 水平方向 子元素Button按照水平方向排列&#xff0c;如果一行排滿了自動換下一…

Woody:開源Java應用性能診斷分析工具

核心價值 Woody是一款專注于Java應用性能問題診斷的工具&#xff0c;旨在幫助開發者 定位高GC頻率問題&#xff0c;識別內存分配熱點分析CPU使用率過高的代碼路徑追蹤接口耗時瓶頸&#xff0c;定位內部操作耗時占比診斷鎖競爭問題&#xff0c;支持精準優化針對特定業務接口/請…

《山東棒球》板球比賽規則·棒球1號位

? Baseball vs Cricket 終極科普&#xff5c;規則異同發展史全解&#xff01;Hey sports babes&#xff01;別再傻傻分不清棒球?和板球&#xff01;全網最清晰雙運動對照指南來啦&#xff5e;? 棒球 Baseball&#xff5c;美式激情風暴Core Goal核心目標擊球員&#xff08;Ba…

【游戲開發】Houdini相較于Blender在游戲開發上有什么優劣勢?我該怎么選擇開發工具?

在游戲開發中&#xff0c;Houdini與Blender的選擇需結合項目規模、技術需求和團隊資源綜合考量。以下是兩者的核心優劣勢對比及決策建議&#xff1a; 一、核心優劣勢對比 Houdini的優勢與局限 優勢&#xff1a;程序化內容生成的統治力 Houdini的節點系統&#xff08;如VEX語言、…

基于開源AI智能名片鏈動2+1模式S2B2C商城小程序的用戶活躍度提升與價值挖掘策略研究

摘要&#xff1a;本文聚焦于在開源AI智能名片鏈動21模式S2B2C商城小程序環境下&#xff0c;探討如何提高用戶活躍度并挖掘用戶價值。在用戶留存的基礎上&#xff0c;通過分析該特定模式與小程序的特點&#xff0c;提出一系列針對性的策略&#xff0c;旨在借助開源AI智能名片以及…

《投資-41》- 自然=》生物=》人類社會=》商業=》金融=》股市=》投資,其層層疊加構建中內在的相似的規律和規則

從自然到投資的層層遞進中&#xff0c;盡管各領域看似差異巨大&#xff0c;但內在遵循著相似的規律和規則。這些規律體現了“底層邏輯的普適性”&#xff0c;即不同系統在動態平衡、資源分配、信息傳遞和反饋調節等方面具有共性。以下是關鍵規律的解析&#xff1a;1. 能量流動與…

VSCode中調試python腳本

VSCode中安裝以下插件 ms-python.python&#xff1a;python調試ms-python.vscode-pylance&#xff1a;代碼跳轉&#xff08;非必要&#xff09; 配置launch.json 在當前工作區&#xff0c;按此路徑.vscode\launch.json新建launch.json文件&#xff0c;并配置以下參數&#x…

動作指令活體檢測通過動態交互驗證真實活人,保障安全

在當今社會&#xff0c;人臉識別技術已深入日常生活的方方面面&#xff0c;從手機解鎖、移動支付到遠程開戶、門禁考勤&#xff0c;人臉識別技術已無處不在。然而&#xff0c;這項技術也面臨著嚴峻的安全挑戰&#xff1a;打印照片、播放視頻、制作3D面具等簡單的“欺騙手段”都…

KingbaseES數據庫:開發基礎教程,從部署到安全的全方位實踐

KingbaseES數據庫&#xff1a;開發基礎教程&#xff0c;從部署到安全的全方位實踐 KingbaseES數據庫&#xff1a;開發基礎教程&#xff0c;從部署到安全的全方位實踐&#xff0c;本文圍繞 KingbaseES 數據庫開發核心基礎展開。先介紹三種部署模式&#xff0c;即單機、雙機熱備、…

安裝nodejs安裝node.js安裝教程(Windows Linux)

文章目錄Linux**一、下載 Node.js**1. **訪問官網**&#xff1a;2. **選擇版本**&#xff1a;**二、安裝 Node.js****方法 1&#xff1a;使用包管理器&#xff08;推薦&#xff09;****Ubuntu/Debian 系統**1. **更新包列表**&#xff1a;2. **安裝 Node.js**&#xff1a;3. **…

shell腳本函數介紹

1. 函數 (Functions)定義與優勢函數是可重復使用的功能模塊優勢&#xff1a;代碼復用&#xff0c;直接調用解決問題分類內置函數&#xff1a;編程語言自帶的函數&#xff08;如 print&#xff09;自定義函數&#xff1a;程序員自己編寫的函數定義語法# 方式一 function 函數名(…

DAY 20 奇異值SVD分解-2025.9.1

奇異值SVD分解 知識點回顧&#xff1a; 線性代數概念回顧奇異值推導奇異值的應用 a. 特征降維&#xff1a;對高維數據減小計算量、可視化 b. 數據重構&#xff1a;比如重構信號、重構圖像&#xff08;可以實現有損壓縮&#xff0c;k 越小壓縮率越高&#xff0c;但圖像質量損失…