主頁 > 移動端開發 > Audio HAL 通話錄音上下行分離

Audio HAL 通話錄音上下行分離

2021-08-15 07:57:22 移動端開發

背景需求

做語音識別和答錄功能的app大都需要將通話錄音上下行VOICE_DOWNLINK/VOICE_UPLINK單獨分離實時轉義識別,

兩個解決方向:

1、AudioRecord 方法的立體聲錄制PCM資料混合音源了,二進制檔案無法區分哪些資料是左聲道,哪些是右聲道,

通過修改AudioRecord立體聲PCM錄制方案,每8位一組資料,左聲道8位在前,右聲道8位在后,

2、使用兩個實體同時運行MediaRecorder的VOICE_UPLINK,VOICE_DOWNLINK錄制上行或下行資料,且資料不重用,

基于這兩個方向我們都需要去看 Audio HAL 代碼,

最終結論

一開始從方向二出發,由于安卓本身的錄音機制同一時間只允許一個應用錄音,首先需要破解這個機制,

Android Audio - 支持多應用同時錄音_Android8.1修改方法 參考這個修改后,確實可以同時 start 兩個 MediaRecorder,但最終問題是不管如何設定 AudioSource

VOICE_DOWNLINK 和 VOICE_UPLINK,保存錄音檔案內容都是一樣的,依舊是上下行混在一起,最終發現底層 buffer 是同一個,所以

保存的資料一樣,這塊嘗試改了好幾天,沒啥進展,具體分析程序看下文,

再嘗試方向一,通過 AudioRecord 獲取原始 pcm 資料按 8 位分離,最終通過這個搞定了!

分析程序

主要的代碼目錄位于

vendor/mediatek/proprietary/hardware/audio/common/V3/

framework 相關的分析這里就不寫了,根據這篇查看

Android 5.1 Audio HAL分析

這里從 adev_open_input_stream() 接入

vendor\mediatek\proprietary\hardware\audio\common\aud_drv\audio_hw_hal.cpp

