【Agorans 征文】浅谈视频中的SEI以及测试中的使用场景

熟悉旁路推流的同学们应该都耳熟一个词:SEI,那么SEI是什么呢,又如何在实际中进行使用呢,本文就来初步探讨一下视频中的SEI。

1. SEI的简介

1.1 什么是SEI

SEI(Supplemental Enhancement Information),是定义在视频码流里面,提供在视频码流中添加信息的方法。在当前的流媒体中,可以用来添加一些视频无关的信息,例如歌词等,来做到同步显示。SEI可以在编码时候进行添加,也可以在传输时候进行添加。

1.2 NAL unit

在继续介绍SEI之前,我们先来了解一下NAL unit这个概念。在H.264/H.265的标准中,都使用了视频编码层(VCL)和网络适配层(NAL)的双层架构。VCL就是实际有效的视频数据,而NAL就是给出了一个标准化的方式保证数据的传输,NAL unit就是NAL的基本语法。以H.264为例,原始码流就是由一个一个的NALU组成的,其中每个NALU是由NAL header和来自VCL的原始数据字节流(RBSP)组成,如下图所示:
1
每个NALU之间通过起始码进行分隔,起始码分成两种:0x000001(3Byte)或者0x00000001(4Byte)。只要找到0x000001或者0x00000001即可认为是一个NALU的起始。下面我们就分H.264和H.265来给大家介绍一下。

1.2.1 H.264

NAL unit type储存在NAL header中,在H.264标准中,有以下的一些定义:


从上图中,我们可以看到nal_unit_type为6即为我们今天的主题SEI。那么当我们拿到一个视频序列时候,该如何去找到SEI呢,我们接着看标准中是如何读取的。

我们找到标准中对于此部分的定义,仅看header部分,我们可以从上图中知道:头部由一个字节组成,其中分为三个部分。第一部分为forbidden_zero_bit,占1bit,0为正确;第二部分为nal_ref_idc,占2bit,表示当前NAL的优先级;第三部分为nal_unit_type,占5bit。所以我们取到NALU的第一个字节后 & 0x1f 就可得到nal_unit_type。即 int type = (code & 0x1F)

1.2.2 H.265

同理,我们通过阅读标准可知:


nal_unit_type 39和40代表的为SEI。根据下图我们可以知道,nal_unit_type为6bit,所以我们将第一个字节 & 0x7e 后右移一位即可得到nal_unit_type。即int type = (code & 0x7E)>>1
5

1.2.3 例子

image
上图是一个H.264的视频序列,我们可以读到StartCode之后的第一个字节为0x67,0x67即为01100111,所以nal_unit_type为7,查询上表可知为SPS。
image
上图是一个H.265的视频序列,我们可以读到0x40即为01000000,所以nal_unit_type为32,查询上表可知为VPS。

1.3 SEI详情

1.3.1 SEI解析

通过上述说明,我们知道了如何去读取到SEI,那我们现在来讲解一下SEI的一些解析规则:


如图所示,当我们读到NAL unit type为SEI的时候,我们就开始先读取SEI payloadType。我们一次读取8bit,只要是非0xFF,那么这8bit的值即为SEI payloadType;如果是0xFF,那么我们SEI payloadType + 255继续去读,直到有8bit不是0xFF为止。同理,SEI payloadSize也是相同的读法。H.264和H.265对于SEI payloadType和payloadSize的定义都是相同的,全都是浮动的值,给予了我们足够的灵活性。

1.3.2 SEI类型

标准中定义的一种常见的SEI Type就是5,对于type为5的指定处理方法为user_data_unregistered(),常用于存储编码器的编码参数信息等。

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

通过上面的代码我们可以知道,我们需要传入16个字节的UUID,剩下的自定义数据部分的大小就为payloadSize-16,所以payloadSize一定大于16。
上面我们也提到了SEI type是一个可以浮动的值,所以在标准定义之外,我们声网自定义的SEI类型还有:

typedef enum
{
  SEI_H264_RECOVERY_POINT = 6,
  // it is recommended to use range between 70 - 100
  SEI_H264_SLICE_INFO     = 98,
  SEI_H264_REF_IDX        = 99,
  SEI_H264_LAYOUT         = 100,
  SEI_H264_AGORA          = 101,
} SEI_H264_Type;

其中100是旁路推流中所使用的,101在Native和Web中可以看到。

