前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用libswresample库实现音频重采样

使用libswresample库实现音频重采样

作者头像
故乡的樱花开了
发布2023-10-22 13:58:21
2170
发布2023-10-22 13:58:21
举报
文章被收录于专栏:Android技术专栏Android技术专栏

一.初始化音频重采样器

  在音频重采样时,用到的核心结构是SwrContext,我们可以通过swr_alloc()获取swr_ctx实例,然后通过av_opt_set_int()函数和av_opt_set_sample_fmt()函数来设置音频重采样的参数,最后通过swr_init()函数初始化SwrContext实例即可。下面给出代码:

代码语言:javascript
复制
//audio_resampler_core.cpp
#define SRC_NB_SAMPLES 1152
static SwrContext *swr_ctx;
static AVFrame *input_frame= nullptr;
int32_t dst_nb_samples,max_dst_nb_samples,dst_nb_channels,dst_rate,src_rate;
enum AVSampleFormat src_sample_fmt=AV_SAMPLE_FMT_NONE,dst_sample_fmt=AV_SAMPLE_FMT_NONE;
uint8_t **dst_data= nullptr;
int32_t dst_linesize=0;
static int32_t init_frame(int sample_rate,int sample_format,uint64_t channel_layout){
    int32_t result=0;
    input_frame->sample_rate=sample_rate;
    input_frame->nb_samples=SRC_NB_SAMPLES;
    input_frame->format=sample_format;
    input_frame->channel_layout=channel_layout;
    result= av_frame_get_buffer(input_frame,0);
    if(result<0){
        cerr<<"Error:av_frame_get_buffer failed."<<endl;
        return -1;
    }
    return 0;
}
int32_t init_audio_resampler(int32_t in_sample_rate,const char *in_sample_fmt,const char *in_ch_layout,int32_t out_sample_rate,const char *out_sample_fmt,const char *out_ch_layout){
    int32_t result=0;
    swr_ctx=swr_alloc();
    if(!swr_ctx){
        cerr<<"Error:swr_alloc failed."<<endl;
        return -1;
    }
    int64_t src_ch_layout=-1,dst_ch_layout=-1;
    if(!strcasecmp(in_ch_layout,"MONO")){
        src_ch_layout=AV_CH_LAYOUT_MONO;
    }
    else if(!strcasecmp(in_ch_layout,"STEREO")){
        src_ch_layout=AV_CH_LAYOUT_STEREO;
    }
    else if(!strcasecmp(in_ch_layout,"SURROUND")){
        src_ch_layout=AV_CH_LAYOUT_SURROUND;
    }
    else{
        cerr<<"ERROR:unsupported input channel layout."<<endl;
        return -1;
    }
    if(!strcasecmp(out_ch_layout,"MONO")){
        dst_ch_layout=AV_CH_LAYOUT_MONO;
    }
    else if(!strcasecmp(out_ch_layout,"STEREO")){
        dst_ch_layout=AV_CH_LAYOUT_STEREO;
    }
    else if(!strcasecmp(out_ch_layout,"SURROUND")){
        dst_ch_layout=AV_CH_LAYOUT_SURROUND;
    }
    else{
        cerr<<"ERROR:unsupported output channel layout."<<endl;
        return -1;
    }
    if(!strcasecmp(in_sample_fmt,"fltp")){
        src_sample_fmt=AV_SAMPLE_FMT_FLTP;
    }
    else if(!strcasecmp(in_sample_fmt,"s16")){
        src_sample_fmt=AV_SAMPLE_FMT_S16P;
    }
    else{
        cerr<<"Error:unsupported input sample format."<<endl;
        return -1;
    }
    if(!strcasecmp(out_sample_fmt,"fltp")){
        dst_sample_fmt=AV_SAMPLE_FMT_FLTP;
    }
    else if(!strcasecmp(out_sample_fmt,"s16")){
        dst_sample_fmt=AV_SAMPLE_FMT_S16P;
    }
    else{
        cerr<<"Error:unsupported output sample format."<<endl;
        return -1;
    }
    src_rate=in_sample_rate;
    dst_rate=out_sample_rate;
    av_opt_set_int(swr_ctx,"in_channel_layout",src_ch_layout,0);
    av_opt_set_int(swr_ctx,"in_sample_rate",src_rate,0);
    av_opt_set_sample_fmt(swr_ctx,"in_sample_fmt",src_sample_fmt,0);
    av_opt_set_int(swr_ctx,"out_channel_layout",dst_ch_layout,0);
    av_opt_set_int(swr_ctx,"out_sample_rate",dst_rate,0);
    av_opt_set_sample_fmt(swr_ctx,"out_sample_fmt",dst_sample_fmt,0);
    result=swr_init(swr_ctx);
    if(result<0){
        cerr<<"Error:failed to initialize SwrContext."<<endl;
        return -1;
    }
    input_frame=av_frame_alloc();
    if(!input_frame){
        cerr<<"Error:av_frame_alloc failed."<<endl;
        return -1;
    }
    result= init_frame(in_sample_rate,src_sample_fmt,src_ch_layout);
    if(result<0){
        cerr<<"Error:init_frame failed."<<endl;
        return -1;
    }
    max_dst_nb_samples=dst_nb_samples=av_rescale_rnd(SRC_NB_SAMPLES,out_sample_rate,in_sample_rate,AV_ROUND_UP);
    dst_nb_channels= av_get_channel_layout_nb_channels(dst_ch_layout);
    cout<<"max_dst_nb_samples:"<<max_dst_nb_samples<<",dst_nb_channels:"<<dst_nb_channels<<endl;
    return 0;
}

