欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

H.264 编码基础知识

程序员文章站 2022-07-07 11:54:30
...

说明,以下内容很多参考 从零了解H.264结构

基本资料

H.264,又称为MPEG-4第10部分,高级视频编码(英语:MPEG-4 Part 10, Advanced Video Coding,缩写为MPEG-4 AVC)是一种面向块,基于运动补偿的视频编码标准。由ITU-T视频编码专家组与ISO/IEC联合工作组开发。

H.264 协议标准

音视频基础知识

在线视频兴起之前,互联网上看片的主要方式是下载到电脑后观看。比如下载了一个 test.mp4的视频,这个 mp4是一个容器,又叫封装格式,就是把已经编码封装好的视频、音频按照一定的规范放到一起。常见的容器格式有: avi, mp4, mov, ts, mkv, rmvb/rm, wmv, flv, 3gp, asf, webm 。
裸视频是非常大的,所以需要对视频做编码压缩,常见的视频编码格式有: mpeg-1, mpeg-2, mpeg-4, H.264/AVC/mpeg-4part 10, h.265/hevc, vc-1, RealVideo, AVS 。

H.264 编码基础知识

H.264 视频编码

视频编码主要是利用空间、时间的冗余信息进行编码达到压缩的目的。空间冗余信息压缩是做每一帧图片内的压缩,跟 jpeg 图片压缩类似,可能会是降采样、联合周边像素信息等方式来进行编码。 时间冗余信息主要是参考前后帧的信息来进行编码,比如前后两帧的图片通常是有很多像素点是一样的,比如要表示两针图片的信息,能想到最简单的方式就是用第一帧的全量信息+第二帧相对第一帧有变化的变量信息来表示。H.264 的编码很复杂,但是大体的原理就是利用空间、时间信息来编码。
因为有利用时间信息来编码,所以必然要利用多帧的图片来编码,所以需要缓存多帧信息,给编解码以及视频流带来一些需要权衡的问题,比如要利用更多前后帧信息来进行编码,能够提高压缩比,但是相应的延迟就会比较高,对于实时视频推流是需要权衡的;另外解码顺序和渲染顺序也需要注意。

简介

H.264 编码功能分为两层,VCL(视频编码层/Video Coding Layer)和 NAL(网络提取层/Network Abstraction Layer)。VCL 主要分为 5 部分: 帧间和帧内预测(Estimation)、变换(Transform)和反变换、量化(Quantization)和反量化、环路滤波(Loop Filter)、熵编码(Entropy Coding)。VCL 负责怎么高效的进行编码,编码后的数据怎么进行存储和传输由 NAL 来负责,存到了NAL单元(NALU)。一帧图片经过 H.264 编码器之后,就被编码为一个或多个片(slice),而装载着这些片(slice)的载体,就是 NALU 了。片(slice)是 H.264 中提出的新概念,是通过编码图片后切分通过高效的方式整合出来的概念,一张图片至少有一个或多个片(slice)。除了装载编码数据的slice,有些NALU装载了编码的一些 meta 信息,比如 SPS(Sequence parameter set), PPS(Picture parameter set)。

H.264 编码基础知识

H.264 编码基础知识

  • 1 Frame (帧) = 1…n个Slice (片)1 Slice (片) = 1…n个Marcoblock(宏块)1 Marcoblock(宏块) = 16x16yuv数据
  • 1 Slice (片) = Slice Header + Slice Data
  • 1 NALU = 一组对应于视频编码的NALU头部信息 + 一个原始字节序列负荷(RBSP,Raw Byte Sequence Payload).

NALU

H.264的结构全部都是以 NALU 为主,理解了 NALU,就理解了 H.264 的结构。一个 NALU 由 NAL头+RBSP组成。一个原始的 H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload]三部分组成,其中 Start Code 用于标示这是一个 NALU 单元的开始,必须是 00 00 00 0100 00 01

H.264 编码基础知识

NALU Header

NAL Header 由三部分组成,F/forbidden_bit(1bit),NRI/nal_reference_bit(2bits)(优先级),Type/nal_unit_type(5bits)(类型)。

+---------------+ 
|0|1|2|3|4|5|6|7| 
+-+-+-+-+-+-+-+-+ 
|F|NRI| Type    |
+---------------+ 

H.264 编码基础知识

比较经常会用到的 NALU 类型有:

00 00 00 01 06:  SEI信息   
00 00 00 01 67:  0x67&0x1f = 0x07 :SPS
00 00 00 01 68:  0x68&0x1f = 0x08 :PPS
00 00 00 01 65:  0x65&0x1f = 0x05: IDR Slice
00 00 00 01 41:  0x41&0x1f = 0x01: Coded slice of a non-IDR picture

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

分片(Slice)

我们可以理解为一 张/帧 图片可以包含一个或多个分片(Slice),而每一个分片(Slice)包含整数个宏块(Macroblock),即每片(Slice)至少一个宏块(Macroblock)。 Slice = Slice Header + Slice Data

H.264 编码基础知识

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

有五种分片类型

