当前位置: 首页 > news >正文

GStreamer 简明教程(十一):插件开发,以一个音频生成(Audio Source)插件为例

系列文章目录

  • GStreamer 简明教程(一):环境搭建,运行 Basic Tutorial 1 Hello world!
  • GStreamer 简明教程(二):基本概念介绍,Element 和 Pipeline
  • GStreamer 简明教程(三):动态调整 Pipeline
  • GStreamer 简明教程(四):Seek 以及获取文件时长
  • GStreamer 简明教程(五):Pad 相关概念介绍,Pad Capabilities/Templates
  • GStreamer 简明教程(六):利用 Tee 复制流数据,巧用 Queue 实现多线程
  • GStreamer 简明教程(七):实现管道的动态数据流
  • GStreamer 简明教程(八):常用工具介绍
  • GStreamer 简明教程(九):Seek 与跳帧
  • GStreamer 简明教程(十):插件开发,以一个音频特效插件为例

文章目录

  • 系列文章目录
  • 前言
  • 一、准备工作
  • 二、Show me the code
    • 3.1 线程模型分析
        • 消费端模型
        • 生产端实现方案
    • 3.2 深入 audiotestsrc 的实现逻辑
      • 3.2.1 生产者线程的启动时机
      • 3.2.2 GStreamer Pad Task 机制详解
        • 核心接口与功能
        • 设计优势
      • 3.2.3 loop 中做了哪些事情
      • 3.2.4 格式协商
    • 3.2 写一个 AudioSource 插件
      • 3.2.1 初始化函数
      • 3.2.2 激活函数
      • 3.3.3 loop 循环
  • 总结
  • 参考


前言

GStreamer 中插件分为三种:Source、Filter 和 Sink,在上一章中我们学习了如何写一个 Filter 插件,可以说 Filter 插件是最简单的,因为它只需要关系数据的处理逻辑,而 Source 和 Sink 就更加复杂一些。本章我们来讨论如何写一个 Source 插件。

本章所提及的代码你可以在 my_plugin 找到。

一、准备工作

准备工作与 GStreamer 简明教程(十):插件开发,以一个音频特效插件为例 中提到的类似,不再赘述。

二、Show me the code

接下来详细说明代码中各个细节,其中很多逻辑都是参考 audiotestsrc 来实现的,大家如果想自己对代码进行详细的分线,建议写一个 audiotestsrc 的 demo,进行代码调试。

3.1 线程模型分析

要理解GStreamer pipeline中的数据流动机制,需要首先明确其线程模型。我们以基础音频流水线为例:

audiotestsrc -> autoaudiosink
  1. 生产者audiotestsrc 元素,负责生成音频测试信号
  2. 消费者autoaudiosink 元素,负责将音频输出到系统声卡
消费端模型

autoaudiosink 的实现通常会依赖系统音频服务(如ALSA/PulseAudio)的回调机制:

  • 系统音频线程定期通过回调请求数据
  • 形成天然的"消费线程"驱动模型
生产端实现方案

生产者有两种典型的实现范式:

方案A:Push模式(主动生产)

  • audiotestsrc 创建独立的生产者线程
  • 持续生成数据并推送(push)至下游
  • 下游可能需维护数据缓冲区
  • 优势:实现直接,适合连续数据流
  • 缺点:可能需要维护缓冲区

方案B:Pull模式(按需生产)

  • audiotestsrc 保持被动状态
  • autoaudiosink需要数据时,通过链式调用向上游拉取(pull)
  • 优势:流量控制精准
  • 难点:需要实现复杂的同步机制

由于Push模式更符合"生产者-消费者"的直观理解,且实现复杂度较低,本文选择方案A作为实现基础。Pull模式涉及GStreamer更底层的调度机制,将在后续深入研究后另文探讨。

3.2 深入 audiotestsrc 的实现逻辑

我们先分析官方 audiotestsrc 的关键实现,学习 GStreamer 标准 source 元素的调度机制。

3.2.1 生产者线程的启动时机

当 pipeline 进入播放流程时,audiotestsrc 的生产者线程在 PAUSED 状态下就已经启动了。通过调试分析,其线程启动流程如下:

