Android 11 Audio音频系统配置文件解析

在AudioPolicyService的启动过程中,会去创建AudioPolicyManager对象,进而去解析配置文件

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
        : AudioPolicyManager(clientInterface, false /*forTesting*/)
{
    loadConfig();
}

loadConfig

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
void AudioPolicyManager::loadConfig() {
    if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {
        ALOGE("could not load audio policy configuration file, setting defaults");
        getConfig().setDefault();
    }
}

deserializeAudioPolicyXmlConfig

//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {
    char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];
    std::vector<const char*> fileNames;
    status_t ret;
    //省略
    fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);//AUDIO_POLICY_XML_CONFIG_FILE_NAME:audio_policy_configuration.xml

    for (const char* fileName : fileNames) {
        for (const auto& path : audio_get_configuration_paths()) {
            snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
                     "%s/%s", path.c_str(), fileName);
            ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);//1
            if (ret == NO_ERROR) {
                config.setSource(audioPolicyXmlConfigFile);
                return ret;
            }
        }
    }
    return ret;
}

audio_get_configuration_paths返回的路径为“/odm/etc”, “/vendor/etc”,“/system/etc” 。然后遍历这些路径,找到audio_policy_configuration.xml文件,然后调用注释1处的deserializeAudioPolicyFile来解析这个文件

deserializeAudioPolicyFile

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config)
{
    PolicySerializer serializer;
    return serializer.deserialize(fileName, config);
}

PolicySerializer::deserialize

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig *config)
{
    auto doc = make_xmlUnique(xmlParseFile(configFile));
    
    xmlNodePtr root = xmlDocGetRootElement(doc.get());
   
   	//省略:对文件的合法性进行效验
   	
    // Lets deserialize children
    // Modules
    ModuleTraits::Collection modules;
    status_t status = deserializeCollection<ModuleTraits>(root, &modules, config);//1
    if (status != NO_ERROR) {
        return status;
    }
    //设置mHwModules
    config->setHwModules(modules);

	//省略

    return android::OK;
}

注释1处,开始解析配置文件中的modules标签。注意modules类型为 ModuleTraits::Collection

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
template <class Trait>
status_t deserializeCollection(const xmlNode *cur,
        typename Trait::Collection *collection,
        typename Trait::PtrSerializingCtx serializingContext)
{
    for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
        const xmlNode *child = NULL;
        if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::collectionTag))) {
            child = cur->xmlChildrenNode;
        } else if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {
            child = cur;
        }
        for (; child != NULL; child = child->next) {
            if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {
                auto element = Trait::deserialize(child, serializingContext);//1
                if (element.isOk()) {
                    status_t status = Trait::addElementToCollection(element, collection);//添加元素
                } else {
                    return BAD_VALUE;
                }
            }
        }
    }
    return NO_ERROR;
}

注释1处,针对不同的Trait类型,就调用不同的子类的deserialize处理。对于modules标签,调用ModuleTraits的deserialize处理。

