FFmpeg - 从AV_SAMPLE_FMT_FLTP重新采样到AV_SAMPLE_FMT_S16的声音质量非常差(慢,失调,噪音)

kai*_*nfr 7 audio ffmpeg aac pcm resampling

我对新ffmpeg的重新取样结果感到困惑.我将AAC音频解码为PCM,ffmpeg将音频信息显示为:

Stream #0:0: Audio: aac, 44100 Hz, stereo, fltp, 122 kb/s
Run Code Online (Sandbox Code Playgroud)

在新的ffmpeg中,输出样本是fltp格式,所以我必须将它从AV_SAMPLE_FMT_FLTP转换为AV_SAMPLE_FMT_S16

PS:在旧的ffmpeg中作为libavcodec 54.12.100,它直接是S16,因此不需要重新采样且没有任何声音质量问题.

然后我尝试了三种方法重新取样,

  1. 使用swr_convert

  2. 使用avresample_convert

  3. 转换manualy

但是它们都会产生相同的结果,声音质量非常糟糕,非常缓慢和失调,还有一些噪音.

我的重采样代码如下:

void resampling(AVFrame* frame_, AVCodecContext* pCodecCtx, int64_t want_sample_rate, uint8_t* outbuf){
    SwrContext      *swrCtx_ = 0;
    AVAudioResampleContext *avr = 0;

    // Initializing the sample rate convert. We only really use it to convert float output into int.
    int64_t wanted_channel_layout = AV_CH_LAYOUT_STEREO;

#ifdef AV_SAMPLEING
    avr = avresample_alloc_context();
    av_opt_set_int(avr, "in_channel_layout", frame_->channel_layout, 0);
    av_opt_set_int(avr, "out_channel_layout", wanted_channel_layout, 0);
    av_opt_set_int(avr, "in_sample_rate", frame_->sample_rate, 0);
    av_opt_set_int(avr, "out_sample_rate", 44100, 0);
    av_opt_set_int(avr, "in_sample_fmt", pCodecCtx->sample_fmt, 0); //AV_SAMPLE_FMT_FLTP
    av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
    av_opt_set_int(avr, "internal_sample_fmt", pCodecCtx->sample_fmt, 0);
    avresample_open(avr);
    avresample_convert(avr, &outbuf, frame_->linesize[0], frame_->nb_samples, frame_->extended_data, frame_->linesize[0], frame_->nb_samples);
    avresample_close(avr);
    return;
#endif

#ifdef USER_SAMPLEING
    if (pCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP)
    {
            int nb_samples = frame_->nb_samples;
            int channels = frame_->channels;
            int outputBufferLen = nb_samples & channels * 2;
            auto outputBuffer = (int16_t*)outbuf;

            for (int i = 0; i < nb_samples; i++)
            {
                    for (int c = 0; c < channels; c++)
                    {
                            float* extended_data = (float*)frame_->extended_data[c];
                            float sample = extended_data[i];
                            if (sample < -1.0f) sample = -1.0f;
                            else if (sample > 1.0f) sample = 1.0f;
                            outputBuffer[i * channels + c] = (int16_t)round(sample * 32767.0f);
                    }
            }
            return;
    }
#endif
    swrCtx_ = swr_alloc_set_opts(
            NULL, //swrCtx_,
            wanted_channel_layout,
            AV_SAMPLE_FMT_S16,
            want_sample_rate,
            pCodecCtx->channel_layout,
            pCodecCtx->sample_fmt,
            pCodecCtx->sample_rate,
            0,
            NULL);

    if (!swrCtx_ || swr_init(swrCtx_) < 0) {
            printf("swr_init: Failed to initialize the resampling context");
            return;
    }

    // convert audio to AV_SAMPLE_FMT_S16
    int swrRet = swr_convert(swrCtx_, &outbuf, frame_->nb_samples, (const uint8_t **)frame_->extended_data, frame_->nb_samples);
    if (swrRet < 0) {
            printf("swr_convert: Error while converting %d", swrRet);
            return;
    }
}
Run Code Online (Sandbox Code Playgroud)

该怎么办?

PS1:玩ffplay很好.

PS2:将重新采样S16 PCM保存到文件中并播放它会产生相同的音质问题.

非常感谢您的帮助和建议!


我还注意到,在旧的ffmpeg中,aac被重新识别为FLT格式并直接解码为16位PCM,而在新的ffmpeg中,aac被计为FLTP格式并产生32位IEEE浮点输出.

因此,相同的代码将使用不同版本的ffmpeg生成完全不同的输出.那么,我想问一下在新版本中将AAC音频转换为16位PCM的正确方法是什么?

非常感谢提前!

小智 5

您需要记住 AV_SAMPLE_FMT_FLTP 是平面模式。如果您的代码需要 AV_SAMPLE_FMT_S16(交错模式)输出,则需要在转换后对样本重新排序。考虑 2 个音频通道并使用交错模式,样本排序为“c0, c1, c0, c1, c0, c1, ...”。平面模式为“c0,c0,c0,...,c1,c1,c1,...”。

类似问题:AV_SAMPLE_FMT_S16P 和 AV_SAMPLE_FMT_S16 有什么区别?

详细信息在这里:http ://www.ffmpeg.org/doxygen/2.0/samplefmt_8h.html