音频重采样 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,                // ctx
                                 AV_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;
#endif
            swr_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位。
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VectorAL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值