Android 7.0 Audio :通话中的音频调用接口

本文详细解析了在通话过程中Audio处理的不同路径,包括通话中打开扬声器的具体流程及DTMF音播放的技术实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于Audio在通话中的处理,根据不同的功能,有不同的调用路径,

 

 

 

1) 通话中 打开speaker 流程   enableSpeaker

 

对于在通话中打开扬声器,走图示1的调用路径,

 

 

在telephony部分,调用流程如下,

InCallAudioManager. enableSpeaker--telecomAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER)/binderIPC-- mInCallService.setAudioRoute(route);-- mPhone.setAudioRoute(route);-- mInCallAdapter.setAudioRoute(route);--mAdapter.setAudioRoute(route);/binder IPC--…--CallsManager.

setAudioRoute --mCallAudioManager.setAudioRoute(route)--CallAudioRouteStateMachine.sendMessageWithSessionInfo / CallAudioRouteStateMachine.USER_SWITCH_SPEAKER/--通过ActiveSpeakerRoute向下..—setSpeakerphoneOn-- mAudioManager.setSpeakerphoneOn(on)

 

这样,就到了Audio系统了,根据之前的章节,就知道接下来就是AM/AService/ASystem了,

AudioManager.setSpeakerphoneOn/ binder IPC--setSpeakerphoneOn --/ mForcedUseForComm = AudioSystem.FORCE_SPEAKER / sendMsg(mAudioHandler,MSG_SET_FORCE_USE, SENDMSG_QUEUE,

               AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0) /内部handler--setForceUse --setForceUseInt_SyncDevices -- AudioSystem.setForceUse/jni-AudioSystem::setForceUse

 

在native部分的调用流程如下,

AudioSystem::setForceUse--AudioPolicyService::setForceUse(AudioPolicyInterfaceImpl.cpp)--AudioPolicyManager::setForceUse

APM处理部分如下,

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage,

                                         audio_policy_forced_cfg_t config)

{

 

    if (mEngine->setForceUse(usage, config) != NO_ERROR) {

        ALOGW("setForceUse() could not set force cfg %d for usage %d", config, usage);

        return;

}

 

    checkA2dpSuspend();

    checkOutputForAllStrategies();

    updateDevicesAndOutputs();

 

    if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {

        audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, true /*fromCache*/);

        updateCallRouting(newDevice);

    }

    for (size_t i = 0; i < mOutputs.size(); i++) {

        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);

        audio_devices_t newDevice = getNewOutputDevice(outputDesc, true /*fromCache*/);

        if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) {

            setOutputDevice(outputDesc, newDevice, (newDevice != AUDIO_DEVICE_NONE));

        }

        if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {

            applyStreamVolumes(outputDesc, newDevice, 0, true);

        }

    }

 

    audio_io_handle_t activeInput = mInputs.getActiveInput();

    if (activeInput != 0) {

        sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput);

        audio_devices_t newDevice = getNewInputDevice(activeInput);

        // Force new input selection if the new device can not be reached via current input

        if (activeDesc->mProfile->getSupportedDevices().types() & (newDevice & ~AUDIO_DEVICE_BIT_IN)) {

            setInputDevice(activeInput, newDevice);

        } else {

            closeInput(activeInput);

        }

    }

}

 

 

 

 

a)mEngine->setForceUse

 

实现在Engine.cpp里面,主要是判断当前对设备设置是否合逻辑,并给mForceUse的成员赋值。

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)

{

    switch(usage) {

    case AUDIO_POLICY_FORCE_FOR_COMMUNICATION:

        if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO &&

            config != AUDIO_POLICY_FORCE_NONE) {

            ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);

            return BAD_VALUE;

        }

        mForceUse[usage] = config;

        break;

 

 

 

b)checkA2dpSuspend

checkA2dpSuspend--AudioPolicyService::AudioPolicyClient::suspendOutput--AudioFlinger::suspendOutput--thread->suspend

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

               void        suspend() { (void) android_atomic_inc(&mSuspended); }

 

最终是给&mSuspended赋值。

 

 

c)checkOutputForStrategy 根据策略类型和当前设置获取设备类型,并最input和output进行处理,最后invalidateStream。

