主頁 >  其他 > Android 音頻原始碼分析——AndroidRecord錄音(一)

Android 音頻原始碼分析——AndroidRecord錄音(一)

2021-03-01 22:57:12 其他

基于Android 原始碼版本:9.0

java代碼路徑:frameworks/base/media/java/android/media/
jni代碼路徑:frameworks/base/core/jni/
C++代碼路徑:frameworks/av/media/libaudioclient/
audioflinger: frameworks/av/sevices/audioflinger/

由于涉及原始碼比較多,所以只貼出部分,中間由洗掉許多,有興趣的可以自行看原始碼,

1.簡介

使?AudioRecord的錄音流程,分為以下?步

  1. 獲取 創建AudioRecord 所需的buffer size 大小;
  2. 根據?頻設備和AudioRecord引數,創建AudioRecord
  3. 調?AudioRecord.startRecording開始錄音,
  4. 讀取錄制的音頻資料AudioRecord.read(data, 0, bufferSize),
  5. 停止錄音,并釋放;
    主要使用的API
//靜態方法
AudioRecord.getMinBufferSize(sampleRate, channel, audioFormat)
//創建AudioRecord物件
new AudioRecord(MediaRecorder.AudioSource.MIC,
						sampleRate,
						channel,
						AudioFormat.ENCODING_PCM_16BIT,
						bufferSize
				);
audiorecord.startRecording()

2.getMinBufferSize

AudioRecord構造器中需要傳遞一個buffersize,該值需要通過AudioRecord的靜態函式getMinBufferSize獲取,我們先看下getMinBufferSize
getMinBufferSize:靜態方法,用來獲取AudioRecord物件所需的最小緩沖區大小(位元組),

//引數分別是 采樣率、聲道、音頻格式
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
    int channelCount = 0;
    switch (channelConfig) {
   		//省略,這里單生道轉換為1, 雙聲道轉換為2;
    }
	//呼叫native方法,
    int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
    if (size == 0) {
        return ERROR_BAD_VALUE;
    }
    else if (size == -1) {
        return ERROR;
    }
    else {
        return size;
    }
}

接著看jni代碼,android_media_AudioRecord.cpp

// 回傳成功創建AudioRecord實體所需的最小大小,
//如果不支持引陣列合,則回傳0,
//如果查詢buffer size出錯,則回傳-1,
static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
    jint sampleRateInHertz, jint channelCount, jint audioFormat) {
    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
          sampleRateInHertz, channelCount, audioFormat);
    size_t frameCount = 0;
    audio_format_t format = audioFormatToNative(audioFormat);//java 物件轉C++物件
    //呼叫C++ AudioRecord類的getMinFrameCount方法
    status_t result = AudioRecord::getMinFrameCount(&frameCount,
            sampleRateInHertz,
            format,
            audio_channel_in_mask_from_count(channelCount));
    if (result == BAD_VALUE) {
        return 0;
    }
    if (result != NO_ERROR) {
        return -1;
    }
    return frameCount * channelCount * audio_bytes_per_sample(format);
}

C++ AudioRecord.cpp

status_t AudioRecord::getMinFrameCount(
        size_t* frameCount,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask)
{
    if (frameCount == NULL) {
        return BAD_VALUE;
    }
    size_t size;
    status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);
    if (status != NO_ERROR) {
        return status;
    }
    //檢測size是否符合規則
    // We double the size of input buffer for ping pong use of record buffer.
    // Assumes audio_is_linear_pcm(format)
    if ((*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) *
            audio_bytes_per_sample(format))) == 0) {
        return BAD_VALUE;
    }
    return NO_ERROR;
}

AudioSystem 通過binder方式,呼叫AudioFlinger getInputBufferSize函式,
AudioSystem.cpp

status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
        audio_channel_mask_t channelMask, size_t* buffSize)
{
    const sp<AudioFlingerClient> afc = getAudioFlingerClient();
    if (afc == 0) {
        return NO_INIT;
    }
    return afc->getInputBufferSize(sampleRate, format, channelMask, buffSize);
}

