(二)Audio子系统之new AudioRecord()
在上一篇文章《(一)Audio子系统之AudioRecord.getMinBufferSize》中已经介绍了AudioRecord如何获取最小缓冲区大小,接下来,继续分析AudioRecorder方法中的new AudioRecorder的实现,本文基于Android5.1,Android4.4请戳这里
函数原型:
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes) throws IllegalArgumentException
作用:
创建AudioRecord对象
参数:
audioSource:录制源,这里设置MediaRecorder.AudioSource.MIC,其他请见MediaRecorder.AudioSource录制源定义,比如MediaRecorder.AudioSource.FM_TUNER等;
sampleRateInHz:默认采样率,单位Hz,这里设置为44100,44100Hz是当前唯一能保证在所有设备上工作的采样率;
channelConfig: 描述音频通道设置,这里设置为AudioFormat.CHANNEL_CONFIGURATION_MONO,CHANNEL_CONFIGURATION_MONO保证能在所有设备上工作;
audioFormat:音频数据保证支持此格式,这里设置为AudioFormat.ENCODING_16BIT;
bufferSizeInBytes:在录制过程中,音频数据写入缓冲区的总数(字节),即getMinVufferSize()获取到的值。
异常:
当参数设置不正确或不支持的参数时,将会抛出IllegalArgumentException
接下来进入系统分析具体实现
frameworks/base/media/java/android/media/AudioRecord.java
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) throws IllegalArgumentException { this((new AudioAttributes.Builder()) .setInternalCapturePreset(audioSource) .build(), (new AudioFormat.Builder()) .setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,//0x10 true/*allow legacy configurations*/)) .setEncoding(audioFormat) .setSampleRate(sampleRateInHz) .build(), bufferSizeInBytes, AudioManager.AUDIO_SESSION_ID_GENERATE); }
调用相应的方法,检查参数的合法性,然后对参数进行保存等操作,然后调用自己的构造函数this()
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int sessionId) throws IllegalArgumentException { mRecordingState = RECORDSTATE_STOPPED; if (attributes == null) { throw new IllegalArgumentException("Illegal null AudioAttributes"); } if (format == null) { throw new IllegalArgumentException("Illegal null AudioFormat"); } // remember which looper is associated with the AudioRecord instanciation if ((mInitializationLooper = Looper.myLooper()) == null) { mInitializationLooper = Looper.getMainLooper(); } // is this AudioRecord using REMOTE_SUBMIX at full volume? if (attributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX) { final AudioAttributes.Builder filteredAttr = new AudioAttributes.Builder(); final Iterator<String> tagsIter = attributes.getTags().iterator(); while (tagsIter.hasNext()) { final String tag = tagsIter.next(); if (tag.equalsIgnoreCase(SUBMIX_FIXED_VOLUME)) { mIsSubmixFullVolume = true; Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume"); } else { // SUBMIX_FIXED_VOLUME: is not to be propagated to the native layers filteredAttr.addTag(tag); } } filteredAttr.setInternalCapturePreset(attributes.getCapturePreset()); mAudioAttributes = filteredAttr.build(); } else { mAudioAttributes = attributes; } int rate = 0; if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0) { rate = format.getSampleRate(); } else { rate = AudioSystem.getPrimaryOutputSamplingRate(); if (rate <= 0) { rate = 44100; } } int encoding = AudioFormat.ENCODING_DEFAULT; if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0) { encoding = format.getEncoding(); } audioParamCheck(attributes.getCapturePreset(), rate, encoding); mChannelCount = AudioFormat.channelCountFromInChannelMask(format.getChannelMask()); mChannelMask = getChannelMaskFromLegacyConfig(format.getChannelMask(), false); audioBuffSizeCheck(bufferSizeInBytes); int[] session = new int[1]; session[0] = sessionId; //TODO: update native initialization when information about hardware init failure // due to capture device already open is available. int initResult = native_setup( new WeakReference<AudioRecord>(this), mAudioAttributes, mSampleRate, mChannelMask, mAudioFormat, mNativeBufferSizeInBytes, session); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing native AudioRecord object."); return; // with mState == STATE_UNINITIALIZED } mSessionId = session[0]; mState = STATE_INITIALIZED; }
在这个函数中,主要做了如下工作
1.标记mRecordingState为stoped状态;
2.获取一个MainLooper;
3.判断录音源是否是REMOTE_SUBMIX,有兴趣的童鞋可以深入研究;
4.重新获取rate与format参数,这里会根据AUDIO_FORMAT_HAS_PROPERTY_X来判断从哪里获取参数,而在之前的构造函数中,设置参数的时候已经标记了该标志位,所以这两个参数还是我们设置的;
5.调用audioParamCheck对参数再一次进行检查合法性;
6.获取声道数以及声道掩码,单声道掩码为0x10,双声道掩码为0x0c;
7.调用audioBuffSizeCheck检查最小缓冲区大小是否合法;
8.调用native_setup的native函数,注意这里传过去的参数包括:指向自己的指针,录制源,rate,声道掩码,format,minBuffSize,session[];
9.标记mRecordingState为inited状态;
注:关于SessionId
一个Session就是一个会话,每个会话都有一个独一无二的Id来标识。该Id的最终管理在AudioFlinger中。
一个会话可以被多个AudioTrack对象和MediaPlayer共用。
共用一个Session的AudioTrack和MediaPlayer共享相同的AudioEffect(音效)。
我们只分析native_setup函数
frameworks/base/core/jni/android_media_AudioRecord.cpp
static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa, jint sampleRateInHertz, jint channelMask, // Java channel masks map directly to the native definition jint audioFormat, jint buffSizeInBytes, jintArray jSession) { if (jaa == 0) { ALOGE("Error creating AudioRecord: invalid audio attributes"); return (jint) AUDIO_JAVA_ERROR; } if (!audio_is_input_channel(channelMask)) { ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask); return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; } uint32_t channelCount = audio_channel_count_from_in_mask(channelMask); // compare the format against the Java constants audio_format_t format = audioFormatToNative(audioFormat); if (format == AUDIO_FORMAT_INVALID) { ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat); return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; } size_t bytesPerSample = audio_bytes_per_sample(format); if (buffSizeInBytes == 0) { ALOGE("Error creating AudioRecord: frameCount is 0."); return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; } size_t frameSize = channelCount * bytesPerSample; size_t frameCount = buffSizeInBytes / frameSize; jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { ALOGE("Can't find %s when setting up callback.", kClassPathName); return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; } if (jSession == NULL) { ALOGE("Error creating AudioRecord: invalid session ID pointer"); return (jint) AUDIO_JAVA_ERROR; } jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); return (jint) AUDIO_JAVA_ERROR; } int sessionId = nSession[0]; env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; // create an uninitialized AudioRecord object sp<AudioRecord> lpRecorder = new AudioRecord(); audio_attributes_t *paa = NULL; // read the AudioAttributes values paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); const jstring jtags = (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); const char* tags = env->GetStringUTFChars(jtags, NULL); // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); env->ReleaseStringUTFChars(jtags, tags); paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource); paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags); audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE; if (paa->flags & AUDIO_FLAG_HW_HOTWORD) { flags = AUDIO_INPUT_FLAG_HW_HOTWORD; } // create the callback information: // this data will be passed with every AudioRecord callback audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie; lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); // we use a weak reference so the AudioRecord object can be garbage collected. lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); lpCallbackData->busy = false; 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, paa); if (status != NO_ERROR) { ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.", status); goto native_init_failure; } nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); goto native_init_failure; } // read the audio session ID back from AudioRecord in case a new session was created during set() nSession[0] = lpRecorder->getSessionId(); env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; { // scope for the lock Mutex::Autolock l(sLock); sAudioRecordCallBackCookies.add(lpCallbackData); } // 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; // failure: native_init_failure: env->DeleteGlobalRef(lpCallbackData->audioRecord_class); env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); delete lpCallbackData; env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; }
在这个函数中主要工作如下:
1.判断声道掩码是否合法,然后通过掩码计算出声道数;
2.由于最小缓冲区大小是采样帧数量*每个采样帧大小得出,每个采样帧大小为所有声道数所占的字节数,从而求出采样帧数量frameCount;
3.进行一系列的JNI处理录音源,以及把AudioRecord.java的指针绑定到lpCallbackData回调数据中,这样就能把数据通过回调的方式通知到上层;
4.调用AudioRecord的set函数,这里注意下flags,他的类型为audio_input_flags_t,定义在system\core\include\system\audio.h中,作为音频输入的标志,这里设置为AUDIO_INPUT_FLAG_NONE
typedef enum { AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks" AUDIO_INPUT_FLAG_HW_HOTWORD = 0x2, // prefer an input that captures from hw hotword source } audio_input_flags_t;
5.把lpRecorder对象以及lpCallbackData回调保存到javaAudioRecordFields的相应字段中。
这里分析lpRecorder->set函数
frameworks\av\media\libmedia\AudioRecord.cpp
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, const audio_attributes_t* pAttributes) { switch (transferType) { case TRANSFER_DEFAULT: if (cbf == NULL || threadCanCallJava) { transferType = TRANSFER_SYNC; } else { transferType = TRANSFER_CALLBACK; } break; case TRANSFER_CALLBACK: if (cbf == NULL) { ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL"); return BAD_VALUE; } break; case TRANSFER_OBTAIN: case TRANSFER_SYNC: break; default: ALOGE("Invalid transfer type %d", transferType); return BAD_VALUE; } mTransfer = transferType; AutoMutex lock(mLock); // invariant that mAudioRecord != 0 is true only after set() returns successfully if (mAudioRecord != 0) { ALOGE("Track already in use"); return INVALID_OPERATION; } if (pAttributes == NULL) { memset(&mAttributes, 0, sizeof(audio_attributes_t)); mAttributes.source = inputSource; } else { // stream type shouldn't be looked at, this track has audio attributes memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t)); ALOGV("Building AudioRecord with attributes: source=%d flags=0x%x tags=[%s]", mAttributes.source, mAttributes.flags, mAttributes.tags); } if (sampleRate == 0) { ALOGE("Invalid sample rate %u", sampleRate); return BAD_VALUE; } mSampleRate = sampleRate; // these below should probably come from the audioFlinger too... if (format == AUDIO_FORMAT_DEFAULT) { format = AUDIO_FORMAT_PCM_16_BIT; } // validate parameters if (!audio_is_valid_format(format)) { ALOGE("Invalid format %#x", format); return BAD_VALUE; } // Temporary restriction: AudioFlinger currently supports 16-bit PCM only if (format != AUDIO_FORMAT_PCM_16_BIT) { ALOGE("Format %#x is not supported", format); return BAD_VALUE; } mFormat = format; 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 if (sessionId == AUDIO_SESSION_ALLOCATE) { mSessionId = AudioSystem::newAudioUniqueId(); } else { mSessionId = sessionId; } ALOGV("set(): mSessionId %d", mSessionId); mFlags = flags; mCbf = cbf; if (cbf != NULL) { mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava); mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO); } // create the IAudioRecord status_t status = openRecord_l(0 /*epoch*/); if (status != NO_ERROR) { if (mAudioRecordThread != 0) { mAudioRecordThread->requestExit(); // see comment in AudioRecord.h mAudioRecordThread->requestExitAndWait(); mAudioRecordThread.clear(); } return status; } mStatus = NO_ERROR; mActive = false; mUserData = user; // TODO: add audio hardware input latency here mLatency = (1000*mFrameCount) / sampleRate; mMarkerPosition = 0; mMarkerReached = false; mNewPosition = 0; mUpdatePeriod = 0; AudioSystem::acquireAudioSessionId(mSessionId, -1); mSequence = 1; mObservedSequence = mSequence; mInOverrun = false; return NO_ERROR; }
在这个函数中主要工作如下:
1.在JNI中传递过来的参数:transferType为TRANSFER_DEFAULT,cbf!=null,threadCanCallJava=true,所以mTransfer设置为TRANSFER_SYNC,他是决定如何从AudioRecord传输数据方式,后面会用到;
2.保存相关的参数,如录制源mAttributes.source,采样率mSampleRate,采样精度mFormat,声道掩码mChannelMask,声道数mChannelCount,采样帧大小mFrameSize,采样帧数量mReqFrameCount,通知帧计数mNotificationFramesReq,mSessionId在这里更新了,音频输入标志mFlags还是之前的AUDIO_INPUT_FLAG_NONE
3.当cbf数据回调函数不为null时,开启一个录音线程AudioRecordThread;
4.调用openRecord_l(0)创建IAudioRecord对象;
5.如果建立失败,就销毁录音线程AudioRecordThread,否则更新参数;
这里继续分析如何创建IAudioRecord对象
status_t AudioRecord::openRecord_l(size_t epoch) { status_t status; const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_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) && !( // use case: callback transfer mode (mTransfer == TRANSFER_CALLBACK) && // matching sample rate (mSampleRate == afSampleRate))) { ALOGW("AUDIO_INPUT_FLAG_FAST denied by client"); // 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(); } } audio_io_handle_t input; status = AudioSystem::getInputForAttr(&mAttributes, &input, (audio_session_t)mSessionId, mSampleRate, mFormat, mChannelMask, mFlags); 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; sp<IMemory> iMem; // for cblk sp<IMemory> bufferMem; //return recordHandle = new RecordHandle(recordTrack); //class RecordHandle : public android::BnAudioRecord sp<IAudioRecord> record = audioFlinger->openRecord(input, mSampleRate, mFormat, mChannelMask, &temp, &trackFlags, tid, &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) { mAudioRecord->asBinder()->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 mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize); mProxy->setEpoch(epoch); mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); mAudioRecord->asBinder()->linkToDeath(mDeathNotifier, this); return NO_ERROR; } release: AudioSystem::releaseInput(input, (audio_session_t)mSessionId); if (status == NO_ERROR) { status = NO_INIT; } return status; }
在这个函数中主要工作如下:
1.获取IAudioFlinger对象,其通过binder和AudioFlinger通信,所以也就是相当于直接调用到AudioFlinger服务中了;
2.判断音频输入标志,是否需要清除AUDIO_INPUT_FLAG_FAST标志位,这里不需要,一直是AUDIO_INPUT_FLAG_NONE;
3.调用AudioSystem::getInputForAttr获取输入流的句柄input;
4.调用audioFlinger->openRecord创建IAudioRecord对象;
5.通过IMemory共享内存,获取录音数据;
6.更新AudioRecordClientProxy客户端代理的录音数据;
下面主要分析第3、4点:
首先看下AudioRecord.cpp::openRecord_l(0)的第3步.获取输入流的句柄input
frameworks\av\media\libmedia\AudioSystem.cpp
status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return NO_INIT; return aps->getInputForAttr(attr, input, session, samplingRate, format, channelMask, flags); }
获取AudioPolicy的服务,继续调用AudioPolicyService的函数
frameworks\av\services\audiopolicy\AudioPolicyInterfaceImpl.cpp
status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags) { 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()) || ((attr->source == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) { return BAD_VALUE; } sp<AudioPolicyEffects>audioPolicyEffects; status_t status; AudioPolicyInterface::input_type_t inputType; { Mutex::Autolock _l(mLock); // the audio_in_acoustics_t parameter is ignored by get_input() status = mAudioPolicyManager->getInputForAttr(attr, input, session, samplingRate, format, channelMask, flags, &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_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; }
在这个函数中主要的工作如下:
1.对source为HOTWORD或FM_TUNER的录音源,判断是否具有相应的录音权限(根据应用进程号);
2.继续调用AudioPolicyManager的方法获取input以及inputType;
3.检查应用是否具有该inputType的录音权限;
4.判断是否需要添加音效(audioPolicyEffects),需要则使用audioPolicyEffects->addInputEffects添加音效;
继续分析第2步
frameworks\av\services\audiopolicy\AudioPolicyManager.cpp
status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, audio_input_flags_t flags, 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; if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX && strncmp(attr->tags, "addr=", strlen("addr=")) == 0) { device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; address = String8(attr->tags + strlen("addr=")); ssize_t index = mPolicyMixes.indexOfKey(address); if (index < 0) { ALOGW("getInputForAttr() no policy for address %s", address.string()); return BAD_VALUE; } if (mPolicyMixes[index]->mMix.mMixType != MIX_TYPE_PLAYERS) { ALOGW("getInputForAttr() bad policy mix type for address %s", address.string()); return BAD_VALUE; } policyMix = &mPolicyMixes[index]->mMix; *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; } 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 { *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; } } } sp<IOProfile> profile = getInputProfile(device, address, samplingRate, format, channelMask, flags); if (profile == 0) { PLOGV("profile == 0"); //retry without flags audio_input_flags_t log_flags = flags; flags = AUDIO_INPUT_FLAG_NONE; profile = getInputProfile(device, address, samplingRate, format, channelMask, flags); if (profile == 0) { ALOGW("getInputForAttr() could not find profile for device 0x%X, samplingRate %u," "format %#x, channelMask 0x%X, flags %#x", device, samplingRate, format, channelMask, log_flags); return BAD_VALUE; } } if (profile->mModule->mHandle == 0) { PLOGV("getInputForAttr(): HW module %s not opened", profile->mModule->mName); ALOGE("getInputForAttr(): HW module %s not opened", profile->mModule->mName); return NO_INIT; } audio_config_t config = AUDIO_CONFIG_INITIALIZER; config.sample_rate = samplingRate; config.channel_mask = channelMask; config.format = format; status_t status = mpClientInterface->openInput(profile->mModule->mHandle, input, &config, &device, address, halInputSource, flags); // only accept input with the exact requested set of parameters if (status != NO_ERROR || *input == AUDIO_IO_HANDLE_NONE || (samplingRate != config.sample_rate) || (format != config.format) || (channelMask != 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 = samplingRate; inputDesc->mFormat = format; inputDesc->mChannelMask = channelMask; 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; }
在这个函数中主要工作如下:
1.调用getDeviceAndMixForInputSource函数获取policyMix设备以及对应的audio_device_t设备类型(device),device定义在system\core\include\system\audio.h中,这里使用了内置的MIC,所以device为AUDIO_DEVICE_IN_BUILTIN_MIC,另外如果还需要新增一种音频设备的话,需要在这里增加;
enum { AUDIO_DEVICE_NONE = 0x0, /* reserved bits */ AUDIO_DEVICE_BIT_IN = 0x80000000, AUDIO_DEVICE_BIT_DEFAULT = 0x40000000, /* output devices */ AUDIO_DEVICE_OUT_EARPIECE = 0x1, AUDIO_DEVICE_OUT_SPEAKER = 0x2, AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, ... /* input devices */ AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1, AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2, AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, ... AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | AUDIO_DEVICE_IN_AMBIENT | AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET | AUDIO_DEVICE_IN_WIRED_HEADSET | AUDIO_DEVICE_IN_HDMI | AUDIO_DEVICE_IN_TELEPHONY_RX | AUDIO_DEVICE_IN_BACK_MIC | AUDIO_DEVICE_IN_REMOTE_SUBMIX | AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | AUDIO_DEVICE_IN_USB_ACCESSORY | AUDIO_DEVICE_IN_USB_DEVICE | AUDIO_DEVICE_IN_FM_TUNER | AUDIO_DEVICE_IN_TV_TUNER | AUDIO_DEVICE_IN_LINE | AUDIO_DEVICE_IN_SPDIF | AUDIO_DEVICE_IN_BLUETOOTH_A2DP | AUDIO_DEVICE_IN_LOOPBACK | AUDIO_DEVICE_IN_AF | AUDIO_DEVICE_IN_DEFAULT), AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_DEVICE_IN_ALL_USB = (AUDIO_DEVICE_IN_USB_ACCESSORY | AUDIO_DEVICE_IN_USB_DEVICE), }; typedef uint32_t audio_devices_t;
2.获取inputType的类型
typedef enum { API_INPUT_INVALID = -1, API_INPUT_LEGACY = 0,// e.g. audio recording from a microphone API_INPUT_MIX_CAPTURE,// used for "remote submix", capture of the media to play it remotely API_INPUT_MIX_EXT_POLICY_REROUTE,// used for platform audio rerouting, where mixes are // handled by external and dynamically installed // policies which reroute audio mixes } input_type_t;
3.更新channelMask,适配声道到输入源;
4.调用getInputProfile,根据传进来的采样率/精度/掩码等参数与获得的设备支持的Input Profile比较,返回一个与设备Profile匹配的IOProfile对象,IOProfile是用来描述输出或输入流的能力,策略管理器使用它来确定输出或输入是否适合于给定的用例,相应地打开/关闭它,以及连接/断开音频轨道;
5.如果获取失败的话,则使用AUDIO_INPUT_FLAG_NONE再次获取一遍,如果依然失败,则return一个bad news;
6.继续调用mpClientInterface->openInput建立起输入流;
7.根据IOProfile对象构造AudioInputDescriptor,并绑定到input流中,最后更新AudioPortList;
这里我们着重分析下第1,6步
首先看下AudioPolicyManager.cpp::getInputForAttr()的第1步.获取policyMix设备以及对应的audio_device_t设备类型(device)
audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, AudioMix **policyMix) { audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; for (size_t i = 0; i < mPolicyMixes.size(); i++) { if (mPolicyMixes[i]->mMix.mMixType != MIX_TYPE_RECORDERS) { continue; } for (size_t j = 0; j < mPolicyMixes[i]->mMix.mCriteria.size(); j++) { if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mPolicyMixes[i]->mMix.mCriteria[j].mRule && mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mSource == inputSource) || (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mPolicyMixes[i]->mMix.mCriteria[j].mRule && mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mSource != inputSource)) { if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { if (policyMix != NULL) { *policyMix = &mPolicyMixes[i]->mMix; } return AUDIO_DEVICE_IN_REMOTE_SUBMIX; } break; } } } return getDeviceForInputSource(inputSource); } audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource) { uint32_t device = AUDIO_DEVICE_NONE; audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; switch (inputSource) { case AUDIO_SOURCE_VOICE_UPLINK: if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { device = AUDIO_DEVICE_IN_VOICE_CALL; break; } break; case AUDIO_SOURCE_DEFAULT: case AUDIO_SOURCE_MIC: if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; } else if ((mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO) && (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) { device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { device = AUDIO_DEVICE_IN_WIRED_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { device = AUDIO_DEVICE_IN_USB_DEVICE; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { device = AUDIO_DEVICE_IN_BUILTIN_MIC; } break; case AUDIO_SOURCE_VOICE_COMMUNICATION: // Allow only use of devices on primary input if in call and HAL does not support routing // to voice call path. if ((mPhoneState == AUDIO_MODE_IN_CALL) && (mAvailableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) { availableDeviceTypes = availablePrimaryInputDevices() & ~AUDIO_DEVICE_BIT_IN; } switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { case AUDIO_POLICY_FORCE_BT_SCO: // if SCO device is requested but no SCO device is available, fall back to default case if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; break; } // FALL THROUGH default: // FORCE_NONE if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { device = AUDIO_DEVICE_IN_WIRED_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { device = AUDIO_DEVICE_IN_USB_DEVICE; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { device = AUDIO_DEVICE_IN_BUILTIN_MIC; } break; case AUDIO_POLICY_FORCE_SPEAKER: if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { device = AUDIO_DEVICE_IN_BACK_MIC; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { device = AUDIO_DEVICE_IN_BUILTIN_MIC; } break; } break; case AUDIO_SOURCE_VOICE_RECOGNITION: case AUDIO_SOURCE_HOTWORD: if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO && availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { device = AUDIO_DEVICE_IN_WIRED_HEADSET; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { device = AUDIO_DEVICE_IN_USB_DEVICE; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { device = AUDIO_DEVICE_IN_BUILTIN_MIC; } break; case AUDIO_SOURCE_CAMCORDER: if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { device = AUDIO_DEVICE_IN_BACK_MIC; } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { device = AUDIO_DEVICE_IN_BUILTIN_MIC; } break; case AUDIO_SOURCE_VOICE_DOWNLINK: case AUDIO_SOURCE_VOICE_CALL: if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { device = AUDIO_DEVICE_IN_VOICE_CALL; } break; case AUDIO_SOURCE_REMOTE_SUBMIX: if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; } break; case AUDIO_SOURCE_FM_TUNER: if (availableDeviceTypes & AUDIO_DEVICE_IN_FM_TUNER) { device = AUDIO_DEVICE_IN_FM_TUNER; } break; default: ALOGW("getDeviceForInputSource() invalid input source %d", inputSource); break; } ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); return device; }
这里就是通过InputSource去获取相应的policyMix与audio_device_t设备类型了,从这里也可以看出Android系统上对Audio设备的分类有多少种了。
然后再看下AudioPolicyManager.cpp::getInputForAttr()的第6步.mpClientInterface->openInput如何建立起输入流
frameworks\av\services\audiopolicy\AudioPolicyClientImpl.cpp
status_t AudioPolicyService::AudioPolicyClient::openInput(audio_module_handle_t module, audio_io_handle_t *input, audio_config_t *config, audio_devices_t *device, const String8& address, audio_source_t source, audio_input_flags_t flags) { sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); if (af == 0) { ALOGW("%s: could not get AudioFlinger", __func__); return PERMISSION_DENIED; } return af->openInput(module, input, config, device, address, source, flags); }
这里就调用到了AF端的openInput函数了
frameworks\av\services\audioflinger\AudioFlinger.cpp
status_t AudioFlinger::openInput(audio_module_handle_t module, audio_io_handle_t *input, audio_config_t *config, audio_devices_t *device, const String8& address, audio_source_t source, audio_input_flags_t flags) { Mutex::Autolock _l(mLock); if (*device == AUDIO_DEVICE_NONE) { return BAD_VALUE; } sp<RecordThread> thread = openInput_l(module, input, config, *device, address, source, flags); if (thread != 0) { // notify client processes of the new input creation thread->audioConfigChanged(AudioSystem::INPUT_OPENED); return NO_ERROR; } return NO_INIT; } sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t module, audio_io_handle_t *input, audio_config_t *config, audio_devices_t device, const String8& address, audio_source_t source, audio_input_flags_t flags) { AudioHwDevice *inHwDev = findSuitableHwDev_l(module, device); if (inHwDev == NULL) { *input = AUDIO_IO_HANDLE_NONE; return 0; } if (*input == AUDIO_IO_HANDLE_NONE) { *input = nextUniqueId(); } audio_config_t halconfig = *config; audio_hw_device_t *inHwHal = inHwDev->hwDevice(); audio_stream_in_t *inStream = NULL; //获取inStream对象 status_t status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig, &inStream, flags, address.string(), source); // If the input could not be opened with the requested parameters and we can handle the // conversion internally, try to open again with the proposed parameters. The AudioFlinger can // resample the input and do mono to stereo or stereo to mono conversions on 16 bit PCM inputs. if (status == BAD_VALUE && config->format == halconfig.format && halconfig.format == AUDIO_FORMAT_PCM_16_BIT && (halconfig.sample_rate <= 2 * config->sample_rate) && (audio_channel_count_from_in_mask(halconfig.channel_mask) <= FCC_2) && (audio_channel_count_from_in_mask(config->channel_mask) <= FCC_2)) { // FIXME describe the change proposed by HAL (save old values so we can log them here) ALOGV("openInput_l() reopening with proposed sampling rate and channel mask"); inStream = NULL; status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig, &inStream, flags, address.string(), source); // FIXME log this new status; HAL should not propose any further changes } if (status == NO_ERROR && inStream != NULL) { #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 sp<NBAIO_Sink> teeSink; enum { TEE_SINK_NO, // don't copy input TEE_SINK_NEW, // copy input using a new pipe TEE_SINK_OLD, // copy input using an existing pipe } kind; NBAIO_Format format = Format_from_SR_C(halconfig.sample_rate, audio_channel_count_from_in_mask(halconfig.channel_mask), halconfig.format); if (!mTeeSinkInputEnabled) { kind = TEE_SINK_NO; } else if (!Format_isValid(format)) { kind = TEE_SINK_NO; } else if (mRecordTeeSink == 0) { kind = TEE_SINK_NEW; } else if (mRecordTeeSink->getStrongCount() != 1) { kind = TEE_SINK_NO; } else if (Format_isEqual(format, mRecordTeeSink->format())) { kind = TEE_SINK_OLD; } else { kind = TEE_SINK_NEW; } 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); 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 *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(), device #ifdef TEE_SINK , teeSink #endif ); mRecordThreads.add(*input, thread); ALOGV("openInput_l() created record thread: ID %d thread %p", *input, thread.get()); return thread; } *input = AUDIO_IO_HANDLE_NONE; return 0; }
在这个函数中主要工作如下:
1.findSuitableHwDev_l中通过IOProfile中的module.handle与audio_device_t设备类型找到Hw模块;
2.调用HAL层inHwHal->open_input_stream打开输入流;
3.如果失败了,再继续调用一次;
4.根据inHwDev与inStream创建AudioStreamIn对象,如此,就建立起了一个输入流了,AudioStreamIn定义在frameworks\av\services\audioflinger\AudioFlinger.h;
5.创建一个RecordThread线程,并把该线程加入到mRecordThreads线程中,这个线程是在AudioRecord.cpp::set()函数中创建的;
这里我们着重分析第2、5步:
首先看下AudioFlinger.cpp::openInput()的第2步:打开输入流
hardware\aw\audio\tulip\audio_hw.c
static int adev_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in) { struct sunxi_audio_device *ladev = (struct sunxi_audio_device *)dev; struct sunxi_stream_in *in; int ret; int channel_count = popcount(config->channel_mask); *stream_in = NULL; if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0) return -EINVAL; in = (struct sunxi_stream_in *)calloc(1, sizeof(struct sunxi_stream_in)); if (!in) return -ENOMEM; in->stream.common.get_sample_rate = in_get_sample_rate; in->stream.common.set_sample_rate = in_set_sample_rate; in->stream.common.get_buffer_size = in_get_buffer_size; in->stream.common.get_channels = in_get_channels; in->stream.common.get_format = in_get_format; in->stream.common.set_format = in_set_format; in->stream.common.standby = in_standby; in->stream.common.dump = in_dump; in->stream.common.set_parameters = in_set_parameters; in->stream.common.get_parameters = in_get_parameters; in->stream.common.add_audio_effect = in_add_audio_effect; in->stream.common.remove_audio_effect = in_remove_audio_effect; in->stream.set_gain = in_set_gain; in->stream.read = in_read; in->stream.get_input_frames_lost = in_get_input_frames_lost; in->requested_rate = config->sample_rate; // default config memcpy(&in->config, &pcm_config_mm_in, sizeof(pcm_config_mm_in)); in->config.channels = channel_count; //in->config.in_init_channels = channel_count; in->buffer = malloc(in->config.period_size * audio_stream_frame_size(&in->stream.common) * 8); if (!in->buffer) { ret = -ENOMEM; goto err; } memset(in->buffer, 0, in->config.period_size * audio_stream_frame_size(&in->stream.common) * 8); //mute ladev->af_capture_flag = false; //devices = AUDIO_DEVICE_IN_WIFI_DISPLAY;//for test if (devices == AUDIO_DEVICE_IN_AF) { ALOGV("to malloc PcmManagerBuffer: Buffer_size: %d", AF_BUFFER_SIZE); ladev->PcmManager.BufStart= (unsigned char *)malloc(AF_BUFFER_SIZE); if(!ladev->PcmManager.BufStart) { ret = -ENOMEM; goto err; } ladev->PcmManager.BufExist = true; ladev->PcmManager.BufTotalLen = AF_BUFFER_SIZE; ladev->PcmManager.BufWritPtr = ladev->PcmManager.BufStart; ladev->PcmManager.BufReadPtr = ladev->PcmManager.BufStart; ladev->PcmManager.BufValideLen = ladev->PcmManager.BufTotalLen; ladev->PcmManager.DataLen = 0; ladev->PcmManager.SampleRate = config->sample_rate; ladev->PcmManager.Channel = 2; ladev->af_capture_flag = true; ladev->PcmManager.dev = (struct sunxi_audio_device *)ladev; } in->dev = ladev; in->standby = 1; in->device = devices & ~AUDIO_DEVICE_BIT_IN; *stream_in = &in->stream; return 0; err: if (in->resampler) release_resampler(in->resampler); free(in); return ret; }
在这个函数中主要工作如下:
1.检查rate,format,channel参数是否支持;
2.给sunxi_stream_in输入流对象分配内存空间;
3.绑定相应参数的获取/设置方法;
4.为输入流创建buff空间:in->config.period_size *audio_stream_frame_size(&in->stream.common) * 8;
5.如果是AUDIO_DEVICE_IN_AF类型的设备的话,则对PcmManager做相应处理;
这个输入流对象会绑定到AF中的AudioStreamIn对象中,所以到这里,输入流对象就已经完全创建好了。
然后继续分析AudioFlinger.cpp::openInput()的第5步:创建RecordThread线程:
frameworks\av\services\audioflinger\Threads.cpp
AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice #ifdef TEE_SINK , const sp<NBAIO_Sink>& teeSink #endif ) : ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD), mInput(input), mActiveTracksGen(0), mRsmpInBuffer(NULL), // mRsmpInFrames and mRsmpInFramesP2 are set by readInputParameters_l() mRsmpInRear(0) #ifdef TEE_SINK , mTeeSink(teeSink) #endif , mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize, "RecordThreadRO", MemoryHeapBase::READ_ONLY)) // mFastCapture below , mFastCaptureFutex(0) // mInputSource // mPipeSink // mPipeSource , mPipeFramesP2(0) // mPipeMemory // mFastCaptureNBLogWriter , mFastTrackAvail(false) { snprintf(mName, kNameLength, "AudioIn_%X", id); mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName); readInputParameters_l(); // create an NBAIO source for the HAL input stream, and negotiate mInputSource = new AudioStreamInSource(input->stream); size_t numCounterOffers = 0; const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount, mFormat)}; ssize_t index = mInputSource->negotiate(offers, 1, NULL, numCounterOffers); ALOG_ASSERT(index == 0); // initialize fast capture depending on configuration bool initFastCapture; switch (kUseFastCapture) { case FastCapture_Never: initFastCapture = false; break; case FastCapture_Always: initFastCapture = true; break; case FastCapture_Static: uint32_t primaryOutputSampleRate; { AutoMutex _l(audioFlinger->mHardwareLock); primaryOutputSampleRate = audioFlinger->mPrimaryOutputSampleRate; } initFastCapture = // either capture sample rate is same as (a reasonable) primary output sample rate (((primaryOutputSampleRate == 44100 || primaryOutputSampleRate == 48000) && (mSampleRate == primaryOutputSampleRate)) || // or primary output sample rate is unknown, and capture sample rate is reasonable ((primaryOutputSampleRate == 0) && ((mSampleRate == 44100 || mSampleRate == 48000)))) && // and the buffer size is < 12 ms (mFrameCount * 1000) / mSampleRate < 12; break; // case FastCapture_Dynamic: } if (initFastCapture) { // create a Pipe for FastMixer to write to, and for us and fast tracks to read from NBAIO_Format format = mInputSource->format(); size_t pipeFramesP2 = roundup(mSampleRate / 25); // double-buffering of 20 ms each size_t pipeSize = pipeFramesP2 * Format_frameSize(format); void *pipeBuffer; const sp<MemoryDealer> roHeap(readOnlyHeap()); sp<IMemory> pipeMemory; if ((roHeap == 0) || (pipeMemory = roHeap->allocate(pipeSize)) == 0 || (pipeBuffer = pipeMemory->pointer()) == NULL) { ALOGE("not enough memory for pipe buffer size=%zu", pipeSize); goto failed; } // pipe will be shared directly with fast clients, so clear to avoid leaking old information memset(pipeBuffer, 0, pipeSize); Pipe *pipe = new Pipe(pipeFramesP2, format, pipeBuffer); const NBAIO_Format offers[1] = {format}; size_t numCounterOffers = 0; ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers); ALOG_ASSERT(index == 0); mPipeSink = pipe; PipeReader *pipeReader = new PipeReader(*pipe); numCounterOffers = 0; index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers); ALOG_ASSERT(index == 0); mPipeSource = pipeReader; mPipeFramesP2 = pipeFramesP2; mPipeMemory = pipeMemory; // create fast capture mFastCapture = new FastCapture(); FastCaptureStateQueue *sq = mFastCapture->sq(); #ifdef STATE_QUEUE_DUMP // FIXME #endif FastCaptureState *state = sq->begin(); state->mCblk = NULL; state->mInputSource = mInputSource.get(); state->mInputSourceGen++; state->mPipeSink = pipe; state->mPipeSinkGen++; state->mFrameCount = mFrameCount; state->mCommand = FastCaptureState::COLD_IDLE; // already done in constructor initialization list //mFastCaptureFutex = 0; state->mColdFutexAddr = &mFastCaptureFutex; state->mColdGen++; state->mDumpState = &mFastCaptureDumpState; #ifdef TEE_SINK // FIXME #endif mFastCaptureNBLogWriter = audioFlinger->newWriter_l(kFastCaptureLogSize, "FastCapture"); state->mNBLogWriter = mFastCaptureNBLogWriter.get(); sq->end(); sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED); // start the fast capture mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO); pid_t tid = mFastCapture->getTid(); int err = requestPriority(getpid_cached, tid, kPriorityFastMixer); if (err != 0) { ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", kPriorityFastCapture, getpid_cached, tid, err); } #ifdef AUDIO_WATCHDOG // FIXME #endif mFastTrackAvail = true; } failed: ; // FIXME mNormalSource }
在这个线程中主要工作如下:
1.调用readInputParameters_l函数把录音参数读取到线程空间中;
2.创建AudioStreamInSource对象,作为线程中间中的输入流,其实现是在frameworks\av\media\libnbaio\AudioStreamInSource.cpp;
所以可以猜到,后续在启动录音时,RecordThread中将会通过AudioStreamInSource对象进行获取数据,实时更新共享内存中的数据。
再回到AudioRecord.cpp::openRecord_l(0)的第4步.创建IAudioRecord对象
sp<IAudioRecord> AudioFlinger::openRecord( audio_io_handle_t input, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t *frameCount, IAudioFlinger::track_flags_t *flags, pid_t tid, 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(); // check calling permissions if (!recordingAllowed()) { ALOGE("openRecord() permission denied: recording not allowed"); lStatus = PERMISSION_DENIED; goto Exit; } // further sample rate checks are performed by createRecordTrack_l() if (sampleRate == 0) { ALOGE("openRecord() invalid sample rate %u", sampleRate); lStatus = BAD_VALUE; goto Exit; } // we don't yet support anything other than 16-bit PCM if (!(audio_is_valid_format(format) && audio_is_linear_pcm(format) && format == AUDIO_FORMAT_PCM_16_BIT)) { ALOGE("openRecord() invalid format %#x", format); lStatus = BAD_VALUE; goto Exit; } // further channel mask checks are performed by createRecordTrack_l() if (!audio_is_input_channel(channelMask)) { ALOGE("openRecord() invalid channel mask %#x", channelMask); lStatus = BAD_VALUE; goto Exit; } { Mutex::Autolock _l(mLock); RecordThread *thread = checkRecordThread_l(input); if (thread == NULL) { ALOGE("openRecord() checkRecordThread_l failed"); lStatus = BAD_VALUE; goto Exit; } pid_t pid = IPCThreadState::self()->getCallingPid(); client = registerPid(pid); if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) { lSessionId = *sessionId; } else { // if no audio session id is provided, create one here lSessionId = nextUniqueId(); if (sessionId != NULL) { *sessionId = lSessionId; } } // TODO: the uid should be passed in as a parameter to openRecord recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask, frameCount, lSessionId, notificationFrames, IPCThreadState::self()->getCallingUid(), flags, tid, &lStatus); LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0)); if (lStatus == NO_ERROR) { // Check if one effect chain was awaiting for an AudioRecord to be created on this // session and move it to this thread. sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)lSessionId); if (chain != 0) { Mutex::Autolock _l(thread->mLock); thread->addEffectChain_l(chain); } } } if (lStatus != NO_ERROR) { // remove local strong reference to Client before deleting the RecordTrack so that the // Client destructor is called by the TrackBase destructor with mClientLock held // Don't hold mClientLock when releasing the reference on the track as the // destructor will acquire it. { Mutex::Autolock _cl(mClientLock); client.clear(); } recordTrack.clear(); goto Exit; } cblk = recordTrack->getCblk(); buffers = recordTrack->getBuffers(); // return handle to client recordHandle = new RecordHandle(recordTrack); Exit: *status = lStatus; return recordHandle; }
在这个函数中主要工作如下:
1.调用recordingAllowed检查录音权限;
2.判断参数是否非法;
3.调用checkRecordThread_l函数,根据input从AudioRecordThread线程中获取该input的RecordThread,在前面的分析中可以得知,这个RecordThread是在AudioFlinger.cpp::openInput函数中创建并添加到AudioRecordThread中的;
4.调用createRecordTrack_l方法创建一个RecordTrack对象,RecordThread::RecordTrack对象的作用是管理RecordThread中的音频数据;
5.通过SessionId获取是否存在effect chain,若有,则加到RecordThread中;
6.通过RecordTrack获取cblk以及buffers,他们就是CblkMemory以及BufferMemory;
7.根据recordTrack,创建RecordHandle对象,实现位置:frameworks\av\services\audioflinger\Tracks.cpp,也就完成了IAudioRecord对象的创建了,也就是说IAudioRecord的方法是在Tracks.cpp中实现的;
这里再看下第2步:createRecordTrack_l函数
sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l( const sp<AudioFlinger::Client>& client, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t *pFrameCount, int sessionId, size_t *notificationFrames, int uid, IAudioFlinger::track_flags_t *flags, pid_t tid, status_t *status) { size_t frameCount = *pFrameCount; sp<RecordTrack> track; status_t lStatus; // client expresses a preference for FAST, but we get the final say if (*flags & IAudioFlinger::TRACK_FAST) { if ( // use case: callback handler (tid != -1) && // frame count is not specified, or is exactly the pipe depth ((frameCount == 0) || (frameCount == mPipeFramesP2)) && // PCM data audio_is_linear_pcm(format) && // native format (format == mFormat) && // native channel mask (channelMask == mChannelMask) && // native hardware sample rate (sampleRate == mSampleRate) && // record thread has an associated fast capture hasFastCapture() && // there are sufficient fast track slots available mFastTrackAvail ) { ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%u mFrameCount=%u", frameCount, mFrameCount); } else { ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%u mFrameCount=%u mPipeFramesP2=%u " "format=%#x isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u " "hasFastCapture=%d tid=%d mFastTrackAvail=%d", frameCount, mFrameCount, mPipeFramesP2, format, audio_is_linear_pcm(format), channelMask, sampleRate, mSampleRate, hasFastCapture(), tid, mFastTrackAvail); *flags &= ~IAudioFlinger::TRACK_FAST; } } // compute track buffer size in frames, and suggest the notification frame count if (*flags & IAudioFlinger::TRACK_FAST) { // fast track: frame count is exactly the pipe depth frameCount = mPipeFramesP2; // ignore requested notificationFrames, and always notify exactly once every HAL buffer *notificationFrames = mFrameCount; } else { // not fast track: max notification period is resampled equivalent of one HAL buffer time // or 20 ms if there is a fast capture // TODO This could be a roundupRatio inline, and const size_t maxNotificationFrames = ((int64_t) (hasFastCapture() ? mSampleRate/50 : mFrameCount) * sampleRate + mSampleRate - 1) / mSampleRate; // minimum number of notification periods is at least kMinNotifications, // and at least kMinMs rounded up to a whole notification period (minNotificationsByMs) static const size_t kMinNotifications = 3; static const uint32_t kMinMs = 30; // TODO This could be a roundupRatio inline const size_t minFramesByMs = (sampleRate * kMinMs + 1000 - 1) / 1000; // TODO This could be a roundupRatio inline const size_t minNotificationsByMs = (minFramesByMs + maxNotificationFrames - 1) / maxNotificationFrames; const size_t minFrameCount = maxNotificationFrames * max(kMinNotifications, minNotificationsByMs); frameCount = max(frameCount, minFrameCount); if (*notificationFrames == 0 || *notificationFrames > maxNotificationFrames) { *notificationFrames = maxNotificationFrames; } } *pFrameCount = frameCount; lStatus = initCheck(); if (lStatus != NO_ERROR) { ALOGE("createRecordTrack_l() audio driver not initialized"); goto Exit; } { // scope for mLock Mutex::Autolock _l(mLock); track = new RecordTrack(this, client, sampleRate, format, channelMask, frameCount, NULL, sessionId, uid, *flags, TrackBase::TYPE_DEFAULT); lStatus = track->initCheck(); if (lStatus != NO_ERROR) { ALOGE("createRecordTrack_l() initCheck failed %d; no control block?", lStatus); // track must be cleared from the caller as the caller has the AF lock goto Exit; } mTracks.add(track); // disable AEC and NS if the device is a BT SCO headset supporting those pre processings bool suspend = audio_is_bluetooth_sco_device(mInDevice) && mAudioFlinger->btNrecIsOff(); setEffectSuspended_l(FX_IID_AEC, suspend, sessionId); setEffectSuspended_l(FX_IID_NS, suspend, sessionId); if ((*flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful, // so ask activity manager to do this on our behalf sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp); } } lStatus = NO_ERROR; Exit: *status = lStatus; return track; }
这个函数重要的一点就是重新计算了frameCount大小,然后根据新的参数创建了RecordTrack对象,然后return。
总结:
当应用层new AudioRecord时,系统建立起了输入流,并创建了RecordThread线程,现在录音的准备工作已经完成,就等待应用层开启录音了。
由于作者内功有限,若文章中存在错误或不足的地方,还请给位大佬指出,不胜感激!
作者:pngcui
博客园:http://www.cnblogs.com/pngcui/
github:https://github.com/pngcui
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。