相信在你的电脑里,必定存有一些已经下载好的视频文件,若是你硬说没有,那我相信你曾经总有吧?曾经也没有?那我想对你说曾经免费的时候你不下载,直到电影都收费才后悔那些年错过下载的大片。html
好了,言归正传,在平常咱们必定见过不少后缀为avi, mp4, rmvb, flv等格式的视频文件。而不多有人真正挖掘这些文件究竟是什么?其实以上格式都是封装视频的封装格式。ios
什么是封装格式 ?git
把音频数据和视频数据打包成一个文件的规范。不用封装格式差距不大, 各有千秋。github
从视频播放器播放一个互联网上的视频文件算法
须要通过如下几个步骤:解协议,解封装,解码视音频,视音频同步。若是播放本地文件则不须要解协议,其余步骤相同。小程序
为何要对视频数据进行编码bash
视频编码的主要做用是将视频像素数据(RGB,YUV等)压缩成视频码流,从而下降视频的数据量。举个例子:好比当前手机的屏幕分辨率是1280 * 720(即咱们平时在视频软件中可选的720P),假设一秒钟30帧(即1秒钟传输30张图片),那么一秒钟的数据为 1280 * 720(位像素)*30(张) / 8(1字节8位)(结果B)
,也就是一秒钟的数据量为3.456M数据量,一分钟就是207.36M,那么咱们日常看一部电影就是大约18G的流量,试想下若是是这样对于存储即网络传输是件多么恐怖的事情。网络
正是由于以上缘由,咱们须要对视频数据进行编码,以最小程序减少清晰度与最大程序下降数据量,而H264正是目前普遍使用的一种编码格式,下面咱们将主要介绍下H264的码流结构。post
刷新图像概念编码
在咱们的印象中,一张图片就是一张图像,而在H264中图像是个集合的概念。
帧、顶场、底场均可以称为图像。一帧一般就是一幅完整的图像。当采集视频信号时,若是采用逐行扫描,则每次扫描获得的信号就是一副图像,也就是一帧。当采集视频信号时,若是采用隔行扫描(奇、偶数行),则扫描下来的一帧图像就被分为了两个部分,这每一部分就称为「场」,根据次序分为:「顶场」和「底场」。「帧」和「场」的概念又带来了不一样的编码方式:帧编码、场编码。逐行扫描适合于运动图像,因此对于运动图像采用帧编码更好;隔行扫描适合于非运动图像,因此对于非运动图像采用场编码更好。
H264原始码流
StartCode : Start Code 用于标示这是一个NALU 单元的开始,必须是”00 00 00 01” 或”00 00 01”
NALU Header 下表为 NAL Header Type
例如:
00 00 00 01 06: SEI信息
00 00 00 01 07: SPS
00 00 00 01 08: PPS
00 00 00 01 05: IDR Slice
复制代码
什么是切片(slice)?
片的主要做用是用做宏块(Macroblock)的载体(ps:下面会介绍到宏块的概念)。片之因此被创造出来,主要目的是为限制误码的扩散和传输。
那么片(slice)的具体结构,咱们用一张图来直观说明吧:
上图结构中,咱们不难看出,每一个分片也包含着头和数据两部分:
什么是宏块?
宏块是视频信息的主要承载者,它包含着每个像素的亮度和色度信息。视频解码的主要工做则是提供高效的方式从码流中得到宏块中的像素阵列。
宏块的组成:一个宏块由一个16*16亮度像素和附加的一个8 * 8Cb和一个8 * 8Cr彩色像素块组成。每一个图像中,若干宏块被排列成片的形式。
下面是宏块的结构图:
切片(slice)类型跟宏块类型的关系
切片(slice)来说,分为如下几种类型:
P-slice. Consists of P-macroblocks (each macro block is predicted using one reference frame) and / or I-macroblocks.
B-slice. Consists of B-macroblocks (each macroblock is predicted using one or two reference frames) and / or I-macroblocks.
I-slice. Contains only I-macroblocks. Each macroblock is predicted from previously coded blocks of the same slice.
SP-slice. Consists of P and / or I-macroblocks and lets you switch between encoded streams.
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×1六、16×八、8×16 或 8×8 亮度像素块(以及附带的彩色像素);若是选了 8×8 的子宏块,则可再分红各类子宏块的分割,其尺寸为 8×八、8×四、4×8 或 4×4 亮度像素块(以及附带的彩色像素)。
B片:可包 B和I宏块,B 宏块则利用双向的参考图象(当前和 来的已编码图象帧)进行帧内预测。
SP片(切换P):用于不一样编码流之间的切换,包含 P 和/或 I 宏块
SI片:扩展档次中必须具备的切换,它包含了一种特殊类型的编码宏块,叫作 SI 宏块,SI 也是扩展档次中的必备功能。
H.264的码流结构并无那么复杂,编码后视频的每一组图像(GOP,图片组)都给予了传输的序列(PPS)和自己这个帧的图像参数(SPS),因此,总体给够以下
GOP 图像组主要形容一个I帧到下一个I帧之间间隔了多少帧,增大图片组能有效的减小编码后视频的体积,可是也会下降视频质量,至于怎么取舍,得看需求。
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: 以上括号()中的为类型描述
复制代码
I帧 | P帧 | B帧 |
---|---|---|
帧内编码帧 | 前向预测编码帧 | 双向预测编码帧 |
I帧一般是每一个GOP的第一帧,通过适度压缩,做为随机访问的参考点,可当作一个图片通过压缩后的产物 | 经过充分低于图像序列中前面已编码帧的时间冗余信息来压缩传输数据编码图像,也叫预测帧 | 既考虑与源图像序列前面已编码帧,也顾及源图像序列后面已编码帧之间的时间冗余信息来压缩传输数据量的编码图像,也叫双向预测帧 |
GOP是画面组,一个GOP是一组连续的画面。 GOP通常有两个数字,如M=3,N=12.M制定I帧与P帧之间的距离,N指定两个I帧之间的距离。那么如今的GOP结构是 I BBP BBP BBP BB I
一个序列的第一个图像叫作 IDR 图像(当即刷新图像),IDR 图像都是 I 帧图像。
I和IDR帧都使用帧内预测。I帧不用参考任何帧,可是以后的P帧和B帧是有可能参考这个I帧以前的帧的。IDR就不容许这样。
咱们能够经过第 一、二、三、四、5 块的编码来推测和计算第 6 块的编码,所以就不须要对第 6 块进行编码了,从而压缩了第 6 块,节省了空间
能够看到先后两帧的差别实际上是很小的,这时候用帧间压缩就颇有意义。 这里涉及到几个重要的概念:块匹配,残差,运动搜索(运动估计),运动补偿. 帧间压缩最经常使用的方式就是块匹配(Block Matching)。找找看前面已经编码的几帧里面,和我当前这个块最相似的一个块,这样我不用编码当前块的内容了,只须要编码当前块和我找到的快的差别(残差)便可。找最想的块的过程叫运动搜索(Motion Search),又叫运动估计。用残差和原来的块就能推算出当前块的过程叫运动补偿(Motion Compensation).