音视频基础(二)下:编码之h264解析①

音视频基础(二)下:编码之h264解析①

上篇我们分析了压缩编码的两种类型:无损压缩和有损压缩。并且介绍了使用ffmpeg将PCM编码成AAC的步骤以及可配置项。上篇文章由于篇幅以及能力问题,没有详细介绍AAC解码的过程。

上篇地址:juejin.cn/post/726478...

本篇将介绍H264这一编码格式的具体压缩编码过程,以及如何解码h264格式为y4m(即原始yuv数据)。

1 帧内压缩

帧内压缩,实际上就是图像压缩。H264对于图像压缩的步骤如下:

1.1 划分宏块和子块

宏块(MacroBlock)是一个像素组,在h264中为一个 16 * 16(或者 8 * 8 )的像素组。

划分好宏块之后,会计算宏块中的像素值,如果宏块中像素值差别较大,会划分子块。

子块的大小非常灵活,可以是(4,8,16) * (4,8,16)的任意结果,子块的作用就是让一个块中的像素值相对统一

如下面这个宏块,可以进一步划分为这样的子块:

1.2 帧内预测&残差计算

图片中像素的分布一般是有规律的,我们甚至可以用几种模式大致的匹配像素分布的样子,这就是帧内预测的原理。

对于h264来说,它有三种预测方式:

预测方式

1.对细节比较多的图片,也就是划分了子块的图片,进行 4*4的亮度块预测

预测实际上就是根据周围的亮度信息预测这个块中的亮度信息

2.对比较平坦的图片,也就是只有宏块的图片,进行 16 * 16的亮度快预测:

3.对于色度,预测方法同16*16亮度块预测中的4种,只不过块大小是8 * 8

预测模式编码

如果把所有块都一个一个进行预测,很明显计算量比较大,所以h264可以进行预测模式编码,简单的来说就是 根据周边块的预测模式决定当前块的预测模式

这样就可以减少预测计算量,同时使预测结果相对一致。

残差计算

很明显,虽然h264有众多的预测模式,但是现实中的图片肯定与预测模式多少有所差别:

所以我们还需要计算 残差:原始图像和预测图像的差别,这样我们就得到了残差图(residual picture)。在存储时,我们将残差图和预测模式信息存储起来,这样解码时就可还原成原图了。

Prediction Mode Info + Residual picture = Compression ≈ Original Picture

1.3 离散余弦变换(DCT)

我们得到了残差图,但是残差图实际上也是很大的一张图片,我们现在的压缩比还不是很高。

所以我们可以用离散余弦变换对残差图进行进一步处理。

DCT实际上是原始变换信号是实偶函数的离散傅里叶变换(DFT),作用就是在保存较好的频域能量聚集度的同时尽可能压缩图片,本文不做过多介绍,有兴趣请查看详解离散余弦变换(DCT)

处理结果如下:

1.4 量化

H.264采用标量量化技术,它将每个图像样点编码映射成较小的数值。一般标量量化器的原理为:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> F Q = r o u n d ( y / Q P ) F Q = r o u n d ( y / Q P ) </math>FQ=round(y/QP)

其中,y 为输入样本点编码,QP 为量化步长,FQ 为 y 的量化值,round()为取整函数(其输出为与输入实数最近的整数)

量化步长 QP 决定量化器的编码压缩率及图像精度。如果 QP 比较大,则量化值 FQ 动态范围较小,其相应的编码长度较小,数据压缩率高但会损失较多的图像细节信息;

简单的来说,量化就是进一步的压缩图片,虽然代价是进一步丢失图像细节信息

1.5 熵编码

前面量化得到的数据还可以进一步进行压缩,因为前面的压缩实际上都是 针对图片这一特质进行压缩的 ,无论是预测模式还是DCT变换,都是针对诸如亮度分布等信息进行压缩,我们还可以基于 数据自身的统计特征 再进行一次压缩,这就是 熵编码

实际上就是在信息熵极限范围内,想办法提高信息熵。由于没有突破信息熵极限,所以不会市镇,因而是无损压缩。

H264中使用的熵编码是算数编码,具体来说可能是