其中,

AudioPolicyManager::checkOutputForStrategy--audio_devices_tAudioPolicyManager::getDeviceForStrategy--mEngine->getDeviceForStrategy,

Engine.cpp里,

根据strategy为STRATEGY_PHONE,以及刚刚设置的mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]为AUDIO_POLICY_FORCE_SPEAKER,匹配到正确的设备类型,刚好为AUDIO_DEVICE_OUT_SPEAKER,

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy,

                                                DeviceVector availableOutputDevices,

                                                DeviceVector availableInputDevices,

                                                const SwAudioOutputCollection &outputs) const

{

    uint32_t device = AUDIO_DEVICE_NONE;

    uint32_t availableOutputDevicesType = availableOutputDevices.types();

 

    switch (strategy) {

    case STRATEGY_PHONE:

 

        switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) {

 

        case AUDIO_POLICY_FORCE_SPEAKER:

            // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to

            // A2DP speaker when forcing to speaker output

 

            device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;

            break;

        }

    break;

 

 

d)之后是updateCallRouting,设置device的操作。

 

 

 

2)dtmf音

 

在telephony模块,调用流程(这里只分析了声音播放,并不包含信令处理)

CallsManager.playDtmfTone --DtmfLocalTonePlayer .playTone--mToneGenerator.startTone

 

 

在Audio模块,调用流程

 

ToneGenerator.startTone--jni--android_media_ToneGenerator_startTone--ToneGenerator::startTone

 

ToneGenerator则直接使用了AT,先initAudioTrack创建AT实例,prepareWave生成PCM数据,再mpAudioTrack->start()开始,

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool ToneGenerator::startTone(tone_type toneType, int durationMs) {

 

toneType = getToneForRegion(toneType);

 

    if (mState == TONE_IDLE) {

        ALOGV("startTone: try to re-init AudioTrack");

        if (!initAudioTrack()) {

            return lResult;

        }

    }

 

    if (mState == TONE_INIT) {

        if (prepareWave()) {

            ALOGV("Immediate start, time %d", (unsigned int)(systemTime()/1000000));

            lResult = true;

            mState = TONE_STARTING;

            if (clock_gettime(CLOCK_MONOTONIC, &mStartTime) != 0) {

                mStartTime.tv_sec = 0;

            }

            mLock.unlock();

            mpAudioTrack->start();

    }

    mLock.unlock();

}

 

 

 

 

可以看到,initAudioTrack创建AT实例时,使用了AUDIO_OUTPUT_FLAG_FAST的方式播放,通过audioCallback回调完成数据填充。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool ToneGenerator::initAudioTrack() {

 

    // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size

    mpAudioTrack = new AudioTrack();

    ALOGV("Create Track: %p", mpAudioTrack.get());

 

    mpAudioTrack->set(mStreamType,

                      0,    // sampleRate

                      AUDIO_FORMAT_PCM_16_BIT,

                      AUDIO_CHANNEL_OUT_MONO,

                      0,    // frameCount

                      AUDIO_OUTPUT_FLAG_FAST,

                      audioCallback,

                      this, // user

                      0,    // notificationFrames

                      0,    // sharedBuffer

                      mThreadCanCallJava,

                      AUDIO_SESSION_ALLOCATE,

                      AudioTrack::TRANSFER_CALLBACK);

 

    if (mpAudioTrack->initCheck() != NO_ERROR) {

        ALOGE("AudioTrack->initCheck failed");

        goto initAudioTrack_exit;

    }

 

    mpAudioTrack->setVolume(mVolume);

 

    mState = TONE_INIT;

 

    return true;

 

initAudioTrack_exit:

 

    ALOGV("Init failed: %p", mpAudioTrack.get());

 

    // Cleanup

    mpAudioTrack.clear();

 

    return false;

}

 

这条通道走的是AF的路径,并不在上图的三个path之中。

 

 

 

3)标记3的场景有,Telephony向Audio设置当前是通话状态,这个会用setMode (MODE_INCALL);向Audio设置当前是VOICE通话还是LTE通话,这个会用AudioSystem::setParameters(),

 

vendor\qcom\proprietary\qcril\qcril_qmi\qcril_am.cc

 