AudioFlinger 運行在audioserver系統行程中,與hal層進行互動,

3.初始化AudioRecord

流程:AudioRecord.java ->android.media.AudioRecord.cpp->AudioRecord.cpp->AudioSystem.cpp->IAudioFlinger.cpp->AudioFlinger.cpp

AudioRecord構建器

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
        int bufferSizeInBytes)
throws IllegalArgumentException {
    this((new AudioAttributes.Builder())
                .setInternalCapturePreset(audioSource)
                .build(),
            (new AudioFormat.Builder())
                .setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,
                                    true/*allow legacy configurations*/))
                .setEncoding(audioFormat)
                .setSampleRate(sampleRateInHz)
                .build(),
            bufferSizeInBytes,
            AudioManager.AUDIO_SESSION_ID_GENERATE);
}

創建AudioAttributes、AudioFormat物件,并呼叫AudioRecord另一個構建器,
AudioManager.AUDIO_SESSION_ID_GENERATE表示一個特殊的音頻會話ID,用于指示未知的音頻會話ID,并且framework應生成一個新值,

@SystemApi
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
        int sessionId) throws IllegalArgumentException {
    mRecordingState = RECORDSTATE_STOPPED;
	//..省略部分代碼, 檢查attributes, format
	
	//sampleRate channel等轉換
    ,,,,,
	//檢查buffer size
    audioBuffSizeCheck(bufferSizeInBytes);

    int[] sampleRate = new int[] {mSampleRate};
    int[] session = new int[1];
    session[0] = sessionId;
    //呼叫natvie方法,初始化設備
    //TODO: update native initialization when information about hardware init failure
    //      due to capture device already open is available.
    int initResult = native_setup( new WeakReference<AudioRecord>(this),
            mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
            mAudioFormat, mNativeBufferSizeInBytes,
            session, getCurrentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
    if (initResult != SUCCESS) {
        loge("Error code "+initResult+" when initializing native AudioRecord object.");
        return; // with mState == STATE_UNINITIALIZED
    }
    //初始化成功,獲取底層回傳的 samplerate,及sessionid,
    mSampleRate = sampleRate[0];
    mSessionId = session[0];
    mState = STATE_INITIALIZED;
}

native_setup 對應jni中的android_media_AudioRecord_setup函式,

android_media_AudioRecord_setup

主要包含:

  • 一些引數的轉換、檢查;AudioAttributes sampleRate channelIndexMask fromat
  • 創建C++ 層AudioRecord物件
  • lpRecorder->set 設定引數
  • 更新sessionid, sampleRate到java層
  • 將C++ 層物件保存在 java 某些欄位中
static jint
android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
        jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
        jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
        jlong nativeRecordInJavaObj)
{
   	//.........
	//獲取channel session
    audio_attributes_t *paa = NULL;
    sp<AudioRecord> lpRecorder = 0;
    audiorecord_callback_cookie *lpCallbackData = NULL;
    
    // 判斷是否需要創建C++ 層AudioRecord
    if (nativeRecordInJavaObj == 0) {
    	//.........
		//檢查 AudioAttributes sampleRate channelIndexMask fromat等
        
        size_t bytesPerSample = audio_bytes_per_sample(format);

        if (buffSizeInBytes == 0) {
             ALOGE("Error creating AudioRecord: frameCount is 0.");
            return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
        }
        size_t frameSize = channelCount * bytesPerSample;
        size_t frameCount = buffSizeInBytes / frameSize;

        // 創建C++ 層AudioRecord物件;
        lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));

        // create the callback information:
        // this data will be passed with every AudioRecord callback
        lpCallbackData = new audiorecord_callback_cookie;
        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
        // 我們使用弱參考,以便可以對AudioRecord物件進行垃圾回收,
        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
        lpCallbackData->busy = false;
		//AudioRecord設定引數
        const status_t status = lpRecorder->set(paa->source,
            sampleRateInHertz,
            format,        // word length, PCM
            localChanMask,
            frameCount,
            recorderCallback,// callback_t
            lpCallbackData,// void* user
            0,             // notificationFrames,
            true,          // threadCanCallJava
            sessionId,
            AudioRecord::TRANSFER_DEFAULT,
            flags,
            -1, -1,        // default uid, pid
            paa);

        if (status != NO_ERROR) {
            ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
                    status);
            goto native_init_failure;
        }
    } else { // end if nativeRecordInJavaObj == 0)
        lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
        lpCallbackData = new audiorecord_callback_cookie;
        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
        // we use a weak reference so the AudioRecord object can be garbage collected.
        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
        lpCallbackData->busy = false;
    }
	//設定sessionid, sampleRate到java層
    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    if (nSession == NULL) {
        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
        goto native_init_failure;
    }
    // read the audio session ID back from AudioRecord in case a new session was created during set()
    nSession[0] = lpRecorder->getSessionId();
    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    nSession = NULL;

    {
        const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
        env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
    }

    {   // scope for the lock
        Mutex::Autolock l(sLock);
        sAudioRecordCallBackCookies.add(lpCallbackData);
    }
    // 將新創建的C ++ AudioRecord保存在Java物件的“ nativeRecorderInJavaObj”欄位中
    setAudioRecord(env, thiz, lpRecorder);
    // 將新創建的回呼資訊保存在Java物件的中(在mNativeCallbackCookie中),
    // 以便我們可以呼叫finalize()釋放native中的記憶體
    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
	//回傳狀態
    return (jint) AUDIO_JAVA_SUCCESS;
    // failure:
