Android音频开发(5):音频数据的编解码

Android音频开发(5):音频数据的编解码

前面四篇文章分别介绍了音频开发必备的基础知识、如何采集一帧音频、如何播放一帧音频、如何存储和解析wav格式的文件,建议有兴趣的小伙伴们先读一读,本文则重点关注如何对一帧音频数据进行编码和解码。


1. Android 官方的 MediaCodec API


首先,我们了解一下 Android 官方提供的音频编解码的 API,即 MediaCodec 类,该 API 是在 Andorid 4.1 (API 16) 版本引入的,因此只能工作于 Android 4.1 以上的手机上。


1.1 MediaCodec 基本介绍


(1)提供了一套访问 Android 底层多媒体模块的接口,主要是音视频的编解码接口


(2)Android 底层多媒体模块采用的是 OpenMax 框架,任何 Android 底层编解码模块的实现,都必须遵循 OpenMax 标准。Google 官方默认提供了一系列的软件编×××:包括:OMX.google.h364.encoder,OMX.google.h364.encoder, OMX.google.aac.encoder, OMX.google.aac.decoder 等等,而硬件编解码功能,则需要由芯片厂商依照 OpenMax 框架标准来完成,所以,一般采用不同芯片型号的手机,硬件编解码的实现和性能是不同的


(3)Android 应用层统一由 MediaCodec API 来提供各种音视频编解码功能,由参数配置来决定采用何种编解码算法、是否采用硬件编解码加速等等


1.2 MediaCodec 核心原理


我不准备详细介绍 MediaCodec API 的每个函数是怎么用,示例代码大家可以在后面给出的资源链接中查看和学习。


这里我准备重点介绍一下 MediaCodec 的核心工作原理,因为只有搞清楚了这一点,你才会明白为什么 MediaCodec API 提供的接口是这个样子的。


MediaCodec 使用的基本流程是:


- createEncoderByType/createDecoderByType
- configure
- start
- while(1) {
    - dequeueInputBuffer
    - queueInputBuffer
    - dequeueOutputBuffer
    - releaseOutputBuffer
}
- stop
- release


由此可以看到,Buffer 队列的操作是其最核心的部分之一,关于 MediaCodec 的 Buffer 队列 ,示意图如下:

Android音频开发(5):音频数据的编解码



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”:


Android音频开发(5):音频数据的编解码

“生产者”和“消费者”其实是共用这一个缓冲区队列,“生产者”负责从队列中取出未使用的 Buffer,填入数据,然后放回队列,“消费者”则负责取出填入数据后的 Buffer,进行处理,处理结束后,再把 Buffer 标记为“空”,退回到队列中去以供“生产者”继续填充数据。


在 input 端,“Client”是这个环形缓冲区“生产者”,“MediaoCodec 模块”是“消费者”。

在 output 端,“MediaoCodec 模块”是这个环形缓冲区“生产者”,而“Client”则变成了“消费者”。


这就是其核心的工作原理,其实并不复杂,大家静下心来,很快就能理解其中的奥妙。


1.3  参考资源


关于 MediaCodec 的示例代码,网上其实也很多了,我就直接给出一些个人觉得不错的链接,有兴趣的小伙伴们可以去研究一下。


(1)Android 官方文档: 《MediaCodec》

(2)《Android MediaCodec stuff》

(3)《HWEncoderExperiments》

(4)一些开源的播放器 Android 源码,如 VLC、ijkplayer


2. 第三方音频编解码的库


官方的 MediaCodec API 虽然支持硬件编解码加速,但是问题和局限还是很多的,一方面是只能在 Android 4.1 以上机型上才能使用,另一方面,由于 Android 手机种类繁多,厂商对底层源码的修改各不相同,导致 MediaCodec API 在实际使用中,会遇到很多坑,有很多兼容性的问题,因此,我们也可以考虑采用第三方的编解码库。


这里,我简单推荐几款第三方音频编解码库(可以移植到 Android 平台的),大家可以直接去官网或者项目主页了解其详细信息。


(1) opus 编解码库


很喜欢 opus,低码率下 opus 完胜曾经优势明显的 HE AAC,我曾经用它实现了一款 Android 局域网的VoIP网络电话应用:“飞鸽电话”,效果很不错。


opus 官网地址:https://www.opus-codec.org


注:如今 Android 5.0 已经官方支持 opus 格式了,关于 Android 支持的多媒体格式列表可以查看 Android developer guide:《Supported Media Formats》


(2) Speex 编解码库


老牌的音频处理库,不仅是编解码,还提供了包括音频去噪、回声消除、静音检测等功能,官网地址:http://www.speex.org


(3) ffmpeg


大名鼎鼎的 ffmpeg 肯定不能错过,官网:https://www.ffmpeg.org


(4)Android AAC Encoder


一款轻量级的 Android aac 编码库:https://github.com/timsu/android-aac-enc


(5)opencore-amr-android


从 opencore 抽取出来的 amr 编解码库,地址:https://github.com/kevinho/opencore-amr-android


(6)iLBC-Android


iLBC 是著名的 WebRTC 项目的音频编解码模块,iLBC-Android 是从中抽取 iLBC 模块移植到 Android 平台的个人项目,地址:https://github.com/lukeweber/iLBC-Android


关于第三方编解码库就简单介绍到这里了,最后三个是个人项目,我没有使用过,真心感谢这些作者的无私奉献,另外,更多的第三方库欢迎大家留言或者来信补充。


3. 小结


关于如何在 Android 平台编解码音频数据就简单介绍到这里了,文章中有不清楚的地方欢迎留言或者来信 lujun.hust@gmail.com 交流,或者关注我的新浪微博 @卢_俊 或者 微信公众号 @Jhuster 获取最新的文章和资讯。


Android音频开发(5):音频数据的编解码