典型触发路径

  1. 状态切换触发
    gst_element_set_state(pipeline, GST_STATE_PAUSED) 被调用时,会触发对所有元素的 pad 激活操作:

    gst_pad_set_active(pad, TRUE)  // 激活所有 pad
    
  2. Pad 激活回调
    audiotestsrc 的 src pad 重写了 activatemode_function,此时会调用继承链:

    → gst_base_src_activate_mode()  // GstBaseSrc 的标准实现
    
  3. 任务线程创建
    gst_base_src_activate_mode() 中,最终通过:

    gst_pad_start_task(pad, gst_base_src_loop, ...)
    

    启动独立线程执行主循环逻辑

  4. 主循环工作
    gst_base_src_loop() 包含完整处理逻辑:

    • 格式协商(caps negotiation)
    • 发送 STREAM_START 事件
    • 生成音频数据
    • 数据推送(gst_pad_push()

关键结论

  • 线程启动的实际触发点是 PAUSED 状态下的 pad 激活,而非 PLAYING 状态
  • 通过重写 activatemode_function 可以自定义启动逻辑
  • GstBaseSrc 已封装标准线程调度框架,子类只需实现数据生成

3.2.2 GStreamer Pad Task 机制详解

在 GStreamer 框架中,GstPad 不仅负责数据流的连接与协商,还提供了一套完整的异步任务(Task)接口,允许开发者将线程逻辑直接封装在 Pad 层面,而非传统的 Element 中。这一设计显著提升了模块化程度和灵活性。

核心接口与功能
  1. 任务启动:gst_pad_start_task()

    gboolean gst_pad_start_task(GstPad *pad,GstTaskFunction func,gpointer user_data,GDestroyNotify notify
    );
    
    • 作用:启动一个专用线程,循环执行指定的 GstTaskFunction
    • 关键特性
      • 线程会自动进入循环,持续调用目标函数,无需开发者手动实现循环逻辑。
      • 典型应用场景:在 GstBaseSrc 的子类中,gst_base_src_loop() 仅需实现单次数据生成逻辑,任务线程会负责循环调度。例如音频源(audiosource)可通过此机制持续生成音频帧。
  2. 任务暂停:gst_pad_pause_task()

    • 行为:临时挂起任务线程的执行,但保留任务状态(如内部变量)。
    • 用途:实现动态流控,如响应管道的暂停状态或资源限制。
  3. 任务终止:gst_pad_stop_task()

    • 行为:完全停止任务线程并释放相关资源。
    • 注意:与暂停不同,停止后需重新调用 start_task 才能恢复执行。
设计优势
  • 逻辑解耦:将线程管理与业务逻辑分离,Element 只需关注数据处理,Pad Task 处理线程调度。
  • 性能优化:避免在 Element 层频繁创建/销毁线程,任务线程可复用。
  • 标准化的流控:通过统一的任务接口实现暂停/恢复,简化状态管理。

3.2.3 loop 中做了哪些事情

在 GStreamer 中,audiotestsrc 这类源元素(source element)通过 gst_pad_start_task 启动一个任务循环(gst_base_src_loop),其中有两件事情非常重要

  1. 格式协商(negotiate)
    和下游商量用什么格式传递数据(比如采样率、位深等)。这一步确保数据能被正确处理。

  2. 生成数据并推送(push)
    按协商好的格式生成音频数据,然后推给下游。

接下来我们重点讲 格式协商,当格式确定后如何生成数据和推给下游就会变得简单很多

3.2.4 格式协商

格式协商的前提是两个元素已经 link 成功。两个元素能够相互连接的前提是它们 pad 的 Capability 是有交集的,比如 src pad 支持的音频采样率是 [1, 96000],那么如果下游支持的采样率在这个范围内,他们就能 link 成功,否则在元素 link 阶段就会失败

auto ok = gst_element_link_many(ele0, ele1, NULL);
if(!ok){printf("link failed");
}

在 link 阶段仅仅是确认了元素之间支持的数据格式是包含一个子集的,那么接下来在运行阶段,我们要从这个子集中,确认唯一的格式,这样才能确定数据是以什么形式进行流动,例如确定音频采样率是 44100,声道数 2,32位浮点数。也就是说,协商的过程就是找到一个这样的固定格式,audiotestsrc 根据这个固定格式来生成音频数据。为了说明这一点,我这边举一个简单的例子。

有三个元素 Source、Filter 和 Sink,它们顺序相互连接,支持的采样率分别是:

  • Source : {16000, 32000}
  • Filter: [1, Max]
  • Sink: {32000, 44100, 48000}
+--------+       +--------+       +--------+
| Source |------>| Filter |------>|  Sink  |
+--------+       +--------+       +--------+
{16000, 32000}   [1, Max]       {16000, 32000, 44100, 48000}

首先,它们的采样率有一个公共的子集,即 {16000, 32000},因此它们在 link 阶段是成功的;接着,格式协商由 Source 发起,它用自己的格式作为格式过滤器(filter),获取 peer 端的所支持的格式,流程大致是:

  1. Sink 支持采样率 {16000, 32000, 44100, 48000}{16000, 32000} 取交集,得到 {16000, 32000} 记作 A
  2. Filter 支持采样率 [1, Mac] 与 A 取交集,得到 {16000, 32000} 记作 B
  3. Source 支持采样率 {16000, 32000} 与 B 取交集,得到 {16000, 32000} 记作 peercaps

这时候 peercaps 仍然是一个范围,不是一个固定的值,最终由 source 来决定使用哪个固定值,固定下来后,再将它作为 source 的 caps,并通过事件通知给其他元素,其他元素会收到 GST_QUERY_ACCEPT_CAPS 事件,在这个事件中获取 caps 数据做相应的处理。

// 获取 source 的 caps
thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (src), NULL);
// 以 thiscaps 作为 filter,获取 peercaps
peercaps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (basesrc), thiscaps);
// basesrc 来决定最终使用哪些固定值
caps = gst_base_src_fixate (basesrc, caps);
// 固定 caps,并发送 GST_QUERY_ACCEPT_CAPS 事件
result = gst_base_src_set_caps (basesrc, caps);

