Android 音频策略(音频优先级,音频输入输入,声音优先级)

本文详细解释了Android系统中音频设备的枚举及其在Engine.cpp中的输出和输入策略,特别关注了通话策略下的设备选择和如何调整音频输出优先级。

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

1、音频设备

音频设备(/system/core/include/system/audio.h

enum {
    AUDIO_DEVICE_NONE                          = 0x0,
    /* reserved bits */
    AUDIO_DEVICE_BIT_IN                        = 0x80000000,
    AUDIO_DEVICE_BIT_DEFAULT                   = 0x40000000,
    /* output devices */这些是输出声音的设备,就是我们听声音的设备
    AUDIO_DEVICE_OUT_EARPIECE                  = 0x1,    // 听筒
    AUDIO_DEVICE_OUT_SPEAKER                   = 0x2,    // 扬声器
    AUDIO_DEVICE_OUT_WIRED_HEADSET             = 0x4,    // 线控耳机,可以通过耳机控制远端播放、暂停、音量调节等功能的耳机
    AUDIO_DEVICE_OUT_WIRED_HEADPHONE           = 0x8,    // 普通耳机,只能听,不能操控播放
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO             = 0x10,   // 单声道蓝牙耳机,十进制16
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET     = 0x20,   // 车载免提蓝牙设备,十进制32
    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT      = 0x40,   // 立体声蓝牙耳机,十进制64
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP            = 0x80,   // 蓝牙耳机
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,  // 十进制256
    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER    = 0x200,  // 十进制512
    AUDIO_DEVICE_OUT_AUX_DIGITAL               = 0x400,  // HDMI输出
    AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET         = 0x800,  // 十进制2048
    AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET         = 0x1000, // 十进制4096
    AUDIO_DEVICE_OUT_USB_ACCESSORY             = 0x2000,
    AUDIO_DEVICE_OUT_USB_DEVICE                = 0x4000, //USB设备
    AUDIO_DEVICE_OUT_REMOTE_SUBMIX             = 0x8000,
    AUDIO_DEVICE_OUT_DEFAULT                   = AUDIO_DEVICE_BIT_DEFAULT,
    /* input devices */ (这里主要是接收声音的设备就是我们对着说话的设备)
    AUDIO_DEVICE_IN_COMMUNICATION         = AUDIO_DEVICE_BIT_IN | 0x1,
    AUDIO_DEVICE_IN_AMBIENT               = AUDIO_DEVICE_BIT_IN | 0x2,
    AUDIO_DEVICE_IN_BUILTIN_MIC           = AUDIO_DEVICE_BIT_IN | 0x4,  //手机自带MIC
    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8,     //蓝牙耳机
    AUDIO_DEVICE_IN_WIRED_HEADSET         = AUDIO_DEVICE_BIT_IN | 0x10,  //3.5MM线性耳机
    AUDIO_DEVICE_IN_AUX_DIGITAL           = AUDIO_DEVICE_BIT_IN | 0x20,
    AUDIO_DEVICE_IN_VOICE_CALL            = AUDIO_DEVICE_BIT_IN | 0x40,
    AUDIO_DEVICE_IN_BACK_MIC              = AUDIO_DEVICE_BIT_IN | 0x80,
    AUDIO_DEVICE_IN_REMOTE_SUBMIX         = AUDIO_DEVICE_BIT_IN | 0x100,
    AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET     = AUDIO_DEVICE_BIT_IN | 0x200,
    AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET     = AUDIO_DEVICE_BIT_IN | 0x400,
    AUDIO_DEVICE_IN_USB_ACCESSORY         = AUDIO_DEVICE_BIT_IN | 0x800,
    AUDIO_DEVICE_IN_USB_DEVICE            = AUDIO_DEVICE_BIT_IN | 0x1000,   //USB耳机
    AUDIO_DEVICE_IN_DEFAULT               = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT,
};

注意:这些设备比较常用的就是HDMI,扬声器,3.5mm圆孔耳机,USB耳机,还有蓝牙耳机。本文也主要针对这些设备的输入和输出策略做出说明。

2、音频策略
Android音频策略主要是在 frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp中,接下来我们看一下如何根据自己的需求在这个文件中调整自己的 音频输入输出策略。

2.1、音频输出策略

音频输出策略是在Engine::getDeviceForStrategyInt这个函数里面:我们主要捡重要的讲

DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
                                              DeviceVector availableOutputDevices,
                                              DeviceVector availableInputDevices,
                                              const SwAudioOutputCollection &outputs) const
{
 DeviceVector devices;
 switch (strategy) {         //这个函数就是在我们播放声音的时候,他负责选择合适的音频输出策略,
                            //所以这里开头就是一个Switch来进行选择
     case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
          ........
     case STRATEGY_SONIFICATION_RESPECTFUL:
          ........
     case STRATEGY_DTMF:
          ........
     case STRATEGY_PHONE:      //这个是比较重要的一个 ,我们在进行腾讯会议,或者打电话通话的时候(也就是语音通话)
                               //一句话:语音通话的声音输出走这个策略
        // Force use of only devices on primary output if:
        // - in call AND
        //   - cannot route from voice call RX OR
        //   - audio HAL version is < 3.0 and TX device is on the primary HW module
        ....省略不重要信息
        // for phone strategy, we first consider the forced use and then the available devices by
        // order of priority
         switch (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)) {
        case AUDIO_POLICY_FORCE_BT_SCO:(蓝牙通话的时候输出会走这里)
            if (!isInCall() || strategy != STRATEGY_DTMF) {
                devices = availableOutputDevices.getDevicesFromType(
                        AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT);
                if (!devices.isEmpty()) break;
            }
            devices = availableOutputDevices.getFirstDevicesFromTypes({
                    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, AUDIO_DEVICE_OUT_BLUETOOTH_SCO});
            注意:上面这行就是常用选择设备函数,都是后面会出现很多这样的形式,这个也是我们调整策略必须要操作的
 
            if (!devices.isEmpty()) break;
            // if SCO device is requested but no SCO device is available, fall back to default case
            FALLTHROUGH_INTENDED;
 
        default:    // FORCE_NONE(如果不是蓝牙通话)
            devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
            if (!devices.isEmpty()) break;
            // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
            if (!isInCall() &&
                    (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
                     outputs.isA2dpSupported()) {
                devices = availableOutputDevices.getFirstDevicesFromTypes({
                        AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
                        AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES});
                if (!devices.isEmpty()) break;
            }
            devices = availableOutputDevices.getFirstDevicesFromTypes({
                    AUDIO_DEVICE_OUT_WIRED_HEADPHONE, AUDIO_DEVICE_OUT_WIRED_HEADSET,
                    AUDIO_DEVICE_OUT_LINE, AUDIO_DEVICE_OUT_USB_HEADSET,
                    AUDIO_DEVICE_OUT_USB_DEVICE});
            //这句就是关键啦,如果你想调整你的音频策略就在这个列表里面去调整,这里就明确了优先级:
         带mic耳机>不带麦的耳机>USB耳机   意思就是如果你同时插着USB耳机和圆孔耳机,然后你又是在打电话,
         ,声音就从圆孔耳机走了。
 
 
重点:如果你想调整音频策略怎么做?直接调整列表里的类型顺序就好啦
 
 
          ......
        }
    break;
 
    case STRATEGY_SONIFICATION:
             ............
    case STRATEGY_ENFORCED_AUDIBLE:
             ..........
    case STRATEGY_ACCESSIBILITY:
             ..........
    case STRATEGY_REROUTING:
             ..........
    case STRATEGY_MEDIA: {         //重点又来啦,这里是播放媒体声音时走的策略
        DeviceVector devices2;
        ........
        .......
        前面省略的都是一堆forceuse,如果没有设置强制使用,那么就走到这句,看到没,用扬声器输出啊
        if (devices2.isEmpty()) {
            devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
        }
        //如果你想要调整音频策略,就照着这个写把列表里的字段换成你想要的设备然后往前面放就好了,他是按照顺序向下执行
的,如果device2有了设备后面的就不执行了。
 
        DeviceVector devices3;
        if (strategy == STRATEGY_MEDIA) {
            // ARC, SPDIF and AUX_LINE can co-exist with others.
            devices3 = availableOutputDevices.getDevicesFromTypes({
                    AUDIO_DEVICE_OUT_HDMI_ARC, AUDIO_DEVICE_OUT_SPDIF, AUDIO_DEVICE_OUT_AUX_LINE});
        }
 
        devices2.add(devices3);
        // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or
        // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise
        devices.add(devices2);
 
        // If hdmi system audio mode is on, remove speaker out of output list. //这里是HDMI输出我们是可以设置HDMI
和扬声器同时输出的,就是在最后面可以或device2 | 多个设备。
        if ((strategy == STRATEGY_MEDIA) &&
            (getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO) ==
                AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) {
            devices.remove(devices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER));
        }
       
        }
        } break;
 
    case STRATEGY_CALL_ASSISTANT:
 
    default:
        ALOGW("getDevicesForStrategy() unknown strategy: %d", strategy);
        break;
    }
 
    if (devices.isEmpty()) {
        ALOGV("getDevicesForStrategy() no device found for strategy %d", strategy);
        sp<DeviceDescriptor> defaultOutputDevice = getApmObserver()->getDefaultOutputDevice();
        if (defaultOutputDevice != nullptr) {
            devices.add(defaultOutputDevice);
        }
        ALOGE_IF(devices.isEmpty(),
                 "getDevicesForStrategy() no default device defined");
    }
    ALOGVV("getDevices ForStrategy() strategy %d, device %s",
           strategy, dumpDeviceTypes(devices.types()).c_str());
    return devices;
}
 
