对于下面data和linesize的解释(参考下面3.4中的av_samples_alloc_array_and_samples函数说明):
1)data是通道的意思,例如双通道,data[0]代表左声道,data[1]代表右声道。
2)linesize为采样个数的最大大小字节空间。
例如aac,64位,双通道,则对于交错模式最大为:linesize = 2 x 1024 x 8 = 16384。此时也是一个音频帧的大小。
对于平面模式最大为:linesize = 1024 x 8 = 8192,平面模式时会有多个平面通道,例data[0],data[1],注意,断点看时参不一定是最大大小字节空间数,此时linesize不代表什么,他只是代表单个通道的所有样本数所占的字节数。
3)额外注意,已经写入的音频文件是交错模式,而FFmpeg库默认是平面模式处理的,所以将音频文件读到内存使用FFmpeg处理时需要转成平面模式;同理FFmpeg写入文件需要转成交错模式再写入。
一 音频通道数、采样率、采样个数(样本数)、采样位数的概念
1 通道数: 个人理解,就是同时有个几个设备在进行音频的采样,这样对上面的公式更好理解,最少为1,一般通道数越多,音质越好。
2 采样频率: 也称为采样速度,定义了每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)来表示。
3 采样位数(采样格式): 既然采样频率表示每秒采样的个数,那么如何描述每个采样点呢?用什么方法独立每个采样点值的区别呢?也就是如何度量每个采样点,而这正是采样格式出现的意义。通常使用16bit(2字节),也就是2的16次方,共有65536个不同的度量值,这样采样位数越高,音频度量化的就越精细,音质同样也就越高。
简括采样位数就是,例如我1s采样了10个采样点,那么我需要区分这10个采样点,就需要给它一个范围值区分,一般以2字节16位的来保存这个值,所以既然每个采样点占2字节,那么
所有采样点的总字节=采样个数 x 其所占字节数。即10 x 2 = 20。若为双通道采样,那么就是2 x 10 x 2 = 40。
4 采样个数(样本数)(nb_samples):
采样个数就是采样的个数。
对于采样频率,采样频率是一秒采样的个数,例如48000HZ,每秒采样个数为48000,44100HZ,每秒采样个数为44100。
而对于一帧音频的采样个数,AAC固定一帧采样1024个,MP3格式则为1152。
至于为何固定,下面会进行解释。
二 计算一帧音频的大小、每秒播放的音频字节大小、一帧音频的播放时长
1 一帧音频的大小(字节) = 通道数 x 采样个数 x 采样位数。例如该音频帧是FLTP格式的PCM数据,那么就是aac,所以一帧中包含1024个采样个数,并且是双声道的话,那么该音频帧包含的数据量是 2 x 1024 x 4 = 8192字节。
若格式改成AV_SAMPLE_FMT,那那么采样位数是64位8字节,数据量为 2 x 1024 x 8 = 16384字节。
2 每秒播放的音频字节大小(字节) = 通道数 x 采样个数 x 采样位数。公式是一样的,但是由于求的是每秒的数据量而不是一帧的数据量,所以我们需要知道它的采样频率。 例如当采样频率为48kHZ时,一秒包含48k个采样个数而不是1024个,同样是双声道,FLTP格式,那么每秒的数据量是 2 x 48000 x 4 = 384000字节。
3 一帧音频的播放时长
以采样率44100HZ来计算,每秒44100个sample,而正常一帧为1024个sample,由于比是相等的,可知每帧播放时间/1024 = 1000ms/44100,得到每帧播放时间=1024*1000/44100 = 23.2ms(更精确的是23.21995464852608)。
或者用另一种方式去理解公式,1s显示的帧数 = 44100 / 1024 = 43.06640625帧。所以每一帧的播放时长 = 1s / 43.06640625 = 1000ms / 43.06640625 = 23.21995464852607ms。和上面的公式一样(浮点数尾部运算存在极小误差是正常)。所以转换一下公式:
一帧播放时间(毫秒) = 1000ms / (44100 / 1024) = 1000ms * 1024 / 44100 = 23.2ms(更精确的是23.21995464852607)。
所以最终都是转化成下面的公式:
一帧播放时间(毫秒) = nb_sample样本数 * 1000 / 采样率 。
播放时长的精度是否可以舍弃呢?
答:不能。
例如当采样频率为44.1kHZ:
1)一帧播放时间(毫秒) = nb_sample样本数 * 1000 / 采样率 = 1024 * 1000 / 44100 = 23.21995464852608ms -> 约等于23.2ms,精确损失了0.011995464852608ms,如果累计10万帧,误差 > 1199毫秒,如果有视频一起的就会出现音视频同步的问题,如果按着23.2msm去计算pts(0 23.2 46.4 …)就会有累积误差。
这里播放10万个音频帧大概需要的时长:((23.21995464852608ms x 100000) / 1000)s / 60 = 38.699924min。乘以10万得到ms,除以1000单位变成秒,除以60单位变成分钟。
采样频率为48kHZ时的一帧播放时长:
2)1024 * 1000 / 48000 = 21.33333333333ms。同理,精度不能舍弃。
以上是AAC格式的播放时长。
下面是MP3的一音频帧的播放时长:
- 1)44.1kHZ,一帧播放时间(毫秒) = nb_sample样本数 * 1000 / 采样率 = 1152 * 1000 / 44100 = 26.1224489795918367ms -> 约等于26.2ms。
- 2)48kHZ,一帧播放时间(毫秒) = nb_sample样本数 * 1000 / 采样率 = 1152 * 1000 / 48000 = 24ms,刚好能整除。
解释为何AAC和MP3为何一帧固定采样个数。
从我自己的理解来看,固定AAC为1024,MP3为1152肯定是有道理的,从一帧音频帧的播放时长中就可以看出,范围在21ms,24ms,26ms范围左右,而视频一帧的时长一般是40ms,人体对图片变化的感知也在20-60ms内感知良好,所以个人认为采样数固定,是在考虑人眼,与音视频同步的方便程度,音频压缩的质量等方面因素后,最终确定下来的采样数。
三 音频重采样
3.1 什么是重采样
将音频三元组(采样率,采样大小和通道数)其中任意一个值发生改变就称为重采样, 例如48000/32/2 转成 44100/16/2。
3.2 为什么要重采样
- 1)从设备采集的音频数据与编码器要求的数据不一致。
- 2)扬声器要求的音频数据与要播放的音频数据不一致。
- 3)更方便运算(回音消除须使用单声道,需要先转换)。
3.3 重采样的步骤
- 1)创建重采样上下文。
- 2)设置参数。
- 3)初始化重采样。
- 4)进行重采样。
3.4 几个重要的API
- swr_alloc_set_opts(创建上下文,设置参数)。
- swr_init(初始化)。
- av_samples_alloc_array_and_samples(给输入源分配内存空间,其中参1为输入源,参2为采样个数的最大大小字节空间(src_linesize),例如aac,64位,双通道,则对于交错模式最大为:src_linesize = 2x1024x8 = 16384,对于平面模式最大为:src_linesize = 1024x8 = 8192,平面模式时会有多个平面通道,例data[0],data[1],注意,断点看时参不一定是最大大小字节空间数,这就是对于参2行大小的解释。参345就是参2对应的变量,参6是内存对齐,赋0即可)。
- swr_convert(具体音频帧转换)。
- swr_free(释放上下文占用资源)。
3.5 重采样时,输出的样本数怎么求
进行重采样时,输入和输出时间是需要相等的。所以我们可以根据上面的公式去求出输出的样本数即采样点。
根据上面一帧音频的播放时长公式有,输入输出都去掉1000,有下面的公式:
可以看到,上面公式的规律:
- 1)当高采样频率转低采样频率,在输入的参数一定即输入的样本数和输入采样频率一定时,输出的样本数随着低采样率而变低。
- 2)当低采样频率转高采样频率,在输入的参数一定即输入的样本数和输入采样频率一定时,输出的样本数随着高采样频率而变高。
- 3)并且注意,计算输出的样本数后,如果是一个浮点数,将对其采用向上取整。
即若计算出来的out_count=940.8,那么向上取整后为941。
代码的实现常常利用av_rescale_rnd:
它的作用是计算 “a * b / c” 的值并分五种方式来取整。a,b,c分别代表参数123,参4是取整的方式,例如这里的AV_ROUND_UP。
-
//AV_ROUND_UP代表向上取整
-
dst_nb_samples = av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);