android 音频策略介绍

1:AudioPolicyService 和 AudioPolicyManager

       AudioPolicyService是Android音频系统的两大服务之一,另一个服务是AudioFlinger,这两大服务都在系统启动时有MediaSever加载,加载的代码位于:  frameworks/base/media/mediaserver/main_mediaserver.cpp。
AudioFlinger主要负责管理音频数据处理以及和硬件抽象层相关的工作;AudioPolicyService主要用于计算路由和音量。


     AudioPolicyService主要完成以下任务:
     JAVA应用层通过JNI,经由IAudioPolicyService接口,访问AudioPolicyService提供的服务 
     输入输出设备的连接状态 
     系统的路由策略(strategy)的切换 
     音量参数的设置 


进一步说明::

      1. AudioPolicyService继承了IAudioPolicyService接口,这样AudioPolicyService就可以基于Android的Binder机制,向外部提供服务;

      2. AudioPolicyService同时也继承了AudioPolicyClientInterface类,他有一个AudioPolicyInterface类的成员指针mpPolicyManager,实际上就是指向了AudioPolicyManager;

      3. AudioPolicyManager类继承了AudioPolicyInterface类以便向AudioPolicyService提供服务,反过来同时还有一个AudioPolicyClientInterface指针,该指针在构造函数中被初始化,指向了AudioPolicyService,实际上,AudioPolicyService是通过成员指针mpPolicyManager访问AudioPolicyManager,而AudioPolicyManager则通过AudioPolicyClientInterface(mpClientInterface)访问AudioPolicyService;

      4. AudioPolicyService有一个内部线程类AudioCommandThread,顾名思义,所有的命令(音量控制,输入、输出的切换等)最终都会在该线程中排队执行;


AudioPolicyManager
    AudioPolicyService的很大一部分管理工作都是在AudioPolicyManager中完成的。包括音量管理,路由策略(strategy)管理,输入输出设备管理。

输入输出设备管理

        音频系统为音频设备定义了一个枚举:AudioSystem::audio_devices,例如:DEVICE_OUT_SPEAKER,DEVICE_OUT_WIRED_HEADPHONE,DEVICE_OUT_BLUETOOTH_A2DP,DEVICE_IN_BUILTIN_MIC,DEVICE_IN_VOICE_CALL等等,每一个枚举值其实对应一个32bit整数的某一个位,所以这些值是可以进行位或操作的,例如我希望同时打开扬声器和耳机,那么可以这样:newDevice = DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADPHONE;
setOutputDevice(mHardwareOutput, newDevice); 

        音频系统为音频设备定义了一个枚举:AudioSystem::audio_devices,例如:DEVICE_OUT_SPEAKER,DEVICE_OUT_WIRED_HEADPHONE,DEVICE_OUT_BLUETOOTH_A2DP,DEVICE_IN_BUILTIN_MIC,DEVICE_IN_VOICE_CALL等等,每一个枚举值其实对应一个32bit整数的某一个位,所以这些值是可以进行位或操作的,例如我希望同时打开扬声器和耳机,那么可以这样:
newDevice = DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADPHONE;
setOutputDevice(mHardwareOutput, newDevice); 

        setForceUse()  设置某种场合强制使用某一设备,例如setForceUse(FOR_MEDIA, FORCE_SPEAKER)会在播放音乐时打开扬声器 
        startOutput()/stopOutput() 
startInput()/stopInput() 

音量管理

AudioPolicyManager提供了一下几个与音量相关的函数:
initStreamVolume(AudioSystem::stream_type stream, int indexMin, int indexMax) 
setStreamVolumeIndex(AudioSystem::stream_type stream, int index) 
getStreamVolumeIndex(AudioSystem::stream_type stream) 
AudioService.java中定义了每一种音频流的最大音量级别:

private int[] MAX_STREAM_VOLUME = new int[] {
        5,  // STREAM_VOICE_CALL
        7,  // STREAM_SYSTEM
        7,  // STREAM_RING
        15, // STREAM_MUSIC
        7,  // STREAM_ALARM
        7,  // STREAM_NOTIFICATION
        15, // STREAM_BLUETOOTH_SCO
        7,  // STREAM_SYSTEM_ENFORCED
        15, // STREAM_DTMF
        15  // STREAM_TTS
    };

          由此可见,电话铃声可以有7个级别的音量,而音乐则可以有15个音量级别,java的代码通过jni,最后调用AudioPolicyManager的initStreamVolume(),把这个数组的内容传入AudioPolicyManager中,这样AudioPolicyManager也就记住了每一个音频流的音量级别。应用程序可以调用setStreamVolumeIndex设置各个音频流的音量级别,setStreamVolumeIndex会把这个整数的音量级别转化为适合人耳的对数级别,然后通过AudioPolicyService的AudioCommandThread,最终会将设置应用到AudioFlinger的相应的Track中。

音频策略管理

我想首先要搞清楚stream_type,device,strategy三者之间的关系:
AudioSystem::stream_type  音频流的类型,一共有10种类型 
AudioSystem::audio_devices  音频输入输出设备,每一个bit代表一种设备,见前面的说明 
AudioPolicyManager::routing_strategy 音频路由策略,可以有4种策略 

成员变量mOutputs

KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs;   // list of output descriptors
这是AudioPolocyManager用管理输出的键值对向量(数组),通常AudioPolocyManager会打开3个输出句柄(audio_io_handle_t),它实际上就是AudioFlinger中某个PlaybackTread的ID。这3个句柄分别是:

mHardwareOutput            // hardware output handler 
mA2dpOutput                   // A2DP output handler 
mDuplicatedOutput          // duplicated output handler: outputs to hardware and A2DP 
可以通过startOutput()把某一个stream type放入到相应的输出中。

AudioCommandThread

这是AudioPolicyService中的一个线程,主要用于处理音频设置相关的命令。包括:
START_TONE 
STOP_TONE 
SET_VOLUME 
SET_PARAMETERS 
SET_VOICE_VOLUME 

每种命令的参数有相应的包装:
class ToneData 
class VolumeData 
class ParametersData 
class VoiceVolumeData 
    START_TONE/STOP_TONE:播放电话系统中常用的特殊音调,例如:TONE_DTMF_0,TONE_SUP_BUSY等等。
    SET_VOLUME:最终会调用AudioFlinger进行音量设置
    SET_VOICE_VOLUME:最终会调用AudioFlinger进行电话音量设置

SET_PARAMETERS:通过一个KeyValuePairs形式的字符串进行参数设置,KeyValuePairs的格式可以这样: 
"sampling_rate=44100" 
"channels=2" 
"sampling_rate=44100;channels=2"     // 组合形式 
    这些KeyValuePairs可以通过AudioPolicyService的成员函数setParameters()传入。


与声音路由策略有关的因素


与声音路由有关的分为如下三个方面,其仲裁发生了AudioPolicyManager类中,:
a、 setPhoneState(int state)。用来设置MODE_NORMAL和MODE_IN_CALL这两种状态,用来表示是否在通话中。进入或者退出电话状态的时候,将会产生一次具体的声道切换。此函数同样会将audio_mode设置到AudioHardware。
b、 AudioPolicyManager::setDeviceConnectionState(AudioSystem::audio_devices device,AudioSystem::device_connection_state state, const char *device_address)设置某个设备的连接状态,一次只能设置一个设备。
首先将设备的连接状态记录在mAvailableOutputDevices成员变量中,接着将会产生一次具体的声道切换。
c、 stream_type。根据pcm流steam_type类型的不同,分为如下的策略,参见:

AudioPolicyManager::getStrategy(AudioSystem::stream_type stream)函数

STRATEGY_PHONEVOICE_CALL
BLUETOOTH_SCO
STRATEGY_SONIFICATION
RING,
NOTIFICATION
ALARM,ENFORCED_AUDIBLE
STRATEGY_DTMF DTMF
STRATEGY_MEDIA SYSTEM
MUSIC

1、当系统刚启动的时候,默认是speaker, ear piece and microphone处于连接在状态,参见AudioPolicyManager的构造函数:
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0)
{
mpClientInterface = clientInterface;// 反过来指向AudioPolicyServie,
// 用来最终访问AudioFlinger
    for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
        mForceUse[i] = AudioSystem::FORCE_NONE; 
    }

