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.h264.encoder,OMX.google.h264.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 队列 ,示意图如下:

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”则变成了“消费者”。

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

1.3  参考资源

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

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

(2)《Android MediaCodec stuff》

(3)《HWEncoderExperiments》

(4)一些开源的播放器 Android 源码,如 VLCijkplayer

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 平台编解码音频数据就简单介绍到这里了,文章中有不清楚的地方欢迎留言或者来信 [email protected] 交流,或者关注我的新浪微博@卢_俊或者 微信公众号 @Jhuster 获取最新的文章和资讯。

时间: 2024-10-12 08:50:33

Android音频开发(5):音频数据的编解码的相关文章

Android底层开发之音频输入通道的软硬件分析

Android底层开发之音频输入通道的软硬件分析 我们都知道耳机Mic集成在一直的那种四段耳机Mic插头是Android设备上比较常用.但是也会有分开的情况,比较如果在普通的PC机中装Android系统,那么就是这种情况.所以就有必要对音频输入通道的软件硬件进行统一的分析一下,接下来分析一个实例. 该设备的硬件连接为:基于3157的模拟开关实现的 通道切换. 设备是完全靠硬件实现的,那么就没有软件的什么工作了.但是这并不是一个理想的实现方法,真下的实现方法应该是所有的Mic都是并行的,每个Mic

libavcodec是一款LGPL自由软件编解码库,用于视频和音频数据的编解码工作

http://zh.wikipedia.org/zh-cn/Libavcodec http://baike.baidu.com/view/856526.htm libavcodec是一款LGPL自由软件编解码库,用于视频和音频数据的编解码工作.带有这个名字的库有FFmpeg项目和Libav项目,但是它们却彼此不兼容. libavcodec是个集成了许多开源多媒体应用和框架.常见的MPlayer.xine和VLC媒体播放器都使用它作为它们的主要内置解码引擎,用于许多音频视频格式在所有支持的平台上的

【Mark】Android应用开发SharedPreferences存储数据的使用方法

Android应用开发SharedPreferences存储数据的使用方法 SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-value(键值对)SharedPreferences常用来存储一些轻量级的数据. 1.使用SharedPreferences保存数据方法如下: //实例化SharedPreferences对象(第一步) SharedPreferences mySharedPreferences=

iOS开发网络篇之Base64编解码

郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 如果文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额随意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源码下载:点我传送 游戏官方下载:http://dwz.cn/RwTjl 游戏视频预览:http://dwz.cn/RzHHd 游戏开发博客:http://dwz.cn/RzJzI 游戏源码传送:http://dwz.cn/Nret1 在iPhone开发中很多时候都需要将数据进行Base64编解码. 在一些大项目中,就需要将通过R

IOS开发各种加解密,编解码

1.AES加解密,给NSData添加类别 NSData+AES 添加头文件#import <CommonCrypto/CommonCryptor.h> - (NSData *)AES256EncryptWithKey:(NSString *)key {//加密 char keyPtr[kCCKeySizeAES256+1]; bzero(keyPtr, sizeof(keyPtr)); [key getCString:keyPtr maxLength:sizeof(keyPtr) encodi

Android应用开发基础之数据存储和界面展现(一)

Android项目的目录结构 Activity:应用被打开时显示的界面 src:项目代码 R.java:项目中所有资源文件的资源id Android.jar:Android的jar包,导入此包方可使用Android的api libs:导入第三方jar包 assets:存放资源文件,比方说mp3.视频文件 bin:存放编译打包后的文件 res:存放资源文件,存放在此文件夹下的所有资源文件都会生成资源id drawable:存放图片资源 layout:存放布局文件,把布局文件通过资源id指定给act

Android应用开发基础之数据存储和界面展现(三)

生成XML文件备份短信 创建几个虚拟的短信对象,存在list中 备份数据通常都是备份至sd卡 使用StringBuffer拼接字符串 把整个xml文件所有节点append到sb对象里 sb.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"); //添加smss的开始节点 sb.append("<smss>"); ....... 把sb写到输出流中 fos.wri

Android应用开发基础之数据存储和界面展现(二)

常见布局 相对布局 RelativeLayout 组件默认左对齐.顶部对齐 设置组件在指定组件的右边 android:layout_toRightOf="@id/tv1" 设置在指定组件的下边 android:layout_below="@id/tv1" 设置右对齐父元素 android:layout_alignParentRight="true" 设置与指定组件右对齐 android:layout_alignRight="@id/tv

Android应用开发SharedPreferences存储数据的使用方法

SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-value(键值对)SharedPreferences常用来存储一些轻量级的数据 SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-value(键值对).SharedPreferences常用来存储一些轻量级的数据. //实例化SharedPreferences对象(第