我们先看一张pcm转化为aac的流程图
我们根据流程图
第一步获取AVOutputFormat
ff_const59 AVOutputFormat *av_guess_format(const char *short_name,
const char *filename,
const char *mime_type);
函数会根据文件名字,mime类型返回最适合的输出格式
第二步创建我们要创建的aac文件的上下文AVFormatContext *ofmt_ctx
avformat_alloc_output_context2(&ofmt_ctx, oformat, oformat->name, output)
对aac文件的操作要用到上下文,创建者上下文,我们知道要用AVOutputFormat的内容,还有输出文件的地址
第三步 创建编码器
我们要对pcm进行aac编码,要设置编码器的codec_type,位深,声道布局,声道数, 采样率,比特率,profile(具体的aac,aac low, aac he v1, aac he v2)
第四步创建输出流并设置输出流的编码参数
AVStream *out_stream = avformat_new_stream(ofmt_ctx, pCodec);
avcodec_parameters_from_context(out_stream->codecpar, pCodecCtx);
第五步 打开编码器 avcodec_open2(pCodecCtx, pCodec, NULL)
第六步 获取输出aac文件的AVIOContext
avio_open
第七步 写aac文件头
avformat_write_header adif 和 adts头
第八步 创建AVFrame ,设置声道数,单通道采样数,位深
第九步 初始化重采样上下文,设置重采样的输入格式输出格式
第十步 从输入文件读取单通道采样数 * 通道数 * 位深字节数大小的数据
第十一步 对读取的数据进行重采样
第十二步 设置pts,表明当前数据在aac的位置
第十三步 avcodec_send_frame 发送数据到编码器
第十四步 avcodec_receive_packet 获取到编码数据 并通过av_write_frame写入aac文件
第十五步 循环 读取数据并写入文件
第十六步 flush_encoder
第十七步 av_write_trailer
第十八步 释放资源
#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
int flush_encoder(AVFormatContext *ofmt_ctx, AVCodecContext * pCodecCtx, int stream_index)
{
AVCodecParameters *codecpar = ofmt_ctx->streams[stream_index]->codecpar;
int got_fame = 0;
AVPacket *pkt = av_packet_alloc();
int ret = 0;
while (1)
{
av_init_packet(pkt);
pkt->data = NULL;
pkt->size = 0;
if (avcodec_send_frame(pCodecCtx, NULL) < 0)
{
printf("fail to send frame\n");
return -1;
}
// 第十四步 avcodec_receive_packet 获取到编码数据 并通过av_write_frame写入aac文件
while (avcodec_receive_packet(pCodecCtx, pkt) >= 0)
{
pkt->stream_index = stream_index;
av_write_frame(ofmt_ctx, pkt);
av_packet_unref(pkt);
}
if (ret < 0)
{
break;
}
}
av_packet_free(&pkt);
return 0;
}
int main(int argc, char *argv[])
{
int ret = 0;
const char *input;
const char *output;
input = argv[1];
output = argv[2];
// 第一步获取AVOutputFormat
AVOutputFormat *oformat = av_guess_format(NULL, output, NULL);
if (oformat == NULL)
{
av_log(NULL, AV_LOG_ERROR, "fail to find the output format\n");
ret = -1;
goto __ERROR;
}
// 第二步创建我们要创建的aac文件的上下文AVFormatContext *ofmt_ctx
AVFormatContext *ofmt_ctx = avformat_alloc_context();
if (avformat_alloc_output_context2(&ofmt_ctx, oformat, oformat->name, output) < 0)
{
av_log(NULL, AV_LOG_ERROR, "fail to alloc output context\n");
ret = -1;
goto __ERROR;
}
//第三步 创建编码器
AVCodec *pCodec = avcodec_find_encoder_by_name("libfdk_aac");
AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);
pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; //其他会出错
pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
pCodecCtx->sample_rate = 44100;
pCodecCtx->bit_rate = 0;
pCodecCtx->profile = FF_PROFILE_AAC_HE_V2;
//第四步创建输出流并设置输出流的编码参数
AVStream *out_stream = avformat_new_stream(ofmt_ctx, pCodec);
avcodec_parameters_from_context(out_stream->codecpar, pCodecCtx);
if (out_stream == NULL)
{
av_log(NULL, AV_LOG_ERROR, "fail to create new stream\n");
ret = -1;
goto __ERROR;
}
if (pCodec == NULL)
{
av_log(NULL, AV_LOG_ERROR, "fail to find codec\n");
ret = -1;
goto __ERROR;
}
// 第五步 打开编码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
av_log(NULL, AV_LOG_ERROR, "fail to open codec\n");
ret = -1;
goto __ERROR;
}
av_dump_format(ofmt_ctx, 0, output, 1);
// 第六步 获取输出aac文件的AVIOContext
if (avio_open(&ofmt_ctx->pb, output, AVIO_FLAG_WRITE) < 0)
{
av_log(NULL, AV_LOG_ERROR, "fail to open output\n");
ret = -1;
goto __ERROR;
}
// 第七步写aac文件头
if (avformat_write_header(ofmt_ctx, NULL) < 0)
{
av_log(NULL, AV_LOG_ERROR, "fail to write header");
ret = -1;
goto __ERROR;
}
//第八步创建AVFrame ,设置声道数,位深,单通道采样数,为pFrame中的data分配空间
AVFrame *pframe = av_frame_alloc();
pframe->channels = pCodecCtx->channels;
pframe->format = pCodecCtx->sample_fmt;
pframe->nb_samples = pCodecCtx->frame_size;
int size = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);
uint8_t *out_buffer = (uint8_t *)av_malloc(size);
avcodec_fill_audio_frame(pframe, pCodecCtx->channels, pCodecCtx->sample_fmt, (const uint8_t *)out_buffer, size, 1);
// 第九步 初始化重采样上下文,设置重采样的输入格式输出格式
SwrContext *pSwrCtx = swr_alloc();
swr_alloc_set_opts(pSwrCtx, pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate,
pCodecCtx->channel_layout, AV_SAMPLE_FMT_S16, 44100, 0, NULL);
swr_init(pSwrCtx);
/*重采样后的数据保存的缓冲区*/
uint8_t **data = (uint8_t **)av_calloc(pCodecCtx->channels, sizeof(*data));
av_samples_alloc(data, NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);
AVPacket *pkt = av_packet_alloc();
av_new_packet(pkt, size);
pkt->data = NULL;
pkt->size = 0;
FILE *fp = fopen(input, "rb");
if (fp == NULL)
{
printf("fail to open file\n");
ret = -1;
goto __ERROR;
}
int count = 1;
while (1)
{
// 第十步 从输入文件读取单通道采样数 * 通道数 * 位深字节数大小的数据
size = pframe->nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * pframe->channels;
if (fread(out_buffer, 1, size, fp) < 0)
{
printf("fail to read raw data\n");
ret = -1;
goto __ERROR;
}
else if (feof(fp))
{
break;
}
// 第十一步 对读取的数据进行重采样
swr_convert(pSwrCtx, data, pCodecCtx->frame_size, (const uint8_t **)pframe->data, pframe->nb_samples);
size = pCodecCtx->frame_size * av_get_bytes_per_sample(pCodecCtx->sample_fmt);
memcpy(pframe->data[0], data[0], size);
memcpy(pframe->data[1], data[1], size);
// 第十二步 设置pts,表明当前数据在aac的位置
pframe->pts = count * 100;
// 第十三步 avcodec_send_frame 发送数据到编码器
if (avcodec_send_frame(pCodecCtx, pframe) < 0)
{
printf("fail to send frame\n");
ret = -1;
goto __ERROR;
}
// 第十四步 avcodec_receive_packet 获取到编码数据 并通过av_write_frame写入aac文件
while (avcodec_receive_packet(pCodecCtx, pkt) >= 0)
{
pkt->stream_index = out_stream->index;
av_log(NULL, AV_LOG_INFO, "write %d frame\n", count);
av_write_frame(ofmt_ctx, pkt);
av_packet_unref(pkt);
}
count++;
}
// 第十六步 flush_encoder
flush_encoder(ofmt_ctx, pCodecCtx, out_stream->index);
// 第十七步 av_write_trailer
av_write_trailer(ofmt_ctx);
__ERROR:
// 第十八步 释放资源
if (pkt)
{
av_packet_free(&pkt);
}
if (pSwrCtx)
{
swr_free(&pSwrCtx);
}
if (out_buffer)
{
av_free(out_buffer);
}
if (pframe)
{
av_frame_free(&pframe);
}
if (ofmt_ctx->pb)
{
avio_close(ofmt_ctx->pb);
}
if (pCodec)
{
}
if (pCodecCtx)
{
avcodec_close(pCodecCtx);
avcodec_free_context(&pCodecCtx);
}
if (ofmt_ctx)
{
avformat_free_context(ofmt_ctx);
}
return 0;
}
对于音频来说最重要的是采样率、采样大小、声道数(通道数)。
用慢来对抗块;
用体系对抗碎片;
用原理对抗招式。
加油!!!!!!!!!!!!!