您的位置:首页 > 其它

ffmpeg学习十:将pcm格式的音频编码为aac格式

2016-12-23 15:35 549 查看
上一节,我们使用alsa库编写了音频的采集和播放的程序。这一节,我们将在采集到的pcm格式的音频数据的基础上,进一步将其编码为aac格式。

音频编码概述

pcm是最原始的音频编码格式,这种编码是无损的。同时意味着存储这种数据的文件将会很庞大,因此必须进行压缩。pcm是音频的编码格式,它不是文件的封装格式,上一节我们录制的声音存储在一个.pcm为后缀的文件中,这只是我们愿意这么做而已,你完全可以不这么做,这没有关系。

aac既是一种文件的封装格式,又是音频的编码格式。一aac为封装格式的文件,以.aac为后缀。aac封装格式一般内部的音频数据编码格式也为aac。

音频编码和视频编码的流程基本一致,而视频编码我们在前面已经做过了。因此,关于程序的流程就没有太多需要废话的了。下面介绍几个音频相关的参数,这几个参数是编码器进行编码所必需的。

我们总共需要设置四个参数即可:

1.sample_rate

codecContext->sample_rate = frame->sample_rate;

sample_rate指的是采样率。也就是我们一秒钟采集多少次声音样本。

2.frame->channels

codecContext->channels = frame->channels;

frame->channels之的是通道的数目。音频一般有双通道或者单通道之分,一般都是双通道吧,我们的程序里面也是设置为双通道的。也就是frame->channels=2.

3.frame->format

codecContext->sample_fmt = frame->format;

frame->format指的是样本的格式。一个音频的样本一般用两个字节来描述,分为大小端。我们的程序中使用的是16bit的小端格式。

4.channel_layout

codecContext->channel_layout = AV_CH_LAYOUT_STEREO;

channel_layout 用来设置输出通道布局。这个参数不太理解!!!

程序概述

程序流程图如下:



程序

下面结合程序流程图和程序中的注释,来看程序:

//created by Jinwei Liu
#include <math.h>
#include <stdlib.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libavutil/frame.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

int main(int argc, char **argv){
AVFrame *frame;
AVCodec *codec = NULL;
AVPacket packet;
AVCodecContext *codecContext;
int readSize=0;
int ret=0,getPacket;
FILE * fileIn,*fileOut;
int frameCount=0;
/* register all the codecs */
av_register_all();

if(argc!=3){
fprintf(stdout,"usage:./a.out xxx.pcm xxx.aac\n");
return -1;
}

//1.我们需要读一帧一帧的数据,所以需要AVFrame结构
//读出的一帧数据保存在AVFrame中。
frame  = av_frame_alloc();
frame->format = AV_SAMPLE_FMT_S16;
frame->nb_samples = 1024;
frame->sample_rate = 11025;
frame->channels = 2;
fileIn =fopen(argv[1],"r+");
frame->data[0] = av_malloc(1024*4);

//2.读出来的数据保存在AVPacket中,因此,我们还需要AVPacket结构体
//初始化packet
memset(&packet, 0, sizeof(AVPacket));
av_init_packet(&packet);

//3.读出来的数据,我们需要编码,因此需要编码器
//下面的函数找到h.264类型的编码器
/* find the mpeg1 video encoder */
codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!codec){
fprintf(stderr, "Codec not found\n");
exit(1);
}