二.循环对音频帧进行重采样

  音频重采样用到的核心函数是swr_convert(),不过在进行重采样的时候,需要注意每次要去判断目标采样点个数是否大于最大目标采样点个数,如果大于,需要重新给输出缓冲区分配内存空间。下面给出代码:

代码语言:javascript
复制
audio_resampler_core.cpp
static int32_t resampling_frame(){
    int32_t result=0;
    int32_t dst_bufsize=0;
    dst_nb_samples=av_rescale_rnd(swr_get_delay(swr_ctx,src_rate)+SRC_NB_SAMPLES,dst_rate,src_rate,AV_ROUND_UP);
    if(dst_nb_samples>max_dst_nb_samples){
        av_freep(&dst_data[0]);
        result=av_samples_alloc(dst_data,&dst_linesize,dst_nb_channels,dst_nb_samples,dst_sample_fmt,1);
        if(result<0){
            cerr<<"Error:failed to reallocate dst_data."<<endl;
            return -1;
        }
        cout<<"nb_samples exceeds max_dst_nb_samples,buffer reallocated."<<endl;
        max_dst_nb_samples=dst_nb_samples;
    }
    result=swr_convert(swr_ctx,dst_data,dst_nb_samples,(const uint8_t **)input_frame->data,SRC_NB_SAMPLES);
    if(result<0){
        cerr<<"Error:swr_convert failed."<<endl;
        return -1;
    }
    dst_bufsize= av_samples_get_buffer_size(&dst_linesize,dst_nb_channels,result,dst_sample_fmt,1);
    if(dst_bufsize<0){
        cerr<<"Error:Could not get sample buffer size."<<endl;
        return -1;
    }
    write_packed_data_to_file(dst_data[0],dst_bufsize);
    return 0;
}
int32_t audio_resampling(){
    int32_t result= av_samples_alloc_array_and_samples(&dst_data,&dst_linesize,dst_nb_channels,dst_nb_samples,dst_sample_fmt,0);
    if(result<0){
        cerr<<"Error:av_samples_alloc_array_and_samples failed."<<endl;
        return -1;
    }
    cout<<"dst_linesize:"<<dst_linesize<<endl;
    while(!end_of_input_file()){
        result= read_pcm_to_frame2(input_frame,src_sample_fmt,2);//这个函数的代码请看我上篇博客
        if(result<0){
            cerr<<"Error:read_pcm_to_frame2 failed."<<endl;
            return -1;
        }
        result=resampling_frame();
        if(result<0){
            cerr<<"Error:resampling_frame failed."<<endl;
            return -1;
        }
    }
    return 0;
}

三.将重采样后的数据写入输出文件

  在初始化重采样器的时候,我们设置了目标采样格式为s16p,声道数量为1,所以只需要将dst_data[0]的数据写入输出文件即可,下面给出代码:

代码语言:javascript
复制
//io_data.cpp
int32_t write_packed_data_to_file(uint8_t *data,int32_t size){
    fwrite(data,1,size,output_file);
}

四.销毁音频重采样器

代码语言:javascript
复制
//audio_resampler_core.cpp
void destroy_audio_resampler(){
    av_frame_free(&input_frame);
    if(dst_data){
        av_freep(&dst_data[0]);
    }
    av_freep(&dst_data);
    swr_free(&swr_ctx);
}

五.main函数实现

代码语言:javascript
复制
int main(){
    const char *input_file_name="../input.pcm";
    int32_t in_sample_rate=44100;
    const char *in_sample_fmt="fltp";
    const char *in_sample_layout="STEREO";
    const char *output_file_name="../output.pcm";
    int32_t out_sample_rate=22050;
    const char *out_sample_fmt="s16";
    const char *out_sample_layout="MONO";
    int32_t result=open_input_output_files(input_file_name,output_file_name);
    if(result<0){
        return -1;
    }
    result=init_audio_resampler(in_sample_rate,in_sample_fmt,in_sample_layout,out_sample_rate,out_sample_fmt,out_sample_layout);
    if(result<0){
        return -1;
    }
    result=audio_resampling();
    if(result<0){
        return -1;
    }
    destroy_audio_resampler();
    close_input_output_files();
    return 0;
}

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

  ffplay -f s16le -ac 1 -ar 22050 -i output.pcm

  没有给出的函数代码请看我前面的博客

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-07-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档