什么是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;但图像质量损失…