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

时间:2014-04-02 20:37:18

标签: audio ffmpeg pcm aac resampling

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

Stream #0:0: Audio: aac, 44100 Hz, stereo, fltp, 122 kb/s

在新的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

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

    我的重新采样代码如下:

    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;
        }
    }
    

    该怎么办?

    PS1:玩ffplay就好了。

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

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


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

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

    提前多多感谢!

3 个答案:

答案 0 :(得分:2)

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

类似的问题:What is the difference between AV_SAMPLE_FMT_S16P and AV_SAMPLE_FMT_S16?

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

答案 1 :(得分:1)

我做了类似的事情,祝你好运。在您的代码块

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);
   }

}

尝试替换以下内容:

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++) {
      outputBuffer[i*channels+c] = (int16_t)(((float *)frame_->extended_data[c]) * 32767.0f);
   }
}

答案 2 :(得分:0)

只有在转换为不同的采样率时才需要重新采样。如果采样率相同,则只需要将浮点平面格式转换为固定的16交错格式。