ModuleTraits::deserialize

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<ModuleTraits::Element> ModuleTraits::deserialize(const xmlNode *cur, PtrSerializingCtx ctx)
{
	std::string name = getXmlAttribute(cur, Attributes::name);//得到名字,如:primary

	Element module = new HwModule(name.c_str(), versionMajor, versionMinor);//创建HwModule对象
	
	//开始解析mixPorts标签
	MixPortTraits::Collection mixPorts;
    status_t status = deserializeCollection<MixPortTraits>(cur, &mixPorts, NULL);
    module->setProfiles(mixPorts);
    //开始解析devicePorts标签
    DevicePortTraits::Collection devicePorts;
    status = deserializeCollection<DevicePortTraits>(cur, &devicePorts, NULL);
    module->setDeclaredDevices(devicePorts);
    //开始解析routes标签
    RouteTraits::Collection routes;
    status = deserializeCollection<RouteTraits>(cur, &routes, module.get());
    module->setRoutes(routes);
    //开始解析attachedDevices标签
    for (const xmlNode *children = cur->xmlChildrenNode; children != NULL;
         children = children->next) {
        if (!xmlStrcmp(children->name, reinterpret_cast<const xmlChar*>(childAttachedDevicesTag))) {
            for (const xmlNode *child = children->xmlChildrenNode; child != NULL;
                 child = child->next) {
                if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(childAttachedDeviceTag))) {
                    auto attachedDevice = make_xmlUnique(xmlNodeListGetString(
                                    child->doc, child->xmlChildrenNode, 1));
                    if (attachedDevice != nullptr) {
                        sp<DeviceDescriptor> device = module->getDeclaredDevices().
                                getDeviceFromTagName(std::string(reinterpret_cast<const char*>(
                                                        attachedDevice.get())));
                        ctx->addDevice(device);
                    }
                }
            }
        }
	//开始解析defaultOutputDevice标签
	if (!xmlStrcmp(children->name,reinterpret_cast<const xmlChar*>(childDefaultOutputDeviceTag))) {
            auto defaultOutputDevice = make_xmlUnique(xmlNodeListGetString(
                            children->doc, children->xmlChildrenNode, 1));
            if (defaultOutputDevice != nullptr) {
                sp<DeviceDescriptor> device = module->getDeclaredDevices().getDeviceFromTagName(
                        std::string(reinterpret_cast<const char*>(defaultOutputDevice.get())));
                if (device != 0 && ctx->getDefaultOutputDevice() == 0) {
                    ctx->setDefaultOutputDevice(device);
                }
            }
        }
}

解析mixPorts标签

//vendor/etc/audio_policy_configuration.xml
<mixPorts>
                <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="44100,48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                </mixPort>
                <mixPort name="spdif_passthrough" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="32000,44100,48000"
                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                    <profile name="" format="AUDIO_FORMAT_IEC61937"
                             samplingRates="32000,44100,48000"
                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                </mixPort>
                <mixPort name="hdmi" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="32000,44100,48000,96000,176400,192000"
                             channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_QUAD,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/>
                    <profile name="" format="AUDIO_FORMAT_IEC61937"
                             samplingRates="32000,44100,48000,96000,176400,192000"
                             channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_QUAD,AUDIO_CHANNEL_OUT_5POINT1,AUDIO_CHANNEL_OUT_7POINT1"/>
                    <profile name=""/>
                </mixPort>
                <mixPort name="primary input" role="sink">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/>
                </mixPort>
            </mixPorts>

