本篇文章要把pcm原始音频数据编码为AAC格式的音频数据。由于aac格式只支持fltp格式的pcm数据,也就是float planar格式,所以,打算输入两个pcm原始音频文件,一个是左声道,一个是右声道,两文件都是f32le、单声道、采样率48000的音频文件。这样读入两个文件的内容,分别放入AVFrame的data[0]和data[1]当中。
编码为AAC格式,还要加入ADTS头,才能播放。
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
{
const enum AVSampleFormat *p = codec->sample_fmts;
while (*p != AV_SAMPLE_FMT_NONE) {
if (*p == sample_fmt)
return 1;
p++;
}
return 0;
}
int find_sample_index(int samplerate)
{
int adts_sample_rates[] = {96000,882000,64000,48000,441000,32000,24000,22050,16000,12000,11025,8000,7350,0,0,0};
int i;
for(i=0; i < 16;i++)
{
if(samplerate == adts_sample_rates[i])
return i;
}
return 16 - 1;
}
static void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt,
FILE *output)
{
int ret;
/* send the frame for encoding */
ret = avcodec_send_frame(ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending the frame to the encoder\n");
exit(1);
}
/* read all the available output packets (in general there may be any
* number of them */
while (ret >= 0) {
ret = avcodec_receive_packet(ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error encoding audio frame\n");
exit(1);
}
char bits[7] = {0};
int aac_frame_length = 7 + pkt->size;
int sample_index = find_sample_index(ctx->sample_rate);
int channels = ctx->channels;
if (channels == 8) {
channels = 7;
}
bits[0] = 0xff;
bits[1] = 0xf9;
bits[2] = (ctx->profile << 6);
bits[2] |= (sample_index << 2);
bits[2] |= (channels >> 2);
bits[3] |= ((channels << 6) & 0xC0);
bits[3] |= (aac_frame_length >> 11);
bits[4] = ((aac_frame_length >> 3) & 0xFF);
bits[5] = ((aac_frame_length << 5) & 0xE0);
bits[5] |= (0x7FF >> 6);
bits[6] = 0xfc;
fwrite(bits, 1, 7, output);
fwrite(pkt->data, 1, pkt->size, output);
av_packet_unref(pkt);
}
}
void encode_audio(void)
{
const char *infilename_left = "/Users/zhw/Desktop/sintel_f32le_left_1_48000.pcm";
const char *infilename_right = "/Users/zhw/Desktop/sintel_f32le_right_1_48000.pcm";
const char *outfilename = "/Users/zhw/Desktop/result.aac";
const AVCodec *codec;
AVCodecContext *c= NULL;
AVFrame *frame;
AVPacket *pkt;
int ret;
FILE *out_file, *in_file_left, *in_file_right;
/* 编码为aac格式 */
codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate audio codec context\n");
exit(1);
}
/* 设置码率 */
c->bit_rate = 64000;
/* 检查编码器是否支持给定的sample_fmt */
c->sample_fmt = AV_SAMPLE_FMT_FLTP;
if (!check_sample_fmt(codec, c->sample_fmt)) {
fprintf(stderr, "Encoder does not support sample format %s",
av_get_sample_fmt_name(c->sample_fmt));
exit(1);
}
/* 设置采样率,声道等参数 */
c->sample_rate = 48000;
c->channel_layout = AV_CH_LAYOUT_STEREO;
c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
/* 打开编码器 */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
in_file_left = fopen(infilename_left, "rb");
if (!in_file_left) {
fprintf(stderr, "Could not open %s\n", infilename_left);
exit(1);
}
in_file_right = fopen(infilename_right, "rb");
if (!in_file_right) {
fprintf(stderr, "Could not open %s\n", infilename_right);
exit(1);
}
out_file = fopen(outfilename, "wb");
if (!out_file) {
fprintf(stderr, "Could not open %s\n", outfilename);
exit(1);
}
/* 创建pkt,用于存放编码后的输出数据 */
pkt = av_packet_alloc();
if (!pkt) {
fprintf(stderr, "could not allocate the packet\n");
exit(1);
}
/* 创建frame,存放编码前端原始数据 */
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
}
frame->nb_samples = c->frame_size;
frame->format = c->sample_fmt;
frame->channel_layout = c->channel_layout;
/*设置完frame->nb_samples,frame->format,frame->channel_layout等参数后,根据这些参数创建buffer,
即frame->data */
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate audio data buffers\n");
exit(1);
}
int bytes_per_sample = av_get_bytes_per_sample(c->sample_fmt);
while (!feof(in_file_left) && !feof(in_file_right)) {
//确保frame是可写的
ret = av_frame_make_writable(frame);
if (ret < 0)
exit(1);
fread(frame->data[0], 1, c->frame_size * bytes_per_sample, in_file_left);
fread(frame->data[1], 1, c->frame_size * bytes_per_sample, in_file_right);
encode(c, frame, pkt, out_file);
}
/* flush encoder中的剩余数据 */
encode(c, NULL, pkt, out_file);
fclose(in_file_left);
fclose(in_file_right);
fclose(out_file);
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&c);
printf("finish encode\n");
}