音频重采样:
改变音频的 sample rate(采样率)、 sample format(采样格式)、channel layout(通道布局)等参数,使之按照我们期望的参数输出。
相关参数解析:
采样率:采样设备每秒抽取样本的次数
采样格式及量化精度(位宽):
每种音频格式有不同的量化精度(位宽),位数越多,表示值就越精确,声音表现自然就越精准。
FFMpeg音频格式如下:
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
音频数据在声道中存储方式:
分片(plane)和打包(packed)
以双声道为例,
带P(plane)的数据格式在存储时,其左声道和右声道的数据是分开存储的,
左声道的数据存储在data[0],右声道的数据存储在data[1],每个声道的所占用的字节数为linesize[0]和linesize[1];
不带P(packed)的音频数据在存储时,是按照LRLRLR...的格式交替存储在data[0]中,
linesize[0]表示总的数据量 。上面ffmpeg音频格式源码有表明。
声道分布(channel_layout)
AV_CH_LAYOUT_STEREO(双声道)定义为:
#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
AV_CH_LAYOUT_SURROUND(三声道)定义为:
#define AV_CH_LAYOUT_SURROUND
(AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
音频的数据量
⼀帧音频的数据量(字节)
=channel数 * nb_samples样本数 * 每个样本占用的字节数(上面提到的枚举AVSampleFormat)
如果该⾳频帧是S16P格式的PCM数据,包含1024个样本,双声道,那么该⾳频帧包含的⾳频数据量是
2*1024*2=4096 Byte
AV_SAMPLE_FMT_DBL : 2*1024*8 = 16384 Byte
音频时间计算
以采样率 44100hz为例,每秒抽取样本数为44100,一帧样本数为1024,
一帧播放时间(转为毫秒)就为 1024*1000/44100 = 23.21995464852608 ≈ 23.2
精度损失了0.011995464852608ms,如果累计10万帧,误差>1199毫秒,
如果有音视频同步的问题。 如果按着23.2去计算pts(0 23.2 46.4 )就会有累积误差。
采样率转化中样本数的变化
由于每帧播放时间(1024*1000/44100 = 23.21995464852608)不变
44100Hz采样率 48000Hz
AV_SAMPLE_FMT_FLTP --转化后-> AV_SAMPLE_FMT_FLTP
1024样本数 1115(向上取整)
1024/44100 *48000 = 1,114.557823129252 ->向上取整 1115
函数swr_convert内部会缓存,这一次输出可能为1114 ,下一次为1115,***1115为输出最大采样点数***。
相关api分析
音频缓冲区——AVAudioFifo
AVAudioFifo是FFmpeg提供的一个先入先出的音频缓冲队列.
单位为样本数并非字节数
支持多通道的格式,不管是planar还是packed类型。
当写入一个已满的buffer时会自动重新分配内存。
av_audio_fifo_write(AVAudioFifo *af, void **data, int nb_samples);
将数据写入AVAudioFifo ,如果可用的空间小于传入nb_samples参数AVAudioFifo将自动重新分配空间。
int av_audio_fifo_size(AVAudioFifo *af);
返回可用于读取的采样数量
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) av_const;
根据时间,输入输出采样率,输入样本数算出输出样本数
int64_t swr_get_delay(struct SwrContext *s, int64_t base);
计算缓存中剩余采样点数
int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
const uint8_t **in , int in_count);
转换并返回成功转换采样点数