上一期文章我分享了一些视频播放里面的术语和基本概念。这一篇文章我会主要介绍容器(container format file)格式文件的细节,以最多见的MP4文件入手。而后会简短的介绍一个标准的播放器的启动,解析,播放流程。本篇仍是以基础知识为主,虽然很枯燥,可是对视频开发的学习有很是大的好处,我本身我的的感觉就是,若是在不少专有名字,概念都不熟悉的状况下,想要去阅读播放器源码会是至关困难的事情。好比Exoplayer,谷歌的分包策略就是根据播放器的组件来分包。若是不熟悉播放器的基础构建的话,连哪一个部分的代码在哪一个包都不知道。但愿你们若是真的想进阶的话仍是耐心的理解好每一个基础概念。html
- Mp4格式文件的构成
- Mp4头文件的构成
- 标准播放器的启动流程
- 在线视频播放的技术基础(online video streaming)
在上期咱们大概介绍了Mp4文件的结构api
可是这样抽象的介绍可能仍是比较难理解,咱们深刻一些。缓存
通俗的说,MP4实际上是一种格式的规范,这个规范是被ISO机构认证的,也就是说,只要你经过Codec生成了一个mp4文件,那么这个文件的格式必须是按照ISO机构的规矩来。。。。既然是规范,那么咱们看看到底ISO对mp4作了什么规范:app
请你们打开连接->ISO的mp4文件规范ide
你们可能会有点懵逼,看不懂。其实这个规范很好理解,它定义了一个MP4文件里面,哪些数据应该放在什么位置(以字节为单位),哪些数据的长度是多少。我截取了一段:post
你们看,上面这一段规范定义了ftyp这个头文件header所在的位置和长度(以字节为单位)。 至于这些头文件是有什么用,我在上一篇文章大概提到过,他们属于meta data的一部分。在本章我会更详细的介绍。学习
因此说,任何容器,包括mp4都是相似的结构化文件,只不过不一样的格式文件ISO对其有严格的要求,数据的摆放顺序,排列等等不一样而已。有兴趣的同窗能够对比一下rmvb,mp4,mkv这些格式的要求有什么不一样,优劣势各是什么。ui
关于mp4文件的头文件格式(meta data),苹果官网对其进行了详细的描述(这个介绍是基于QuickTime播放器支持的mp4文件来介绍的,quciktime播放器对mp4的要求有些许不一样,可是差异不大,咱们能够忽略):atom
咱们不追究太多细节,有兴趣的同窗能够本身查看,咱们专一于一些基础的信息。
首先,在Meta Data里面,每个Header,头文件,咱们都叫他们Atom Header(不知道咋翻译)。Atom Header分为Leaf Atom 和 Container Atom。前者表明一个链接着字符串信息的头文件,后者是一个包含了若干个子Atom的头文件,他们互相之间是有层级关系的(参考上图)。每次播放器获取了movie atom以后(moov),会根据层级关系,向下,或者向下读取相关的其余信息。每个头文件都会对它的子头文件保存位置的引用,因此只要根据mp4文件的规范获取了最顶级的头文件moov,就能够顺势往下读取其余头文件了。
咱们来看看mp4的头文件结构
看起来很复杂,可是对于一个播放器来讲,不少信息都不是必须。咱们须要知道的最重要的信息是采样索引表(Sample Table Atoms).对应图中“**stbl **”这个atom header。这个索引表保存了mp4文件全部的采样(sample)与视频时间的对应关系(通常以微秒为单位),还有包括每一个采样的大小,在mp4文件中的起始位置(以本身为单位)。
那么既然咱们已经知道一个容器文件的格式规范了,播放器就能够经过解析容器的头文件来控制播放(playback)了。
一般播放器由三个部分构成
- 读取器(Extractor)
- 渲染器(TrackRenderer)
- 加载控制器(Load Controller)
- 数据源(Source)
读取器负责从source文件读取数据,加载控制器负责控制读取数据的策略(好比说在线视频播放的时候缓冲策略),渲染器负责接收读取器读取的数据,并渲染到屏幕上。
在播放器能够把数据提交给渲染器以前,播放器须要把必需的头文件所有解析并存入内存,好比以前说的采样索引表。通常播放器在解析完毕后,会构建三个个表,一个存放时间对应采样索引,一个存放采样索引对应在mp4文件中的起始位置(以字节为单位),一个存放采样索引对应大小(以字节为单位)。如下图为例
假设播放器须要从第1微秒开始播放,那么须要把第1微秒的数据放入渲染器。因此会查找下面这三个表。
经过表1,咱们知道该微秒对应第1个采样(sample),从第一个和第二个表咱们知道,第1个采样的数据范围(在mp4文件内)是从第0字节到300(0+300)字节,那么播放器就会去读取这个范围的数据而且放入渲染器中进行渲染。
同时,加载器会基于当前已经缓存的数据,决定是否还须要不停的读取数据进入内存。通常来讲每一个播放器都有默认的缓存值,也会有一个基准线,只有当缓存足够数据才能放进渲染器进行渲染。
最后同理,当咱们拖动滑动控制器(SeekBar)想快进的时候,咱们和第一步同样,经过咱们想滑动的时间获取采样的索引,再从新开始读取数据。
综上所述,播放器在正式播放视频文件以前,必需要把头文件所有读取并解析(这会是一段很是耗时的程序),这也是在线视频播放的等待时间的瓶颈。在接下来的章节我会介绍自适应视频播放(Adaptive Streaming),这个技术的发明使得了分段式mp4文件(Fragmented Mp4)技术得以诞生,大大的减小了在线视频播放的等待时间。
在线视频的播放其实和播放本地视频的局别就是Extractor读取的Source,数据源不同,在线播放须要下载数据到内存,再交由Extractor读取分析。可是既然是在线视频播放,咱们确定不能把整个容器文件下载到内存或者硬盘再开始解析播放。咱们但愿能控制下载的进度,好比我当前在看第10s的视频内容,因此我只想缓存/下载视频内容到第20s的位置。
咱们俗称的渐进式下载(Progressive Downloading)就解决了这一难题。
说的好像是很吓人的黑科技啊!!!!
其实就是HTTP1.1协议支持的分段式下载而已。。。。。
在HTTP请求里面假如一个叫RANGE的header,放入起始字节和结束字节,就能够只下载对应部分的数据,这一header的支持也是各类下载软件实现断点下载的基础。每次断网的时候记录下来已经下载的数据的字节数,下次再下载的时候从字节数+1处从新下载而且写入原有文件就能够了。
分割线
因此此次分享就结束啦,下一期分享我会开始进入正题,在安卓平台里面,对视频播放的支持,像api啊等等,以及其变迁历史。
周末愉快!