音频重采样 Ubuntu + Qt + ffmpeg

为什么要进行音频重采样

  1. 从设备采集的音频数据与编码器要求的数据不一致
  2. 扬声器要求的音频数据与要播放的音频数据不一致
  3. 更方便运算,例如:处理回音消除时,需要将音频数据转换成单声道的数据,方便计算

ffmpeg重采样主要API

swr_init : 初始化重采样上下文
swr_alloc_set_opts: 设置重采样参数
swr_convert: 重采样
swr_free: 释放重采样上下文空间

重采样

  1. 初始化重采样上下文
// 初始化重采样上下文
SwrContext* init_swr()
{// 重采样上下文SwrContext *swr_ctx = NULL;swr_ctx = swr_alloc_set_opts(NULL,                // ctxAV_CH_LAYOUT_STEREO, // 输出channel布局AV_SAMPLE_FMT_FLT,   // 输出的采样大小44100,               // 输出的采样率AV_CH_LAYOUT_STEREO, // 输入的channel布局AV_SAMPLE_FMT_S16,   // 输入的采样大小44100,               // 输入的采样率0, NULL);if (!swr_ctx){printf("Failed to init SwrContext, \n");return NULL;}if (swr_init(swr_ctx) < 0){printf("Failed to init SwrContext, \n");return NULL;}return  swr_ctx;
}
  1. 进行重采样,并将重采样的音频数据写入文件
    // 初始化重采样上下文SwrContext *swr_ctx = init_swr();// 初始化重采样缓冲区uint8_t **src_data = NULL;int src_linesize = 0;uint8_t **dst_data = NULL;int dst_linesize = 0;// 创建输入缓冲区av_samples_alloc_array_and_samples(&src_data,  // 输入数据的缓冲区地址&src_linesize,  // 缓冲区大小2,          // 通道数512,        // 单通道采样个数//注:512=2048//2/2。采集的每包数据2048个字节,采样大小为16位,即2个字节,双通道采集。所以2048除以2为每个通道采集的字节数,再除以2(采样大小,2个字节),为每个通道采样的个数AV_SAMPLE_FMT_S16,  // 采样大小0);// 创建输出缓冲区av_samples_alloc_array_and_samples(&dst_data,  // 输出数据的缓冲区地址&dst_linesize,  // 缓冲区大小2,          // 通道数512,        // 单通道采样个数AV_SAMPLE_FMT_FLT,  // 采样大小0);int swr_num = 0;// 我的环境是Linux虚拟机,每次获取的音频数据只有64个字节,而ffmpeg中最低采样个数为32个,所以只有 64 字节数据时,无法进行重采样。所以这里增加了一个存储音频数据的缓冲区,等缓冲区达到一定大小时再进行重采样。uint8_t *bufferData = new uint8_t[2048];// 从设备读取数据while ((ret = av_read_frame(fmt_ctx, &packet) == 0) && m_status){av_log(NULL, AV_LOG_INFO, "Packet size: %d(%p), count = %d\n",packet.size, packet.data, count++);#ifdef false // 错误代码,swr_num到2048以后不进if中保存数据,直接到else中使用缓冲区进行重采样,但是此时av_read_frame也读了一包数据,没有放到bufferData中,所以会丢掉一包数据。就是每读2048字节的数据就会漏掉一包数据if (swr_num < 2048){for (int i = 0; i < packet.size; ++i){bufferData[i + swr_num] = packet.data[i];}swr_num += packet.size;}else{
#else// 更正if (swr_num < 2048) {for (int i = 0; i < packet.size; ++i){bufferData[i + swr_num] = packet.data[i];}swr_num += packet.size;}else{//把最后一次判断未进if中存放的一包数据放入临时缓冲区bufferData中for (int i = 0; i < packet.size; ++i){bufferData[i + swr_num] = packet.data[i];}swr_num += packet.size;
#endifswr_num = 0;memcpy(src_data[0], bufferData, 2048);// 重采样swr_convert(swr_ctx,                        // 重采样上下文dst_data,                       // 输出缓冲区512,                            // 输出每个通道的采样数(const uint8_t **)src_data,     // 输入缓冲区512);                           // 输入每个通道的采样数// 写入文件//fwrite(packet.data, packet.size, 1, outFile);fwrite(dst_data[0], 1, dst_linesize, outFile);fflush(outFile);}// 释放packet空间av_packet_unref(&packet);}// 释放重采样上下文av_free(swr_ctx);

我的环境是Linux虚拟机,每次获取的音频数据只有64个字节,而ffmpeg中最低采样个数为32个,所以只有 64 字节数据时,无法进行重采样。所以代码中增加了一个存储音频数据的缓冲区,等缓冲区达到一定大小时再进行重采样。非虚拟机环境,不需要增加缓冲区,直接使用**memcpy(src_data[0], packet.data, packet.size);**即可。

我的设备采集音频的参数是采样率44100, 双声道,16位采样大小。重采样后的音频数据为采样率44100,双声道,采样大小32位。

下图显示了未进行重采样时音频文件的数据格式:
未进行重采样

使用ffplay播放重采样后的音频文件,可以看到重采样后的音频数据发生了变化,由16位采样大小变成了浮点类型,32位。
在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部