• (五)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方法

    frameworksavmedialibmediaAudioRecord.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状态;

    frameworksavmedialibmediaAudioTrackShared.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类中实现的

    frameworksavservicesaudioflingerTracks.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方法

    frameworksavservicesaudioflingerThreads.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();

    frameworksavmedialibmediaAudioSystem.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方法

    frameworksavservicesaudiopolicyAudioPolicyInterfaceImpl.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方法了。

    由于作者内功有限,若文章中存在错误或不足的地方,还请给位大佬指出,不胜感激!

  • 相关阅读:
    Hibernate之Query接口的uniqueResult()方法
    一个安静的地方
    AJAX实现简单的省市二级联动
    为了你们,而活着。
    PL/SQL Developer 连接远程Oracle数据库
    dotfuscator混淆的问题
    查询表内属于主键的列SQLServer2005
    关于net2.0里面新出现的类backgroundworker的应用 引用自http://www.cnblogs.com/dlwang2002/archive/2006/12/07/585093.html
    SQLServer int to base16
    数据库查询问题日期格式
  • 原文地址:https://www.cnblogs.com/pngcui/p/10016608.html
Copyright © 2020-2023  润新知