native_init_failure:
    /.....
    // lpRecorder goes out of scope, so reference count drops to zero
    return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;

AudioRecord.cpp

AudioRecord的構造器比較簡單,只進行了一些變數默認值初始化,

AudioRecord::AudioRecord(const String16 &opPackageName)
    : mActive(false), mStatus(NO_INIT), mOpPackageName(opPackageName),
      mSessionId(AUDIO_SESSION_ALLOCATE),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE)
{}

看一下AudioRecord set函式

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,
        audio_session_t sessionId,
        transfer_type transferType,
        audio_input_flags_t flags,
        uid_t uid,
        pid_t pid,
        const audio_attributes_t* pAttributes,
        audio_port_handle_t selectedDeviceId)
{
    status_t status = NO_ERROR;
    
    // invariant that mAudioRecord != 0 is true only after set() returns successfully
    //檢查是否已經set過了
    if (mAudioRecord != 0) {
        ALOGE("Track already in use");
        status = INVALID_OPERATION;
        goto exit;
    }
	//....
    mOrigFlags = 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()
    }

    // 創建 IAudioRecord
    status = createRecord_l(0 /*epoch*/, mOpPackageName);

    if (status != NO_ERROR) {
        if (mAudioRecordThread != 0) {
            mAudioRecordThread->requestExit();   // see comment in AudioRecord.h
            mAudioRecordThread->requestExitAndWait();
            mAudioRecordThread.clear();
        }
        goto exit;
    }
   //申請audio sessionid
    AudioSystem::acquireAudioSessionId(mSessionId, -1);

    return status;
}
  • 當cbf 回呼 不為null時,開啟一個錄音執行緒AudioRecordThread;
  • 呼叫createRecord_l(0)創建IAudioRecord物件;
  • 如果建立失敗,就銷毀錄音執行緒AudioRecordThread;
  • 建立成功,申請audio sessionId,

接著看一下createRecord_l函式
const sp& audioFlinger = AudioSystem::get_audio_flinger()

  • 獲取IAudioFlinger物件,通過binder進行行程間通信,呼叫到AudioFlinger中;
  • audioFlinger->createRecord 創建IAudioRecord物件,對應AudioFlinger中RecordHandle;
  • 設定IMemory共享記憶體;
  • 更新AudioRecordClientProxy;
