You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用swr_convert重采样音频帧出现大量噪声问题求助

音频重采样出现噪声的问题分析与修复

你的代码存在几个关键问题,直接导致重采样后出现明显噪声:

1. 每帧重新创建并初始化SwrContext

SwrContext内部维护着重采样滤波器所需的延迟缓存(历史数据),每处理一帧就重新创建并初始化SwrContext会直接丢弃这些缓存,破坏音频帧之间的连续性,引发断裂和噪声。

2. 错误覆盖输出样本数计算

你先基于delay + aframe->nb_samples计算了包含延迟缓存的输出样本数,随后又将其覆盖为仅基于输入帧样本数的结果,导致输出缓冲区无法容纳所有待转换的样本,部分数据丢失引发噪声。

3. 未处理SwrContext缓存中的剩余样本

重采样完成后,SwrContext内部可能残留未输出的样本,若不主动获取这些数据,会导致音频不完整,产生断音或噪声。


修复后的代码示例

步骤1:将SwrContext初始化移到帧处理循环外

仅初始化一次SwrContext,在整个重采样流程中复用:

// 可定义为全局变量、类成员变量,或在循环外创建
SwrContext* swr_ctx = nullptr;

// 初始化SwrContext(仅执行一次)
void init_swr(AVSampleFormat in_fmt, AVSampleFormat out_fmt, 
              uint64_t in_ch_layout, uint64_t out_ch_layout,
              int in_sample_rate, int out_sample_rate) {
    swr_ctx = swr_alloc();
    if (!swr_ctx) {
        throw std::runtime_error("Failed to allocate SwrContext");
    }

    av_opt_set_int(swr_ctx, "in_sample_fmt", in_fmt, 0);
    av_opt_set_int(swr_ctx, "out_sample_fmt", out_fmt, 0);
    av_opt_set_int(swr_ctx, "in_channel_layout", in_ch_layout, 0);
    av_opt_set_int(swr_ctx, "out_channel_layout", out_ch_layout, 0);
    av_opt_set_int(swr_ctx, "in_sample_rate", in_sample_rate, 0);
    av_opt_set_int(swr_ctx, "out_sample_rate", out_sample_rate, 0);

    if (swr_init(swr_ctx) < 0) {
        swr_free(&swr_ctx);
        throw std::runtime_error("Failed to initialize SwrContext");
    }
}

步骤2:逐帧处理的正确逻辑

AVFrame* resample_frame(AVFrame* aframe, int output_sample_rate, 
                        AVSampleFormat output_format, uint64_t output_channel_layout) {
    if (!swr_ctx) {
        throw std::runtime_error("SwrContext not initialized");
    }

    AVFrame* output_frame = av_frame_alloc();
    if (!output_frame) {
        throw std::runtime_error("Failed to allocate output AVFrame");
    }

    AVRational output_time_base = {1, output_sample_rate};
    output_frame->format = output_format;
    output_frame->channel_layout = output_channel_layout;
    output_frame->sample_rate = output_sample_rate;

    // 计算输出样本数:包含延迟缓存 + 当前输入帧样本
    int64_t delay = swr_get_delay(swr_ctx, aframe->sample_rate);
    int output_nb_samples = av_rescale_rnd(
        delay + aframe->nb_samples, output_sample_rate, aframe->sample_rate, AV_ROUND_UP);

    if (av_frame_get_buffer(output_frame, 0) < 0) {
        av_frame_free(&output_frame);
        throw std::runtime_error("Failed to allocate buffer for output AVFrame");
    }

    // 转换当前输入帧
    int ret = swr_convert(swr_ctx,
                          output_frame->data, output_frame->nb_samples,
                          (const uint8_t**)aframe->data, aframe->nb_samples);

    if (ret < 0) {
        av_frame_free(&output_frame);
        throw std::runtime_error("Failed to resample audio data");
    }

    // 更新输出帧的实际有效样本数
    output_frame->nb_samples = ret;

    // 所有输入帧处理完成后,需调用以下代码获取缓存中剩余样本
    // ret = swr_convert(swr_ctx, output_frame->data, output_frame->nb_samples, nullptr, 0);
    // if (ret > 0) {
    //     // 处理剩余样本
    // }

    // 设置PTS
    if (aframe->pts != AV_NOPTS_VALUE) {
        output_frame->pts = av_rescale_q(aframe->pts, {1, aframe->sample_rate}, output_time_base);
    } else {
        output_frame->pts = AV_NOPTS_VALUE;
    }

    return output_frame;
}

步骤3:重采样结束后清理资源

void cleanup_swr() {
    if (swr_ctx) {
        swr_free(&swr_ctx);
        swr_ctx = nullptr;
    }
}

额外注意事项

  • 若输入音频的格式、通道布局或采样率发生变化,需重新初始化SwrContext。
  • 所有输入帧处理完成后,必须调用swr_convert(swr_ctx, ..., nullptr, 0)获取缓存中剩余的样本,避免音频结尾数据丢失。

内容的提问来源于stack exchange,提问作者Bruce Hu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:29:52