天天微速讯:如何将PCM格式的原始音频采样数据编码为MP3格式或AAC格式的音频文件?
2023-06-20 12:23:11
|
来源:博客园
一.打开和关闭输入文件和输出文件以及判断输入文件是否读取完毕
(资料图片仅供参考)
//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文件来测试效果。