..

About_video

参考:https://github.com/leandromoreira/digital_video_introduction/blob/master/README-cn.md

基础图像格式表达

RGB

  • RGB565 每个像素用16位表示,RGB分量分别使用5位、6位、5位

  • RGB555 每个像素用16位表示,RGB分量都使用5位(剩下1位不用)

  • RGB24 每个像素用24位表示,RGB分量各使用8位

  • RGB32 每个像素用32位表示,RGB分量各使用8位(剩下8位不用)

  • ARGB32 每个像素用32位表示,RGB分量各使用8位(剩下的8位用于表示Alpha通道值)

对于RGB24格式图像来说,每个像素点都包含 R(red)、G(green)、B(blue)三个分量。每个分量均占用一个字节。对于一个width * height的图像来说,占用的字节数为 width * height * 3

一般名称 宽x高 Bytes=宽x高x3 KB=Bytes/1024 MB=KB/1024
720P 1280x720 2764800 2700 2.636…
1080P 1920x1080 6220800 6075 5.932…
2K 2048x1080 6635520 6480 6.328125
4K 3840x2160 24883200 24300 23.73…
8K 7680x4320 99532800 97200 94.921875

YUV

Y表示亮度,U表示表示蓝色与亮度的差异,V表示红色与亮度的差异。YCbCr 是 YUV 的数字形式。

大体分类

YUV格式有两大类:planar和packed。

  • 对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。

  • 对于packed的YUV格式,每个像素点的Y,U,V是连续交叉存储的。

像素格式

YUV-4:4:4:每个像素有 Y、U 和 V 三个分量,每个分量占 1 个字节(8 位),与 RGB 相同,每个像素需要 3 个字节。

YUV-4:2:2:每两个像素共享一个 U 和一个 V 分量,即每 2 个像素有 2 个 Y 分量、1 个 U 分量和 1 个 V 分量。 每个像素的内存大小为:(2+1+1)/2 = 2Bytes=16bits=8bit(Y)+4bits(U)+4bits(V)

YUV-4:2:0:每四个像素共享一个 U 和一个 V 分量,即每 4 个像素有 4 个 Y 分量、1 个 U 分量和 1 个 V 分量。每个像素的内存大小为:(4+1+1)/4=1.5Bytes=12bits=8bits(Y)+2bits(U)+2bits(V)

一些在内存里的表达差异

  • YUV420SP(安卓平台特有,又叫NV21):①先把Y数据全部排序完;②UV数据交替排序完;
  • YUV420P(I420):①先把Y数据全部排序完;②U数据排序完;③V数据排序完;
  • 注意:YUV420SP和YUV420P表达效果完全一致,只是放到内存里面的顺序变了

NV12和NV21的区别

NV12:UV 交替排列,U 在前,V 在后。

NV21:VU 交替排列,V 在前,U 在后。

格式转换

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
B = Y + 1.772Cb
G = Y - 0.344Cb - 0.714Cr

基础图像压缩

帧类型

I 帧(关键帧)

I 帧(Intra-coded picture,关键帧,帧内编码)是独立编码的帧,不依赖其他帧的信息。

P 帧(预测帧)

P 帧(Predictive-coded Picture,前向预测编码图像帧)是前向预测编码帧,依赖前面的 I 帧或 P 帧

B帧(双向预测帧)

B帧(Bidirectionally predicted picture,双向预测编码图像帧)是双向预测编码帧,依赖前后的 I 帧或 P 帧。B帧压缩率相对来说是最高的。

IDR

IDR(Instantaneous Decoding Refresh Frame) 帧是特殊的 I 帧,不仅独立解码,还会刷新解码器的参考帧列表,确保解码器从 IDR 帧开始不会参考之前的任何帧。

GOP(这个应该不算帧类型)

GOP(Group of Pictures) 是一组连续的帧序列,其中包含一个 I 帧和若干个 P 帧和/或 B 帧。

帧类型的编解码顺序(无关压缩算法)

由于I帧、P帧、B帧的特性,注定就意味着解码顺序和显示顺序会不一致,会引申出一个显示顺序和解码顺序的概念。称之为显示时间戳PTS(Presentation Time Stamp)和解码时间戳DTS(Decoding Time Stamp)。虽然 DTS、PTS 是用于指导播放端的行为,但它们是在编码的时候由编码器生成的。

例如采集到的一系列图像的显示顺序为IBBPBBP,那么对应的PTS对应的就是I(1)-B(2)-B(3)-P(4)-B(5)-B(6)-P(7)编码顺序应该如下

  1. 首先是编码I(1)
  2. B(2)依赖I(1)P(4)P(4)依赖I(1),由于有步骤1所以这里编码P(4)
  3. B(2)依赖I(1)P(4),由于步骤1提供I(1),步骤2提供P(4)所以这里编码B(2)
  4. 同理,由于步骤1和步骤2这里编码B(3)
  5. B(5)B(6)依赖P(4)P(7),由于步骤2提供了P(4),并且P(7)也依赖P(4),所以这里编码P(7)
  6. 同理,编码B(5)
  7. 同理,编码B(6)

