音频framework杂记1
备注:来源于网络
device:指声卡上有喇叭、耳机
为了便于管理, 把一个设备上具有相同参数的一组device称为output
一个module能支持哪些output,一个output能支持哪些device,使用配置文件/system/etc/audio_policy.conf来描述
module: 硬件操作库, 用来操作device
output: 一组有相同参数的,来自同一硬件的device
device: 喇叭,耳机,....
需要通过设置/system/etc/audio_policy.conf
profile : 配置,用来描述output
a. 本可以支持哪些设备
b. 参数: 采样率,通道
AudioPolicyService启动过程分析
a. 加载解析/vendor/etc/audio_policy.conf或/system/etc/audio_policy.conf
对于配置文件里的每一个module项, new HwModule(name), 放入mHwModules数组
对于module里的每一个output, new IOProfile, 放入module的mOutputProfiles
对于module里的每一个input, new IOProfile, 放入module的mInputProfiles
b. 根据module的name加载厂家提供的so文件 (通过AudioFlinger来加载)
c. 打开对应的output (通过AudioFlinger来open output)
问: 默认声卡是? 声卡/有耳机孔/喇叭,如何告知Andrdoi系统?
由厂家决定,用一个配置文件申明
AndroidPolicyService:
a. 读取,解析配置文件
b. AndroidPolicyService根据配置文件,调用AudioFlinger来打开output,创建线程
总结: 对于audio_policy,conf 里的每一个module:
使用loadHwModule来处理
a. new HwModule(名字"primary")
b. mOutputProfiles: 每一项对应于output的profile
c. mInputProfiles: 每一项对应一个Input的prifile
在创建一个AudioPolicyService对象时,主要是:
1. 创建两个AudioCommand线程,一个线程用于Tone的播放,另一个用于声音的音量及其它参数设置;
2.创建音频策略管理器(AudioPolicyManager)。Android Frameworks为音频策略管理器定义了统一的接口--AudioPolicyInterface,并提供一个缺省的音频管理器--AudioPolicyManagerBase,它为运行在模拟器上Android所使用,也可以使用宏激活该通用管理器。不同的硬件厂商,可以编写一个继承AudioPolicyInterface的子类,针对自己的硬件平台,实现自己的音频策略管理。
在音频策略服务(AudioPolicyService)中,会根据一定的条件去判断创建何种音频策略管理器:
AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService() , mpPolicyManager(NULL)
{
char value[PROPERTY_VALUE_MAX];
// start tone playback thread
mTonePlaybackThread = new AudioCommandThread(String8(“”));
// start audio commands thread
mAudioCommandThread = new AudioCommandThread(String8(“ApmCommandThread”));
#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
mpPolicyManager = new AudioPolicyManagerBase(this);//通用类型
LOGV(“build for GENERIC_AUDIO – using generic audio policy”);
#else
// if running in emulation – use the emulator driver
if (property_get(“ro.kernel.qemu”, value, 0)) {
LOGV(“Running in emulation – using generic audio policy”);
mpPolicyManager = new AudioPolicyManagerBase(this);//Android模拟器上,注意将this指针传递了过去,实际上AudioPolicyService也继承了AudioPolicyClientInterface的缘故,因此可以把它看作该接口的指针。在音频策略管理器AudioPolicyManagerBase中会通过AudioPolicyClientInterface指针去调用AudioFlinger
}
else {
LOGV(“Using hardware specific audio policy”);
mpPolicyManager = createAudioPolicyManager(this);//由硬件平台厂商实现该创建函数,然后返回自己的AudioPolicyManager。亦即,硬件平台厂商应实现一个自己的AudioPolicyInterface子类,然后在该创建函数中返回该子类的对象
}
#endif
// load properties
property_get(“ro.camera.sound.forced”, value, “0″);
mpPolicyManager->setSystemProperty(“ro.camera.sound.forced”, value);
}
下图反映了几个类之间的关系:AudioPolicyService继承自BnAudioPolicyService,所以它提供了IAudioPolicyService接口的真正实现,可以通过该接口跨进程调用到它。AudioPolicyService也继承了AudioPolicyClientInterface,所以它实现了该抽象类的接口,其实现是通过AudioSystem调用到AudioFlinger(下图AudioPolicyService到AudioFlinger的虚线所示)。如上面代码所示,在创建AudioPolicyService时,会创建一个音频策略管理器,如Android默认的管理器AudioPolicyManagerBase,也可能创建的是硬件平台厂家提供的音频管理器,这些管理器实现了AudioPolicyInterface接口。在创建音频管理器时,将this指针传递给了它,意味着音频管理器可以通过AudioPolicyClientInterface接口指针(图中策略管理器到AudioPolicyClientInterface的虚线)来使用AudioPolicyService,进而使用AudioFlinger。
音频策略管理器(Audio Policy Manager)
音频策略管理器用于管理声音的输入输出路由,它决定各种类型的声音(即流类型)优先送往系统中的哪种输出设备,或优先采用哪种输入设备进行声音的采样。如手机连有蓝牙耳机或耳机(HeadPhone)时,将优先使用它们作为输入输出设备。在Android中,有一个通用实现:类AudioPolicyManagerBase。硬件平台厂家往往需要实现自己的音频策略管理器,所有的重新实现必继承抽象类AudioPolicyInterface,该继承类定义了统一的接口函数。继承自该抽象类的通用实现AudioPolicyManagerBase位于文件frameworks/base/services/audioflinger/AudioPolicyManagerBase.cpp中。
在进行播放时,需使用策略管理器的getOutput函数去获得一个音频输入输出句柄audio_io_handle_t。这个句柄用来标识策略管理器中使用的一个数据结构:音频输出描述符AudioOutputDescriptor。一个描述符对应着一次音频输出,由输出句柄标识。音频输出描述符包含了某音频输出对应的输出设备、音量、格式、声道、采样率等信息。在策略管理器中,也维护着句柄和描述符的映射表:
KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs;
音频输入输出句柄audio_io_handle_t,实际上是一个整型数,是在AduioFlinger打开输出(openOutput)时递增分配一个整型id号,用于标示新创建的播放线程(DirectOutputThread或MixerThread)。这个id号和新创建的线程作为数据对添加到向量表mPlaybackThreads中。AudioFlinger中维护的播放线程向量表如下,由句柄这个id号标识:
DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads;
因此,在策略管理中,可以由句柄得到对应的音频输出描述符,进而可以得到对应的音频相关信息和输出设备。在AudioFlinger中,可以由句柄得到对应的播放线程(该线程用于将音频数据送往HAL音频硬件)。
在Android中,将声音区分为不同的流类型。不同的流类型往往使用不同的输入输出设备进行输出,这就是音频策略。流类型由AudioSystem统一定义,但音频策略由平台实现者定义。因不同的平台厂商往往有自己的输入输出设备,音频的输入输出与这些设备有很大的相关性。因此,音频策略由平台厂商定义。在通用的实现AudioPolicyManagerBase中,定义有4种音频策略类型:
enum routing_strategy {
STRATEGY_MEDIA, //媒体类型
STRATEGY_PHONE, //电话类型
STRATEGY_SONIFICATION, //通知类型
STRATEGY_DTMF, //DTMF类型
NUM_STRATEGIES
};
根据流类型(Stream Type),应该可以得到对应的音频输入输出策略,这是由成员函数getStrategyForStream(AudioPolicyInterface中定义的接口之一)来实现。在通用实现AudioPolicyManagerBase中,由成员函数getStrategy真正去完成该功能。该函数实现的对应关系如下表:
Stream类型 | Strategy类型 |
AudioSystem::VOICE_CALL | AudioPolicyManagerBase ::STRATEGY_PHONE |
AudioSystem::BLUETOOTH_SCO | |
AudioSystem::RING | AudioPolicyManagerBase ::STRATEGY_SONIFICATION |
AudioSystem::NOTIFICATION | |
AudioSystem::ALARM | |
AudioSystem::ENFORCED_AUDIBLE | |
AudioSystem::DTMF | AudioPolicyManagerBase ::STRATEGY_DTMF |
AudioSystem::SYSTEM | AudioPolicyManagerBase ::STRATEGY_MEDIA |
AudioSystem::TTS | |
AudioSystem::MUSIC |
第一栏是流的类型,第二栏是音频策略。从表中可以看出,某些流可以使用相同的音频策略进行输出或输入。
通过流确定了策略之后,就可以确定输入输出设备。在通常情况下,一种策略又决定着声音何种输入输出设备。 这由函数AudioPolicyManagerBase::getDeviceForStrategy来实现:
- 若在调用时第二个实参为true,则表示直接从cache(见数组mDeviceForStrategy,在强制指定输入输出设备和某些设备连接上后会更新该数组)中获取策略对应何种输入或输出设备;否则:
- 按照一定的优先级顺序,检查有哪些可用的输入输出设备,然后选择一个用来输入输出。下表概略地反映了由策略到选择的设备的映射关系:
音频策略 | 选择的输入输出设备 |
STRATEGY_DTMF | 非通话状态则进入STRATEGY_MEDIA,通话状态则进入STRATEGY_PHONE |
STRATEGY_PHONE | AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSETAudioSystem::FORCE_BT_SCO
AudioSystem::DEVICE_OUT_WIRED_HEADPHONE AudioSystem::DEVICE_OUT_WIRED_HEADSET AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES AudioSystem::DEVICE_OUT_EARPIECE AudioSystem::FORCE_SPEAKER |
STRATEGY_SONIFICATION | AudioSystem::DEVICE_OUT_SPEAKER(非通话状态),通话状态则进入STRATEGY_PHONE |
STRATEGY_MEDIA | AudioSystem::DEVICE_OUT_AUX_DIGITALAudioSystem::DEVICE_OUT_WIRED_HEADPHONE
AudioSystem::DEVICE_OUT_WIRED_HEADSET AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER AudioSystem::DEVICE_OUT_SPEAKER |
有时,我们不希望策略管理器选定的设备,而希望自己选择输入输出设备,在SDK的API中,提供了两个函数来为通话状态强制选择扬声器和蓝牙设备,这两个函数API是:android.media.AudioManager的setSpeakerphoneOn和setBluetoothScoOn。它们为通话状态(AudioSystem.FOR_COMMUNICATION)强制指定语音输入和输出设备。这两个API函数调用顺序如下:android.media.AudioManager->android.media.AudioService->android.media.AudioSystem->JNI层(文件android_media_AudioSystem.cpp)->AudioSystem-> AudioPolicyService->AudioPolicyManagerBase::setForceUse。
通过上述调用后,函数setForceUse将记录强制使用的设备,以优先使用。下表第一栏是四种场景类型,第二栏是可以强制使用的语音输入输出设备:
场景类型(AudioSystem::force_use) | 强制的输入输出设备类型取值 |
AudioSystem::FOR_COMMUNICATION(通话,包括通常的语音电话和VoIP) | AudioSystem::FORCE_SPEAKER,AudioSystem::FORCE_BT_SCO,AudioSystem::FORCE_NONE |
AudioSystem::FOR_MEDIA(多媒体播放) | AudioSystem::FORCE_HEADPHONES ,AudioSystem::FORCE_BT_A2DP,
AudioSystem::FORCE_WIRED_ACCESSORY, AudioSystem::FORCE_NONE |
AudioSystem::FOR_RECORD(录音) | AudioSystem::FORCE_BT_SCO,AudioSystem::FORCE_WIRED_ACCESSORY,
AudioSystem::FORCE_NONE |
AudioSystem::FOR_DOCK() | AudioSystem::FORCE_NONE,AudioSystem::FORCE_BT_CAR_DOCK,
AudioSystem::FORCE_BT_DESK_DOCK, AudioSystem::FORCE_WIRED_ACCESSORY |
更多的强制选择,可以修改setForceUse的对应检查后,然后依据自己的代码所在的层次,参照对setForceUse的调用层次,调用适当的函数。
对于各种输入输出设备是否可用,如蓝牙耳机或线控耳机是否连接上,则是在Java层的android.media.AudioSystem中进行检测处理。当这些外设连接状态发生变化时,对应的模块会广播发送Intent,而android.media.AudioSystem中的AudioServiceBroadcastReceiver会接收到这些Intent,从而判断出设备状态变化,通过如下调用顺序来修可用IO设备列表:android.media.AudioSystem->JNI层 -> AudioSystem-> AudioPolicyService -> AudioPolicyManagerBase的setDeviceConnectionState。
创建AudioPolicyManagerBase时,会在其构造函数里
mpClientInterface = clientInterface;//指向AudioPolicyService的指针
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;//初始默认的输入设备
#ifdef WITH_A2DP//一般情况下A2DP是打开的
mA2dpOutput = 0;
mDuplicatedOutput = 0;
mA2dpDeviceAddress = String8(“”);
#endif
mScoDeviceAddress = String8(“”);
// open hardware output
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
mHardwareOutput = //打开一个默认到speaker的输出
mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannels,
&outputDesc->mLatency,
outputDesc->mFlags);
if (mHardwareOutput == 0) {//失败
LOGE(“Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d”,
outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
} else {//成功
addOutput(mHardwareOutput, outputDesc);//添加句柄和描述到表中
setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);//为其指定输出设备
//TODO: configure audio effect output stage here
}
updateDeviceForStrategy();//更新数组(cache)中的输出设备
从上面的代码看出,在创建音频管理器的初始时刻,就会打开一个默认的输出,这个输出句柄赋值给mHardwareOutput,并添加到对应的列表中。
可以使用函数setOutputDevice为输出句柄(output)指定输出设备:
void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
它首先检查是复制输出,是否是A2DP输出,然后调用mpClientInterface(实际调用到AudioFlinger)的setParameters来设置输出设备。还要将输出设备信息,更新到句柄output对应的输出描述符中。
获取音频IO句柄
audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
AudioSystem::output_flags flags)
该函数根据steam类型得到strategy,再进而得到输出设备,然后创建一个输出描述符AudioOutputDescriptor,储存相关信息,最后将句柄和描述符添加到策略管理器维护的映射表中。注意:句柄的获取实际调用的是mpClientInterface的openOutput(实际调用到AudioFlinger的openOutput去准备播放线程进行播放输出)。
开始输出
status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
AudioSystem::stream_type stream,
int session)
主要是调用setOutputDevice为ouput句柄指定输出设备,最后为其设定流对应的音量
主要是调用setOutputDevice为ouput句柄恢复为默认的输出设备
status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output,
AudioSystem::stream_type stream,
int session)
将输出
获得输入句柄
audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
AudioSystem::audio_in_acoustics acoustics,
AudioSystem::audio_input_clients *inputClientId)
开始输入
status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input)
通过输入句柄得到对应的输入描述符,然后通过mpClientInterface(实际调用到AudioFlinger)的setParameters将AudioParameter::keyRouting,inputDesc->mDevice,AudioParameter::keyInputSource,inputDesc->mInputSource
停止输入
status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input)
类似于输入,通过setParameters将AudioParameter::keyRouting对应的值修改为0
本文地址:https://blog.csdn.net/jlgcumt/article/details/107906528
推荐阅读
-
Electron – 项目打包报错(1): WARNING: Make sure that .NET Framework 4.5 or later and Powershell 3 or later are installed, otherwise extracting the Electron z
-
1.翻译:EF基础系列--什么是Entity Framework?
-
初学python杂记--求int型正整数在内存中存储时1的个数
-
Win10下安装Sql Server 2014反复提示需安装.NET Framework 3.5 SP1的解决方案
-
配置Visual Studio 以调试.net framework源代码第1/2页
-
McAfee Common Framework 提示 80040154 @ 1 错误的解决方法
-
Metasploit Framework(1)基本命令、简单使用
-
科技与现实的碰撞:雷柏Z1系列智能音频眼镜评测
-
PHP Zend Framework2入门(1)
-
音频framework杂记1