status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
{
    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
    IAudioFlinger::CreateRecordInput input;
    IAudioFlinger::CreateRecordOutput output;
    audio_session_t originalSessionId;
    sp<media::IAudioRecord> record;
    void *iMemPointer;
    audio_track_cblk_t* cblk;
    status_t status;

    if (audioFlinger == 0) {
        ALOGE("Could not get audioflinger");
        status = NO_INIT;
        goto exit;
    }
	//.........

	//跨行程,創建record
    record = audioFlinger->createRecord(input,
                                                              output,
                                                              &status);
    if (status != NO_ERROR) {
        ALOGE("AudioFlinger could not create record track, status: %d", status);
        goto exit;
    }
    ALOG_ASSERT(record != 0);
    // 現在,AudioFlinger擁有對I / O句柄的參考,因此我們不再負責釋放它,

	if (output.cblk == 0) {
        ALOGE("Could not get control block");
        status = NO_INIT;
        goto exit;
    }
    iMemPointer = output.cblk ->pointer();
    if (iMemPointer == NULL) {
        ALOGE("Could not get control block pointer");
        status = NO_INIT;
        goto exit;
    }
    cblk = static_cast<audio_track_cblk_t*>(iMemPointer);

    // 共享記憶體中緩沖區的起始地址,
    //緩沖區要么緊接在控制塊之后,要么由服務決定是否位于單獨的區域中,
    void *buffers;
    if (output.buffers == 0) {
        buffers = cblk + 1;
    } else {
        buffers = output.buffers->pointer();
        if (buffers == NULL) {
            ALOGE("Could not get buffer pointer");
            status = NO_INIT;
            goto exit;
        }
    }
	//..................
    // 僅當set()成功回傳后,mAudioRecord!= 0才為真
    if (mAudioRecord != 0) {
        IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
        mDeathNotifier.clear();
    }
    //賦值AudioRecord
    mAudioRecord = record;
    mCblkMemory = output.cblk;
    mBufferMemory = output.buffers;
    IPCThreadState::self()->flushCommands();

    // 我們保留I/O句柄的副本,但不擁有參考
    mInput = output.inputId;
    mRefreshRemaining = true;

    mFrameCount = output.frameCount;
    // If IAudioRecord is re-created, don't let the requested frameCount
    // decrease.  This can confuse clients that cache frameCount().
    if (mFrameCount > mReqFrameCount) {
        mReqFrameCount = mFrameCount;
    }

    // update proxy
    mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize);
    mProxy->setEpoch(epoch);
    mProxy->setMinimum(mNotificationFramesAct);

    mDeathNotifier = new DeathNotifier(this);
    IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);

exit:
    mStatus = status;
    // sp<IAudioTrack> track destructor will cause releaseOutput() to be called by AudioFlinger
    return status;
}

AudioFlinger.cpp

接著看AudioFlinger

  • getInputForAttr 獲取輸入流;
  • checkRecordThread_l 獲取input對應的RecordThread,這個RecordThread是由getInputForAttr創建;
  • createRecordTrack_l創建RecordTrack物件,管理音頻資料;
  • 創建RecordHandle物件并回傳;
