视频
视频采集装置一秒钟会捕捉几十帧数据,动画一般25帧以上,一般视频文件30帧数据,对于捕捉一些清晰动作的甚至要达到60帧以上,但是对于一组帧,帧和帧之间的变化很小,为了压缩数据,我们将第一帧(I帧)完整保存下来,没有关键帧,后面数据帧无法解码。接下来章节主要着重介绍我们经常在视频编码当中用到的 H.264编码
常用的视频编解码器
x264/x265:x265压缩比更高,占用CPU也更高
openH264:支持SVC(会将图分层,小中大,根据网络情况发送,缺点是移动端很多硬件不支持,只能使用软编,cpu消耗高)
vp8/vp9:vpx系列,vp8对应x264,vp9对应x265
H.264背景
H.264是[国际标准化组织]([ISO])和国际电信联盟(ITU)共同提出的继MPEG4之后的新一代数字视频压缩格式。H.264是在MPEG-4技术的基础之上建立起来的,其编解码流程主要包括5个部分:帧间和帧内预测、变换(Transform)和反变换、量化(Quantization)和反量化、环路滤波(Loop Filter)、熵编码(Entropy Coding)。与其它现有的视频编码标准相比,在相同的带宽下提供更加优秀的图象质量。通过该标准,在同等图象质量下的压缩效率比以前的标准(MPEG2)提高了2倍左右
H.264中的基本概念
I帧
I帧:关键帧,采用帧内压缩技术P帧
P帧:向前参考帧,压缩时参考前一帧(帧间压缩技术,后面帧只存与它前一帧的差异)B帧
B帧:双向参考帧,压缩时既参考前一帧也参考后一帧,帧间压缩技术PTS与DTS
PTS(Presentation Time Stamp):PTS主要用于度量解码后的视频帧什么时候被显示出来。
DTS(Decode Time Stamp):DTS主要是标识内存中的bit流再什么时候开始送入解码器中进行解码。
备注:DTS主要用户视频的解码,在解码阶段使用。PTS主要用于视频的同步和输出,在display的时候使用。再没有B frame的时候输出顺序一样
-
GOF
Group of Frame(一组帧),GOP是画面组,一个GOP是一组连续的画面,GOP一般有两个数字,如M=3,N=12(M指定I帧与P帧之间的距离,N指定两个I帧之间的距离),GOF结构格式如下:
-
IDR
在H.264中,图像以序列为单位进行组织。一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。H.264引入IDR图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。IDR 图像一定是 I 图像,但 I 图像不一定是IDR图像。I帧之后的图像有可能会使用I帧之前的图像做运动参考
SPS与PPS
SPS:Sequence Parameter Set,序列参数集,存放帧数、参考帧数目、解码图像尺寸、帧场编码模式选择标识等
PPS:Picture Parameter Set,图像参数稽核,存放熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等
备注:在接收一组帧之前我们首先会收到SPS和PPS,如果没有这两个参数我们是无法解码的
H.264原理与结构
主要使用压缩技术
- 帧内预测压缩(解决空域数据冗余问题,一幅图里面的颜色,色彩,光亮,人不是很敏感,可以删掉)
- 帧间预测压缩(解决时域数据冗余问题,随着时间轴的推进删除重复图)
- 整数离散余弦变换(DCT),将空间上的相关性变为频域上无关的数据,然后进行量化
CABAC压缩(无损压缩)
H.264码流
SODB:String Of Data Bits原始数据比特流,长度不一定是8的倍数,由VCL层产生
-
RBSP:Raw Byte Sequence PayLoad (SODB + trailing bits)相当于给SODB补结束标记(在SODB最后一位补1,如果不是8字节对齐,则补0进行对齐)
EBSP:Encapsulate Byte Sequence Payload 在生成压缩流之后,在每个帧的开始处加一个起始位(起始码:00 00 01或00 00 00 01,如果实际数据有00 00那么需增加一个0x03进行冲突区分)
-
NALU:NAL Header(1个字节) + RBSP
备注:NAL Header 由三部分组成,forbidden_bit(1bit),nal_reference_bit(2bits)(优先级),nal_unit_type(5bits)(类型)
H.264原始码流是由一个接一个NALU组成,它的功能分为两层,VCL(视频编码层)和 NAL(网络提取层).
VCL(Video Coding Layer) + NAL(Network Abstraction Layer)
VCL:Video Coding Layer 视频数据编码层,包括核心压缩引擎和块,宏块和片的语法级别定义,设计目标是尽可能地独立于网络进行高效的编码
NAL:Network Abstraction Layer 视频数据网络抽象层,负责将VCL产生的比特字符串适配到各种各样的网络和多元环境中,覆盖了所有片级以上的语法级别
在VCL进行数据传输或存储之前,这些编码的VCL数据,被映射或封装进NAL单元(NALU)
H.264结构
整体结构参考如下图
NALU单元详解
- NAL Header
F:
forbidden_zero_bit,在H.264规范中规定该位必须为0NRI:
指示重要性Type:
NALU单元类型
nal_unit_type | 说明 |
---|---|
0 | 没有定义 |
1-23 |
NAL单元 单个 NAL 单元包 |
1 | 非IDR图像中不采用数据划分的片段 |
2 | 非IDR图像中A类数据划分片段 |
3 | 非IDR图像中B类数据划分片段 |
4 | 非IDR图像中C类数据划分片段 |
5 | IDR图像分片 |
6 | 补充增强信息单元(SEI) |
7 | 序列参数集(SPS) |
8 | 图像参数集(PPS) |
9 | 分解符 |
10 | 序列结束 |
11 | 码流结束 |
12 | 填充 |
13-23 | 保留 |
24 | STAP-A 单一时间的组合包 |
25 | STAP-B 单一时间的组合包 |
26 | MTAP16 多个时间的组合包 |
27 | MTAP24 多个时间的组合包 |
28 | FU-A 分片的单元 |
29 | FU-B 分片的单元 |
30-31 | 没有定义 |
-
NAL类型介绍
单一类型:一个RTP包只包含一个NALU
组合类型:一个RTP包含多个NALU,类型是24-27
分片类型:一个NALU单元分成多个RTP包,类型是28和29
-
FU Header
S:
start bit,用于指明分片的开始
E:
end bit,用于指明分片的结束
R:
未使用,设置为0
Type:
指明分片NAL类型
Slice片详解
由H264码流分层图可得知,NALU的主体就是Slice(片),片是H.264提出的新概念,通过编码图片后切分通过高效的方式整合出来的概念。一张图片有一个或者多个片,而片由NALU装载并进行网络传输的。一个Slice包含一帧图像的部分或全部数据,换言之,一帧视频图像可以编码为一个或若干个Slice。一个Slice最少包含一个宏块,最多包含整帧图像的数据。在不同的编码实现中,同一帧图像中所构成的Slice数目不一定相同。设置片的目的是为了限制误码的扩散和传输,应使编码片相互间是独立的。某片的预测不能以其他片中的宏块为参考图像,这样某一片中的预测误差才不会传播到其他片中。
一个Slice由Slice Header + Slice Data构成,如下图所示:
- Slice 类型
slice类型 | 说明 |
---|---|
I slice | 帧内编码的条带(只包含I宏块) |
P slice | 单向帧间编码的条带(包含P和I宏块) |
B slice | 双向帧间编码的条带(包含B和I宏块) |
SI slice | 切换I条带,用于扩展档次中码流切换使用(一种特殊类型的编码宏块) |
SP slice | 切换P条带,用于扩展档次中码流切换使用(包含P 和I或 I宏块,用于不同码流之间的切换) |
- Slice Header
Slice header中主要保存了当前slice的一些全局的信息,slice body中的宏块在进行解码时需依赖这些信息。其中比较常见的一些语法元素有
slice header参数 | 说明 |
---|---|
first_mb_in_slice | 第一个宏块在slice的位置 |
slice_type | slice的类型 |
pic_parameter_set_id | 当前slice所依赖的pps的id;范围 0 到 255 |
colour_plane_id | 当标识位separate_colour_plane_flag为true时,colour_plane_id表示当前的颜色分量,0、1、2分别表示Y、U、V分量 |
frame_num | 表示当前帧序号的一种计量方式 |
field_pic_flag | 场编码标识位。当该标识位为1时表示当前slice按照场进行编码;该标识位为0时表示当前slice按照帧进行编码 |
bottom_field_flag | 底场标识位。该标志位为1表示当前slice是某一帧的底场;为0表示当前slice为某一帧的顶场 |
idr_pic_id | 表示IDR帧的序号。某一个IDR帧所属的所有slice,其idr_pic_id应保持一致。该值的取值范围为[0,65535] |
pic_order_cnt_lsb | 表示当前帧序号的另一种计量方式 |
delta_pic_order_cnt_bottom | 表示顶场与底场POC差值的计算方法,不存在则默认为0 |
slice_qp_delta | 用于计算当前slice内所使用的初始qp值 |
slice_type
slice_type值 | 说明 |
---|---|
0 | P (P slice) |
1 | B (B slice) |
2 | I (I slice) |
3 | SP (SP slice) |
4 | SI (SI slice) |
5 | P (P slice) |
6 | B (B slice) |
7 | I (I slice) |
8 | SP (SP slice) |
9 | SI (SI slice) |
宏块详解
宏块是视频信息的主要承载者。一个编码图像通常划分为多个宏块组成.包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中像素阵列 。
一个宏块 = 一个16*16的亮度像素 + 一个8×8Cb + 一个8×8Cr彩色像素块组成。(YCbCr 是属于 YUV 家族的一员,在YCbCr 中 Y 是指亮度分量,Cb 指蓝色色度分量,而 Cr 指红色色度分量)
在 H.264 中,句法元素共被组织成 序列、图像、片、宏块、子宏块五个层次, 句法元素的分层结构有助于更有效地节省码流。例如,再一个图像中,经常会在各个片之间有相同的数据,如果每个片都同时携带这些数据,势必会造成码流的浪费。更为有效的做法是将该图像的公共信息抽取出来,形成图像一级的句法元素,而在片级只携带该片自身独有的句法元素。宏块的具体结构如下:
宏块结构
宏块结构 | 说明 |
---|---|
mb_type | 宏块类型,确定该 MB 是帧内或帧间(P 或 B)编码模式,确定该 MB 分割的尺寸 |
mb_pred | 预测类型,确定帧内预测模式(帧内宏块)确定表 0 或表 1 参考图 像,和每一宏块分割的差分编码的运动矢量(帧间宏块,除 8×8 宏块分割的帧内 MB) |
sub_mb_pred | 预测类型,(只对 8×8MB 分割的帧内 MB)确定每一子宏块的子宏 块分割,每一宏块分割的表 0 和/或表 1 的参考图象;每一 宏块子分割的差分编码运动矢量 |
coded_block_pattern | 指出哪个 8×8 块(亮度和彩色)包 编码变换系数 |
mb_qp_delta | 量化参数的改变值 |
YUV(YCbCr)
YUV是从电视系统衍生出来的一种颜色编码方法,Y表示明亮度,也就是灰阶值,它是基础信号,U和V表示的则是色度,UV的作用是描述影像色彩及饱和度,它们用于指定像素的颜色。YUV亮度信息(Y)与色彩信息(UV)分离,没有 UV 信息一样可以显示完整的图像,没有UV分量,则只是黑白显示,YUV 不像RGB 那样要求三个独立的视频信号同时传输,而且用 YUV 数据占用的内容更少。
YCbCr 中,Y 是指亮度分量,Cb 指蓝色色度分量,而 Cr 指红色色度分量
- YUV与RGB的相互转换
RGB转YUV:
Y = 0.299R + 0.587G + 0.114B
Cb = 0.564(B - Y)
Cr = 0.713(R - Y)
YUV转RGB:
R = Y + 1.402Cr
G = Y - 0.344Cb - 0.714Cr
B = Y + 1.772Cb
- YUV常见格式
- YUV4:2:0:
YUV4:2:0并不意味着只有Y和Cb两个分量,而没有Cr分量。它实际指的是对每行扫描线来说,只有一种色度分量,它以2:1的抽样率存储。例如相邻的扫描行存储不同的色度分量,也就是说,如果一行时4:2:0的话,下一行就是4:0:2,在下一行就是4:0:2,在下一行就是4:2:0进行以此类推 - YUV4:2:2
- YUV4:4:4
举例一副图片假设像素矩阵如下:
上图中每个像素的 3 个分量的信息是完整的,Y : Cb : Cr = 4 : 4 : 4,属于 YUV444 格式。
人类视觉系统对亮度信号比色度信号敏感的原理,我们可以省略图片的一些信息,对图片的质量影响却不会太大,比如将该像素矩阵存储为:
此时,每两个 Y 共用一组 UV 分量,Y : Cb : Cr = 4 : 2 : 2,属于 YUV422 格式。
同理我们进一步得出YUV420格式如下:
每四个 Y 共用一组 UV 分量,Cb、Cr 交替出现,在第一行数据里 Y : Cb : Cr = 4 : 2 : 0;而在第二行数据,Y : Cb : Cr = 4 : 0 : 2,这就是最常见的 YUV420 格式。
- YUV存储格式分类
- planar平面存储
I420(YUV420P): YYYYYYYY UU VV
YV12(YUV420P):YYYYYYYY VV UU - packed打包存储
NV12(YUV420SP):YYYYYYYY UVUV
NV21(YUV420SP):YYYYYYYY VUVU
planar 先存储所有 Y,紧接着存储所有 U,最后是 V;而 packed 则是每个像素点的 Y、U、V 连续交叉存储。
现在我们不管是移动端(安卓和IOS)也好,PC端也好,在转成YUV之后可能出现图像倒置或反转,可能和平台支持的YUV存储格式不同有关,例如安卓默认的是NV21,ios默认是NV12,PC端是I420,屏幕倒置旋转主要原因还是存储格式不一样,要让他们一样就是要做成一个统一的格式