1. 引言

从这一节开始,我们真正进入了解码的过程。
相信很多人和我一样,刚开始的时候都会很好奇,为什么h264可以实现这么强大的压缩比,要知道,1张1080p的YUV420就是3MB,想实现1秒钟30帧,千兆网就基本跑满了,这也太可怕了,基本上只有条件很好的局域网才能达到这个水平。但是h264的出现把这个数据量降到了百分之一,2个数量级,这实在太可怕了,技术的发展真的是强大。
其实h264编解码,就是从YUV文件和h264文件中相互转化的过程。不同的是,YUV数据量大,结构简单,适合在本地显示使用,h264数据量小,无法直接显示,适合传输使用。这也就是编码器和解码器存在的意义。

2. 基础知识

2.1 为什么h264可以压缩

本来没打算写这些,聊到这里感觉避不过去,就简单聊两句。
为什么h264可以实现压缩?

  • 在我们的物理世界中,图像一般都是连续的,而且现在的帧率一般足够高,导致前后帧同一个位置的图像一般不会出现大的变化,这样前后帧的图像就有很多的相同之处,H264在编码的时候只需要去编那些少量不同的地方就行了。这就是时间冗余
  • 同样,在物理世界中,物体也是连续的,大部分情况下,相邻像素的变化也不大,比如同一个物体,单独裁出一小部分的时候你很难看出是什么位置,在h264中,我们可以用已知的相邻像素来推断当前的像素,这就是空间冗余
  • 在我们常规的编码中,都使用的是等长编码,这在所有字符出现概率未知的情况下是合理的,但是在h264中,明显部分数据的出现概率要更高,比如一些小数,1,0,-1 这些,给这些高概率的数据分配更短的码字,也同样有助于减小数据量,这就是编码冗余
  • 根据研究表明,人眼对一些颜色或者形状更加敏感,而对另外一些则更容易忽略,这种情况下,我们可以着重编码这些被重视的部分,而略过那些难以分辨的部分,在h264中,使用了量化技术来大大降低了数据量,这就是视觉冗余
  • 通过一些先验知识来消除 知识冗余,这个我没想到264里是哪里用的,倒是NN encoder我觉得很符合这个。暂时挂在这里,以后想到了再来补坑。

3. NALU

经过2.1 的解释,我们可以得知,最终的码流文件是将原始的YUV文件经过一定的规则编码而成一个新的二进制文件,这个文件的大小相比之前小了很多,但是却不能直观的得到图像的数据,我们的任务就是从这个二进制文件里获取到原来YUV文件的像素YUV的值。
打开一个h264文件,两眼一黑。

这都是些啥,一点规律没有,这怎么看?
别急,接下来,让我们一点点来分析。

3.1 NALU是什么

H264分为Network abstract layer 网络抽象层 和 video coding layer 视频编码层。
NALU是 Network abstract layer unit,也是网络发送的基本单元,每个NALU都有自己的作用,类型,数据格式以及重要性。不同NALU之间在发送上是相互独立的,发送端甚至可以使用不同的传输模式来传输码流,比如使用稳定的TCP来传递重要性高的NALU,使用快速的UDP来传输重要性低的NALU,这些都是可以的。
也就是说,这个我们的打开的H264的二进制文件中,其实是由很多个NALU单元组成的,我们第一步要做的,就是从文件中找到这些NALU,挨个取出他们来进行分析。

3.2 如何获取一个NALU

因为每个NALU的长度不一且未知,解码器需要根据他们的打包方式来进行解码。
在NALU组合码流的过程中,一般有两种使用比较常见的打包方式: AnnxB 和 avcC。

  • Annex B [əˈneks]
    Annex B 是比较常用的一种打包方式,详见《Rec. ITU-T H.264 (03/2010)》 305面。

    根据表格可以看出,在 nal_unit之前,插入了一个 start_code_prefix_one_3bytes的 起始码作为标记,这个起始码的值为0x00 0x00 0x01。
    我们在解码中,只需要去找固定的0x00 0x00 0x01的字符串即可。
    实际在264码流里面一般会用到两种起始码,4字节 0x00 00 00 01或者3字节 0x00 00 01.
    一般4字节起始码用于SPS,PPS和每帧的第一个Slice,3字节起始码用于其他的NALU(例如多slice时一帧内的非第一slice)。
    在同一个码流中,也会两种混用。
    不过我觉得问题不大,只要找到0x00 00 01,作为开头,准没错。我在自己的解码器里也是这么找的。

  • avcC
    avcC的使用没有AnnexB的模式使用的多,avcC会把Nalu的长度写在开头,然后去找固定长度的字节即可。
    详见这里,这里不做过多介绍。

