(五)Audio子系統之AudioRecord.stop


在上一篇文章《(四)Audio子系統之AudioRecord.read》中已經介紹了AudioRecord如何獲取音頻數據,接下來,繼續分析AudioRecord方法中的stop的實現

 

  函數原型:

  public void stop() throws IllegalStateException

       作用:

    暫停錄制

  參數:

    無

  返回值:

    無

  異常:

    若沒有初始化完成時,拋出IllegalStateException

 

接下來進入系統分析具體實現

frameworks/base/media/java/android/media/AudioRecord.java

    public void stop()
    throws IllegalStateException {
        if (mState != STATE_INITIALIZED) {
            throw new IllegalStateException("stop() called on an uninitialized AudioRecord.");
        }

        // stop recording
        synchronized(mRecordingStateLock) {
            handleFullVolumeRec(false);
            native_stop();
            mRecordingState = RECORDSTATE_STOPPED;
        }
    }

首先判斷是否已經初始化完畢了,在new AudioRecord()中,mState已經是STATE_INITIALIZED狀態了。所以繼續分析native_stop函數,最后標記mState已經是RECORDSTATE_STOPPED

frameworks/base/core/jni/android_media_AudioRecord.cpp

static void
android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
{
    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    if (lpRecorder == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }

    lpRecorder->stop();
}

繼續調用AudioRecord的stop方法

frameworks\av\media\libmedia\AudioRecord.cpp

void AudioRecord::stop()
{
    AutoMutex lock(mLock);
    if (!mActive) {
        return;
    }
    mActive = false;
    mProxy->interrupt();
    mAudioRecord->stop();
    // the record head position will reset to 0, so if a marker is set, we need
    // to activate it again
    mMarkerReached = false;
    sp<AudioRecordThread> t = mAudioRecordThread;
    if (t != 0) {
        t->pause();
    } else {
        setpriority(PRIO_PROCESS, 0, mPreviousPriority);
        set_sched_policy(0, mPreviousSchedulingGroup);
    }
}

在這個函數中主要工作如下:

    1.更新mActive為false;

    2.調用mProxy->interrupt,設置cblk->mFlags標記CBLK_INTERRUPT,這個標記是在應用獲取共享內存中數據的時候進行判斷,是否緩沖區處於INTERRUPT狀態;

frameworks\av\media\libmedia\AudioTrackShared.cpp

void ClientProxy::interrupt()
{
    audio_track_cblk_t* cblk = mCblk;
    if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->mFlags) & CBLK_INTERRUPT)) {
        android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
        (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
                1);
    }
}

    3.調用mAudioRecord->stop()方法;

    4.標記mMarkerReached為false,這個變量在AudioRecordThread線程中用到了,他會回調,發送EVENT_MARKER事件給應用;

    5.調用AudioRecordThread線程中的pause方法,暫停AudioRecordThread線程;

void AudioRecord::AudioRecordThread::pause()
{
    AutoMutex _l(mMyLock);
    mPaused = true;
}

我們知道,還有一個重要的線程:RecordThread,這個線程怎么停止呢,所以繼續分析mAudioRecord->stop(),這個mAudioRecord是IAudioRecord對象,之前在分析mAudioRecord->start()的時候已經知道,IAudioRecord類是在RecordHandle類中實現的

frameworks\av\services\audioflinger\Tracks.cpp

void AudioFlinger::RecordHandle::stop() {
    stop_nonvirtual();
}

void AudioFlinger::RecordHandle::stop_nonvirtual() {
    ALOGV("RecordHandle::stop()");
    mRecordTrack->stop();
}

繼續調用mRecordTrack->stop()函數

void AudioFlinger::RecordThread::RecordTrack::stop()
{
    sp<ThreadBase> thread = mThread.promote();
    if (thread != 0) {
        RecordThread *recordThread = (RecordThread *)thread.get();
        if (recordThread->stop(this) && isExternalTrack()) {
            AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId);
        }
    }
}

這里做了兩件事:

    1.獲取RecordThread線程,然后調用stop方法;

    2.之前在startRecording分析中已經知道這個recordTrack是外部的Track;

    3.調用AudioSystem::stopInput();

首先看下RecordTrack::stop()的第1步:RecordThread線程的stop方法

frameworks\av\services\audioflinger\Threads.cpp

bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
    ALOGV("RecordThread::stop");
    AutoMutex _l(mLock);
    if (mActiveTracks.indexOf(recordTrack) != 0 || recordTrack->mState == TrackBase::PAUSING) {
        return false;
    }
    // note that threadLoop may still be processing the track at this point [without lock]
    recordTrack->mState = TrackBase::PAUSING;
    // do not wait for mStartStopCond if exiting
    if (exitPending()) {
        return true;
    }
    // FIXME incorrect usage of wait: no explicit predicate or loop
    mStartStopCond.wait(mLock);
    // if we have been restarted, recordTrack is in mActiveTracks here
    if (exitPending() || mActiveTracks.indexOf(recordTrack) != 0) {
        ALOGV("Record stopped OK");
        return true;
    }
    return false;
}

