首页 > 其他分享 >如何使用libavfilter库给pcm音频采样数据添加音频滤镜?

如何使用libavfilter库给pcm音频采样数据添加音频滤镜?

时间:2023-07-03 11:14:45浏览次数:40  
标签:libavfilter 音频 filter 滤镜 result input output frame

一.初始化音频滤镜

  初始化音频滤镜的方法基本上和初始化视频滤镜的方法相同,不懂的可以看上篇博客,这里直接给出代码:

//audio_filter_core.cpp
#define INPUT_SAMPLERATE 44100
#define INPUT_FORMAT AV_SAMPLE_FMT_FLTP
#define INPUT_CHANNEL_LAYOUT AV_CH_LAYOUT_STEREO
static AVFilterGraph *filter_graph;
static AVFilterContext *abuffersrc_ctx;
static AVFilterContext *volume_ctx;
static AVFilterContext *aformat_ctx;
static AVFilterContext *abuffersink_ctx;
static AVFrame *input_frame= nullptr,*output_frame= nullptr;
int32_t init_audio_filter(const char *volume_factor){
    int32_t result=0;
    char ch_layout[64];
    char options_str[1024];
    AVDictionary *options_dict= nullptr;
    //创建滤镜图
    filter_graph=avfilter_graph_alloc();
    if(!filter_graph){
        cerr<<"Error:Unable to create filter graph."<<endl;
        return -1;
    }
    //创建abuffer滤镜
    const AVFilter *abuffer= avfilter_get_by_name("abuffer");
    if(!abuffer){
        cerr<<"Error:Could not find abuffer filter."<<endl;
        return -1;
    }
    abuffersrc_ctx= avfilter_graph_alloc_filter(filter_graph,abuffer,"src");
    if(!abuffersrc_ctx){
        cerr<<"Error:could not allocate the abuffer instance."<<endl;
        return -1;
    }
    av_get_channel_layout_string(ch_layout,sizeof(ch_layout),0,INPUT_CHANNEL_LAYOUT);
    av_opt_set(abuffersrc_ctx,"channel_layout",ch_layout,AV_OPT_SEARCH_CHILDREN);
    av_opt_set(abuffersrc_ctx,"sample_fmt",av_get_sample_fmt_name(INPUT_FORMAT),AV_OPT_SEARCH_CHILDREN);
    av_opt_set_q(abuffersrc_ctx,"time_base",(AVRational){1,INPUT_SAMPLERATE},AV_OPT_SEARCH_CHILDREN);
    av_opt_set_int(abuffersrc_ctx,"sample_rate",INPUT_SAMPLERATE,AV_OPT_SEARCH_CHILDREN);
    result= avfilter_init_str(abuffersrc_ctx, nullptr);
    if(result<0){
        cerr<<"Error:could not initialize the abuffer filter."<<endl;
        return -1;
    }
    //创建volume滤镜
    const AVFilter *volume= avfilter_get_by_name("volume");
    if(!volume){
        cerr<<"Error:could not find volume filter."<<endl;
        return -1;
    }
    volume_ctx= avfilter_graph_alloc_filter(filter_graph,volume,"volume");
    if(!volume_ctx){
        cerr<<"Error:could not allocate volume filter instance."<<endl;
        return -1;
    }
    av_dict_set(&options_dict,"volume",volume_factor,0);
    result= avfilter_init_dict(volume_ctx,&options_dict);
    av_dict_free(&options_dict);
    if(result<0){
        cerr<<"Error:could not initialize volume filter instance."<<endl;
        return -1;
    }
    //创建aformat滤镜
    const AVFilter *aformat=avfilter_get_by_name("aformat");
    if(!aformat){
        cerr<<"Error:could not find aformat filter."<<endl;
        return -1;
    }
    aformat_ctx= avfilter_graph_alloc_filter(filter_graph,aformat,"aformat");
    if(!aformat_ctx){
        cerr<<"Error:could not allocate aformat filter instance."<<endl;
        return -1;
    }
    snprintf(options_str,sizeof(options_str),"sample_fmts=%s:sample_rates=%d:channel_layouts=stereo",av_get_sample_fmt_name(AV_SAMPLE_FMT_S16),44100);
    result= avfilter_init_str(aformat_ctx,options_str);
    if(result<0){
        cerr<<"Error:could not initialize aformat filter."<<endl;
        return -1;
    }
    //创建abuffersink滤镜
    const AVFilter *abuffersink= avfilter_get_by_name("abuffersink");
    if(!abuffersink){
        cerr<<"Error:could not find abuffersink filter."<<endl;
        return -1;
    }
    abuffersink_ctx= avfilter_graph_alloc_filter(filter_graph,abuffersink,"sink");
    if(!abuffersink_ctx){
        cerr<<"Error:could not allocate abuffersink filter instance."<<endl;
        return -1;
    }
    result= avfilter_init_str(abuffersink_ctx, nullptr);
    if(result<0){
        cerr<<"Error:could not initialize abuffersink filter."<<endl;
        return -1;
    }
    //连接创建好的滤镜
    result=avfilter_link(abuffersrc_ctx,0,volume_ctx,0);
    if(result>=0){
        result=avfilter_link(volume_ctx,0,aformat_ctx,0);
    }
    if(result>=0){
        result=avfilter_link(aformat_ctx,0,abuffersink_ctx,0);
    }
    if(result<0){
        fprintf(stderr,"Error connecting filters\n");
        return -1;
    }
    //配置滤镜图
    result=avfilter_graph_config(filter_graph, nullptr);
    if(result<0){
        cerr<<"Error:Error configuring the filter graph."<<endl;
        return -1;
    }
    //创建输入输出帧
    input_frame=av_frame_alloc();
    output_frame=av_frame_alloc();
    if(!input_frame||!output_frame){
        cerr<<"Error:frame allocation failed."<<endl;
        return -1;
    }
    return 0;
}

二.初始化输入音频帧

  在这一步需要给输入音频帧设置一些参数,包括采样率,采样点个数,声道布局,音频帧格式等,然后就可以给音频帧分配内存空间了。代码如下:

//audio_filter_core.cpp
static int32_t init_frames(){
    int result=0;
    input_frame->sample_rate=44100;
    input_frame->format=AV_SAMPLE_FMT_FLTP;
    input_frame->channel_layout=AV_CH_LAYOUT_STEREO;
    input_frame->nb_samples=1024;
    result= av_frame_get_buffer(input_frame,0);
    if(result<0){
        cerr<<"Error:av_frame_get_buffer failed."<<endl;
        return -1;
    }
    result= av_frame_make_writable(input_frame);
    if(result<0){
        cerr<<"Error:av_frame_make_writable failed."<<endl;
        return -1;
    }
    return 0;
}

三.循环编辑音频帧

  在这一步需要注意的是,每次将输入音频帧放入滤镜图前,都要做一次初始化音频帧操作,否则会报错:filter context - fmt: fltp r: 44100 layout: 3 ch: 2, incoming frame - fmt: (null) r: 0 layout: 3 ch: 2 pts_time: NOPTS【Changing audio frame properties on the fly is not supported.】。注意一定是每次,不要只初始化一次,这样只有第一帧初始化了,后面的帧还是会报错,因为输入帧的格式要和滤镜上下文保持一致,如果没有每次都初始化,后面的帧的格式和采样率就识别不到,为null了。下面给出代码:

//audio_filter_core.cpp
static int32_t filter_frame(){
    int32_t result= av_buffersrc_add_frame(abuffersrc_ctx,input_frame);
    if(result<0){
        cerr<<"Error:add frame to buffersrc failed."<<endl;
        return -1;
    }
    while(1){
        result=av_buffersink_get_frame(abuffersink_ctx,output_frame);
        if(result==AVERROR(EAGAIN)||result==AVERROR_EOF){
            return 1;
        }
        else if(result<0){
            cerr<<"Error:av_buffersink_get_frame failed."<<endl;
            return -1;
        }
        cout<<"Output channels:"<<output_frame->channels<<",nb_samples:"<<output_frame->nb_samples<<",sample_fmt:"<<output_frame->format<<endl;
        write_samples_to_pcm2(output_frame);
        av_frame_unref(output_frame);
    }
    return 0;
}
int32_t audio_filtering(){
    int32_t result=0;
    while(!end_of_input_file()){
        init_frames();//每次都要执行
        result=read_pcm_to_frame2(input_frame,INPUT_FORMAT,2);
        if(result<0){
            cerr<<"Error:read_pcm_to_frame2 failed."<<endl;
            return -1;
        }
        result=filter_frame();
        if(result<0){
            cerr<<"Error:filter_frame failed."<<endl;
            return -1;
        }
    }
    return 0;
}

