这段时间一直在进行编写H264文件的解析类,因此对于H264文件的格式有了初步的了解,官方文档也看了个大概。这篇文章主要是总结了一些为解码H264文件而需要的一些前期知识,话不多说,下面是干货,有些是自己在wiki上翻译过来的,有些是看官方文档后发现的一些关键部分。 
首先了解一下视频文件中的一些知识:


Video compression picture types(视频压缩图像类型)

  1. 1. 视频帧的压缩使用了不同的算法,这些应用于视频帧的不同算法被称为图像类型或者帧类型。
  2. a. I - 该帧可压缩程度最低,也不需要通过其他视频帧解码。
  3. b. P - 该帧可以引用前面的帧的数据来解压缩并且相对于I帧来说,该帧可以压缩程度更高。
  4. c. B - 该帧可以引用前面的帧和后面的帧的数据,从而压缩程度最高。

 
(图为一段视频帧,由2个关键帧(I-frame),1个向前预测帧(P-frame)和1个双向预测帧(B-frame)构成)

这里共有三种类型的帧被用在视频压缩技术中:I-帧,P-帧 和 B-帧。 
I-帧是一种“内编码图片”,实际上是一种信息十分详细的图片,跟传统的静态图片文件相似。P-帧 和 B-帧 只存储了部分的图片信息,因此相比于I-帧来说,他们需要更少的存储空间,也因此而提高了压缩速率。 
P-帧(“预测图片”)只保存图片中与前一帧的不同的地方。比如,在一个场景中,汽车行驶在静止的背景中,只有汽车的运动需要被编码。编码器无需存储未改变的背景像素信息到P-帧中,因此而节省了空间,P-帧 也同样被称为 delta-帧。 
B-帧(“双向预测图片”)通过比较现在的帧和前后的帧的不同来指定具体存储的内容,从而节省了更多的空间。


Slices(条带)

一个条带是一个帧中比较独特的区域,不同于该帧中的其他区域。在最新的国际标准中,这里已经存在 I-slices(I-条带),P-slices(P-条带)和 B-slices(B-条带)。


Macroblocks(宏块)

普遍来说,图片(帧)被分割成许多宏块和个体预测类型能够在宏块层被挑选出来,并且整个图片(帧)中的宏块与宏块并不相同,如下: 
1. I-帧 只能包含内部宏块 
2. P-帧 既能包含内部宏块,也能包含预测宏块 
3. B-帧 能包含内部宏块,预测宏块和双向预测宏块 
进一步说,在视频编解码H.264中,帧能够被分割成宏块序列,该宏块序列被称为条带(slices)。编解码过程不是使用I,B和P 帧类型作为选集,而是通过每个条带明确无误的选择出预测类型。在H.264文件中也能找到几个额外的 帧/条带 类型: 
1. SI-帧/条带(Switching I);使编码流之间的转换变得更加容易;包含了 SI-宏块(一种内部编码宏块的特殊类型) 
2. SP-帧/条带(Switching P);使编码流之间的转换变得更加容易;包含了 P-宏块 和/或 I-宏块。 
3. muti-帧 动态预测(包括16个引用帧,或32个引用域)


Intra coded frames/slices(I-frames/slices or Key frames)(内编码帧/条带(I帧/条带 或者 关键帧))

  1. 1. I-帧 编码不依赖于其他帧
  2. 2. 可能被编码器生成并创建于一个随机访问点(来允许解码器在图片的该位置开始解码)
  3. 3. 当区分图片细节时也可能为了避免产生有效的 P-帧 B-帧 而生成
  4. 4. 普遍来说比其他类型的帧需要更多的位数来编码

通常,I-帧 被用来随机访问和作为其他图片(帧)解码时的引用。内部刷新周期通常为半秒(在数字电视广播和DVD存储介质中很常见)。在一些环境下,也可能需要更长的时间周期。比如,在视频会议系统中,通常会间歇的发送I-帧(不频繁的发送)。


Predicted frames/slices(P-frames/slices)(预测帧/条带(P-帧/条带))

  1. 1. 为了解码的需要,要优先解码一些其他图片(帧)
  2. 2. 可能包含图像数据或运动向量偏移量或者两者的组合
  3. 3. 能够按照解码顺序引用前面的图片
  4. 4. 旧的设计标准在解码时只使用一张前面的解码图片作为引用,并且需要该图片也要在P-帧图片之前显示。
  5. 5. H.264文件中,在解码过程中能够使用多个先前解码的图片作为引用,并且相对于被用来做预测的图片帧,该帧能够以任意顺序显示。
  6. 6. 通常,相对于编码I-帧来说,编码预测帧需要更少的比特位数。

