新版的ffmpeg 编码AAC只支持的AV_SAMPLE_FMT_FLTP,老版本的是AV_SAMPLE_FMT_S16,如果输入的PCM数据是AV_SAMPLE_FMT_S16的,avcodec_encode_audio2会返回-22错误.
#include <libavcodec/avcodec.h>
#include <libavutil/samplefmt.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
void encode(AVCodecContext* codecCtx,AVFrame* frame,AVPacket* packet,AVFormatContext* formatCtx);
static int ptsIndex = 0;
int main(int argc,char* argv[])
{
AVCodecContext* codecCtx = NULL;
AVCodec* codec = NULL;
int ret = 0;
AVFrame* frame = NULL;
AVPacket* packet = NULL;
AVFormatContext* formatCtx = NULL;
AVStream* stream = NULL;
/* 根据传入的音频类型ID寻找并返回对应解码器 */
codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if(!codec)
{
printf("codec not find\n");
exit(1);
}
/* 给编解码上下文分配内存并赋默认值 */
codecCtx = avcodec_alloc_context3(codec);
if(!codecCtx)
{
printf("Could not allocate audio codec context\n");
exit(1);
}
/* 设置码率,大概为62kb/s */
codecCtx->bit_rate = 64000;
/* 采样格式,类似于图像存储格式,位数越多,表示值越精确 */
codecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
/* 检查编码器是否支持这种采样格式 */
const enum AVSampleFormat* sampleFmt = codec->sample_fmts;
while(*sampleFmt != AV_SAMPLE_FMT_NONE)
{
if(*sampleFmt == codecCtx->sample_fmt)
break;
sampleFmt++;
}
if(*sampleFmt != codecCtx->sample_fmt)
{
printf("Encoder not support sample format,%d %d\n",*sampleFmt,codecCtx->sample_fmt);
exit(1);
}
/* 采样率:采样设备每秒抽取样本的次数;下面操作是选择最高的采样率 */
if(!codec->supported_samplerates)
codecCtx->sample_rate = 44100;
const int* tempPtr = codec->supported_samplerates;
int bestSampleRate = 0;
while(*tempPtr)
{
if(!bestSampleRate || abs(44100 - *tempPtr) < abs(44100 - bestSampleRate))
bestSampleRate = *tempPtr;
tempPtr++;
}
codecCtx->sample_rate = bestSampleRate;
/* 通道布局:是一个64位整数,每个值为1的位对应一个通道;
下面操作从编码器中选择具有最高通道计数的布局 */
if(!codec->channel_layouts)
codecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
else
{
const uint64_t* tempPtr2 = codec->channel_layouts;
int bestNbChannel = 0;
uint64_t bestChLayout = 0;
while(*tempPtr2)
{
int nbChannel = av_get_channel_layout_nb_channels(*tempPtr2);
if(nbChannel > bestNbChannel)
{
bestChLayout = *tempPtr2;
bestNbChannel = nbChannel;
}
tempPtr2++;
}
codecCtx->channel_layout = bestChLayout;
}
/*具体通道个数*/
codecCtx->channels = av_get_channel_layout_nb_channels(codecCtx->channel_layout);
/*初始化AVCodecContext,各种分配内存和检查,初始化到具体的编码器库*/
ret = avcodec_open2(codecCtx,codec,NULL);
if(ret != 0)
{
fprintf(stderr, "Could not open codec\n");
exit(1);
}
/* 新版的ffmpeg 编码AAC只支持的AV_SAMPLE_FMT_FLTP,老版本的是AV_SAMPLE_FMT_S16
如果输入的PCM数据是AV_SAMPLE_FMT_S16的,avcodec_encode_audio2会返回-22错误 */
const char* inFilename = "source/test.pcm";
const char* outFilename = "source/test.aac";
FILE* inFile = fopen(inFilename,"rb");
packet = av_packet_alloc();
frame = av_frame_alloc();
frame->nb_samples = codecCtx->frame_size;
frame->format = codecCtx->sample_fmt;
frame->channel_layout = codecCtx->channel_layout;
ret = av_frame_get_buffer(frame,0);
if (ret < 0)
{
fprintf(stderr, "Could not allocate audio data buffers\n");
exit(1);
}
/* 分配内存,根据格式名(第三个参数)或者文件名(第四个参数)获取输出格式 */
avformat_alloc_output_context2(&formatCtx,NULL,NULL,outFilename);
if(!formatCtx)
{
printf("could not alloc output context,exit!!\n");
exit(1);
}
stream = avformat_new_stream(formatCtx,codec);
if(avcodec_parameters_from_context(stream->codecpar,codecCtx) < 0)
{
fprintf(stderr,"copy parameters fail!\n");
exit(1);
}
/* AVFMT_NOFILE跟是否是一个真实的文件无关 */
if(!(formatCtx->oformat->flags & AVFMT_NOFILE))
{
if(avio_open2(&formatCtx->pb,outFilename,AVIO_FLAG_WRITE,NULL,NULL))
{
printf("avio open fail\n");
exit(1);
}
}
/* 每个通道占的字节数 */
int dataSize = av_get_bytes_per_sample(codecCtx->sample_fmt);
/* 针对不同的编码格式,写不同的文件头*/
if(avformat_write_header(formatCtx,NULL) < 0)
{
fprintf(stderr, "Error occurred when opening output file: %s\n",av_err2str(ret));
goto end;
}
while(!feof(inFile))
{
/* 确保frame是可写的,例如编码器要缓存此帧,下面函数就会分配新的缓冲区,并复制参数 */
ret = av_frame_make_writable(frame);
if(ret < 0)
{
printf("%s %d ret:%d\n",__FUNCTION__,__LINE__,ret);
goto end;
}
for(int i = 0;i < codecCtx->frame_size;++i)
for(int j = 0; j < codecCtx->channels;++j)
fread(frame->data[j]+dataSize*i,1,dataSize,inFile);
encode(codecCtx,frame,packet,formatCtx);
}
/* 发送一个空帧,可以防止编码器中数据未处理完 */
encode(codecCtx,NULL,packet,formatCtx);
/* 针对不同的编码格式,写不同的文件尾 */
if(av_write_trailer(formatCtx) != 0)
printf("write file trail fail\n");
printf("%s %d end!!!\n",__FUNCTION__,__LINE__);
end:
if(inFile)
fclose(inFile);
avcodec_free_context(&codecCtx);
avformat_free_context(formatCtx);
av_frame_free(&frame);
av_packet_free(&packet);
return 0;
}
void encode(AVCodecContext* codecCtx,AVFrame* frame,AVPacket* packet,AVFormatContext* formatCtx)
{
int ret = 0;
ret = avcodec_send_frame(codecCtx,frame);
if(ret < 0)
{
printf(" send frame to encodec faile! ret:%d\n",ret);
exit(1) ;
}
while(ret >= 0)
{
ret = avcodec_receive_packet(codecCtx,packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error encoding audio frame\n");
exit(1);
}
packet->pts = ptsIndex*100;
packet->dts = ptsIndex*100;
packet->pos = -1;
packet->stream_index = 0;
if(av_interleaved_write_frame(formatCtx,packet) != 0)
{
printf("write frame fail\n");
break;
}
ptsIndex++;
av_packet_unref(packet);
}
}