FFmpeg SwrContext单声道音频重采样出现噪音问题寻求修复
修复FFmpeg SwrContext重采样后音频噪音问题
我实现了一个基于FFmpeg SwrContext的单声道音频重采样函数,输入为float数组,搭配自定义write_wav函数将重采样后的音频保存为WAV文件。但重采样后的音频(如24000Hz转48000Hz)出现大量类似丢样的噪音,原始音频与重采样后音频的波形图差异明显,请问如何修复该问题?
重采样函数代码
int resample_and_save(float *audio_input, int input_sample_rate, int output_sample_rate, int input_num_of_samples, const char *output_file) { AVChannelLayout ch_layout = AV_CHANNEL_LAYOUT_MONO; SwrContext *swr_ctx = swr_alloc(); uint8_t **resampled_data = NULL; float *audio_output = NULL; int ret = SUCCESS; if (!swr_ctx) { fprintf(stderr, "Could not allocate resampler context\n"); return ERROR_NOTENOUGH_MEMORY; } av_opt_set_chlayout(swr_ctx, "in_chlayout", &ch_layout, 0); av_opt_set_int(swr_ctx, "in_sample_rate", input_sample_rate, 0); av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0); av_opt_set_chlayout(swr_ctx, "out_chlayout", &ch_layout, 0); av_opt_set_int(swr_ctx, "out_sample_rate", output_sample_rate, 0); av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); if ((ret = swr_init(swr_ctx)) < 0) { ret = handle_av_error(ret); goto cleanup; } int output_samples_count = av_rescale_rnd(swr_get_delay(swr_ctx, input_sample_rate) + input_num_of_samples, output_sample_rate, input_sample_rate, AV_ROUND_UP); ret = av_samples_alloc_array_and_samples(&resampled_data, NULL, 1, output_samples_count, AV_SAMPLE_FMT_FLT, 0); if (ret < 0) { ret = handle_av_error(ret); goto cleanup; } const uint8_t *in_samples[1] = { (const uint8_t *)audio_input }; int frame_count = swr_convert(swr_ctx, resampled_data, output_samples_count, in_samples, input_num_of_samples); if (frame_count < 0) { fprintf(stderr, "Error while resampling\n"); ret = ERROR_UNKNOWN; goto cleanup; } while (1) { int frame_count_flush = swr_convert(swr_ctx, resampled_data, output_samples_count, NULL, 0); if (frame_count_flush <= 0) { break; } frame_count += frame_count_flush; } audio_output = (float *)malloc(frame_count * sizeof(float)); if (!audio_output) { fprintf(stderr, "Could not allocate memory for output\n"); ret = ERROR_NOTENOUGH_MEMORY; goto cleanup; } memcpy(audio_output, resampled_data[0], frame_count); ret = write_wav(output_file, audio_output, frame_count, output_sample_rate); cleanup: if (resampled_data != NULL) av_freep(&resampled_data[0]); swr_free(&swr_ctx); if (audio_output != NULL) free(audio_output); return ret; }
write_wav函数代码
int write_wav(const char *filename, float *samples, int num_samples, int sample_rate) { FILE *file = fopen(filename, "wb"); if (!file) { fprintf(stderr, "Unable to open file for writing\n"); return ERROR_UNKNOWN; } unsigned char header[44] = { 'R','I','F','F', // RIFF 0,0,0,0, // Chunk size (to be filled later) 'W','A','V','E', // WAVE 'f','m','t',' ', // fmt 16,0,0,0, // Subchunk1Size (16 for PCM) 1,0, // AudioFormat (1 for PCM) 1,0, // NumChannels (1 for mono) 0,0,0,0, // SampleRate (to be filled later) 0,0,0,0, // ByteRate (to be filled later) 4,0, // BlockAlign (4 bytes per sample frame for float) 32,0, // BitsPerSample (32 for float samples) 'd','a','t','a', // Subchunk2ID 0,0,0,0 // Subchunk2Size (to be filled later) }; int byte_rate = sample_rate * 4; int subchunk2_size = num_samples * 4; int chunk_size = 36 + subchunk2_size; memcpy(header + 24, &sample_rate, 4); memcpy(header + 28, &byte_rate, 4); memcpy(header + 40, &subchunk2_size, 4); memcpy(header + 4, &chunk_size, 4); fwrite(header, sizeof(header), 1, file); fwrite(samples, sizeof(float), num_samples, file); fclose(file); return 0; }
波形对比
原始音频(24000Hz)波形:
重采样后音频(48000Hz)波形:
问题根源与修复方案
1. 内存拷贝长度错误
重采样函数中memcpy(audio_output, resampled_data[0], frame_count);存在致命问题:frame_count是采样点数量,但每个float类型采样点占4字节,当前代码只拷贝了1/4的有效数据,剩余内存为随机值,直接导致噪音。
修复代码:
memcpy(audio_output, resampled_data[0], frame_count * sizeof(float));
2. 重采样缓存覆盖问题
flush阶段调用swr_convert时,直接使用原始缓存起始地址,会覆盖之前重采样生成的有效数据。需要从已写入的采样点后方追加数据:
修改flush循环代码:
int output_offset = frame_count; // 从已生成的采样点末尾开始追加 while (1) { // 传入缓存的偏移地址,避免覆盖已有数据 int frame_count_flush = swr_convert(swr_ctx, (uint8_t **)((float *)resampled_data[0] + output_offset), output_samples_count - output_offset, NULL, 0); if (frame_count_flush <= 0) { break; } frame_count += frame_count_flush; output_offset += frame_count_flush; }
3. 可选:提升重采样质量
默认重采样算法质量一般,可在swr_init前添加以下配置,启用更高质量的重采样参数:
av_opt_set_int(swr_ctx, "filter_size", 16, 0); // 增大滤波器尺寸 av_opt_set_int(swr_ctx, "cutoff", 99, 0); // 设置99%的带宽截止,保留更多高频 av_opt_set_int(swr_ctx, "linear_interp", 0, 0); // 禁用线性插值,使用更高质量算法
内容的提问来源于stack exchange,提问作者leeeeeeeeess
相关产品推荐
相关产品推荐