这里就结束了音频的输出策略

2.2、音频输入策略

音频输出策略是在Engine::getDeviceForInputSource这个函数里面:我们主要捡重要的讲

sp<DeviceDescriptor> Engine::getDeviceForInputSource(audio_source_t inputSource) const
{
    const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
    const DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
    const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
    DeviceVector availableDevices = availableInputDevices;
    sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput();
    DeviceVector availablePrimaryDevices = primaryOutput == nullptr ? DeviceVector()
            : availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle());
    sp<DeviceDescriptor> device;
 
    // when a call is active, force device selection to match source VOICE_COMMUNICATION
    // for most other input sources to avoid rerouting call TX audio
    if (isInCall()) {
        switch (inputSource) {
        case AUDIO_SOURCE_DEFAULT:
        case AUDIO_SOURCE_MIC:  //一般会走到这里
        case AUDIO_SOURCE_VOICE_RECOGNITION:
        case AUDIO_SOURCE_UNPROCESSED:
        case AUDIO_SOURCE_HOTWORD:
        case AUDIO_SOURCE_CAMCORDER:
        case AUDIO_SOURCE_VOICE_PERFORMANCE:
            inputSource = AUDIO_SOURCE_VOICE_COMMUNICATION;   //也会走这个策略
            break;
        default:
            break;
        }
    }
 
    switch (inputSource) {
    case AUDIO_SOURCE_DEFAULT:
    case AUDIO_SOURCE_MIC:
        device = availableDevices.getDevice(
                AUDIO_DEVICE_IN_BLUETOOTH_A2DP, String8(""), AUDIO_FORMAT_DEFAULT);
        if (device != nullptr) break;
        if (getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO) {
            device = availableDevices.getDevice(
                    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, String8(""), AUDIO_FORMAT_DEFAULT);
            if (device != nullptr) break;
        }
        device = availableDevices.getFirstExistingDevice({
                AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
                AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
        //打电话应该就是走的mic输入,收录声音就是用的mic,但是可以从列表里面看出来如果有圆孔耳机或者USB耳机,圆孔耳机和
USB耳机的优先级是要比BUILTIN_MIC (麦克风)高的
        break;
 
    case AUDIO_SOURCE_VOICE_COMMUNICATION: //这个策略就是我们进行腾讯会议这类型语音通话类的app进行声音输入时走
的策略
        // Allow only use of devices on primary input if in call and HAL does not support routing
        // to voice call path.
        if ((getPhoneState() == AUDIO_MODE_IN_CALL) &&
                (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX,
                        String8(""), AUDIO_FORMAT_DEFAULT)) == nullptr) {
            LOG_ALWAYS_FATAL_IF(availablePrimaryDevices.isEmpty(), "Primary devices not found");
            availableDevices = availablePrimaryDevices;
        }
 
        switch (getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)) {
        case AUDIO_POLICY_FORCE_BT_SCO:
            // if SCO device is requested but no SCO device is available, fall back to default case
            device = availableDevices.getDevice(
                    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, String8(""), AUDIO_FORMAT_DEFAULT);
            if (device != nullptr) {
                break;
            }
            FALLTHROUGH_INTENDED;
 
        default:    // FORCE_NONE
            device = availableDevices.getFirstExistingDevice({
                    AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_IN_USB_HEADSET,
                    AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_IN_BUILTIN_MIC});
           //起决定性作用的还是这里,如果想要修改顺序,调整列表里的设备即可
            break;
 
        case AUDIO_POLICY_FORCE_SPEAKER:
            device = availableDevices.getFirstExistingDevice({
                    AUDIO_DEVICE_IN_BACK_MIC, AUDIO_DEVICE_IN_BUILTIN_MIC});
            break;
        }
        break;
 
    case AUDIO_SOURCE_VOICE_RECOGNITION:
    case AUDIO_SOURCE_UNPROCESSED:
    case AUDIO_SOURCE_HOTWORD:
    case AUDIO_SOURCE_CAMCORDER:
    case AUDIO_SOURCE_VOICE_DOWNLINK:
    case AUDIO_SOURCE_VOICE_CALL:
    case AUDIO_SOURCE_VOICE_UPLINK:
    case AUDIO_SOURCE_VOICE_PERFORMANCE:
    case AUDIO_SOURCE_REMOTE_SUBMIX:
    case AUDIO_SOURCE_FM_TUNER:
    case AUDIO_SOURCE_ECHO_REFERENCE:
    default:
        ALOGW("getDeviceForInputSource() invalid input source %d", inputSource);
        break;
    }
    if (device == nullptr) {
        ALOGV("getDeviceForInputSource() no device found for source %d", inputSource);
        device = availableDevices.getDevice(
                AUDIO_DEVICE_IN_STUB, String8(""), AUDIO_FORMAT_DEFAULT);
        ALOGE_IF(device == nullptr,
                 "getDeviceForInputSource() no default device defined");
    }
    ALOGV_IF(device != nullptr,
             "getDeviceForInputSource()input source %d, device %08x",
             inputSource, device->type());
    return device;
}

