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.xmlfor (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);//1if (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// ModulesModuleTraits::Collection modules;status_t status = deserializeCollection<ModuleTraits>(root, &modules, config);//1if (status != NO_ERROR) {return status;}//设置mHwModulesconfig->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);//1if (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);//得到名字,如:primaryElement 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 inputstd::string role = getXmlAttribute(child, Attributes::role);//sink或者sourceaudio_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++) {//遍历得到的IOProfileaddProfile(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或者sourceaudio_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);//解析typeaudio_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);//解析sinksp<PolicyAudioPort> sink = ctx->findPortByTagName(sinkAttr);//根据解析得到的sink的名字,从ports链表中找到DeviceDescriptorroute->setSink(sink);//将找到的DeviceDescriptor添加到AudioRoute的mSink中std::string sourcesAttr = getXmlAttribute(cur, Attributes::sources);//解析sourcesPolicyAudioPortVector 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,可以找到对应的IOProfilesources.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 devicesrefreshSupportedDevices();
}

主要是将解析的结果,保存在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 链表中根据名字找到DeviceDescriptorctx->addDevice(device);//更加type类型,分别添加到AudioPolicyConfig的mOutputDevices和mInputDevices中}}}}

来看一下addDevice方法

///frameworks/av/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.hvoid 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.hvoid 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中

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在生产环境中以非 Root 用户启动 Kafka

目录 如何在生产环境中以非 Root 用户启动 Kafka1. 创建 Kafka 用户2. 设置目录权限3. 配置 systemd 服务文件4. 启动和启用 Kafka 服务5. 验证 Kafka 服务经验总结 为了在生产环境中以非 root 用户&#xff08;如 kafka 用户&#xff09;启动 Kafka&#xff0c;您需要确保 Ka…

【软件测试】bug篇|软件测试的生命周期|描述bug的要素|bug的级别|bug的生命周期|高频面试题:与开发产⽣争执怎么处理

目录 一、软件测试的⽣命周期 二、BUG 2.1 bug的概念 2.2 描述bug的要素 2.3 bug级别 2.4 bug的⽣命周期 &#x1f4a1;2.5 与开发产⽣争执怎么办&#xff08;⾼频考题&#xff09; &#x1f4a1; 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&…

Unity实现首行缩进两个字符

效果 在Unity中如果想实现首行缩进两个字符&#xff0c;你会发现按空格是没法实现的。 实现原理&#xff1a;用空白的透明的字替代原来的位置。 代码&#xff1a; <color#FFFFFF00>XXX</color> 赶紧去试试吧&#xff01;

CASS11自定义宗地图框

1、找到CASS11的安装路径&#xff0c;找到如下文件夹&#xff1a; 2、打开【report】文件夹&#xff0c;如下&#xff1a; 3、打开其中一个压缩包&#xff0c;如【标准宗地图】压缩包&#xff0c;结果如下&#xff1a; 4、打开后&#xff0c;将其另存为到桌面&#xff0c;随后关…

新书推荐:7.5 goto、break、continue语句

本节必须掌握的知识点&#xff1a; 示例二十六 代码分析 汇编解析 示例二十七 代码分析 汇编解析 7.5.1 示例二十六 ■goto语句&#xff1a;无条件转移语句。 语法格式&#xff1a; goto label; label : 代码; ●语法解析&#xff1a; 执行到goto语句时&#xff0c;则无…

释放 OSINT 的力量:在线调查综合指南

开源情报 (OSINT) 是从公开信息中提取有价值见解的艺术。无论您是网络安全专业人士、道德黑客还是情报分析师&#xff0c;OSINT 都能为您提供先进的技术&#xff0c;帮助您筛选海量的数字数据&#xff0c;发现隐藏的真相。 在本文中&#xff0c;我们将深入研究大量的OSINT 资源…

项目十三:搜狗——python爬虫实战案例

根据文章项目十二&#xff1a;简单的python基础爬虫训练-CSDN博客的简单应用&#xff0c;这一次来升级我们的技术&#xff0c;那么继续往下看&#xff0c;希望对技术有好运。 还是老样子&#xff0c;按流程走&#xff0c;一条龙服务&#xff0c;嘿嘿。 第一步&#xff1a;导入…

12.2 通道-阻塞与流程控制、通道型函数、退出通道

阻塞与流程控制 通常在并发程序中要尽力避免阻塞式操作&#xff0c;但有时又需要让代码暂时处于阻塞状态&#xff0c;以等待某种条件、信号或数据&#xff0c;然后再继续运行。 对于无缓冲通道&#xff0c;试图从无人写入的通道中读取&#xff0c;或者向无人读取的通道中写入…