Bi-directional predicted frames/slices(B-frames/slices)(双向预测帧/条带(B-帧/条带))

  1. 1. 为了解码的需要,要优先解码一些其他图片(帧)
  2. 2. 可能包含图像数据或运动向量偏移量或者两者的组合
  3. a. 旧的标准有一个关于整个帧的全局运动补偿向量
  4. b. 一些标准关于每个宏块都有一个运动补偿向量
  5. 3. 一些标准允许每个宏块使用两个运动补偿向量(双向预测)
  6. 4. 在旧的设计标准中(比如MPEG-2),B-帧从不用来作为其他图片(帧)的预测引用。因此,B-帧可以用一个稍低质量的编码(编码量少的)来完成,因为一些细节的丢失不会对预测后续图片(帧)的质量带来影响。
  7. 5. H.264文件中,有可能被作为解码其他图片(帧)的引用(由编码器决定)
  8. 6. 在旧的设计标准中(比如MPEG-2),在解码过程中会使用前面的2帧解码图片作为引用,并且需要其中一帧在B-帧之前显示,另外一帧在B-帧之后显示。
  9. 7. H.264文件中,能够使用1,2或者更多先前解码的图片(帧)作为解码过程中的引用,并且相对于被用来做预测的图片帧,该帧能够以任意顺序显示。
  10. 8. 通常,相对于编码I-帧 P-帧来说,编码该帧需要更少的比特位数。

有了已上的基本知识以后,我们已经了解到I-帧是关键帧,并且是占空间最多的帧,因为很多数据都是内编码的,而P-帧是预测帧,需要借助前面的帧来作为引用,从而得到完整的数据,而B-帧是双向预测帧,需要借助前面和后面的帧来作为引用。因此一般来说占用空间排序:I-帧 > P-帧 > B-帧 (I-帧占用空间最多)。

对于如何解析帧类型(I,P还是B帧等等),需要用到nal_unit_type这一字段,在Nalu中它占据第一个字节的3~8位,Nalu的第一个字节(忽略起始码startcode:0x000001 or 0x00000001)如下: 
forbidden_zero_bit: 1 bit 
nal_ref_idc: 2 bit 
nal_unit_type: 5 bit 
官方文档也给出了语义解析,以伪代码的形式给出的: 

后面的f(1),u(2),u(5)是代表要使用的解析方法,前面加深的粗体forbidden_zero_bit,nal_ref_idc,nal_unit_type称为syntax_element,语法元素,即:解析这些语法元素要用到后面的f(1),u(2),u(5)方法,下面是官方文档中给出的解析方法: 


这里面的解析方法,个人感觉只有Exp-Golomb-coded的解析有些麻烦(解析具体是什么帧需要用到该方法),其他的用简单的几个位操作就能轻松应对了吧,下面讲讲指数哥伦布编码的相关信息:


Exponential-Golomb(universal code):指数哥伦布编码

  1. 以下是wiki上给出的解释(给翻译过来的,英文水平不好,请轻喷,thanks
  2. 1. (默认k=0的形式)
  3. a. 将原数加1写成二进制形式
  4. b. 1中的二进制的位数减一并记下,添加这么多个01中二进制位数的前面
  5. 例子:
  6. 0 1 1
  7. 1 10 010
  8. 2 11 011
  9. 3 100 00100
  10. 4 101 00101
  11. 5 110 00110
  12. 6 111 00111
  13. 7 1000 0001000
  14. 8 1001 0001001
  15. 将其推广到负数范围表示,方法如下:
  16. (1) 非负整数x0映射到偶数-2x
  17. (2) 正整数x>0映射到奇数2x - 1
  18. 例子:
  19. 0 0 1 1
  20. 1 1 10 010
  21. 1 2 11 011
  22. 2 3 100 00100
  23. 2 4 101 00101
  24. 3 5 110 00110
  25. 3 6 111 00111
  26. 4 7 1000 0001000
  27. 4 8 1001 0001001
  28. 2. 衍生到k阶(k0)形式
  29. a. 用于编码更大的数,编码后形成更少的二进制位(编码比较小的数需要使用更多的二进制位数)
  30. b. 要以k阶形式编码一个非负整数x,方法如下:
  31. i. 0阶方式编码⌊x/2^k
  32. ii. x取模2^k
  33. 等价方式:
  34. i. 使用0exp-Golomb编码方式编码x+2k1
  35. ii. 从编码结构中删除前面的k0


官方文档有关于解析哥伦布指数编码的方法,of course,仍然是以伪代码的形式给出: ) 


再继续说解析帧类型的方法… 
依然是官方文档…. 
发现了这么一个东东 
 
这应该就是我们要分析该Nalu是什么帧类型的关键吧!于是找找它在哪里出现过 

很明显,第3行出现了,再找找slice_header出现的地方 


然后再继续逆向查找它们分别出现在哪里,结果最后都在一张表中找到了 

于是开始分析:

    1. 对于slice_layer_without_partitioning_rbsp: 
      1. nal_unit_type = 1,5,19
    2. 对于slice_data_partition_a_layer_rbsp: 
      1. nal_unit_type = 2
    3. 对于slice_layer_extension_rbsp: 
      1. nal_unit_type = 20 
        于是下面就是具体分析nal_unit_type的值来进行相应处理的过程: 
        首先,在代码中枚举如下NAL单元类型(参考了下ffmpeg) 

        相应的,枚举如下Slice类型,根据Slice类型即能得出帧类型 
         
        (具体为什么要都定义2种,因为根据官方文档来写的,其实每个第二种类型数值-5即为第一种类型,没有仔细研究) 
        具体的解析代码在我的GitHub上,欢迎参观:yijiazhen的GitHub 
        刚刚接触H264文件解析不久,如有分析错误或是不到位的地方,恳请各路好汉批评指正,希望可以互相交流,共同提高。

