本程序流程:
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(); //初始化上下文结构体AVFormatContextAVOutputFormat* 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_S16acc->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;
}