android 6.0 AudioRecord 录音流程分析(一
先看一段android 应用调用java代码的例子
public class MicRecord extends Thread{
AudioRecord audioRecord;
AudioTrack audioTrack;
volatile boolean canPlay = true;//个人推荐用带原子操作的对象AtomicBoolean代替
@Override
public void run() {
final int recordbuffsize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_DEFAULT,
AudioFormat.ENCODING_PCM_16BIT);
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100,
AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, recordbuffsize);
final int playBuffsize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, playBuffsize, AudioTrack.MODE_STREAM);
audioRecord.startRecording();
audioTrack.play();
byte[] recordData = new byte[recordbuffsize];
while(canPlay){
int readSize = audioRecord.read(recordData, 0, recordbuffsize);
audioTrack.write(recordData, 0, readSize);
}
audioRecord.stop();
audioRecord.release();
audioTrack.stop();
audioTrack.release();
}
public void stopRecord(){
canPlay = false;
}
}
下面是个人空闲之余整理的简述AudioRecord 的初始化流程和跨进程和mediaserver 的audioFlinger,audioPolicy爱恨情仇。和startRecording开始录音的流程。如果不是底层系统工程师做HAL对接输入设备驱动的话,流程是大概懂了但是实际功能上是用不上改动这部分代码的。HAL的用法法则也稍懂些但可惜已经不在linux os岗位,故无需详细了解。熟悉底层原理对做出好应用是也是很有必要的。
AudioRecord流程图
AudioRecord初始化
说到AudioRecord.java 这个类就不得不提和它相应的注册native方法,在/base/core/jni/android_media_AudioRecord.cpp 文件中,具体这些native方法是什么时候load的呢?
int register_android_media_AudioRecord(JNIEnv *env)
这个要看由应用启动时zygote创建的AndroidRuntime由
int AndroidRuntime::startReg(JNIEnv * env) 接口注册了。jni注册数组固定在AndroidRuntime.cpp 里面的
static const RegJNIRec gRegJNI[] = {
....
REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioTrack),
....
};
所以在java类调用涉及到对象native 方法的都不需要重新加载so等。
AudioRecord对象的创建需要参数 音频audioSource,采集频率sampleRateInHz,通道模式channelConfig,音频格式audioFormat,采集数据保存区大小bufferSizeInBytes。
AudioRecord构造时对参数进行检查和进行native_setup
int initResult = native_setup( new WeakReference<AudioRecord>(this),
mAudioAttributes, mSampleRate, mChannelMask, mChannelIndexMask,
mAudioFormat, mNativeBufferSizeInBytes,
session, ActivityThread.currentOpPackageName());
跳到android_media_AudioRecord.cpp去
static jint
android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jaa, jint sampleRateInHertz, jint channelMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName)
{
......
// create an uninitialized AudioRecord object
sp<AudioRecord> lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
......
const status_t status = lpRecorder->set(paa->source,
sampleRateInHertz,
format, // word length, PCM
channelMask,
frameCount,
recorderCallback,// callback_t
lpCallbackData,// void* user
0, // notificationFrames,
true, // threadCanCallJava
sessionId,
AudioRecord::TRANSFER_DEFAULT,
flags,
-1, -1, // default uid, pid
paa);
......
// save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
// of the Java object
setAudioRecord(env, thiz, lpRecorder);
// save our newly created callback information in the "nativeCallbackCookie" field
// of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
return (jint) AUDIO_JAVA_SUCCESS;
}
static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
{
Mutex::Autolock l(sLock);
sp<AudioRecord> old =
(AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
if (ar.get()) {
ar->incStrong((void*)setAudioRecord);
}
if (old != 0) {
old->decStrong((void*)setAudioRecord);
}
env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
return old;
}
AudioRecord的native_setup初始化只截留重要部分分析,sp<AudioRecord> lpRecorder 是jni层的/av/media/libmedia/AudioRecord.cpp AudioRecord对象,创建后用一波反手骚操作,把这个对象的引用指针赋值给java AudioRecord的(long)mNativeRecorderInJavaObj 来保存,后面如果要用到lpRecorder则会直接获取该long型引用数据强转回jni的AudioRecord引用, jni使用的常用招式。
static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
AudioRecord* const ar =
(AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
return sp<AudioRecord>(ar);
}
lpRecorder->set(xx[])初始化则是整个录音流程重点,它会通过audioFlinger来获取audio_io_handle_t 输入输出handle,打开HAL的设备获取audio_stream_in_t 输入流,并且打开创建录音线程RecordThread等等。下面描述
status_t AudioRecord::set(
audio_source_t inputSource,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
callback_t cbf,
void* user,
uint32_t notificationFrames,
bool threadCanCallJava,
int sessionId,
transfer_type transferType,
audio_input_flags_t flags,
int uid,
pid_t pid,
const audio_attributes_t* pAttributes)
{
......
if (!audio_is_input_channel(channelMask)) {
ALOGE("Invalid channel mask %#x", channelMask);
return BAD_VALUE;
}
mChannelMask = channelMask;
uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
mChannelCount = channelCount;
if (audio_is_linear_pcm(format)) {
mFrameSize = channelCount * audio_bytes_per_sample(format);
} else {
mFrameSize = sizeof(uint8_t);
}
// mFrameCount is initialized in openRecord_l
mReqFrameCount = frameCount;
mNotificationFramesReq = notificationFrames;
// mNotificationFramesAct is initialized in openRecord_l
/**
和audioflinger绑定的会话id
**/
if (sessionId == AUDIO_SESSION_ALLOCATE) {
mSessionId = AudioSystem::newAudioUniqueId();
} else {
mSessionId = sessionId;
}
ALOGV("set(): mSessionId %d", mSessionId);
int callingpid = IPCThreadState::self()->getCallingPid();
int mypid = getpid();
if (uid == -1 || (callingpid != mypid)) {
mClientUid = IPCThreadState::self()->getCallingUid();
} else {
mClientUid = uid;
}
if (pid == -1 || (callingpid != mypid)) {
mClientPid = callingpid;
} else {
mClientPid = pid;
}
mFlags = flags;
mCbf = cbf;
if (cbf != NULL) {
mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava);
mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
// thread begins in paused state, and will not reference us until start()
}
// create the IAudioRecord
status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);
......
AudioSystem::acquireAudioSessionId(mSessionId, -1);
......
return NO_ERROR;
}
会话id mSessionId 默认是采用分配方式AUDIO_SESSION_ALLOCATE,再通过audioFlinger->newAudioUniqueId创建当前对象独一无二的id,然后audioFlinger->acquireAudioSessionId(int audioSession, pid_t pid) 会判断是否重复申请和用vector存放持有这俩个参数的对象AudioSessionRef。
只关注 status_t status = openRecord_l(0 /epoch/, mOpPackageName);
status_t AudioRecord::openRecord_l(size_t epoch, const String16& opPackageName)
{
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
/*IAudioFlinger实际获取的是跑在mediaServer里面的binder服务"media.audio_flinger"
*/
if (audioFlinger == 0) {
ALOGE("Could not get audioflinger");
return NO_INIT;
}
// Fast tracks must be at the primary _output_ [sic] sampling rate,
// because there is currently no concept of a primary input sampling rate
uint32_t afSampleRate = AudioSystem::getPrimaryOutputSamplingRate();
if (afSampleRate == 0) {
ALOGW("getPrimaryOutputSamplingRate failed");
}
// Client can only express a preference for FAST. Server will perform additional tests.
if ((mFlags & AUDIO_INPUT_FLAG_FAST) && !((
// either of these use cases:
// use case 1: callback transfer mode
(mTransfer == TRANSFER_CALLBACK) ||
// use case 2: obtain/release mode
(mTransfer == TRANSFER_OBTAIN)) &&
// matching sample rate
(mSampleRate == afSampleRate))) {
ALOGW("AUDIO_INPUT_FLAG_FAST denied by client; transfer %d, track %u Hz, primary %u Hz",
mTransfer, mSampleRate, afSampleRate);
// once denied, do not request again if IAudioRecord is re-created
mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
}
IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
pid_t tid = -1;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
trackFlags |= IAudioFlinger::TRACK_FAST;
if (mAudioRecordThread != 0) {
tid = mAudioRecordThread->getTid();
}
}
if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
}
/*
在这里会先通过"media.audio_policy" 服务获取,
在/av/media/services/Audiopolicy/service/AudioPolicyInterfaceImpl.cpp
的AudioPolicyService实现类,然后成员mAudioPolicyManager
/av/media/services/Audiopolicy/managerdefault/AudioPolicyManager.cpp 根据参数
获取输入audio_io_handle_t 和设备的选取,再通过AudioFlinger 的openInput打开,
到最后真正实现录音线程RecordThread和AudioStreamIn。
*/
audio_io_handle_t input;
status_t status = AudioSystem::getInputForAttr(&mAttributes, &input,
(audio_session_t)mSessionId,
IPCThreadState::self()->getCallingUid(),
mSampleRate, mFormat, mChannelMask,
mFlags, mSelectedDeviceId);
if (status != NO_ERROR) {
ALOGE("Could not get audio input for record source %d, sample rate %u, format %#x, "
"channel mask %#x, session %d, flags %#x",
mAttributes.source, mSampleRate, mFormat, mChannelMask, mSessionId, mFlags);
return BAD_VALUE;
}
{
// Now that we have a reference to an I/O handle and have not yet handed it off to AudioFlinger,
// we must release it ourselves if anything goes wrong.
size_t frameCount = mReqFrameCount;
size_t temp = frameCount; // temp may be replaced by a revised value of frameCount,
// but we will still need the original value also
int originalSessionId = mSessionId;
// The notification frame count is the period between callbacks, as suggested by the server.
size_t notificationFrames = mNotificationFramesReq;
/*IMemory 在里面起到共享内存作用,后期的音频数据有可以通过共享内存来get到。iMem的创建
初始化在AudioFlinger,同时会创建audio_track_cblk_t对象引用指针赋给iMem的iMem->pointer()。
audio_track_cblk_t作用下面细说,audioFlinger->openRecord最终返回的IAudioRecord
是控制整个录音流程的关键对象,比如开始录音,结束录音等等。
*/
sp<IMemory> iMem; // for cblk
sp<IMemory> bufferMem;
sp<IAudioRecord> record = audioFlinger->openRecord(input,
mSampleRate,
mFormat,
mChannelMask,
opPackageName,
&temp,
&trackFlags,
tid,
mClientUid,
&mSessionId,
¬ificationFrames,
iMem,
bufferMem,
&status);
ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId,
"session ID changed from %d to %d", originalSessionId, mSessionId);
if (status != NO_ERROR) {
ALOGE("AudioFlinger could not create record track, status: %d", status);
goto release;
}
ALOG_ASSERT(record != 0);
// AudioFlinger now owns the reference to the I/O handle,
// so we are no longer responsible for releasing it.
if (iMem == 0) {
ALOGE("Could not get control block");
return NO_INIT;
}
void *iMemPointer = iMem->pointer();
if (iMemPointer == NULL) {
ALOGE("Could not get control block pointer");
return NO_INIT;
}
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
// Starting address of buffers in shared memory.
// The buffers are either immediately after the control block,
// or in a separate area at discretion of server.
void *buffers;
if (bufferMem == 0) {
buffers = cblk + 1;
} else {
buffers = bufferMem->pointer();
if (buffers == NULL) {
ALOGE("Could not get buffer pointer");
return NO_INIT;
}
}
// invariant that mAudioRecord != 0 is true only after set() returns successfully
if (mAudioRecord != 0) {
IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
mDeathNotifier.clear();
}
mAudioRecord = record;
mCblkMemory = iMem;
mBufferMemory = bufferMem;
IPCThreadState::self()->flushCommands();
mCblk = cblk;
// note that temp is the (possibly revised) value of frameCount
if (temp < frameCount || (frameCount == 0 && temp == 0)) {
ALOGW("Requested frameCount %zu but received frameCount %zu", frameCount, temp);
}
frameCount = temp;
mAwaitBoost = false;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
if (trackFlags & IAudioFlinger::TRACK_FAST) {
ALOGV("AUDIO_INPUT_FLAG_FAST successful; frameCount %zu", frameCount);
mAwaitBoost = true;
} else {
ALOGV("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %zu", frameCount);
// once denied, do not request again if IAudioRecord is re-created
mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
}
}
// Make sure that application is notified with sufficient margin before overrun
if (notificationFrames == 0 || notificationFrames > frameCount) {
ALOGW("Received notificationFrames %zu for frameCount %zu", notificationFrames, frameCount);
}
mNotificationFramesAct = notificationFrames;
// We retain a copy of the I/O handle, but don't own the reference
mInput = input;
mRefreshRemaining = true;
mFrameCount = frameCount;
// If IAudioRecord is re-created, don't let the requested frameCount
// decrease. This can confuse clients that cache frameCount().
if (frameCount > mReqFrameCount) {
mReqFrameCount = frameCount;
}
/* update proxy AudioRecord client代理,看到cblk这个参数就明白它是代理了一些音
频数据获取相关的功能。比如obtainBuffer,从cblk成员指针进行内存共享获取录音数据。*/
mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize);
mProxy->setEpoch(epoch);
mProxy->setMinimum(mNotificationFramesAct);
mDeathNotifier = new DeathNotifier(this);
/**
由于mAudioRecord是通过AudioFlinger获得的binder接口对象,所以还是要注册
死亡监听和通知释放资源
*/
IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
if (mDeviceCallback != 0) {
AudioSystem::addAudioDeviceCallback(mDeviceCallback, mInput);
}
return NO_ERROR;
}
release:
AudioSystem::releaseInput(input, (audio_session_t)mSessionId);
if (status == NO_ERROR) {
status = NO_INIT;
}
return status;
}
AudioSystem::getInputForAttr这个接口填充一些flags参数,采样率,格式,通道和会话ID mSessionId, 进程uid等,接下的流程是到/av/media/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp的 "media.audio_policy"服务,audioPolicy->getInputForAttr 进行一些参数audio_attributes_t,uid的检验, 和还会对申请到的inputType 选择的类型进行"android.permission.MODIFY_AUDIO_ROUTING" 或"android.permission.CAPTURE_AUDIO_OUTPUT"权限检测。
l跑在android环境的linux应用要检测app应用权限的原理方式也很简单,利用"permission"这个跑在systemServer的binder服务跨进程检查App应用所带的权限,参数pid,uid,和权限字符串。
"permission"是在ActivityManagerService.java 里面注册进Servicemanager里的,实现类是
static class PermissionController extends IPermissionController.Stub{}
在这里class PermissionController实现的是相当于Bn端,而Bp端是实现定义在/framework/native/libs/binder/IPermissonController.cpp
status_t AudioPolicyService::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)
{
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
// already checked by client, but double-check in case the client wrapper is bypassed
if (attr->source >= AUDIO_SOURCE_CNT && attr->source != AUDIO_SOURCE_HOTWORD &&
attr->source != AUDIO_SOURCE_FM_TUNER) {
return BAD_VALUE;
}
if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
return BAD_VALUE;
}
sp<AudioPolicyEffects>audioPolicyEffects;
status_t status;
AudioPolicyInterface::input_type_t inputType;
// if the caller is us, trust the specified uid
if (IPCThreadState::self()->getCallingPid() != getpid_cached || uid == (uid_t)-1) {
uid_t newclientUid = IPCThreadState::self()->getCallingUid();
if (uid != (uid_t)-1 && uid != newclientUid) {
ALOGW("%s uid %d tried to pass itself off as %d", __FUNCTION__, newclientUid, uid);
}
uid = newclientUid;
}
{
Mutex::Autolock _l(mLock);
// the audio_in_acoustics_t parameter is ignored by get_input()
/**
默认的AudioPolicyManager实现在/av/services/audioPolicy/managerdefault/AudioPolicyManager.cpp
*/
status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid,
samplingRate, format, channelMask,
flags, selectedDeviceId,
&inputType);
audioPolicyEffects = mAudioPolicyEffects;
if (status == NO_ERROR) {
// enforce permission (if any) required for each type of input
switch (inputType) {
case AudioPolicyInterface::API_INPUT_LEGACY:
break;
case AudioPolicyInterface::API_INPUT_TELEPHONY_RX:
// FIXME: use the same permission as for remote submix for now.
case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:
//权限检查
if (!captureAudioOutputAllowed()) {
ALOGE("getInputForAttr() permission denied: capture not allowed");
status = PERMISSION_DENIED;
}
break;
case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:
//权限检查
if (!modifyAudioRoutingAllowed()) {
ALOGE("getInputForAttr() permission denied: modify audio routing not allowed");
status = PERMISSION_DENIED;
}
break;
case AudioPolicyInterface::API_INPUT_INVALID:
default:
LOG_ALWAYS_FATAL("getInputForAttr() encountered an invalid input type %d",
(int)inputType);
}
}
if (status != NO_ERROR) {
if (status == PERMISSION_DENIED) {
mAudioPolicyManager->releaseInput(*input, session);
}
return status;
}
}
if (audioPolicyEffects != 0) {
/**
录音输入的音效设置相关
*/
// create audio pre processors according to input source
status_t status = audioPolicyEffects->addInputEffects(*input, attr->source, session);
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("Failed to add effects on input %d", *input);
}
}
return NO_ERROR;
}
mAudioPolicyManager->getInputForAttr 这里进行策略性获得audio_devices_t 这个东西实则是uint32所以就可以说成是输入设备id。获取完该需要的device和audio_config_t配置和功能性flags参数,就又绕回到AudioFlinger去openInput该设备了。
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 = AUDIO_IO_HANDLE_NONE;
*inputType = API_INPUT_INVALID;
audio_devices_t device;
// handle legacy remote submix case where the address was not always specified
String8 address = String8("");
bool isSoundTrigger = false;
audio_source_t inputSource = attr->source;
audio_source_t halInputSource;
AudioMix *policyMix = NULL;
if (inputSource == AUDIO_SOURCE_DEFAULT) {
inputSource = AUDIO_SOURCE_MIC;
}
halInputSource = inputSource;
// Explicit routing?
sp<DeviceDescriptor> deviceDesc;
for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
if (mAvailableInputDevices[i]->getId() == selectedDeviceId) {
deviceDesc = mAvailableInputDevices[i];
break;
}
}
mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
status_t ret = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
if (ret != NO_ERROR) {
return ret;
}
*inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
address = String8(attr->tags + strlen("addr="));
} else {
device = getDeviceAndMixForInputSource(inputSource, &policyMix);
if (device == AUDIO_DEVICE_NONE) {
ALOGW("getInputForAttr() could not find device for source %d", inputSource);
return BAD_VALUE;
}
if (policyMix != NULL) {
address = policyMix->mRegistrationId;
if (policyMix->mMixType == MIX_TYPE_RECORDERS) {
// there is an external policy, but this input is attached to a mix of recorders,
// meaning it receives audio injected into the framework, so the recorder doesn't
// know about it and is therefore considered "legacy"
*inputType = API_INPUT_LEGACY;
} else {
// recording a mix of players defined by an external policy, we're rerouting for
// an external policy
*inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
}
} else if (audio_is_remote_submix_device(device)) {
address = String8("0");
*inputType = API_INPUT_MIX_CAPTURE;
} else if (device == AUDIO_DEVICE_IN_TELEPHONY_RX) {
*inputType = API_INPUT_TELEPHONY_RX;
} else {
*inputType = API_INPUT_LEGACY;
}
// adapt channel selection to input source
switch (inputSource) {
case AUDIO_SOURCE_VOICE_UPLINK:
channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK;
break;
case AUDIO_SOURCE_VOICE_DOWNLINK:
channelMask = AUDIO_CHANNEL_IN_VOICE_DNLINK;
break;
case AUDIO_SOURCE_VOICE_CALL:
channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK;
break;
default:
break;
}
if (inputSource == 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;
}
}
}
// find a compatible input profile (not necessarily identical in parameters)
sp<IOProfile> profile;
// samplingRate and flags may be updated by getInputProfile
uint32_t profileSamplingRate = samplingRate;
audio_format_t profileFormat = format;
audio_channel_mask_t profileChannelMask = channelMask;
audio_input_flags_t profileFlags = flags;
for (;;) {
/**
对当前确认好的设备id和配置地址flags进行功能检查
*/
profile = getInputProfile(device, address,
profileSamplingRate, profileFormat, profileChannelMask,
profileFlags);
if (profile != 0) {
break; // success
} else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
} else { // fail
ALOGW("getInputForAttr() could not find profile for device 0x%X, samplingRate %u,"
"format %#x, channelMask 0x%X, flags %#x",
device, samplingRate, format, channelMask, flags);
return BAD_VALUE;
}
}
if (profile->getModuleHandle() == 0) {
ALOGE("getInputForAttr(): HW module %s not opened", profile->getModuleName());
return NO_INIT;
}
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = profileSamplingRate;
config.channel_mask = profileChannelMask;
config.format = profileFormat;
/**
mpClientInterface就是AudioFlinger的客户端形式接口
*/
status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
input,
&config,
&device,
address,
halInputSource,
profileFlags);
// only accept input with the exact requested set of parameters
if (status != NO_ERROR || *input == AUDIO_IO_HANDLE_NONE ||
(profileSamplingRate != config.sample_rate) ||
(profileFormat != config.format) ||
(profileChannelMask != config.channel_mask)) {
ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d,"
" channelMask %x",
samplingRate, format, channelMask);
if (*input != AUDIO_IO_HANDLE_NONE) {
mpClientInterface->closeInput(*input);
}
return BAD_VALUE;
}
sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile);
inputDesc->mInputSource = inputSource;
inputDesc->mRefCount = 0;
inputDesc->mOpenRefCount = 1;
inputDesc->mSamplingRate = profileSamplingRate;
inputDesc->mFormat = profileFormat;
inputDesc->mChannelMask = profileChannelMask;
inputDesc->mDevice = device;
inputDesc->mSessions.add(session);
inputDesc->mIsSoundTrigger = isSoundTrigger;
inputDesc->mPolicyMix = policyMix;
ALOGV("getInputForAttr() returns input type = %d", *inputType);
addInput(*input, inputDesc);
mpClientInterface->onAudioPortListUpdate();
return NO_ERROR;
}
回到AudioFlinger
status_t AudioFlinger::openInput(audio_module_handle_t module,
audio_io_handle_t *input,
audio_config_t *config,
audio_devices_t *devices,
const String8& address,
audio_source_t source,
audio_input_flags_t flags)
{
Mutex::Autolock _l(mLock);
if (*devices == AUDIO_DEVICE_NONE) {
return BAD_VALUE;
}
sp<RecordThread> thread = openInput_l(module, input, config, *devices, address, source, flags);
if (thread != 0) {
// notify client processes of the new input creation
thread->ioConfigChanged(AUDIO_INPUT_OPENED);
return NO_ERROR;
}
return NO_INIT;
}
根据input, audio_config_t,audio_devices_t, audio_input_flags_t,audio_source_t进行HAL打开输入流audio_stream_in_t *inStream的同时创建一个RecordThread管理这个输入流。
sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t module,
audio_io_handle_t *input,
audio_config_t *config,
audio_devices_t devices,
const String8& address,
audio_source_t source,
audio_input_flags_t flags)
{
AudioHwDevice *inHwDev = findSuitableHwDev_l(module, devices);
.......
audio_hw_device_t *inHwHal = inHwDev->hwDevice();
audio_stream_in_t *inStream = NULL;
status_t status = inHwHal->open_input_stream(inHwHal, *input, devices, &halconfig,
&inStream, flags, address.string(), source);
......
if (status == NO_ERROR && inStream != NULL) {
/**
tee_sink功能是官方开放用来拦截音频流管道的原始PCM数据,并且可以定义生成wav文件。官方功能使用自行百度,如果单是PCM数据想要截取的话,还不如直接在/av/services/audioflinger/Threads.cpp改代码RecordThread或PlaybackThread来read出PCM数据再存放到固定文件。
*/
#ifdef TEE_SINK
// Try to re-use most recently used Pipe to archive a copy of input for dumpsys,
// or (re-)create if current Pipe is idle and does not match the new format
......
switch (kind) {
case TEE_SINK_NEW: {
Pipe *pipe = new Pipe(mTeeSinkInputFrames, format);
size_t numCounterOffers = 0;
const NBAIO_Format offers[1] = {format};
ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
ALOG_ASSERT(index == 0);
/*
tee sink 功能需要的管道
*/
PipeReader *pipeReader = new PipeReader(*pipe);
numCounterOffers = 0;
index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
ALOG_ASSERT(index == 0);
mRecordTeeSink = pipe;
mRecordTeeSource = pipeReader;
teeSink = pipe;
}
break;
case TEE_SINK_OLD:
teeSink = mRecordTeeSink;
break;
case TEE_SINK_NO:
default:
break;
}
#endif
/** AudioStreamIn单纯一个结构体存放inHwDev和inStream的
*/
AudioStreamIn *inputStream = new AudioStreamIn(inHwDev, inStream);
// Start record thread
// RecordThread requires both input and output device indication to forward to audio
// pre processing modules
sp<RecordThread> thread = new RecordThread(this,
inputStream,
*input,
primaryOutputDevice_l(),
devices,
mSystemReady
#ifdef TEE_SINK
, teeSink
#endif
);
/* 把当前的拿到的RecordThread依赖audio_io_handle_t input存到键值表KeyedVector里,等到客户端后续需要调用该线程做start,stop操作时,可以凭自身的input在mRecordThreads取得。
*/
mRecordThreads.add(*input, thread);
return thread;
}
}
RecordThread类在/av/services/audioflinger/Thread.cpp里面,顺便一提播放音频线程PlaybackThread类也在Thread.cpp。
完成RecordThread录音线程创建和HAL输入流打开的后,
应用进程里面的Jni里的AudioRecord对象的初始化AudioSystem::getInputForAttr接口算是走完了。此时的AudioRecord初始化还并没有结束,还有
sp<IAudioRecord> record = audioFlinger->openRecord(input,mSampleRate,mFormat, mChannelMask,opPackageName,
&temp,&trackFlags, tid,mClientUid,&mSessionId, ¬ificationFrames,iMem,
bufferMem,&status);
IMemory iMem这个IInterface接口参数很重要。IAudioRecord是固定绑定上AudioFlinger操控录音线程RecordThread的接口对象,流程去到
sp<IAudioRecord> AudioFlinger::openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
const String16& opPackageName,
size_t *frameCount,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
int clientUid,
int *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
sp<IMemory>& buffers,
status_t *status)
{
sp<RecordThread::RecordTrack> recordTrack;
sp<RecordHandle> recordHandle;
sp<Client> client;
status_t lStatus;
int lSessionId;
cblk.clear();
buffers.clear();
// 通过包名检测权限"android.permission.RECORD_AUDIO"
if (!recordingAllowed(opPackageName)) {
ALOGE("openRecord() permission denied: recording not allowed");
lStatus = PERMISSION_DENIED;
goto Exit;
}
......
Mutex::Autolock _l(mLock);
RecordThread *thread = checkRecordThread_l(input);
......
pid_t pid = IPCThreadState::self()->getCallingPid();
client = registerPid(pid);
......
/*
recordTrack是录音线程内部类起核心作用的对象
*/
// TODO: the uid should be passed in as a parameter to openRecord
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
frameCount, lSessionId, notificationFrames,
clientUid, flags, tid, &lStatus);
......
/*
cblk是和共享内存相关的引用
*/
cblk = recordTrack->getCblk();
buffers = recordTrack->getBuffers();
......
/**
recordHandle 继承IAudioRecord,相当返回给应用调用端的客户端一个binder控制RecordTrack接口
*/
// return handle to client
recordHandle = new RecordHandle(recordTrack);
return recordHandle;
}
上面代码把重要部分保留了,其他省略。checkRecordThread_l函数,按前面说的会根据audio_io_handle_t input来从键值表里拿出对应的录音线程RecordThread。RecordThread还要createRecordTrack_l 创建一个recordTrack。RecordTrack录音轨道里面又新创建了三个关键对象RecordBufferConverter 数据目标格式转换类,AudioRecordServerProxy 被AudioFlinger使用的录音服务代理和ResamplerBufferProvider看名字取义是重采样数据提供者。
RecordTrack的初始化
mRecordBufferConverter = new RecordBufferConverter(
thread->mChannelMask, thread->mFormat, thread->mSampleRate,
channelMask, format, sampleRate);
mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack());
mResamplerBufferProvider = new ResamplerBufferProvider(this);
为什么说关键,mRecordBufferConverter有个转换接口
convert(void *dst, AudioBufferProvider *provider, size_t frames)
在RecordThread的threadLoop里面进行对HAL流读取到原始数据后,会通过mRecordBufferConverter将provider的 getNextBuffer 函数从RecordThread间接拿到原始数据转换成对应目标格式后拷贝到void *dst对应指向的内存。等下会介绍threadLoop函数的开始录音到读取原始数据和结束录音的流程。而mServerProxy的唯一目的是创造一个循环形式使用的内存buffer用于读取或写入录音数据。
另外RecordTrack初始化时还为App客户端AudioRecord初始化用于共享内存的cblk和buffers申请了共享内存堆。
cblk = recordTrack->getCblk();
buffers = recordTrack->getBuffers();
就是RecordTrack申请完毕后返回的IMemory 引用。客户端会靠这个IMemory来read录音数据。返回的RecordHandle对象装载了recordTrack,不用想,这个只是集中外观模式的处理来自客户端的控制录音功能。