转载自原文地址:https://juejin.cn/post/6901218687081480206
一、视频的编解码方式
谈到视频,我们经常会听到视频编解码、H.264等诸如此类的内容。那么视频编解码是什么意思呢?
视频编解码,其实就是对视频数据进行压缩和解压缩的过程,而在讨论到编解码时,又经常同H.264联系到一起,那么H.264又是什么?
H.264是一种视频压缩标准,简称AVG,一种被广泛使用的高精度视频的录制、压缩和发布格式。该标准引入了一系列新的能够大大提高压缩性能的技术,并能够同时在高码率端和低码率端大大超越以前的诸标准,可以说是一种压缩算法,但又不仅仅止步于此。
H.264是H.26X系列中的一种,目前最新是H.265,简称HEVC,在同样的画面质量下,后者的压缩率是前者两倍(比特率减少50%),是未来发展趋势,iOS上的Video ToolBox目前可支持H.265的编解码。
视频编码还有其他的标准,比如AMV、AVS等等,但主流基本是H.26X系列。
二、关于H.264
H.264是视频的原始码流,分为两层,分别是视频编码层(VCL)和网络提取层(NAL),前者是H.264编码/压缩的核心,主要负责将视频数据编码/压缩,再切分,后者负责格式化VCL数据并提供头信息,以保证数据适合各种信道和存储介质的传输,基本单位是NALU。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
通过上图可以看到,一个视频帧包含了一个或多个NALU,开头一般是SPS和PPS,这两个主要是用于初始化解码器所需要的参数信息,也是解码的关键,接下去就是I/P/B帧等NALU数据,而NALU细分下去还有切片、宏等单位,这里由于篇幅原因就不在展开述论。
三、视频帧相关概念
在视频的编解码中,经常会讨论到几个很关键的名词,如I帧、P帧、B帧等。接下来从编码和解码角度来理解它们。
从编码角度上来说:
- I帧:帧内编码帧, I帧通常是每个GOP(MPEG所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,作为随机访问的参考点,可以当成静态图像,I帧压缩掉视频的空间冗余信息;
- P帧:前向预测编码帧,通过将图像序列中前面已编码帧的时间冗余信息充分去除来压缩传输数据量的编码图像;
- B帧:双向预测内插编码帧,既考虑源图像序列前面的已编码帧,又顾及源图像序列后面的已编码帧之间的时间冗余信息,来压缩传输数据量的编码图像。
从解码角度上来说:
- I帧自身可以通过视频解压算法解压成一张单独的完整视频画面,所以I帧去掉的是视频帧在空间维度上的冗余信息;
- P帧需要参考其前面的一个I帧或者P帧来解码成一张完整的视频画面;
- B帧则需要参考其前一个I帧或者P帧及其后面的一个P帧来生成一张完整的视频画面,所以P帧与B帧去掉的是视频帧在时间维度上的冗余信息。
除次之外,还有PTS、DTS、GOP。
PTS是用来描述视频解码阶段的帧输出顺序,DTS用来描述视频解码阶段的帧解码顺序,在无B帧的情况下,PTS等于DTS,在有B帧的情况下,输出顺序需要参考PTS。
而GOP,描述的是两个I帧之间形成的一组图片。
第二部分 MP4解封装
一、MP4定义
MP4是我们常见的视频资源,但MP4的本质又是什么呢?和H.264有什么不可告人的秘密呢?
MP4也叫MPEG-4,是一种多媒体容器格式,后缀是.mp4,其容器格式借鉴了Apple的QuickTime的组织方式,即以一系列BOX为基本单位。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
二、Box理解
编辑切换为居中
添加图片注释,不超过 140 字(可选)
MP4文件由若干个box组成,box的示意图如上图所示:
- box由header和body组成,header指明了box的size和type;
- size包含了box的header和body整个大小;如果size为1,表示box的长度需要用更多的bits位来描述,可以通过一个64bits位的largesize来描述box的长度,如果size为0,表示该box为文件的最后一个box;
- type,通常使用4个ASCII码的字符如"ftyp"、“moov"等表示,这些box type是已经预定义好的,表示固定的含义,如果是"uuid”,表示该box为用户自定义拓展的类型,如果box type是未定义的,应将其忽略;
- box中可以包含box,这种box叫做container box;
- box分为两种,Box和Fullbox,FullBox是Box的拓展,header中增加了8位的version和24位的flags字段。
三、常见的MP4文件工具
- mp4box.js:一个在线解析mp4的工具。
- bento4:包含mp4dump、mp4edit、mp4encrypt等工具。
- MP4Box:类似于bento4,包含很全面的工具。
- mp4info.exe: windows平台图形界面展示mp4基本信息的工具。
我们使用mp4box.js,可以看到MP4文件由许多box组成,每个box包含不同的信息,以树形的方式组织,主要的box如下:
编辑切换为居中
添加图片注释,不超过 140 字(可选)
通过上表,我们可以看到有4个树节点:
- ftyp:文件类型;
- moov box:媒体的metadata的描述和信息;
- mdat:具体的媒体数据;
- free:无关紧要的内容。
四、基本Box介绍
File Type Box
ftyp简单的说就是为了标识它的developer是谁,兼容哪些标准等,如“mp42”表示它的major brand是MP4 v2,而“mp42”和“mp41”则表示它的compatible brands是MP4 v2和MP4 v1。
- major_brand:推荐兼容性的版本;
- minor_version:最低兼容性的版本;
- compatible_brands[]:所有的兼容性的版本。
Movie Box(moov)
包含了文件媒体的metadata信息,是一个container box,一般在ftyp后面。moov包含1个mvhd和若干个trak,其中mvhd是header box,作为第一个子box出现,trak包含一个trakc的相关信息,是一个container box。
Movie Header Box(mvhd)
mvhd(Movie Header Box),主要存放着视频文件的meta data,其中的time scale和 duration对文件的播放有着重要作用
- version:box的版本,0或者1,一般为0;
- creation time:创建时间(相对于UTC时间1904-01-01零点的秒数);
- modification time:修改时间;
- timescale:文件媒体在1秒时间内的刻度,可理解为1s长度的时间单元数;
- duration:该track的时间长度;
- rate:推荐播放速率,高16位和低16位分别为小数点整数部分和小数部分,[16.16]格式,该值为1.0(0x00010000)表示正常向前播放;
- volume:推荐播放音量,[8.8]格式,1.0(0x0100)表示最大音量。
- martix:视频变换矩阵
- newxt track id:下一个track使用的id
Track Header Box(tkhd)
- version:box版本,0或者1,一般为0;
- flags:24-bit整数,按位或操作结果值;
- track id:track id号,不能重复且不能为0;
- duration:track的时间长度;
- volume:[8.8]格式,如果为音频track,1.0(0x0100)表示最大音量,否则为0;
- width:宽度,[16.16]格式;
- height:高度,[16.16]格式,不必与sample的像素尺寸一致,用于播放时显示的宽高。
Media Box(mdia)
Media Boxyeshi 也是一个container box,其定义了track媒体类型以及smaple数据,描述sample的信息。 mdia包含:
- 一个Media Header Atom,即mdhd;
- 一个Handler Reference,即hdlr;
- 一个media information,即minf,解码的关键。
Media Header Box(mdhd)
mdhd和tkhd内容上大致一致,不过tkhd通常是指定的track设定的相关属性和内容,而mdhd是针对独立的media来设置的,字段包含
- version:box的版本,0或者1,一般为0;
- timescale:比mvhd中的timescale精度更高;
- duration:track的时间长度;
- language:媒体语言码,最高位为0。
handler Reference Box(hdlr)
hdlr解释了媒体的播放过程信息,可获取track类型信息,主要是有字段handler_type(uint32_t)区分,具体含义如下:
- handler type:该值为4个字符,会有以下取值:
- vide:视频;
- soun:音频;
- hint:这个特殊的track并不包含媒体数据,而是包含了一些将其他数据track打包成流媒体的指示信息。
Media Information Box(minf)
重要的容器box,存储了解释track媒体数据的handler-specific信息,media handler用这些信息将媒体时间映射到媒体数据并行处理,minf是一个container box,其实际内容由子box说明。 一般情况下,minf包含了一个header box,一个dinf和一个stbl,其中header box的数据类型有vmhd(视频轨)、smhd(音轨)、hmhd(hint track)、nmhd(null track),dinf为data information box,stbl为sample table box。
五、重要Box
Sample Table Box(stbl)
stbl包含了关于track中sample所有时间和位置的信息,以及sample的编解码等信息。利用这个表,可以解释sample的时序、类型、大小以及在各自存储容器中的位置。stbl是MP4文件中最复杂的一个box了,也是解开MP4文件格式的主干,是一个container box;
stbl包含:
- stsd:sample description box,样本的描述信息;
- stts:time to sample box,sample解码时间的压缩表;
- ctts:composition time to sample box,sample的CTS与DTS的时间差压缩表;
- stss:sync sample box,针对视频,关键帧的序号;
- stsz/stz2:sample size box,每个smaple的字节大小;
- stsc:sample to chunk box,sample-chunk映射表;
- stco/co64:chunk offset box,chunk在文件中的偏移。
在mp4文件中,sample是一个媒体流的基本单元,例如视频流的一个sample代表实际的nal数据。chunk是数据存储的基本单位,它是一系列sample数据的集合,一个chunk中可以包含一个或多的sample。
编辑
添加图片注释,不超过 140 字(可选)
Sample Description Box(stsd)
存储了编码类型和初始化解码器需要的信息。 stsd有多种类型的子box,具体取决于该track是视频或是音频或是其他,对于视频,若是通过使用了H.264编码的,子box为avc1,avc1的主要字段:
- type:avc1;
- width、height:视频像素宽高;
- horizresolution、vertresolution:每英寸的像素值,[16,16]格式的数据;
- frame_count:每个sample的视频帧数,默认是1;
- avcC:存放sps和pps内容。
Decoding Time to Sample Box(stts)
包含一个Sample编码时序表,通过这个表可以从解码时间映射到sample序号,表中的每一项是连续相同的编码时间增量(Decode Delta)的个数和编码时间增量,通过把时间增量累加就可以建立一个完整的time to sample表。
Composition time to sample box(ctts)
这个box提供了decoding time到composition time的offset的表,用于计算pts。
- 这个表在Decoding time和composition time不一样的情况下是必须的。
- 如果box的version等于0,decoding time必须小于等于composition time,因而差值用一个无符号的数字表示。
- sample_count:连续相同的offset的个数;
- sample_offset:CT和DT之间的offset。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
- DT(decoding time):编码时间;
- CT(composition offset):创作时间;
- Decode Delta:编码时间增量,通过前面提到的stts表可获取;
- Composition offset:显示时间同解码时间的差值,等于CT-DT,通过ctts获取。
由此可知,通过stts和ctts可以方向推出解码顺序和视频播放顺序,当无B帧视频时,ctts不存在,解码顺序和播放顺序一致。
Sync Sample box(stss)
它包含media中的关键帧的sample表,如果此表不存在,则每一帧都是关键帧。
主要字段sample_number:媒体流中同步的sample序号。
Sample Size Box(stsz/stz2)
包含sample的数量和每个sample的字节大小,box体积有点大,主要字段有:
- sample_size:指定默认的sample的字节大小,如果smaple大小不一样,则这个字段为0;
- sample_count:track中的sample的数量;
- sample_sizes:每个sample的字节大小。
Sample To Chunk Box(stsc)
meida中的sample被分组成chunk,chunk可以有不同的大小,chunk内的sample可以有不同的大小,通过stsc中的sample-chunk映射表可以找到指定的smaple的chunk,从而找到这个sample。结构相同的chunk可以聚集在一起形成一个entry,这个entry就是一个stsc映射表的表项。
主要字段说明:
- first_chunk:一组chunk的第一个chunk的序号; chunk的编号从1开始。
- samples_per_chunk:每个chunk有多少个sample;
- sample_desc_idx:stsd中的sample desc信息的索引。
Chunk Offset Box(stco/co64)
Chunk Offset表存储了每个thunk在文件中的位置,这样就可以找到媒体数据,而不用解析box了。
总结
感谢您阅读到这里,本次分享的内容只是视频方面的基础,过程有些枯燥,如若有什么描述不对或不得当,欢迎指出。
>>> 音视频开发 视频教程: https://ke.qq.com/course/3202131?flowToken=1031864
>>> 音视频开发学习资料、教学视频,免费分享有需要的可以自行添加学习交流群: 739729163 领取