Android -- Audio系统之AudioTrack内部实现简析(二) 下载本文

Android -- Audio系统之AudioTrack内

部实现简析(二)

AudioTrack可以工作在两种模式下:

STREAM模式:应用程序通过调用write()方法将连续的音频数据传输给AudioTrack对象。它的处理是阻塞的,只有当音频数据从Java层传入到Native层,并且加入到回放队列后,它才会返回。STREAM模式经常用于处理量较大的音频数据。

STATIC模式:当处理的音频数据量较小,能一次性填充到内存用以播放;且对播放时延要求较高时,会使用这种模式。

可能是MediaPlayer功能太完备,我们使用AudioTrack的机会不多。但借助它来分析Audio系统的上下层调用关系和实现流程,还是很有效的。希望在分析完AudioTrack的内部实现之后,我们都能对Android Audio模块有一些基本的了解和认识。

接下来,我们就借助一个STREAM模式下的代码Demo,一层层分析它的内部实现流程;了解AudioTrack是如何与AudioFlinger/AudioPolicyServcie这些Native服务交互的。

示例代码:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 public void initAudioTrack() {

//1、 int bufsize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT);

//2、指定音频流类型、采样率、声道、格式、工作模式等信息

AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufsize, AudioTrack.MODE_STREAM);

//3、

track.play();

byte pkg[] = new byte[1024];//无效数据,只是用于分析;多数时候会在嵌套在某个循环之中

//4、

track.write(pkg, 0, pkg.length);

//5、

track.stop();

//6、

track.release();

}

示例代码罗列了使用AudioTrack时所需的一些重要步骤。我们通过分解这些步骤,区分代码调用层次,一层层的从Java调用分析到JNI调用、Native调用等;最终达到我们的分析目的。

一、获取需要的最小Buffer大小

音频数据是具有很多属性的,比如采样率、音频格式以及声道数等等;而这些属性又跟我们的Audio Interface有关。我们配置不同的音频属性,底层播放这段数据所需的最小Buffer空间大小也会变化。所以,我们需要根据配置的音频信息,先得到它对应的最小Buffer空间;再继续后面的操作。

AudioTrack::getMinBufferSize()函数的定义如下:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 /**

* Returns the estimated minimum buffer size required for an AudioTrack * object to be created in the {@link #MODE_STREAM} mode.

* The size is an estimate because it does not consider either the route or the sink, * since neither is known yet. Note that this size doesn't

* guarantee a smooth playback under load, and higher values should be chosen according to * the expected frequency at which the buffer will be refilled with additional data to play. * For example, if you intend to dynamically set the source sample rate of an AudioTrack * to a higher value than the initial source sample rate, be sure to configure the buffer size * based on the highest planned sample rate.

* @param sampleRateInHz the source sample rate expressed in Hz.

* {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted. * @param channelConfig describes the configuration of the audio channels. * See {@link AudioFormat#CHANNEL_OUT_MONO} and * {@link AudioFormat#CHANNEL_OUT_STEREO}

* @param audioFormat the format in which the audio data is represented. * See {@link AudioFormat#ENCODING_PCM_16BIT} and * {@link AudioFormat#ENCODING_PCM_8BIT},

* and {@link AudioFormat#ENCODING_PCM_FLOAT}.

* @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed, * or {@link #ERROR} if unable to query for output properties, * or the minimum buffer size expressed in bytes. */

static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {

int channelCount = 0;

switch(channelConfig) {//根据配置判断声道数

case AudioFormat.CHANNEL_OUT_MONO:

case AudioFormat.CHANNEL_CONFIGURATION_MONO: channelCount = 1; break;

case AudioFormat.CHANNEL_OUT_STEREO:

case AudioFormat.CHANNEL_CONFIGURATION_STEREO: channelCount = 2; break; default:

if (!isMultichannelConfigSupported(channelConfig)) {

loge(\ return ERROR_BAD_VALUE; } else {

channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig); } }

if (!AudioFormat.isPublicEncoding(audioFormat)) {//判断音频格式 loge(\ return ERROR_BAD_VALUE; }

// sample rate, note these values are subject to change

// Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||

(sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {//判断音频采样率是否合法,是否在人贰可识别的频率范围内

loge(\rate.\

return ERROR_BAD_VALUE; }

int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);//因为采样率、声道数等信息都跟硬件的支持有关,需要通过JNI查询硬件信息 if (size <= 0) {

loge(\ return ERROR; } else {

return size; } }

根据代码中的注释,首先会进行传参的有效性检测;最后通过JNI调用将函数处理带入Native