audio音频数据从一个源走到一个目的都是需要根据配置文件来决定,所以理解configuration配置文件中各个标签项转化为c++实体类的及各成员至关重要,本文先直接给出各标签和对应实体类的结果,后再简单分析其解析过程
audio_policy_configuration.xml文件对应C++实体类
configuration文件(audio_policy_configuration的缩写)为音频audio的设备、流以及路由等配置文件,里面写明了audio音频部分有哪些设备、哪些流以及它们支持的编码、格式以及通道存储布局等等;
文件通常保存在odm/etc、/vendor/etc、/system/etc目录下,文件内容大致如下:
- Speaker
- Built-In Mic
- Built-In Back Mic
Speaker ....
查看源码,在AudioPolicyManager初始化的时候,在方法deserializeAudioPolicyXmlConfig中,当解析正确完第一个configuration文件就会return,所以应该不会解析完所有的config文件;以上xml配置最终转化为以下c++类AudioPolicyConfig:
class AudioPolicyConfig {
std::string mSource; //为config字符串目录,一般在odm/etc、/vendor/etc、/system/etc下的audio_policy_configuration.xml
HwModuleCollection &mHwModules; //保存了配置文件中所有的所有module标签集合,每个module标签对应一个HwModule类
DeviceVector &mAvailableOutputDevices; //attchedDevices标签中,设备名称名字和devicePort标签的tagName相同,且type中有OUT字眼的DeviceDescriptor实体类集合,如上Speaker
DeviceVector &mAvailableInputDevices; //同mAvailableOutputDevices一样,只不过type中有IN的DeviceDescriptor实体类集合,如上Built-In Mic
sp &mDefaultOutputDevice; // 保存defaultOutputDevice标签内名字和devicePort标签的tagName相同,如Speaker
}
module标签
每个module标签对应有自己的hal,也就是hal的源码实现都不一样,如primary、usb、a2dp等
module标签对应C++实体类HWModule
class HWModule {
mName = "primary"
mHalVersion = 3.0
OutputProfileCollection mOutputProfiles; //mixport标签role为source类型,对应IOProfle实体类集合
InputProfileCollection mInputProfiles; //mixport标签role为sink的类型,对应IOProfle实体类集合
DeviceVector mDeclaredDevices; //所有的deviceport标签,对应DeviceDescriptor实体类的集合
AudioPortVector mPorts; //所有的mixport,deviceport标签对应的实体类,因为IOProfle和DeviceDescriptor都继承了AudioPort,所以相当于这是一个AudioPort集合
AudioRouteVector mRoutes; //所有的route
}
MixPort标签
mixport标签可以理解为stream流,流配置了自己的格式、采样率以及mask,并且氛围输出、输入流
注意:一个mixPort标签可能有多个profile属性,也就是支持很多编码格式属性
每个mixport标签对应一个IOProfile实体类
class IOProfile : public AudioPort {
int maxActiveCount;
DeviceVector mSupportedDevices;
}
class AudioPort {
mName = "primary output" //对应name
(枚举,下同)audio_port_type_t mType = AUDIO_PORT_TYPE_MIX //此处固定
audio_port_role_t mRole = AUDIO_PORT_ROLE_SOURCE/AUDIO_PORT_ROLE_SINK //由config的role决定
AudioProfileVector mProfiles; //AudioProfile的集合,对应mixport里面的多个profile
mFlag = flags
sp mModule //通过attach函数与HwModule绑定
AudioRouteVector mRoutes //相关连的route标签集合,多个route里面可能都会包含同一个name的mixport,所以这里是集合
}
mixport内部的Profile标签
在解析以上标签至profile时,会单独创建AudioProfile,如上xml配置会创建:
class AudioProfile {
mName = "" //空串
audio_format_t mFormat ; //format字符对应enum的枚举值,enum在TypeConverter.cpp的FormatConverte的mTable中
ChannelsVector mChannelMasks = //同上,也是枚举值,而不是字符串,定义在OutputChannelConverter、InputChannelConverter和ChannelIndexConverter的mTable中
SampleRateVector mSamplingRates = //同上
//以下三个对应上面三位,如果三位都有值,则为false固定的,如果xml没有指定值,则为true表示是动态的值
bool mIsDynamicFormat = false
bool mIsDynamicChannels = false;
bool mIsDynamicRate = false;
}
DevicePort标签
devicePort标签可以理解为一个device设备,设备也分output和input,但是不在像mixport那样以role来分,而是以type中有关键字“IN”和“OUT”来分,如下:
对应实体类DeviceDescriptor
class DeviceDescriptor : public AudioPort, public AudioPortConfig {
audio_devices_t mDeviceType;
String8 mTagName = "BT A2DP Headphones"
FormatVector mEncodedFormats = Vector上面encodedFormats转换的枚举值
}
class AudioPort {
mName = ""
audio_port_type_t mType = AUDIO_PORT_TYPE_DEVICE //固定值
audio_port_role_t mRole = AUDIO_PORT_ROLE_SOURCE/AUDIO_PORT_ROLE_SINK //由role决定
AudioProfileVector mProfiles = //对应deviceport里面的多个profile标签,AudioProfile的集合,
sp mModule = null //目前没有attach到HwModule上
AudioRouteVector mRoutes //相关连的route标签集合,多个route里面可能都会包含同一个name的deviceport标签,所以这里是集合
}
同上MixPort一样,也会在解析内部profile标签,创建新的AudioProfile,如下:
class AudioProfile {
mName = "" 空串
audio_format_t mFormat; //同上mixport中的audioprofile
ChannelsVector mChannelMasks;
SampleRateVector mSamplingRates;
//对应上面三位,如果三位都有值,则为false固定的,如果xml没有指定值,则为true表示是动态的值
bool mIsDynamicFormat = false
bool mIsDynamicChannels = false;
bool mIsDynamicRate = false;
}
route标签
route是把deviceport和mixport连接起来的路由,数据由一个stream输出到另一个device,或者从一个device输出到另一个stream;
对应的AudioRoute类:
class AudioRoute {
audio_route_type_t mType = AUDIO_ROUTE_MIX/AUDIO_ROUTE_MUX//根据type而定是互斥还是可融合
sp mSink; //所有的deviceport、mixport标签转化的实体类都保存到HwModule的mPorts成员了,所以是用name去mPorts里面查找;
AudioPortVector mSources; //同上,只是source可能是多个,这里用集合保存
}
configuration配置文件中关键点理解 devicePort和mixport如何通过route串联
route路由决定了哪些mixport的流数据可以传到devicePort的设备里,建立他们之间的连接关系;在代码中的体现就是通过mixport标签对应的实体类IOProfile,在IOProfile里面有一个mSupportedDevices成员,它是一个DeviceDescriptor集合类型,意思也就是IOProfile支持的设备集合,这些设备集合可以把音频数据传递给IOProfile或IOProfile可以把数据传给device;那IOProfile是如何找到他的DeviceDescriptor的?
主要是通过route标签对应AudioRoute,只要route标签内,不管sink或source内容只要有自己的名字,就把这条route保存到自己IOProfile的mRoutes中去,最后通过遍历mRoute来查找自己支持的设备DeviceDescriptor,如下代码:
DeviceVector sourceDevices;
//input stream to sink device
for (const auto& route : stream->getRoutes()) {
sp sink = route->getSink();
if (sink == 0 || stream != sink) {
ALOGE("%s: Invalid route attached to input stream", __FUNCTION__);
continue;
}
//过滤route里面的source中的deviceport而不是mixport
DeviceVector sourceDevicesForRoute = getRouteSourceDevices(route);
if (sourceDevicesForRoute.isEmpty()) {
ALOGE("%s: invalid source devices for %s", __FUNCTION__, stream->getName().string());
continue;
}
sourceDevices.add(sourceDevicesForRoute);
}
DeviceVector HwModule::getRouteSourceDevices(const sp &route) const
{
DeviceVector sourceDevices;
for (const auto& source : route->getSources()) {
//type在AudioPort里面,过滤得到deviceport而不是mixport
if (source->getType() == AUDIO_PORT_TYPE_DEVICE) {
sourceDevices.add(mDeclaredDevices.getDeviceFromTagName(source->getTagName()));
}
}
return sourceDevices;
}
上面是一个sink输入流案例,查找规则如下:
- 遍历其父类的成员mRoutes,因为是输入流,所以遍历mRoute集合中sink为自己的route,也就是找有哪些源source设备把数据传给自己。
- 找到route后,根据route中source保存的对象,且对象type是AUDIO_PORT_TYPE_DEVICE类型(就是devicesPort标签对应的实体类DeviceDescriptor)
- 把DeviceDescriptor保存在集合中,保存在以下mSupportedDevices中,作为其支持的设备;
输出流,同理;最终的结果就是:
作为输出流source,mSupportedDevices保存此流可以输出到对应的device,stream -> device
作为输入流sink,mSupportedDevices保存了其他device能输出数据到此流, device -> stream
输出流source同理,就不在阐述了,最后层级依赖大致如下:
MixPort中的flag
| AUDIO_OUTPUT_FLAG | Description |
|---|---|
| AUDIO_OUTPUT_FLAG_PRIMARY | 表示音频流需要输出到主输出设备,一般用于铃声类声音 |
| AUDIO_OUTPUT_FLAG_DIRECT | 表示音频流直接输出到音频设备,不需要软件混音,一般用于 HDMI 设备声音输出 |
| AUDIO_OUTPUT_FLAG_FAST | 表示音频流需要快速输出到音频设备,一般用于按键音、游戏背景音等对时延要求高的场景 |
| AUDIO_OUTPUT_FLAG_DEEP_BUFFER | 表示音频流输出可以接受较大的时延,一般用于音乐、视频播放等对时延要求不高的场景 |
| AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | 表示音频流没有经过软件解码,需要输出到硬件解码器,由硬件解码器进行解码 |
在TypeConveter的OutputFlagConverter和InputFlagConverter还有定义的很多flag,如下:
AUDIO_OUTPUT_FLAG_NON_BLOCKING AUDIO_OUTPUT_FLAG_HW_AV_SYNC AUDIO_OUTPUT_FLAG_TTS AUDIO_OUTPUT_FLAG_RAW AUDIO_OUTPUT_FLAG_SYNC AUDIO_OUTPUT_FLAG_IEC958_NonAUDIO AUDIO_OUTPUT_FLAG_DIRECT_PCM AUDIO_OUTPUT_FLAG_MMAP_NOIRQ AUDIO_OUTPUT_FLAG_VOIP_RX AUDIO_OUTPUT_FLAG_INCALL_MUSIC
不是很懂这些flag,希望懂的朋友交流下!
解析xml文件标签代码架构
这里不谈具体的解析过程,而是探讨Android源码中这块的设计框架,源码在/frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp中,博主觉得它设计很精巧,使用template模板来减少大量的冗余代码,同时将各个模块类串联起来;
首先,它为mixport、deviceport所有标签分别创建单独的模块,如MixPortTraits,定义标签名字属性和解析方法:
struct MixPortTraits : public AndroidCollectionTraits{ static constexpr const char *tag = "mixPort"; static constexpr const char *collectionTag = "mixPorts"; struct Attributes { static constexpr const char *name = "name"; static constexpr const char *role = "role"; static constexpr const char *roleSource = "source"; static constexpr const char *flags = "flags"; static constexpr const char *maxOpenCount = "maxOpenCount"; static constexpr const char *maxActiveCount = "maxActiveCount"; }; static Return deserialize(const xmlNode *cur, PtrSerializingCtx serializingContext); // Children: GainTraits };
同时,也创建了deviceport的DevicePortTraits模块,但是deserialize方法形参和返回值均相同; 而Attributes则根据自己的标签内容定义,其他route、profile也有对应的独立模块,相互之间互不干扰;
其次,用一个模板函数将每个模块连接起来,如下:
templatestatus_t deserializeCollection(const xmlNode *cur, typename Trait::Collection *collection, typename Trait::PtrSerializingCtx serializingContext)
使用deserializeCollection



