浅聊OpenSL ES音频开发
导语
开发Android上的音频应用,一般是使用Android提供的AudioRecord采集音频,使用AudioTrack播放音频,使用MediaCodec来编解码,但这些API均是Android提供的JAVA层API,无论是采集、播放还是编解码,都需要将音频数据从java层拷贝到native层,或从native层拷贝到java层,影响性能。为了开发更加高效的Android音频应用,则建议使用Android NDK提供的OpenSL ES API接口,它支持在native层直接处理音频数据。
当前腾讯视频自研播放器的音频输出逻辑,都是将解码后的音频数据都抛到Java层采用AudioTrack进行渲染输出。为了提高效率减少JNI拷贝,合理调整播放引擎与上层逻辑的架构,我们做了音频渲染模块的重构,将音频渲染模块下沉到播放引擎层,采用OpenSL在native层进行音频输出,并将现有的java层AudioTrack作为备份逻辑。
一、OpenSL ES API接口及使用流程
OpenSL ES是一种针对嵌入式系统特别优化过的硬件音频加速API,无授权费并且可以跨平台使用,它提供的高性能、标准化、低延迟的特性实现为嵌入式媒体开发提供了标准。关于OpenSL ES的介绍可以参考官方文档《OpenSL_ES_Specification_1.0.1.pdf》,这里不再赘述。在OpenSL ES中,一切API的访问和控制都是通过接口完成的,以下是OpenSL ES音频播放场景。
从上图可以看出,一般播放实现思路是:
1.创建并初始化Audio Engine(音频引擎,是和底层交互的入口)
2.打开OutputMix(音频输出),配置相关参数DataSource和DataSink,创建播放器AudioPlayer和缓冲队列BufferQueue
3.设置输出的callback回调,实现输出渲染逻辑
OpenSL使用回调机制来访问音频IO,但它的回调里并不会把音频数据作为参数传递,回调方法仅仅是告诉我们:BufferQueue已经就绪,可以接受/获取数据了。我们可以在回调函数中直接调用Enqueue方法往音频设备中放入数据,也可以仅通知程序进行渲染。
二、音频播放逻辑实现
因这里音频播放的数据来源于解码器的输出,为了不影响解码线程,这里采用异步渲染的方式。主要流程如下图所示,主要包括几大模块:初始化、数据缓存、音频数据渲染、播放消息处理四大模块。