音视频6.1—— H264编码基础概念

音视频开发路线:

Android 音视频开发入门指南_Jhuster的专栏的技术博客_51CTO博客_android 音视频开发入门

demo地址:

GitHub - wygsqsj/videoPath: 音视频学习路线demo

H264编码

我们通常看到得视频格式,比如mp4,avi,是将音频和视频打包成一个文件,这种指的是封装格式,而H264则是视频的编码格式;视频之所以要进行编码是因为,我们得视频是由一帧接一帧得画面构建而成,一帧画面我们可以看作由像素所构成的图片,假设每个像素占3个字节,这一帧画面的宽高为,1080*1920,那么这一帧画面的大小为:3*1080*1920,那么一帧画面有将近6M,我们的电影一般为每秒24帧,这一部电影算下来要占700多G,这肯定不符合我们的需求,而H264就是用来对视频进行压缩的一种编码方式

宏块

H264将一帧画面划分为不同的块,比如说4*4,16*16大小,具体的划分规则由算法本身去计算,比如块的颜色相近就会划分大一点,颜色丰富的时候就划分的小一点,而划分的这些块,只需要记住他的左侧和右侧像素,再通过算法得到预测的方向,根据这个预测方向去还原原本的颜色信息,这样本来我们需要记录 4*4个像素,现在我们只需要记住4+4-1个像素和预测方向即可,大大减少了需要记录的像素信息:

YUV数据

我们平常看的jpg和rng图片的编码格式为rgb或者grgb,通过红绿蓝三种颜色交替得到所有的颜色值,这种方式每种颜色都要占一个字节和采集通道,占用的字节太多,而我们人眼对色值要求不高,对亮度要求很高,所以同样的画面,我们减少他的色值采集,再补全亮度,这样既可以减少存储大小又不失真,这就是YUV编码格式,我们摄像头采集的数据转换出来的格式即YUV数据,其中Y表示亮度,UV表示的是色值,他们根据比例可划分为YUV4:4:4,YUV4:2:2,YUV4:2:0

空心表示UV值,实心圆表示Y值,其中YUV420是安卓摄像头常用的采样格式

根据交错顺序的不同又分为:

我们MediaCodec能进行操作的格式为NV12,摄像头采集的格式一般为NV21或者YV12,需要转换一下再给MediaCodec进行编码。 

I帧 P帧 B帧

我们可以吧一帧理解为一副画面

I帧 我们视频文件的第一帧肯定是I帧,这种类型的帧包含了最多的信息,例如宽高,编码格式,分辨率,最重要的宏块的信息也在I帧中,所以I帧的占的字节最多,他是P帧和B帧的基础

P帧 依托于I帧存在,与I帧的差异度为30%,在编码顺序里是第二帧,但是播放顺序不是,我们可以这么理解,一个画面是在运动的,I帧是运动开始的画面,当运动到一定程度差异逐渐扩大,P帧就产生了,他大小为中间

B帧 而B帧则是在I和P帧之间的帧,与I帧差异度为5%,他在播放顺序里面是安装 IBBP的顺序进行播放的,但是编码顺序不是,I帧编码出来之后,对外输出,后续画面数据进来编码,这时候画面运动幅度较小,编码出来的是B帧,我们的编码器会将B帧暂存,一直到P帧出现,即画面有了更多的差异时,当P帧出现了再把缓存的B帧拿出来,放在P帧后面进行输出,即编码顺序为:I P B B,而视频的播放顺序为 I B B P,B帧所占的字节最少,而且B帧不一定会出现,有可能视频里面是由I和P帧构成

B帧的出现与运动矢量有关,I帧已经记录了所有的宏块信息,后续的画面我们就没有必要再记录这些重复的宏块信息了,这样会节省很大的资源,比如有一颗树在I帧里面已经记录下所有的宏块信息了,那么B帧只需要记录下这棵树的运动方向,即这一堆宏块的运动方向,在B帧里面,将这些宏块按方向移动一定的位置即可,那么我在B帧里面只需要记录下宏块的序号运动方向和长度,这个有方向的长度就是运动矢量,有了运动矢量,将会大大减少