四.将编辑后的数据写入输出文件

  在这一步需要注意的是,由于在滤镜图中有一个滤镜实例将音频帧的采样格式设置为了AV_SAMPLE_FMT_S16,这是packed格式的帧,左右声道的数据交错存储在frame->data[0]指向的内存单元中,所以在写入的时候,需要注意这一点。下面给出代码:

//io_data.cpp
int32_t write_samples_to_pcm2(AVFrame* frame){
    int16_t* samples = reinterpret_cast<int16_t*>(frame->data[0]);
    int dataSize = frame->nb_samples * frame->channels * sizeof(int16_t);
    fwrite(samples, 1, dataSize, output_file);
    return 0;
}

  数据读入代码:

//io_data.cpp
static FILE* input_file= nullptr;
static FILE* output_file= nullptr;
int32_t read_pcm_to_frame2(AVFrame *frame,enum AVSampleFormat sample_fmt,int channels){
    int data_size= av_get_bytes_per_sample(sample_fmt);
    if(data_size<0){
        cerr<<"Error:Failed to calculate data size."<<endl;
        return -1;
    }
    for(int i=0;i<frame->nb_samples;i++){
        for(int ch=0;ch<channels;ch++){
            fread(frame->data[ch]+i*data_size,1,data_size,input_file);
        }
    }
    return 0;
}
int32_t open_input_output_files(const char* input_name,const char* output_name){
    if(strlen(input_name)==0||strlen(output_name)==0){
        cout<<"Error:empty input or output file name."<<endl;
        return -1;
    }
    close_input_output_files();
    input_file=fopen(input_name,"rb");//rb:读取一个二进制文件,该文件必须存在
    if(input_file==nullptr){
        cerr<<"Error:failed to open input file."<<endl;
        return -1;
    }
    output_file=fopen(output_name,"wb");//wb:打开或新建一个二进制文件,只允许写
    if(output_file== nullptr){
        cout<<"Error:failed to open output file."<<endl;
        return -1;
    }
    return 0;
}
void close_input_output_files(){
    if(input_file!= nullptr){
        fclose(input_file);
        input_file= nullptr;
    }
    if(output_file!= nullptr){
        fclose(output_file);
        output_file= nullptr;
    }
}
int32_t end_of_input_file(){
    return feof(input_file);
}

五.销毁资源

audio_filter_core.cpp
static void free_frames(){
    av_frame_free(&input_frame);
    av_frame_free(&output_frame);
}
void destroy_audio_filter(){
    free_frames();
    avfilter_graph_free(&filter_graph);
}

六.main函数实现

int main(){
    const char *input_file_name="../input.pcm";
    const char *output_file_name="../output.pcm";
    const char *volume_factor="0.9";
    int32_t result=0;
    result= open_input_output_files(input_file_name,output_file_name);
    if(result<0){
        return -1;
    }
    result= init_audio_filter(volume_factor);
    if(result<0){
        return -1;
    }
    result=audio_filtering();
    if(result<0){
        return -1;
    }
    destroy_audio_filter();
    close_input_output_files();
    return 0;
}

  最后,可以使用下面的指令测试输出的pcm文件:

  ffplay -ac 2 -ar 44100 -f s16le -i output.pcm

 

标签:libavfilter,音频,filter,滤镜,result,input,output,frame
From: https://www.cnblogs.com/luqman/p/audio_filter.html

