SEI(Supplemental Enhancement Information)

2024-01-15 20:32

本文主要是介绍SEI(Supplemental Enhancement Information),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

流媒体是采用流式传输方式在网络上播放的媒体格式,视频网站内容、短视频、在线直播这些视频形态,均属于流媒体的不同分支。流媒体大致包含三个层级:码流、封装和协议。从音视频编码器输出的码流,经过某种封装格式后,经过特定的协议传输、保存,构成了流媒体世界的基础功能。

在直播应用的开发过程中,如果把主播端消息事件传递到观众端,一般会以Instant Messaging(即时通讯)的方式传递过去,但因为消息分发通道和直播通道是分开的,因此消息与直播音视频数据的同步性就会出现很多问题。那么有没有在音视频内部传递消息的方法呢?答案是SEI

1. SEI的介绍

补充增强信息(Supplemental Enhancement Information)是码流范畴里面的概念,提供了向视频码流中加入信息的办法,是H.264/H.265 视频压缩标准的特性之一。SEI 有基本的特征:

  1. 并不是解码过程的必须项;
  2. 有可能对解码过程(容错、纠错)有帮助;
  3. 集成在视频码流中;

这意味着视频编码器在输出视频码流的时候,可以不提供SEI信息 。同时我们也要清楚:视频传输过程、解封装、解码环节,都可能因为某种原因丢弃SEI

在视频内容的生成端、传输过程中,都可以插入SEI 信息。插入的信息,和其他视频内容一起经过传输链路到达了消费端。那么在SEI 中可以添加哪些信息呢?这里举几个例子,用户场景可以任意扩展:

  1. 传递编码器参数;
  2. 传递视频版权信息;
  3. 传递摄像头参数;
  4. 传递内容生成过程中的剪辑事件(引发场景切换);

1.1 NAL unit类型

网络抽象层(Network Abstract Layer)简称为NAL。在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(Video Coding Layer - VCL)和网络抽象层面(Network Abstraction Layer - NAL)。VCL负责表示有效视频数据的内容,NAL 负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。NAL unitNAL的基本语法结构,它包含一个字节的头信息(NAL header)和一系列来自VCL的原始数据字节流(RBSP)。

1.1.1 H.264/AVC 中的情况

NAL unit type储存在NAL header中,在H.264/AVC标准中,可用的NAL unit type一共有17种,其中值为6时表征SEI 内容。比较常见的类型如下表所示:

NAL unit typeNAL unit content
1非IDR图像,且不采用数据划分的片段
5IDR图像
6补充增强信息(SEI)
7序列参数集(SPS)
8图像参数集(PPS)
11流结束符

《ISO/IEC 14496-10:2014》是MPEG专家组为AVC编解码器制定的标准,H.264/AVCNAL unit类型完整定义都在该标准的7-1表中,标准一共预留了32种类型,所以在NAL header里面,用5 bits表征NAL unit type

如下图所示,8 bits的NAL header里面:

  1. 第0位是禁止位0,值为1时表示语法出错;
  2. 第1~2位为参考级别(NRI,NAL ref idc);
  3. 第3~7为是NAL unit type;

nal_unit_type in H.264/AVC

NRI取值为 "00" (二进制)时,表征NAL unit不参与重建参考图像,这时的NAL unit是可以丢弃的。大于 "00"(二进制)时,NAL unit 不能被丢弃。

1.1.H.265/HEVC 中的情况

《ISO/IEC 23008-2:2015》是MPEG专家组为HEVC编解码器制定的标准,H.265/HEVCNAL unit类型完整定义都在该标准的7-1表中,可用的NAL unit type一共有40种之多,其中39和40都表征SEI内容。因为标准一共预留64种类型,所以在NAL header里面,用6 bits表征NAL unit type

如下图所示,16 bits的NAL header里面:

  1. 第0位是禁止位0,值为1时表示语法出错;
  2. 第1~6位是NAL unit type
  3. 第7~12位是NUH layer id
  4. 第13~15位是temporal_id

nal_unit_type in H.265/HEVC

1.2 SEI 类型

H.264/AVC视频编码标准中,并没有规定SEI payload type的范围,所以表征payload type的字节数是浮动的。
语法分析如下所示,当开始解析类型为SEINAL时,持续读取8bit,直到非0xff为止,然后把读取的数值累加,累加值即为SEI payload type