3.3 NALU header

当我们根据起始码成功定位出NALU的位置之后,每个NALU的第一个字节都是固定的NALU header。
这个header非常重要,决定了这个NALU的类型,重要性,也决定了解码器如何去解码,以及能获取到的信息。

根据协议可以看出NALU header的组成是:

NALU header: 1字节 8bit,具体分为
fordidden_zero_bit(1bit) | nal_ref_idc(2bit) | nal_unit_type(5bit)

如图

其中,

  • fordidden_zero_bit:固定为0,如果解码器检测到不为0,表示NALU出错,解码器可以选择丢弃或者修复这个NALU。
  • nal_ref_idc:重要程度,0~3. 值越高,说明越重要。一般SPS,PPS,IDR的slice都会选择大于0的值
  • nal_unit_type:NALU类型如下图,比较重要的是 SPS(7),PPS(8),IDR slice(5) 等等。

3.4 NAL的防竞争码, EBSP与RBSP

EBSP 扩展字节序列载荷,协议中未定义,JM中使用的。
RBSP。
因为我们需要根据0x00 00 01的起始码来将码流数据分成多个NALU,但是,如果原始数据里就存在0x00 00 01这样的数据,就会导致识别错误,从来切分NALU失败。
这里采用了防竞争码的方式,将原始码 中的一些特定组合进行防竞争码的转换。

0x 00 00 01 -> 0x 00 00 03 01
0x 00 00 02 -> 0x 00 00 03 02
0x 00 00 03 -> 0x 00 00 03 03

解码过程则逆过来即可,在真实码流中的NALU中,检测到0x00 00 03 01就转成 0x00 00 01,检测到0x00 00 03 02就转成 0x00 00 02,检测到0x00 00 03 03就转成 0x00 00 03,。
可能大家又会担心,那原始码流中如果本来就有类似0x00 00 03 01 ,0x00 00 03 02 这种组合呢?是不是转换回来就错了?
其实不会的。
假设原始码流中有 0x00 00 03 02, 在编码时,会被替换防竞争码, 变成 0x00 00 03 03 02,这样,在解码的时候,依然可以原样的解回来,就不会出错了。

3.5 获取SODB

视频在编码时,是按照bit来编的,这样可能导致编出来的SODB码流长度不是整byte的。
因此,协议规定,如果遇到这种情况,先写入 1 bit 数据,数据内容是 1,然后开始补齐 0,直到补齐到一整个字节。
那如果正好是整byte的呢?那就再补1个0x80的字节(1bit的1,剩下补0)。
解码方法也是一样,从RBSP里的末尾开始按bit找0,找到第一个不是0的位,就是尾部的开始。

至此,我们已经成功拿到了一个NALU的Data,下面,我们需要根据NALU header里解析出来的type,按照不同的语法对其进行解析。具体请见下一篇博文。

4. 参考

  • https://www.jianshu.com/p/d21cdf1a4f64

