当前位置: 移动技术网 > IT编程>开发语言>.net > 音频framework杂记1

音频framework杂记1

2020年08月12日  | 移动技术网IT编程  | 我要评论
备注:来源于网络device:指声卡上有喇叭、耳机为了便于管理, 把一个设备上具有相同参数的一组device称为output一个module能支持哪些output,一个output能支持哪些device,使用配置文件/system/etc/audio_policy.conf来描述module:硬件操作库,用来操作deviceoutput:一组有相同参数的,来自同一硬件的devicedevice:喇叭,耳机,....需要通过设置/system/etc/audio_polic...

备注:来源于网络

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来实现:

  1. 若在调用时第二个实参为true,则表示直接从cache(见数组mDeviceForStrategy,在强制指定输入输出设备和某些设备连接上后会更新该数组)中获取策略对应何种输入或输出设备;否则:
  2. 按照一定的优先级顺序,检查有哪些可用的输入输出设备,然后选择一个用来输入输出。下表概略地反映了由策略到选择的设备的映射关系:
音频策略 选择的输入输出设备
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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网