该小节我们修改android系统源代码,让系统支持APP。先来回顾一下上一节的内容:
在源生的代码中,我们找到了其不能同时录制声音的原因:对于同一个声卡的录音通道(input),他对应一个或者对个RecordThread线程。如果我们能实现如下框架:
同一个声卡的input通道,对应一个RecordThread线程,然后RecordThread中包括多个Track,每个Track都对应一个应用程序。在RecordThread其本身是支持多个Track的。
我们先来看看源生代码是怎么做的:
APP:创建一个AudioRecord,通过其中set函数。最终导致1.RecordThread被创建。2.在RecordThread中创建一个Track。之后RecordThread会从硬件读取数据,然后写入到Track之中,通过共享内存,应用程序就能得到数据了。
现在我们要进行改进:1.先判断该输入通道是否已经有了RecordThread,如果有了则返回,否则进行创建。2.其上的输入通道就是APP要使用的通道,APP指定inputSource,然后AudiopolicyManager确定device(type,再找到吻合的profile,即input通道。流程如下:
那么怎么去判断input通道是否已经有了RecordThread线程呢?如下:
在上小节中提到,每个inout通道,都有对应的一个AudioInputDescriptor描述符,AudioInputDescriptor有一个或者多个成员为const sp mProfile,每个mProfile对应一个RecordThread。
我们打开AudioPolicyManager.cpp,其最终会调用到:
status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,audio_io_handle_t *input,audio_session_t session,uid_t uid,uint32_t samplingRate,audio_format_t format,audio_channel_mask_t channelMask,audio_input_flags_t flags, audio_port_handle_t selectedDeviceId,input_type_t *inputType)
/*找到input*/
*input = getInputForDevice(device, address, session, uid, inputSource,samplingRate, format, channelMask, flags,policyMix);
/*从所有的profile中,找到支持的profile,即应用程序需要的输入通道*/
profile = getInputProfile(device, address,profileSamplingRate, profileFormat, profileChannelMask,profileFlags);
找到通道之后,添加我们需要修改的代码如下
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = profileSamplingRate;
config.channel_mask = profileChannelMask;
config.format = profileFormat;
+ /* check wether have an AudioInputDescriptor use the same profile */
+ for (size_t input_index = 0; input_index < mInputs.size(); input_index++) {
+ sp<AudioInputDescriptor> desc;
+ desc = mInputs.valueAt(input_index);
+ if (desc->mProfile == profile) {
+ desc->mOpenRefCount++; // 引用计数加1
+ desc->mSessions.add(session); // session
+ return desc->mIoHandle;
+ }
+ }
status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
下面对我们添加的代码进行讲解:
/* check wether have an AudioInputDescriptor use the same profile */
for (size_t input_index = 0; input_index < mInputs.size(); input_index++) {
sp<AudioInputDescriptor> desc;
/*通过循环,取出每个AudioInputDescriptor*/
desc = mInputs.valueAt(input_index);
/*判断一下是不是我们想要的profile,如果是了,则代表去已经创建RecordThread线程改了*/
if (desc->mProfile == profile) {
desc->mOpenRefCount++; // 引用计数加1
desc->mSessions.add(session); // session
/*返回他的索引值*/
return desc->mIoHandle;
}
}
其上的session是什么?之前我们说应用程序录音时,会创建一个AudioRecord,AudioRecord会与RecordThread中的一个track对应,他们之间会存在一个联系,这个联系就是session。一个应用程序是可以创建多个AudioRecord的:
我们知道每个AudioRecord都会对应到RecordThread的track,这样一个APP则会对应一个RecordThread的多个track。session为一个不重复的整数。通过desc->mSessions.add(session),session会被添加到描述符之中。以后就能通过sp desc获得session,再根据session,可以找到应用程序的AudioRecord,也能找到RecordThread的track。
添加代码之后,重新编译,然后按照上小节博客,同时运行两个c++编写的录音程序即可。