H264编码基础概念+格式分析

 

一、编码基础概念

1、为什么要进行视频编码?

视频是由一帧帧图像组成,就如常见的gif图片,如果打开一张gif图片,可以发现里面是由很多张图片组成。一般视频为了不让观众感觉到卡顿,一秒钟至少需要16帧画面(一般是30帧),假如该视频是一个1280x720分辨率的视频,那么不经过编码一秒钟的大小:
结果:1280x720x60≈843.75M

所以不经过编码的视频根本没法保存,更不用说传输了。

2、视频压缩编码标准

视频中存在很多冗余信息,比如图像相邻像素之间有较强的相关性,视频序列的相邻图像之间内容相似,人的视觉系统对某些细节不敏感等,对这部分冗余信息进行处理的过程就是视频编码。

H.26X系列(由ITU[国际电传视讯联盟]主导)
    H.261:主要在老的视频会议和视频电话产品中使用
    H.263:主要用在视频会议、视频电话和网络视频上
    H.264:H.264/MPEG-4第十部分,或称AVC(Advanced Video Coding,高级视频编码),是一种视频压缩标准,一种被广泛使用的高精度视频的录制、压缩和发布格式。
    H.265:高效率视频编码(High Efficiency Video Coding,简称HEVC)是一种视频压缩标准,H.264/MPEG-4 AVC的继任者。可支持4K分辨率甚至到超高画质电视,最高分辨率可达到8192×4320(8K分辨率),这是目前发展的趋势,尚未有大众化编码软件出现
    