1.標記recordTrack->mState為TrackBase::PAUSING,這個我們在RecordThread::threadLoop中發現,當recordTrack->mState為PAUSING的時候,會把activeTrack從mActiveTracks中remove掉,所以這個線程就又會進入到mWaitWorkCV.wait(mLock);狀態中,開啟又一輪的睡眠。

2.我們之前在分析RecordThread::threadLoop的第8步的時候提到,當時調用了mStartStopCond.broadcast(),意思就是告訴這里的stop函數,你發的TrackBase::PAUSING我已經收到了;

3.最后判斷下activeTrack是否真的從mActiveTracks中remove掉了,如果是,那么表示stop成功了。

好了,這里就把RecordThread線程也停止了,但是注意:AudioRecordThread與RecordThread兩個線程都是處於阻塞狀態,並沒有銷毀。

接下來分析RecordTrack::stop()的第3步:AudioSystem::stopInput();

frameworks\av\media\libmedia\AudioSystem.cpp

status_t AudioSystem::stopInput(audio_io_handle_t input,
                                audio_session_t session)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;
    return aps->stopInput(input, session);
}

繼續調用AudioPolicyService的stopInput方法

frameworks\av\services\audiopolicy\AudioPolicyInterfaceImpl.cpp

status_t AudioPolicyService::stopInput(audio_io_handle_t input,
                                       audio_session_t session)
{
    if (mAudioPolicyManager == NULL) {
        return NO_INIT;
    }
    Mutex::Autolock _l(mLock);

    return mAudioPolicyManager->stopInput(input, session);
}

轉發

status_t AudioPolicyManager::stopInput(audio_io_handle_t input,
                                       audio_session_t session)
{
    ALOGV("stopInput() input %d", input);
    ssize_t index = mInputs.indexOfKey(input);
    if (index < 0) {
        ALOGW("stopInput() unknown input %d", input);
        return BAD_VALUE;
    }
    sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);

    index = inputDesc->mSessions.indexOf(session);
    if (index < 0) {
        ALOGW("stopInput() unknown session %d on input %d", session, input);
        return BAD_VALUE;
    }

    if (inputDesc->mRefCount == 0) {
        ALOGW("stopInput() input %d already stopped", input);
        return INVALID_OPERATION;
    }

    inputDesc->mRefCount--;
    if (inputDesc->mRefCount == 0) {

        // automatically disable the remote submix output when input is stopped if not
        // used by a policy mix of type MIX_TYPE_RECORDERS
        if (audio_is_remote_submix_device(inputDesc->mDevice)) {
            String8 address = String8("");
            if (inputDesc->mPolicyMix == NULL) {
                address = String8("0");
            } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
                address = inputDesc->mPolicyMix->mRegistrationId;
            }
            if (address != "") {
                setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
                                         AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
                                         address);
            }
        }

        resetInputDevice(input);

        if (activeInputsCount() == 0) {
            SoundTrigger::setCaptureState(false);
        }
    }
    return NO_ERROR;
}

在這個函數中的主要工作如下:

    1.從mInputs中獲取input索引index以及inputDesc;

    2.對inputDesc->mRefCount引用計數進行-1操作;

    3.調用resetInputDevice函數重置input;

繼續分析下resetInputDevice函數

status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input,
                                              audio_patch_handle_t *patchHandle)
{
    sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input);
    ssize_t index;
    if (patchHandle) {
        index = mAudioPatches.indexOfKey(*patchHandle);
    } else {
        index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
    }
    if (index < 0) {
        return INVALID_OPERATION;
    }
    sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
    status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
    ALOGV("resetInputDevice() releaseAudioPatch returned %d", status);
    inputDesc->mPatchHandle = 0;
    removeAudioPatch(patchDesc->mHandle);
    nextAudioPortGeneration();
    mpClientInterface->onAudioPatchListUpdate();
    return status;
}

這個函數的主要作用就是更新AudioPatch

所以AudioSystem::stopInput()函數中主要作用就是把mInputs中的inputDesc引用減去,然后重置AudioPatch

 

總結:

    在stop函數中,主要工作就是把AudioRecordThread與RecordThread兩個線程掛起來了,同時把startRecording方法中好不容易建立起來的input流也干掉了,所以如果需要繼續錄音,那么就需要重新調用startRecording方法了。

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM