原创文章,转载请联系作者
西北望乡何处是,东南见月几回圆。
https://github.com/JadynAi/MediaLearn/blob/feature-0.9/mediakit
昨风一吹无人会,今夜清光似往年。
主题
音频是流式数据,并不像视频一样有P帧和B帧的概念。就像砌墙一样,咔咔往上摞就行了。一般来说,AAC编码中生成文件这一步,如果使用的是OutputStream流写入文件的话,就完全不需要计算时间。但在音视频同步或者使用Android自带的MediaMuxer
来生成音频文件时,就需要计算音频帧的时间戳。
参考
本文所涉及到的计算方法和API,为在Android环境下。使用AudioRecord音频录制,MediaCodeC编码AAC格式音频,同时使用MediaMuxer封装AAC格式音频文件。
方法
AAC编码有两种计算时间戳的方式。第一种:使用PCM的数据量来计算;第二种:计算出AAC编码相应参数配置下,一帧的持续时间,再配合帧数来计算。
AAC编码、MediaMuxer生成文件伪代码
MediaCodeC的AAC编码流程不再赘述,这里用伪代码来代替。主要是为了体现在代码何处设置时间戳:
1 | // MediaCodeC获得可用输入队列 |
在以上的伪代码中,presentationTimeUs就是需要我们设置时间戳的地方
填充PCM数据后,在得到MediaCodeC输出后,使用MedaMuxer写入数据,生成AAC文件。
1 | path = 输出路径。后缀aac、或者mp4 |
需要注意的是:使用MediaMuxer生成AAC音频文件时,不需要添加AAC头信息,直接写入即可。
MediaMuxer写入文件时,BufferInfo这个参数就包含了这一帧数据的偏移、以及时间戳等信息。
更加完整的音频编码代码,请参考GitHub地址AudioEncoder
使用PCM的数据量来计算
PCM是没有经过压缩的纯音频数据,我之前写过一篇音频入门的文章初识音频,记录了一些PCM相关的常识问题,感兴趣的可以去看看。
PCM作为最原始的音频数据,可以根据大小来计算出时间,先给出公式:
1 | presentationTimeUs = 1000000L * (totalBytes / 2) / sampleRate |
这是配置为采样率sampletRate、采样位数为16bit、单声道的PCM文件时间戳计算方式
接下来我们来分析以上公式的计算由来:
假设有一段PCM文件,采样率为S
,采样位数为n
–(一般 采样位数的选择有4bit、8bit、16bit、32bit),声道为单声道。那么在1s内,这段PCM的大小为:
1 | size = S * n * 1,单位为bit |
众所周知,1 Byte = 8bit
, 1 Short = 16bit
。那么单位时间内,PCM的大小为:
1 | 以byte为单位 = S * n * 1 / 8 |
那么根据以上就可得到,配置参数为采样率sampleRate、16bit、声道为1的PCM文件,当传入编码器的总大小达到totalByte时,时间戳的计算方式:
1 | currents (微妙) = totalByte / (sampleRate * 16 * 1 / 8) |
当然如果选择以ShortArray来承载PCM数据的话,那么公式则变为:
1 | currents (微妙) = totalShort / (sampleRate * 16 * 1 / 16) |
使用AAC帧时间计算
当编码器每输出一次数据,即可视作输出一帧AAC数据。一帧AAC原始数据包括1024个sample,那么AAC音频文件1s内的帧数为:sampleRate / 1024 帧
。从而得到一帧AAC的持续时间为:
1 | perFrameTime (微妙) = 1000000L / sampleRate / 1024 |
已知每一帧的持续时间的话,那么只需要根据当前帧数,即可计算出当前的时间戳。