本文共 10271 字,大约阅读时间需要 34 分钟。
音频编码分为波形编码和参数编码,常见得编码方式如AAC等是两者之间的编码方式。波形编码就是对声波波形的采样数据进行编码,完全不考虑这个波内部的信息,如时域或者频域上的冗余。参数编码如一个正弦波我们不需要知道在不同时间采样数值,只有知道振幅,频率,相位等信息,编码只保存该信息,在接收方按照这些参数重新建立波形即可播放。G711A/G711U就是波形编码,编码比较简单,只是把样本值从PCM的存储方式16Bit压缩成8Bit,在安防和电话中有应用。
DEMO实现把PCM编码成G711A,代码如下:https://blog.csdn.net/hiwubihe/article/details/81258879
int AVFrame::formatformat of the frame, -1 if unknown or unset Values correspond to enum AVPixelFormat for video frames, enum AVSampleFormat for audio)AVSampleFormat :AV_SAMPLE_FMT_NONE AV_SAMPLE_FMT_U8 unsigned 8 bitsAV_SAMPLE_FMT_S16 signed 16 bitsAV_SAMPLE_FMT_S32 signed 32 bitsAV_SAMPLE_FMT_FLT floatAV_SAMPLE_FMT_DBL doubleAV_SAMPLE_FMT_U8P unsigned 8 bits, planarAV_SAMPLE_FMT_S16P signed 16 bits, planarAV_SAMPLE_FMT_S32P signed 32 bits, planarAV_SAMPLE_FMT_FLTP float, planarAV_SAMPLE_FMT_DBLP double, planarAV_SAMPLE_FMT_NB Number of sample formats. DO NOT USE if linking dynamically.
为什么设置采样率48k 而比特率64k? 不应该48k16bit2通道=比特率?
pcm编码成aac/mp3( mp3必须为palne 而pcm是交叉存储,不是plane👉具体反应在avcodec_open2会失败,故需要重采样)
/*Description: 代码实现PCM编码AAC,MP3格式*/#include#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libswresample/swresample.h" #define INPUT_FILE_NAME ("huangdun_r48000_FMT_S16_c2.pcm")//输出文件前缀#define OUTPUT_FILE_NAME_PREFIX ("huangdun")//输出文件后缀//#define OUTPUT_FILE_NAME_SUFFIX ("aac")//输出文件后缀#define OUTPUT_FILE_NAME_SUFFIX ("mp3")//输出文件比特率 该值越大 音频质量越好 音质损失越小#define OUTPUT_FILE_BIT_RATE (64000) int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){ int ret; int got_frame; AVPacket enc_pkt; if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &CODEC_CAP_DELAY)) return 0; while (1) { enc_pkt.data = NULL; enc_pkt.size = 0; av_init_packet(&enc_pkt); //输入视频帧为NULL ret = avcodec_encode_audio2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,NULL, &got_frame); av_frame_free(NULL); if (ret < 0) break; if (!got_frame) { ret=0; break; } printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size); /* mux encoded frame */ ret = av_write_frame(fmt_ctx, &enc_pkt); if (ret < 0) break; } return ret;} int main(){ static char*pFormatName[]= { "FMT_U8","FMT_S16","FMT_S32","FMT_FLT","FMT_DBL", "FMT_U8P","FMT_S16P","FMT_S32P","FMT_FLTP","FMT_DBLP" }; //各种不同格式对应字节数 static int mapSampleBytes[AV_SAMPLE_FMT_NB] ={ 1,2,4,4,8,1,2,4,4,8}; //PCM原始数据格式 uint64_t iInputLayout = AV_CH_LAYOUT_STEREO; //双声道 AV_CH_LAYOUT_SURROUND则是三声道 int iInputChans = av_get_channel_layout_nb_channels(iInputLayout); AVSampleFormat eInputSampleFormat = AV_SAMPLE_FMT_S16; int iInputSampleRate = 48000; //不同样本格式长度 int iInputSampleBytes = mapSampleBytes[eInputSampleFormat]; //PCM需要重采样的格式 部分编码器不支持原始PCM的数据格式如MP3 uint64_t iOutputLayout = AV_CH_LAYOUT_STEREO; //双声道 AV_CH_LAYOUT_SURROUND则是三声道 int iOutputChans = av_get_channel_layout_nb_channels(iOutputLayout); AVSampleFormat eOutputSampleFormat ; int iOutputSampleRate = 48000; if(strcmp(OUTPUT_FILE_NAME_SUFFIX,"aac") == 0) { eOutputSampleFormat = AV_SAMPLE_FMT_S16; // aac支持pcm的格式AV_SAMPLE_FMT_S16 } else if(strcmp(OUTPUT_FILE_NAME_SUFFIX,"mp3") == 0) { //MP3不支持AV_SAMPLE_FMT_S16这种格式 eOutputSampleFormat = AV_SAMPLE_FMT_S16P; //MP3格式编码只支持样本平行存储的方式 } //编码样本长度 int iOutputSampleBytes = mapSampleBytes[eOutputSampleFormat]; //是否需要重采样 bool bNeedResample = false; if(eInputSampleFormat != eOutputSampleFormat) { bNeedResample = true; } //是否平面存储结构 bool bPlanner = false; if((eOutputSampleFormat>=AV_SAMPLE_FMT_U8P) &&(eOutputSampleFormat<=AV_SAMPLE_FMT_DBLP)) bPlanner = true; //打开输入文件 FILE *pInputFile = fopen("huangdun_r48000_FMT_S16_c2.pcm", "rb"); if(pInputFile == NULL) { } //打开输出文件 char szOutFileName[256]={ 0}; sprintf(szOutFileName,"%s_br%d_sr%d.%s",OUTPUT_FILE_NAME_PREFIX,OUTPUT_FILE_BIT_RATE,iOutputSampleRate,OUTPUT_FILE_NAME_SUFFIX); FILE *pOutputFile = fopen(szOutFileName, "wb"); //打开中间测试文件 char szTempFileName[256]={ 0}; sprintf(szTempFileName,"%s_sr%d_c1.pcm",OUTPUT_FILE_NAME_PREFIX,iOutputSampleRate); FILE *pTempFile = fopen(szTempFileName, "wb"); int iReturn; ///编码器操作 //注册所有编解码器 av_register_all(); //封装格式上下文 AVFormatContext中有AVInputFormat和AVOutputFormat //解复用时avformat_open_input()初始化AVInputFormat,复用时用户自己初始化AVOutputFormat AVFormatContext* pFormatCtx; AVOutputFormat * fmt; //Method 1.分配一个封装格式 pFormatCtx = avformat_alloc_context(); //根据后缀名 填充 输出格式上下文 fmt = av_guess_format(NULL, szOutFileName, NULL); pFormatCtx->oformat = fmt; //Method 2. //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file); //fmt = pFormatCtx->oformat; //添加一个流 AVStream *audio_st = avformat_new_stream(pFormatCtx, 0); //添加一个输出路径 //Create and initialize a AVIOContext for accessing the resource indicated by url. avio_open(&pFormatCtx->pb,szOutFileName, AVIO_FLAG_READ_WRITE); //Show some information 日志信息 av_dump_format(pFormatCtx, 0, szOutFileName, 1); //初始化编码器相关结构体 获取输出流中的编码上下文 AVCodecContext* pCodecCtx = audio_st->codec; pCodecCtx->codec_id = fmt->audio_codec ; pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO ; //立体声 pCodecCtx->channel_layout = iOutputLayout ; pCodecCtx->channels = iOutputChans ; //编码比特率 AAC支持多种比特率 一般比特率越高 视频质量越好 需要传输带宽越大 pCodecCtx->bit_rate = OUTPUT_FILE_BIT_RATE; pCodecCtx->sample_rate = iOutputSampleRate; //PCM样本深度为AV_SAMPLE_FMT_S16 但不是所有格式的编码都支持这种格式 pCodecCtx->sample_fmt = eOutputSampleFormat; //编码器 AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id); if (!pCodec) { printf("Can not find encoder!\n"); return -1; } //打开解码器 有可能失败 -22 错误,原因不同的编码格式支持的样本格式不一样 //如封装AAC格式样本格式是AV_SAMPLE_FMT_FLT,打开就出错 if ((iReturn = avcodec_open2(pCodecCtx, pCodec,NULL)) < 0) { printf("Failed to open encoder :[%d]!\n",iReturn); return -1; } //重采用上下文 SwrContext *pSwrCtx = NULL; //原始数据帧 AVFrame* pRawframe = NULL; //原始帧一Planer的大小 非平面分布的情况就是缓存总大小 int iRawLineSize = 0; //原始帧缓存大小 int iRawBuffSize = 0; //原始帧缓存 uint8_t *pRawBuff= NULL; //重采样后数据帧 AVFrame* pConvertframe = NULL; //重采样后一Planer的大小 int iConvertLineSize = 0; //重采样后缓存大小 int iConvertBuffSize = 0; //重采样后帧缓存 uint8_t *pConvertBuff = NULL; //1帧数据样本数 int iFrameSamples = pCodecCtx->frame_size; // 存储原始数据 iRawLineSize = 0; iRawBuffSize = av_samples_get_buffer_size(&iRawLineSize, iInputChans, iFrameSamples, eInputSampleFormat, 0); pRawBuff = (uint8_t *)av_malloc(iRawBuffSize); //原始数据保存在AVFrame结构体中 pRawframe = av_frame_alloc(); pRawframe->nb_samples = iFrameSamples; pRawframe->format = eInputSampleFormat; pRawframe->channels = iInputChans; avcodec_fill_audio_frame(pRawframe, iInputChans, eInputSampleFormat, (const uint8_t*)pRawBuff, iRawBuffSize, 0); if(bNeedResample) //需要重采样 { pSwrCtx = swr_alloc_set_opts(NULL,iOutputLayout, eOutputSampleFormat, iOutputSampleRate,iInputLayout,eInputSampleFormat , iInputSampleRate,0, NULL); swr_init(pSwrCtx); // 存储转换后数据 iConvertLineSize = 0; iConvertBuffSize = av_samples_get_buffer_size(&iConvertLineSize, iOutputChans, iFrameSamples, eOutputSampleFormat, 0); pConvertBuff = (uint8_t *)av_malloc(iConvertBuffSize); //转换后数据保存在AVFrame结构体中 pConvertframe = av_frame_alloc(); pConvertframe->nb_samples = iFrameSamples; pConvertframe->format = eOutputSampleFormat; pConvertframe->channels = iOutputChans; avcodec_fill_audio_frame(pConvertframe, iOutputChans, eOutputSampleFormat, (const uint8_t*)pConvertBuff, iConvertBuffSize, 0); } //编码以后的数据是AVPacket AVPacket pkt; if(!bNeedResample){ av_new_packet(&pkt,iRawBuffSize); //设定预计需要的payload size }else{ av_new_packet(&pkt,iConvertBuffSize); } //Write Header avformat_write_header(pFormatCtx,NULL); //统计读取样本数 long long lReadTotalSamples = 0; //每次读取样本数 int iReadSamples; //统计所有的帧数 int iFrameNum =0; //是否编码成功 int got_frame =0; //临时 AVFrame* pTempFrame=NULL; //读取数据 保存在pConvertframe->data int iRealRead = fread(pRawBuff, 1, iRawBuffSize, pInputFile); while(iRealRead>0){ iReadSamples = iRealRead/(iInputSampleBytes*iInputChans); //数据长度/(单样本长度*通道数)=样本数 if(bNeedResample) { swr_convert(pSwrCtx, (uint8_t**)pConvertframe->data, iFrameSamples ,(const uint8_t**)pRawframe->data, iFrameSamples ); if(bPlanner) { //只保存一个通道 因为保存多个通道测试工具 audacity看不了 fwrite(pConvertframe->data[0],pConvertframe->linesize[0],1,pTempFile); } printf("Convert Frame :%d\n",++iFrameNum); pTempFrame = pConvertframe; } else { pTempFrame = pRawframe; } pTempFrame->pts = lReadTotalSamples; //一帧的pts为该帧的最后一个样本序号 got_frame = 0; //Encode if(avcodec_encode_audio2(pCodecCtx, &pkt,pTempFrame, &got_frame)<0) { printf("Failed to encode!\n"); return -1; } if (got_frame==1) { printf("Succeed to encode 1 frame! \tsize:%5d\n",pkt.size); pkt.stream_index = audio_st->index; av_write_frame(pFormatCtx, &pkt); av_free_packet(&pkt); } //统计样本数以转换前为准 转换前后样本数是一样的 lReadTotalSamples += (iReadSamples); iRealRead = fread(pRawBuff, 1, iRawBuffSize, pInputFile); } //刷新编码器 if(flush_encoder(pFormatCtx,0)<0) { printf("Flushing encoder failed\n"); return -1; } fclose(pInputFile); fclose(pOutputFile); fclose(pTempFile); av_free(pRawBuff); if(bNeedResample) { av_free(pConvertBuff); swr_free(&pSwrCtx); } printf("Convert Success!!\n"); getchar(); return 0; }
数据buf大小地计算:iRawBuffSize = av_samples_get_buffer_size(&iRawLineSize, iInputChans, iFrameSamples, eInputSampleFormat, 0); pRawframe申请后要设置 样本数 通道数 采样格式 绑定数据buf(计算出来)av_new_packet(&pkt,iRawBuffSize);//读取数据 保存在pConvertframe->dataint iRealRead = fread(pRawBuff, 1, iRawBuffSize, pInputFile);while(iRealRead>0){ iReadSamples = iRealRead/(iInputSampleBytes*iInputChans); pRawframe->pts = lReadTotalSamples; got_frame = 0; //Encode avcodec_encode_audio2(pCodecCtx, &pkt,pRawframe, &got_frame); if (got_frame==1) { printf("Succeed to encode 1 frame! \tsize:%5d\n",pkt.size); //一帧前面头ADTS MakeAdtsHeader(szAdtsHead,&iAdtsHeadLen,iAacProfile,iInputSampleRate,iInputChans ,pkt.size); fwrite(szAdtsHead, iAdtsHeadLen, 1, pOutputFile);//写头(包括同步符) fwrite(pkt.data, 1, pkt.size, pOutputFile); //解码数据写文件 av_free_packet(&pkt); } //统计样本数以转换前为准 转换前后样本数是一样的 lReadTotalSamples += (iReadSamples); iRealRead = fread(pRawBuff, 1, iRawBuffSize, pInputFile);}//Flush Encoder