MPEG系列(由ISO[国际标准组织机构]下属的MPEG[运动图象专家组]开发)
    MPEG-1第二部分:MPEG-1第二部分主要使用在VCD上,有些在线视频也使用这种格式
    MPEG-2第二部分(MPEG-2第二部分等同于H.262,使用在DVD、SVCD和大多数数字视频广播系统中
    MPEG-4第二部分(MPEG-4第二部分标准可以使用在网络传输、广播和媒体存储上
    

3、编码流程
在进行当前信号编码时,编码器首先会产生对当前信号做预测的信号,称作预测信号(predicted signal)

预测的方式:

    时间上的预测(interprediction),亦即使用先前帧的信号做预测
    空间上的预测 (intra prediction),亦即使用同一张帧之中相邻像素的信号做预测

得到预测信号后,编码器会将当前信号与预测信号相减得到残余信号(residual signal),并只对残余信号进行编码,如此一来,可以去除一部份时间上或是空间上的冗余信息。

编码器并不会直接对残余信号进行编码,而是先将残余信号经过变换(通常为离散余弦变换)然后量化以进一步去除空间上和感知上的冗余信息。量化后得到的量化系数会再透过熵编码,去除统计上的冗余信息。

二、H.264编码详解(AVC)

1、H.264是新一代的编码标准,以高压缩高质量和支持多种网络的流媒体传输著称

相关理解:
    在相邻几幅图像画面中,一般有差别的像素只有10%以内的点,亮度差值变化不超过2%,而色度差值的变化只有1%以内
    所以对于一段变化不大图像画面,我们可以先编码出一个完整的图像帧A,随后的B帧就不编码全部图像,只写入与A帧的差别,这样B帧的大小就只有完整帧的1/10或更小!
    B帧之后的C帧如果变化不大,我们可以继续以参考B的方式编码C帧,这样循环下去。
    这段图像我们称为一个序列:序列就是有相同特点的一段数据
    当某个图像与之前的图像变化很大,无法参考前面的帧来生成,那我们就结束上一个序列,开始下一段序列
    也就是对这个图像生成一个完整帧A1,随后的图像就参考A1生成,只写入与A1的差别内容
    

2、H.264三种帧

在H.264中定义了三种帧:
    I帧:完整编码的帧叫I帧
    P帧:参考之前的I帧生成的只包含差异部分编码的帧叫P帧
    B帧:参考前后的帧编码的帧叫B帧

H264采用的核心算法是帧内压缩和帧间压缩:
    帧内压缩是生成I帧的算法
    帧间压缩是生成B帧和P帧的算法

压缩方法:
    分组:把几帧图像分为一组(GOP,也就是一个序列),为防止运动变化,帧数不宜取多
    定义帧:将每组内各帧图像定义为三种类型,即I帧、B帧和P帧;
    预测帧:以I帧做为基础帧,以I帧预测P帧,再由I帧和P帧预测B帧;
    数据传输:最后将I帧数据与预测的差值信息进行存储和传输。

GOP序列:
    在H264中图像以序列为单位进行组织,一个序列是一段图像编码后的数据流。
    一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像:

a、H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。
b、这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。
c、IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。

    一个序列就是一段内容差异不太大的图像编码后生成的一串数据流:

a、当运动变化比较少时,一个序列可以很长,因为运动变化少就代表图像画面的内容变动很小,所以就可以编一个I帧,然后一直P帧、B帧了。
b、当运动变化多时,可能一个序列就比较短了,比如就包含一个I帧和3、4个P帧。

在视频编码序列中,GOP即Group of picture(图像组),指两个I帧之间的距离。

I帧、P帧、B帧实际顺序&&编码顺序:
H264编码基础概念+格式分析_第1张图片
H264编码基础概念+格式分析_第2张图片

三、H.264分层设计

H264算法在概念上分为两层:
    VCL:(Video Coding Layer)视频编码层,负责高效的内容表示。
    NAL:(Network Abstraction Layer)网络提取层,负责以网络所要求的恰当的方式对数据进行打包和传送。
    上面所学习的知识都是VCL层。

NAL设计的目的,是根据不同的网络把数据打包成相应的格式,将VCL产生的比特字符串适配到各种各样的网络和多元环境中。

NAL的封装方式:
    NAL是将每一帧数据写入到一个NAL单元中,进行传输或存储的
    NALU分为NAL头和NAL体
    NALU头通常为00 00 00 01,作为一个新的NALU的起始标识
    NALU体封装着VCL编码后的信息或者其他信息

NAL的封装过程:
    I帧、P帧、B帧都是被封装成一个或者多个NALU进行传输或者存储的
    每一个I帧开始之前也有非VCL的NAL单元,用于保存其他信息,它们是PPS、SPS
    PPS(Picture Parameter Sets):图像参数集
    SPS(Sequence Parameter Set):序列参数集
    在实际的H264数据帧中,往往帧前面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧,后续是B帧、P帧等数据
H264编码基础概念+格式分析_第3张图片

 

 

 

 

 

 

 

 

 

 

 

引言:
在国内直播"欣欣向荣"(ps: 其实大多都亏钱,为的就是炒概念)的年代,相信很多小伙伴也投入了技术的浩瀚大洋当中(ps: 其实就是搬砖),日复一日,音/视频的神秘面纱开始让更多的小伙伴扯下,而本博主,也只是刚窥探门道,慢慢摸索。好了,废话不扯,我们今天就来说说我们经常在视频编码当中用到的 H.264编码格式 的结构,相信 H.264 这个东西很多小伙伴都不陌生了,也有着自己的理解,但这东西颇为巨大,里面算法千千万万,博主也不会讲太高深的东西,只是让各位小伙伴慢慢理解理解 H.264的主体机构,如果没有兴趣的小伙伴请绕道,如果有技术大牛,请指正本博主那愚钝的脑袋。

首先来一段大家都熟悉的官方话来介绍一下 H.264

H.264: H.264/AVC项目的目的是为了创建一个比以前的视频压缩标准,在更低的比特率的情况下依然能够提供良好视频质量的标准(如,一半或者更少于MPEG-2,H.263,或者MPEG-4 Part2 )。同时,还要不会太大的增加设计的复杂性。
优势:
1)网络亲和性,即可适用于各种传输网络
2)高的视频压缩比,当初提出的指标是比 H.263,MPEG-4,约为它们的 2 倍,现在都已基 实现;

那么很明显,什么时候需要到压缩呢?当然是文件体积太大的时候啦,我们想想,所谓的视频,就是像小时候的连环画一样,在一秒内翻过 24 张以上的图片,就感觉图像是连续的了,这就是视频的原理。但是大家有没有想过,一张图片有多大呢?我们的屏幕分辨率按 1280 * 720 算的话,一秒钟的视频大概就 2.64 MB 了,大家想想,我们大部分的小伙伴为了下载个小嗨片省吃俭用才开了个 1M 的网线,然后连个直播都看不了是什么感觉。那肯定不能这样了,所以我们要进行压缩,而 H.264 不仅压缩比比较高,对网络的兼容性也非常好,所以大多数人做直播也就选择了 H.264 作为编码格式了。

编码流程:
那么 H.264 其编解码流程是怎么样的呢?其实可以主要分为 5 部分: 帧间和帧内预测(Estimation)、变换(Transform)和反变换、量化(Quantization)和反量化、环路滤波(Loop Filter)、熵编码(Entropy Coding)。
看起来很高深的样子,实际上也是很高深的样子,因为这里面包含着许许多多的算法和专业知识,这里我们就不做过多的讲解,有兴趣的同学可以上网翻翻,够你看到睡觉的了。H.264详细文档