上下文自适应的变长编码(Context-based Adaptive Variable-Length Coding,CAVLC

或者

上下文自适应的二进制算术编码(Context-based Adaptive Binary Arithmetic Coding,CABAC

这类编码可以说是前文提到的哈夫曼编码的升级,这里同样不做具体介绍,有兴趣可以参考算术编码

在熵编码结束后,我们终于成功实现了帧内压缩!

我们可以简单的概括一下: 帧内压缩 = 帧内预测 + 变换 + 量化 + 熵编码

实际上帧间压缩只是换了预测的过程,后面的不变。这里为了行文完整把后面三步提到帧内压缩中介绍,下面帧间压缩不再重复

2 帧间压缩

2.1 分组

一般来说,视频的帧数都大于物体运动变化的速度。比方说在一个台球赛的30帧的视频中,很可能1秒钟内只有白球在每帧进行缓慢移动。其他都不变。

这意味着我们实际上可以在一定时间内只保留一个帧的数据,其他的帧根据这个帧进行计算,我们可以将这个时间范围内的帧以及其他计算结果称为一个 GOP(Group Of Pictures) 。那么怎么确定GOP的范围呢?H264的算法是:在相邻几幅图像画面中,一般有差别的像素只有10%以内的点,亮度差值变化不超过2%,而色度差值的变化只有1%以内,我们认为这样的图可以分到一组

在这样一组帧中,经过编码后,我们只保留第一帧的完整数据,其它帧都通过参考上一帧计算出来。这个完整帧被称为 I帧,而其他帧则是预测编码帧,不存储完整信息,需要根据别的帧计算。

2.2 定义帧

我们可以将GOP中的帧具体分为三种: I、P、B

I帧

I帧不参考其他图像帧,具有完整信息,同时是其他帧的参考帧,所以其质量直接影响后面的P和B帧的质量。在h264中,一个GOP只有一个I帧,它是GOP的第一帧,也可以称为IDRInstantaneous Decoder Refresh

I帧的编码就是上文所说的帧内编码,不过要加上重构图像并滤波这一步

P帧

P帧不存储完整信息,而是从I帧或者前面的P帧中找出P帧中某点的预测值和运动矢量,取预测差值和运动矢量。

P 帧编码的基本流程:

  • 进行运动估计,计算采用帧间编码模式的率失真函数值。P 帧只参考前面的帧;
  • 进行帧内预测,选取率失真函数值最小的帧内模式与帧间模式比较,确定采用哪种编码模式;
  • 计算实际值和预测值的差值;
  • 对残差进行变换和量化;
  • 若编码,如果是帧间编码模式,编码运动矢量。

因为P帧是通过预测的方式获得的,并且也可以作为P或者B帧的参考帧,所以可能导致错误的传递,造成最后较为严重的失真。

B帧

B帧同样不存储完整信息。与P帧的不同是:B帧既需要之前的图像帧(I 帧或 P 帧),也需要后来的图像帧(P 帧),采用运动预测的方式进行帧间双向预测编码。B代表着Bi-Directional,也就是双向的。

B帧的编码流程与P帧类似,只不过需要同时参考后面的帧。

2.3 预测帧

在2.2中我们提到了:P帧和B帧都是通过运动估计等方法存储信息的,下面我将介绍运动估计、运动补偿、率失真函数等概念。

运动估计

在分组这部分就已经介绍过:一系列帧中可能只有一小部分变化,或者说运动。运动估计就是去计算这部分运动,或者准确的说 运动矢量

H264编码器首先按顺序从缓冲区头部取出两帧视频数据,然后进行宏块扫描。当发现其中一幅图片中有物体时,就在另一幅图的邻近位置(搜索窗口中)进行搜索。如果此时在另一幅图中找到该物体,那么就可以计算出物体的运动矢量了。下面这幅图就是搜索后的台球移动的位置。

通过上图中台球位置相差,就可以计算出台图运行的方向和距离。H264依次把每一帧中球移动的距离和方向都记录下来就成了下面的样子。

运动补偿

简单的说:没有运动的部分就是补偿数据 。在上图中就是绿色的台球桌。我们将补偿数据压缩保存,再存储运动矢量,需要解码时就可以将二者组合回复原图:

率失真函数

有损压缩算法,性能由编码输出的比特率失真共同决定。编码的目的就是在保证一定视频质量的条件下尽量减少编码比特率,或在一定编码比特率限制条件下尽量地减小编码失真。

率失真函数就是平衡输出比特率和失真两者,让它们处于可接受的平衡。由于两者成反比,所以它是一个反比例函数

所谓的"进行帧内预测,选取率失真函数值最小的帧内模式与帧间模式比较,确定采用哪种编码模式",就是指对P帧也进行1中所说的帧内压缩,与P帧的基于参考帧的帧间预测比较,看看哪个模式率失真值越小就选择哪个。

下篇内容

本来打算在这篇中一起将H264的具体码流格式等介绍了,但是文章已经比较长了,再添加内容重点就不清晰了。所以这部分会放在下张一起讲。这样下章就会有比较充足的时间实现js解析h264的方法。

参考

  1. H264基本原理
  2. H264编码--帧内预测
  3. 详解离散余弦变换(DCT)
  4. 视频编码(1):可能是最详尽的 H.264 编码相关概念介绍丨音视频基础
相关推荐
x007xyz1 个月前
前端纯手工绘制音频波形图
前端·音视频开发·canvas
音视频牛哥1 个月前
Android摄像头采集选Camera1还是Camera2?
音视频开发·视频编码·直播
九酒2 个月前
【harmonyOS NEXT 下的前端开发者】WAV音频编码实现
前端·harmonyos·音视频开发
音视频牛哥2 个月前
结合GB/T28181规范探讨Android平台设备接入模块心跳实现
音视频开发·视频编码·直播
哔哩哔哩技术2 个月前
自研点直播转码核心
音视频开发
音视频牛哥2 个月前
Android平台轻量级RTSP服务模块二次封装版调用说明
音视频开发·视频编码·直播
音视频牛哥2 个月前
Android平台RTSP|RTMP直播播放器技术接入说明
音视频开发·视频编码·直播
山雨楼2 个月前
ExoPlayer架构详解与源码分析(15)——Renderer
android·架构·音视频开发
音视频牛哥2 个月前
Windows平台如何实现多路RTSP|RTMP流合成后录像或转发RTMP服务
音视频开发·视频编码·直播
音视频牛哥2 个月前
GB28181设备接入模块和轻量级RTSP服务有什么区别?
音视频开发·视频编码·直播