博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AAC--ffmpeg编码
阅读量:3918 次
发布时间:2019-05-23

本文共 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会失败,故需要重采样)

需要重采样–即swr转换

1.ffmpeg使用封装格式实现pcm👉aac/mp3

/*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
你可能感兴趣的文章
printf()用法
查看>>
c++ 启动exe(启动别的exe程序)
查看>>
Dynamic Forking of Win32 EXE
查看>>
重载CDialog::PreCreateWindow是无效的
查看>>
vs2012窗口布局恢复
查看>>
WINDOWS下的ANSI字符串和UTF8字符串之间的相互转换
查看>>
第八章 右左法则----复杂指针解析
查看>>
使用CFileFind遍历递归删除文件和文件夹
查看>>
使用opencv 录摄像头数据到文件
查看>>
error C1189:#error:This file requires _WIN32_WINNT to be #defined at least to 0x0403
查看>>
CentOS yum 源的配置与使用
查看>>
error while loading shared libraries: libevent-2.0.so.5 安装好mamcache,启动服务时
查看>>
c++ web编程:写出你的CGI程序
查看>>
linux使用小技巧
查看>>
16 条技巧让你更高效使用 SSH
查看>>
Linux RPM 命令参数使用详解 查看 rpm包依赖性
查看>>
linux shell 脚本实现tcp/upd协议通讯(重定向应用)
查看>>
upstart 分析 and linux启动分析 (非常详细)
查看>>
图片ascii化的方法
查看>>
xargs使用技巧
查看>>