(一)Audio子系统之AudioRecord.getMinBufferSize
在文章《基于Allwinner的Audio子系统分析(Android-5.1)》中已经介绍了Audio的系统架构以及应用层调用的流程,接下来,继续分析AudioRecorder方法中的getMinBufferSize的实现
函数原型:
public static int getMinBufferSize (int sampleRateInHz, int channelConfig, int audioFormat)
作用:
返回成功创建AudioRecord对象所需要的最小缓冲区大小
参数:
sampleRateInHz:默认采样率,单位Hz,这里设置为44100,44100Hz是当前唯一能保证在所有设备上工作的采样率;
channelConfig: 描述音频声道设置,这里设置为AudioFormat.CHANNEL_CONFIGURATION_MONO,CHANNEL_CONFIGURATION_MONO保证能在所有设备上工作;
audioFormat:音频数据的采样精度,这里设置为AudioFormat.ENCODING_16BIT;
返回值:
返回成功创建AudioRecord对象所需要的最小缓冲区大小。 注意:这个大小并不保证在负荷下的流畅录制,应根据预期的频率来选择更高的值,AudioRecord实例在推送新数据时使用此值
如果硬件不支持录制参数,或输入了一个无效的参数,则返回ERROR_BAD_VALUE(-2),如果硬件查询到输出属性没有实现,或最小缓冲区用byte表示,则返回ERROR(-1)
接下来进入系统分析具体实现
frameworks/base/media/java/android/media/AudioRecord.java
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch (channelConfig) {
case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT //1
case AudioFormat.CHANNEL_IN_MONO: //16
case AudioFormat.CHANNEL_CONFIGURATION_MONO://2
channelCount = 1;
break;
case AudioFormat.CHANNEL_IN_STEREO: //12
case AudioFormat.CHANNEL_CONFIGURATION_STEREO://3
case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK): // 16||32
channelCount = 2;
break;
case AudioFormat.CHANNEL_INVALID://0
default:
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
} // PCM_8BIT is not supported at the moment
if (audioFormat != AudioFormat.ENCODING_PCM_16BIT) {
loge("getMinBufferSize(): Invalid audio format.");
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;
}
}
对音频通道与音频采样精度进行判断,单声道(MONO)时channelCount为1,立体声(STEREO)时channelCount为2,且A64上仅支持PCM_16BIT采样,其值为2,然后继续调用native函数
frameworks/base/core/jni/android_media_AudioRecord.cpp
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;
//从java转成jni的format类型
audio_format_t format = audioFormatToNative(audioFormat);//AUDIO_FORMAT_PCM_16_BIT=0x1 //获取frameCount,并判断硬件是否支持
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);
}
调用服务端的函数,获取frameCount大小,最后返回了frameCount*声道数*采样精度,其中frameCount表示最小采样帧数,继续分析frameCount的计算方法
frameworks/av/media/libmedia/AudioRecord.cpp
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;
}
//计算出最小的frame
// 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;
}
此时frameCount= size*2/(声道数*采样精度),注意这里需要double一下,而size是由hal层得到的,AudioSystem::getInputBufferSize()函数最终会调用到HAL层
frameworks/av/media/libmedia/AudioSystem.cpp
status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, size_t* buffSize)
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) {
return PERMISSION_DENIED;
}
Mutex::Autolock _l(gLockCache);
// Do we have a stale gInBufferSize or are we requesting the input buffer size for new values
size_t inBuffSize = gInBuffSize;
if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)
|| (channelMask != gPrevInChannelMask)) {
gLockCache.unlock();
inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);
gLockCache.lock();
if (inBuffSize == 0) {
ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",
sampleRate, format, channelMask);
return BAD_VALUE;
}
// A benign race is possible here: we could overwrite a fresher cache entry
// save the request params
gPrevInSamplingRate = sampleRate;
gPrevInFormat = format;
gPrevInChannelMask = channelMask; gInBuffSize = inBuffSize;
}
*buffSize = inBuffSize; return NO_ERROR;
}
这里通过get_audio_flinger获取到了一个AudioFlinger对象
const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
{
sp<IAudioFlinger> af;
sp<AudioFlingerClient> afc;
{
Mutex::Autolock _l(gLock);
if (gAudioFlinger == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.audio_flinger"));
if (binder != 0)
break;
ALOGW("AudioFlinger not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (gAudioFlingerClient == NULL) {
gAudioFlingerClient = new AudioFlingerClient();
} else {
if (gAudioErrorCallback) {
gAudioErrorCallback(NO_ERROR);
}
}
binder->linkToDeath(gAudioFlingerClient);
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
LOG_ALWAYS_FATAL_IF(gAudioFlinger == 0);
afc = gAudioFlingerClient;
}
af = gAudioFlinger;
}
if (afc != 0) {
af->registerClient(afc);
}
return af;
}
然后判断是否参数是之前配置过的参数,这样做是为了防止重复多次调用getMinBufferSize导致占用硬件资源,所以当第一次调用或更新参数调用后,则调用AF中的getInputBufferSize方法获取BuffSize,而af是IAudioFlinger类型的智能指针,所以实际上会通过binder到达AudioFlinger中
frameworks\av\services\audioflinger\AudioFlinger.cpp
size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask) const
{
status_t ret = initCheck();
if (ret != NO_ERROR) {
return 0;
} AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
audio_config_t config;
memset(&config, 0, sizeof(config));
config.sample_rate = sampleRate;
config.channel_mask = channelMask;
config.format = format; audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
size_t size = dev->get_input_buffer_size(dev, &config);
mHardwareStatus = AUDIO_HW_IDLE;
return size;
}
把参数传递给hal层,获取buffer大小
hardware\aw\audio\tulip\audio_hw.c
static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
const struct audio_config *config)
{
size_t size;
int channel_count = popcount(config->channel_mask);
if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0)
return 0;
return get_input_buffer_size(config->sample_rate, config->format, channel_count);
}
再次检查一次参数是否正确,为什么在很多函数里面都做一次检查参数呢?可能在其他的地方也调用到了这个函数,所以最好做一次检查,确保万无一失
static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count)
{
size_t size;
size_t device_rate; if (check_input_parameters(sample_rate, format, channel_count) != 0)
return 0; /* take resampling into account and return the closest majoring
multiple of 16 frames, as audioflinger expects audio buffers to
be a multiple of 16 frames */
size = (pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate;
size = ((size + 15) / 16) * 16; return size * channel_count * sizeof(short);
}
这里包含一个结构体struct pcm_config,定义了一个周期包含了多少采样帧,并根据结构体的rate数据进行重采样计算,这里的rate是以MM_SAMPLING_RATE为标准,即44100,一个采样周期有1024个采样帧,然后计算出重采样之后的size
同时audioflinger的音频buffer是16的整数倍,所以再次计算得出一个最接近16倍的整数,最后返回size*声道数*1帧数据所占字节数
struct pcm_config pcm_config_mm_in = {
.channels = 2,
.rate = MM_SAMPLING_RATE,
.period_size = 1024,
.period_count = CAPTURE_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
};
总结:
minBuffSize = ((((((((pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate) + 15) / 16) * 16) * channel_count * sizeof(short)) * 2) / (audio_channel_count_from_in_mask(channelMask) * audio_bytes_per_sample(format))) * channelCount * audio_bytes_per_sample(format);
=(((((((pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate) + 15) / 16) * 16) * channel_count * sizeof(short)) * 2)
其中:pcm_config_mm_in.period_size=1024;pcm_config_mm_in.rate=44100;这里我们可以看到他除掉(channelCount*format),后面又乘回来了,这个是因为在AudioRecord.cpp对frameCount进行了一次校验,判断是否支持该参数的设置。
以getMinBufferSize(44100, MONO, 16BIT);为例,即sample_rate=44100,channel_count=1, format=2,那么
BufferSize = (((1024*sample_rate/44100)+15)/16)*16*channel_count*sizeof(short)*2 = 4096
即最小缓冲区大小为:周期大小 * 重采样 * 采样声道数 * 2 * 采样精度所占字节数;这里的2的解释为We double the size of input buffer for ping pong use of record buffer,采样精度:PCM_8_BIT为unsigned char,PCM_16_BIT为short,PCM_32_BIT为int。
由于作者内功有限,若文章中存在错误或不足的地方,还请给位大佬指出,不胜感激!
(一)Audio子系统之AudioRecord.getMinBufferSize的更多相关文章
- (四)Audio子系统之AudioRecord.read
在上一篇文章<(三)Audio子系统之AudioRecord.startRecording>中已经介绍了AudioRecord如何开始录制音频,接下来,继续分析AudioRecord方 ...
- (五)Audio子系统之AudioRecord.stop
在上一篇文章<(四)Audio子系统之AudioRecord.read>中已经介绍了AudioRecord如何获取音频数据,接下来,继续分析AudioRecord方法中的stop的实现 函 ...
- (六)Audio子系统之AudioRecord.release
在上一篇文章<(五)Audio子系统之AudioRecord.stop>中已经介绍了AudioRecord如何暂停录制,接下来,继续分析AudioRecord方法中的release的实 ...
- (三)Audio子系统之AudioRecord.startRecording
在上一篇文章<(二)Audio子系统之new AudioRecord()>中已经介绍了Audio系统如何创建AudioRecord对象以及输入流,并创建了RecordThread线程,接下 ...
- (二)Audio子系统之new AudioRecord()
在上一篇文章<(一)Audio子系统之AudioRecord.getMinBufferSize>中已经介绍了AudioRecord如何获取最小缓冲区大小,接下来,继续分析AudioReco ...
- (二)Audio子系统之new AudioRecord()(Android4.4)
在上一篇文章<(一)Audio子系统之AudioRecord.getMinBufferSize>中已经介绍了AudioRecord如何获取最小缓冲区大小,接下来,继续分析AudioReco ...
- 基于Allwinner的Audio子系统分析(Android-5.1)
前言 一直想总结下Audio子系统的博客,但是各种原因(主要还是自己懒>_<),一直拖到现在才开始重新整理,期间看过H8(Android-4.4),T3(Android-4.4),A64( ...
- Android 4.4KitKat AudioRecord 流程分析
Android是架构分为三层: 底层 Linux Kernel 中间层 主要由C++实现 (Android 60%源码都是C++实现) 应用层 主要由JAVA开发的应用程序 应用程序执行 ...
- Android 音视频开发(二):使用 AudioRecord 采集音频数据并保存到文件
版权声明:转载请说明出处:http://www.cnblogs.com/renhui/p/7457321.html 一.AudioRecord API详解 AudioRecord是Android系统提 ...
随机推荐
- maven scope简单说明
compile:默认的scope.任何定义在compile scope下的依赖将会在所有的class paths下可用.maven工程会将其打包到最终的arifact中.如果你构建一个WAR类型的ar ...
- cannot be cast to
java.lang.ClassCastException: com.service.impl.OrderPlanServiceImpl cannot be cast to com.provider.s ...
- UIPickerView 修改必须滚动才修改值的bug
//相应的选择转动 - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSI ...
- CodeForces - 468A ——(思维题)
Little X used to play a card game called "24 Game", but recently he has found it too easy. ...
- 咏南CS开发框架新的界面风格
咏南CS开发框架新的界面风格 这种风格完全和WINDOWS桌面一样,符合用户的操作习惯. 我的桌面的图标和WINDOWS桌面一样分为二类:1)快捷方式 2)文件夹. 如果快捷方式较多,看得人眼花缭乱 ...
- MSP430 G2553 低功耗模式LPMx
MSP430除了正常运行时的active模式外,还支持五种低功耗模式(Low-power mode),分别为LPM0.LPM1.LPM2.LPM3.LPM4,由状态寄存器中的CPUOFF.OSCOFF ...
- .NET框架源码解读之启动CLR
前面提到在SSCLI环境里运行.NET程序的时候,执行的命令类似java程序的执行过程,即通过clix程序解释执行.net程序.这个过程看起来跟在windows环境下执行.net程序表面上看起来不一样 ...
- 记在WEBAPI中AutoMapper的初使用方法
很早就听说AutoMapper了.这些天一直在写api接口,发现之前的类型转换有点问题,就想到了用AutoMapper.用作DTO转换工具. 废话不多说,直接开些代码 首先 在Vs中找到 工具--- ...
- 自己从0开始学习Unity的笔记 VIII (C#中类继承练习 II)
自己写了一个关于兵种的,因为一直在测试,到底面向对象是个什么玩意...然后就做了这个 namespace 兵种 { class Role //作为父类,构建一个普通角色属性用于继承 { protect ...
- WPF 使用QRCoder生成二维码
vs中使用Nuget获取QRCoder 窗体中添加按钮和Iage <Window x:Class="QRCoderTest.MainWindow" xmlns="h ...