(六)Audio子系统之AudioRecord.release
在上一篇文章《(五)Audio子系统之AudioRecord.stop》中已经介绍了AudioRecord如何暂停录制,接下来,继续分析AudioRecord方法中的release的实现
函数原型:
public void release()
作用:
释放Audio资源
参数:
无
返回值:
无
接下来进入系统分析具体实现
frameworks/base/media/java/android/media/AudioRecord.java
public void release() { try { stop(); } catch(IllegalStateException ise) { // don't raise an exception, we're releasing the resources. } native_release(); mState = STATE_UNINITIALIZED; }
1.再调用一次stop方法,这里会在AudioRecord::stop()方法中遭遇mActive,导致直接return;
2.调用native_release的native方法;
3.更新mState为STATE_UNINITIALIZED;
这里分析下native_release函数
static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0); if (lpRecorder == NULL) { return; } ALOGV("About to delete lpRecorder: %p", lpRecorder.get()); lpRecorder->stop(); audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField( thiz, javaAudioRecordFields.nativeCallbackCookie); // reset the native resources in the Java object so any attempt to access // them after a call to release fails. env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); // delete the callback information if (lpCookie) { Mutex::Autolock l(sLock); ALOGV("deleting lpCookie: %p", lpCookie); while (lpCookie->busy) { if (lpCookie->cond.waitRelative(sLock, milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != NO_ERROR) { break; } } sAudioRecordCallBackCookies.remove(lpCookie); env->DeleteGlobalRef(lpCookie->audioRecord_class); env->DeleteGlobalRef(lpCookie->audioRecord_ref); delete lpCookie; } }
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; }
所以接下来,很多很多地方将会调用自己的析构函数释放相关资源,比如AudioRecordThread与RecordThread两个线程肯定需要释放掉,还有hal层中还打开了mic的设备节点没有关闭,所以也会关闭,这个是在~RecordHandle中调用了,这里简单介绍下
frameworks\av\services\audioflinger\Tracks.cpp
AudioFlinger::RecordHandle::~RecordHandle() { stop_nonvirtual(); mRecordTrack->destroy(); }
void AudioFlinger::RecordThread::RecordTrack::destroy() { // see comments at AudioFlinger::PlaybackThread::Track::destroy() sp<RecordTrack> keep(this); { if (isExternalTrack()) { if (mState == ACTIVE || mState == RESUMING) { AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId); } AudioSystem::releaseInput(mThreadIoHandle, (audio_session_t)mSessionId); } sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { Mutex::Autolock _l(thread->mLock); RecordThread *recordThread = (RecordThread *) thread.get(); recordThread->destroyTrack_l(this); } } }
看AudioSystem::releaseInput函数
frameworks\av\media\libmedia\AudioSystem.cpp
void AudioSystem::releaseInput(audio_io_handle_t input, audio_session_t session) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return; aps->releaseInput(input, session); }
frameworks\av\services\audiopolicy\AudioPolicyInterfaceImpl.cpp
void AudioPolicyService::releaseInput(audio_io_handle_t input, audio_session_t session) { if (mAudioPolicyManager == NULL) { return; } sp<AudioPolicyEffects>audioPolicyEffects; { Mutex::Autolock _l(mLock); mAudioPolicyManager->releaseInput(input, session); audioPolicyEffects = mAudioPolicyEffects; } if (audioPolicyEffects != 0) { // release audio processors from the input status_t status = audioPolicyEffects->releaseInputEffects(input); if(status != NO_ERROR) { ALOGW("Failed to release effects on input %d", input); } } }
mAudioPolicyManager->releaseInput
frameworks\av\services\audiopolicy\AudioPolicyManager.cpp
void AudioPolicyManager::releaseInput(audio_io_handle_t input, audio_session_t session) { ALOGV("releaseInput() %d", input); ssize_t index = mInputs.indexOfKey(input); if (index < 0) { ALOGW("releaseInput() releasing unknown input %d", input); return; } sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index); ALOG_ASSERT(inputDesc != 0); index = inputDesc->mSessions.indexOf(session); if (index < 0) { ALOGW("releaseInput() unknown session %d on input %d", session, input); return; } inputDesc->mSessions.remove(session); if (inputDesc->mOpenRefCount == 0) { ALOGW("releaseInput() invalid open ref count %d", inputDesc->mOpenRefCount); return; } inputDesc->mOpenRefCount--; if (inputDesc->mOpenRefCount > 0) { ALOGV("releaseInput() exit > 0"); return; } closeInput(input); mpClientInterface->onAudioPortListUpdate(); ALOGV("releaseInput() exit"); }
这里把session从mSessions中移出去了,然后继续调用closeInput函数,最后更新了AudioPortList。
void AudioPolicyManager::closeInput(audio_io_handle_t input) { ALOGV("closeInput(%d)", input); sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input); if (inputDesc == NULL) { ALOGW("closeInput() unknown input %d", input); return; } nextAudioPortGeneration(); ssize_t index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle); if (index >= 0) { sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index); status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0); mAudioPatches.removeItemsAt(index); mpClientInterface->onAudioPatchListUpdate(); } mpClientInterface->closeInput(input); mInputs.removeItem(input); }
frameworks\av\services\audiopolicy\AudioPolicyClientImpl.cpp
status_t AudioPolicyService::AudioPolicyClient::closeInput(audio_io_handle_t input) { sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); if (af == 0) { return PERMISSION_DENIED; } return af->closeInput(input); }
frameworks\av\services\audioflinger\AudioFlinger.cpp
status_t AudioFlinger::closeInput(audio_io_handle_t input) { return closeInput_nonvirtual(input); } status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input) { // keep strong reference on the record thread so that // it is not destroyed while exit() is executed sp<RecordThread> thread; { Mutex::Autolock _l(mLock); thread = checkRecordThread_l(input); if (thread == 0) { return BAD_VALUE; } ALOGV("closeInput() %d", input); // If we still have effect chains, it means that a client still holds a handle // on at least one effect. We must either move the chain to an existing thread with the // same session ID or put it aside in case a new record thread is opened for a // new capture on the same session sp<EffectChain> chain; { Mutex::Autolock _sl(thread->mLock); Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l(); // Note: maximum one chain per record thread if (effectChains.size() != 0) { chain = effectChains[0]; } } if (chain != 0) { // first check if a record thread is already opened with a client on the same session. // This should only happen in case of overlap between one thread tear down and the // creation of its replacement size_t i; for (i = 0; i < mRecordThreads.size(); i++) { sp<RecordThread> t = mRecordThreads.valueAt(i); if (t == thread) { continue; } if (t->hasAudioSession(chain->sessionId()) != 0) { Mutex::Autolock _l(t->mLock); ALOGV("closeInput() found thread %d for effect session %d", t->id(), chain->sessionId()); t->addEffectChain_l(chain); break; } } // put the chain aside if we could not find a record thread with the same session id. if (i == mRecordThreads.size()) { putOrphanEffectChain_l(chain); } } audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL); mRecordThreads.removeItem(input); } // FIXME: calling thread->exit() without mLock held should not be needed anymore now that // we have a different lock for notification client closeInputFinish(thread); return NO_ERROR; } void AudioFlinger::closeInputFinish(sp<RecordThread> thread) { thread->exit(); AudioStreamIn *in = thread->clearInput(); ALOG_ASSERT(in != NULL, "in shouldn't be NULL"); // from now on thread->mInput is NULL in->hwDev()->close_input_stream(in->hwDev(), in->stream); delete in; }
最后调用hal层in->hwDev()->close_input_stream关闭设备节点
hardware\aw\audio\tulip\audio_hw.c
static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *stream) { struct sunxi_stream_in *in = (struct sunxi_stream_in *)stream; struct sunxi_audio_device *ladev = (struct sunxi_audio_device *)dev; in_standby(&stream->common); if (in->buffer) { free(in->buffer); in->buffer = 0; } if (in->resampler) { release_resampler(in->resampler); in->resampler = 0; } if (ladev->af_capture_flag) { ladev->af_capture_flag = false; } if (ladev->PcmManager.BufStart) { ladev->PcmManager.BufExist = false; free(ladev->PcmManager.BufStart); ladev->PcmManager.BufStart = 0; } free(stream); normal_record_enable(false); fm_record_enable(false); phone_record_enable(false); ALOGD("adev_close_input_stream set voice record status"); return; }
static int in_standby(struct audio_stream *stream) { struct sunxi_stream_in *in = (struct sunxi_stream_in *)stream; int status; pthread_mutex_lock(&in->dev->lock); pthread_mutex_lock(&in->lock); status = do_input_standby(in); pthread_mutex_unlock(&in->lock); pthread_mutex_unlock(&in->dev->lock); return status; }
static int do_input_standby(struct sunxi_stream_in *in) { struct sunxi_audio_device *adev = in->dev; if (!in->standby) { pcm_close(in->pcm); in->pcm = NULL; adev->active_input = 0; if (in->resampler){ release_resampler(in->resampler); in->resampler = 0; } if (adev->mode != AUDIO_MODE_IN_CALL) { adev->in_device = AUDIO_DEVICE_NONE; select_device(adev); } if (in->echo_reference != NULL) { /* stop reading from echo reference */ in->echo_reference->read(in->echo_reference, NULL); put_echo_reference(adev, in->echo_reference); in->echo_reference = NULL; } in->standby = 1; } return 0; }
终于是调用了pcm_close了,完成Audio录音的一整个闭环。
总结:
在release函数中,主要就是释放掉Android系统中之前申请到的各种资源,以及销毁AudioRecordThread与RecordThread两个线程,最后关闭mic的设备节点,完成Audio所有软硬件资源的释放。
由于作者内功有限,若文章中存在错误或不足的地方,还请给位大佬指出,不胜感激!
作者:pngcui
博客园:http://www.cnblogs.com/pngcui/
github:https://github.com/pngcui
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。