这里做了后插优先,就是那个设备最后插入声音就从哪个设备走,包括输入和输出,主要就是通过获取最后插入的设备类型来动态的调整声音优先级即可

### 设置或修改 Android 系统媒体音频输出设备的优先级Android 系统中,音频输出设备的选择遵循一定的优先级规则。当多个音频输出设备可用时,系统会选择最合适的设备进行音频播放。这种选择通常基于硬件连接状态以及应用程序的需求。 #### 后插优先机制 对于外部音频设备(如耳机、蓝牙设备),Android 实现了一种称为“后插优先”的机制。这意味着每当一个新的音频设备被插入时,它会被赋予更高的优先级,并成为默认的声音输出路径[^1]。此行为确保用户能够立即听到来自新连接设备的声音而无需额外配置。 #### 动态调整声音优先级 为了实现上述功能,在底层框架层面,特别是 `Engine.cpp` 文件中的原生逻辑负责检测并响应各种类型的外设变化事件。一旦发现新的音频设备接入,则依据预定义好的顺序重新评估现有选项,最终决定采用哪一个作为当前活动的输出端口[^2]。 #### 应用程序接口限制 值得注意的是,尽管存在多种可能的音频路由方案,但在 API 层面,开发者仅能通过有限的方式干预这一过程。例如,可以通过调用 `AudioManager.setSpeakerphoneOn()` 或者 `AudioManager.setBluetoothScoOn()` 方法来强制指定扬声器或蓝牙 SCO 连接模式下的语音通信路径。这是因为其他形式的切换操作往往由操作系统自动管理,尤其是涉及到物理连接变动的情况[^3]。 #### 修改音频输出优先级的实际方法 如果希望自定义特定场景下不同种类音频流所使用的输出装置,可以考虑利用广播接收器监听诸如 `ACTION_HEADSET_PLUG` 和 `BluetoothAdapter.ACTION_STATE_CHANGED` 等意图动作,进而根据实际情况更新应用内部的状态机或是提示用户做出相应选择。此外,还可以探索更高级别的编程接口,像 MediaSessionCompat 类所提供的控制能力,允许更加精细地调节多媒体会话期间的行为表现。 ```java // 注册广播接收器以监控耳机插入/移除事件 IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); registerReceiver(headsetPlugReceiver, filter); private final BroadcastReceiver headsetPlugReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int state = intent.getIntExtra("state", -1); // 获取耳机状态 switch (state){ case 0: Log.d(TAG,"Headphones unplugged"); break; case 1: Log.d(TAG,"Headphones plugged in"); AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); audioManager.setMode(AudioManager.MODE_NORMAL); audioManager.startBluetoothSco(); audioManager.setBluetoothScoOn(true); break; } } }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值