H264, H265硬件编解码基础及码流分析 - BranPeng/FFmpeg GitHub Wiki

本文转自:https://juejin.im/post/5ce9f36bf265da1bbd4b5084

需求

在移动端做音视频开发不同于基本的UI业务逻辑工作,音视频开发需要你懂得音视频中一些基本概念,针对编解码而言,我们必须提前懂得编解码器的一些特性,码流的结构,码流中一些重要信息如sps,pps,vps,start code以及基本的工作原理,而大多同学都只是一知半解,所以导致代码中的部分内容虽可以简单理解却不知其意,所以,在这里总结出了当前主流的H.264,H.265编码相关的原理,以供学习.

1. 概览

1.1. 为什么要编码

众所周知,视频数据原始体积是巨大的,以720P 30fps的视频为例,一个像素大约3个字节,如下所得,每秒钟产生87MB,这样计算可得一分钟就将产生5.22GB.

数据量/每秒=1280*720*33*3/1024/1024=87MB

因此,像这样体积重大的视频是无法在网络中直接传输的.而视频编码技术也就因运而生.

1.2. 编码技术

经过很多年的开发迭代,已经有很多大牛实现了视频编码技术,其中最主流的有H.264编码,以及新一代的H.265编码,谷歌也开发了VP8,VP9编码技术.对移动端而言,苹果内部已经实现了如H.264,H.265编码,我们需要使用苹果提供的VideoToolbox框架来实现它.

1.3. 编码分类

  • 软件编码(简称软编):使用CPU进行编码。
  • 硬件编码(简称硬编):不使用CPU进行编码,使用显卡GPU,专用的DSP、FPGA、ASIC芯片等硬件进行编码。

优缺点

  • 软编:实现直接、简单,参数调整方便,升级易,但CPU负载重,性能较硬编码低,低码率下质量通常比硬编码要好一点。

  • 硬编:性能高,低码率下通常质量低于硬编码器,但部分产品在GPU硬件平台移植了优秀的软编码算法(如X264)的,质量基本等同于软编码。

iOS系统中的硬编码
苹果在iOS 8.0系统之前,没有开放系统的硬件编码解码功能,不过Mac OS系统一直有,被称为Video ToolBox的框架来处理硬件的编码和解码,终于在iOS 8.0后,苹果将该框架引入iOS系统。

1.4. 编码原理

对视频执行编码操作后,原始视频数据会被压缩成三种不同类型的视频帧: I帧,P帧,B帧.

  • I帧:关键帧.完整编码的帧.可以理解成是一张完整画面,不依赖其他帧
  • P帧:参考前面的I帧或P帧,即通过前面的I帧与自己记录的不同的部分可以形成完整的画面.因此,单独的P帧无法形成画面.
  • B帧:参考前面的I帧或P帧以及后面的P帧
补充: I帧的压缩率是7(跟JPG差不多),P帧是20,B帧可以达到50. 但是iOS中一般不开启B帧,因为B帧的存在会导致时间戳同步较为复杂.

两种核心算法

  1. 帧内压缩

当压缩一帧图像时,仅考虑本帧的数据而不考虑相邻帧之间的冗余信息,这实际上与静态图像压缩类似。帧内一般采用有损压缩算法,由于帧内压缩是编码一个完整的图像,所以可以独立的解码、显示。帧内压缩一般达不到很高的压缩,跟编码jpeg差不多。

如下图:我们可以通过第 1、2、3、4、5 块的编码来推测和计算第 6 块的编码,因此就不需要对第 6 块进行编码了,从而压缩了第 6 块,节省了空间

  1. 帧间压缩: P帧与B帧的压缩算法

相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小的特点。也即连续的视频其相邻帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步提高压缩量,减小压缩比。帧间压缩也称为时间压缩(Temporal compression),它通过比较时间轴上不同帧之间的数据进行压缩。帧间压缩一般是无损的。帧差值(Frame differencing)算法是一种典型的时间压缩法,它通过比较本帧与相邻帧之间的差异,仅记录本帧与其相邻帧的差值,这样可以大大减少数据量。

如下图:可以看到前后两帧的差异其实是很小的,这时候用帧间压缩就很有意义。

有损压缩与无损压缩

  • 有损压缩: 解压缩后的数据与压缩前的数据不一致.在压缩的过程中要丢失一些人眼和人耳所不敏感的图像或音频信息,而且丢失的信息不可恢复
  • 无损压缩: 压缩前和解压缩后的数据完全一致.优化数据的排列等.

