在计算机中视频是由一帧一帧的图片组成,图片由像素组成。一个像素由红绿蓝与透明度四个数值表示。如果真的直接用一张张图片来合成视频那么一个十几秒的视频可能就需要几个G。为此我们需要一套能高效压缩图片的算法,来精简视频的大小。
因为视频流中一秒钟可能就有很多张图片,例如帧率为30的视频,一秒钟就是30张图片。而这三十张图片的差异在普遍都很小,所以我们有以下两种主流的算法做图片的帧内压缩和帧间压缩。
h264 又名:avc;MPEG-4 AVC
h265 又名:hevc;
图片压缩算法YUV
在我们不考虑透明度的情况下,屏幕现在一个画面时,每一个像素必须由红绿蓝三种颜色的混合而成。那么一个1920*1080的图片我们需要记录的数据为:1920*1080*3 = 6220800 B。这能不能压缩呢?
经过科学研究人们发现人眼睛对图像的亮度感知比对颜色的感知强四倍以上。那么我们经过我们的改造一个图片可以以以下格式存储(YUV);
Y代表亮度
U与V存储色度
那么此时一个2*2像素的图片以RGB的存储方式为:
红,绿,蓝 | 红,绿,蓝 |
红,绿,蓝 | 红,绿,蓝 |
一共需要12个byte存储。
转成yuv420格式为
Y | Y |
Y | Y |
U | V |
仅需要6个byte就能存储。而且画面的质量损失不大。PS:yuv格式存储的图片一定会有质量损失。区别就是损失有多大。
YUV格式有两种子分类
第一种分类是质量分类,有以下三种质量:
YUV420
这是4个像素共用一对uv
YUV422
这是4个像素共用两对uv,就是2个像素用一对uv
YUV444
这是4个像素共用四对uv,就是1个像素用一对uv
第二种分类是存储分类,有以下三种存储方案
以4*4大小图片做演示
NV21(COLOR_FormatYUV420SemiPlanar)
[YYYYYYYYYYYYYYYYVUVUVUVU]
NV12(COLOR_FormatYUV420PackedSemiPlanar)
[YYYYYYYYYYYYYYYYUVUVUVUV]
YV12(I420)(COLOR_FormatYUV420Planar)
[YYYYYYYYYYYYYYYYVVVVUUUU]
YV21
[YYYYYYYYYYYYYYYYUUUUVVVV]
视频压缩算法(h264)
视频压缩算法我们以h264举例。h265与h264的原理差别不大,h265提高了压缩的比例,支持更大更清晰的视频如4k,3d视频等格式的支持。
视频流中是由一帧一帧的图片组成,而我们的h264算法有两种方案去压缩图片。一个是帧内压缩,一个是帧间压缩。
帧内压缩
是指一帧图片的压缩算法。主要是通过宏块来实现。一个宏块是多个像素组成,最小由4*4个像素,最大是16*16个像素。具体怎么划分块大小,要看画面有多复杂。一般来说,运动多,细节多的部分,划分成小块来编码;大片的平坦的无变化的,划分成16*16的大块。
而划分成宏块后我们可以通过方向预测的方式来存储一个宏块数据
例如:
10 | 10 | 10 | 10 |
11 | 11 | 11 | 11 |
12 | 12 | 12 | 12 |
13 | 13 | 13 | 13 |
这样的一个宏块我们可以压缩为:
【10】【10】【10】【10】,【方向向下】【步长1】【高度4】
帧间压缩
帧间压缩将帧分为了不同的类型,我们这里主要说一下三种类型:
I帧(帧内编码)
I帧包含了这一帧内的完整数据。可以直接转换成对于的图片格式数据。
P帧(向前预测编码)
必须依赖I帧才能解析出图片
B帧(向前向后双向预测编码)
必须依赖I帧和P帧才能解析出图片
为什么需要这三种帧类型呢?因为视频中的图像往往是连续的图片如以下数据:
每一个单元格代表一个宏块。背景是纯白的0。有一个黑点1,经过四帧的画面后移动左上角移动到右下角。
未压缩情况下:
第一帧:
1 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
第二帧:
0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
第三帧:
0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 0 | 0 | 0 |
第四帧:
0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 0 | 1 |
经过H264编码后:
第一帧(I帧):
1 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
第二帧(B帧):
宏块【1,1】移动进度33%
第三帧(B帧):
宏块【1,1】移动进度66%
第四帧(P帧):
宏块【1,1】移动到【4,4】方向右下
这种方式我们只需要记录第一帧的完整数据即可。第二帧第三帧通过第一帧和第四帧数据就可以计算出来。第四帧的数据通过第一帧的数据也可以计算出来。通过算法就可以将编码后的数据恢复出来。在h264中这样的一组数据称为:GOP。
如果视频的画面差距不大那么理论上我们可以使用一组GOP数据来标识一个视频,但是这需要看我们的使用场景:例如直播场景。如果只有一个I帧后续都是P帧那么后续进来的用户会因为没有I帧完全无法还原出图片数据导致一直黑屏。