在上一篇我们只是分析了fmp4的结构,也就是box的结构组成,但是还是需要进行remuxer
才可以将flv转成fmp4。
Metadata
在flv那边我们有提到parseAACAudioSpecificConfig
、parseAVCDecoderConfigurationRecord
,其实在那里会通过this._onTrackMetadata
这个方法将对应的音视频的metadata
传给mp4-remuxer
去处理。而script tag
的metadata
没有交给fmp4处理,相信大家也明白,这个数据主要是一些媒体信息。
Audio Metadata
让我们再回到flv-demuxer
,在解析Audio时会优先解析Audio Metadata,也就是音频的Audio Object Type
,channel
,codec
等信息。解析完毕之后会将这些信息都传递给mp4-remuxer
去处理,生成metabox
。
1 | // 'audio/mp4, codecs="codec"' |
Video Metadata
同理,Video在解析数据之前,第一个会是AVC Decoder Configuration Record,即会把chromaFormat
、duration
、frameRate
、presentHeight
、presentWidth
等数据一样传递给mp4-remuxer
,跳过 ftyp 不说,会跟Audio一样产生一个 moov 的box。这里只挑跟Audio不同的数据。
1 | // 'video' |
Data
读取完 metadata 之后,之后读取到 audio 的,就是AAC raw frame data数据了,然后将数据保存到 audio track 中的 sample 数组;当然 video 也是一样,之后的数据就是nalus了,然后同时也将数据保存到 video track 中的 sample 数组。待抓取到的媒体流数据解析完成之后,将解析完的数据帧发送给remuxer
。
1 | this._onDataAvailable(this._audioTrack, this._videoTrack); |
在这里注意一下,在进行remuxVideo
跟remuxAudio
之前,会先计算一个base dts
,这个是什么呢?之后再说。
Video Data
我们拿 video data 来分析,实际上分析了 video data,那么 audio 的部分也是大同小异的了。这里先将 nal 的数据转为 mdat box,这部分参考FMP4结构的BOX部分。这里先写入的是 nal 的头部信息。也就是size、type这些。并不包括真正的数据。
1 | let offset = 8; |
为了修正后续的 samples 能与上一次的 samples 形成连续性(MSE要求连续的segments之间是不能存在时间戳间隙),所以取这次 samples 中第一个 sample 的时间戳,追加到上一次 samples 末尾的位置,根据差值(dtsCorrection),后面所有 sample 时间戳做相应平移。
1 | // calculate dtsCorrection |
下面的一步其实目的就很明了了,就是根据上面产生的差值,来给每一个 sample 时间戳做相对应的平移。并且输出 mp4Samples 。
还记得上面的mdatbox吗?我们之前只对它做头部信息处理,那么接下来的一步就是对它填充我们处理过后的数据。
1 | // Write samples into mdatbox |
生成 moof box。
1 | -> moof |
生成完之后需要对这一次的 sample list 清空。
1 | track.samples = []; |
最后将数据box合并,并将此次的数据派发出去。
Audio Data
Audio这里分为AAC跟MP3这两类音频解码格式。我们就针对AAC这一类来,MP3也是大同小异的,因为只有Firefox支持'audio/mp4,codecs="mp3"'
。calculate dtsCorrection
这部分就不多讲了。跟上面 Video Data 道理一样。但是这里作者做了一个兼容处理,就是为了解决 Edge seek 后卡住的 bug。作者这一块是这样解释的:
这里注意一下:videoSegment.beginDts < firstSampleDts
,假如该空音频是在第一帧,那么就没必要补帧了(AAC.getSilentFrame),补帧的道理也是为了使其数据连续性。Correct dts for each sample
这一步跟Video的处理差不多,只是多了一个large timestamp gap
的处理,这个会产生音视频不同步。剩余的后面不讲。因为看注释以及跟Video data差不多。
音视频同步原理
最后讲一下音视频同步的原理。每一帧视频或音频都有一个duration,认识这一点很重要。然后采样率(sample rate)指的是每秒钟对音频信号的采样次数,采样频率越高声音还原度越高,声音更加自然。单位是赫兹 Hz。这里我们拿44.1kHz来讲,实际还有其他的,具体可以看FLV解析-AudioTag(1)。
视频帧播放时间:
- H.264:
duration = 1000 / fps
,我们常见的 fps 有25,当然也有见过30的。那么 duration 就为40ms,也就是40ms一个视频帧。
音频帧播放时间:
- AAC:
duration = 1024 / sample_rate
,AAC音频一帧有1024个采样样本,我们假如 sample rate 为 44.1kHz,那么 duration 应该为 23.22ms左右。 - MP3:
duration = 1152 / sample_rate
,MP3音频一帧跟AAC不同,只有1152个采样样本,同样假设 sample rate 为 44.1kHz,那么 duration 为 26.12ms。
那么理论下的音视频同步如下:
1 | -------------------------------------------------------------- |
可以看得出,音视频的排列是按照起始时间戳从小到大来排序的,当然这部分可以允许些许波动,但也要注意一旦出现过大就会发现音视频不同步。例如:
当然这是理论上,还需要考虑到其他的影响因素,例如硬件,网速之类的情况。
Related Issues not found
Please contact @lucius0 to initialize the comment