DTS和PTS

  • DTS:主要用于视频的解码,在解码阶段使用.
  • PTS:主要用于视频的同步和输出.在渲染的时候使用.在没有B frame的情况下.DTS和PTS的输出顺序是一样的。

如上图:I帧的解码不依赖于任何的其它的帧.而P帧的解码则依赖于其前面的I帧或者P帧.B帧的解码则依赖于其前的最近的一个I帧或者P帧 及其后的最近的一个P帧.

2. 编码数据码流结构

在我们的印象中,一张图片就是一张图像,视频就是很多张图片的集合.。但是因为我们要做音视频编程,就需要更加深入理解视频的本质.

2.1 刷新图像概念.

在编码的码流中图像是个集合的概念,帧、顶场、底场都可以称为图像,一帧通常就是一幅完整的图像.

  • 逐行扫描:每次扫描得到的信号就是一副图像,也就是一帧. 逐行扫描适合于运动图像
  • 隔行扫描:扫描下来的一帧图像就被分为了两个部分,这每一部分就称为「场」,根据次序分为:「顶场」和「底场」.适合于非运动图像

2.2. 重要参数

  • 视频参数集VPS(Video Parameter Set)

VPS主要用于传输视频分级信息,有利于兼容标准在可分级视频编码或多视点视频的扩展。

(1)用于解释编码过的视频序列的整体结构,包括时域子层依赖关系等。HEVC中加入该结构的主要目的是兼容标准在系统的多子层方面的扩展,处理比如未来的可分级或者多视点视频使用原先的解码器进行解码但是其所需的信息可能会被解码器忽略的问题。

(2)对于给定视频序列的某一个子层,无论其SPS相不相同,都共享一个VPS。其主要包含的信息有:多个子层或操作点共享的语法元素;档次和级别等会话关键信息;其他不属于SPS的操作点特定信息。

(3)编码生成的码流中,第一个NAL单元携带的就是VPS信息

  • 序列参数集SPS(Sequence Parameter Set)

包含一个CVS中所有编码图像的共享编码参数。

(1)一段HEVC码流可能包含一个或者多个编码视频序列,每个视频序列由一个随机接入点开始,即IDR/BLA/CRA。序列参数集SPS包含该视频序列中所有slice需要的信息。

(2)SPS的内容大致可以分为几个部分:1、自引ID;2、解码相关信息,如档次级别、分辨率、子层数等;3、某档次中的功能开关标识及该功能的参数;4、对结构和变换系数编码灵活性的限制信息;5、时域可分级信息;6、VUI。

  • 图像参数集PPS(Picture Parameter Set)

包含一幅图像所用的公共参数,即一幅图像中所有片段SS(Slice Segment)引用同一个PPS。

(1)PPS包含每一帧可能不同的设置信息,其内容同H.264中的大致类似,主要包括:1、自引信息;2、初始图像控制信息,如初始QP等;3、分块信息。

(2)在解码开始的时候,所有的PPS全部是非活动状态,而且在解码的任意时刻,最多只能有一个PPS处于激活状态。当某部分码流引用了某个PPS的时候,这个PPS便被激活,称为活动PPS,一直到另一个PPS被激活。

参数集包含了相应的编码图像的信息。SPS包含的是针对一连续编码视频序列的参数(标识符seq_parameter_set_id、帧数及POC的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等等)。PPS对应的是一个序列中某一幅图像或者某几幅图像 ,其参数如标识符pic_parameter_set_id、可选的seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等等。

通常,SPS 和PPS 在片的头信息和数据解码前传送至解码器。每个片的头信息对应一个 pic_parameter_set_id,PPS被其激活后一直有效到下一个PPS被激活;类似的,每个PPS对应一个 seq_parameter_set_id,SPS被其激活以后将一直有效到下一个SPS被激活。 参数集机制将一些重要的、改变少的序列参数和图像参数与编码片分离,并在编码片之前传送 至解码端,或者通过其他机制传输。

扩展知识点:档次(Profile)、层(Tier)和级别(Level)

  • 档次: 主要规定编码器可采用哪些编码工具或算法。

  • 级别: 指根据解码端的负载和存储空间情况对关键参数(最大采样率、最大图像尺寸、分辨率、最小压缩比、最大比特率、解码缓冲区DPB大小等)加以限制。

考虑到应用可根据最大的码率和CPB大小来区分,因此有些级别定义了两个层Tier:主层和高层,主层用于大多数应用,而高层用于那些最严苛的应用。