使用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
相关产品推荐
相关产品推荐