Slice 内容
I Slice 只包含I宏块
P Slice 包含P和I宏块
B Slice 包含B和I宏块
SP Slice 包含P 和/或 I宏块,用于不同码流之间的切换
SI Slice 一种特殊类型的编码宏块

宏块(Macroblock)

宏块是视频信息的主要承载者。一个宏块由一个16×16亮度像素和附加的一个8×8 Cb和一个 8×8 Cr 彩色像素块组成。每个图象中,若干宏块被排列成片的形式。

H.264 编码基础知识

宏块分类

宏块分类 说明
I Macroblock 利用从当前片中已解码的像素作为参考进行帧内预测
P Macroblock 利用前面已编码图像作为参考进行帧内预测,一个帧内编码的宏块可进一步作宏块的分割:即16×16.16×8.8×16.8×8亮度像素块。如果选了8×8的子宏块,则可再分成各种子宏块的分割,其尺寸为8×8,8×4,4×8,4×4
B Macroblock 利用双向的参考图像(当前和未来的已编码图像帧)进行帧内预测

I,P,B帧与 PTS/DTS

帧的分类 名称 说明
I帧 帧内编码帧,又称intra picture 自身可以通过视频解压算法解压成一张单独的完整的图片
P帧 前向预测编码帧,又称predictive-frame 需要参考其前面的一个I frame 或者B frame来生成一张完整的图片
I帧 双向预测帧,又称bi-directional interpolated prediction frame 则要参考其前一个I或者P帧及其后面的一个P帧来生成一张完整的图片
名称 说明
PTS(Presentation Time Stamp) PTS主要用于度量解码后的视频帧什么时候被显示出来
DTS(Decode Time Stamp) DTS主要是标识内存中的bit流再什么时候开始送入解码器中进行解码

GOP(Group Of Pictures)

GOP (图像组)主要用作形容一个 i 帧 到下一个 i 帧之间的间隔了多少个帧。一个I帧所占用的字节数大于一个P帧,一个P帧所占用的字节数大于一个B帧。在码率不变的前提下,GOP值越大,P、B帧的数量会越多,平均每个I、P、B帧所占用的字节数就越多,也就更容易获取较好的图像质量。在遇到场景切换的情况时,H.264编码器会自动强制插入一个I帧,此时实际的GOP值被缩短了。另一方面,在一个GOP中,P、B帧是由I帧预测得到的,当I帧的图像质量比较差时,会影响到一个GOP中后续P、B帧的图像质量,直到下一个GOP开始才有可能得以恢复,所以GOP值也不宜设置过大。由于P、B帧的复杂度大于I帧,所以过多的P、B帧会影响编码效率,使编码效率降低。

IDR(Instantaneous Decoding Refresh)即时解码刷新

一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。
I和IDR帧都使用帧内预测。I帧不用参考任何帧,但是之后的P帧和B帧是有可能参考这个I帧之前的帧的。IDR就不允许这样。
比如这种情况:
IDR1 P4 B2 B3 P7 B5 B6 I10 B8 B9 P13 B11 B12 P16 B14 B15 这里的B8可以跨过I10去参考P7

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

附录

将一张图片转成 H.264 视频

ffmpeg -i minimal.png -pix_fmt yuv420p minimal_yuv420.h264

下载测试视频

要学习 H.264 编码,就需要有用 H.264 编码的视频 demo 来测试,可以用 you-get 工具到主流的一些视频网站下载个视频来测试。

test you-get -i https://www.bilibili.com/video/av2650963\?from\=search\&seid\=15782581299335183738
site:                Bilibili
title:               Tribute to Hayao Miyazaki(致敬宫崎骏)
streams:             # Available quality and codecs
    [ DEFAULT ] _________________________________
    - format:        flv
      container:     flv
      quality:       高清 1080P
      size:          42.4 MiB (44452950 bytes)
    # download-with: you-get --format=flv [URL]

    - format:        flv720
      container:     flv
      quality:       高清 720P
      size:          42.4 MiB (44452999 bytes)
    # download-with: you-get --format=flv720 [URL]

    - format:        flv360
      container:     flv
      quality:       流畅 360P
      size:          9.0 MiB (9452072 bytes)
    # download-with: you-get --format=flv360 [URL]test you-get --format=flv360 https://www.bilibili.com/video/av2650963\?from\=search\&seid\=15782581299335183738

视频格式转换

视频格式转换大部分都可以用 ffmpeg 来实现。

ffmpeg -i input.flv output.mp4

此过程需要对视频进行重新编码,比较耗费 CPU 等资源。如果不做转码,可以用如下的方式:

ffmpeg -i input.flv -vcodec copy -acodec copy output.mp4

将视频转换为非压缩的裸视频

将视频 input.mp4转换为 pix_fmt nv12yuv 的裸视频 output.yuv, 裸视频可以作为后续视频编码的 input 。

ffmpeg -i input.mp4 -vcodec rawvideo -vframes 100 -pix_fmt nv12 -an output.yuv

flv 转 h264

将视频 input.flv转换为 output.h264

ffmpeg -i input.flv -vcodec copy output.h264

mp4 转 mkv

将视频 input.mp4转换为 output.mkv

ffmpeg -i input.mp4 -vcodec copy -acodec copy output.mkv

参考资料