欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

音频framework杂记1

程序员文章站 2022-06-15 22:39:38
备注:来源于网络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

相关标签: Audio