sei_message(){payloadType = 0while( next_bits(8) == 0xFF){ff_bytepayloadType += 255}last_payload_type_bytepayloadType += last_payload_type_byte
}

读取SEI payload sizepayload type逻辑类似,仍然是读取到0xff为止,这样可以支持任意长度的SEI payload添加。

sei_message(){payloadSize = 0while( next_bits(8) == 0xFF){ff_bytepayloadSize += 255}last_payload_size_bytepayloadSize += last_payload_size_byte
}

当获取了SEI payload类型和大小后,就进入了实际的SEI内容读取。
当前《ISO/IEC 14496-10:2014》Annex D.1.1提供了最大到181的payload类型处理规范,由于类型可以指定任意大小,给SEI的添加、处理创造了很大的自由空间。
其中SEI payload类型值为5时,指定的处理方法叫user_data_unregistered(),字面含义为未注册的用户数据,常用于存储编码器的编码参数信息,是比较常见的payload类型。

读取payload type为5时,具体的语法解析流程如下:

user_data_unregistered(payloadSize){uuid_iso_iec_11578for( i=16; i< payloadSize; i++)user_data_payload_byte
}

其中uuid_iso_iec_11578的详细定义在《
ISO/IEC 11578:1996》Annex A中,大致规定了使用128bits(16个字节)来指定UUID。此处UUID可以表征写入SEI payload的角色ID,或者表征其他业务用途。剩下的payloadSize -16字节,即是业务层传递的具体内容了。
通过user_data_unregistered()语法解析可以看出,当使用SEI payload type为5时,注意事项如下:

  1. payload size应该大于16;
  2. uuid可能出现0x000000/0x000001/0x000002,需要插入0x03做防竞争处理;

构成RBSP时,都需要做RBSP拖尾处理。拖尾处理对所有SODB方式都一致。rbsp_trailing_bits()语法逻辑如下:

rbsp_trailing_bits( ){rbsp_stop_one_bitwhile( !byte_aligned( ) )rbsp_alignment_zero_bit
}

1.3 SEI例子

从video.js的示例中下载oceans.mp4并提取出H.264码流如下:

bitstream from oceans.mp4

NAL header

起始码(暗红底色)"0x00000001"分割出来的比特流即是NAL unit,起始码紧跟的第一个字节(墨绿底色)是NAL header。上图“NAL header”一共出现了四个数值:

  • "0x06",此时NRI为"00B",NAL unit type为SEI类型。
  • “0x67”,此时NRI为“11B”,NAL unit type为SPS类型。
  • “0x68”,此时NRI为“11B”,NAL unit type为PPS类型。
  • “0x65”,此时NRI为“11B”,NAL unit type为IDR图像。

SEI payload type

"0x06"后一个字节为“0x05”(淡黄底色)是SEI payload type,即表征SEI payload分析遵循user_data_unregistered()语法。

SEI payload size

“0x05”后一个字节为“0x2F”(淡蓝底色)是SEI payload size,此时整个payload是47个字节。

SEI payload uuid

"0x2F"随后的16个字节即为uuid,此时uuid为

dc45e9bd-e6d9-48b7-962c-d820d923eeef

SEI payload content

由于payload size是47个字节,除去16字节的uuid,剩下31个字节的content。由于content是字符串,所以有结束符"0x00",有效的30个字符内容是:

Zencoder Video Encoding System

rbsp trailing bits

47个payload字节后的"0x80"(灰底色)即是rbsp trailing bits,在user_data_unregistered()里面都是按字节写入的,所以此时的NAL unit结尾写入的字节一定是0x80

2. SEI的生成

生成SEI的方式很多,大致可以有:

  1. 对已有码流做filter,插入SEI NAL
  2. 视频编码时生成SEI
  3. 容器层写入时插入SEI

以下代码示例来自于FFmpeg origin/master 分支。

2.1 bsf

BitStream Filter(码流过滤)的缩写即为bsf,在不做码流解码的前提下,对已经编码后的比特流做特定的修改、调整。

bsf h264_metadata的调用

The ff* tools have a -bsf option applied per stream, taking a comma-separated list of filters, whose parameters follow the filter name after a ’=’.
使用ffmpeg工具时,可以使用比特流过滤器。基本的filter调用格式如下:

ffmpeg -i INPUT -c:v copy -bsf:v filter1[=opt1=str1:opt2=str2][,filter2] OUTPUT

从上文提到的mp4文件中提取出h.264码流oceans.h264,可以使用* h264_metadata比特流过滤器添加SEI*。下面示例命令添加了类型为未注册的用户数据的SEI,其中uuid为"086f3693-b7b3-4f2c-9653-21492feee5b8",payload内容为"hello":

./ffmpeg  -I oceans.h264 -c:v copy -bsf:v h264_metadata=sei_user_data='086f3693-b7b3-4f2c-9653-21492feee5b8+hello' oceans.sei.h264

其中oceans.h264已经有一个SEI和28个SPS。输出的oceans.sei.h264码流中,共有28个SEI,其中第一个与输入保持一致,剩下27个为新插入的SEI

bsf h264_metadata的代码分析

具体代码位于:libavcodec/h264_metadata_bsf.c中。

// 函数int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out)
if (ctx->sei_user_data && (has_sps || !ctx->sei_first_au)) {H264RawSEI *sei;H264RawSEIPayload *payload;H264RawSEIUserDataUnregistered *udu;int sei_pos, sei_new;ctx->sei_first_au = 1;for (i = 0; i < au->nb_units; i++) {if (au->units[i].type == H264_NAL_SEI ||au->units[i].type == H264_NAL_SLICE ||au->units[i].type == H264_NAL_IDR_SLICE)break;}sei_pos = i;if (sei_pos < au->nb_units &&au->units[sei_pos].type == H264_NAL_SEI) {sei_new = 0;sei = au->units[sei_pos].content;} else {sei_new = 1;sei = &ctx->sei_nal;memset(sei, 0, sizeof(*sei));}
}

以上代码是h264_metadata添加SEI的判断逻辑,当指定了sei_user_data时,满足以下条件之一即可以处理:

  • 读取的access units是第一个au;
  • 当前au包含sps;
    满足插入SEI逻辑后,具体处理过程中:
  • 如果发现第一个NAL已经是SEI,则该au不做插入SEI 处理;
  • 如果au包含了IDR帧或者非IDR未分区的帧,则在其前面插入SEI 信息。

基于以上代码,oceans.sei.h264码流中新插入27个新的SEI 符合处理逻辑。
具体构造SEI NAL Unit代码如下:

        sei->nal_unit_header.nal_unit_type = H264_NAL_SEI;err = ff_cbs_insert_unit_content(ctx->cbc, au,sei_pos, H264_NAL_SEI, sei);if (err < 0) {av_log(bsf, AV_LOG_ERROR, "Failed to insert SEI.\n");goto fail;}payload = &sei->payload[sei->payload_count];payload->payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED;udu = &payload->payload.user_data_unregistered;for (i = j = 0; j < 32 && ctx->sei_user_data[i]; i++) {int c, v;c = ctx->sei_user_data[i];if (c == '-') {continue;} else if (av_isxdigit(c)) {c = av_tolower(c);v = (c <= '9' ? c - '0' : c - 'a' + 10);} else {goto invalid_user_data;}if (i & 1)udu->uuid_iso_iec_11578[j / 2] |= v;elseudu->uuid_iso_iec_11578[j / 2] = v << 4;++j;}if (j == 32 && ctx->sei_user_data[i] == '+') {sei_udu_string = av_strdup(ctx->sei_user_data + i + 1);if (!sei_udu_string) {err = AVERROR(ENOMEM);goto sei_fail;}udu->data = sei_udu_string;udu->data_length = strlen(sei_udu_string);payload->payload_size = 16 + udu->data_length;}

代码完整解释了上文提到的SEI规范,其中"H264_SEI_TYPE_USER_DATA_UNREGISTERED"值为5,对应的即是未注册的用户信息。在解析"ffmpeg"工具输入过程中,将"+"号前面的字符串转换成二进制写入uuid,"+"后内容使用字符串写入payload。

2.2 x264

libx264支持多种SEI类型数据写入,常用的仍然是SEI_USER_DATA_UNREGISTERED,具体的写入函数x264_sei_version_write()位于libx264/encoder/set.c中。