static int adev_open_input_stream(struct audio_hw_device *dev,
                                      audio_io_handle_t handle,
                                      audio_devices_t devices,
                                      struct audio_config *config,
                                      struct audio_stream_in **stream_in,
                                      audio_input_flags_t flags /*__unused*/,
                                      const char *address __unused,
                                      audio_source_t source /*__unused*/) {
#ifdef AUDIO_HAL_PROFILE_ENTRY_FUNCTION
        AudioAutoTimeProfile _p(__func__);
#endif
        struct legacy_audio_device *ladev = to_ladev(dev);
        status_t status = (status_t) handle;;
        struct legacy_stream_in *in;
        int ret;
        AudioParameter param = AudioParameter();
        param.addInt(String8(AudioParameter::keyInputSource), source);
        in = (struct legacy_stream_in *)calloc(1, sizeof(*in));
        if (!in) {
            return -ENOMEM;
        }
//這里加了log列印,發現走的 openInputStreamWithFlags()
#ifdef UPLINK_LOW_LATENCY
        ALOGD("-%s() %d", __FUNCTION__, __LINE__);
        in->legacy_in = ladev->hwif->openInputStreamWithFlags(devices, (int *) &config->format,
                                                              &config->channel_mask, &config->sample_rate,
                                                              &status, (audio_in_acoustics_t)0, flags);
#else
        ALOGD("-%s() %d", __FUNCTION__, __LINE__);
        in->legacy_in = ladev->hwif->openInputStream(devices, (int *) &config->format,
                                                     &config->channel_mask, &config->sample_rate,
                                                     &status, (audio_in_acoustics_t)0);
#endif
        if (!in->legacy_in) {
            ret = status;
            goto err_open;
        }
        in->legacy_in->setParameters(param.toString());
        in->stream.common.get_sample_rate = in_get_sample_rate;
        in->stream.common.set_sample_rate = in_set_sample_rate;
.....

進入 AudioALSAHardware::openInputStreamWithFlags

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSAHardware.cpp

AudioMTKStreamInInterface *AudioALSAHardware::openInputStreamWithFlags(
    uint32_t devices,
    int *format,
    uint32_t *channels,
    uint32_t *sampleRate,
    status_t *status,
    audio_in_acoustics_t acoustics,
    audio_input_flags_t flags) {
	ALOGD("-%s() %d", __FUNCTION__, __LINE__);
    return mStreamManager->openInputStream(devices, format, channels, sampleRate, status, acoustics, flags);
}

繼續跟進 AudioALSAStreamManager::openInputStream

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSAStreamManager.cpp

AudioMTKStreamInInterface *AudioALSAStreamManager::openInputStream(
    uint32_t devices,
    int *format,
    uint32_t *channels,
    uint32_t *sampleRate,
    status_t *status,
    audio_in_acoustics_t acoustics,
    uint32_t input_flag) {

.....

	 // create stream in
    AudioALSAStreamIn *pAudioALSAStreamIn = new AudioALSAStreamIn();
    audio_devices_t input_device = static_cast<audio_devices_t>(devices);
    bool sharedDevice = (input_device & ~AUDIO_DEVICE_BIT_IN) & (AUDIO_DEVICE_IN_BUILTIN_MIC | AUDIO_DEVICE_IN_BACK_MIC | AUDIO_DEVICE_IN_WIRED_HEADSET);
.....	
	pAudioALSAStreamIn->set(devices, format, channels, sampleRate, status, acoustics, input_flag);

    ALOGD("-%s(), in = %p, status = 0x%x, mStreamInVector.size() = %zu",
          __FUNCTION__, pAudioALSAStreamIn, *status, mStreamInVector.size());
    return pAudioALSAStreamIn;

AudioALSAStreamIn 非常重要,最后干活的將是這個物件,這里將 pAudioALSAStreamIn 回傳到 audio_hw_hal.cpp 中

順序呼叫 in->legacy_in->setParameters(param.toString()),其實呼叫 AudioALSAStreamIn->setParameters()

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSAStreamIn.cpp

status_t AudioALSAStreamIn::setParameters(const String8 &keyValuePairs) {
    ALOGD("+%s(): %s", __FUNCTION__, keyValuePairs.string());
    AudioParameter param = AudioParameter(keyValuePairs);

    /// keys
    const String8 keyInputSource = String8(AudioParameter::keyInputSource);
    const String8 keyRouting     = String8(AudioParameter::keyRouting);

    /// parse key value pairs
    status_t status = NO_ERROR;
    int value = 0;
    int oldCount;
    String8 value_str;
    /// intput source
    if (param.getInt(keyInputSource, value) == NO_ERROR) {
        param.remove(keyInputSource);
        oldCount = android_atomic_inc(&mLockCount);
        // TODO(Harvey): input source
        AL_AUTOLOCK(mLock);
        ALOGV("%s() InputSource = %d", __FUNCTION__, value);
        mStreamAttributeTarget.input_source = static_cast<audio_source_t>(value);

......
        

關鍵的地方在 mStreamAttributeTarget.input_source 這個值即上層 setAudioSource 傳遞對應 audio_source_t 值

system\media\audio\include\system\audio-base.h

typedef enum {
    AUDIO_SOURCE_DEFAULT = 0,
    AUDIO_SOURCE_MIC = 1,
    AUDIO_SOURCE_VOICE_UPLINK = 2,
    AUDIO_SOURCE_VOICE_DOWNLINK = 3,
    AUDIO_SOURCE_VOICE_CALL = 4,
    AUDIO_SOURCE_CAMCORDER = 5,
    AUDIO_SOURCE_VOICE_RECOGNITION = 6,
    AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
    AUDIO_SOURCE_REMOTE_SUBMIX = 8,
    AUDIO_SOURCE_UNPROCESSED = 9,
    AUDIO_SOURCE_CNT = 10,
    AUDIO_SOURCE_MAX = 9, // (CNT - 1)
    AUDIO_SOURCE_FM_TUNER = 1998,
    AUDIO_SOURCE_HOTWORD = 1999,
} audio_source_t;


一系列引數設定完成后,將開始 read 資料,回到 audio_hw_hal.cpp 中

    static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
                           size_t bytes) {
#ifdef AUDIO_HAL_PROFILE_ENTRY_FUNCTION
        AudioAutoTimeProfile _p(__func__, AUDIO_HAL_FUNCTION_READ_NS);
#endif
        struct legacy_stream_in *in =
                reinterpret_cast<struct legacy_stream_in *>(stream);
        return in->legacy_in->read(buffer, bytes);
    }

最侄訓是 AudioALSAStreamIn 干活,ssize_t AudioALSAStreamIn::read(void *buffer, ssize_t bytes)

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSAStreamIn.cpp

ssize_t AudioALSAStreamIn::read(void *buffer, ssize_t bytes) {
    ALOGV("%s(), bytes= %zu", __FUNCTION__, bytes);
    if (tempDebugflag) {
        ALOGD("%s()+, bytes= %zu", __FUNCTION__, bytes);
    }
    ssize_t ret_size = bytes;
    /* fast record is RT thread and keep streamin lock.
    so other thread can't get streamin lock. if necessary, read will active release CPU. */
    int tryCount = 10;
    while ((mLockCount != 0 || mSuspendLockCount != 0) && tryCount--) {
        ALOGV("%s, free CPU, mLockCount = %d, mSuspendLockCount = %d, tryCount %d", __FUNCTION__, mLockCount, mSuspendLockCount, tryCount);
        usleep(300);
        if (tryCount == 0) {
            ALOGD("%s, free CPU, mLockCount = %d, mSuspendLockCount = %d, tryCount %d", __FUNCTION__, mLockCount, mSuspendLockCount, tryCount);
        }
    }

....
	    /// check open
        if (mStandby == true) {
            status = open();
        }

看到這 open() 方法,這個方法有點東西

status_t AudioALSAStreamIn::open() {
    // call open() only when mLock is locked.
    ASSERT(AL_TRYLOCK(mLock) != 0);

    ALOGD("%s()", __FUNCTION__);

    status_t status = NO_ERROR;

    if (mStandby == true) {
        // create capture handler
        ASSERT(mCaptureHandler == NULL);
        mCaptureHandler = mStreamManager->createCaptureHandler(&mStreamAttributeTarget);
        if (mCaptureHandler == NULL) {
            status = BAD_VALUE;
            return status;
        }
        mStandby = false;

        // open audio hardware
        status = mCaptureHandler->open();
        ASSERT(status == NO_ERROR);

        OpenPCMDump();
    }

    return status;
}

mStreamManager->createCaptureHandler 將剛剛的引數一并傳遞

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSAStreamManager.cpp

AudioALSACaptureHandlerBase *AudioALSAStreamManager::createCaptureHandler(
    stream_attribute_t *stream_attribute_target) {
    ALOGD("+%s(), mAudioMode = %d, input_source = %d, input_device = 0x%x, mBypassDualMICProcessUL=%d, sample_rate=%d",
          __FUNCTION__, mAudioMode, stream_attribute_target->input_source, stream_attribute_target->input_device, mBypassDualMICProcessUL, stream_attribute_target->sample_rate);
    //AL_AUTOLOCK(mLock);

.....


        else if ((stream_attribute_target->input_source == AUDIO_SOURCE_VOICE_UPLINK) || (stream_attribute_target->input_source == AUDIO_SOURCE_VOICE_DOWNLINK) ||
                 (stream_attribute_target->input_source == AUDIO_SOURCE_VOICE_CALL) ||
                 ((isModeInPhoneCall() == true) && (WCNChipController::GetInstance()->IsBTMergeInterfaceSupported() == false) && (stream_attribute_target->input_device == AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET))) {
            if ((isModeInPhoneCall() == false) ||
                (SpeechDriverFactory::GetInstance()->GetSpeechDriver()->GetApSideModemStatus(SPEECH_STATUS_MASK) == false)) {
                ALOGD("Can not open PhoneCall Record now !! Because it is not PhoneCallMode or speech driver is not ready, return NULL");
                AL_UNLOCK(mLock);
                return NULL;
            }
            pCaptureHandler = new AudioALSACaptureHandlerVoice(stream_attribute_target);

當前正在通話中,且音頻源為 VOICE_CALL、VOICE_UPLINK、VOICE_DOWNLINK,初始化 AudioALSACaptureHandlerVoice 并 return

回到剛剛 AudioALSAStreamIn::open 中,往下順序呼叫 mCaptureHandler->open();

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSACaptureHandlerVoice.cpp

status_t AudioALSACaptureHandlerVoice::open() {
    ALOGD("+%s(), input_device = 0x%x, input_source = 0x%x",
          __FUNCTION__, mStreamAttributeTarget->input_device, mStreamAttributeTarget->input_source);
    //+open(), input_device = 0x80000004, input_source = 0x3
    //+open(), input_device = 0x80000080, input_source = 0x3
    ASSERT(mCaptureDataClient == NULL);

    AudioALSACaptureDataProviderBase *provider = NULL;//

#ifndef LEGACY_VOICE_RECORD
    switch (mStreamAttributeTarget->input_source) {
#ifdef INCALL_DL_RECORD_DISABLED
    default: {
        provider = AudioALSACaptureDataProviderVoiceUL::getInstance();
        break;
    }
#else
    case AUDIO_SOURCE_VOICE_DOWNLINK: {
        provider = AudioALSACaptureDataProviderVoiceDL::getInstance();
        break;
    }
    case AUDIO_SOURCE_VOICE_UPLINK: {
        provider = AudioALSACaptureDataProviderVoiceUL::getInstance();
        break;
    }
    case AUDIO_SOURCE_VOICE_CALL: {
        provider = AudioALSACaptureDataProviderVoiceMix::getInstance();
        break;
    }
    default: {
        provider = AudioALSACaptureDataProviderVoiceUL::getInstance();
        break;
    }
#endif
    }
#else
    // legacy voice record
    provider = AudioALSACaptureDataProviderVoice::getInstance();
#endif


#if defined(MTK_AURISYS_FRAMEWORK_SUPPORT)
    mCaptureDataClient = new AudioALSACaptureDataClientAurisysNormal(provider, mStreamAttributeTarget, NULL);
#else
    mCaptureDataClient = new AudioALSACaptureDataClient(provider, mStreamAttributeTarget);
#endif

    ALOGD("-%s()", __FUNCTION__);
    return NO_ERROR;
}

來到這里終于有點意思了,看到根據不同的音頻源,實體化對應的 AudioALSACaptureDataProvider

這里加了log列印,發現同時開啟兩個錄音執行緒,音頻源分別設定 VOICE_UPLINK VOICE_DOWNLINK,但列印的

都是最后一個,也就是前面的被覆寫了,繼續往下將 provider 傳參到 new AudioALSACaptureDataClient(provider, mStreamAttributeTarget);

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSACaptureDataClient.cpp

AudioALSACaptureDataClient::AudioALSACaptureDataClient(AudioALSACaptureDataProviderBase *pCaptureDataProvider, stream_attribute_t *stream_attribute_target) :
    mCaptureDataProvider(pCaptureDataProvider),
    mAudioSpeechEnhanceInfoInstance(AudioSpeechEnhanceInfo::getInstance()),
    mStreamAttributeSource(mCaptureDataProvider->getStreamAttributeSource()),
    mStreamAttributeTarget(stream_attribute_target),
    mBliSrc(NULL),
    mMicMute(false),
    mMuteTransition(false),
    mSPELayer(NULL),
    mAudioALSAVolumeController(AudioVolumeFactory::CreateAudioVolumeController()),
    mChannelRemixOp(CHANNEL_REMIX_NOP),
    mBesRecTuningEnable(false),
    //echoref+++
    mCaptureDataProviderEchoRef(NULL),
    mStreamAttributeSourceEchoRef(NULL),
    mStreamAttributeTargetEchoRef(NULL),
    mBliSrcEchoRef(NULL),
    mBliSrcEchoRefBesRecord(NULL)
    //echoref---
{
    ALOGD("%s()", __FUNCTION__);

    // init member struct
    memset((void *)&mEchoRefRawDataBuf, 0, sizeof(mEchoRefRawDataBuf));
    memset((void *)&mEchoRefSrcDataBuf, 0, sizeof(mEchoRefSrcDataBuf));

    // raw data
    memset((void *)&mRawDataBuf, 0, sizeof(mRawDataBuf));
    mRawDataBuf.pBufBase = new char[kClientBufferSize];
    mRawDataBuf.bufLen   = kClientBufferSize;
    mRawDataBuf.pRead    = mRawDataBuf.pBufBase;
    mRawDataBuf.pWrite   = mRawDataBuf.pBufBase;
    ASSERT(mRawDataBuf.pBufBase != NULL);
....

 // init SRC
    if (mStreamAttributeSource->sample_rate != mStreamAttributeTarget->sample_rate) {
        ALOGD("sample_rate: %d => %d, num_channels: %d => %d, audio_format: 0x%x => 0x%x",
              mStreamAttributeSource->sample_rate, mStreamAttributeTarget->sample_rate,
              mStreamAttributeSource->num_channels, mStreamAttributeSource->num_channels,
              mStreamAttributeSource->audio_format, mStreamAttributeTarget->audio_format);

        SRC_PCM_FORMAT  SrcFormat = mStreamAttributeTarget->audio_format == AUDIO_FORMAT_PCM_16_BIT ? SRC_IN_Q1P15_OUT_Q1P15 : SRC_IN_Q1P31_OUT_Q1P31;
        mBliSrc = newMtkAudioSrc(
                      mStreamAttributeSource->sample_rate, mStreamAttributeSource->num_channels,
                      mStreamAttributeTarget->sample_rate, mStreamAttributeSource->num_channels,
                      SrcFormat);
        mBliSrc->open();
    }
    if (mStreamAttributeTarget->BesRecord_Info.besrecord_enable) {
        //move CheckNeedBesRecordSRC to here for mStreamAttributeSource info
        CheckNeedBesRecordSRC();
    }

    CheckChannelRemixOp();
    //debug++
    bTempDebug = mAudioSpeechEnhanceInfoInstance->GetDebugStatus();
    //debug--
}

一頓各種初始化完成后,朋友們我們終于要開始錄音了, mBliSrc->open();

回到剛剛對應的 Provider,這里以 AudioALSACaptureDataProviderVoiceUL 為例

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSACaptureDataProviderVoiceUL.cpp

status_t AudioALSACaptureDataProviderVoiceUL::open() {
    ALOGD("%s()", __FUNCTION__);
    ASSERT(mEnable == false);

    SpeechDataProcessingHandler::getInstance()->getStreamAttributeSource(SpeechDataProcessingHandler::VOICE_UL_CALLER, &mStreamAttributeSource);
    mPeriodBufferSize = getPeriodBufSize(&mStreamAttributeSource, UPLINK_NORMAL_LATENCY_MS);
    mRawDataRingBuf.bufLen = mPeriodBufferSize * 4;

    /* Setup ringbuf */
    mRawDataRingBuf.pBufBase = new char[mRawDataRingBuf.bufLen];
    mRawDataRingBuf.pRead    = mRawDataRingBuf.pBufBase;
    mRawDataRingBuf.pWrite   = mRawDataRingBuf.pBufBase;
    mRawDataRingBuf.pBufEnd  = mRawDataRingBuf.pBufBase + mRawDataRingBuf.bufLen;

    ALOGD("%s(), mStreamAttributeSource: audio_format = %d, num_channels = %d, audio_channel_mask = %x, sample_rate = %d, periodBufferSize = %d\n",
          __FUNCTION__, mStreamAttributeSource.audio_format, mStreamAttributeSource.num_channels, mStreamAttributeSource.audio_channel_mask, mStreamAttributeSource.sample_rate, mPeriodBufferSize);

    mEnable = true;

    OpenPCMDump(LOG_TAG);

    return SpeechDataProcessingHandler::getInstance()->recordOn(RECORD_TYPE_UL);
}

這里 SpeechDataProcessingHandler 很關鍵,從字面理解為處理語音資料 handler

來看下 SpeechDataProcessingHandler::getInstance() 都干了啥

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\SpeechDataProcessingHandler.cpp

SpeechDataProcessingHandler::SpeechDataProcessingHandler() {
    ALOGD("+%s()", __FUNCTION__);

    // Init thread resources
    mStopThreadFlag = false;
    mBliSrcUL = NULL;
    mBliSrcDL = NULL;
    mSrcSampleRateUL = 0;
    mSrcSampleRateDL = 0;
    mSpeechRecordOn = false;

    int ret;

    ret = pthread_cond_init(&mSpeechDataNotifyEvent, NULL);
    if (ret != 0) {
        ALOGE("mSpeechDataNotifyEvent create fail!!!");
    }

    ret = pthread_mutex_init(&mSpeechDataNotifyMutex, NULL);
    if (ret != 0) {
        ALOGE("nSpeechDataNotifyMutex create fail!!!");
    }

    ret = pthread_create(&mSpeechDataProcessingThread, NULL, SpeechDataProcessingHandler::threadLoop, (void *)this);
    if (ret != 0) {
        ALOGE("mSpeechDataProcessingThread create fail!!!");
    } else {
        ALOGD("mSpeechDataProcessingThread = %lu created", mSpeechDataProcessingThread);
    }

    ALOGD("-%s()", __FUNCTION__);
}

搞了一個執行緒一直在讀取錄音資料 threadLoop() 這個方法尤其重要,加log看具體的執行程序

收到資料后在 processSpeechPacket() 中處理

剛剛的 AudioALSACaptureDataProviderVoiceUL::open() 中最后一句呼叫 recordOn(RECORD_TYPE_UL)

RECORD_TYPE_UL 型別定義在 SpeechType.h 中

vendor\mediatek\proprietary\hardware\audio\common\include\SpeechType.h

enum record_type_t {
    RECORD_TYPE_UL = 0,
    RECORD_TYPE_DL = 1,
    RECORD_TYPE_MIX = 2
};

static int mUserCounter = 0;
status_t SpeechDataProcessingHandler::recordOn(record_type_t type __unused) {
    ALOGD("+%s(),type_record = %d\n", __FUNCTION__, type);
    AL_AUTOLOCK(speechDataProcessingHandlerLock);

    mUserCounter++;
    if (mUserCounter == 1) {
            SpeechDriverFactory::GetInstance()->GetSpeechDriver()->RecordOn(RECORD_TYPE_MIX);
            //SpeechDriverFactory::GetInstance()->GetSpeechDriver()->RecordOn(type);
        ALOGD("%s(), First user, record on.\n", __FUNCTION__);
    } else {
        ALOGD("%s(), Record already on. user = %d\n", __FUNCTION__, mUserCounter);
    }

    ALOGD("-%s()\n", __FUNCTION__);
    return NO_ERROR;
}

可以看到此處并沒有使用剛剛傳遞的 RECORD_TYPE_UL,而是默認混合 RECORD_TYPE_MIX,是我太天真了,以為將此處修改為

type 就能正常錄取 UL 和 DL,繼續進入 SpeechDriver()->RecordOn(RECORD_TYPE_MIX)

vendor\mediatek\proprietary\hardware\audio\common\V3\speech_driver\SpeechDriverLAD.cpp

/*==============================================================================
 *                     Recording Control
 *============================================================================*/
status_t SpeechDriverLAD::RecordOn(record_type_t type_record) {
    ALOGD("%s(), sample_rate = %d, channel = %d, type_record = %d, MSG_A2M_RECORD_RAW_PCM_ON", __FUNCTION__, mRecordSampleRateType, mRecordChannelType, type_record);
    uint16_t param_16bit;

    SetApSideModemStatus(RAW_RECORD_STATUS_MASK);
    mRecordType = type_record;
    pCCCI->SetPcmRecordType(type_record);
    param_16bit = mRecordSampleRateType  | (mRecordChannelType << 4);
    return pCCCI->SendMessageInQueue(pCCCI->InitCcciMailbox(MSG_A2M_RECORD_RAW_PCM_ON, param_16bit, 0));
}

這樣底層開啟錄音后,剛剛的 threadLoop(void *arg) 中將回圈收取資料,然后根據 rawPcmDir 是 RECORD_TYPE_UL

RECORD_TYPE_DL RECORD_TYPE_MIX 判斷對應 Provider 是否存在,存在呼叫 provideModemRecordDataToProvider(ringBuf)

將資料分發出去,

當呼叫 mMediaRecorderd.stop(); mMediaRecorder.reset(); mMediaRecorder.release(); 后,底層將結束錄音,

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSAStreamIn.cpp

回到 AudioALSAStreamIn::close() 中

status_t AudioALSAStreamIn::close() {
    // call close() only when mLock is locked.
    ASSERT(AL_TRYLOCK(mLock) != 0);

    ALOGD("%s()", __FUNCTION__);

    status_t status = NO_ERROR;

    if (mStandby == false) {
        mStandby = true;

        ASSERT(mCaptureHandler != NULL);

        // close audio hardware
        status = mCaptureHandler->close();
        if (status != NO_ERROR) {
            ALOGE("%s(), close() fail!!", __FUNCTION__);
        }

        ClosePCMDump();
        // destroy playback handler
        mStreamManager->destroyCaptureHandler(mCaptureHandler);
        mCaptureHandler = NULL;
    }

    ASSERT(mCaptureHandler == NULL);
    return status;
}

AudioALSACaptureHandlerVoice::close()

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSACaptureHandlerVoice.cpp

delete 釋放指標 mCaptureDataClient

status_t AudioALSACaptureHandlerVoice::close() {
    ALOGD("+%s()", __FUNCTION__);

    ASSERT(mCaptureDataClient != NULL);
    delete mCaptureDataClient;

    ALOGD("-%s()", __FUNCTION__);
    return NO_ERROR;
}

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSACaptureDataClient.cpp

AudioALSACaptureDataClient::~AudioALSACaptureDataClient() {
    ALOGD("%s()", __FUNCTION__);

.....

    if (mBliSrc != NULL) {
        mBliSrc->close();
        deleteMtkAudioSrc(mBliSrc);
        mBliSrc = NULL;
    }

    //TODO: Sam, add here for temp
    //BesRecord+++
    StopBesRecord();
....

mBliSrc->close() 呼叫到 AudioALSACaptureDataProviderVoiceUL::close()

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\AudioALSACaptureDataProviderVoiceUL.cpp

status_t AudioALSACaptureDataProviderVoiceUL::close() {
    ALOGD("%s()", __FUNCTION__);

    mEnable = false;

    delete[] mRawDataRingBuf.pBufBase;
    memset(&mRawDataRingBuf, 0, sizeof(mRawDataRingBuf));

    ClosePCMDump();

    return SpeechDataProcessingHandler::getInstance()->recordOff(RECORD_TYPE_UL);
}

vendor\mediatek\proprietary\hardware\audio\common\V3\aud_drv\SpeechDataProcessingHandler.cpp

status_t SpeechDataProcessingHandler::recordOff(record_type_t type __unused) {
    ALOGD("+%s()\n", __FUNCTION__);
    AL_AUTOLOCK(speechDataProcessingHandlerLock);

    mUserCounter--;
    if (mUserCounter == 0) {
             SpeechDriverFactory::GetInstance()->GetSpeechDriver()->RecordOff(RECORD_TYPE_MIX);
            //SpeechDriverFactory::GetInstance()->GetSpeechDriver()->RecordOff(type);
        ALOGD("%s(), No user, record off!\n", __FUNCTION__);
    } else {
        ALOGD("%s(), Record is still using. user = %d\n", __FUNCTION__, mUserCounter);
    }
    ALOGD("-%s()\n", __FUNCTION__);
#endif
    return NO_ERROR;
}

vendor\mediatek\proprietary\hardware\audio\common\V3\speech_driver\SpeechDriverLAD.cpp

status_t SpeechDriverLAD::RecordOn(record_type_t type_record) {
    ALOGD("%s(), sample_rate = %d, channel = %d, type_record = %d, MSG_A2M_RECORD_RAW_PCM_ON", __FUNCTION__, mRecordSampleRateType, mRecordChannelType, type_record);
    uint16_t param_16bit;

    SetApSideModemStatus(RAW_RECORD_STATUS_MASK);
    mRecordType = type_record;
    pCCCI->SetPcmRecordType(type_record);
    param_16bit = mRecordSampleRateType  | (mRecordChannelType << 4);
    return pCCCI->SendMessageInQueue(pCCCI->InitCcciMailbox(MSG_A2M_RECORD_RAW_PCM_ON, param_16bit, 0));
}

到此流程基本就分析完了,最終通過修改 audio hal 層 + AudioRecord 錄取 VOICE_CALL 通道方案完美解決,

其它

過濾關鍵tag值

AudioALSAStream
AudioALSAStreamIn
AudioALSAHardware
AudioALSACaptureDataProvider

2019-01-01 10:09:57.325 280-954/? D/AudioALSAHardware: +setParameters(): A2dpSuspended=true
2019-01-01 10:09:57.325 280-954/? W/AudioALSAHardware: setParameters(), still have param.size() = 1, remain param = "A2dpSuspended=true"
2019-01-01 10:09:57.325 280-954/? D/AudioALSAHardware: -setParameters(): A2dpSuspended=true 
2019-01-01 10:09:57.953 280-954/? D/AudioALSAHardware: +routing createAudioPatch Mixer->1
2019-01-01 10:09:57.955 280-954/? D/AudioALSAHardwareResourceManager: +startInputDevice(), new_device = 0x80000004, mInputDevice = 0x0, mStartInputDeviceCount = 0, mMicInverse = 0, mNumPhoneMicSupport = 2
2019-01-01 10:09:57.957 280-954/? D/AudioALSAHardwareResourceManager: +startOutputDevice(), new_devices = 0x1, mOutputDevices = 0x0, mStartOutputDevicesCount = 0 SampleRate = 16000
2019-01-01 10:09:58.042 280-954/? D/AudioALSAHardware: +routing createAudioPatch 80000004->Mixer Src 3
2019-01-01 10:09:58.768 280-954/? D/AudioALSAHardware: +routing createAudioPatch Mixer->2
2019-01-01 10:09:58.790 280-954/? D/AudioALSAHardwareResourceManager: +stopOutputDevice(), mOutputDevices = 0x1, mStartOutputDevicesCount = 1
2019-01-01 10:09:58.791 280-954/? D/AudioALSAHardwareResourceManager: +stopInputDevice(), mInputDevice = 0x80000004, stop_device = 0x80000004, mStartInputDeviceCount = 1, mMicInverse = 0, mNumPhoneMicSupport = 2
2019-01-01 10:09:58.794 280-954/? D/AudioALSAHardwareResourceManager: +startInputDevice(), new_device = 0x80000080, mInputDevice = 0x0, mStartInputDeviceCount = 0, mMicInverse = 0, mNumPhoneMicSupport = 2
2019-01-01 10:09:58.796 280-954/? D/AudioALSAHardwareResourceManager: +startOutputDevice(), new_devices = 0x2, mOutputDevices = 0x0, mStartOutputDevicesCount = 0 SampleRate = 16000
2019-01-01 10:09:58.991 280-954/? D/AudioALSAHardware: setVoiceVolume(), volume = 0.258523, mUseTuningVolume = 1
2019-01-01 10:09:59.000 280-954/? D/AudioALSAHardware: +routing createAudioPatch Mixer->2
2019-01-01 10:09:59.027 280-954/? D/AudioALSAHardware: +routing createAudioPatch 80000080->Mixer Src 3
2019-01-01 10:09:59.448 280-954/? D/AudioALSAHardware: +routing createAudioPatch Mixer->1
2019-01-01 10:09:59.470 280-954/? D/AudioALSAHardwareResourceManager: +stopOutputDevice(), mOutputDevices = 0x2, mStartOutputDevicesCount = 1
2019-01-01 10:09:59.479 280-954/? D/AudioALSAHardwareResourceManager: +stopInputDevice(), mInputDevice = 0x80000080, stop_device = 0x80000080, mStartInputDeviceCount = 1, mMicInverse = 0, mNumPhoneMicSupport = 2
2019-01-01 10:09:59.482 280-954/? D/AudioALSAHardwareResourceManager: +startInputDevice(), new_device = 0x80000004, mInputDevice = 0x0, mStartInputDeviceCount = 0, mMicInverse = 0, mNumPhoneMicSupport = 2
2019-01-01 10:09:59.484 280-954/? D/AudioALSAHardwareResourceManager: +startOutputDevice(), new_devices = 0x1, mOutputDevices = 0x0, mStartOutputDevicesCount = 0 SampleRate = 16000
2019-01-01 10:09:59.607 280-954/? D/AudioALSAHardware: setVoiceVolume(), volume = 0.230409, mUseTuningVolume = 1
2019-01-01 10:09:59.610 280-954/? D/AudioALSAHardware: +routing createAudioPatch Mixer->1
2019-01-01 10:09:59.627 280-954/? D/AudioALSAHardware: +routing createAudioPatch 80000004->Mixer Src 3
2019-01-01 10:10:07.965 280-412/? D/AudioALSAHardware: +setParameters(): A2dpSuspended=false

Android 5.1 Audio HAL分析

Android 錄音實作(AudioRecord)

音頻framework與中間層分析

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/293761.html

標籤:其他

上一篇:2021-08-13

下一篇:Android實作反編譯xml資源檔案

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more