Audio解析strategy配置文件
简介
audio_policy_configuration配置文件配置了音频Audio的设备、数据流信息,而strategy相关配置文件则配置某种streamType的音频使用哪种音量曲线,如voice_call和music他们的音量曲线是不一样,后者这种关系叫做strategy策略.
默认解析路径在/vendor/etc/audio_policy_engine_configuration.xml下,如果找不到可以去:
char* POLICY_USAGE_LIBRARY_PATH[] = {"/odm/etc/", "/vendor/etc/", "/system/etc/"};
下去寻找,解析过程同audio_policy_configuration解析一样,解析时机就在audio_policy_configuration解析完成后,初始化时就立即解析strategy部分,这里直接给出xml文件解析后对应c++实体类,解析过程不在分析,如需要了解解析过程可按照以下逻辑去查看:
1. EngineBase.cpp的loadAudioPolicyEngineConfig()
2. EngineConfig.cpp的parse()
奇怪,查看自己的手机Android11,发现没找到audio_policy_engine_configuration这个文件,难道阉割了吗?
配置文件与实体类
audio_policy_engine_default_stream_volumes.xml
<volumeGroup>
<name>voice_call</name>
<indexMin>1</indexMin>
<indexMax>7</indexMax>
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET">
<point>0,-4200</point>
<point>33,-2800</point>
<point>66,-1400</point>
<point>100,0</point>
</volume>
<volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
</volumeGroup>
上面ref标签是引用了另一个文件内的内容,内容类似于这个样子:
<reference name="DEFAULT_MEDIA_VOLUME_CURVE">
<!-- Default Media reference Volume Curve -->
<point>1,-5800</point>
<point>20,-4000</point>
<point>60,-1700</point>
<point>100,0</point>
</reference>
最终结果就是每个volume标签完全是一样的,没有什么不同!
实体类
class VolumeGroup {
const std::string mName; //对应name标签
const volume_group_t mId; //代码生成ID,与配置文件内容无关
VolumeCurves mGroupVolumeCurves; //音量曲线集合,集合内为VolumeCurve类型
}
VolumeCurves与上面VolumeGroup是包含关系,其内部结构如下:
class VolumeCurves : public KeyedVector<device_category, sp<VolumeCurve> >,
public IVolumeCurves {
int mIndexMin; //对应indexMix标签
int mIndexMax; //对应indexMax标签
AttributesVector mAttributes; //与ProductStrategy里面绑定的strategy的attribute里面的volumeGroup,虽然这里是vector,实际上是一一对应的
StreamTypeVector mStreams; //集合类,哪些stream可以使用我的音乐曲线类,此stream来源于product的attribute里面的streamType
KeyedVector<device_category, sp<VolumeCurve> > mOriginVolumeCurves; //同上标签,device对应的音量曲线
}
class VolumeCurve {
const device_category mDeviceCategory; //对应deviceCategory标签
SortedVector<CurvePoint> mCurvePoints; //集合类型
}
struct CurvePoint
{
uint32_t mIndex; //对应标签point第一个值
int mAttenuationInMb; //对应point标签第二个值
}
最后的结果是是每个volumeGroup标签对应VolumeGroup实体类,每个volume标签对应一个VolumeCures音乐曲线类;并且和indexMix、indexMax标签组合对应成一个VolumeCurves音乐曲线集合类;主要注意两点:
- VolumeCurves是一个Vector集合类型,内部元素是VolumeCurve类型
- VolumeCurves的mStreams也是集合类型,会与后面的strategies配置文件中的streamType联系在一起,表明我这些音乐曲线支持哪些streamType
audio_policy_engine_product_strategies.xml策略文件
许多项目里面可能没有配置audio_policy_engine_product_strategies.xml这个文件,而是使用代码里面定义好的策略,如gDefaultEngineConfig(./av/services/audiopolicy/engine/common/src/EngineDefaultConfig.h),这个全局Config和策略文件分析的结果是一致的,我们看懂了分析xml策略文件,就懂了这个gDefaultEngineConfig是啥?
<ProductStrategy name="STRATEGY_PHONE">
<AttributesGroup streamType="AUDIO_STREAM_VOICE_CALL" volumeGroup="voice_call">
<Attributes> <Usage value="AUDIO_USAGE_VOICE_COMMUNICATION"/> </Attributes>
</AttributesGroup>
<AttributesGroup streamType="AUDIO_STREAM_BLUETOOTH_SCO" volumeGroup="bluetooth_sco">
<Attributes> <Flags value="AUDIO_FLAG_SCO"/> </Attributes>
</AttributesGroup>
</ProductStrategy>
ProductStrategy实体类
class ProductStrategy {
std::string mName = "STRATEGY_PHONE";
//集合类型,但是有点特殊,针对一个Attributes标签,其内部就有1个AudioAttributes元素;有n个AudioAttributes标签,就有n个AudioAttributes元素
AudioAttributesVector mAttributesVector;
product_strategy_t mId; //代码自动生成
}
using AudioAttributesVector = std::vector<AudioAttributes>;
//AudioAttributes封装了一个streamType支持某个volumeGroup
struct AudioAttributes {
audio_stream_type_t mStream ; //对应streamType标签
volume_group_t mVolumeGroup ; //支持音乐曲线group的id,指向上面的VolumeGroup实体类对象的id
audio_attributes_t mAttributes; //对应一个Attributes标签
};
//对应一个Attributes标签标签里面的内容,content_type,usage等等
typedef struct {
audio_content_type_t content_type;
audio_usage_t usage;
audio_source_t source;
audio_flags_mask_t flags;
char tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */
} __attribute__((packed)) audio_attributes_t; // sent through Binder;
这里有点奇怪,AudioAttributesVector内部元素有多少,是根据Attributes标签来的,有多个标签,就有多少个AudioAttributes,并且每个AudioAttributes类型里面指向了当前streamType支持哪些音乐曲线;Attributes标签内部的内容主要是指明stream的用途,如AUDIO_USAGE_VOICE_COMMUNICATION是用于语音通话;最后联系图如下:
针对ProductStrategy来说
- 每一个ProductStrategy标签对应一个实体类ProductStrategy
- 每一个Attributes标签对应一个AudioAttributes结构体,结构体内部mStream代表streamType标签,mAttributes代表Attributes标签,mVolumeGroup是一个id,与VolumeGroup对象的id建立连接
针对VolumeGroup来说
- 每个volumeGroup标签对应一个volumeGroup实体类,并且有一个mId成员
- 每个volume标签对应一个VolumeCurve实体类
- VolumeCurves实体类是集合类型,集合内元素是VolumeCurve实体类,并且是volumeGroup的内的成员变量;
ProductStrategy策略实体类表示内部有多个AudioAttributes属性集合,这个AudioAttributes表示特定的streamType和mAttributes的音频支持特定的音乐属性VolumeGroup,通过AudioAttributes的mVolumeGroup成员变量与VolumeGroup的mId成员相等建立连接,而VolumeGroup内也有多个VolumeCurve音乐曲线属性集合,可以任由AudioAttributes来选择
通常我们在播放音频时,我们会指定音频的content_type、usage等等attr属性,通过这些属性决定这个音频是哪个streamType?
这个过程就是上面的文件配置好的,转化为c++实体类建立好映射,在ProductStrategy实体类的AudioAttributes成员的audio_attributes_t mAttributes成员,封装了音频的content_type、usage等等,找到这个就能决定它是哪个streamType,以及后续的工作
特别说明
从实际项目出发,在odm、vendor、system的etc配置目录下,发现的strategy、volume配置相关的文件,有的是缺少strategy配置文件,有的volume音量曲线文件不是按照以下这种格式:
<volumeGroup>
<name>voice_call</name>
<indexMin>1</indexMin>
<indexMax>7</indexMax>
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET">
<point>0,-4200</point>
<point>33,-2800</point>
<point>66,-1400</point>
<point>100,0</point>
</volume>
<volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
</volumeGroup>
这种很明显的指定了volumeGroup的name、indexMin、indexMax和volume-point,而是以下面这种格式来配置:
<volumes>
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET">
<point>0,-4200</point>
<point>33,-2800</point>
<point>66,-1400</point>
<point>100,0</point>
</volume>
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_SPEAKER">
<point>0,-2400</point>
<point>33,-1600</point>
<point>66,-800</point>
<point>100,0</point>
</volume>
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_EARPIECE">
<point>0,-2400</point>
<point>33,-1600</point>
<point>66,-800</point>
<point>100,0</point>
</volume>
.....
这种以volumes标签开头,没有找到volumeGroup标签,那按照我上面的解析xml的阐述,岂不是错误的,一开始我也不是很理解,后面找到了这段代码才开始释怀了,如下c++:
engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{
.......
//我们这个这个项目没有audio_policy_engine_product_strategies的配置文件
auto result = engineConfig::parse();
if (result.parsedConfig == nullptr) {
ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
//所以使用默认的gDefaultEngineConfig
engineConfig::Config config = gDefaultEngineConfig;
android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);
result = {std::make_unique<engineConfig::Config>(config),
static_cast<size_t>(ret == NO_ERROR ? 0 : 1)};
}
ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement);
loadVolumeGroups(result.parsedConfig->volumeGroups, mVolumeGroups);
loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies,
mVolumeGroups);
mProductStrategies.initialize();
return result;
}
如上代码,在if条件中,如果parseConfig解析的结果为空,也就是我们上面的文件找不到,没有解析到正确的audio config相关文件,就会使用默认的gDefaultEngineConfig和parseLegacyVolumes,在parseLegacyVolumes函数中,会依次调用parseLegacyVolumeFile -->> deserializeLegacyVolumeCollection函数:
static constexpr const char *legacyVolumecollectionTag = "volumes";
static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *cur,
VolumeGroups &volumeGroups,
size_t &nbSkippedElement)
{
std::map<std::string, VolumeCurves> legacyVolumeMap;
for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
if (xmlStrcmp(cur->name, (const xmlChar *)legacyVolumecollectionTag)) {
continue;
}
const xmlNode *child = cur->xmlChildrenNode;
for (; child != NULL; child = child->next) {
//legacyVolumeTag = volume
if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) {
status_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap);
if (status != NO_ERROR) {
nbSkippedElement += 1;
}
}
}
}
for (const auto &volumeMapIter : legacyVolumeMap) {
// In order to let AudioService setting the min and max (compatibility), set Min and Max
// to -1 except for private streams
audio_stream_type_t streamType;
if (!StreamTypeConverter::fromString(volumeMapIter.first, streamType)) {
ALOGE("%s: Invalid stream %s", __func__, volumeMapIter.first.c_str());
return BAD_VALUE;
}
int indexMin = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 0 : -1;
int indexMax = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 100 : -1;
volumeGroups.push_back({ volumeMapIter.first, indexMin, indexMax, volumeMapIter.second });
}
return NO_ERROR;
}
上面这段代码就是解析文件开头以<volumes>标签开头,这不正好就是我们上面那个配置文件开头吗?也就是说明这段代码就是应对上面那种配置文件开头的,最后解析得到legacyVolumeMap结构如下:
而volumeGroups是一个vector数组集合,其中的每一个元素是volumeGroup,volumeGroup的内容大致由以下内容组成:
如上,最终解析的结果对得上,无论哪种audio配置文件,Android都提供对应的解析方法,保障最后得到的数据。