所以DTS对应的就是I(1)-P(4)-B(2)-B(3)-P(7)-B(5)-B(6)。此时编码器也是按照DTS去存储或者传输的帧,不过他会把这时候的每一帧再添加上PTS数据,来告知解码器播放顺序。

视频流信息

SPS

Sequence Parameter Set (SPS)提供全局序列参数,用于描述视频序列的整体特性。这些参数对于解码视频序列中的每一帧都是必需的。具体包括以下内容:

  • 视频序列的宽度和高度
  • 视频的帧率信息
  • 解码能力和限制(如最大参考帧数)
  • 色彩空间信息
  • 比特率控制信息

PPS

Picture Parameter Set (PPS)提供具体帧的参数,用于描述单个帧的特性。这些参数可能在不同的图片之间有所不同。具体包括以下内容:

  • 熵编码模式
  • 去块效应滤波器参数
  • 图像的切片类型和相关信息
  • 每个宏块的量化参数
  • 是否使用Deblocking滤波器

tips

通常,一个视频流会包含一个SPS和多个PPS,因为序列参数是全局的,而图片参数可能会根据帧的具体情况有所变化。

H.264压缩算法

H264 码流实际可以理解为由一个一个的 NALU 单元组成。

压缩流程概览

  1. 切片(slice)宏块(macroblock):将原始图像分成多个切片,每个切片可以独立进行编码和解码。然后将切片进一步分成宏块,通常是16x16像素。每个宏块可以进一步划分为更小的块(如4x4或8x8像素),用于更精细的处理。
  2. 预测:包括帧内预测(利用同一帧中相邻块的信息来预测当前块的像素值,从而减少冗余数据)和帧间预测(主要使用I帧B帧P帧预测来减少冗余数据)。
  3. DCT变化:对预测残差(即实际值和预测值之间的差异)应用离散余弦变换(DCT),将空间域的数据转换到频率域。然后对变换系数进行量化,以减少数据量。量化过程会引入一些损失,这也是压缩的一部分。
  4. 熵编码:量化后的数据以及其他编码信息(如运动矢量、块类型等)使用熵编码技术(CAVLC或CABAC)进行压缩。此时的数据已经是VCL(Video Coding Layer)层的数据了。
  5. 将VCL层的数据封装成NAL单元。

VCL

视频编码层(Video Coding Layer):进行视频编解码,包括预测(帧内预测和帧间预测),DCT 变化和量化,熵编码和切分数据等功能,是为了实现更高的视频压缩比。

NAL

网络提取层(Network Abstraction Layer):负责以网络所要求的恰当的方式对 VCL 数据进行打包和传送。而NAL则是以NALU(NAL Unit)为单元来支持编码数据在基于包交换技术网络中传输的。

NALU

NALU通常以headerbody两个部分组成:

  • header:一般存储标志信息,譬如 NALU 的类型。 NAL 会打包 VCL 数据,但是这并不意味着所有的 NALU 负载的都是 VCL,也有一些 NALU 仅仅存储了和编解码信息相关的数据;
  • body:存储了真正的数据。但实际上,这块也会相对比较复杂,前面其实也提到过 H264 的一个目的是“网络友好性”,说白了就是能够很好地适配各种传输格式。所以根据实际采用数据传输流格式,也会对这部分数据格式再进行处理。

header(8bits = 1Bytes)组成通常为:

  • 1bit(forbidden_zero_bit):在网络传输中发生错误时,会被置为 1,告诉接收方丢掉该单元;否则为 0。
  • 2bits(nal_ref_idc):用于表示当前NALU的重要性,值越大,越重要。 解码器在解码处理不过来的时候,可以丢掉重要性为 0 的 NALU。
  • 5bits(nal_unit_type):表示 NALU 数据的类型。1-4(I/P/B帧):如果 nal_ref_idc 为 0,则表示 I 帧,不为 0 则为 P/B 帧;5(IDR帧):I 帧的一种;6(SEI)提供向视频码流中加入额外信息的方法:;7(SPS)8(PPS)9(AU 分隔符):AU 全称 Access Unit,它是一个或者多个 NALU 的集合,代表了一个完整的帧,有时候用于解码中的帧边界识别。

body

  • SODB(String Of Data Bits):原始数据比特流,就是最原始的编码/压缩得到的数据。
  • RBSP(Raw Byte Sequence Payload):原始字节序列载荷,与SODB关系如下:RBSP = SODB + RBSP Trailing Bits(RBSP尾部补齐字节)
  • EBSP(Encapsulated Byte Sequence Payload):扩展字节序列载荷,与RBSP关系如下:EBSP :RBSP插入防竞争字节(0x03)(可以先认为 H264 会插入一个叫做 StartCode 的字节串来分割 NALU,于是问题来了,如果 RBSP 中也包括了 StartCode(0x000001 或 0x00000001)怎么办呢?所以,就有了防止竞争字节(0x03):编码时,扫描 RBSP,如果遇到连续两个 0x00 字节,就在后面添加 防止竞争字节(0x03);解码时,同样扫描 EBSP,进行逆向操作即可。)