美文网首页Android开发
android 6.0 AudioRecord 录音流程分析(一

android 6.0 AudioRecord 录音流程分析(一

作者: 来吗这里有位置 | 来源:发表于2019-01-26 10:49 被阅读0次

    先看一段android 应用调用java代码的例子

    public class MicRecord extends Thread{
        AudioRecord audioRecord;
        AudioTrack audioTrack;
        volatile boolean canPlay = true;//个人推荐用带原子操作的对象AtomicBoolean代替
    
        @Override
        public void run() {
            final int recordbuffsize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_DEFAULT,
                    AudioFormat.ENCODING_PCM_16BIT);
            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100,
                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, recordbuffsize);
            final int playBuffsize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO,
                    AudioFormat.ENCODING_PCM_16BIT);
            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, playBuffsize, AudioTrack.MODE_STREAM);
    
            audioRecord.startRecording();
            audioTrack.play();
            byte[] recordData = new byte[recordbuffsize];
            while(canPlay){
                int readSize = audioRecord.read(recordData, 0, recordbuffsize);
                audioTrack.write(recordData, 0, readSize);
            }
            audioRecord.stop();
            audioRecord.release();
            audioTrack.stop();
            audioTrack.release();
        }
    
        public void stopRecord(){
            canPlay = false;
        }
    }
    

    下面是个人空闲之余整理的简述AudioRecord 的初始化流程和跨进程和mediaserver 的audioFlinger,audioPolicy爱恨情仇。和startRecording开始录音的流程。如果不是底层系统工程师做HAL对接输入设备驱动的话,流程是大概懂了但是实际功能上是用不上改动这部分代码的。HAL的用法法则也稍懂些但可惜已经不在linux os岗位,故无需详细了解。熟悉底层原理对做出好应用是也是很有必要的。


    AudioRecord流程图

    AudioRecord初始化

    说到AudioRecord.java 这个类就不得不提和它相应的注册native方法,在/base/core/jni/android_media_AudioRecord.cpp 文件中,具体这些native方法是什么时候load的呢?
    int register_android_media_AudioRecord(JNIEnv *env)
    这个要看由应用启动时zygote创建的AndroidRuntime由
    int AndroidRuntime::startReg(JNIEnv * env) 接口注册了。jni注册数组固定在AndroidRuntime.cpp 里面的

    static const RegJNIRec gRegJNI[] = {
    ....
        REG_JNI(register_android_media_AudioRecord),
        REG_JNI(register_android_media_AudioSystem),
        REG_JNI(register_android_media_AudioTrack),
    ....
    };
    

    所以在java类调用涉及到对象native 方法的都不需要重新加载so等。
    AudioRecord对象的创建需要参数 音频audioSource,采集频率sampleRateInHz,通道模式channelConfig,音频格式audioFormat,采集数据保存区大小bufferSizeInBytes。
    AudioRecord构造时对参数进行检查和进行native_setup

    int initResult = native_setup( new WeakReference<AudioRecord>(this),
                    mAudioAttributes, mSampleRate, mChannelMask, mChannelIndexMask,
                    mAudioFormat, mNativeBufferSizeInBytes,
                    session, ActivityThread.currentOpPackageName());
    

    跳到android_media_AudioRecord.cpp去

    static jint
    android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
            jobject jaa, jint sampleRateInHertz, jint channelMask, jint channelIndexMask,
            jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName)
    {
       ......
        // create an uninitialized AudioRecord object
        sp<AudioRecord> lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
      ......
        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,
            -1, -1,        // default uid, pid
            paa);
       ......
        // 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;
    } 
    
    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;
    }
    

    AudioRecord的native_setup初始化只截留重要部分分析,sp<AudioRecord> lpRecorder 是jni层的/av/media/libmedia/AudioRecord.cpp AudioRecord对象,创建后用一波反手骚操作,把这个对象的引用指针赋值给java AudioRecord的(long)mNativeRecorderInJavaObj 来保存,后面如果要用到lpRecorder则会直接获取该long型引用数据强转回jni的AudioRecord引用, jni使用的常用招式。

    static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
    {
        Mutex::Autolock l(sLock);
        AudioRecord* const ar =
                (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
        return sp<AudioRecord>(ar);
    }
    

    lpRecorder->set(xx[])初始化则是整个录音流程重点,它会通过audioFlinger来获取audio_io_handle_t 输入输出handle,打开HAL的设备获取audio_stream_in_t 输入流,并且打开创建录音线程RecordThread等等。下面描述

    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,
            int uid,
            pid_t pid,
            const audio_attributes_t* pAttributes)
    {
        ......
        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
    
    /**
    和audioflinger绑定的会话id
    **/
        if (sessionId == AUDIO_SESSION_ALLOCATE) {
            mSessionId = AudioSystem::newAudioUniqueId();
        } else {
            mSessionId = sessionId;
        }
        ALOGV("set(): mSessionId %d", mSessionId);
    
        int callingpid = IPCThreadState::self()->getCallingPid();
        int mypid = getpid();
        if (uid == -1 || (callingpid != mypid)) {
            mClientUid = IPCThreadState::self()->getCallingUid();
        } else {
            mClientUid = uid;
        }
        if (pid == -1 || (callingpid != mypid)) {
            mClientPid = callingpid;
        } else {
            mClientPid = pid;
        }
    
        mFlags = flags;
        mCbf = cbf;
    
        if (cbf != NULL) {
            mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava);
            mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
            // thread begins in paused state, and will not reference us until start()
        }
    
        // create the IAudioRecord
        status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);
    ......
        AudioSystem::acquireAudioSessionId(mSessionId, -1);
      ......
        return NO_ERROR;
    }
    

    会话id mSessionId 默认是采用分配方式AUDIO_SESSION_ALLOCATE,再通过audioFlinger->newAudioUniqueId创建当前对象独一无二的id,然后audioFlinger->acquireAudioSessionId(int audioSession, pid_t pid) 会判断是否重复申请和用vector存放持有这俩个参数的对象AudioSessionRef。
    只关注 status_t status = openRecord_l(0 /epoch/, mOpPackageName);

    status_t AudioRecord::openRecord_l(size_t epoch, const String16& opPackageName)
    {
        const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
      /*IAudioFlinger实际获取的是跑在mediaServer里面的binder服务"media.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) && !((
                // either of these use cases:
                // use case 1: callback transfer mode
                (mTransfer == TRANSFER_CALLBACK) ||
                // use case 2: obtain/release mode
                (mTransfer == TRANSFER_OBTAIN)) &&
                // matching sample rate
                (mSampleRate == afSampleRate))) {
            ALOGW("AUDIO_INPUT_FLAG_FAST denied by client; transfer %d, track %u Hz, primary %u Hz",
                    mTransfer, mSampleRate, afSampleRate);
            // 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();
            }
        }
    
        if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
            AudioSystem::removeAudioDeviceCallback(mDeviceCallback, mInput);
        }
    
    /*
    在这里会先通过"media.audio_policy"  服务获取,
    在/av/media/services/Audiopolicy/service/AudioPolicyInterfaceImpl.cpp 
    的AudioPolicyService实现类,然后成员mAudioPolicyManager 
    /av/media/services/Audiopolicy/managerdefault/AudioPolicyManager.cpp 根据参数
    获取输入audio_io_handle_t 和设备的选取,再通过AudioFlinger 的openInput打开,
    到最后真正实现录音线程RecordThread和AudioStreamIn。
    */
        audio_io_handle_t input;
        status_t status = AudioSystem::getInputForAttr(&mAttributes, &input,
                                            (audio_session_t)mSessionId,
                                            IPCThreadState::self()->getCallingUid(),
                                            mSampleRate, mFormat, mChannelMask,
                                            mFlags, mSelectedDeviceId);
    
        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;
    
    
    /*IMemory 在里面起到共享内存作用,后期的音频数据有可以通过共享内存来get到。iMem的创建
    初始化在AudioFlinger,同时会创建audio_track_cblk_t对象引用指针赋给iMem的iMem->pointer()。
    audio_track_cblk_t作用下面细说,audioFlinger->openRecord最终返回的IAudioRecord
    是控制整个录音流程的关键对象,比如开始录音,结束录音等等。
    */
        sp<IMemory> iMem;           // for cblk
        sp<IMemory> bufferMem;
        sp<IAudioRecord> record = audioFlinger->openRecord(input,
                                                           mSampleRate,
                                                           mFormat,
                                                           mChannelMask,
                                                           opPackageName,
                                                           &temp,
                                                           &trackFlags,
                                                           tid,
                                                           mClientUid,
                                                           &mSessionId,
                                                           &notificationFrames,
                                                           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) {
            IInterface::asBinder(mAudioRecord)->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 AudioRecord client代理,看到cblk这个参数就明白它是代理了一些音
    频数据获取相关的功能。比如obtainBuffer,从cblk成员指针进行内存共享获取录音数据。*/
        mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize);
        mProxy->setEpoch(epoch);
        mProxy->setMinimum(mNotificationFramesAct);
    
        mDeathNotifier = new DeathNotifier(this);
    /**
    由于mAudioRecord是通过AudioFlinger获得的binder接口对象,所以还是要注册
    死亡监听和通知释放资源
    */
        IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
    
        if (mDeviceCallback != 0) {
            AudioSystem::addAudioDeviceCallback(mDeviceCallback, mInput);
        }
    
        return NO_ERROR;
        }
    
    release:
        AudioSystem::releaseInput(input, (audio_session_t)mSessionId);
        if (status == NO_ERROR) {
            status = NO_INIT;
        }
        return status;
    }
    

    AudioSystem::getInputForAttr这个接口填充一些flags参数,采样率,格式,通道和会话ID mSessionId, 进程uid等,接下的流程是到/av/media/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp的 "media.audio_policy"服务,audioPolicy->getInputForAttr 进行一些参数audio_attributes_t,uid的检验, 和还会对申请到的inputType 选择的类型进行"android.permission.MODIFY_AUDIO_ROUTING" 或"android.permission.CAPTURE_AUDIO_OUTPUT"权限检测。
    l跑在android环境的linux应用要检测app应用权限的原理方式也很简单,利用"permission"这个跑在systemServer的binder服务跨进程检查App应用所带的权限,参数pid,uid,和权限字符串。
    "permission"是在ActivityManagerService.java 里面注册进Servicemanager里的,实现类是

    static class PermissionController extends IPermissionController.Stub{}
    

    在这里class PermissionController实现的是相当于Bn端,而Bp端是实现定义在/framework/native/libs/binder/IPermissonController.cpp

    status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
                                                 audio_io_handle_t *input,
                                                 audio_session_t session,
                                                 uid_t uid,
                                                 uint32_t samplingRate,
                                                 audio_format_t format,
                                                 audio_channel_mask_t channelMask,
                                                 audio_input_flags_t flags,
                                                 audio_port_handle_t selectedDeviceId)
    {
        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()) {
            return BAD_VALUE;
        }
        sp<AudioPolicyEffects>audioPolicyEffects;
        status_t status;
        AudioPolicyInterface::input_type_t inputType;
        // if the caller is us, trust the specified uid
        if (IPCThreadState::self()->getCallingPid() != getpid_cached || uid == (uid_t)-1) {
            uid_t newclientUid = IPCThreadState::self()->getCallingUid();
            if (uid != (uid_t)-1 && uid != newclientUid) {
                ALOGW("%s uid %d tried to pass itself off as %d", __FUNCTION__, newclientUid, uid);
            }
            uid = newclientUid;
        }
    
        {
            Mutex::Autolock _l(mLock);
            // the audio_in_acoustics_t parameter is ignored by get_input()
    /**
    默认的AudioPolicyManager实现在/av/services/audioPolicy/managerdefault/AudioPolicyManager.cpp
    */
            status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid,
                                                         samplingRate, format, channelMask,
                                                         flags, selectedDeviceId,
                                                         &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_TELEPHONY_RX:
                    // FIXME: use the same permission as for remote submix for now.
                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;
    }
    

    mAudioPolicyManager->getInputForAttr 这里进行策略性获得audio_devices_t 这个东西实则是uint32所以就可以说成是输入设备id。获取完该需要的device和audio_config_t配置和功能性flags参数,就又绕回到AudioFlinger去openInput该设备了。

    status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
                                                 audio_io_handle_t *input,
                                                 audio_session_t session,
                                                 uid_t uid,
                                                 uint32_t samplingRate,
                                                 audio_format_t format,
                                                 audio_channel_mask_t channelMask,
                                                 audio_input_flags_t flags,
                                                 audio_port_handle_t selectedDeviceId,
                                                 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;
    
        // Explicit routing?
        sp<DeviceDescriptor> deviceDesc;
        for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
            if (mAvailableInputDevices[i]->getId() == selectedDeviceId) {
                deviceDesc = mAvailableInputDevices[i];
                break;
            }
        }
        mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
    
        if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
                strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
            status_t ret = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
            if (ret != NO_ERROR) {
                return ret;
            }
            *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
            device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
            address = String8(attr->tags + strlen("addr="));
        } 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 if (device == AUDIO_DEVICE_IN_TELEPHONY_RX) {
                *inputType = API_INPUT_TELEPHONY_RX;
            } 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;
                }
            }
        }
    
        // find a compatible input profile (not necessarily identical in parameters)
        sp<IOProfile> profile;
        // samplingRate and flags may be updated by getInputProfile
        uint32_t profileSamplingRate = samplingRate;
        audio_format_t profileFormat = format;
        audio_channel_mask_t profileChannelMask = channelMask;
        audio_input_flags_t profileFlags = flags;
        for (;;) {
    /**
    对当前确认好的设备id和配置地址flags进行功能检查
    */
            profile = getInputProfile(device, address,
                                      profileSamplingRate, profileFormat, profileChannelMask,
                                      profileFlags);
            if (profile != 0) {
                break; // success
            } else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
                profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
            } else { // fail
                ALOGW("getInputForAttr() could not find profile for device 0x%X, samplingRate %u,"
                        "format %#x, channelMask 0x%X, flags %#x",
                        device, samplingRate, format, channelMask, flags);
                return BAD_VALUE;
            }
        }
    
        if (profile->getModuleHandle() == 0) {
            ALOGE("getInputForAttr(): HW module %s not opened", profile->getModuleName());
            return NO_INIT;
        }
    
        audio_config_t config = AUDIO_CONFIG_INITIALIZER;
        config.sample_rate = profileSamplingRate;
        config.channel_mask = profileChannelMask;
        config.format = profileFormat;
        /**
        mpClientInterface就是AudioFlinger的客户端形式接口
      */
        status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
                                                       input,
                                                       &config,
                                                       &device,
                                                       address,
                                                       halInputSource,
                                                       profileFlags);
    
        // only accept input with the exact requested set of parameters
        if (status != NO_ERROR || *input == AUDIO_IO_HANDLE_NONE ||
            (profileSamplingRate != config.sample_rate) ||
            (profileFormat != config.format) ||
            (profileChannelMask != 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 = profileSamplingRate;
        inputDesc->mFormat = profileFormat;
        inputDesc->mChannelMask = profileChannelMask;
        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;
    }
    

    回到AudioFlinger

    status_t AudioFlinger::openInput(audio_module_handle_t module,
                                              audio_io_handle_t *input,
                                              audio_config_t *config,
                                              audio_devices_t *devices,
                                              const String8& address,
                                              audio_source_t source,
                                              audio_input_flags_t flags)
    {
        Mutex::Autolock _l(mLock);
    
        if (*devices == AUDIO_DEVICE_NONE) {
            return BAD_VALUE;
        }
    
        sp<RecordThread> thread = openInput_l(module, input, config, *devices, address, source, flags);
    
        if (thread != 0) {
            // notify client processes of the new input creation
            thread->ioConfigChanged(AUDIO_INPUT_OPENED);
            return NO_ERROR;
        }
        return NO_INIT;
    }
    

    根据input, audio_config_t,audio_devices_t, audio_input_flags_t,audio_source_t进行HAL打开输入流audio_stream_in_t *inStream的同时创建一个RecordThread管理这个输入流。

    sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t module,
                                                             audio_io_handle_t *input,
                                                             audio_config_t *config,
                                                             audio_devices_t devices,
                                                             const String8& address,
                                                             audio_source_t source,
                                                             audio_input_flags_t flags)
    {
        AudioHwDevice *inHwDev = findSuitableHwDev_l(module, devices);
        .......
        audio_hw_device_t *inHwHal = inHwDev->hwDevice();
        audio_stream_in_t *inStream = NULL;
        status_t status = inHwHal->open_input_stream(inHwHal, *input, devices, &halconfig,
                                            &inStream, flags, address.string(), source);
        ......
        if (status == NO_ERROR && inStream != NULL) {
    /**
    tee_sink功能是官方开放用来拦截音频流管道的原始PCM数据,并且可以定义生成wav文件。官方功能使用自行百度,如果单是PCM数据想要截取的话,还不如直接在/av/services/audioflinger/Threads.cpp改代码RecordThread或PlaybackThread来read出PCM数据再存放到固定文件。
    */
    #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
         ......
            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);
            /*
        tee sink 功能需要的管道
        */
                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单纯一个结构体存放inHwDev和inStream的
          */
            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(),
                                      devices,
                                      mSystemReady
    #ifdef TEE_SINK
                                      , teeSink
    #endif
                                      );
        /*  把当前的拿到的RecordThread依赖audio_io_handle_t input存到键值表KeyedVector里,等到客户端后续需要调用该线程做start,stop操作时,可以凭自身的input在mRecordThreads取得。
    */
            mRecordThreads.add(*input, thread);
            return thread;
        }
    }
    

    RecordThread类在/av/services/audioflinger/Thread.cpp里面,顺便一提播放音频线程PlaybackThread类也在Thread.cpp。
    完成RecordThread录音线程创建和HAL输入流打开的后,
    应用进程里面的Jni里的AudioRecord对象的初始化AudioSystem::getInputForAttr接口算是走完了。此时的AudioRecord初始化还并没有结束,还有

    sp<IAudioRecord> record = audioFlinger->openRecord(input,mSampleRate,mFormat, mChannelMask,opPackageName,
      &temp,&trackFlags, tid,mClientUid,&mSessionId, &notificationFrames,iMem,
       bufferMem,&status);
    

    IMemory iMem这个IInterface接口参数很重要。IAudioRecord是固定绑定上AudioFlinger操控录音线程RecordThread的接口对象,流程去到

    sp<IAudioRecord> AudioFlinger::openRecord(
            audio_io_handle_t input,
            uint32_t sampleRate,
            audio_format_t format,
            audio_channel_mask_t channelMask,
            const String16& opPackageName,
            size_t *frameCount,
            IAudioFlinger::track_flags_t *flags,
            pid_t tid,
            int clientUid,
            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();
    
        // 通过包名检测权限"android.permission.RECORD_AUDIO"
        if (!recordingAllowed(opPackageName)) {
            ALOGE("openRecord() permission denied: recording not allowed");
            lStatus = PERMISSION_DENIED;
            goto Exit;
        }
       ......
            Mutex::Autolock _l(mLock);
            RecordThread *thread = checkRecordThread_l(input);
      ......
            pid_t pid = IPCThreadState::self()->getCallingPid();
            client = registerPid(pid);
      ......
          /*
     recordTrack是录音线程内部类起核心作用的对象
    */
            // TODO: the uid should be passed in as a parameter to openRecord
            recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
                                                      frameCount, lSessionId, notificationFrames,
                                                      clientUid, flags, tid, &lStatus);
       ......
      /*
    cblk是和共享内存相关的引用
    */
        cblk = recordTrack->getCblk();
        buffers = recordTrack->getBuffers();
      ......
        /**
        recordHandle 继承IAudioRecord,相当返回给应用调用端的客户端一个binder控制RecordTrack接口
      */
        // return handle to client
        recordHandle = new RecordHandle(recordTrack);
        return recordHandle;
    }
    

    上面代码把重要部分保留了,其他省略。checkRecordThread_l函数,按前面说的会根据audio_io_handle_t input来从键值表里拿出对应的录音线程RecordThread。RecordThread还要createRecordTrack_l 创建一个recordTrack。RecordTrack录音轨道里面又新创建了三个关键对象RecordBufferConverter 数据目标格式转换类,AudioRecordServerProxy 被AudioFlinger使用的录音服务代理和ResamplerBufferProvider看名字取义是重采样数据提供者。
    RecordTrack的初始化

       mRecordBufferConverter = new RecordBufferConverter(
                thread->mChannelMask, thread->mFormat, thread->mSampleRate,
                channelMask, format, sampleRate);
        mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
                                                  mFrameSize, !isExternalTrack());
        mResamplerBufferProvider = new ResamplerBufferProvider(this);
    

    为什么说关键,mRecordBufferConverter有个转换接口
    convert(void *dst, AudioBufferProvider *provider, size_t frames)
    在RecordThread的threadLoop里面进行对HAL流读取到原始数据后,会通过mRecordBufferConverter将provider的 getNextBuffer 函数从RecordThread间接拿到原始数据转换成对应目标格式后拷贝到void *dst对应指向的内存。等下会介绍threadLoop函数的开始录音到读取原始数据和结束录音的流程。而mServerProxy的唯一目的是创造一个循环形式使用的内存buffer用于读取或写入录音数据。
    另外RecordTrack初始化时还为App客户端AudioRecord初始化用于共享内存的cblk和buffers申请了共享内存堆。

        cblk = recordTrack->getCblk();
        buffers = recordTrack->getBuffers();
    

    就是RecordTrack申请完毕后返回的IMemory 引用。客户端会靠这个IMemory来read录音数据。返回的RecordHandle对象装载了recordTrack,不用想,这个只是集中外观模式的处理来自客户端的控制录音功能。

    相关文章

      网友评论

        本文标题:android 6.0 AudioRecord 录音流程分析(一

        本文链接:https://www.haomeiwen.com/subject/odxsjqtx.html