1.3.3 防竞争处理

为了防止字节竞争,我们遇到0x000000/0x000001/0x000002的时候,需要插入0x03防竞争处理,即变为0x00000300/0x00000301/0x00000302。所以需要我们客户的解码端能够在解码时去掉0x03这个字节。

1.3.4 例子

7
上图是一个H.264序列,我们可以看到蓝色标注区域就是一个SEI,0x64即为SEI type,是我们声网旁路推流定义的type 100;后面0x0d即为payloadSize,也就是后面13字节为数据部分,我们可以看到是一个unix时间戳。
8
上图是一个H.265序列,我们可以看到蓝色标注区域就是一个SEI,4E 01即代表了nal_unit_header,0x64就是旁路推流定义的type 100,后续解析同264。
9
上图即为一个H.264的SEI type为5的一个示例,按照上面的解析规则,0x05代表了type为5,0x18代表payloadSize为24,减去16位UUID,即用户数据部分为00 00 03 01 73 E1 45 7C 78,中间多出一个03即为防竞争位,将03去掉我们整体解析会发现这是一个unix时间戳:1597212294264。

1.3.5 客户问题&解决技巧

有客户问,为什么你们SEI type有355?
10
我们看上图客户提问,06即为SEI,下个字节我们读到为0x64,所以type为100。再下一个字节为0xFF,我们把size+255,再往下一个是0x0F,所以size为270,我们可以看到下图统计也为270,所以没有任何问题。那么为什么会有客户解析出来355呢,应该是客户解析出错,将64 FF算作了SEI type导致,0x64+0xFF=355。


所以如果再有客户说,我们有了不一样的SEI type,我们可以通过客户的视频文件来分析是否有不正确的。
解决技巧

  1. 分析视频文件时,最好先从封装(mp4/flv等)中取出264或者265的裸流,这样更易看一些,可使用ffmpeg取出裸流。
ffmpeg -i xxx.flv -vcodec copy -an xxx.264
  1. 客户报的SEI type如果小于255,那么直接转换为16进制进行搜索即可;如果大于255,那就减去255后转换16进制前面加上FF一起搜索即可,例如:
    14
    这样即可搜出。如有需要搜索其他type,也可以直接搜索00 01 06,再去读后面的type即可。(推荐使用010 editor,可以直接hex搜索)

2. SEI解析工具–ffmpeg_debug

看了上述读取方法,是不是还是觉得很麻烦,没有关系,我们自己有一个简易工具帮你直接解析。

2.1 实现思路

15

2.2 工具使用

./ffmpeg_debug video sei


即可直接解析出type和相应内容,支持264/265的SEI解析。

2.3 运行环境

  • 支持平台:Mac/Ubuntu

  • 编译使用工具:系统库+修改过的FFmpeg+QT

  • Mac为静态编译,仅需系统库+SDL2即可运行;Windows平台编译比较麻烦,暂不支持

PS: QT可以参考https://rtcdeveloper.com/t/topic/14244,FFmpeg Mac下静态编译有需要可以分享

2.4 工具历史

ffmpeg-debug-qp -> 单老师ffmpeg_debug初版 -> 我修改的ffmpeg_debug第二版
感谢以上各位

3. SEI在测试中的使用

SEI随着视频帧一起到达,可以让我们更加精准的测试视频延时,下面就来分享一下是如何进行的。

3.1 测试场景介绍

本次测试的项目为Cloud Player,是一个RESTful拉流的服务。
整体结构如图:


老师端通过推RTMP到CDN,我们的Cloud Player拉取CDN的流媒体并进入SD-RTN供学生端订阅。

3.2 测试场景介绍


因为当时的方案场景是使用Web观看,所以我们使用Web作为学生端。老师端和学生端为同一台机器,可以消除时间误差,这样就可以很好的估算出端到端延时。同时我们可以将CDN和Cloud Player部署在同一台机器,可以通过diff来算出各环节耗时,为我们的延时优化提供数据指导。
通过与传统的秒表截图估算误差作比较,使用SEI来观测误差有以下几个好处:

  • 数据更加精准更加可信

  • 可以在我们系统的各个流程/环节进行监测,为我们的延时优化提供数据指导

  • 可以进行一个长时间多采样的数据对比

最后

本文仅是个人的理解总结,如有错误,请帮忙指出,感谢各位大佬。
参考:

3赞