【编解码】从零开始写H264解码器(2) NALU相关推荐

  1. 【编解码】从零开始写H264解码器(4) 熵编码之指数哥伦布编码

    1. 引言 经过上一章的学习,我们学会看描述子.这时候我们就会发现,在语法中,除了简单的 u(n),i(n) 这种读取固定长度的二进制解析方法之外,用的更多的还有ue(v),se(v)这些. 只有学会 ...

  2. 【编解码】从零开始写H264解码器(7) SEI解析

    1. 引言 解析完了SPS和PPS,按照NAL type,下一个可以介绍SEI. 不过SEI在码流不是必须的,重要性并没有SPS,PPS,slice那么高. 解析方式也很简单,都没有用熵编码. 先开一 ...

  3. 视频的基本参数及H264编解码相关概念

    概述 上几篇文章介绍了音频的采集以及编码,现在我们开始学习视频相关的知识,同样先从概念开始.本篇文章的主要内容有: 视频相关参数 帧率(fps) 分辨率 DTS和PTS 码率 音视频同步 对视频编解码 ...

  4. VideoCodec 入门篇 - 00 (编解码简介)

    目录 1.基本术语 (Basic Terminology) 1.1.图像 (Image) 1.2.像素 (Pixel) 1.3.颜色深度 (Color Depth) 1.4.分辨率 (Resoluti ...

  5. H264视频传输、编解码----FFmpeg软解码

    记录一下之前项目的实际使用过程. 将按照Java层------>JNI接口------>JNI代码中使用FFmpeg解码. 首先Java层: public class CodecWrapp ...

  6. iOS8系统H264视频硬件编解码说明

    iOS8系统H264视频硬件编解码说明 转载自:http://www.tallmantech.com/archives/206#more-206 公司项目原因,接触了一下视频流H264的编解码知识,之 ...

  7. iOS系统H264视频硬件编解码说明

    公司项目原因,接触了一下视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解.该方法比较通用,但是占用CPU资源,编解码效率不高.一般系统都会 ...

  8. (推荐阅读)H264, H265硬件编解码基础及码流分析

    需求 在移动端做音视频开发不同于基本的UI业务逻辑工作,音视频开发需要你懂得音视频中一些基本概念,针对编解码而言,我们必须提前懂得编解码器的一些特性,码流的结构,码流中一些重要信息如sps,pps,v ...

  9. 音视频系列--H264编解码总结

    一.概述 H264,通常也被称之为H264/AVC(或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC) 对摄像头采集的每一帧视频需要进行编码,由于视频中存在空间和时间的冗余,需要 ...

  10. 视频H264编解码知识整理

    简介 网络提取层(NAL network abstraction layer )和视频编码层(VCL video coding) 码率.帧率.分辨率 其它 总结 简介 视频编解码网上介绍很多,整理了不 ...

最新文章

  1. 生动的解释下什么是 MySQL 的“回表”?
  2. C#实现rabbitmq 延迟队列功能
  3. python2:function
  4. poj 1469 二分图最大匹配
  5. arm中的.a文件如何产生的_可变文件系统:如何在IPFS中处理文件?
  6. 使用TensorFlow进行深度学习-第2部分
  7. 一二三系列之优先队列、st表——Battle,Heapsort,A Magic Lamp
  8. (10)魔兽文件打包器里的传奇哈希表
  9. 简易web服务器系统毕业论文设计,毕业论文 简易的WEB服务器的设计
  10. json接口(使用,以及自定义)
  11. python运维自动化老男孩_老男孩Python高级运维自动化实战 老男孩Python高级运维开发10期 全套Python视频教程下 ......
  12. 普通计算机用的是什么屏幕,电脑显示器什么面板最好?IPS/TN/PLS/VA面板的显示器区别...
  13. 10.5 Vue电商后台管理完善--订单详情页面显示商品信息,添加备注
  14. 配置中心—Consul配置管理
  15. python表情,python玩转emoji表情
  16. AD软件PCB转PADS
  17. Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC
  18. 随笔日记2018 4.10 关于多选框
  19. 线性代数基础----矩阵
  20. 18_ElasticSearch 基于slop参数实现近似匹配

热门文章

  1. 人类自然语音频率范围
  2. 甄零一诺合同——专注合同信息化管理
  3. Win10使用快捷键新建文件夹和.txt文本文档,提升工作效率,让你成为最靓的仔
  4. 波士顿仿生机械狗 原理分析
  5. 学校计算机组管理制度,校园一卡通管理结算中心机房管理制度
  6. 计算机制图知识点,工程制图的基础知识
  7. 现代工程制图及计算机辅助绘图答案,现代工程制图(附习题集第2版高等学校应用型特色规划教材)...
  8. SolidWorks设计助手,可以标注和实体无关的工程图标注
  9. 光缆故障定位检测仪使用方法简介
  10. 【信号处理】单通道盲源分离(SSA-ICA)算法