int x264_sei_version_write( x264_t *h, bs_t *s )
{static const uint8_t uuid[16] ={0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7,0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef};char *opts = x264_param2string( &h->param, 0 );char *payload;int length;if( !opts )return -1;CHECKED_MALLOC( payload, 200 + strlen( opts ) );memcpy( payload, uuid, 16 );sprintf( payload+16, "x264 - core %d%s - H.264/MPEG-4 AVC codec - ""Copy%s 2003-2018 - http://www.videolan.org/x264.html - options: %s",X264_BUILD, X264_VERSION, HAVE_GPL?"left":"right", opts );length = strlen(payload)+1;x264_sei_write( s, (uint8_t *)payload, length, SEI_USER_DATA_UNREGISTERED );x264_free( opts );x264_free( payload );return 0;
fail:x264_free( opts );return -1;
}

libx264提供的uuid和上文举例的uuid一致,payload中主要记录了相关参数和版权信息。以上函数完成了SEI参数的构造,下面的函数x264_sei_write完成了具体语法的写入:

void x264_sei_write( bs_t *s, uint8_t *payload, int payload_size, int payload_type )
{int i;bs_realign( s );for( i = 0; i <= payload_type-255; i += 255 )bs_write( s, 8, 255 );bs_write( s, 8, payload_type-i );for( i = 0; i <= payload_size-255; i += 255 )bs_write( s, 8, 255 );bs_write( s, 8, payload_size-i );for( i = 0; i < payload_size; i++ )bs_write( s, 8, payload[i] );bs_rbsp_trailing( s );bs_flush( s );
}
以上写入的代码逻辑和标准语法说明保持一致。

3. SEI的解析

3.1 解析SEI

FFmpeg在读取和解码NAL unit,都有相同的逻辑处理SEI
读取或者解码数据时,会调用下面函数进行码流的解码,其中buf包含具体的二进制流,buf_size是当前码流长度。函数内部会解析码流并实例出具体的NAL对象:

//Locate in libavcodec/h264dec.c
int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)

如果NAL对象类型是SEI 时,将调用以下函数解码:

//Locate in libavcodec/h264_sei.c
int ff_h264_sei_decode(H264SEIContext *h, GetBitContext *gb,const H264ParamSets *ps, void *logctx)

函数内部会判断SEI payload type进行不同的函数调用,如果是未注册的用户数据,则调用以下函数:

 int decode_unregistered_user_data(H264SEIUnregistered *h, GetBitContext *gb,void *logctx, int size)
{uint8_t *user_data;int e, build, i;if (size < 16 || size >= INT_MAX - 16)return AVERROR_INVALIDDATA;user_data = av_malloc(16 + size + 1);if (!user_data)return AVERROR(ENOMEM);for (i = 0; i < size + 16; i++)user_data[i] = get_bits(gb, 8);user_data[i] = 0;e = sscanf(user_data + 16, "x264 - core %d", &build);if (e == 1 && build > 0)h->x264_build = build;if (e == 1 && build == 1 && !strncmp(user_data+16, "x264 - core 0000", 16))h->x264_build = 67;av_free(user_data);return 0;
}

可以看到,根据SEI语法标准,在解析了SEI payload typelength后,对未注册用户数据的提取,跳过了uuid的分析,只尝试提取了x264的build信息。总体上,并未利用SEI_USER_DATA_UNREGISTERED传递过来的其他相关参数信息。
从解码器逻辑看,H264SEIUnregistered结构体只有一个x264_build属性,并未返回实质有效数据。上层业务如果需要提取SEI_USER_DATA_UNREGISTERED,仍然需要自己提取。提取逻辑,请参考下一小节(ffplay)。

3.2 ffplay

ffplay是一个简单、常用的FFmpeg接口示例工具,常用于测试解码、播放效果。如果在ffplay中示例跑通SEI提取功能,可以很方便的移植到其他平台。
ffplay工具中,通过av_read_frame(ic, pkt);可以快速拿到当前读到的NAL unit,位于pkt->data中,可以从此取出NAL unit type,如果是SEI且是用户未注册数据类型(payload type值为5),则可以参考SEI语法继续读取UUID和其后传递的字符串。

4. 总结

