audio_policy_configuration.xml文件解析

audio_policy_configuration.xml文件解析

简介

audio音频数据从一个源走到一个目的都是需要根据配置文件来决定,所以理解configuration配置文件中各个标签项转化为c++实体类的及各成员至关重要,本文先直接给出各标签和对应实体类的结果,后再简单分析其解析过程


audio_policy_configuration.xml文件对应C++实体类

configuration文件(audio_policy_configuration的缩写)为音频audio的设备、流以及路由等配置文件,里面写明了audio音频部分有哪些设备、哪些流以及它们支持的编码、格式以及通道存储布局等等;
文件通常保存在odm/etc、/vendor/etc、/system/etc目录下,文件内容大致如下:

<module name="a">
    <attachedDevices>
        <item>Speaker</item>
        <item>Built-In Mic</item>
        <item>Built-In Back Mic</item>
    </attachedDevices>
    <defaultOutputDevice>Speaker</defaultOutputDevice>

    <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" .../>
    <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BACK_MIC" .../>
    <mixport .../>
    ....
    <route .../>
</module>
<module name="b"></module>

查看源码,在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<DeviceDescriptor> &mDefaultOutputDevice;    // 保存defaultOutputDevice标签内名字和devicePort标签的tagName相同,如Speaker
}

上面AudioPolicyConfig的4个成员变量,除了mSource外,剩下的都会设置到AudioPolicyMananger类里面,也就是说AudioPolicyManager类里面也有这三个变量;此外AudioPolicyManager中还有其他重要的成员变量,这里简单介绍下,方便后续文章内容的理解:


module标签

每个module标签对应有自己的hal,也就是hal的源码实现都不一样,如primary、usb、a2dp等

<module name="primary" halVersion="3.0">
    <mixport  name="compressed_offload" role="source"...>
        <profile name="" format="AUDIO_FORMAT_MP3" .../>
        <profile name="" format="AUDIO_FORMAT_AAC_LC" .../>
    </mixport>
    <mixport name="...." role="sink"/>
    <deviceport .../>
    <route .../>
</module>

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 name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
        samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
    <profile name="" format="AUDIO_FORMAT_AAC"
        samplingRates="8000,11025,12000,16000" channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/>
</mixport>    

注意:一个mixPort标签可能有多个profile属性,也就是支持很多编码格式属性

每个mixport标签对应一个IOProfile实体类

class IOProfile : public AudioPort {
    /* *同时存活的流的最大数量,默认为1
     * 标签中flag会影响该值,如果role为sink,且flag标记为AUDIO_INPUT_FLAG_MMAP_NOIRQ,则赋值为0,表示无穷大
     * */
    int maxActiveCount;    
    /** 当前流支持的设备集合;
     * 如果是sink输入流,查找规则如下:
    * 1. 遍历其父类的成员mRoutes,因为是输入流,所以遍历mRoute集合中sink为自己的route,也就是找有哪些源source设备把数据传给自己。
    * 2. 找到route后,根据route中source保存的对象,且对象type是AUDIO_PORT_TYPE_DEVICE类型(就是devicesPort标签对应的实体类DeviceDescriptor)
    * 3. 把DeviceDescriptor保存在集合中,保存在以下mSupportedDevices中,作为其支持的设备;
    * 输出流,同理;最终的结果就是:
    * 作为输出流source,mSupportedDevices保存此流可以输出到对应的device,stream -> device
    * 作为输入流sink,mSupportedDevices保存了其他device能输出数据到此流,  device -> stream
    **/
    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
    /* *
     * 标签中flag会影响该值,flag中有INPUT和OUTPUT字眼,如果mixport的role为source,则会去枚举enum为
     * OutputFlagConverter::Table查找对比获取枚举值;反之则会去InputFlagConverter::Table去对比查找;
     * 最后将枚举值设置到这里来;如果role是source角色,则判断flag包含AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
     * 离线加载,则flag |= AUDIO_OUTPUT_FLAG_DIRECT,最后在赋值
     * */
    mFlag = flags  
    sp<HwModule> 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”来分,如下:

<devicePort tagName="BT A2DP Headphones" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES" role="sink"
                            encodedFormats="AUDIO_FORMAT_LDAC AUDIO_FORMAT_APTX AUDIO_FORMAT_APTX_HD AUDIO_FORMAT_AAC AUDIO_FORMAT_SBC">
    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                samplingRates="44100,48000,88200,96000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
    <gains>
            <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
                    minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
    </gains>
 </devicePort>

对应实体类DeviceDescriptor

class DeviceDescriptor  : public AudioPort, public AudioPortConfig {
    /* *AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES字符串对应的enum的整型值,
     *  enum定义在system下的audio-base.h,根据其字符串中就有OUT和IN两种类型
     *  audio_devices_t其实也是一个整型,判断一个device是in或者out也是根据这个判断的;
     *  role标志只是会在audioPort中的mRole保存
     * */
    audio_devices_t mDeviceType;
    String8 mTagName = "BT A2DP Headphones"
    FormatVector mEncodedFormats = Vector<int>上面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<HwModule> mModule = null //目前没有attach到HwModule上
    AudioRouteVector mRoutes //相关连的route标签集合,多个route里面可能都会包含同一个name的deviceport标签,所以这里是集合
     AudioGains mGains;  //gains标签集合类,AudioGains内部是AudioGain对应一个gain标签
}

同上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;
}

做个小结