GOP

两个I帧之间可以理解为一个场景,这个场景就叫做GOP,每个GOP只会含有一个I帧,我们的短视频GOP会比较长,因为要减少文件的大小,I帧越少,代表我的编码效率越高,因为我P帧和B帧就会越多,同样的画面更省空间;但在直播中,I帧要越多越好,因为用户可能在任何一个时刻进来,如果他拿不到I帧,就拿不到对应的那个场景的起点,没办法渲染画面,所以直播中要把I帧搞得多多的,再从分辨率啊,帧速率啊,编码格式等方面降低直播流的大小

NALU和VCL

NALU 值得是传输层面的一个但与,NALU包含NAL头和编码信息,编码信息又叫做VCL,VCL中存放的是我们的编码信息,可以理解为为了网络传输对VCL进行了一次封装,形成NALU,类似于我们的网络协议层,每个层级只编/解对应的数据。

这其中我们最原始的数据成为SODB,对原始数据进行一次对齐,也就是每个字节8位,如果原始数据不足8位,后面补对应的尾部(0或1的组合),这样方便后续的编码,对齐数据称作RBSP。每个NALU之间需要分隔符,这个分隔符为0x000001或者0x00000001,如果我们的RBSP里面也出现这个分隔符编码,h264会将像素信息里面的分隔符插入0x03,即:

而对RBSP里的数据进行了与分隔符冲突处理后的数据称为EBSP,可以理解为 NALU就是由 NALU的头和EBSP构成。

NALU的类型

每个NALU以0x000001或0x0001分割,后面的8位中包含当前NALU的类型,例如

0000 0001 0110 0111

0000 0001 分隔符

0 是禁止位:0 无错误 1错误数据,可以丢弃这个NALU

11 这两位表示重要性,取值位 00 01 10 11 也就是0-3,越高表示这个NALU越重要

00111 这五位用于表示NALU类型,共有32种类型,正好5位可以表示;00111--->十进制 7表示SPS,也就是视频的各项配置信息

常见的类型有 7:SPS;8:PPS;5:I帧

0:未定义
1:非IDR图像不采用数据划分片段
2:非IDR图像采用数据划分片段A部分
3:非IDR图像采用数据划分片段B部分
4:非IDR图像采用数据划分片段C部分
5:IDR图像片段
6:补充增强信息 SEI
7:序列参数集 SPS
8:图像参数集 PPS
9:分隔符
10:序列结束符
11:流结束符
12:填充数据
13:序列参数集扩展
14:带前缀的NALU
15:子序列参数集
16 - 18:保留
19:不采用数据划分的辅助编码图像片段
20:编码片段扩展
21 - 23:保留
24 - 31:未定义

哥伦布编码

先来了解一下定长编码和变长编码,在我的理解看来定长编码就是规定好每一截所代表的意义,我们代码中是以字节为单位去读取数据的,一般我们都是定义一个byte[] 数组,从IO中获取数据,一个字节由8位,  0000 0010  ,定长编码即以 8位 为步长来定义,他最多可以用来表示256(0到255)个类型,但是如果我需要32个类型怎么办,我不需要一整个字节的8位,这样会造成浪费,我只需要5位即可表示,使用定长编码势必会造成3位的浪费,在H264中为了节省大小,采用了定长编码和变长编码混合的方式。

编编码方式通过长度+内容的方式进行编码,用1作为分割,1前面的0代表长度,表示从1开始往后读几位,前面的0的位数根据要记录的数据规定,原数据规定需要+1之后再进行补0的操作,例如元数据十进制为3--->101,3+1=4 --->二进制为100,1后面有2位,所以要在1前面补两个0,变长00100,读取的时候,读到1了,记录下0的个数,即2个0,从1开始读2位,得到100-->十进制的4,再减去1,得到3--->101

参考文章:

NALU详解之EBSP、RBSP与SODB - 知乎

       

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值