原理简介

H.264 原始码流(又称为裸流),是有一个接一个的 NALU 组成的,而它的功能分为两层:视频编码层(VCL, Video Coding Layer)和网络提取层(NAL, Network Abstraction Layer)。
VCL 数据即编码处理的输出,它表示被压缩编码后的视频数据 序列。在 VCL 数据传输或存储之前,这些编码的 VCL 数据,先被映射或封装进 NAL 单元(以下简称 NALU,Nal Unit) 中。每个 NALU 包括一个原始字节序列负荷(RBSP, Raw Byte Sequence Payload)、一组 对应于视频编码的 NALU 头部信息。RBSP 的基本结构是:在原始编码数据的后面填加了结尾 比特。一个 bit“1”若干比特“0”,以便字节对齐。

NAL 单元排列

上图中的 NALU头 + RBSP 就相当与一个 NALU (Nal Unit), 每个单元都按独立的 NALU 传送。 其实说白了,H.264 中的结构全部都是以 NALU 为主的,理解了 NALU,就理解 H.264 的结构了。


一帧图片跟 NALU 的关联 :

究竟 NALU 是怎么由一帧图片变化而来的呀,H.264究竟为什么这么神奇?

一帧图片经过 H.264 编码器之后,就被编码为一个或多个片(slice),而装载着这些片(slice)的载体,就是 NALU 了,我们可以来看看 NALU 跟片的关系(slice)。

H264编码基础概念+格式分析_第4张图片

图片编码后

H264编码基础概念+格式分析_第5张图片

NALU 结构

小伙伴们要明白,片(slice)的概念不同与帧(frame),帧(frame)是用作描述一张图片的,一帧(frame)对应一张图片,而片(slice),是 H.264 中提出的新概念,是通过编码图片后切分通过高效的方式整合出来的概念,一张图片至少有一个或多个片(slice)。

上图中可以看出,片(slice)都是又 NALU 装载并进行网络传输的,但是这并不代表 NALU 内就一定是切片,这是充分不必要条件,因为 NALU 还有可能装载着其他用作描述视频的信息。


什么是切片(slice)?

片的主要作用是用作宏块(Macroblock)的载体(ps:下面会介绍到宏块的概念)。片之所以被创造出来,主要目的是为限制误码的扩散和传输。
如何限制误码的扩散和传输?
每个片(slice)都应该是互相独立被传输的,某片的预测(片(slice)内预测和片(slice)间预测)不能以其它片中的宏块(Macroblock)为参考图像。

那么片(slice)的具体结构,我们用一张图来直观说明吧:

H264编码基础概念+格式分析_第6张图片

我们可以理解为一 张/帧 图片可以包含一个或多个分片(Slice),而每一个分片(Slice)包含整数个宏块(Macroblock),即每片(slice)至少一个 宏块(Macroblock),最多时每片包 整个图像的宏块。

上图结构中,我们不难看出,每个分片也包含着头和数据两部分:
1、分片头中包含着分片类型、分片中的宏块类型、分片帧的数量、分片属于那个图像以及对应的帧的设置和参数等信息。
2、分片数据中则是宏块,这里就是我们要找的存储像素数据的地方。

什么是宏块?

宏块是视频信息的主要承载者,因为它包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中的像素阵列。
组成部分:一个宏块由一个16×16亮度像素和附加的一个8×8 Cb和一个 8×8 Cr 彩色像素块组成。每个图象中,若干宏块被排列成片的形式。

我们先来看看宏块的结构图:

H264编码基础概念+格式分析_第7张图片

从上图中,可以看到,宏块中包含了宏块类型、预测类型、Coded Block Pattern、Quantization Parameter、像素的亮度和色度数据集等等信息。


切片(slice)类型跟宏块类型的关系

对于切片(slice)来讲,分为以下几种类型:

0 P-slice. Consists of P-macroblocks (each macro block is predicted using one reference frame) and / or I-macroblocks.
1 B-slice. Consists of B-macroblocks (each macroblock is predicted using one or two reference frames) and / or I-macroblocks.
2 I-slice. Contains only I-macroblocks. Each macroblock is predicted from previously coded blocks of the same slice.
3 SP-slice. Consists of P and / or I-macroblocks and lets you switch between encoded streams.
4 SI-slice. It consists of a special type of SI-macroblocks and lets you switch between encoded streams.

I片:只包 I宏块,I 宏块利用从当前片中已解码的像素作为参考进行帧内预测(不能取其它片中的已解码像素作为参考进行帧内预测)。