对于mixPorts标签,会循环调用MixPortTraits的deserialize方法解析其子标签,也就是mixPort

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<MixPortTraits::Element> MixPortTraits::deserialize(const xmlNode *child,
        PtrSerializingCtx /*serializingContext*/)
{
    std::string name = getXmlAttribute(child, Attributes::name);//得到名字:primary output,spdif_passthrough,hdmi,primary input
    
    std::string role = getXmlAttribute(child, Attributes::role);//sink或者source
  
    audio_port_role_t portRole = (role == Attributes::roleSource) ?
            AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;

    Element mixPort = new IOProfile(name, portRole);//创建IOProfile对象

    AudioProfileTraits::Collection profiles;
    status_t status = deserializeCollection<AudioProfileTraits>(child, &profiles, NULL);//解析mixPort标签下的profile

    // The audio profiles are in order of listed in audio policy configuration file.
    // Sort audio profiles accroding to the format.
    sortAudioProfiles(profiles);
    mixPort->setAudioProfiles(profiles);//添加到mProfiles链表中

    //省略

根据解析mixPort得到的name和role 创建IOProfile对象,然后开始解析mixPort标签下的profile ,最后将解析结果添加到mProfiles链表中。来看一下profile 的解析

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
Return<AudioProfileTraits::Element> AudioProfileTraits::deserialize(const xmlNode *cur,
        PtrSerializingCtx /*serializingContext*/)
{
    std::string samplingRates = getXmlAttribute(cur, Attributes::samplingRates);//采样率
    std::string format = getXmlAttribute(cur, Attributes::format);//采样位数
    std::string channels = getXmlAttribute(cur, Attributes::channelMasks);//通道

    Element profile = new AudioProfile(formatFromString(format, gDynamicFormat),
            channelMasksFromString(channels, ","),
            samplingRatesFromString(samplingRates, ","));//创建AudioProfile对象

    profile->setDynamicFormat(profile->getFormat() == gDynamicFormat);
    profile->setDynamicChannels(profile->getChannels().empty());
    profile->setDynamicRate(profile->getSampleRates().empty());

    return profile;
}

该部分解析完成后,可得到以下结果:每个mixPort标签都会转化成IOProfile对象,mixPort标签下的profile,都会转化成AudioProfile对象,并将该对象添加到对应IOProfile的mProfiles链表。
回到modules的解析方法中,mixPorts标签解析完成后,调用HwModule的setProfiles保存解析的结果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setProfiles(const IOProfileCollection &profiles)
{
    for (size_t i = 0; i < profiles.size(); i++) {//遍历得到的IOProfile
        addProfile(profiles[i]);
    }
}

status_t HwModule::addProfile(const sp<IOProfile> &profile)
{
    switch (profile->getRole()) {
    case AUDIO_PORT_ROLE_SOURCE:
        return addOutputProfile(profile);
    case AUDIO_PORT_ROLE_SINK:
        return addInputProfile(profile);
    case AUDIO_PORT_ROLE_NONE:
        return BAD_VALUE;
    }
    return BAD_VALUE;
}

可以看出,对于role为sink的IOProfile,则添加到HwModule的mInputProfiles中,比如上面场景的primary input。对于role为source的IOProfile,则是添加到mOutputProfiles中,比如上面场景的primary output,hdmi等。比如针对上面的场景,经过解析之后可以得到以下信息
在这里插入图片描述
解析devicePorts标签

<devicePorts>
                <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
                </devicePort>
                <devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink">
                </devicePort>
                <devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink">
                </devicePort>
                <devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink">
                </devicePort>
                <devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink">
                </devicePort>
                <devicePort tagName="BT SCO Car Kit" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT" role="sink">
                </devicePort>
                <devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink">
                </devicePort>
                <devicePort tagName="SPDIF Out" type="AUDIO_DEVICE_OUT_SPDIF" role="sink">
                </devicePort>

                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
                </devicePort>
                <devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source">
                </devicePort>
                <devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source">
                </devicePort>
                <devicePort tagName="HDMIIn" type="AUDIO_DEVICE_IN_HDMI" role="source">
                </devicePort>
            </devicePorts>

对于devicePorts标签,循环调用DevicePortTraits的deserialize来解析其子标签

Return<DevicePortTraits::Element> DevicePortTraits::deserialize(const xmlNode *cur,
        PtrSerializingCtx /*serializingContext*/)
{
    std::string name = getXmlAttribute(cur, Attributes::tagName);//得到名字:Speaker,Wired Headset等
   
    std::string typeName = getXmlAttribute(cur, Attributes::type);//得到类型
   
    std::string role = getXmlAttribute(cur, Attributes::role);//sink或者source
    
    audio_port_role_t portRole = (role == Attributes::roleSource) ?
                AUDIO_PORT_ROLE_SOURCE : AUDIO_PORT_ROLE_SINK;

    //省略
    
    Element deviceDesc = new DeviceDescriptor(type, name, address, encodedFormats);//创建DeviceDescriptor对象

    AudioProfileTraits::Collection profiles;
    status_t status = deserializeCollection<AudioProfileTraits>(cur, &profiles, NULL);//解析devicePort标签下的profile标签
  
    // The audio profiles are in order of listed in audio policy configuration file.
    // Sort audio profiles accroding to the format.
    sortAudioProfiles(profiles);
    deviceDesc->setAudioProfiles(profiles);
    //省略

首先解析得到name和type,然后创建DeviceDescriptor对象,最后也是解析profile标签并将得到的AudioProfile添加到对应的DeviceDescriptor对象的mProfiles链表中。解析完成后,回到ModuleTraits::deserialize函数中,调用HwModule的setDeclaredDevices保存解析的结果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setDeclaredDevices(const DeviceVector &devices)
{
    mDeclaredDevices = devices;//保存到mDeclaredDevices中
    for (size_t i = 0; i < devices.size(); i++) {
        mPorts.add(devices[i]);//保存到mPorts中
    }
}

最后将结果保存在HwModule的mDeclaredDevices和mPorts中。针对上面的场景,可以得到以下解析结果
在这里插入图片描述
解析routes标签

<routes>
                <route type="mix" sink="Speaker"
                       sources="primary output"/>
                <route type="mix" sink="Wired Headset"
                       sources="primary output"/>
                <route type="mix" sink="Wired Headphones"
                       sources="primary output"/>
                <route type="mix" sink="BT SCO"
                       sources="primary output"/>
                <route type="mix" sink="BT SCO Headset"
                       sources="primary output"/>
                <route type="mix" sink="BT SCO Car Kit"
                       sources="primary output"/>
                <route type="mix" sink="HDMI Out"
                       sources="primary output,hdmi"/>
                <route type="mix" sink="SPDIF Out"
                       sources="primary output,spdif_passthrough"/>

                <route type="mix" sink="primary input"
                       sources="Built-In Mic,Wired Headset Mic,BT SCO Headset Mic,HDMIIn"/>
            </routes>

对于routes下的子标签,调用RouteTraits的deserialize解析处理

Return<RouteTraits::Element> RouteTraits::deserialize(const xmlNode *cur, PtrSerializingCtx ctx)
{
    std::string type = getXmlAttribute(cur, Attributes::type);//解析type
   
    audio_route_type_t routeType = (type == Attributes::typeMix) ?
                AUDIO_ROUTE_MIX : AUDIO_ROUTE_MUX;

    Element route = new AudioRoute(routeType);//创建AudioRoute对象

    std::string sinkAttr = getXmlAttribute(cur, Attributes::sink);//解析sink
   
    sp<PolicyAudioPort> sink = ctx->findPortByTagName(sinkAttr);//根据解析得到的sink的名字,从ports链表中找到DeviceDescriptor
  
    route->setSink(sink);//将找到的DeviceDescriptor添加到AudioRoute的mSink中

    std::string sourcesAttr = getXmlAttribute(cur, Attributes::sources);//解析sources
    
    PolicyAudioPortVector sources;
    std::unique_ptr<char[]> sourcesLiteral{strndup(
                sourcesAttr.c_str(), strlen(sourcesAttr.c_str()))};
    char *devTag = strtok(sourcesLiteral.get(), ",");
    while (devTag != NULL) {
        if (strlen(devTag) != 0) {
            sp<PolicyAudioPort> source = ctx->findPortByTagName(devTag);//根据source,可以找到对应的IOProfile
           
            sources.add(source);
        }
        devTag = strtok(NULL, ",");
    }

   sink->addRoute(route);//将该route添加到sink的mRoutes链表中
    for (size_t i = 0; i < sources.size(); i++) {
        sp<PolicyAudioPort> source = sources.itemAt(i);
        source->addRoute(route);//将该route添加到source的mRoutes链表中
    }
    route->setSources(sources);//将source添加到AudioRoute的mSources中
    return route;
 }

该方法主要是解析sink和source,然后根据其名字,分别找到对应的PolicyAudioPort,设置AudioRoute的mSink和mSources。回到ModuleTraits::deserialize函数中,调用HwModule的setRoutes保存解析的结果

//frameworks/av/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
void HwModule::setRoutes(const AudioRouteVector &routes)
{
    mRoutes = routes;//将结果保存在mRoutes 中
    // Now updating the streams (aka IOProfile until now) supported devices
    refreshSupportedDevices();
}

主要是将解析的结果,保存在HwModule的mRoutes 中。针对以上场景,可以得到如下结果
在这里插入图片描述
解析attachedDevices标签

<attachedDevices>
	<item>Speaker</item>
	<item>Built-In Mic</item>
	<item>HDMIIn</item>
</attachedDevices>

attachedDevices标签的解析,就在ModuleTraits::deserialize函数中

for (const xmlNode *children = cur->xmlChildrenNode; children != NULL;
         children = children->next) {
        if (!xmlStrcmp(children->name, reinterpret_cast<const xmlChar*>(childAttachedDevicesTag))) {
            ALOGV("%s: %s %s found", __func__, tag, childAttachedDevicesTag);
            for (const xmlNode *child = children->xmlChildrenNode; child != NULL;
                 child = child->next) {
                if (!xmlStrcmp(child->name,
                                reinterpret_cast<const xmlChar*>(childAttachedDeviceTag))) {
                    auto attachedDevice = make_xmlUnique(xmlNodeListGetString(
                                    child->doc, child->xmlChildrenNode, 1));
                    if (attachedDevice != nullptr) {
                        sp<DeviceDescriptor> device = module->getDeclaredDevices().
                                getDeviceFromTagName(std::string(reinterpret_cast<const char*>(
                                                        attachedDevice.get())));//从mDeclaredDevices 链表中根据名字找到DeviceDescriptor
                        ctx->addDevice(device);//更加type类型,分别添加到AudioPolicyConfig的mOutputDevices和mInputDevices中
                    }
                }
            }
        }

来看一下addDevice方法

///frameworks/av/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
  void addDevice(const sp<DeviceDescriptor> &device)
    {
        if (audio_is_output_device(device->type())) {
            mOutputDevices.add(device);
        } else if (audio_is_input_device(device->type())) {
            mInputDevices.add(device);
        }
    }

解析defaultOutputDevice标签

<defaultOutputDevice>Speaker</defaultOutputDevice>

对于该标签的解析比较简单,主要从mDeclaredDevices 链表中根据名字找到对应的DeviceDescriptor,然后将其赋值给AudioPolicyConfig的mDefaultOutputDevice成员

回到PolicySerializer::deserialize方法,当moudles解析完成后,会将解析得到的HwModule保存在AudioPolicyConfig的mHwModules中

//frameworks/av/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
 void setHwModules(const HwModuleCollection &hwModules)
    {
        mHwModules = hwModules;
    }

总结

  1. 解析mixPorts标签,每个mixPort子项,都会构建一个IOProfile对象,对于mixPort标签下的每个profile项,都会构建一个AudioProfile对象,并将AudioProfile对象放到对应IOProfile对象的mProfiles中。最后这些IOProfile对象,根据type类型,分别添加到HwModule的mOutputProfiles和mInputProfiles中。并且这些IOProfile也会添加到HwModule的mPorts中
  2. 解析devicePorts标签,每个devicePort都会创建DeviceDescriptor对象,最后这些DeviceDescriptor都会添加到HwModule的mDeclaredDevices和mPorts中
  3. 解析routes标签,对每个route,都会创建AudioRoute对象。分别根据sink和source的名字,从前面得到的mPorts中找到对应项,设置AudioRoute的mSink和mSource,最后将这些AudioRoute添加到HwModule的mRoutes中
  4. AudioPolicyConfig 有几个成员
HwModuleCollection &mHwModules; /**< Collection of Module, with Profiles, i.e. Mix Ports. */
DeviceVector &mOutputDevices;
DeviceVector &mInputDevices;
sp<DeviceDescriptor> &mDefaultOutputDevice;

其中mHwModules保存的是modules标签下的每个HwModule,解析attachedDevices标签时,会根据其名字找到对应的DeviceDescriptor,然后根据这些DeviceDescriptor的type,分别放入mOutputDevices和mInputDevices中。解析defaultOutputDevice标签时,根据名字找到对应的DeviceDescriptor,然后保存在mDefaultOutputDevice中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/666296.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

19.1 简易抽奖

准备一个数组&#xff0c;里面添加10个奖品数据&#xff0c;让奖品数据快速的在盒子中随机显示&#xff0c;通过按钮控制盒子里面的内容停止。 效果图&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">&…

01-1.2.3 算法的空间复杂度

什么是空间复杂度&#xff1f; 代码在运行之前需要先装入内存&#xff0c;程序代码需要占一定的位置&#xff08;在这边假设是100B&#xff09; 定义的变量和参数i&#xff0c;n都需要占用内存空间 //算法一——逐步递增型 void loveYou(int n) { //n为问题规模int i 1; /…

红色文化VR虚拟线上云展馆将烈士纪念馆的宏伟全貌细致入微地呈现在您的眼前

在手机屏幕上轻轻滑动&#xff0c;即刻启程一场别开生面的线上打卡之旅。720全景线上展馆是深圳VR公司华锐视点运用先进的web3d开发和VR虚拟现实技术&#xff0c;将烈士纪念馆的宏伟全貌细致入微地呈现在您的眼前。 720全景线上展馆敢于打破时空的界限&#xff0c;带您回到那段…

618 购物指南:简单点 618 捡便宜,用这个利器就行

直接干货&#xff0c;看效果&#xff0c;安装脚本直接显示商家额外优惠券&#xff1a; 1、安装好脚本后&#xff0c;打开京东、淘宝(天猫) 商品详情页面&#xff0c;脚本会自动获取优惠券进行展示。 2、如果当前商品 处于 30 天最低价&#xff0c;脚本将自动进行标记 提醒&…

input输入框的一些复习

<template><div><div style"text-align: center;margin: 10px 0;"><span style"font-size: 15px;font-weight: bold;">input输入框的基本应用</span></div><el-descriptions :column"3" size"defau…

现如今AI大环境究竟怎样?

遇到难题不要怕&#xff01;厚德提问大佬答&#xff01; 厚德提问大佬答10 你是否对AI绘画感兴趣却无从下手&#xff1f;是否有很多疑问却苦于没有大佬解答带你飞&#xff1f;从此刻开始这些问题都将迎刃而解&#xff01;你感兴趣的话题&#xff0c;厚德云替你问&#xff0c;你…

VR导航的实现原理、技术优势和应用场景

VR导航通过虚拟现实技术提供沉浸式环境&#xff0c;结合室内定位技术实现精准导航。目前&#xff0c;VR导航已在多个领域展现出其独特的价值和潜力&#xff0c;预示着智能导航系统的未来发展。 一、实现原理 VR导航技术依托于虚拟现实(VR)和室内定位系统。VR技术利用计算机模…

鸿蒙ArkTS声明式开发:跨平台支持列表【透明度设置】 通用属性

透明度设置 设置组件的透明度。 说明&#xff1a; 开发前请熟悉鸿蒙开发指导文档&#xff1a; gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版…

Python深度学习基于Tensorflow(13)目标检测实战

文章目录 RPN 整体代码RPN 具体实现过程数据标注读取标注数据固定图片大小调整目标框使用预训练模型获取 feature_shape定义 RPN 网络生成RPN 的 CLS 和 REG 数据集获取所有的锚点计算锚点与目标框的IOU 定义 RPN loss 和 训练过程 参考资料 这里实现的是二阶段目标检测&#x…

【UE5:CesiumForUnreal】——加载无高度地形数据

目录 1.实现目的 2.数据准备 2.1下载数据 2.2 数据切片 3.加载无地形数据 1.实现目的 在CesiumForUnreal插件中&#xff0c;我们加载地图和地形图层之后&#xff0c;默认都是加载的带有高程信息的地形数据&#xff0c;在实际的项目和开发中&#xff0c;有时候我们需要加载无…

双指针法 ( 快乐数 )

「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1&#xff0c;那么这个数就是快乐数 编写一个算法来判断一个…

buuctf的RSA(五)

[RoarCTF2019]RSA 一看到题目&#xff0c;我就有些蒙了&#xff0c;A是代表了什么&#xff0c; 先来分解n 接下来可以暴力破解e了&#xff0c;因为e没有给出来&#xff0c;应该不会太大&#xff0c;猜测是四位数字 import gmpy2 import libnum from Crypto.Util.number import…

Spring Boot 项目中使用 JSP

文章目录 Spring Boot 项目中使用 JSP项目结构引入依赖包编写页面和后台运行方式一&#xff1a;Maven 命令运行方式二&#xff1a;在 IDEA 中运行方式三&#xff1a;打 war 包部署运行 Spring Boot 项目中使用 JSP 在 Spring Boot 项目中不是不可以使用 JSP 。想在 Spring Boo…

科普健康短视频:成都鼎茂宏升文化传媒公司

科普健康短视频&#xff1a;引领健康知识新潮流 在数字化时代的浪潮中&#xff0c;短视频以其短小精悍、直观易懂的特点&#xff0c;迅速成为大众获取信息的重要渠道。其中&#xff0c;科普健康短视频更是凭借其科学、权威、实用的内容&#xff0c;吸引了大量关注健康的观众。…

linux下使用cmake-gui编译WXQT

一.编译环境 操作系统&#xff1a;Ubuntu 22.04.3 LTS wxWidgets源码&#xff1a;wxWidgets-3.1.5 编译工具&#xff1a;CMake-gui qt版本&#xff1a;5.13.2 二.编译步骤 1.将源码解压。 2.打开CMake-gui&#xff0c;并设置好源码目录和构建目录 3.点击configure 会弹出…

Jenkins常用插件与应用详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Jenkins是一个平台我们通过安装插件来解决我们想要完成的任务 1、Jenkins常用插件 Allure&#…

springboot+vue 社区养老服务系统

Springbootvue社区居家养老服务系统&#xff0c;数据库mysql&#xff0c;mybatis框架&#xff0c;有可视化页面。 功能&#xff1a; 用户管理 养老服务管理 护理人员管理 服务类型管理 健康状况管理 社区管理 服务区管理 娱乐资讯管理 咨询分类管理 反馈建议 系统简历管理 轮播…

Linux - 磁盘管理1

1.磁盘的分区 1.1 磁盘的类型&#xff08;标签&#xff09; MBR&#xff1a; ① 最大支持2T以内的硬盘 ② 有主分区p 拓展分区e 逻辑分区l之分 > 主分区编号1-4&#xff0c;主分区可以格式化使用 拓展分区编号1-4&#xff0c;拓展分区不能格式化 拓展分区最多能有1个&…

Vue2 + Element UI 封装 Table 递归多层级列表头动态

1、在 components 中创建 HeaderTable 文件夹&#xff0c;在创建 ColumnItem.vue 和 index.vue。 如下&#xff1a; 2、index.vue 代码内容&#xff0c;如下&#xff1a; <template><div><el-table:data"dataTableData"style"width: 100%"…

TXT文档拆分、合并、添加内容,修改内容、删除内容——首助编辑高手软件一招解决

下面这个TXT文档里面是一篇长篇小说&#xff0c;大家都知道一般小说文字内容是比较大的一个文件呢&#xff0c;想要拆分&#xff0c;拆分肯定是有方法呢&#xff0c;比如比较重统的方法手动一章一章复制出来&#xff0c;粘贴到另一个文档里面去粘贴&#xff0c;手动操作是不是很…