环境是ubuntu18.04+Qt5.9.2+ffmpeg4.3,大致流程是从音频设备中实时读取音频数据,并将音频数据进行重采样然后使用fdk-aac对数据进行编码,最后保存到文件中去。
1.首先是在ubuntu18.04下,使用ffmpeg命令得到的音频数据参数为:采样率为48000,数据格式为S16le,通道数为2,而fdk-aac编码器所需要的数据参数是:采样率44100,数据格式为S16le,通道数为2,所以需要对原始数据进行重采样才能把数据丢给编码器对数据进行编码。从音频设备上读取的一帧数据大小是64字节,单通道采样数=64/2/2=16,不能直接对其进行重采样,好像最小的重采样数不能小于32,反正我在直接进行重采样得到音频文件播放啥声音都没有。那么到底应该凑够多少帧数据后重采样是一个问题,我一开始想的是凑够1024个采样数然后进行重采样,因为在给fdk-aac送数据的时候,送的每一帧数据的采样数要是1024编码器才能正常进行编码,所以我想到的是直接把数据积累到1024个采样数,重采样后,把数据直接送给编码器进行编码,事实上这种方案是行不通的,我这样做了之后得到的音频数据,播放出来发现有滋滋的声音而且声音变快这两个特征,后来看了文章,链接:速度变快和滋滋声的问题分析,大佬给了一种方案,就是读取设备480次后,然后再对这些数据进行重采样,再进行编码,要使480与441进行对应,这样才能正确进行重采样,刚学不久还不是很懂,以后理解更深刻了在来填坑。这种方案理论上是可以的,重采样后的数据播放正常。但是问题又来了,读取设备480次后单通道采样数就是480*16,重采样后就是441*16,到时候还需要分割frame,因为fdk-aac编码器它要的采样数是1024
对于第一个问题给出我自己的解决方法,就是在使用swr_convert函数进行重采样的时候,源缓冲区单通道的采样数src_nb_samples为480,输出缓冲区单通道采样数dst_nb_samples为441,这样就能使480对应441了,重采样后的音频能够正确进行播放
2.经过重采样后的数据不能直接丢给编码器直接编码,因为编码后单通道采样数是441,2通道,那么就是882,而fdk-aac编码器需要的采样数是1024,给出我解决此问题的方法:使用ffmpeg提供的一个先进先出的缓冲区av_audio_fifo,在重采样后把数据放到此缓冲区,等到满足编码器的条件后,在取出数据,把数据丢给编码器进行编码。
3.当停止录数据,注意有几个缓冲区的数据还要进行编码,1:如果第一种用了缓冲区buf1,其中包含未重采样的数据data1;2.第二种用了缓冲区buf2,里面数据是重采样后但是未编码的数据data2;3.编码器里面缓冲区没有进行编码的数据,我自己处理的顺序是:先将第一个缓冲区buf中的数据进行重采样然后丢到buf2中,然后将buf2中的数据按照规则丢给编码器,对第三种数据丢一帧空数据进去,强迫编码器将缓冲区中的数据进行编码。
4.如果采样后的数据是aac格式的话,播放如果使用ffplay播放,不需要加参数,如:ffplay audio.aac。如果你加了参数播放的话:ffplay -ar 44100 -ac 2 -f s16le -i audio.aac,那么就是报错:如截图
就是上述情况,其实数据已经编码并写入了文件,只是播放格式出错了,所以导致了出现了这种错误。
补充一:重采样之后有电流声,可能这个问题导致编码之后仍然会有电流声,主要是在重采样之后写入文件的数据不能直接是dst_linesize,而是要计算一下,如:buf_size = av_samples_get_buffer_size(&dst_linesize,2,dst_nb_samples,AV_SAMPLE_FMT_S16,1);重采样之后不要直接写入文件,会有电流声,经过这样计算一下,在fwrite的时候,大小填buf_size就不会有问题。
学了一个月音视频小菜鸡的一些问题总结,有啥错误大家可以一起交流。以后有这方面的问题,会来补充!
把我觉得比较核心的代码贴在下面:
while((ret = av_read_frame(av_fmt_ctx,pkt)) == 0 && 1 == MyThread::status){//从中读取数据并且读取的状态为true才继续进行录音
av_audio_fifo_write(audio_fifo,(void **)&pkt->data,pkt->size>>2);
if(src_nb_samples == av_audio_fifo_size(audio_fifo)){//达到了输入缓冲区的单通道采样数就进行重采样
av_audio_fifo_read(audio_fifo,(void **)src_data,src_nb_samples);
//重采样
swr_convert(swr_ctx, //1.重采样的上下文
dst_data, //2.输出缓冲区
dst_nb_samples, //3.单通道的采样个数
(const uint8_t **)src_data, //4.源输入缓冲区
src_nb_samples); //5.单通道的采样个数
buf_size = av_samples_get_buffer_size(&dst_linesize,2,dst_nb_samples,AV_SAMPLE_FMT_S16,1);
av_audio_fifo_write(frame_fifo,(void **)dst_data,buf_size>>2);//将重采样后的数据缓冲到frame_fifo,当攒到512送给编码器进行编码
if(av_audio_fifo_size(frame_fifo) >= 512){
av_audio_fifo_read(frame_fifo,(void **)&frame->data[0],512);
encode(cod_ctx,frame,newpkt,outfile);
}
}
av_packet_unref(pkt);//对之前的packet进行解引用
}
完整的代码链接:音频重采样