开发平台,安卓4.4。
最近一项目中客户有一需求:提供一接口,设置音频输出设备:听筒或者扬声器。设置后所有的音频都从同一输出设备中输出。
而通话默许走的是听筒,播放音乐等走的是扬声器,这该怎么办呢?
通过设置AudioManager.setMode和AudioManager.setSpeakerphoneOn两个方法所说也可以实现,但是其中逻辑有些复杂,于是放弃了。
下面用的方法非常简单:
我们知道AudioPolicyManagerBase.cpp是管理音频策略的,所以就直接修改它的策略就行了。
先讲几个概念
安卓系统的音频设置比较复杂,不同的音频流走的哪个输出设备之间对应着几种策略
安卓系统音频流分为如下几种:
AudioManager.java中就是引用的AudioSystem.java中定义的音频流类型
//frameworks/base/media/java/android/media/AudioSystem.java /* * If these are modified, please also update Settings.System.VOLUME_SETTINGS * and attrs.xml and AudioManager.java. */ /* The audio stream for phone calls */ public static final int STREAM_VOICE_CALL = 0; /* The audio stream for system sounds */ public static final int STREAM_SYSTEM = 1; /* The audio stream for the phone ring and message alerts */ public static final int STREAM_RING = 2; /* The audio stream for music playback */ public static final int STREAM_MUSIC = 3; /* The audio stream for alarms */ public static final int STREAM_ALARM = 4; /* The audio stream for notifications */ public static final int STREAM_NOTIFICATION = 5; /* @hide The audio stream for phone calls when connected on bluetooth */ public static final int STREAM_BLUETOOTH_SCO = 6; /* @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */ public static final int STREAM_SYSTEM_ENFORCED = 7; /* @hide The audio stream for DTMF tones */ public static final int STREAM_DTMF = 8; /* @hide The audio stream for text to speech (TTS) */ public static final int STREAM_TTS = 9; // modified for FM start // add STREAM_FM /* @hide The audio stream for FM */ public static final int STREAM_FM = 10; // modified for FM end /**
音频流的作用:
a. 设备选择,比如MUSIC类型不插耳机只从扬声器出来,手上耳机从耳机出来。来电时RING类型会从扬声器和耳机同时出来
b. 音量控制,不同流类型的音量级不同,例如MUSIC有15个音量级,而STREAM_VOICE_CALL 只有7个音量级,且最低为1
声音模式
也就是AudioManager.setMode(mode)的这个mode值。看下它的定义:
//frameworks/base/media/java/android/media/AudioSystem.java /* modes for setPhoneState, must match AudioSystem.h audio_mode */ //这些定义同样在AudioSystem.h中有,且必须是对应的 public static final int MODE_INVALID = -2; public static final int MODE_CURRENT = -1; public static final int MODE_NORMAL = 0; public static final int MODE_RINGTONE = 1; public static final int MODE_IN_CALL = 2; public static final int MODE_IN_COMMUNICATION = 3; public static final int NUM_MODES = 4;
AudioSystem区分Phone的状态,与Phone的硬件结构是有关系的,看一张图:
- 系统有一个音频DSP,声音的输入输出都由它来处理(蓝牙除外)。它处理完的数字信号,需要通过D/A转换后送到输出设备上,如扬声器,耳机,听筒等。
- 系统有一个应用处理核心AP(Application Processor),可以理解为我们的CPU,还有一个基带处理核心BP(Baseband Processor),与通信相关。
- AP和BP都能向DSP发送数据,硬件链路上互不干扰,但是如果AP和BP同时往DSP送数据,这时就会出现通话声和音乐声音混杂。所以在打电话时将由AP上的Phone程序,主动设置Audio系统的mode,比如设置为MODE_IN_CALL,在这种模式下把MUSIC的音量减小。
强制使用及配置
通话时可以设置强制使用扬声器,就是这个意思。
可以强制使用的设备有:
//frameworks/base/media/java/android/media/AudioSystem.java // device categories config for setForceUse, must match audio_policy_forced_cfg_t public static final int FORCE_NONE = 0; public static final int FORCE_SPEAKER = 1; public static final int FORCE_HEADPHONES = 2; public static final int FORCE_BT_SCO = 3; public static final int FORCE_BT_A2DP = 4; public static final int FORCE_WIRED_ACCESSORY = 5; public static final int FORCE_BT_CAR_DOCK = 6; public static final int FORCE_BT_DESK_DOCK = 7; public static final int FORCE_ANALOG_DOCK = 8; public static final int FORCE_DIGITAL_DOCK = 9; public static final int FORCE_NO_BT_A2DP = 10; public static final int FORCE_SYSTEM_ENFORCED = 11; public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12; public static final int FORCE_ENCODED_SURROUND_NEVER = 13; public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14; public static final int NUM_FORCE_CONFIG = 15; public static final int FORCE_DEFAULT = FORCE_NONE;
那么使用的时机是什么呢?所以还有定义了在什么情况下强制使用:
//frameworks/base/media/java/android/media/AudioSystem.java // usage for setForceUse, must match audio_policy_force_use_t public static final int FOR_COMMUNICATION = 0; public static final int FOR_MEDIA = 1; public static final int FOR_RECORD = 2; public static final int FOR_DOCK = 3; public static final int FOR_SYSTEM = 4; public static final int FOR_HDMI_SYSTEM_AUDIO = 5; public static final int FOR_ENCODED_SURROUND = 6; private static final int NUM_FORCE_USE = 7;
输出设备的定义
//frameworks/base/media/java/android/media/AudioSystem.java // output devices, be sure to update AudioManager.java also public static final int DEVICE_OUT_EARPIECE = 0x1; public static final int DEVICE_OUT_SPEAKER = 0x2; public static final int DEVICE_OUT_WIRED_HEADSET = 0x4; public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8; public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10; public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20; public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40; public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80; public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100; public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200; public static final int DEVICE_OUT_AUX_DIGITAL = 0x400; public static final int DEVICE_OUT_HDMI = DEVICE_OUT_AUX_DIGITAL; public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800; public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000; public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000; public static final int DEVICE_OUT_USB_DEVICE = 0x4000; public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000; public static final int DEVICE_OUT_TELEPHONY_TX = 0x10000; public static final int DEVICE_OUT_LINE = 0x20000; public static final int DEVICE_OUT_HDMI_ARC = 0x40000; public static final int DEVICE_OUT_SPDIF = 0x80000; public static final int DEVICE_OUT_FM = 0x100000; public static final int DEVICE_OUT_AUX_LINE = 0x200000; public static final int DEVICE_OUT_SPEAKER_SAFE = 0x400000; public static final int DEVICE_OUT_IP = 0x800000; public static final int DEVICE_OUT_BUS = 0x1000000; public static final int DEVICE_OUT_PROXY = 0x2000000; public static final int DEVICE_OUT_USB_HEADSET = 0x4000000;
安卓系统的音频路由策略有如下几种
那么音频流和Phone的模式以及输出设备之间是如何协调的呢,就需要为不同的流和输出设备之间制定一定的规则
//AudioPolicyManagerBase.h中定义了如下几种路由策略 enum routing_strategy { STRATEGY_MEDIA, STRATEGY_PHONE, STRATEGY_SONIFICATION, STRATEGY_SONIFICATION_RESPECTFUL, STRATEGY_DTMF, STRATEGY_ENFORCED_AUDIBLE, STRATEGY_FM, //modified for FM NUM_STRATEGIES };
再放一张图说明一下它们之间的关系:
分析上图中策略的代码实现
获取音频策略的方法,为了方便起见,我只贴出了STRATEGY_PHONE和STRATEGY_MEDIA,其它的是类似的:
audio_devices_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache){ uint32_t device = AUDIO_DEVICE_NONE; switch (strategy) { case STRATEGY_PHONE: //对于phone的策略,首先考虑强制使用的设备,如果强制使用的设备不可用再考虑其它可用设备 switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) { case AudioSystem::FORCE_BT_SCO: if (!isInCall() || strategy != STRATEGY_DTMF) { device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; if (device) break; } device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; if (device) break; // if SCO device is requested but no SCO device is available, fall back to default case // FALL THROUGH case AudioSystem::FORCE_SPEAKER: // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to // A2DP speaker when forcing to speaker output if (mHasA2dp && !isInCall() && (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && (getA2dpOutput() != 0) && !mA2dpSuspended) { device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; if (device) break; } if (mPhoneState != AudioSystem::MODE_IN_CALL) { device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; if (device) break; } device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; if (device) break; device = mDefaultOutputDevice; if (device == AUDIO_DEVICE_NONE) { ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER"); } break; default: // FORCE_NONE,没有定义强制使用 // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP if (mHasA2dp && !isInCall() && (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && (getA2dpOutput() != 0) && !mA2dpSuspended) { device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; if (device) break; } device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET; if (device) break; if (mPhoneState != AudioSystem::MODE_IN_CALL) { device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; if (device) break; device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; if (device) break; } //如果没有定义强制使用,默认为EARPIECE device = mAvailableOutputDevices & AUDIO_DEVICE_OUT_EARPIECE; if (device) break; device = mDefaultOutputDevice; if (device == AUDIO_DEVICE_NONE) { ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE"); } break; } break; case STRATEGY_MEDIA: { uint32_t device2 = AUDIO_DEVICE_NONE; if (strategy != STRATEGY_SONIFICATION) { // no sonification on remote submix (e.g. WFD) device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; } if (strategy == STRATEGY_MEDIA || strategy == STRATEGY_ENFORCED_AUDIBLE) { switch (mForceUse[AudioSystem::FOR_MEDIA]) { case AudioSystem::FORCE_SPEAKER: ALOGD("geting device of force_speaker"); if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; } //强制使用SPEAKER device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; default://如果没有定义,默认改为听筒 ALOGI("bianjb--getDeviceForStrategy() STRATEGY_MEDIA-aaa"); device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_EARPIECE; break; } } if ((device2 == AUDIO_DEVICE_NONE) && mHasA2dp && (mForceUse[AudioSystem::FOR_MEDIA] != AudioSystem::FORCE_NO_BT_A2DP) && (getA2dpOutput() != 0) && !mA2dpSuspended) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; } if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; } } switch (mForceUse[AudioSystem::FOR_FM]) { case AudioSystem::FORCE_SPEAKER: ALOGI("geting device of force_speaker :STRATEGY_FM"); if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; } } if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; } if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_WIRED_HEADSET; } if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY; } if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE; } if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; } if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { // no sonification on aux digital (e.g. HDMI) device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_AUX_DIGITAL; } if ((device2 == AUDIO_DEVICE_NONE) && (mForceUse[AudioSystem::FOR_DOCK] == AudioSystem::FORCE_ANALOG_DOCK)) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; } if (device2 == AUDIO_DEVICE_NONE) { device2 = mAvailableOutputDevices & AUDIO_DEVICE_OUT_SPEAKER; } // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise device |= device2; if (device) break; device = mDefaultOutputDevice; if (device == AUDIO_DEVICE_NONE) { ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA"); } } break; default: ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); break; }
接下来就是设置强制用了代码了
在自己的应用中做如下设置,当然必须为系统应用,否则还是不能调用的:
if(TextUtils.equals("1", ssamValue)){ AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER); AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_SPEAKER); Log.d(TAG, "set force use speaker"); replyMsg(getResString(R.string.ok)); }else if(TextUtils.equals("0", ssamValue)){ AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE); AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE); Log.d(TAG, "set force use headphone"); replyMsg(getResString(R.string.ok)); }
写的不是很详细,有什么问题欢迎交流!