Audio解析strategy配置文件

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音乐曲线集合类;主要注意两点:

  1. VolumeCurves是一个Vector集合类型,内部元素是VolumeCurve类型
  2. 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都提供对应的解析方法,保障最后得到的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅气好男人_Jack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值