sp<media::IAudioRecord> AudioFlinger::createRecord(const CreateRecordInput& input,
                                                   CreateRecordOutput& output,
                                                   status_t *status)
{
    // 不是常規回圈,而是總共最多進行兩次迭代的重試回圈,
     //首先嘗試使用FAST標志,如果失敗,再嘗試使用FAST標志,
     //通過break退出回圈,如果沒有錯誤,則退出,如果錯誤,
     //重新進入范圍時,將洗掉sp <>參考,
     //縮進的缺乏是有意為之,以減少代碼混亂并簡化合并,
    for (;;) {
    // 如果重試,則釋放先前打開的輸入,
    if (output.inputId != AUDIO_IO_HANDLE_NONE) {
        recordTrack.clear();
        AudioSystem::releaseInput(portId);
        output.inputId = AUDIO_IO_HANDLE_NONE;
        output.selectedDeviceId = input.selectedDeviceId;
        portId = AUDIO_PORT_HANDLE_NONE;
    }
    lStatus = AudioSystem::getInputForAttr(&input.attr, &output.inputId,
                                      sessionId,
                                    // FIXME compare to AudioTrack
                                      clientPid,
                                      clientUid,
                                      input.opPackageName,
                                      &input.config,
                                      output.flags, &output.selectedDeviceId, &portId);

    {
        Mutex::Autolock _l(mLock);
        RecordThread *thread = checkRecordThread_l(output.inputId);
        if (thread == NULL) {
            ALOGE("createRecord() checkRecordThread_l failed");
            lStatus = BAD_VALUE;
            goto Exit;
        }
        output.sampleRate = input.config.sample_rate;
        output.frameCount = input.frameCount;
        output.notificationFrameCount = input.notificationFrameCount;
        recordTrack = thread->createRecordTrack_l(client, input.attr, &output.sampleRate,
                                                  input.config.format, input.config.channel_mask,
                                                  &output.frameCount, sessionId,
                                                  &output.notificationFrameCount,
                                                  clientUid, &output.flags,
                                                  input.clientInfo.clientTid,
                                                  &lStatus, portId);
        LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
        // lStatus == BAD_TYPE means FAST flag was rejected: request a new input from
        // audio policy manager without FAST constraint
        if (lStatus == BAD_TYPE) {
            continue;
        }
        if (lStatus != NO_ERROR) {
            goto Exit;
        }

        // Check if one effect chain was awaiting for an AudioRecord to be created on this
        // session and move it to this thread.
        sp<EffectChain> chain = getOrphanEffectChain_l(sessionId);
        if (chain != 0) {
            Mutex::Autolock _l(thread->mLock);
            thread->addEffectChain_l(chain);
        }
        break;
    }
    // End of retry loop.
    // The lack of indentation is deliberate, to reduce code churn and ease merges.
    }

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

    // return handle to client
    recordHandle = new RecordHandle(recordTrack);
    
    *status = lStatus;
    return recordHandle;
}

注意這里回傳RecordHandle物件,并賦值spmedia::IAudioRecord,后續AudioRecord.cpp 中的mAudioRecord直接和RecordHandle通信,

getInputForAttr

通過Binder ,呼叫AudioPlicyService getInputForAttr函式.

status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr,
                                audio_io_handle_t *input,
                                audio_session_t session,
                                pid_t pid,
                                uid_t uid,
                                const String16& opPackageName,
                                const audio_config_base_t *config,
                                audio_input_flags_t flags,
                                audio_port_handle_t *selectedDeviceId,
                                audio_port_handle_t *portId)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return NO_INIT;
    return aps->getInputForAttr(
            attr, input, session, pid, uid, opPackageName,
            config, flags, selectedDeviceId, portId);
}

frameworks\av\services\audiopolicy\service/AudioPolicyInterfaceImpl.cpp
AudioPolicyService::getInputForAttr -》AudioPolicyManager->getInputForAttr

AudioPolicyManager getInputForAttr

  • getDeviceAndMixForInputSource函式獲取audio_device_t 設備,和policymix;
  • getInputForDevice 獲取設備的input流;
status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
                                             audio_io_handle_t *input,
                                             audio_session_t session,
                                             uid_t uid,
                                             const audio_config_base_t *config,
                                             audio_input_flags_t flags,
                                             audio_port_handle_t *selectedDeviceId,
                                             input_type_t *inputType,
                                             audio_port_handle_t *portId)
{
	//.....
	if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
            strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
        status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
        if (status != NO_ERROR) {
            goto error;
        }
        *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
        device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
        address = String8(attr->tags + strlen("addr="));
    } else {
        device = getDeviceAndMixForInputSource(inputSource, &policyMix);
       	//.....
    }
    *input = getInputForDevice(device, address, session, uid, inputSource,
                               config, flags,
                               policyMix);
    if (*input == AUDIO_IO_HANDLE_NONE) {
        status = INVALID_OPERATION;
        goto error;
    }
    inputDevices = mAvailableInputDevices.getDevicesFromType(device);
    *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
            : AUDIO_PORT_HANDLE_NONE;

    return NO_ERROR;
}