解码H264文件的一些基础知识的更多相关文章

  1. 【RL-TCPnet网络教程】第35章 FTP文件传输协议基础知识

    第35章      FTP文件传输协议基础知识 本章节为大家讲解FTP(File Transfer Protocol,文件传输协议)的基础知识,方便后面章节的实战操作. (本章的知识点主要整理自网络) ...

  2. linux 文件权限的基础知识

    由于自己总是记不住linux里权限的一些知识,因此简单总结如下: 查看文件权限 // 列出所有文件 ls -al // 最前面的一串10个字母的字符串 // 可能像 drwxrwxr-x // 第一位 ...

  3. 音视频处理基础知识扫盲:数字视频YUV像素表示法以及视频帧和编解码概念介绍

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt+moviepy音视频剪辑实战 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一. ...

  4. 【STM32H7教程】第57章 STM32H7硬件JPEG编解码基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第57章       STM32H7硬件JPEG编解码基础知识 ...

  5. 【RL-TCPnet网络教程】第38章 TFTP简单文件传输基础知识

    第38章      TFTP简单文件传输基础知识 本章节为大家讲解TFTP(Trivial File Transfer Protocol,简单文件传输协议)的基础知识,方便后面章节的实战操作. (本章 ...

  6. Linux基础知识第三讲,拷贝文件跟移动文件命令

    目录 Linux基础知识第三讲,拷贝文件跟移动文件命令 一丶常用命令 1.tree命令常用选项 2.cp复制文件命令 3.mv 命令的使用 Linux基础知识第三讲,拷贝文件跟移动文件命令 一丶常用命 ...

  7. APK反编译之一:基础知识—APK、Dalvik字节码和smali文件

    refs: APK反编译之一:基础知识http://blog.csdn.net/lpohvbe/article/details/7981386 APK反编译之二:工具介绍http://blog.csd ...

  8. python基础知识六 文件的基本操作+菜中菜

    基础知识六 文件操作 ​ open():打开 ​ file:文件的位置(路径) ​ mode:操作文件模式 ​ encoding:文件编码方式 ​ f :文件句柄 f = open("1.t ...

  9. Linux基础知识之文件的权限(一)

    Linux基础知识之文件权限(一) Linux优点之一就是它拥有多用户多任务的环境,在提供文件共享的同时也能保证用户文件的安全性.所以,设置文件的权限管理变得尤为重要. 权限讲解 [der@Der ~ ...

随机推荐

  1. Why Use C++/CLI?

    来源:http://www.asawicki.info/Download/Productions/Publications/CPP_CLI_tutorial.pdf Why Use C++/CLI? ...

  2. SQL语句性能优化操作

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引. 2.应尽量避免在where子句中对字段进行null值判断,创建表时NULL是默认值,但大多数时候应 ...

  3. Android Studio第一次启动失败的解决办法

    Android Studio Android 开发环境 由于GFW的问题,安装后第一次启动会在显示Fetching android sdk component information对话框后,提示错误 ...

  4. Android4.4 GPS框架分析【转】

    本文转载自:http://blog.csdn.net/junzhang1122/article/details/46674569 GPS HAL层代码在目录trunk/Android/hardware ...

  5. 山东省第四届ACM程序设计竞赛A题:Rescue The Princess(数学+计算几何)

    Rescue The Princess Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 412  Solved: 168[Submit][Status][ ...

  6. JSON.stringify出现 "Converting circular structure to JSON"

    JSON.stringify()  我们很熟悉了,将一个对象转换为json形式的字符串. 但是如果你在浏览器控制台中输出 JSON.stringify(window). 如果期望输出一段文字, 可能会 ...

  7. Vim设置括号自动补全和快速跳出

    一.设置括号自动补全 inoremap ' ''<ESC>i inoremap " ""<ESC>i inoremap ( ()<ESC&g ...

  8. hadoop集群的安装

    Hadoop集群安装 1.配置JDK环境和设置主机名,本地解析 JDK环境教程: http://www.cnblogs.com/wangweiwen/p/6104189.html 本地解析: vim ...

  9. umount 卸载 无响应的 NFS 文件系统

    当NFS Client 无法访问 NFS Server的适合,在Client上df操作等就会挂起. 这个适合需要将挂载的NFS卸载掉.在不知道挂载点的情况下,可以使用nfsstat -m 命令来查看. ...

  10. iOS 自定义UINavigationController返回按钮

    主要代码如下: //自定义导航栏返回按钮 self.navigationItem.leftBarButtonItem = ({ //导航栏返回背景视图 UIView *view = [[UIView ...