Audio FFmpeg-从AV_SAMPLE_FMT_FLTP到AV_SAMPLE_FMT_S16的重采样获得非常差的音质(缓慢、失调、噪声)

Audio FFmpeg-从AV_SAMPLE_FMT_FLTP到AV_SAMPLE_FMT_S16的重采样获得非常差的音质(缓慢、失调、噪声),audio,ffmpeg,pcm,aac,resampling,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,因此不需要重新采样,并且没有任何音质问题 然后我尝试

我对新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,因此不需要重新采样,并且没有任何音质问题

然后我尝试了三种重新采样的方法

  • 使用swr_转换

  • 使用avresample\u转换

  • 手动转换

  • 但所有这些都产生了相同的结果,音质非常差,非常慢,不协调,还有一些噪音

    我的重采样代码如下:

    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;
        }
    }
    
    void重采样(AVFrame*frame、AVCodecContext*pCodecCtx、int64\u t want\u sample\u rate、uint8\u t*extuf){
    SwrContext*swrCtx_u2;=0;
    AVAudioResampleContext*avr=0;
    //正在初始化采样率转换。我们只使用它将浮点输出转换为int。
    int64想要的频道布局=AV频道布局立体声;
    #ifdef AV_采样
    avr=avresample_alloc_context();
    av_opt_set_int(avr,“在通道布局中”,帧>通道布局,0);
    av_opt_set_int(avr,“输出通道布局”,通缉通道布局,0);
    av_opt_set_int(avr,“输入采样率”,帧->采样率,0);
    av_opt_set_int(avr,“输出采样率”,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,“内部样本fmt”,PCODECTX->样本fmt,0);
    avr重采样开放(avr);
    avresample_convert(avr,&EXBUF,帧->线宽[0],帧->nb_样本,帧->扩展_数据,帧->线宽[0],帧->nb_样本);
    自动重采样(avr);
    返回;
    #恩迪夫
    #ifdef用户_采样
    如果(pCodecCtx->sample\u fmt==AV\u sample\u fmt\u FLTP)
    {
    int nb_samples=帧->nb_samples;
    int通道=帧->通道;
    int outputBufferLen=nb_采样和通道*2;
    自动输出缓冲区=(int16_t*)突发;
    对于(int i=0;i扩展_数据[c];
    浮动样本=扩展_数据[i];
    如果(样本<-1.0f)样本=-1.0f;
    如果(样本>1.0f)样本=1.0f,则为其他情况;
    outputBuffer[i*通道+c]=(int16_t)轮(样本*32767.0f);
    }
    }
    返回;
    }
    #恩迪夫
    swrCtx=swr\U alloc\U set\U opts(
    NULL,//swrCtx,
    通缉(频道)(版面),,
    AV_样品FMT_S16,
    想要样品价格,
    PCODECTX->channel_布局,
    PCODECTX->样本\u fmt,
    PCODECTX->采样率,
    0,
    无效);
    如果(!swrCtx|swr|u init(swrCtx|u)<0){
    printf(“swr_init:初始化重采样上下文失败”);
    返回;
    }
    //将音频转换为AV_SAMPLE_FMT_S16
    int swrRet=swr\u convert(swrCtx\u和exputf,帧->nb\u样本,(const uint8\u t**)帧->扩展数据,帧->nb\u样本);
    如果(swrRet<0){
    printf(“swr_转换:转换%d时出错”,swrRet);
    返回;
    }
    }
    
    该怎么办

    PS1:玩ffplay很好

    PS2:将重采样S16 PCM保存到文件中,播放时会出现相同的音质问题

    非常感谢您的帮助和建议


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

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


    提前多谢

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

    类似问题:


    这里的细节:

    我很幸运做了类似的事情。在代码块上

    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\u samples=frame\uu->nb\u samples;
    int通道=帧->通道;
    int outputBufferLen=nb_采样和通道*2;
    自动输出缓冲区=(int16_t*)突发;
    对于(int i=0;i扩展_数据[c];
    浮动样本=扩展_数据[i];
    如果(样本<-1.0f)样本=-1.0f;
    如果(样本>1.0f)样本=1.0f,则为其他情况;
    outputBuffer[i*通道+c]=(int16_t)轮(样本*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);
       }
    }
    
    int nb\u samples=frame\uu->nb\u samples;
    int通道=帧->通道;
    int outputBufferLen=nb_采样和通道*2;
    自动输出缓冲区=(int16_t*)突发;
    对于(int i=0;i扩展_数据[c])*32767.0f);
    }
    }
    
    仅当转换为不同的采样率时,才需要重新采样。如果采样率相同,您只需将浮点平面格式转换为固定16交错格式。

    为什么不让FFmpeg为您完成这项工作并输出16位PCM?请告诉我如何操作?它应该是一个音频流。我做到了