Android录制音频,一般会使用AudioRecord。今天我们来分析一下AudioRecord的源代码,看看它是怎么完成音频录制的。本文的代码基于Android 9.0。
使用AudioRecord录制音频,主要包含以下几个步骤:
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
。AudioRecord还有其它构造函数,我们这里以这个构造函数为例。这里,audioSource指定音频源,音频源在MediaRecorder.AudioSorce中定义,可定时MIC,VOICE_COMMUNICATION等。具体可以查阅AudioSource的源码。sampleRateInHz指定采样频率,一般采用44100Hz保证在所有设备上都可以正常工作。channelConfig指定声道,这在AudioFormat中定义,可以是CHANNEL_IN_MONO(单声道),CHANNEL_IN_STEREO(立体声)等。audioFormat质地感音频格式,即采样深度,可以是ENCODING_PCM_8BIT, ENCODING_PCM_16BIT等,定义在AudioFormat中。最后一个参数是bufferSizeInBytes,即录制过程中缓冲区的大小。缓冲区的大小一般通过getMinBufferSize获取。getMinBufferSize决定录制过程中所需要的最小缓冲区,如果我们传给bufferSizeInBytes的缓冲区大小比getMinBufferSize返回的值更小,AudioRecord的初始化可能会失败。关于AudioRecord构造函数内部逻辑比较简单,这里不展开讨论。我们重点关注一下getMinBufferSize的实现,它是如何确定这个最小缓冲区大小的呢?getMinBufferSize函数的源代码如下:
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch (channelConfig) {
case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
case AudioFormat.CHANNEL_IN_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_IN_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK):
channelCount = 2;
break;
case AudioFormat.CHANNEL_INVALID:
default:
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
}
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if (size == 0) {
return ERROR_BAD_VALUE;
}
else if (size == -1) {
return ERROR;
}
else {
return size;
}
}
首先需要根据channelConfig,确定通道数。这其实很好理解,双通道因为有两个通道,需要的数据缓冲区自然要是单通道的两倍。如果channelConfig无效,将会返回错误ERROR_BAD_VALUE。在成功获取了通道数之后,通过,native_get_min_buff_size方法来获取缓冲区的大小。核心逻辑在native_get_min_buff_size中。接下来,我们分析native_get_min_buff_size的代码。
native_get_min_buff_size定义在android_media_AudioRecord.cpp中:{"native_get_min_buff_size", 874 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}
。这里可以看到,native_get_min_buff_size对应的native方法是android_media_AudioRecord_get_min_buff_size。
android_media_AudioRecord_get_min_buff_size的源代码如下:
static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
jint sampleRateInHertz, jint channelCount, jint audioFormat) {
ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
sampleRateInHertz, channelCount, audioFormat);
size_t frameCount = 0;
audio_format_t format = audioFormatToNative(audioFormat);
status_t result = AudioRecord::getMinFrameCount(&frameCount,
sampleRateInHertz,
format,
audio_channel_in_mask_from_count(channelCount));
if (result == BAD_VALUE) {
return 0;
}
if (result != NO_ERROR) {
return -1;
}
return frameCount * channelCount * audio_bytes_per_sample(format);
android_media_AudioRecord_get_min_buff_size首先把java层的audioFormat转换成c++层的audio_format_t,然后调用AudioRecord(c++)类的getMinFrameCount方法,获取最小帧数frameCount。然后将最小帧数frameCount,通道数channelCount,和根据format计算出的每一个采样的字节数相乘,得出最小缓冲区大小。这个公式`frameCount * channelCount * audio_bytes_per_sample(format)'很好理解,重点在于AudioRecord::getMinFrameCount,它的代码如下:
status_t AudioRecord::getMinFrameCount(
size_t* frameCount,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask)
{
if (frameCount == NULL) {
return BAD_VALUE;
}
size_t size;
status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);
if (status != NO_ERROR) {
ALOGE("AudioSystem could not query the input buffer size for sampleRate %u, format %#x, "
"channelMask %#x; status %d", sampleRate, format, channelMask, status);
return status;
}
// We double the size of input buffer for ping pong use of record buffer.
// Assumes audio_is_linear_pcm(format)
if ((*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) *
audio_bytes_per_sample(format))) == 0) {
ALOGE("Unsupported configuration: sampleRate %u, format %#x, channelMask %#x",
sampleRate, format, channelMask);
return BAD_VALUE;
}
return NO_ERROR;
}
这里有两处关键代码:AudioSystem::getInputBufferSize
,和(*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) * audio_bytes_per_sample(format)))
。首先我们看AudioSystem::getInputBufferSize的实现:
status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, size_t* buffSize)
{
const sp afc = getAudioFlingerClient();
if (afc == 0) {
return NO_INIT;
}
return afc->getInputBufferSize(sampleRate, format, channelMask, buffSize);
}
AudioSystem::getInputBufferSize通过AudioFlingerClient的getInputBufferSize获取输入缓冲区缓冲区大小。我们继续跟进,看一下AudioFlingerClient的getInputBufferSize方法做了什么:
size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask) const
1344{
1345 status_t ret = initCheck();
1346 if (ret != NO_ERROR) {
1347 return 0;
1348 }
1349 if ((sampleRate == 0) ||
1350 !audio_is_valid_format(format) || !audio_has_proportional_frames(format) ||
1351 !audio_is_input_channel(channelMask)) {
1352 return 0;
1353 }
1354
1355 AutoMutex lock(mHardwareLock);
1356 mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
1357 audio_config_t config, proposed;
1358 memset(&proposed, 0, sizeof(proposed));
1359 proposed.sample_rate = sampleRate;
1360 proposed.channel_mask = channelMask;
1361 proposed.format = format;
1362
1363 sp dev = mPrimaryHardwareDev->hwDevice();
1364 size_t frames;
1365 for (;;) {
1366 // Note: config is currently a const parameter for get_input_buffer_size()
1367 // but we use a copy from proposed in case config changes from the call.
1368 config = proposed;
1369 status_t result = dev->getInputBufferSize(&config, &frames);
1370 if (result == OK && frames != 0) {
1371 break; // hal success, config is the result
1372 }
1373 // change one parameter of the configuration each iteration to a more "common" value
1374 // to see if the device will support it.
1375 if (proposed.format != AUDIO_FORMAT_PCM_16_BIT) {
1376 proposed.format = AUDIO_FORMAT_PCM_16_BIT;
1377 } else if (proposed.sample_rate != 44100) { // 44.1 is claimed as must in CDD as well as
1378 proposed.sample_rate = 44100; // legacy AudioRecord.java. TODO: Query hw?
1379 } else {
1380 ALOGW("getInputBufferSize failed with minimum buffer size sampleRate %u, "
1381 "format %#x, channelMask 0x%X",
1382 sampleRate, format, channelMask);
1383 break; // retries failed, break out of loop with frames == 0.
1384 }
1385 }
1386 mHardwareStatus = AUDIO_HW_IDLE;
1387 if (frames > 0 && config.sample_rate != sampleRate) {
1388 frames = destinationFramesPossible(frames, sampleRate, config.sample_rate);
1389 }
1390 return frames; // may be converted to bytes at the Java level.
1391}
这里是将参数传递个HAL层,然后获取inputBuffer的size。硬件不同,这个值也会不同。关键的代码在for循环中。首先尝试调用dev->getInputBufferSize
,如果能够正常获取InputBufferSize,则返回该值。否则,调整参数为一般参数,通过destinationFramesPossible再次尝试获取InputBufferSize。
总结:以上就是getMinBufferSize的主要流程。可以看出,这个值是根据不同的硬件会得出不同的值,以保证在不同硬件上能够正常工作。