无论devicePort和mixPort标签中,都有profile标签,这个profile表示支持sampleRate、format和channelMask,这个profile对应C++实体类AudioProfile,但是他们的父级标签对应的却是DeviceDescriptor和IOProfile两个类,这两个类有一个共同的父类叫AudioPort,而这个AudioPort类里面的成员mProfiles就保存了AudioProfile,如下图:
在这里插入图片描述


route标签

route是把deviceport和mixport连接起来的路由,数据由一个stream输出到另一个device,或者从一个device输出到另一个stream;

<route type="mix" sink="Speaker"
                       sources="primary output,raw,deep_buffer,compressed_offload,mmap_no_irq_out,voip_rx"/>

对应的AudioRoute类:

class AudioRoute {
    audio_route_type_t mType = AUDIO_ROUTE_MIX/AUDIO_ROUTE_MUX//根据type而定是互斥还是可融合
    sp<AudioPort> 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<AudioPort> 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<AudioRoute> &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输入流案例,查找规则如下:

  1. 遍历其父类的成员mRoutes,因为是输入流,所以遍历mRoute集合中sink为自己的route,也就是找有哪些源source设备把数据传给自己。
  2. 找到route后,根据route中source保存的对象,且对象type是AUDIO_PORT_TYPE_DEVICE类型(就是devicesPort标签对应的实体类DeviceDescriptor)
  3. 把DeviceDescriptor保存在集合中,保存在以下mSupportedDevices中,作为其支持的设备;
    输出流,同理;最终的结果就是:
    作为输出流source,mSupportedDevices保存此流可以输出到对应的device,stream -> device
    作为输入流sink,mSupportedDevices保存了其他device能输出数据到此流, device -> stream

输出流source同理,就不在阐述了,最后层级依赖大致如下:

在这里插入图片描述


MixPort中的flag

AUDIO_OUTPUT_FLAGDescription
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<IOProfile, IOProfileCollection>
{
    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"; /**< <attribute role source value>. */
        static constexpr const char *flags = "flags";
        static constexpr const char *maxOpenCount = "maxOpenCount";
        static constexpr const char *maxActiveCount = "maxActiveCount";
    };

    static Return<Element> deserialize(const xmlNode *cur, PtrSerializingCtx serializingContext);
    // Children: GainTraits
};

同时,也创建了deviceport的DevicePortTraits模块,但是deserialize方法形参和返回值均相同; 而Attributes则根据自己的标签内容定义,其他route、profile也有对应的独立模块,相互之间互不干扰;

其次,用一个模板函数将每个模块连接起来,如下:

template <class Trait>
status_t deserializeCollection(const xmlNode *cur,
        typename Trait::Collection *collection,
        typename Trait::PtrSerializingCtx serializingContext)

使用deserializeCollection<MixPortTraits>来发起调用,在函数内部用模板调用模块内部deserialize就串联起来了,这样看起来清晰易读,结构也分明,以后的设计可参考参考此类型设计,相互独立模块,又相互联系,具体的解析又是一致的场景

解析架构图

在这里插入图片描述

  • 16
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
`jpeg_compress_struct` 是 libjpeg 库中的一个结构体,它包含了压缩 JPEG 图像所需的所有参数和选项。该结构体的定义如下: ```c typedef struct jpeg_compress_struct { struct jpeg_error_mgr * err; /* 错误处理器 */ struct jpeg_memory_mgr * mem; /* 内存管理器 */ struct jpeg_destination_mgr * dest; /* 输出数据处理器 */ JDIMENSION image_width; /* 图像宽度 */ JDIMENSION image_height; /* 图像高度 */ int input_components; /* 输入图像的颜色分量数 */ J_COLOR_SPACE in_color_space; /* 输入图像的颜色空间 */ jpeg_component_info * comp_info; /* 颜色分量信息 */ JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; /* 量化表指针 */ JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; /* DC 熵编码表指针 */ JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; /* AC 熵编码表指针 */ UINT8 * script_space; /* 算法脚本空间 */ size_t script_space_size; /* 算法脚本空间大小 */ int num_scans; /* 扫描数 */ const jpeg_scan_info * scan_info; /* 扫描信息 */ BOOL raw_data_in; /* 是否为原始数据 */ BOOL arith_code; /* 是否使用算术编码 */ BOOL optimize_coding; /* 是否优化编码 */ int smoothing_factor; /* 平滑因子 */ J_DCT_METHOD dct_method; /* DCT 变换方法 */ int restart_interval; /* 重启间隔 */ int restart_in_rows; /* 以行数为重启间隔 */ J_BUF_MODE buffer_mode; /* 数据缓冲模式 */ J_COLOR_TRANSFORM color_transform; /* 颜色转换方法 */ BOOL progressive_mode; /* 是否为渐进式扫描 */ int max_h_samp_factor; /* 采样因子的最大水平值 */ int max_v_samp_factor; /* 采样因子的最大垂直值 */ JDIMENSION total_iMCU_rows; /* 图像的总 iMCU 行数 */ JSAMPLE * sample_range_limit; /* 量化范围限制器 */ /* 还有一些其他的成员,比较少用到 */ } jpeg_compress_struct; ``` 这个结构体中的各个成员变量用来描述压缩 JPEG 图像所需的各种信息,包括输入图像的宽度和高度、颜色空间、颜色分量的信息、量化表、熵编码表、算法脚本空间等等。在使用 libjpeg 库进行 JPEG 图像压缩时,需要创建一个 `jpeg_compress_struct` 结构体,并对其中的成员变量进行设置,然后再调用相应的函数进行压缩操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

帅气好男人_Jack

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

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

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

打赏作者

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

抵扣说明:

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

余额充值