接著看getInputForDevice

audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device,
                                                        String8 address,
                                                        audio_session_t session,
                                                        uid_t uid,
                                                        audio_source_t inputSource,
                                                        const audio_config_base_t *config,
                                                        audio_input_flags_t flags,
                                                        const sp<AudioPolicyMix> &policyMix)
{
	//,,,,,
    sp<AudioSession> audioSession = new AudioSession(session,
                                                     inputSource,
                                                     config->format,
                                                     samplingRate,
                                                     config->channel_mask,
                                                     flags,
                                                     uid,
                                                     isSoundTrigger,
                                                     policyMix, mpClientInterface);
                                                     
    sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile, mpClientInterface);

    audio_config_t lConfig = AUDIO_CONFIG_INITIALIZER;
    lConfig.sample_rate = profileSamplingRate;
    lConfig.channel_mask = profileChannelMask;
    lConfig.format = profileFormat;

    if (address == "") {
        DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(device);
        // the inputs vector must be of size >= 1, but we don't want to crash here
        address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress : String8("");
    }

    status_t status = inputDesc->open(&lConfig, device, address,
            halInputSource, profileFlags, &input);

    // only accept input with the exact requested set of parameters
    if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE ||
        (profileSamplingRate != lConfig.sample_rate) ||
        !audio_formats_match(profileFormat, lConfig.format) ||
        (profileChannelMask != lConfig.channel_mask)) {
        ALOGW("getInputForAttr() failed opening input: sampling rate %d"
              ", format %#x, channel mask %#x",
              profileSamplingRate, profileFormat, profileChannelMask);
        if (input != AUDIO_IO_HANDLE_NONE) {
            inputDesc->close();
        }
        return AUDIO_IO_HANDLE_NONE;
    }

    inputDesc->mPolicyMix = policyMix;
    inputDesc->addAudioSession(session, audioSession);

    addInput(input, inputDesc);
    mpClientInterface->onAudioPortListUpdate();

    return input;
}

inputDesc->open對應AudioInputDescriptor open函式

status_t AudioInputDescriptor::open(const audio_config_t *config,
                                       audio_devices_t device,
                                       const String8& address,
                                       audio_source_t source,
                                       audio_input_flags_t flags,
                                       audio_io_handle_t *input)
{
    audio_config_t lConfig;
    if (config == nullptr) {
        lConfig = AUDIO_CONFIG_INITIALIZER;
        lConfig.sample_rate = mSamplingRate;
        lConfig.channel_mask = mChannelMask;
        lConfig.format = mFormat;
    } else {
        lConfig = *config;
    }
    mDevice = device;

    status_t status = mClientInterface->openInput(mProfile->getModuleHandle(),
                                                  input,
                                                  &lConfig,
                                                  &mDevice,
                                                  address,
                                                  source,
                                                  flags);

    if (status == NO_ERROR) {
        mSamplingRate = lConfig.sample_rate;
        mChannelMask = lConfig.channel_mask;
        mFormat = lConfig.format;
        mId = AudioPort::getNextUniqueId();
        mIoHandle = *input;
        mProfile->curOpenCount++;
    }

    return status;
}

mClientInterface->openInput
frameworks\av\services\audiopolicy\service/AudioPolicyClientImpl.cpp

status_t AudioPolicyService::AudioPolicyClient::openInput(audio_module_handle_t module,
                                                          audio_io_handle_t *input,
                                                          audio_config_t *config,
                                                          audio_devices_t *device,
                                                          const String8& address,
                                                          audio_source_t source,
                                                          audio_input_flags_t flags)
{
    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
    if (af == 0) {
        ALOGW("%s: could not get AudioFlinger", __func__);
        return PERMISSION_DENIED;
    }

    return af->openInput(module, input, config, device, address, source, flags);
}

