nodejs作为服务端语言,从理论上来说,解析视频文件应该没有问题。我们从比较流行h264编码的mp4视频出发,看是否能够解析出第一个关键帧,这样,我们就有了视频封面。在实操之前,我们有必要一起学习一下mp4以及h264的相关知识点。
mp4是一种封装格式,本身并不对视频或音频编码压缩,真正压缩的是h264规范。现在有几个问题需要解决:
- mp4中如何确定编码格式?
- 如何确定关键帧的位置?
- 取到原始数据后,如何解码还原关键帧?
下面就来逐个分析。
mp4中如何确定编码格式
一般方式上,我们只要用个小工具就可以看到编码格式,这样的小工具有很多,比如格式工厂、VLC等,自行百度即可。我想,背后的原理都是一样的,一定是根据mp4文件中某些字段进行判别的,到底是不是这样?确实是这样。
先看一下样例mp4视频的盒子结构,用的是mp4 reader工具解析的。
在mp4视频中,真正存放数据的是mdat盒子,而描述信息并不在里面,而是在moov盒子中各自的trak中,这里的trak可以理解为轨道,用过视频编辑软件的同学应该对此比较熟悉,音轨、视频轨、字幕轨道都可以独立存在,mp4中也是这样,在各自trak中,详细描述了这个轨道类型、时长以及编码方式,其中,轨道类型在trak->mdia->hdlr盒子中,如果看到字段vide
,对应视频轨道,如果看到字段soun
,对应音频轨道,目前了解这两点,其它暂不用管。可以借助下图帮助理解。
现在找到了视频轨道,接下来就是确定编码格式,视频编码格式常见的有H261、H263、H264、MPEG等,这里不展开,我们以使用相对广泛的h264编码为例,编码格式的字段也在mida盒子中,具体位置为moov->trak->mdia->minf->stbl->stsd,我们看到avc1
,从
MP4文件两种格式AVC1和H264的区别的文章中了解到,avc1就是H264的封装,H264有两种封装格式,avc1描述和h264描述,这个容易被忽略,avc1是不带起始码0x00 00 00 01的,而h264描述是带起始码0x00 00 00 01的,这一定在后面解码中会用到。
在H264之NALU解析-MP4 audio一文中提到H264有两种封装:
⼀种是annexb模式,传统模式,有startcode,SPS和PPS是在ES中。
⼀种是mp4模式,⼀般mp4 mkv都是mp4模式,没有startcode,SPS和PPS以及其它信息。被封装在container中,每⼀个frame前⾯4个字节是这个frame的⻓度。个人理解这就是上面所说的h264描述吧。
还有一文解码中的AnnexB和avcC两种分割数据方式中提到H264包装方式有两种,一种叫做 AnnexB,一种叫做 avcC。
可能三篇文章说的是同一个东西,个人觉得最后一种说法更专业,参考另外一篇码流格式: Annex-B, AVCC(H.264)与HVCC(H.265), extradata详解,我用的样例mp4文件中也找到了相关字段avvc
,说明是h264的avvc编码。
这样我们就确定了这个视频的编码格式了,就是h264编码
如何确定关键帧的位置
这个问题也很有意思,本来我们确定一个片段的位置只要知道关键帧在文件中的偏移量就行了,但是实际上要复杂的多,个人理解这里用的是一种分层管理的概念,类似于关系数据库中的关系表。了解这几张表之前,先说一下mp4的媒体数据结构,有几个概念:nalu、sample、chunk,nalu(Network Abstract Layer Unit)是抽象的网络层单元,包含了5种内容,分别是
- SPS:序列参数集,SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。
- PPS:图像参数集,对应的是一个序列中某一副图像或者某几副图像的参数。
- I帧:帧内编码,可独立解码生成完整的图片
- P帧:前向预测编码帧,需要参考其前⾯的⼀个I 或者B 来⽣成⼀张完整的图⽚。
- B帧: 双向预测内插编码帧,则要参考其前⼀个I或者P帧及其后⾯的⼀个P帧来⽣成⼀张完整的图⽚。
我们不用关心具体细节,这里就把帧内编码记下来,也就是I帧,到时候独立解码成完成图片。
下图很好地说明了h264视频流的构成,就是一个接一个的nalu构成的。
nalu介绍先放一边,我们再来看一下sample和chunk,他们的关系在本人看来可以理解为页和段落,chunk就是页,sample就是段落,一个chunk至少有一个sample,也可以多个,sample的大小也是不固定的,大小在别的表中有描述,sample中放的是什么,就是上面的nalu,video sample即为一帧视频,或一组连续视频帧,audio sample即为一段连续的压缩音频,它们统称sample。
了解了以上概念,我们大概清楚了如何找到这个关键帧,首先是找到相应的chunk位置,这个可以从stco表中获取到,其次是找到对应的sample,这个可以在stsc表中获取到,然后还要知道sample在这个chunk中的位置,这个偏移量可以在stsz这个表中获取到,整体来看还是比较复杂的,为什么不用一张表记录,这个就是标准的问题,谁让我们不是标准的缔造者呢?
找到了关键帧数据,是否可以马上还原出关键帧呢?答案是肯定的,但里面有很多细节,这一部分先到这里,下一篇将介绍如何还原这个关键帧。
参考资料:
- https://www.jianshu.com/p/7aa6b20f7cb7
- MP4格式详解 https://blog.csdn.net/DONGHONGBAI/article/details/84401397
- mp4解码介绍 https://blog.csdn.net/yu305306/article/details/80736988
- MP4文件两种格式AVC1和H264的区别及利用FFMPEG demux为h264码流事项 http://www.mworkbox.com/wp/work/314.html
- H264之NALU解析-MP4 audio https://blog.csdn.net/qq_15090773/article/details/120202112
- 解码中的AnnexB和avcC两种分割数据方式 https://blog.csdn.net/qq_34754747/article/details/122605822
- 在线mp4分析 https://www.onlinemp4parser.com/
这是本人在掘金上更文的一篇文章,转载到此:https://juejin.cn/post/7106790298637828132