视频是一个数据流,就像我们最初对这个世界的理解,像一团混沌,但是世界往下细分,会看到物种、细胞、分子、元素、质子、电子以及光子。视频这个数据流,如同世界一样,也是可以往下细分的,视频就是一帧一帧的图片,再结合运动压缩算法以及封装方法得到的序列化数据。
视频内三要素:封装(MP4、PES、TS、FLV等等)、编码(H264、H265、VP8、VP9)、图片(YUV图、RGB图、ARGB图)
图片
图片的产生来源于光学传感器,简单来说就是摄像头。传感器通过周期性曝光进行外部信息的采集,形成图片,图片本身具有空间分辨率(像素大小、dpi等信息)、色彩分辨率(黑白图和彩色图)。当然,除了光学传感器,目前也有红外传感器、激光传感器,能感知外部信息的,都可以作为图片信息来源的传感器,并形成不同性质的图片,图片总是以光学形式呈现给用户。
以光学传感器来说,如果每秒能够曝光60次,那么就意味着每秒能产生60张图片,每张图片的像素大小如果是1080P(1920×1080),并且是进行的RGB信息的采集,意味着一张图片的字节大小为(1920*1080*3)= 6M左右,而达到人眼视频能流畅的效果,至少每秒8帧,通常的视频在25帧左右,那么每秒的字节大小为6M*25=150M。
一个两小时的电影,图片的字节大小可以达到:2*60*60*150M=1T.按照这个估算,大家的硬盘应该放置不了那么多的电影。
视频,就是由具有时间戳的n张图片组成的,按照对应的时间间隔依次播放图片,既是视频的效果,倍速播放,则是把时间间隔缩短一倍,以此类推。
下面一段就来讲讲如何让大家的硬盘能够放的下足够多的电影,编码。
编码
编码,既是把图片字节数据量变小的过程。
经常见到的一种压缩编码效果,就是jpg、png,这些格式其实就是把原始图像数据进行压缩编码,从而达到减少数据量的办法。压缩又分为有损和无损压缩,有损压缩比远大于无损压缩比,但是对于有些行业,必须采用无损压缩,如医疗图像。
有损压缩为了达到更高的压缩比,那么对于一些小幅度的差异,会进行抹除,从而得到更好的压缩比,这种情况下,进行数据还原时,还原的数据和原始数据会有些许出入。而无损压缩,要确保数据还原后,和原始数据一致无二。
实际中我们经常看到直播视频、电影,采用的都是有损压缩。高清和普清,主打差异也是在这个点上,取决于有损比例的大小。
目前常用的视频流编码方式为H264和H265,265比264的压缩比更高,但是编解码开销更大。以H264来描述编码过程。
编码出来的图片至少包括如下两种类型:I帧(Intra-coded Frame)和P帧(Predictive Frame),还有B帧(Bidirectional Frame)之类的。
每张图片可以独立编码,编码出来的是I帧,压缩大概能达到6倍左右,也就是6M的图片会变为1M,看起来已经取得了可观的效果,但是运动编码的出现,让效果可以变得更好。
不管视频是静态画面也好,动态画面也好,都可以认为场景中画面的变化是一个渐变的过程,所以当前帧和前一帧的差异,会比原图像的信息量少很多,压缩效果自然就会更好。
而P帧就是把当前帧和前一帧做了差异,而后再进行了压缩编码,这个压缩比就可以很高,也是让我们看一部高清视频,而只需要2G的文件大小即可。
虽然压缩效果好了,但是在解码时,单独的P帧是无法自还原的,因为解码出来的数据是和前一帧的差异,所以必须有前一帧的数据,再整合这个差异,才能得到当前帧的数据。
而为了保持能够拖进和任意点进入浏览的效果,I帧会周期性的产生,通常1-2秒一个I帧。那么当你在1.5秒开始获取到视频流,需要在到达2秒时,获得第一个I帧,而后依次解码,才能获得可人眼浏览的视频。
一个H264的I帧,至少包含三个信息单元,SPS(NalUnit 0x67),PPS(0x68),I帧数据(0x65)。一个P帧,包含0x61的一个数据帧。 SPS和PPS包含了解码所需的必要信息。
看到了这里,一个视频流里的数据其实是由多个帧构成的,大致如下:
I P P P P P P P P P P P P P P P P P P P P P I P P P P P P P P P P P P ......I P P P
解码时需要分离出I帧,进行解码,获得第一张图片(覆盖缓存),而后解码P帧,融合前一张图片,得到当前图片(覆盖缓存),再解码P帧,融合前一张图片,得到当前图片(覆盖缓存)...... ,获得I帧,自解码获得图片(覆盖缓存)...... 。如此依次循环解码,则依次获得所有的视频图片,结合时间戳信息,可以进行正常、快速和慢速播放的效果。
备注:编码的帧内不包含时间戳信息
为了从数据流中分离帧信息,涉及到如何封装这些信息,以便进行解析。常用的一种简单封装是每个信息单元前面放置四个字节0x00 0x00 0x00 0x01,这四个字节作为帧开始的标识符,大多数解码器通过这个标识分离帧,进而进行解码。
封装
H264的RAW模式封装,就是使用四个字节0x00 0x00 0x00 0x01进行了每个NalUnit的分割,但它不封装时间戳信息,如果播放这类文件数据,播放器会默认按照每秒25帧(不同播放器会有出入)的帧率进行播放,如果实际视频帧率为60,那么播放的话就会有一个慢速的效果。正常应该1秒播放60帧,实际2秒才播放了50帧。
另外还有PES封装、TS封装、FLV封装、MP4封装、MKV封装等等,这些封装能够完整的封装视频数据以及时间戳等信息。
谈及封装的主要目的,在理解了上述信息之后,如果要搭建一个流媒体服务器,那么采集端在采集到视频数据,并能分离出视频帧,而后采用私有协议包装这些数据,包括帧的时间戳,发送给流媒体服务器,流媒体服务器再按照请求方的不同请求协议进行包装推送,即可完成一个流媒体服务器的搭建。
设备APP采集视频数据,按照私有封装把帧数据发送给流媒体服务器(当然也可以采用标准协议,如RTMP PUSH、RTSP Record),流媒体服务器收取到帧数据以及它对应的信息后,按照请求方的标准(RTSP、RTMP,HLS)或私有协议进行数据转发,实现视频流数据的分发。
封装的目的在于有效包装视频帧以及其相关信息(主要是时间戳),以提供序列化的效果,进而能够进行网络传输,接收方进行对应的反序列化,最终得到一串视频数据帧。