前言
Android的音频模块相对来说是Android系统中比较简单的一个模块,但是仅仅是相对来说,Android系统中任何一个模块都非常的复杂,但是如果想学习framework相关的知识,我觉得音频模块是一个很好的切入点。Android系统中的音频模块几乎涵盖了Android系统中的所有层次,下图是它的框图:
简单的介绍下这张图,Framework层和JNI层的工作相对来说比较简单,主要是对数据的一个验证、校验、封装传输的作用,核心的地方都是在Native层处理,包括对音频数据流的处理,音频策略的选择,HAL层主要是根据Native层选择的策略正确的将音频流写入声卡中,还有支持对上层的数据访问。而TinyAlsa是Linux一个轻量级的音频开源库,这里我们就不需要过多的了解,因为还要了解很多音频的相关专业知识,我们只需要知道音频pcm流是如何传输的,音频策略是如何选择的,整个音频框架是什么样的?这样就达到我们对音频模块的学习。
源码分析
由于篇幅问题,本篇先来学习下音频模块Java层的初始化流程,首先我们来看下AudioTrack的API使用,相信应用层的同学一般使用MediaPlayer比较多,而AudioTrack是更为底层的音频API接口,只能播放pcm流,没有编解码的功能。
int minBuffSize = AudioTrack.getMinBufferSize(8000, //采样率
AudioFormat.CHANNEL_OUT_STEREO, //声道数: 双声道
AudioFormat.ENCODING_PCM_16BIT); //采样精度: 16bit
AudioTrack player = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ALARM)
.setContentType(CONTENT_TYPE_MUSIC)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(441000)
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
.build())
.setBufferSizeInBytes(minBuffSize)
.build();
player.play();
player.write(bytes, 0 , bytes.length);
player.stop();
player.release();
首先需要通过查询硬件机器支持的最小buffer是多少,这个buffer和音频流传输有关,后文在分析。首先看看getMinBufferSize()方法的JNI调用:
// ----------------------------------------------------------------------------
// returns the minimum required size for the successful creation of a streaming AudioTrack
// returns -1 if there was an error querying the hardware.
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
jint sampleRateInHertz, jint channelCount, jint audioFormat) {
size_t frameCount;
const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
sampleRateInHertz);
if (status != NO_ERROR) {
ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
sampleRateInHertz, status);
return -1;
}
const audio_format_t format = audioFormatToNative(audioFormat);
if (audio_is_linear_pcm(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
return frameCount * channelCount * bytesPerSample;
} else {
return frameCount;
}
}
从代码中我们可以看出决定最小buffer大小的和传入的参数和机器的参数有着密不可分的关系,详细的参数含义这里就不介绍了,最后调用了audioFormatToNative(),最后hal层会根据audioFormat查询硬件参数,返回相应的结果。再来看看对AudioTrack对象的构造:
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId)
throws IllegalArgumentException {
// mState already == STATE_UNINITIALIZED
//参数校验...
int rate = 0;
if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
{
rate = format.getSampleRate();
} else {
rate = AudioSystem.getPrimaryOutputSamplingRate();
if (rate <= 0) {
rate = 44100;
}
}
int channelIndexMask = 0;
if ((format.getPropertySetMask()
& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0) {
channelIndexMask = format.getChannelIndexMask();
}
int channelMask = 0;
if ((format.getPropertySetMask()
& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0) {
channelMask = format.getChannelMask();
} else if (channelIndexMask == 0) { // if no masks at all, use stereo
channelMask = AudioFormat.CHANNEL_OUT_FRONT_LEFT
| AudioFormat.CHANNEL_OUT_FRONT_RIGHT;
}
int encoding = AudioFormat.ENCODING_DEFAULT;
if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0) {
encoding = format.getEncoding();
}
audioParamCheck(rate, channelMask, channelIndexMask, encoding, mode);
mStreamType = AudioSystem.STREAM_DEFAULT;
audioBuffSizeCheck(bufferSizeInBytes);
mAttributes = new AudioAttributes.Builder(attributes).build();
//...
// 调用jni方法
int initResult = native_setup(new WeakReference<Audi