相关文章

  • Matlab-对wav音频文件AM调制及解调
    1.读取wav音乐文件%读取音频文件filename='jay.wav';[sound_data,fs]=audioread(filename);%9507502x244100sound_data_1=sound_data(:,1);sound_data_1=sound_data_1';%转置sound_data有两列,因为此音乐文件有两个通道,音频采样率为44100;这......
  • 如何使用libavfilter库给输入文件input.yuv添加视频滤镜?
    一.视频滤镜初始化本次代码实现的是给输入视频文件添加水平翻转滤镜,在视频滤镜初始化部分我们可以分为以下几步进行:1.创建滤镜图结构视频滤镜功能最核心的结构为滤镜图结构,即AVFilterGraph结构,我们调用avfilter_graph_alloc()函数就可以创建一个滤镜图结构。......
  • 如何将mp4文件解复用并且解码为单独的.yuv图像序列以及.pcm音频采样数据?
    一.初始化解复用器在音视频的解复用的过程中,有一个非常重要的结构体AVFormatContext,即输入文件的上下文句柄结构,代表当前打开的输入文件或流。我们可以将输入文件的路径以及AVFormatContext**format_ctx传入函数avformat_open_input(),就可以打开对应的音视频文件或流。接......
  • 小程序进入页面自动播放音频
    以前我开发PC和H5时知道音频/视频如果自动播放是需要静音的 小程序是不一样的1. 首先肯定是要在onLoad/onShow中调用我们准备播放用的函数 这里根据自己需求写2.重点 我们需要构建一个InnerAudioContext对象//创建内部audio上下文InnerAudioContext......
  • Hugging News #0626: 音频课程更新、在线体验 baichuan-7B 模型、ChatGLM2-6B 重磅发
    每一周,我们的同事都会向社区的成员们发布一些关于HuggingFace相关的更新,包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等,我们将其称之为「HuggingNews」,本期HuggingNews有哪些有趣的消息,快来看看吧!重要更新最新音频课程现已发布近期,我们......
  • nginx RTMP推拉流,多个音频流合并。
    使用nginxRTMP(nginx的一个插件模块,具体的网上搜一下)做一个简易的多人音频通话流媒体服务器,多个端通话时,客户端无法处理其他端发过来的音频流,比如A、B、C三个端通话,A设备同时持有B、C的音频流,这样对设备端非常的不友好。这时候就需要用到一个强大的工具,FFMPEG,安装网上很多,搜下就......
  • PS滤镜插件套装 Nik Collection 6 中文版下载
    NikCollection是由Google开发的一套Photoshop插件,包含了7个不同的插件,它们分别是:AnalogEfexPro:模拟胶片摄影风格,包含了多种特效和滤镜。ColorEfexPro:提供了超过50种颜色调整和增强工具,可以让你轻松地进行颜色校正、对比度调整等操作。SilverEfexPro:专门用于黑白照片处理......
  • 使用libavcodec将mp3音频文件解码为pcm音频采样数据【[mp3float @ 0x561c1ec49940] He
    一.打开和关闭输入文件和输出文件想要解决上面提到的问题,我们需要对mp3文件的格式有个大致了解,为了方便讲解,我这里画了个示意图:ID3V2包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量。Frame一系列的帧,个数由文件大小和帧长决定ID3V1包含了作者,作曲,专......
  • Logic Pro-mac苹果专业音频制作软件
    LogicPro是一款专业的音频编辑软件,具有高质量的音频编辑功能,让您可以在线处理您的音频文件,为您提供高质量的音效和完美的声音。LogicPro用于多种用途的专业音频编辑软件,无论是制作音乐、特效、混音或音频录制等,都可以使用LogicPro。该软件功能强大、简单易用。LogicPro使用先......
  • python+音频数字信号处理
    一、在网上下载了一个wav文件,周杰伦的《告白气球》网址:https://www.xmwav.com/ 二、一些参数说明针对一个音频信号:2.1、通道数是在采集声音时用几个通道去录制声音,单声道和双声道的音频文件较为常见。例如在声源的不同位置放置通道去录制,则可以获得多通道的音频数据。以双......