呼叫AudioFlinger的openInput函式

  • findSuitableHwDev_l獲取hw模塊;
  • inHwHal->openInputStream hal層 打開輸入流;
  • 創建AudioStreamIn物件;
  • 創建RecordThread執行緒,并保存;
    hal層代碼暫不分析,
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<ThreadBase> 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;
}

sp<AudioFlinger::ThreadBase> 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);
    if (inHwDev == NULL) {
        *input = AUDIO_IO_HANDLE_NONE;
        return 0;
    }

    // Audio Policy can request a specific handle for hardware hotword.
    // The goal here is not to re-open an already opened input.
    // It is to use a pre-assigned I/O handle.
    if (*input == AUDIO_IO_HANDLE_NONE) {
        *input = nextUniqueId(AUDIO_UNIQUE_ID_USE_INPUT);
    } else if (audio_unique_id_get_use(*input) != AUDIO_UNIQUE_ID_USE_INPUT) {
        ALOGE("openInput_l() requested input handle %d is invalid", *input);
        return 0;
    } else if (mRecordThreads.indexOfKey(*input) >= 0) {
        // This should not happen in a transient state with current design.
        ALOGE("openInput_l() requested input handle %d is already assigned", *input);
        return 0;
    }

    audio_config_t halconfig = *config;
    sp<DeviceHalInterface> inHwHal = inHwDev->hwDevice();
    sp<StreamInHalInterface> inStream;
    status_t status = inHwHal->openInputStream(
            *input, devices, &halconfig, flags, address.string(), source, &inStream);

    // If the input could not be opened with the requested parameters and we can handle the
    // conversion internally, try to open again with the proposed parameters.
    if (status == BAD_VALUE &&
        audio_is_linear_pcm(config->format) &&
        audio_is_linear_pcm(halconfig.format) &&
        (halconfig.sample_rate <= AUDIO_RESAMPLER_DOWN_RATIO_MAX * config->sample_rate) &&
        (audio_channel_count_from_in_mask(halconfig.channel_mask) <= FCC_8) &&
        (audio_channel_count_from_in_mask(config->channel_mask) <= FCC_8)) {
        // FIXME describe the change proposed by HAL (save old values so we can log them here)
        ALOGV("openInput_l() reopening with proposed sampling rate and channel mask");
        inStream.clear();
        status = inHwHal->openInputStream(
                *input, devices, &halconfig, flags, address.string(), source, &inStream);
        // FIXME log this new status; HAL should not propose any further changes
    }

    if (status == NO_ERROR && inStream != 0) {
        AudioStreamIn *inputStream = new AudioStreamIn(inHwDev, inStream, flags);
        if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0) {
            sp<MmapCaptureThread> thread =
                    new MmapCaptureThread(this, *input,
                                          inHwDev, inputStream,
                                          primaryOutputDevice_l(), devices, mSystemReady);
            mMmapThreads.add(*input, thread);
            ALOGV("openInput_l() created mmap capture thread: ID %d thread %p", *input,
                    thread.get());
            return thread;
        } else {

            // 創建錄音執行緒RecordThread需要輸入和輸出設備指示,以轉發到音頻預處理模塊
            sp<RecordThread> thread = new RecordThread(this,
                                      inputStream,
                                      *input,
                                      primaryOutputDevice_l(),
                                      devices,
                                      mSystemReady
                                      );
            mRecordThreads.add(*input, thread);
            ALOGV("openInput_l() created record thread: ID %d thread %p", *input, thread.get());
            return thread;
        }
    }

    *input = AUDIO_IO_HANDLE_NONE;
    return 0;
}

4.startRecording

開啟錄音

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

    // start recording
    synchronized(mRecordingStateLock) {
        if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
            handleFullVolumeRec(true);
            mRecordingState = RECORDSTATE_RECORDING;
        }
    }
}

接著native_start看jni,