// devices available by default are speaker, ear piece and microphone
    mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
                        AudioSystem::DEVICE_OUT_SPEAKER;
    mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;


    mA2dpDeviceAddress = String8("");
    mScoDeviceAddress = String8("");


    // open hardware output

AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
    outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;

mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
                                    &outputDesc->mSamplingRate,
                                    &outputDesc->mFormat,
                                    &outputDesc->mChannels,
                                    &outputDesc->mLatency,
                                    outputDesc->mFlags);


    if (mHardwareOutput == 0) {

 LOGE("…”);
    } else {
        mOutputs.add(mHardwareOutput, outputDesc);
        setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
    }


    mA2dpOutput = 0;
    mDuplicatedOutput = 0;
}

声音路由策略分析

一、获取当前的路由策略。
按照如下的顺序逐个进行检查(检查的过程中如果某个条件为真,则不会继续往下进行检查)
PHONE > SONIFICATION > MEDIA > DTMF
1、 如果mPhoneState == AudioSystem::MODE_IN_CALL为真,说明当前处于电话状态,使用STRATEGY_PHONE的策略
2、 如果当前有某个属于STRATEGY_PHONE的流处于活动状态,则使用STRATEGY_SONIFICATION的路由策略
if(mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_PHONE)

3、如果当前有某个属于STRATEGY_SONIFICATION 的流处于活动状态,则使用STRATEGY_SONIFICATION的路由策略
4、 如果当前有某个属于STRATEGY_MEDIA 的流处于活动状态,则使用STRATEGY_MEDIA的路由策略
5、 如果当前有某个属于STRATEGY_DTMF 的流处于活动状态,则使用STRATEGY_DTMF的路由策略

二、根据当前的路由策略,从当前的可用输出设备mAvailableOutputDevices中找出合适的设备,参见getDeviceForStrategy函数。
现在根据四种路由策略STRATEGY_MEDIA,STRATEGY_PHONE,STRATEGY_SONIFICATION,
STRATEGY_DTMF逐个进行讨论。
1、STRATEGY_DTMF采用跟STRATEGY_MEDIA同样的设备。如果在通话过程中调用getDeviceForStrategy函数获取STRATEGY_DTMF的设备的话,则使用STRATEGY_PHONE的设备。

2、对于STRATEGY_MEDIA的路由策略,从可用设备列表mAvailableOutputDevices中逐个进行检查。一旦检查到某个设备存在,则停止检查,使用这个设备。检查的顺序如下:

DEVICE_OUT_AUX_DIGITAL》
DEVICE_OUT_BLUETOOTH_A2DP》
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES》
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER》
DEVICE_OUT_FM_HEADPHONE》
DEVICE_OUT_FM_SPEAKER》
DEVICE_OUT_WIRED_HEADPHONE》
DEVICE_OUT_WIRED_HEADSET》
如果检查一遍,发现所有都没有找到,那就使用DEVICE_OUT_SPEAKER设备,因为这个设备是一定存在的。


3、对于STRATEGY_SONIFICATION策略,使用从STRATEGY_MEDIA策略获取的设备 再加上 DEVICE_OUT_SPEAKER设备。如果在通话过程中调用getDeviceForStrategy函数获取STRATEGY_SONIFICATION的设备的话,则使用STRATEGY_PHONE的设备。


4、STRATEGY_PHONE的路由策略。按照ForceUse中FOR_COMMUNICATION情况进行分类:
a、 mForceUse[AudioSystem::FOR_COMMUNICATION] == FORCE_NONE。按照如下顺序逐个进行检查:
DEVICE_OUT_WIRED_HEADPHONE》
DEVICE_OUT_WIRED_HEADSET》
DEVICE_OUT_EARPIECE
b、 mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO
按照如下顺序进行检查:
DEVICE_OUT_BLUETOOTH_SCO_CARKIT
DVICE_OUT_BLUETOOTH_SCO_HEADSET》

