1. 程式人生 > >(六)Audio子系統之AudioRecord.release (五)Audio子系統之AudioRecord.stop

(六)Audio子系統之AudioRecord.release (五)Audio子系統之AudioRecord.stop

 

在上一篇文章《(五)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;
    }
}
這裡呼叫setAudioRecord,和之前的getAudioRecord稍有不同,然後把相關的資源都delete掉
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所有軟硬體資源的釋放。

 

由於作者內功有限,若文章中存在錯誤或不足的地方,還請給位大佬指出,不勝感激!