限于篇幅,本文主要对H.264码流中涉及用户未注册数据的SEI进行了分析。总体而言,SEI只是视频标准里面很小的一部分,但在应用过程中,比如直播问答项目中SEI承载的信息,就极大提升了直播观看和答题操作的整体用户体验。所以说,从SEI的例子中,我们就会发现,视频标准里面还有很多金矿等待着大家的挖掘,这就是多媒体技术的魅力。



作者:金山视频云
链接:https://www.jianshu.com/p/4d9120dfcd69
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

这篇关于SEI(Supplemental Enhancement Information)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/610115

相关文章

计算机视觉中,什么是上下文信息(contextual information)?

在计算机视觉中,上下文信息(contextual information)是指一个像素或一个小区域周围的环境或背景信息,它帮助模型理解图像中对象的相对位置、大小、形状,以及与其他对象的关系。上下文信息在图像中提供了全局的语义和结构线索,使模型不仅依赖局部细节,而且能够考虑整个场景或图像的大局。 上下文信息的具体含义 局部与全局信息的结合: 局部信息:这是指某个小区域或某个像素点的特征。通过小

使用ffmpeg的c++库读取视频流和其中的SEI数据

使用ffmpeg读取视频流和其中的SEI数据(未完待续) FFmpeg是一个多媒体软件框架,支持多种新旧视频编码格式,提供解码、编码、 转码、多路复用、解复用、流式传输、过滤和播放等功能。其包含: C++库libavcodec、libavutil、libavformat、libavfilter、libavdevice、 libswscale和libswresample基于库构建的命令行工具ff

Hive 2.3.0 MetaException(message:Version information not found in metastore. )

使用Hive 2.3.0 配置远程模式(Remote)时,执行hive --service metastore命令时出现MetaException(message:Version information not found in metastore. )错误。 解决办法: The necessary tables required for the metastore are missing i

AWS S3对象无法下载——This XML file does not appear to have any style information associated with it

最近,需要从AWS S3上下载渲染后的图片,遇到了如下问题: This XML file does not appear to have any style information associated with it. The document tree is shown below. <Error><Code>AccessDenied</Code><Message>Acce

Oracle 附加日志(supplemental log)

参考资料: 1.https://blog.csdn.net/li19236/article/details/41621179

在Windows 系统中开启IIS(‌Internet Information Services)‌服务

在Windows 系统中开启IIS(‌Internet Information Services)‌服务,‌用户可以通过以下步骤进行操作:‌ 首先,打开“控制面板”。‌ 在控制面板中,‌点击“程序”选项。‌ 接着,‌选择“启动或关闭Windows功能”。‌ 在Windows功能列表中,‌找到并勾选“Internet Information Services”及其相关的子选项。‌

论文学习 Learning Robust Representations via Multi-View Information Bottleneck

Code available at https://github.com/mfederici/Multi-View-Information-Bottleneck 摘要:信息瓶颈原理为表示学习提供了一种信息论方法,通过训练编码器保留与预测标签相关的所有信息,同时最小化表示中其他多余信息的数量。然而,最初的公式需要标记数据来识别多余的信息。在这项工作中,我们将这种能力扩展到多视图无监督设置,其中提供

SEI的最新工作:Open AI协作、生成式AI和零信任

作为让您了解我们最新持续投入的部分工作,这篇文章总结了SEI在网络安全大语言模型、软件工程和生成式AI获取、零信任、大型语言模型等领域最近发表的一些出版物。以及网络安全、基于能力的规划、供应链的风险管理、软件工程和采购中的生成式人工智能以及量子计算。 这些出版物重点介绍了SEI技术专家在这些领域的最新工作。这篇文章包含每个出版物、作者以及可以在SEI网站上访问的链接的列表。 评估网络安全任务的

php: /usr/local/lib/libxml2.so.2: no version information available (required by php)

Linux下执行php *.php报php: /usr/local/lib/libxml2.so.2: no version information available (required by php)这个错误 解决办法: 把/usr/local/lib/libxml2.so.2这个文件删除就可以了,这是linux版本混乱的原因~~

解决android studio每次启动都要在fetching Android sdk compoment information

原因, 每次启动都会检测SDK,并且国内连google经常连不上,导致卡住,解决方法是直接禁用掉 2、解决android studio每次启动都要在fetching Android sdk compoment information 1)进入刚安装的Android Studio目录下的bin目录。找到idea.properties文件,用文本编辑器打开。2)在idea.properties