P片:可包 P和I宏块,P 宏块利用前面已编码图象作为参考图象进行帧内预测,一个帧内编码的宏块可进一步作宏块的分割:即 16×16、16×8、8×16 或 8×8 亮度像素块(以及附带的彩色像素);如果选了 8×8 的子宏块,则可再分成各种子宏块的分割,其尺寸为 8×8、8×4、4×8 或 4×4 亮度像素块(以及附带的彩色像素)。

B片:可包 B和I宏块,B 宏块则利用双向的参考图象(当前和 来的已编码图象帧)进行帧内预测。

SP片(切换P):用于不同编码流之间的切换,包含 P 和/或 I 宏块

SI片:扩展档次中必须具有的切换,它包 了一种特殊类型的编码宏块,叫做 SI 宏块,SI 也是扩展档次中的必备功能。


整体结构
通过剖析了这么多个小零件,是时候个大家一个世界地图了,
那么我们的 NALU 整体结构可以呼之欲出了,以下就引用 H.264 文档当中的一幅图了

H264编码基础概念+格式分析_第8张图片


其实 H.264 的码流结构并没有大家想的那么复杂,编码后视频的每一组图像(GOP,图像组)都给予了传输中的序列(PPS)和本身这个帧的图像参数(SPS),所以,我们的整体结构,应该如此:

H264编码基础概念+格式分析_第9张图片

GOP (图像组)主要用作形容一个 i 帧 到下一个 i 帧之间的间隔了多少个帧,增大图片组能有效的减少编码后的视频体积,但是也会降低视频质量,至于怎么取舍,得看需求了。


主题外:(未完待续)

那么,NALU 头部中的类型确定着什么信息呢?
我们首先来看看 NALU 中究竟有哪几种类型,我们来看看 H.264 中源码对 nal_unit_type_e 中的定义:

enum nal_unit_type_e
{
NAL_UNKNOWN = 0, // 未使用
NAL_SLICE = 1, // 不分区、非 IDR 图像的片(片的头信息和数据)
NAL_SLICE_DPA = 2, // 片分区 A
NAL_SLICE_DPB = 3, // 片分区 B
NAL_SLICE_DPC = 4, // 片分区 C
NAL_SLICE_IDR = 5, /* ref_idc != 0 / // IDR 图像中的片
NAL_SEI = 6, /
ref_idc == 0 */ // 补充增强信息单元

  •  

参数集是 H.264 标准的一个新概念,是一种通过改进视频码流结构增强错误恢复能力的方法。
NAL_SPS = 7, // 序列参数集 (包括一个图像序列的所有信息,即两个 IDR 图像间的所有图像信息,如图像尺寸、视频格式等)
NAL_PPS = 8, // 图像参数集 (包括一个图像的所有分片的所有相关信息, 包括图像类型、序列号等,解码时某些序列号的丢失可用来检验信息包的丢失与否)

  • NAL_AUD = 9, // 分界符
    NAL_FILLER = 12, // 填充(哑元数据,用于填充字节)
    /* ref_idc == 0 for 6,9, 10 (表明下一图像为 IDR 图像),11(表明该流中已没有图像),12 */
    };
    ps: 以上括号()中的为类型描述

上面NALU类型当中,分片/切片(slice)的概念我们都已经很清楚了,但是用 NALU 作载体的还有 SEI、SPS、PPS 等等。

今天我们不一一聚述这些类型对整个流程的作用了,我们挑出两个符合我们今天主题的类型来讲,PPS 和 SPS。

H264编码基础概念+格式分析_第10张图片

H264编码基础概念+格式分析_第11张图片


那么今天我们讲的 H.264 的码流结构相信大家都有个大概轮廓的了解了,总结的一句话就是:

H.264 中,句法元素共被组织成 序列、图像、片、宏块、子宏块五个层次。

希望大家用心体会,毕竟手工打字和作图不易,大家能关注的关注,能有闲钱的打赏一个,能有赞的赞一下嘛


最后福利:
文章的最后为了方便小伙伴学习,本博主用 ffmpeg 在 iOS 设备写了一个实例代码,主要功能是利用摄像头实时录制 yuv 格式文件并编码 H.264 后,实时分析 H.264 中每个帧(frame)中的 NALU 的数据,希望对小伙伴们有所帮助,找到入门的方法。
(本博主将所有代码只放在个人博客末尾当中: 代码地址『所有代码只用做学习只用,并不提供商业用途,如果违反,必究。』)



作者:Abson在简书
链接:https://www.jianshu.com/p/9522c4a7818d
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

你可能感兴趣的:(嵌入式)