今热点:如何将PCM格式的原始音频采样数据编码为MP3格式或AAC格式的音频文件?
来源:博客园时间:2023-06-20 12:18:59

一.打开和关闭输入文件和输出文件以及判断输入文件是否读取完毕


(资料图片仅供参考)

//io_data.cppstatic FILE* input_file= nullptr;static FILE* output_file= nullptr;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."<

二.音频编码器的初始化

//audio_encoder_core.cppstatic const AVCodec* codec= nullptr;static AVCodecContext* codec_ctx= nullptr;static AVFrame* frame= nullptr;static AVPacket* pkt= nullptr;static enum AVCodecID audio_codec_id;int32_t init_audio_encoder(const char* codec_name){    if(strcasecmp(codec_name,"MP3")==0){        audio_codec_id=AV_CODEC_ID_MP3;        cout<<"Select codec id:MP3"<bit_rate=128000;    codec_ctx->sample_fmt=AV_SAMPLE_FMT_FLTP;    codec_ctx->sample_rate=44100;    codec_ctx->channel_layout=AV_CH_LAYOUT_STEREO;    codec_ctx->channels=2;    int32_t result=avcodec_open2(codec_ctx,codec, nullptr);    if(result<0){        cerr<<"Error:could not open codec."<nb_samples=codec_ctx->frame_size;//采样点数量    frame->format=codec_ctx->sample_fmt;    frame->channel_layout=codec_ctx->channel_layout;    result= av_frame_get_buffer(frame,0);    if(result<0){        cerr<<"Error:AVFrame could not get buffer."<

三.编码循环体

1.PCM文件的存储结构

音频采样格式可以分为packed和planar两类。以packed格式保存的采样数据,各声道间按照采样值交替存储;以planar格式保存的采样数据,各个采样值按照不同声道连续存储

下面以8bit为例展示planar和packed格式是如何保存音频采样数据的:

packed:
左声道0右声道0左声道1右声道1左声道2右声道2左声道3右声道3

planar:

左声道0左声道1左声道2左声道3右声道0右声道1右声道2右声道3

2.读取PCM音频采样数据

由于我们代码里设置了采样格式为fltp,即planar格式,而输入的PCM音频采样数据是packed格式的,因此我们需要将packed格式转化为planar格式进行保存:

//io_data.cppint32_t read_pcm_to_frame(AVFrame* frame,AVCodecContext* codec_ctx){    int data_size= av_get_bytes_per_sample(codec_ctx->sample_fmt);    if(data_size<0){        cerr<<"Error:Failed to calculate data size."<channels;ch++){            fread(frame->data[ch]+i*data_size,1,data_size,input_file);        }    }    return 0;}

3.编码音频采样数据

//audio_encoder_core.cppstatic int32_t encode_frame(bool flushing){    int32_t result=0;    if(!flushing){        cout<<"Send frame to encoder with pts:"<<frame->pts<=0){        result= avcodec_receive_packet(codec_ctx,pkt);        if(result==AVERROR(EAGAIN)||result==AVERROR_EOF){//尚未完成对新一帧的编码,要传入后续帧或编码器已完全输出内部缓存的码流            return 1;        }        else if(result<0){            cerr<<"Error:avcodec_receive_packet failed."<dts<<",pts:"<pts<<", "<

4.写出码流数据  

//io_data.cppvoid write_pkt_to_file(AVPacket* pkt){    fwrite(pkt->data,1,pkt->size,output_file);}

5.实现编码循环

//audio_encoder_core.cppint32_t audio_encoding(){    int32_t result=0;    while(!end_of_input_file()){        result= read_pcm_to_frame(frame,codec_ctx);        if(result<0){            cerr<<"Error:read_pcm_to_frame failed."<

6.关闭编码器

//audio_encoder_core.cppvoid destroy_audio_encoder(){    av_frame_free(&frame);    av_packet_free(&pkt);    avcodec_free_context(&codec_ctx);}

7.最终main函数的实现如下:

int main(){    const char* input_file_name="../input.pcm";    const char* output_file_name="../output.mp3";    const char* codec_name="MP3";    int32_t result= open_input_output_files(input_file_name,output_file_name);    if(result<0){        return result;    }    result=init_audio_encoder(codec_name);    if(result<0){        return result;    }    result=audio_encoding();    if(result<0){        return result;    }    destroy_audio_encoder();    close_input_output_files();    return 0;}

与视频文件类似,可以使用ffplay播放输出的.mp3文件来测试效果。

关键词: