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

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