<think>我们正在讨论Android录音机APK的录音机制,特别是音频通道和调用方式。根据引用内容,我们可以总结以下几点: 1.权限要求:录音需要`RECORD_AUDIO`权限,存储需要`WRITE_EXTERNAL_STORAGE`权限(引用[1])。 2.使用`MediaRecorder`类进行录音:设置音频源(如麦克风)、输出格式、编码器、输出文件等(引用[2])。3.音频源(音频通道)设置:通过`setAudioSource`方法设置,常用的是`MediaRecorder.AudioSource.MIC`(麦克风)(引用[2])。4.其他可能的音频源:除了麦克风,Android还支持其他音频源,如语音通话、语音识别等(但标准录音机通常使用麦克风)。5.调用方式:通过Java代码初始化`MediaRecorder`,配置参数,然后开始录音(引用[2]和[4])。 具体到音频通道,Android系统提供了多个音频源(通道),定义在`MediaRecorder.AudioSource`中。其中: -`MIC`:主麦克风。 -`VOICE_UPLINK`:上行通话语音。- `VOICE_DOWNLINK`:下行通话语音。-`VOICE_CALL`:上下行通话语音混合。 -等等。但标准的录音机应用通常使用`MIC`作为音频源。实现步骤大致如下:1.创建`MediaRecorder`实例。 2.设置音频源(`setAudioSource`)。3.设置输出格式(`setOutputFormat`),如`THREE_GPP`。 4.设置音频编码器(`setAudioEncoder`),如`AMR_NB`。 5.设置输出文件路径(`setOutputFile`)。 6.准备(`prepare`)并开始(`start`)录音。此外,引用[3]提到了H5录音,但这不是原生Android的方式,而是Web方式。因此,Android录音机APK录音时使用的音频通道主要是麦克风(`MediaRecorder.AudioSource.MIC`),调用方式是通过`MediaRecorder`API。 下面我们按照要求生成回答。</think>### Android录音机APK录音机制分析 #### 一、音频通道与调用方式 Android录音机APK主要通过`MediaRecorder` API实现录音功能,核心音频通道为**麦克风输入通道**(MIC),调用方式如下: 1. **音频源设置** 使用`setAudioSource()`指定音频通道,标准录音机采用主麦克风通道: ```java mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 主麦克风通道 ``` 其他可选通道包括: - `VOICE_UPLINK`(上行通话) - `VOICE_DOWNLINK`(下行通话) - `VOICE_CALL`(双向通话2. **参数配置流程** 完整调用链包含四个关键步骤: ```java MediaRecorder recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 音频通道 recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); // 输出格式 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 编码格式 recorder.setOutputFile("/sdcard/recording.3gp"); // 存储路径 ``` 3. **权限声明** 需在`AndroidManifest.xml`声明权限: ```xml <uses-permission android:name="android.permission.RECORD_AUDIO"/> <!-- 录音权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 存储权限 --> ``` #### 二、底层实现机制 1. **音频流路径** $$ \text{麦克风} \xrightarrow{\text{ALSA驱动}} \text{AudioFlinger} \xrightarrow{\text{音频处理}} \text{MediaRecorder} $$ 2. **多通道支持** 通过修改音频源可实现多路录音(需硬件支持): ```java // 同时启用多麦克风(需API 23+) recorder.setAudioSource(MediaRecorder.AudioSource.UNPROCESSED); ``` 3. **音频格式兼容性** 常用格式对比如下: | 格式 | 码率 | 适用场景 | |---|---|---| | AMR_NB | 4.75-12.2 kbps | 语音通话 | | AAC_LC | 96-320 kbps | 高质量录音 | | OPUS | 6-510 kbps | 实时流媒体 | #### 三、适配注意事项 1. **Android 7.0+存储适配** 需使用`FileProvider`访问外部存储: ```xml <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider> ``` 2. **运行时权限处理** Android 6.0+需动态申请权限: ```java ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE ); ``` > **关键引用**:标准录音机使用`MediaRecorder.AudioSource.MIC`作为主录音通道,通过`MediaRecorder` API实现音频捕获和编码存储[^2][^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值