总结TS文件格式,早在几个月前就有了这个想法,但一直拖到今天才真正准备写一篇博文来介绍。
再不介绍的话,估计几月后又要去故纸堆里翻东西了,毕竟个人笔记中总结记录的东西太多,搞不好哪天给意外弄丢了。
该系列打算写三篇,本篇是第一篇,从局外人角度看TS封装;第二篇介绍封装的细节;第三篇介绍常见兼容性问题。
1. Android的实现
本次博文(下个博文介绍细节吧)以Android实现的MPEG2TSWriter.cpp来介绍,其包括两部分功能:ts_muxer + writer,前者是文件格式封装,
后者是写数据到文件。如果想读懂Android的实现,则需要对native层多媒体开发的基础库——foundation的实现有一定的了解,尤其是AMessage/
ALooper/AHandler/HanderReflect这些Android消息处理模型,否则,你可能都不知道函数调用到哪儿了。
当然,为了理解消息处理模型,我特地写了一个demo,抛开文件封装的细节来验证Android的消息模型。
同时,我也用另外一种方式,抛开Android依赖,使用C语言,实现了一个ts_muxer,参考这个开源项目(Pluto-Project中的一个子模块)。这个实现
的封装格式细节,大部分是移植Android的,当然,为了解决诸多兼容性问题,加入了我的一些优化。有需要的可以拿去,就当我是雷锋。。。
2. TS格式是什么?有什么特点?
TS格式跟MP4格式类似,是一种视频文件封装格式(当然,内部可以有声音数据)。
TS格式的存在,有其核心竞争力——录制中系统crash,已存入存储设备的数据,仍能回放。相反,MP4封装格式数据写入过程中,如果意外终止,
那么前面写入的数据就都废了,不能播放。
3. TS与MP4封装格式差异
上面第2条给出了结论,但是为什么是这样的结果?答案是——封装格式差异导致其能否在中断后进行回放。
再进行更细节解释:
3.1. MP4这种封装格式,只有在写完最后一笔音视频帧后,由其用户发起结束封装,才能写入文件头数据。因此,假如还没写头就意外crash,
文件头自然就写不到文件中,也就不能进行播放。
3.2. TS这种封装格式,每写入一帧数据,都会将头数据和编码数据一起打包,写入到文件中。而在播放时,即使某帧数据异常,而该帧后面的帧只要数
据正常,根据头信息,仍能抽取出该编码帧,就可以进行解码了。
3.3. 能否播放的一个前置条件是,能否从文件中抽取完整的编码帧出来,能的话就可以恢复回放,不能的话就无法回放。
4. 一个形象的比喻
上面第3段是用比较专业的术语来回答能否回放以及背后原因,但作为一个外行人来看,可能还是一头雾水,那么我就做个形象的比喻来讲背后的机制。
毕业论文,我们都写过,目录整理,是在什么时候做的?
当然是在每一个章节都仔细校对、排版完成后,才能进行!因为目录里要加入每一个章节的开始页码!
对了,MP4就是类似一本书,用文件头记录每一帧的起始地址、大小、时间戳等信息。
TS格式呢?其实也是类似一本书,但是这本书是特制的,现在读书的小朋友已基本不再使用了,那就是——毛笔字练习本。每一页只能写固定数量的字,
如果欲写入的一段故事内容已经完成,该页还空着几个空格,那么将这些空的地方也填充上固定字!下一个故事必须从下一页开始写起。
而TS文件格式呢,是按照188Bytes的包来写的,一个编码帧肯定不止188个字节(aac音频帧除外),因此,一个编码帧通常打包到若干个188Bytes的
基础包中,而在解封装(demux)时,再从这若干个188Bytes基础包中抽取出来。每帧的第一个188Bytes的包,会加入一些同步字节来标记帧类型、帧时间
戳等信息,用于分割前后不同的编码帧。
5. 应用场景
TS这种格式,通常用于误码严重的场景中,因为方便找到同步字节进行恢复。
例如,电视台的无线信号,传到家用电视机里,由于信道不稳定,可能产生了误码,这个没事,干脆就把该帧数据丢掉不要即可,等到检测到下一帧的
同步起始包,再进行新包的数据提取。再比如,行车记录仪类产品,一般也用该封装格式,当撞车发生时,记录仪设备可能瞬间物理损坏,如果是mp4格式,
则不能查看事故前的视频图像。