DVICE_OUT_BLUETOOTH_SCO。
如果找到了则返回,否则使用FORCE_NONE的设备。
c、mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem:: FORCE_SPEAKER
按照如下顺序进行检查:
DEVICE_OUT_BLUETOOTH_SCO_CARKIT》
DEVICE_OUT_FM_SPEAKER》
DEVICE_OUT_SPEAKER


三、AudioPolicyManager根据当前的audio_mode、流类型、mAvailableOutputDevices,选出设备device之后,使用AudioSystem:: setParameters接口,经过AudioFlinger把这个路由的命令发给AudioStreamOut::setParameters或者AudioStreamIn::setParameters。在AudioHardware进行进一步的判定,以把最后真实的路由信息发送到声卡驱动。

输出设备目前支持的声音输出节点罗列如下(AudioHardware中定义的设备):
enum audio_out_routes {
    SND_DEVICE_CALL_EARPIECE,            //听筒:电话状态
    SND_DEVICE_SPEAKER,                  //扬声器
    SND_DEVICE_CALL_HANDFREE,            //扬声器:电话状态
    SND_DEVICE_HIFI_HEADSET,             //耳机.非通话期间不区分耳机和耳麦
    SND_DEVICE_CALL_HEADSET,             //耳机:电话状态,with mic

SND_DEVICE_CALL_HEADPHONE,           //耳机:电话状态,no mic
    SND_DEVICE_HEADSET_AND_SPEAKER,      //耳机和扬声器
    SND_DEVICE_BLUETOOTH_SCO,            //蓝牙通话
    SND_DEVICE_BLUETOOTH_A2DP,           //蓝牙播音乐
};
在AudioHardware检查到确实需要切换路由的时候,禁用旧设备,启用新设备。在AudioHardware需要做进一步的检查确认的理由如下:

a、AudioHardware知道我们的声卡具体支持那些设备,可以拒绝一些不支持的设备切换申请。
b、在AudioPolicyManager中,如果输出到耳机设备,是不区分通话状态还是非通话状态的。但是我们的声卡是需要区分。


四、对于录音,首先需要MediaRecoder指定audio_source,目前我们使用的audio_source如下:

enum audio_source {
    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,
};

关于input device的连接状态,目前我们只用到了DEVICE_IN_BLUETOOTH_SCO_HEADSET和DEVICE_IN_WIRED_HEADSET,因为默认的DEVICE_IN_BUILTIN_MIC是肯定处于连接状态的。
a、在系统启动时,默认的情况下是内置的mic是连接上的。参见AudioPolicyManager的构造函数,
mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;

b、在以后setDeviceConnectionState函数中,如果是输入device,就跟输入device的连接状态,更新mAvailableInputDevices变量。
c、根据inputSource从mAvailableInputDevices中挑选出input-device。






  • 1
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android音频策略是指安卓操作系统对于设备音频功能的管理和控制策略。它包括了音频的输入、输出、路由和控制等方面。 首先,Android音频策略规定了音频输入的管理方式。通过底层API,Android可以管理和控制设备的麦克风输入。它允许应用程序访问和使用麦克风数据,进行录音或语音识别等功能。同时,音频策略还考虑了不同应用程序之间的优先级,确保高优先级的应用程序能够独占麦克风输入。 其次,音频策略还涉及音频输出的路由和管理。通过音频路由,Android可以将音频输出到不同的设备,如扬声器、耳机或蓝牙耳机。这使得用户可以根据需求选择合适的音频输出方式。此外,音频策略还允许应用程序控制音量和音调等参数,以满足用户的个性化需求。 此外,Android音频策略还考虑了多媒体的播放和通信功能。它允许多个应用程序同时使用音频功能,如同时播放背景音乐和接听电话。通过合理的资源管理和使用优先级,音频策略可以确保不同应用程序之间的协调和平衡。 最后,安卓音频策略还考虑了能耗和性能方面的因素。它通过动态调整设备的工作状态和功耗来优化音频体验。例如,在没有音频输出时,安卓可以自动关闭耳机插孔以节省电量。同时,它还可以通过优化音频处理算法和资源管理,减少对系统性能的影响。 总结来说,Android音频策略通过管理和控制音频输入、输出、路由和控制等方面,提供了全面的音频功能管理和优化能力,以提供良好的音频体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值