3.2 写一个 AudioSource 插件

了解了上面的知识后,我们来开始写一个自己的音频生成插件,为了让代码简单,我们做了这些简化:

  1. 只支持单声道、F32LE 、interleave 格式的数据
  2. 只支持 push 模式

详细的代码实现参考 my_plugin,使用 demo 参考 gstmyaudiotestsrc_example

3.2.1 初始化函数

在类初始化函数如下:

static void gst_my_audio_test_src_class_init(GstMyAudioTestSrcClass *klass) {
//...gobject_class->set_property = gst_my_audio_test_src_set_property;gobject_class->get_property = gst_my_audio_test_src_get_property;gobject_class->finalize = gst_my_audio_test_src_finalize;gst_my_audio_test_class_init_install_properties(gobject_class, klass);
// ...
}

我们覆写三个函数

  1. set_property,用于设置属性值
  2. get_property,用于获取属性值
  3. finalize,用于类的析构,释放一些申请的资源

gst_my_audio_test_class_init_install_properties 函数中注册了属性,具体大家自行看源码,不展开说了。

实例的初始化函数如下:

static void gst_my_audio_test_src_init(GstMyAudioTestSrc *filter) {filter->impl = new GstMyAudioTestSrcImpl();filter->srcpad = gst_pad_new_from_static_template(&gst_audio_test_src_src_template, "src");gst_pad_set_activatemode_function(filter->srcpad,gst_my_audio_test_src_activate_mode);gst_element_add_pad(GST_ELEMENT(filter), filter->srcpad);
}
  1. 申请 GstMyAudioTestSrcImpl 实例,它用 c++ 来写,可以简化一些代码逻辑
  2. 创建 srcpad,并设置 srcpad 的激活函数(_activatemode_function

3.2.2 激活函数

前面提到,Pad 的 _activatemode_function 是线程启动的入口,我们看看函数逻辑是怎么样的


static gboolean gst_my_audio_test_src_activate_mode(GstPad *pad,GstObject *parent,GstPadMode mode,gboolean active) {auto *src = GST_MYAUDIOTESTSRC(parent);switch (mode) {case GST_PAD_MODE_PULL: {res = gst_my_audio_test_src_pull();break;}case GST_PAD_MODE_PUSH: {res = gst_my_audio_test_src_activate_push(src->srcpad, parent, active);break;}// ...
}

目前 GST_PAD_MODE_PULL 是不支持的,因此看 GST_PAD_MODE_PUSH 即可

static gboolean gst_my_audio_test_src_activate_push(GstPad *srcpad,GstObject *parent,gboolean active) {if (active) {g_print("start loop");gst_pad_start_task(srcpad, (GstTaskFunction)gst_my_audio_test_src_loop,srcpad, NULL);} else {g_print("stop loop");gst_pad_stop_task(srcpad);}return TRUE;
}

_activate_push 函数很简单,启动 _src_loop 或者停止 _src_loop

3.3.3 loop 循环

接下来看最重要的 _src_loop 函数,主要做的两个事情就是格式协商和数据填充

格式协商逻辑如下:


static void gst_my_audio_test_src_loop(GstPad *pad) {GstMyAudioTestSrc *src;src = GST_MYAUDIOTESTSRC(GST_OBJECT_PARENT(pad));GstCaps *caps = NULL;gboolean result = FALSE;if (gst_pad_check_reconfigure(pad)) {g_print("need renegotiate\n");GstCaps *thiscaps = gst_pad_query_caps(src->srcpad, NULL);GST_DEBUG_OBJECT(src, "caps of src: %" GST_PTR_FORMAT, thiscaps);GstCaps *peercaps = gst_pad_peer_query_caps(src->srcpad, thiscaps);GST_DEBUG_OBJECT(src, "caps of peer: %" GST_PTR_FORMAT, peercaps);if (peercaps) {caps = peercaps;gst_caps_unref(thiscaps);} else {caps = thiscaps;}if (caps && !gst_caps_is_empty(caps)) {caps = gst_my_audio_test_src_fixate(src, caps);caps = gst_caps_fixate(caps);GST_DEBUG_OBJECT(src, "fixated to: %" GST_PTR_FORMAT, caps);if (gst_caps_is_fixed(caps)) {/* yay, fixed caps, use those then, it's possible that the subclass* does not accept this caps after all and we have to fail. */result = gst_my_audio_test_src_set_caps(src, caps);if (result) {result = gst_pad_push_event(src->srcpad, gst_event_new_caps(caps));}}}if (!result) {GST_DEBUG_OBJECT(src, "negotiation failed");gst_pad_pause_task(pad);}}// ...
}

逻辑大致是:

  1. 获取当前 src pad 的 caps,获取 peer pad 的 caps,两者取交集
  2. 调用 gst_my_audio_test_src_fixate 去设置 audio src 最期望的数据格式,然后调用 gst_caps_fixate 固化数据格式(此时所有数据格式已经确定,不再是一个范围值)
  3. gst_my_audio_test_src_set_caps 函数从 caps 中获取 AudioInfo 信息,拿到例如采样率、声道数等关键信息
  4. gst_pad_push_event 发送事件,通知下游数据格式

数据填充数据如下:

static void gst_my_audio_test_src_loop(GstPad *pad) {//...// generate audio dataGstBuffer *buf = NULL;guint blocksize = impl->samples_per_buffer;guint bufferSize = blocksize * sizeof(float);buf = gst_buffer_new_allocate(NULL, bufferSize, NULL);if (buf == NULL) {GST_DEBUG_OBJECT(src, "alloc buffer failed");}GstMapInfo map;gst_buffer_map(buf, &map, GST_MAP_WRITE);float *data = (float *)map.data;impl->fill(data, blocksize);auto ret = gst_pad_push(src->srcpad, buf);if (ret != GST_FLOW_OK) {GST_DEBUG_OBJECT(src, "push buffer failed");gst_pad_pause_task(pad);}
}
  1. gst_buffer_new_allocate 申请 GstBuffer,用于存放音频数据
  2. gst_buffer_map 从 GstBuffer 中拿到可写音频数据的地址
  3. impl->fill(data, blocksize); 用于填充音频数据,这部分用的是一个 LFO 生成器,具体逻辑大家可以不用在意。总之就是往一块内存中,写入生成的音频数据,你甚至可以写入随机噪声。。
  4. gst_pad_push 将数据 push 到下游

总结

以上,我们就将 AudioSource 如何生成数据的逻辑大致讲了一遍,运行 gstmyaudiotestsrc_example 之后可以听到正弦波的声音。后面我会去研究下如何实现 pull 模式,以及支持更多类型的音频数据和波形。

参考

  • my_plugin
  • gstmyaudiotestsrc_example

相关文章:

GStreamer 简明教程(十一):插件开发,以一个音频生成(Audio Source)插件为例

系列文章目录 GStreamer 简明教程(一):环境搭建,运行 Basic Tutorial 1 Hello world! GStreamer 简明教程(二):基本概念介绍,Element 和 Pipeline GStreamer 简明教程(三…...

chrome://inspect/#devices 调试 HTTP/1.1 404 Not Found 如何解决

使用chrome是需要翻墙的,可以换个浏览器进行使用 可以使用edge浏览器,下载地址如下 微软官方edge浏览器|Mac版:浏览更智能,工作更高效 下载Edge浏览器 edge://inspect/#devices 点击inspect即可 qq浏览器 1. 下载qq浏览器 2. …...

RFID使用指南

## 什么是RFID? RFID(Radio Frequency Identification)即射频识别技术,是一种通过无线电波进行非接触式数据交换的技术。 ## RFID系统的主要组成部分 1. **RFID标签(Tag)** - 包含芯片和天线 - 分为有源标…...

初识Redis · 哨兵机制

目录 前言: 引入哨兵 模拟哨兵机制 配置docker环境 基于docker环境搭建哨兵环境 对比三种配置文件 编排主从节点和sentinel 主从节点 sentinel 模拟哨兵 前言: 在前文我们介绍了Redis的主从复制有一个最大的缺点就是,主节点挂了之…...

JAVA设计模式——(七)代理模式

JAVA设计模式——(七)代理模式 介绍理解实现抽象主题角色具体主题角色代理类测试 应用 介绍 代理模式和装饰模式还是挺像的。装饰模式是抽象类对装饰对象的实现,在继承装饰对象。代理模式则是直接对代理对象的实现。 理解 代理模式可以看成…...

Redis 原子操作

文章目录 前言✅ 一、什么是「原子操作」?🔍 二、怎么判断一个操作是否原子?🧪 三、项目中的原子 vs 非原子案例(秒杀系统)✅ 原子性(OK)❌ 非原子性(高风险)…...

待办事项日历组件实现

待办事项日历组件实现 今天积累一个简易的待办事项日历组件的实现方法。 需求: 修改样式,变成符合项目要求的日历样式日历上展示待办事项提示(有未完成待办:展示黄点,有已完成待办:展示绿点)…...

Flask 请求数据获取方法详解

一、工作原理 在 Flask 中,所有客户端请求的数据都通过全局的 request 对象访问。该对象是 请求上下文 的一部分,仅在请求处理期间存在。Flask 在收到请求时自动创建 request 对象,并根据请求类型(如 GET、POST)和内容…...

PicoVR眼镜在XR融合现实显示模式下无法显示粒子问题

PicoVR眼镜开启XR融合现实显示模式下,Unity3D粒子效果无法显示问题,其原因是XR融合显示模式下,Unity3D应用显示层在最终合成到眼镜显示器时,驱动层先渲染摄像机画面,再以Alpha透明方式渲染应用层画面,问题就…...

vue-lottie的使用和配置

一、vue-lottie 简介 vue-lottie 是一个 Vue 组件,用于在 Vue 项目中集成 Airbnb 的 Lottie 动画库。它通过 JSON 文件渲染 After Effects 动画,适用于复杂矢量动画的高效展示。 二、安装与基础使用 1. 安装 npm install vue-lottielatest # 或 yarn…...

PyTorch 实现食物图像分类实战:从数据处理到模型训练

一、简介 在计算机视觉领域,图像分类是一项基础且重要的任务,广泛应用于智能安防、医疗诊断、电商推荐等场景。本文将以食物图像分类为例,基于 PyTorch 框架,详细介绍从数据准备、模型构建到训练测试的全流程,帮助读者…...

传统中台的重生——云原生如何重塑政务系统后端架构

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:传统后端架构的“痛”与“变” 在过去十年中,无数企业和机构纷纷构建中台系统,尤其是政务、金融、交通、教育等领域。这些中台系统一般基于 Java EE 单体架构,集中部署于虚拟机上,靠人…...

jQuery AJAX、Axios与Fetch

jQuery AJAX、Axios与Fetch对比 #mermaid-svg-FRNqb7d4i2fmbavm {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FRNqb7d4i2fmbavm .error-icon{fill:#552222;}#mermaid-svg-FRNqb7d4i2fmbavm .error-text{fill:#552…...

【Hive入门】Hive数据导出完全指南:从HDFS到本地文件系统的专业实践

目录 引言 1 Hive数据导出概述 1.1 数据导出的核心概念 1.2 典型导出场景 2 Hive到HDFS导出详解 2.1 INSERT OVERWRITE DIRECTORY方法 2.2 多目录导出技术 2.3 动态分区导出 3 HDFS到本地文件系统转移 3.1 hadoop fs命令操作 3.2 分布式拷贝工具DistCp 4 直接导出到…...

stack __ queue(栈和队列)

1. stack的介绍和使用 栈和队列里面都叫容器适配器 存储数据就要交给别的容器 通过封装别的容器,可以进行相应的操作,来达到目的 适配的本质就是复用 这就没有迭代器了,不支持随便遍历 2. queue的介绍和使用 下面用一些题来深入理解 栈…...

UML 类图基础和类关系辨析

UML 类图 目录 1 概述 2 类图MerMaid基本表示法 3 类关系详解 3.1 实现和继承 3.1.1 实现(Realization)3.1.2 继承/泛化(Inheritance/Generalization) 3.2 聚合和组合 3.2.1 组合(Composition)3.2.2 聚…...

STM32F103C8T6信息

STM32F103C8T6 完整参数列表 一、核心参数 内核架构‌ ARM Cortex-M3 32位RISC处理器 最大主频:72 MHz(基于APB总线时钟) 运算性能:1.25 DMIPS/MHz(Dhrystone 2.1基准) 总线与存储‌ 总线宽度&#xff…...

unity 读取csv

1.读取代码 string filePath Application.streamingAssetsPath "\\data.csv"; public List<MovieData> movieData new List<MovieData>(); private void ReadCSV(string filePath) { List<List<string>> data new List<…...

那些年踩过的坑之Arrays.asList

一、前言 熟悉开发的兄弟都知道&#xff0c;在写新增和删除功能的时候&#xff0c;大多数时候会写成批量的&#xff0c;原因也很简单&#xff0c;批量既支持单个也支持多个对象的操作&#xff0c;事情也是发生在这个批量方法的调用上&#xff0c;下面我简单说一下这个事情。 二…...

ASP.NET Core 自动识别 appsettings.json的机制解析

ASP.NET Core 自动识别 appsettings.json 的机制解析 在 ASP.NET Core 中&#xff0c;IConfiguration 能自动识别 appsettings.json 并直接读取值的机制&#xff0c;是通过框架的 “约定优于配置” 设计和 依赖注入系统 共同实现的。以下是详细原理&#xff1a; 默认配置源的自…...

深入解析Mlivus Cloud核心架构:rootcoord组件的最佳实践与调优指南

作为大禹智库的向量数据库高级研究员,同时也是《向量数据库指南》的作者,我在过去30年的向量数据库和AI应用实战中见证了这项技术的演进与革新。今天,我将以专业视角为您深入剖析Mlivus Cloud的核心组件之一——rootcoord,这个组件在系统架构中扮演着至关重要的角色。如果您…...

ApplicationEventPublisher用法-笔记

1.ApplicationEventPublisher简介 org.springframework.context.ApplicationEventPublisher 是 Spring 框架中用于发布自定义事件的核心接口。它允许你在 Spring 应用上下文中触发事件&#xff0c;并由其他组件&#xff08;监听器&#xff09;进行响应。 ApplicationEventPub…...

数字孪生:从概念到实践,重构未来产业的“虚拟镜像”

一、开篇&#xff1a;为什么数字孪生是下一个技术风口&#xff1f; 现象级案例引入&#xff1a; “特斯拉用数字孪生技术将电池故障预测准确率提升40%&#xff1b;西门子通过虚拟工厂模型缩短30%产品研发周期&#xff1b;波音777X飞机设计全程零实物原型……” 数据支撑&#…...

Python笔记:VS2013编译Python-3.5.10

注&#xff1a;本文是编译老版本&#xff0c;有点麻烦&#xff0c;测试了编译新版&#xff0c;基本上是傻瓜是操作即可 1. python官网下载源码 https://www.python.org/ftp/python/3.5.10/Python-3.5.10.tgz 2. 编译前查看目录中相关文档 源码目录结构 看README文档 经过查…...

STM32八股【6】-----CortexM3的双堆栈(MSP、PSP)设计

STM32的线程模式&#xff08;Thread Mode&#xff09;和内核模式&#xff08;Handler Mode&#xff09;以及其对应的权级和堆栈指针 线程模式&#xff1a; 正常代码执行时的模式&#xff08;如 main 函数、FreeRTOS任务&#xff09; 可以是特权级&#xff08;使用MSP&#xff…...

MySQL触法器

1. 什么是触发器及其特点 MySQL数据库中触发器是一个特殊的存储过程&#xff0c;不同的是执行存储过程要使用 CALL 语句来调用&#xff0c;而触发器的执行不需要使用 CALL 语句来调用&#xff0c;也不需要手工启动&#xff0c;只要一个预定义的事件发生就会被 MySQL自动调用。…...

金仓数据库征文-政务领域国产化数据库更替:金仓 KingbaseES 应用实践

目录 一.金仓数据库介绍 二.政务领域数据库替换的时代需求​ 三.金仓数据库 KingbaseES 在政务领域的替换优势​ 1.强大的兼容性与迁移能力​ 2.高安全性与稳定性保障​ 3.良好的国产化适配性​ 四.金仓数据库 KingbaseES 在政务领域的典型应用实践​ 1.电子政务办公系…...

微服务架构在云原生后端的深度融合与实践路径

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:后端架构的演变,走向云原生与微服务融合 过去十余年,后端架构经历了从单体应用(Monolithic)、垂直切分(Modularization)、到微服务(Microservices)的演进,每一次变化都是为了解决…...

北斗导航 | 北斗卫星导航单点定位与深度学习结合提升精度

以下是北斗卫星导航单点定位(SPP)与深度学习结合提升精度的关键方法总结,综合了误差建模、信号识别、动态环境适应等技术方向: 一、非直射信号(NLOS)抑制与权重修正 1. 双自注意力网络(Dual Self-Attention Network) 原理:通过同时建模卫星信号的空间环境特征(如天空…...

AlarmClock4.8.4(官方版)桌面时钟工具软件下载安装教程

1.软件名称&#xff1a;AlarmClock 2.软件版本&#xff1a;4.8.4 3.软件大小&#xff1a;187 MB 4.安装环境&#xff1a;win7/win10/win11(64位) 5.下载地址&#xff1a; https://www.kdocs.cn/l/cdZMwizD2ZL1?RL1MvMTM%3D 提示&#xff1a;先转存后下载&#xff0c;防止资…...

精益数据分析(23/126):把握创业阶段与第一关键指标

精益数据分析&#xff08;23/126&#xff09;&#xff1a;把握创业阶段与第一关键指标 在创业和数据分析的学习过程中&#xff0c;每一次深入探索都可能为我们打开新的大门。今天&#xff0c;我依旧带着和大家共同进步的想法&#xff0c;来解读《精益数据分析》中的重要内容—…...

【华为HCIP | 华为数通工程师】821—多选解析—第十六页

多选814、关于OSPF AS-External-LSA说法正确的是: A、Net mask被设置全0 B、Link State ID被设置为目的网段地址 C、Advertising Router被设置为ASBR的Router ID D、使用Link State ID和Advertising Router可以唯一标识一条AS-External-LSA 解析:Net mask代表的是掩码…...

Linux:进程间通信->匿名管道实现内存池

1. 进程间通信 (1) 概念 进程间通信(IPC) 就是不同进程间交换数据的方法&#xff0c;进程间是独立的所以不能访问彼此的内存&#xff0c;需要某种机制来通信(管道、消息队列&#xff0c;共享内存等) (2) 目的 数据传输&#xff1a;一个进程需要他的数据发送给另一个进程 资源…...

Linux服务器离线安装ollama及大模型

Linux服务器离线安装ollama及大模型 核心思路&#xff1a;使用一台可以联网的电脑将需要的ollama安装包和大模型下载到本地&#xff0c;之后传输到Linux服务器上安装并配置 环境说明 联网机&#xff1a;macOS M1Pro Linux服务器&#xff1a;x86_64 安装ollama版本&#xff1a;…...

C++ 类及函数原型详解

一、引言 在C 编程中&#xff0c;类&#xff08;Class&#xff09;是面向对象编程的核心概念之一&#xff0c;它是一种用户自定义的数据类型&#xff0c;封装了数据和操作数据的函数&#xff08;成员函数&#xff09;。函数原型则为函数的声明提供了必要的信息&#xff0c;让编…...

大内存生产环境tomcat-jvm配置实践

话不多讲&#xff0c;奉上代码&#xff0c;分享经验&#xff0c;交流提高&#xff01; 64G物理内存,8核CPU生产环境tomcat-jvm配置如下&#xff1a; JAVA_OPTS-server -XX:MaxMetaspaceSize4G -XX:ReservedCodeCacheSize2G -XX:UseG1GC -Xms48G -Xmx48G -XX:MaxGCPauseMilli…...

各类前端开发的框架比较及其核心特性、开发体验、生态系统以及在不同项目中的适用性

前端开发框架多种多样&#xff0c;每种框架都有其独特的优势和局限性。以下是几种流行的前端框架及其特点、优缺点和适合的项目类型介绍&#xff1a;包括它们的核心特性、开发体验、生态系统以及在不同项目中的适用性。 1. React.js 核心特性&#xff1a; 虚拟DOM&#xff1a…...

Git基本使用(很详细)

一&#xff1a;Git 概述 1.1 定义&#xff1a;分布式版本控制系统 1.2 版本控制 &#xff08;1&#xff09;定义&#xff1a; 版本控制时一种记录文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统 &#xff08;2&#xff09;举例 多副本 优化&#xff1a; 不使用多…...

mybatis-plus里的com.baomidou.mybatisplus.core.override.MybatisMapperProxy 类的详细解析

以下是 com.baomidou.mybatisplus.core.override.MybatisMapperProxy 类的详细解析&#xff1a; 1. 类的作用 MybatisMapperProxy 是 MyBatis-Plus 框架中用于实现 Mapper 接口动态代理的核心类。它继承自 MyBatis 的 MapperProxy&#xff0c;并扩展了以下功能&#xff1a; …...

[密码学实战]商用密码产品密钥体系架构:从服务器密码机到动态口令系统

[密码学实战]商用密码产品密钥体系架构&#xff1a;从服务器密码机到动态口令系统 关键词&#xff1a;商用密码、密钥体系、服务器密码机、金融数据密码机、动态口令、智能密码钥匙 摘要&#xff1a;本文深度解读商用密码产品的核心密钥体系架构&#xff0c;涵盖服务器密码机、…...

BongoCat - 跨平台键盘猫动画工具

本文翻译整理自&#xff1a;https://github.com/ayangweb/BongoCat 文章目录 一、关于 BongoCat相关链接资源关键功能特性 二、下载安装系统要求下载方式macOSWindowsLinux (X11) 三、灵感来源四、效果演示 一、关于 BongoCat BongoCat 是一款跨平台的键盘猫动画工具&#xf…...

跨Linux发行版CPU指令集兼容性深度解析与实践指南

一、指令集差异全景透视 1.1 Ubuntu与Debian指令集差异对比 # 查询语句&#xff1a; lscpu | grep Flags # 结果 # Ubuntu 22.04 LTS Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma dcpop asimddp asimdfhm ssbs# De…...

docker的安装和简单使用(ubuntu环境)

环境准备 这里用的是linux的环境&#xff0c;如果没有云服务器的话&#xff0c;就是用虚拟环境吧。 虚拟环境的安装参考&#xff1a;vmware17的安装 linux镜像的安装 docker安装 我使用的是ubuntu&#xff0c;使用以下命令&#xff1a; 更新本地软件包索引 sudo apt u…...

Flutter 环境搭建 (Android)

目标 上一篇Flutter应用已经能在iOS&#xff0c;macOS和,chrome环境下正常运行了,这次把Android跑通。 环境 macOS 15.4.1 Visual Studio Code 1.99.3 Flutter 3.29.3 • channel stable • https://github.com/flutter/flutter.git Framework • revision ea121f8859 (11 da…...

数字化技术的五个环节:大数据、云计算、人工智能、区块链、移动互联网

在21世纪的科技浪潮中&#xff0c;数字化技术以其强大的生命力和无限的潜力&#xff0c;正逐步重塑着我们的世界。大数据、云计算、人工智能、区块链、移动互联网&#xff0c;这五大数字化技术的环节&#xff0c;如同构建智慧未来的基石&#xff0c;每一方面都承载着推动社会进…...

PWNOS:2.0(vulnhub靶机)

文章目录 靶机地址主机发现、端口扫描web渗透目录探测漏洞利用权限提升 解密工具地址总结 靶机地址 https://download.vulnhub.com/pwnos/pWnOS_v2.0.7z 这里如果是windows系统直接使用vmware或者virtubox打开可以使用,如果是mac系统需再去做一个配置&#xff0c;比较麻烦 这里…...

ubuntu22.04部署Snipe-IT

文章目录 参考链接一、写在前二、安装操作系统三、安装 PHP四、下载 Snipe-IT五、安装依赖六、安装数据库并创建用户七、安装 Snipe-IT八、安装 Nginx九、Web 继续安装 Snipe-IT补充&#xff1a; 最后 参考链接 How to Install Snipe-IT on Ubuntu 22.04 https://www.rosehost…...

【EDA】EDA中聚类(Clustering)和划分(Partitioning)

在VLSI物理设计自动化中&#xff0c;聚类&#xff08;Clustering&#xff09;和划分&#xff08;Partitioning&#xff09;是两个不同的关键步骤&#xff0c;主要区别如下&#xff1a; 1. 目标与核心任务 聚类&#xff08;Clustering&#xff09; 目标&#xff1a;将电路中的…...

Java 安全:如何实现用户认证与授权?

Java 安全&#xff1a;如何实现用户认证与授权&#xff1f; 在当今数字化的世界中&#xff0c;用户认证与授权是 Java 应用程序安全的关键环节。它们确保只有经过授权的用户才能访问特定资源&#xff0c;保护系统免受未授权访问的威胁。本文将深入探讨如何在 Java 中实现用户认…...

六个能够白嫖学习资料的网站

一、咖喱君的资源库 地址&#xff1a;https://flowus.cn/galijun/share/de0f6d2f-df17-4075-86ed-ebead0394a77 这是一个学习资料/学习网站分享平台&#xff0c;包含了英语、法语、德语、韩语、日语、泰语等几十种外国语言的学习资料及平台&#xff0c;这个网站的优势就是外语…...