AudioRecord一个设备只创建一个线程
Posted 迅哥儿00001
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AudioRecord一个设备只创建一个线程相关的知识,希望对你有一定的参考价值。
不要太相信方法名,尤其是那种看起来很简单的方法名。
在很多博客下面都已经讲过AudioRecord的创建流程,从android_media_AudioRecord.cpp--->AudioRecord--->AudidPolicyService--->AudioPolicyManager--->AudioFlinger
其中过程就不细讲,其中最关键的方法就是getInputForAttr()
前面的调用不提,最终会调用到AudioPolicyManager.cpp的getInputForAttr,这个方法先根据attr的source得到对应的device,然后再用这个device去获取Input,也就是AudioPolicyManager.cpp的getInputForDevice方法:
audio_io_handle_t AudioPolicyManager::getInputForDevice(const sp<DeviceDescriptor> &device,
audio_session_t session,
const audio_attributes_t &attributes,
const audio_config_base_t *config,
audio_input_flags_t flags,
const sp<AudioPolicyMix> &policyMix)
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
audio_source_t halInputSource = attributes.source;
bool isSoundTrigger = false;
// 特殊源处理(语音识别和电话等)
if (attributes.source == AUDIO_SOURCE_HOTWORD)
ssize_t index = mSoundTriggerSessions.indexOfKey(session);
if (index >= 0)
input = mSoundTriggerSessions.valueFor(session);
isSoundTrigger = true;
flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_HW_HOTWORD);
ALOGV("SoundTrigger capture on session %d input %d", session, input);
else
halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION;
else if (attributes.source == AUDIO_SOURCE_VOICE_COMMUNICATION &&
audio_is_linear_pcm(config->format))
flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_VOIP_TX);
// find a compatible input profile (not necessarily identical in parameters)
sp<IOProfile> profile;
// sampling rate and flags may be updated by getInputProfile
uint32_t profileSamplingRate = (config->sample_rate == 0) ?
SAMPLE_RATE_HZ_DEFAULT : config->sample_rate;
audio_format_t profileFormat;
audio_channel_mask_t profileChannelMask = config->channel_mask;
audio_input_flags_t profileFlags = flags;
for (;;)
profileFormat = config->format; // reset each time through loop, in case it is updated
// 通过采样率,采样格式等参数去和配置文件夹在的那个匹配来获取Profile
profile = getInputProfile(device, profileSamplingRate, profileFormat, profileChannelMask,
profileFlags);
if (profile != 0)
break; // success
else if (profileFlags & AUDIO_INPUT_FLAG_RAW)
profileFlags = (audio_input_flags_t) (profileFlags & ~AUDIO_INPUT_FLAG_RAW); // retry
else if (profileFlags != AUDIO_INPUT_FLAG_NONE)
profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
else // fail
ALOGW("%s could not find profile for device %s, sampling rate %u, format %#x, "
"channel mask 0x%X, flags %#x", __func__, device->toString().c_str(),
config->sample_rate, config->format, config->channel_mask, flags);
return input;
// Pick input sampling rate if not specified by client
uint32_t samplingRate = config->sample_rate;
if (samplingRate == 0)
samplingRate = profileSamplingRate;
if (profile->getModuleHandle() == 0)
ALOGE("getInputForAttr(): HW module %s not opened", profile->getModuleName());
return input;
// canOpenNewIo:能打开新的IO,加个!,就是不能打开新的IO
if (!profile->canOpenNewIo())
for (size_t i = 0; i < mInputs.size(); )
sp <AudioInputDescriptor> desc = mInputs.valueAt(i);
if (desc->mProfile != profile)
i++;
continue;
// if sound trigger, reuse input if used by other sound trigger on same session
// else
// reuse input if active client app is not in IDLE state
//
RecordClientVector clients = desc->clientsList();
bool doClose = false;
for (const auto& client : clients)
if (isSoundTrigger != client->isSoundTrigger())
continue;
// 语音识别直接复用
if (client->isSoundTrigger())
if (session == client->session())
return desc->mIoHandle;
continue;
// 如果客户端未处于空闲状态,直接重用这个输入
if (client->active() && client->appState() != APP_STATE_IDLE)
return desc->mIoHandle;
doClose = true;
if (doClose)
closeInput(desc->mIoHandle);
else
i++;
// 创建新的输入对象
sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile, mpClientInterface);
audio_config_t lConfig = AUDIO_CONFIG_INITIALIZER;
lConfig.sample_rate = profileSamplingRate;
lConfig.channel_mask = profileChannelMask;
lConfig.format = profileFormat;
// 内部会调用audioFlinger来创建新的RecordThread
status_t status = inputDesc->open(&lConfig, device, halInputSource, profileFlags, &input);
// only accept input with the exact requested set of parameters
if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE ||
(profileSamplingRate != lConfig.sample_rate) ||
!audio_formats_match(profileFormat, lConfig.format) ||
(profileChannelMask != lConfig.channel_mask))
ALOGW("getInputForAttr() failed opening input: sampling rate %d"
", format %#x, channel mask %#x",
profileSamplingRate, profileFormat, profileChannelMask);
if (input != AUDIO_IO_HANDLE_NONE)
inputDesc->close();
return AUDIO_IO_HANDLE_NONE;
inputDesc->mPolicyMix = policyMix;
addInput(input, inputDesc);
mpClientInterface->onAudioPortListUpdate();
return input;
===============IOProfile.h======================
bool canOpenNewIo()
if (maxOpenCount == 0 || curOpenCount < maxOpenCount)
return true;
return false;
IOProfile(const String8 &name, audio_port_role_t role)
: AudioPort(name, AUDIO_PORT_TYPE_MIX, role),
maxOpenCount(1),
curOpenCount(0),
maxActiveCount(1),
curActiveCount(0)
之前我看到canOpenNewIo以为是能否打开新的输入流,我一个设备可以打开多个输入流,所以到这一步肯定返回true,加上!就是false,我就直接略过了,琢磨了好几天还是没想通他怎么实现一个设备只有一个RecordThread的,就再次去看了下,emmmm,就是这个判断:
构造方法那写死了,最大可打开数是1,着只要打开过设备,那肯定会返回false,那第二个用这个设备的录音就肯定会进入这个判断,然后返回AudioInputDescriptor的IOhandle,然后创建一个 RecordClientDescriptor加入到AudioInputDescriptor进行管理。
再讲一下这个返回的IOhandle,它是getInputForAttr 的第二个参数,回到调用的最开始(就是AudioFlinger里面createRecord调用的那个output.inputId
用checkRecordThread_l(output.inputId)的时候就可以拿到AudioInputDescriptor里面创建的那个RecordThread
继续讲讲没啥用的后续过程:,通过checkRecordThread_l拿到RecordThread,然后创建一个RecordTrack,然后把这个track放到recordHandle里保存着,后面start的时候就可以直接对track操作,然后thread会处理这个改变后的track
以上是关于AudioRecord一个设备只创建一个线程的主要内容,如果未能解决你的问题,请参考以下文章
Audio子系统之AudioRecord.startRecording
Android 音视频开发:使用AudioRecord采集音频PCM并保存