android音频编码

libfdk_aac 编码 AAC
软件编码 AAC ,将基于 FFmpeg API 来编写,而不像第 2 章那样直
接使用 LAME 库的 API 来编码 MP3 。这样做的好处是,只需要编写一份
音频编码的代码即可,对于不同的编码器,只需要调整相应的编码器 ID
或者编码器 Name ,就可以编码出不同格式的音频文件。当然,既然要
使用第三方库 libfdk_aac 编码 AAC 文件,那么必须在做交叉编译的时候
libfdk_aac 库编译到 FFmpeg 中去。可编写一个 C++ 的类,命名为
audio_encoder ,然后对外提供三个接口,分别是初始化、编码以及销毁
方法,并且要求该类可以同时运行在 Android 平台和 iOS 平台之上。首先
来看一下初始化接口:
int init(int bitRate, int channels, int sampleRate, int bitsPerSample,
const char* aacFilePath, const char * codec_name);
对于其中的传入参数,说明如下。
首先是比特率,也就是最终编码出来的文件的码率,接着是声道
数、采样率,这两个将不再赘述,然后是最终编码的文件路径,最后是
编码器的名字。其实现步骤具体如下。
首先调用方法 av_register_all ()将所有的封装格式以及编解码器注
册到 FFmpeg 框架中;然后调用 avformat_alloc_output_context2 方法传入
输出文件格式,分配出上下文,即分配出封装格式;之后调用
avio_open2 方法传入 AAC 的编码路径,相当于打开文件连接通道。前面
分析过 FFmpeg 的源码,通过上述两行代码可以确定出 Muxer
Protocol ,然后就是分配 Codec 的相关内容了,所以接下来要为
AVFormatContext 上下文填充一轨 AVStream
audioStream = avformat_new_stream(avFormatContext, NULL);
现在,要为 audioStream Codec 属性填充内容了。 Codec 属性是一个
AVCodecContext 的结构体类型,需要为该结构体填充如下几个属性,首
先是 codec_type ,赋值为 AVMEDIA_TYPE_AUDIO ,代表其是音频类 型;其次是 bit_rate sample_rate channels 等基本属性,然后是
channel_layout (其表示的意义与 channels 是一样的,只不过可选值是两
个常量,分别是 AV_CH_LAYOUT_MONO 代表单声道、
AV_CH_LAYOUT_STEREO 代表立体声),最后也是最重要的
sample_fmt ,代表了如何数字化表示采样,使用的是
AV_SAMPLE_FMT_S16 ,即用一个 short 来表示一个采样点,这样就把
AVCodecContext 结构体构造完成了。
下面要准备最重要的编码器了,通过调用
avcodec_find_encoder_by_name 函数来找出对应的编码器,然后调用方
avcodec_open2 来为该编码器上下文打开这个编码器,接下来为编码
器指定 frame_size 的大小,一般指定 1024 作为一帧的大小,至此我们就
把编码器部分给分配好了。
在此需要注意的是,某些编码器只允许特定格式的 PCM 作为输入
源,比如声道数、采样率、表示格式(比如 LAME 编码器就不允许
SInt16 的表示格式)的要求,所以需要构造一个重采样器来将 PCM 数据
转换为可适配编码器输入的 PCM 数据,即前面讲解过的需要将输入的声
道、采样率、表示格式和输出的声道、采样率、表示格式传递给初始化
方法,然后分配出重采样上下文 SwrContext 。此外,还要分配一个
AVFrame 类型的 inputFrame ,作为客户端代码输入的 PCM 数据存放的地
方,这里需要知道 inputFrame 分配的 buffer 的大小,如上一步所述,默认
一帧的大小是 1024 ,所以对应的 buffer (按照 uint8_t 类型作为一个元素
来分配)大小就应该是:
bufferSize = frame_size * sizeof(SInt16) * channels;
当然无需自己进行计算,可以调用 FFmpeg 提供的方法
av_samples_get_buffer_size 来帮助开发者计算。其实这个方法内部的计
算公式就是上面所列的公式。如果需要进行重采样处理,那就需要额外
分配一个重采样之后的 AVFrame 类型的 swrFrame ,作为最终得到结果的
AVFrame
在初始化方法的最后,需要调用 FFmpeg 提供的方法
avformat_write_header 将该音频文件的 Header 部分写进去,然后记录一
个标志 isWriteHeaderSuccess ,使其为 true ,因为后续在销毁资源的阶段
需要根据该标志来判断是否调用 write trailer 方法,即写入文件尾部的方 法,否则会造成 Crash ,这在前面分析 FFmpeg 源码的时候就已经讲解过
了。
接下来看一下提供的第二个接口方法:
void encode(byte* buffer, int size);
这里传入的参数是 uint8_t 类型的指针,以及这块内存所表示的数据
长度,具体的实现是将该 buffer 填充入 inputFrame ,由于前面已经知道了
每一帧 buffer 需要填充的大小是多少,所以这里可以利用一个 while 循环
来做数据的缓冲,一次性填充到 AVFrame 中去,然后调用编码方法
avcodec_encode_audio2 ,该方法会将编码好的数据放入 AVPacket 的结构
体中,紧接着就可以将该 AVPacket 类型的结构体写到文件中去了,而调
av_interleaved_write_frame 方法,则可以将该 packet 输出到最终的文件
中去。
最后,来看一下第三个接口方法:
void destroy();
上述代码所述方法需要销毁前面所分配的资源以及打开的连接通
道。
如果初始化了重采样器,那么就销毁重采样的数据缓冲区以及重采
样上下文;然后销毁为输入 PCM 数据分配的 AVFrame 类型的
inputFrame ,再判断标志 isWriteHeaderSuccess 变量,决定是否需要填充
duration 以及调用方法 av_write_trailer ,最终关闭编码器以及连接通道。
这个类写完之后,就可以集成到 Android iOS 平台了,外界控制层
需要初始化该类,然后负责读写文件调用 encode 方法,最终调用销毁资
源的方法。
本节的代码实例, Android 工程是 Android 代码仓库中的
FDKAACAudioEncoder 工程, iOS 工程是 iOS 的代码仓库中的
FDKAACEncoder 工程,两个工程都是将 PCM 文件编码为一个 AAC 的文
件。运行 Android 工程之前,需要将 resource 目录下面的 PCM 文件放入 SDCard 目录下的根目录下面,最终可以播放编码后的 AAC 文件进行试
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值