前面四篇文章分别介绍了 音频开发必备的基础知识 、 如何采集一帧音频 、 如何播放一帧音频 、 如何存储和解析wav格式的文件 ,建议有兴趣的小伙伴们先读一读,本文则重点关注如何对一帧音频数据进行编码和解码。
首先,我们了解一下 Android 官方提供的音频编解码的 API,即 MediaCodec 类,该 API 是在 Andorid 4.1 (API 16) 版本引入的,因此只能工作于 Android 4.1 以上的手机上。
(1)提供了一套访问 Android 底层多媒体模块的接口,主要是音视频的编解码接口
(2)Android 底层多媒体模块采用的是 OpenMax 框架,任何 Android 底层编解码模块的实现,都必须遵循 OpenMax 标准。Google 官方默认提供了一系列的软件编解码器:包括:OMX.google.h264.encoder,OMX.google.h264.encoder, OMX.google.aac.encoder, OMX.google.aac.decoder 等等,而硬件编解码功能,则需要由芯片厂商依照 OpenMax 框架标准来完成,所以,一般采用不同芯片型号的手机,硬件编解码的实现和性能是不同的
(3)Android 应用层统一由 MediaCodec API 来提供各种音视频编解码功能,由参数配置来决定采用何种编解码算法、是否采用硬件编解码加速等等
我不准备详细介绍 MediaCodec API 的每个函数是怎么用,示例代码大家可以在后面给出的资源链接中查看和学习。
这里我准备重点介绍一下 MediaCodec 的核心工作原理,因为只有搞清楚了这一点,你才会明白为什么 MediaCodec API 提供的接口是这个样子的。
MediaCodec 使用的基本流程是:
- createEncoderByType/createDecoderByType - configure - start - while(1) { - dequeueInputBuffer - queueInputBuffer - dequeueOutputBuffer - releaseOutputBuffer } - stop - release
由此可以看到,Buffer 队列的操作是其最核心的部分之一,关于 MediaCodec 的 Buffer 队列 ,示意图如下:
MediaCodec 架构上采用了2个缓冲区队列,异步处理数据,下面描述的 Client 和 MediaCodec 模块是并行工作的(注:这里的 Client 就是指 “开发者,API 的使用者”):
(1)Client 从 input 缓冲区队列申请 empty buffer [dequeueInputBuffer]
(2)Client 把需要编解码的数据拷贝到 empty buffer,然后放入 input 缓冲区队列 [queueInputBuffer]
(3)MediaCodec 模块从 input 缓冲区队列取一帧数据进行编解码处理
(4)编解码处理结束后,MediaCodec 将原始数据 buffer 置为 empty 后放回 input 缓冲区队列,将编解码后的数据放入到 output 缓冲区队列
(5)Client 从 output 缓冲区队列申请编解码后的 buffer [dequeueOutputBuffer]
(6)Client 对编解码后的 buffer 进行渲染/播放
(7)渲染/播放完成后,Client 再将该 buffer 放回 output 缓冲区队列 [releaseOutputBuffer]
MediaCodec 在架构上,其实是采用了一种基于“环形缓冲区”的“生产者-消费者”模式,它设计了 2 个基于 idx 序号的“环形缓冲区”,注意,是 2 个,一个在 input 端, 一个在 output 端。
我曾经在 Github 上分享过一段 Linux C 代码,名叫:“rw_queue”,就是这种环形缓冲区的简化版,大家有兴趣可以看看,地址: https://github.com/Jhuster/clib/tree/master/rw_queue
基于 idx 的环形缓冲区的总体示意图如下,图中,wp 代表 “写指针”,指向的是 “empty buffer”, 而 rp 代表 “读指针”,指向的是 “filled buffer”:
“生产者”和“消费者”其实是共用这一个缓冲区队列,“生产者”负责从队列中取出未使用的 Buffer,填入数据,然后放回队列,“消费者”则负责取出填入数据后的 Buffer,进行处理,处理结束后,再把 Buffer 标记为“空”,退回到队列中去以供“生产者”继续填充数据。
在 input 端,“Client”是这个环形缓冲区“生产者”,“MediaoCodec 模块”是“消费者”。
在 output 端,“MediaoCodec 模块”是这个环形缓冲区“生产者”,而“Client”则变成了“消费者”。
这就是其核心的工作原理,其实并不复杂,大家静下心来,很快就能理解其中的奥妙。
关于 MediaCodec 的示例代码,网上其实也很多了,我就直接给出一些个人觉得不错的链接,有兴趣的小伙伴们可以去研究一下。
(1)Android 官方文档: 《MediaCodec》
(2) 《Android MediaCodec stuff》
(3) 《HWEncoderExperiments》
(4)一些开源的播放器 Android 源码,如 VLC 、 ijkplayer
官方的 MediaCodec API 虽然支持硬件编解码加速,但是问题和局限还是很多的,一方面是只能在 Android 4.1 以上机型上才能使用,另一方面,由于 Android 手机种类繁多,厂商对底层源码的修改各不相同,导致 MediaCodec API 在实际使用中,会遇到很多坑,有很多兼容性的问题,因此,我们也可以考虑采用第三方的编解码库。
这里,我简单推荐几款第三方音频编解码库(可以移植到 Android 平台的),大家可以直接去官网或者项目主页了解其详细信息。
很喜欢 opus,低码率下 opus 完胜曾经优势明显的 HE AAC,我曾经用它实现了一款 Android 局域网的VoIP网络电话应用:“飞鸽电话”,效果很不错。
opus 官网地址: https://www.opus-codec.org
注:如今 Android 5.0 已经官方支持 opus 格式了,关于 Android 支持的多媒体格式列表可以查看 Android developer guide: 《Supported Media Formats》
老牌的音频处理库,不仅是编解码,还提供了包括音频去噪、回声消除、静音检测等功能,官网地址: http://www.speex.org
大名鼎鼎的 ffmpeg 肯定不能错过,官网: https://www.ffmpeg.org
一款轻量级的 Android aac 编码库: https://github.com/timsu/android-aac-enc
从 opencore 抽取出来的 amr 编解码库,地址: https://github.com/kevinho/opencore-amr-android
iLBC 是著名的 WebRTC 项目的音频编解码模块,iLBC-Android 是从中抽取 iLBC 模块移植到 Android 平台的个人项目,地址: https://github.com/lukeweber/iLBC-Android
关于第三方编解码库就简单介绍到这里了,最后三个是个人项目,我没有使用过,真心感谢这些作者的无私奉献,另外,更多的第三方库欢迎大家留言或者来信补充。
关于如何在 Android 平台编解码音频数据就简单介绍到这里了,文章中有不清楚的地方欢迎留言或者来信 lujun.hust@gmail.com 交流,或者关注我的新浪微博@卢_俊或者 微信公众号 @Jhuster 获取最新的文章和资讯。