static jint
android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
{
	//獲取之前保存的C++ AudioRecord指標
    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    if (lpRecorder == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return (jint) AUDIO_JAVA_ERROR;
    }

    return nativeToJavaStatus(
            lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
}

呼叫C++ AudioRecord start方法,并將status 轉換為java狀態回傳,
接著看AudioRecord start

status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t triggerSession)
{
    AutoMutex lock(mLock);

    status_t status = NO_ERROR;
    if (!(flags & CBLK_INVALID)) {
        status = mAudioRecord->start(event, triggerSession).transactionError();
        if (status == DEAD_OBJECT) {
            flags |= CBLK_INVALID;
        }
    }
    if (flags & CBLK_INVALID) {
        status = restoreRecord_l("start");
    }
    return status;
}

mAudioRecord.start -.>RecordHandle::start->spRecordThread::RecordTrack start

RecordHandle和RecordThread::RecordTrack 代碼對應
frameworks/av/sevices/audioflinger/Tracks.cpp

status_t AudioFlinger::RecordThread::RecordTrack::start(AudioSystem::sync_event_t event,
                                                        audio_session_t triggerSession)
{
    sp<ThreadBase> thread = mThread.promote();
    if (thread != 0) {
        RecordThread *recordThread = (RecordThread *)thread.get();
        return recordThread->start(this, event, triggerSession);
    } else {
        return BAD_VALUE;
    }
}

RecordThread
frameworks/av/sevices/audioflinger/Threads.cpp

status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack,
                                           AudioSystem::sync_event_t event,
                                           audio_session_t triggerSession)
{
    //....
    {
        status = AudioSystem::startInput(recordTrack->portId(), &silenced);
    }
    return status;
}

接著看AudioSystem startInput

status_t AudioSystem::startInput(audio_port_handle_t portId, bool *silenced)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;
    return aps->startInput(portId, silenced);
}

AudioPolicyIntefaceImpl.cpp
frameworks/av/sevices/audiopolicy/service/
AudioPolicyService::startInput -》mAudioPolicyManager->startInput

status_t AudioPolicyManager::startInput(audio_io_handle_t input,
                                        audio_session_t session,
                                        bool silenced,
                                        concurrency_type__mask_t *concurrency)
{
	//獲取輸入設備
    sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);

    // 確保我們從正確的靜音狀態開始
    audioSession->setSilenced(silenced);
    // 在呼叫下面的getNewInputDevice()之前增加活動計數,因為只考慮活動會話進行設備選擇
    audioSession->changeActiveCount(1);

    // Routing?
    mInputRoutes.incRouteActivity(session);
    if (audioSession->activeCount() == 1 || mInputRoutes.getAndClearRouteChanged(session)) {
        // 如果從主硬體模塊上的麥克風開始捕獲,則指示對聲音觸發服務進行了有效捕獲
        audio_devices_t device = getNewInputDevice(inputDesc);
        setInputDevice(input, device, true /* force */);
		//計數加1
        status_t status = inputDesc->start();
        if (status != NO_ERROR) {
            mInputRoutes.decRouteActivity(session);
            audioSession->changeActiveCount(-1);
            return status;
        }
        //....
    }

    return NO_ERROR;
}
  • setInputDevice 設定input 設備
  • RecordThread執行緒 start

startRecording函式中,建立起了錄音通道路由route,并且開啟了錄音執行緒,并把錄音資料從驅動中讀取到AudioBuffer環形緩沖區來,此時錄音設備節點已經被open了,并開始read資料了

AudioRecord.read 及stop 后續繼續更新;

參考:https://www.cnblogs.com/pngcui/p/10016563.html

歡迎大家關注、評論、點贊、打賞,
你們的支持是我堅持的動力,Thank you!

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

標籤:其他

上一篇:無需Avatarify 無需剪輯工具 一鍵生成多人版 “螞蟻呀嘿“視頻

下一篇:高通QCA9880無線板卡使用ath10k驅動,5GHz 的AP模式無法跑起來

標籤雲
其他(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)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more