//有了编码器,我们还需要编码器的上下文环境,用来控制编码的过程
codecContext = avcodec_alloc_context3(codec);//分配AVCodecContext实例
if (!codecContext){
fprintf(stderr, "Could not allocate video codec context\n");
return -1;
}
/* put sample parameters */
codecContext->sample_rate = frame->sample_rate;
codecContext->channels = frame->channels;
codecContext->sample_fmt = frame->format;
/* select other audio parameters supported by the encoder */
codecContext->channel_layout = AV_CH_LAYOUT_STEREO;
//准备好了编码器和编码器上下文环境,现在可以打开编码器了
//根据编码器上下文打开编码器
if (avcodec_open2(codecContext, codec, NULL) < 0){
fprintf(stderr, "Could not open codec\n");
return -1;
}
//4.准备输出文件
fileOut= fopen(argv[2],"w+");
//下面开始编码
while(1){
//读一帧数据出来
readSize = fread(frame->data[0],1,1024*4,fileIn);
if(readSize == 0){
fprintf(stdout,"end of file\n");
frameCount++;
break;
}
//初始化packet
av_init_packet(&packet);
/* encode the image */
frame->pts = frameCount;
ret = avcodec_encode_audio2(codecContext, &packet, frame, &getPacket); //将AVFrame中的像素信息编码为AVPacket中的码流
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
goto out;
}

if (getPacket) {
frameCount++;
//获得一个完整的编码帧
printf("Write frame %3d (size=%5d)\n", frameCount, packet.size);
fwrite(packet.data, 1,packet.size, fileOut);
av_packet_unref(&packet);
}

}

/* flush buffer */
for ( getPacket= 1; getPacket; frameCount++){
frame->pts = frameCount;
ret = avcodec_encode_audio2(codecContext, &packet, NULL, &getPacket);       //输出编码器中剩余的码流
if (ret < 0){
fprintf(stderr, "Error encoding frame\n");
goto out;
}
if (getPacket){
printf("flush buffer Write frame %3d (size=%5d)\n", frameCount, packet.size);
fwrite(packet.data, 1, packet.size, fileOut);
av_packet_unref(&packet);
}
} //for (got_output = 1; got_output; frameIdx++)

out:
fclose(fileIn);
fclose(fileOut);
av_frame_free(&frame);
avcodec_close(codecContext);
av_free(codecContext);
return 0;
}


实验

使能aac编码器

现在,我们还不能运行这个程序,因为我们在便宜ffmpeg工程的时候没有使能aac编码器,所以我们需要重新编译ffmpeg,是的aac编码器可用。

libfdk_aac:

官网关于使能aac的介绍

-enable-nonfree –enable-libfdk-aac

同时我们需要安装libfdk-aac库:

sudo apt-get install libfdk-aac-dev

然后我们需要重新配置和便宜ffmpeg工程:

1.执行

./configure --enable-libx264 --enable-gpl --enable-decoder=h264 --enable-encoder=libx264 --enable-shared --enable-static --disable-yasm --enable-nonfree  --enable-libfdk-aac --enable-shared --prefix=tmp


2.make

3.make install

运行程序

有了aac编码器以后,再次运行这个程序,就可以将pcm格式的音频数据编码为aac格式了,例如,执行如下命令:

./out.bin recorder.pcm recorder.aac

使用ffplay recorder.aac即可听到声音。

附注

Makefile

VAR_INCLUDE := -I /home/mlinux/work/ffmpeg-2.7/ffmpeg-2.7.6/tmp/include
VAR_SHARED_LIB_DIR = -L /home/mlinux/work/ffmpeg-2.7/ffmpeg-2.7.6/tmp/lib
VAR_SHARED_LIBS = -l avcodec -l avformat  -l avdevice -l avutil -l swresample -l avfilter -l swscale

out.bin:pcm_2_aac.o
gcc  -o $@ $^  $(VAR_INCLUDE)  $(VAR_SHARED_LIB_DIR) $(VAR_SHARED_LIBS)

%.o:%.c
gcc   -c $<  $(VAR_INCLUDE)  $(VAR_SHARED_LIB_DIR) $(VAR_SHARED_LIBS)
clean:
rm -rf *.o *.bin


init.sh

export LD_LIBRARY_PATH=/home/mlinux/work/ffmpeg-2.7/ffmpeg-2.7.6/tmp/lib


看过前面文章的reader就应该知道,init.sh就是到处共享库的位置,让我们的可执行程序知道动态链接库的位置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ffmpeg 编码 aac pcm