【Sql Server】随机查询一条表记录,并重重温回顾下自定义函数的封装和使用

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言随机查询语…

MongoDB数据库(10亿条数据)清理策略: 自动化过期数据删除实战

1、引言 随着应用程序和业务数据的持续增长&#xff0c;有效地管理数据库存储空间成为维护系统性能的关键。在MongoDB这类NoSQL数据库中&#xff0c;定期清理过期数据变得尤为重要&#xff0c;这不仅能释放宝贵的存储资源&#xff0c;还能优化查询性能&#xff0c;确保数据库运…

5,串口编程---实现简单的用串口发送接收数据

单片机通过串口向PC机发送数据 PC机通过串口接收单片机发过来的数据 1.UART和USART的区别&#xff1a; USART支持同步通信方式,可以通过外部时钟信号进行同步传输,而UART仅支持异步通信方式 本开发板STM32F103ZET6有5个串口&#xff0c;用串口1作调试串口&#xff0c;因为串…

让AI学相机对焦: Learning to AutoFocus

前言 分析来自谷歌发表在 CVPR 2020 上的论文 Learning to Autofocus &#xff1a;https://arxiv.org/pdf/2004.12260 目前网上对这篇论文的分析较少&#xff0c;有的分析并没有指出关键点&#xff0c;如&#xff1a;论文解读&#xff1a; Learning to AutoFocus-CSDN博客&am…

spring常用知识点

1、拦截器和过滤器区别 1. 原理不同&#xff1a; 拦截器是基于java的反射机制&#xff0c;而过滤器采用责任链模式是基于函数回调的。 2. 使用范围不同&#xff1a; 过滤器Filter的使用依赖于Tomcat等容器&#xff0c;导致它只能在web程序中使用 拦截器是一个Sping组件&am…

jQuery 常用API

一、jQuery 选择器 1、jQuery 基础选择器 原生JS获取元素方式很多&#xff0c;很杂&#xff0c;而且兼容性情况不一致&#xff0c;因此jQuery给我们做了封装&#xff0c;使获取元素统一标准 2、jQuery 层级选择器 3、隐式迭代 遍历内部 DOM 元素&#xff08;伪数组形式存储&am…

存储+调优:存储-IP-SAN-EXTENSION

存储调优&#xff1a;存储-IP-SAN-EXTENSION 文件系统的锁标记 GFS&#xff08;锁表空间&#xff09; ----------- ------------ ------------- 节点 | ndoe1 | | node2 | | node3 | ---------- ------…

STM32建立工程问题汇总

老版本MDK&#xff0c;例如MDK4 工程内容如下&#xff1a; User文件夹中存放main.c文件&#xff0c;用户中断服务函数&#xff08;stm32f1xx.it.c&#xff09;&#xff0c;用户配置文件&#xff08;stm32f1xx_hal_conf.h&#xff09;等用户程序文件&#xff0c;或者mdk启动程序…

Spring Cloud Gateway 网关

一. 什么是网关&#xff08;Gateway&#xff09; 网关就是一个网络连接到另一个网络的关口。 在同一个项目或某一层级中&#xff0c;存在相似或重复的东西&#xff0c;我们就可以将这些相似重复的内容统一提取出来&#xff0c;向前或向后抽象成单独的一层。这个抽象的过程就是…

AURIX TC3xx单片机介绍-启动过程介绍3

如下的内容是英文为主,对于TC3xx芯片启动原理不清楚的,可以给我留言,我来解答你们的问题! 3.2.1 Reset类型识别 Reset类型的识别是用来判断上次的复位是Application Reset还是System Reset还是CPU0 Reset。基于复位的原因,启动软件会运行不同的分支逻辑。复位原因可以通…

常用目标检测预训练模型大小及准确度比较

目标检测是计算机视觉领域中的一项重要任务&#xff0c;旨在检测和定位图像或者视频中的目标对象。当人类观看图像或视频时&#xff0c;我们可以在瞬间识别和定位感兴趣的对象。目标检测的目标是使用计算机复制这种智能。 近年来&#xff0c;目标检测网络的发展日益成熟&#…

Java GC问题排查的一些个人总结和问题复盘

个人博客 Java GC问题排查的一些个人总结和问题复盘 | iwts’s blog 是否存在GC问题判断指标 有的比较明显&#xff0c;比如发布上线后内存直接就起飞了&#xff0c;这种也是比较好排查的&#xff0c;也是最多的。如果单纯从优化角度&#xff0c;看当前应用是否需要优化&…