本程序流程:
1、创建输出码流的上下文AVFormatContext,并初始化
2、打开输出文件:avio_open2()
3、创建新流:avformat_new_stream() //用于保存音频流信息,一个完整的视频文件包含多个流信息:视频流、音频流、字幕流等
4、创建编码器上下文并设置编码参数
5、查找编码器并打开编码器
6、写入文件头信息:avformat_write_header()
7、打开输入文件
8、循环读取输入文件的yuv值,并进行编码;编码成功写入文件:av_write_frame()
9、对编码器中剩余数据编码
10、写入文件尾信息:av_write_trailer()
11、释放资源
音频编码流程和视频编码大致相同,部分解析可查看FFMPEG编码实现:将YUV文件编码为H264
/*
* 编码 pcm->acc
*/
#include <iostream>
#include <cstdio>
using namespace std;
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
//#include <libswresample/swresample.h> //音频重采样库
}a
const char* infile = "out.pcm";
const char* outfile = "out.aac";
int audiocount = 0;
int encodec_one_frame_audio(AVFormatContext *ctx, AVCodecContext *codec_ctx, AVPacket pkt, AVFrame *frame)
{
int ret = 0;
if (ret = avcodec_send_frame(codec_ctx, frame) != 0)
{
cout << "send audio packet to decodec error" << endl;
return -1;
}
while(ret >= 0)
{
ret = avcodec_receive_packet(codec_ctx, &pkt);
if (ret == 0)
{
//int audio_buffersize = av_samples_get_buffer_size(NULL, codec_ctx->channels, codec_ctx->frame_size, AV_SAMPLE_FMT_S16P, 1); //获取音频数据大小
cout << "channels = " << codec_ctx->channels << "rate = " << codec_ctx->sample_rate << "sample_fmt = " << codec_ctx->sample_fmt << endl; //输出音频的通道数、采样率和采样格式(这里输出8,为AV_SAMPLE_FMT_FLTP)
if(av_write_frame(ctx, &pkt) < 0)
{
cout << "write error" << endl;
return -4;//写入文件失败,可选择退出程序
}
audiocount++; //计数,输出看写入的第几个数据包
cout << "write pcm one frame succeed, this is " << audiocount << endl;
}
else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
cout << "receive audio frame error, need again" << endl;
return -2; //接收到的数据无效 需要重新读入
}
else
{
cout << "avcodec_receive_frame audio error code:" << ret << endl;
return -3; //编码错误,可选择直接退出程序
}
}
return 0; //解码成功
}
int main(int argc, char** argv)
{
AVFormatContext* afc = avformat_alloc_context(); //初始化上下文结构体AVFormatContext
AVOutputFormat* outformate;
outformate = av_guess_format(NULL, outfile, NULL);
if(outformate == NULL)
{
cout << "outformat error" << endl;
return -1;
}
afc->oformat = outformate;
if(avio_open2(&afc->pb, outfile, AVIO_FLAG_READ_WRITE, NULL, NULL) < 0) //已读写的方式打开输出文件,赋值IO上下文pb
{
cout << "open outfile error" << endl;
return -1;
}
AVStream *newstream = avformat_new_stream(afc, NULL); //创建新流,用作音频流
if(newstream == NULL)
{
cout << "new stream create error" << endl;
return -1;
}
AVCodec *codec = avcodec_find_encoder(acc->codec_id); //这里也可以使用avcodec_find_encoder_by_name("aac")
if(codec == NULL)
{
cout << "open encodec error" << endl;
return -1;
}
//设置编码参数
AVCodecContext* acc = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(acc, newstream->codecpar);//acc = newstream->codec;新版本丢弃了AVStream::codec成员,增加了codecpar成员,利用avcodec_parameters_to_context()赋值
acc->codec_id = afc->oformat->audio_codec;
acc->codec_type = AVMEDIA_TYPE_AUDIO; //编码器类型
acc->sample_fmt = AV_SAMPLE_FMT_FLTP; //音频采样格式//AV_SAMPLE_FMT_S16有些库不支持AV_SAMPLE_FMT_S16
acc->sample_rate = 48000; //采样率
acc->channel_layout = AV_CH_LAYOUT_STEREO; //声道布局
acc->channels = 2; //通道数
if (avcodec_open2(acc, codec, NULL) < 0)
{
cout << "open encodec error" << endl;
return -1;
}
AVFrame* aframe = av_frame_alloc();
aframe->nb_samples = acc->frame_size;
aframe->format = acc->sample_fmt;
int audiosize = av_samples_get_buffer_size(NULL, acc->channels, acc->frame_size, acc->sample_fmt, 1);
uint8_t *pcmdata; //用于存放输入文件的pcm数据信息
pcmdata = (uint8_t*)av_malloc(audiosize);
AVPacket pkt;
av_new_packet(&pkt, audiosize);//av_packet_init(&pkt);//(AVPacket*)av_malloc(sizeof(AVPacket));
//写文件头信息
avformat_write_header(afc, NULL);
FILE* inF = fopen(infile, "rb");
while (true)
{
if(fread(pcmdata, 1, audiosize, inF) <= 0)
{
if(feof(inF))
break;
else
{
cout << "read data error" << endl;
return -1;
}
}
aframe->data[0] = pcmdata;
int re = encodec_one_frame_audio(afc, acc, pkt, aframe);
if (re == -3 || re == -1 || re == -4)
{
cout << "encodec error" << endl;
return -1;
}
}
//再次编码输出解码器中的数据,否则会丢失部分数据
int re = encodec_one_frame_audio(afc, acc, pkt, NULL);
if (re == -3 || re == -1 || re == -4)
{
cout << "encodec error" << endl;
return -1;
}
//写文件尾
av_write_trailer(afc, NULL);
//释放资源
fclose(inF);
av_packet_unref(&pkt);
av_frame_free(&aframe);
avcodec_free_context(&acc);
avformat_close_input(&afc);
return 0;
}