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

Linux 系统蓝牙音频服务实现分析

Linux 系统蓝牙音频服务实现分析

    • 蓝牙音频设备连接管理

Linux 系统中,蓝牙音频服务实现为系统音频服务 PulseAudio 的可加载模块,它用来以 PulseAudio 标准的方式描述蓝牙音频设备,将其嵌入 PulseAudio 的音频处理流水线,并呈现给用户,支持用户切换音频设备,如蓝牙耳机。

蓝牙音频设备连接管理

音频蓝牙协议栈主要有 HSP/HFP 和 A2DP/AVRCP 两种,这两种协议栈在系统蓝牙服务 bluez 和音频服务 pulseaudio 中具有不同的处理流程。通常一个蓝牙耳麦对这两种协议栈都支持,并可以在这两种协议栈之间进行切换。

对于 A2DP/AVRCP 协议栈,在 pa_bluetooth_discovery_get() 函数的执行过程中,会初始化蓝牙 A2DP endpoint 配置,并向 D-BUS 注册 endpoint 处理程序 endpoint_handler()。蓝牙音频设备在物理上与蓝牙控制器/适配器建立连接之后,系统蓝牙服务 bluez 会收到通知,在回调中处理连接,并通过 D-BUS 调用 pulseaudio 注册的 endpoint 处理程序 endpoint_handler()

对于 HSP/HFP 协议栈,主要通过蓝牙后端,即蓝牙 headset 后端处理。 get_managed_objects_reply() 函数执行的最后,创建蓝牙后端对象。pulseaudio 支持两种蓝牙后端,分别为 native 和 ofono,native 后端直接与 BlueZ 交互;ofono 后端通过 ofono(一个开源的电话栈)实现蓝牙音频支持,主要用于支持电话功能(如 HFP),pulseaudio 通常使用 native 蓝牙后端。get_managed_objects_reply() 调用 pa_bluetooth_native_backend_new() 函数创建 native 蓝牙后端对象,这个函数定义 (位于 pulseaudio/src/modules/bluetooth/backend-native.c) 如下:

struct pa_bluetooth_backend {pa_core *core;pa_dbus_connection *connection;pa_bluetooth_discovery *discovery;pa_hook_slot *adapter_uuids_changed_slot;bool enable_shared_profiles;bool enable_hsp_hs;bool enable_hfp_hf;PA_LLIST_HEAD(pa_dbus_pending, pending);
};. . . . . .
static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m,DBusPendingCallNotifyFunction func, void *call_data) {pa_dbus_pending *p;DBusPendingCall *call;pa_assert(backend);pa_assert(m);pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1));p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data);PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p);dbus_pending_call_set_notify(call, func, p, NULL);return p;
}. . . . . .
static void register_profile_reply(DBusPendingCall *pending, void *userdata) {DBusMessage *r;pa_dbus_pending *p;pa_bluetooth_backend *b;pa_bluetooth_profile_t profile;pa_assert(pending);pa_assert_se(p = userdata);pa_assert_se(b = p->context_data);pa_assert_se(profile = (pa_bluetooth_profile_t)p->call_data);pa_assert_se(r = dbus_pending_call_steal_reply(pending));if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {pa_log_info("Couldn't register profile %s because it is disabled in BlueZ", pa_bluetooth_profile_to_string(profile));profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE);goto finish;}if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {pa_log_error(BLUEZ_PROFILE_MANAGER_INTERFACE ".RegisterProfile() failed: %s: %s", dbus_message_get_error_name(r),pa_dbus_get_error_message(r));profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE);goto finish;}profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_REGISTERED);finish:dbus_message_unref(r);PA_LLIST_REMOVE(pa_dbus_pending, b->pending, p);pa_dbus_pending_free(p);
}static void register_profile(pa_bluetooth_backend *b, const char *object, const char *uuid, pa_bluetooth_profile_t profile) {DBusMessage *m;DBusMessageIter i, d;dbus_bool_t autoconnect;dbus_uint16_t version, chan;pa_assert(profile_status_get(b->discovery, profile) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE);pa_log_debug("Registering Profile %s %s", pa_bluetooth_profile_to_string(profile), uuid);pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MANAGER_INTERFACE, "RegisterProfile"));dbus_message_iter_init_append(m, &i);pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &object));pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &uuid));dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRINGDBUS_TYPE_STRING_AS_STRINGDBUS_TYPE_VARIANT_AS_STRINGDBUS_DICT_ENTRY_END_CHAR_AS_STRING,&d);if (pa_bluetooth_uuid_is_hsp_hs(uuid)) {/* In the headset role, the connection will only be initiated from the remote side */autoconnect = 0;pa_dbus_append_basic_variant_dict_entry(&d, "AutoConnect", DBUS_TYPE_BOOLEAN, &autoconnect);chan = HSP_HS_DEFAULT_CHANNEL;pa_dbus_append_basic_variant_dict_entry(&d, "Channel", DBUS_TYPE_UINT16, &chan);/* HSP version 1.2 */version = 0x0102;pa_dbus_append_basic_variant_dict_entry(&d, "Version", DBUS_TYPE_UINT16, &version);}dbus_message_iter_close_container(&i, &d);profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_REGISTERING);send_and_add_to_pending(b, m, register_profile_reply, (void *)profile);
}. . . . . .
static void profile_init(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) {static const DBusObjectPathVTable vtable_profile = {.message_function = profile_handler,};const char *object_name;const char *uuid;pa_assert(b);switch (profile) {case PA_BLUETOOTH_PROFILE_HSP_HS:object_name = HSP_AG_PROFILE;uuid = PA_BLUETOOTH_UUID_HSP_AG;break;case PA_BLUETOOTH_PROFILE_HSP_AG:object_name = HSP_HS_PROFILE;uuid = PA_BLUETOOTH_UUID_HSP_HS;break;case PA_BLUETOOTH_PROFILE_HFP_HF:object_name = HFP_AG_PROFILE;uuid = PA_BLUETOOTH_UUID_HFP_AG;break;default:pa_assert_not_reached();break;}pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(b->connection), object_name, &vtable_profile, b));profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE);register_profile(b, object_name, uuid, profile);
}. . . . . .
static void native_backend_apply_profile_registration_change(pa_bluetooth_backend *native_backend, bool enable_shared_profiles) {if (enable_shared_profiles) {profile_init(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG);if (native_backend->enable_hfp_hf)profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF);} else {profile_done(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG);if (native_backend->enable_hfp_hf)profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF);}
}. . . . . .pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_shared_profiles) {pa_bluetooth_backend *backend;DBusError err;pa_log_debug("Bluetooth Headset Backend API support using the native backend");backend = pa_xnew0(pa_bluetooth_backend, 1);backend->core = c;dbus_error_init(&err);if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) {pa_log("Failed to get D-Bus connection: %s", err.message);dbus_error_free(&err);pa_xfree(backend);return NULL;}backend->discovery = y;backend->enable_shared_profiles = enable_shared_profiles;backend->enable_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(y);backend->enable_hsp_hs = pa_bluetooth_discovery_get_enable_native_hsp_hs(y);backend->adapter_uuids_changed_slot =pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), PA_HOOK_NORMAL,(pa_hook_cb_t) adapter_uuids_changed_cb, backend);if (!backend->enable_hsp_hs && !backend->enable_hfp_hf)pa_log_warn("Both HSP HS and HFP HF bluetooth profiles disabled in native backend. Native backend will not register for headset connections.");if (backend->enable_hsp_hs)profile_init(backend, PA_BLUETOOTH_PROFILE_HSP_HS);if (backend->enable_shared_profiles)native_backend_apply_profile_registration_change(backend, true);return backend;
}

pulseaudio 中用 pa_bluetooth_backend 对象描述蓝牙后端对象,pa_bluetooth_native_backend_new() 函数创建 pa_bluetooth_backend 对象,注册蓝牙控制器/适配器 UUID 改变处理程序,并根据配置初始化不同的 profile。配置具体指 enable_hsp_hsenable_hfp_hfenable_shared_profiles,它们主要来源于 module-bluez5-discover 模块的模块参数。

HSP 是蓝牙协议栈中最早的耳机配置文件,主要用于支持蓝牙耳机和手机之间的基本音频通信,它支持单声道音频传输(通常用于语音通话),和基本的远程控制功能(如接听/挂断电话),主要用于简单的蓝牙耳机,功能较为基础。HFP 是 HSP 的升级版,提供了更丰富的功能,主要用于支持免提设备(如车载蓝牙系统)与手机之间的通信,它支持单声道音频传输(语音通话),更复杂的远程控制功能(如音量控制、重拨、拒接电话等),电话状态信息(如电池电量、信号强度等),及多方通话和语音识别,主要用于车载蓝牙系统、高端蓝牙耳机等。

enable_hfp_hf 用于配置是否开启 HFP,默认为 tureenable_hsp_hs 用于配置是否开启 HSP,默认为 falseenable_shared_profiles 根据 headset 蓝牙后端的配置得到,headset 蓝牙后端为 native 时为 ture,否则为 false,默认为 ture。默认初始化的 profile 如下:

(4480.011|   0.000) D: [pulseaudio] backend-native.c: Registering Profile headset_audio_gateway 00001108-0000-1000-8000-00805f9b34fb
(4480.011|   0.000) D: [pulseaudio] backend-native.c: Registering Profile handsfree_head_unit 0000111f-0000-1000-8000-00805f9b34fb

即初始化 profile PA_BLUETOOTH_PROFILE_HSP_AGPA_BLUETOOTH_PROFILE_HFP_HF

dbus_connection_register_object_path() 是 D-Bus 库的一个函数,用于向 D-Bus 连接注册一个对象路径。这个对象路径表示一个在 D-Bus 总线上可以被其它应用程序调用的方法、信号或者属性。通过注册对象路径,可以定义应用程序在 D-Bus 上的接口,使其可以被其它应用程序访问和调用。初始化 profile 由 profile_init() 函数处理,主要是为 profile 注册对应的对象路径,并通过 D-BUS 向蓝牙服务 BlueZ 注册 profile。不同 profile 的对象路径不同,它们的对应关系如下:

  • PA_BLUETOOTH_PROFILE_HSP_HSHSP_AG_PROFILE (/Profile/HSPAGProfile)
  • PA_BLUETOOTH_PROFILE_HSP_AGHSP_HS_PROFILE (/Profile/HSPHSProfile)
  • PA_BLUETOOTH_PROFILE_HFP_HFHSP_AG_PROFILE (/Profile/HFPAGProfile)

对象路径上的处理程序为 profile_handler() 函数。向蓝牙服务 BlueZ 注册 profile,将调用 BlueZ 注册的 org.bluez.ProfileManager1 接口的 RegisterProfile 方法。

对于 HFP/HSP 协议栈,蓝牙音频设备在物理上与蓝牙控制器/适配器建立连接之后,系统蓝牙服务 bluez 会收到通知,在回调中处理连接,并通过 D-BUS 调用 pulseaudio 注册的 profile 的 D-BUS 对象路径上的处理程序 profile_handler()

下面是蓝牙耳机连上蓝牙服务 BlueZ 时的一段日志:

2月 17 14:41:45 workstation bluetoothd[17672]: src/adapter.c:connected_callback() hci0 device 24:D0:DF:96:AD:6E connected eir_len 18
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avctp.c:avctp_confirm_cb() AVCTP: incoming connect from 24:D0:DF:96:AD:6E
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avctp.c:avctp_set_state() AVCTP Connecting
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avctp.c:avctp_connect_cb() AVCTP: connected to 24:D0:DF:96:AD:6E
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avctp.c:init_uinput() AVRCP: uinput initialized for AirPods Pro
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avrcp.c:controller_init() 0xaaab0f5d2f90 version 0x0105
2月 17 14:41:46 workstation bluetoothd[17672]: src/service.c:change_state() 0xaaab0f5bf950: device 24:D0:DF:96:AD:6E profile audio-avrcp-target state changed: disconnected -> connected (0)
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avrcp.c:target_init() 0xaaab0f5e00e0 version 0x0105
2月 17 14:41:46 workstation bluetoothd[17672]: src/service.c:change_state() 0xaaab0f5cafb0: device 24:D0:DF:96:AD:6E profile avrcp-controller state changed: disconnected -> connected (0)
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avctp.c:avctp_set_state() AVCTP Connected
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avrcp.c:handle_vendordep_pdu() AVRCP PDU 0x10, company 0x001958 len 0x0001
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avrcp.c:avrcp_handle_get_capabilities() id=3
2月 17 14:41:46 workstation bluetoothd[17672]: src/profile.c:ext_confirm() incoming connect from 24:D0:DF:96:AD:6E
2月 17 14:41:46 workstation bluetoothd[17672]: src/service.c:btd_service_ref() 0xaaab0f5db3e0: ref=3
2月 17 14:41:46 workstation bluetoothd[17672]: src/profile.c:ext_confirm() Hands-Free Voice gateway authorizing connection from 24:D0:DF:96:AD:6E
2月 17 14:41:46 workstation bluetoothd[17672]: src/profile.c:ext_auth() 24:D0:DF:96:AD:6E authorized to connect to Hands-Free Voice gateway
2月 17 14:41:46 workstation bluetoothd[17672]: src/profile.c:ext_connect() Hands-Free Voice gateway connected to 24:D0:DF:96:AD:6E
2月 17 14:41:46 workstation bluetoothd[17672]: src/service.c:change_state() 0xaaab0f5db3e0: device 24:D0:DF:96:AD:6E profile Hands-Free Voice gateway state changed: disconnected -> connecting (0)
2月 17 14:41:46 workstation bluetoothd[17672]: src/service.c:change_state() 0xaaab0f5db3e0: device 24:D0:DF:96:AD:6E profile Hands-Free Voice gateway state changed: connecting -> connected (0)
2月 17 14:41:46 workstation bluetoothd[17672]: src/device.c:device_profile_connected() Hands-Free Voice gateway Success (0)
2月 17 14:41:46 workstation bluetoothd[17672]: plugins/policy.c:service_cb() Added Hands-Free Voice gateway reconnect 1
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avrcp.c:handle_vendordep_pdu() AVRCP PDU 0x31, company 0x001958 len 0x0005
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/a2dp.c:confirm_cb() AVDTP: incoming connect from 24:D0:DF:96:AD:6E
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/sink.c:sink_set_state() State changed /org/bluez/hci0/dev_24_D0_DF_96_AD_6E: SINK_STATE_DISCONNECTED -> SINK_STATE_CONNECTING
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_connect_cb() AVDTP: connected signaling channel to 24:D0:DF:96:AD:6E
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_connect_cb() AVDTP imtu=672, omtu=1023
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_register_remote_sep() seid 1 type 1 media 0 delay_reporting true
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/a2dp.c:register_remote_sep() Found remote SEP: /org/bluez/hci0/dev_24_D0_DF_96_AD_6E/sep1
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_register_remote_sep() seid 2 type 1 media 0 delay_reporting true
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/a2dp.c:register_remote_sep() Found remote SEP: /org/bluez/hci0/dev_24_D0_DF_96_AD_6E/sep2
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_register_remote_sep() seid 3 type 1 media 0 delay_reporting true
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/a2dp.c:register_remote_sep() Found remote SEP: /org/bluez/hci0/dev_24_D0_DF_96_AD_6E/sep3
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/a2dp.c:load_remote_sep() LastUsed: lseid 4 rseid 1
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_ref() 0xaaab0f5e7c20: ref=1
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:set_disconnect_timer() timeout 1
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:session_cb()
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_parse_cmd() Received DISCOVER_CMD
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:session_cb()
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_parse_cmd() Received  GET_ALL_CAPABILITIES_CMD
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/a2dp.c:endpoint_getcap_ind() Source 0xaaab0f5b0030: Get_Capability_Ind
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:session_cb()
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_parse_cmd() Received  GET_ALL_CAPABILITIES_CMD
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/a2dp.c:endpoint_getcap_ind() Source 0xaaab0f5ce470: Get_Capability_Ind
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:session_cb()
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_parse_cmd() Received  GET_ALL_CAPABILITIES_CMD
2月 17 14:41:46 workstation bluetoothd[17672]: profiles/audio/a2dp.c:endpoint_getcap_ind() Source 0xaaab0f5d11e0: Get_Capability_Ind
2月 17 14:41:47 workstation bluetoothd[17672]: profiles/audio/avdtp.c:session_cb()
2月 17 14:41:47 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_parse_cmd() Received  GET_ALL_CAPABILITIES_CMD
2月 17 14:41:47 workstation bluetoothd[17672]: profiles/audio/a2dp.c:endpoint_getcap_ind() Source 0xaaab0f5d1480: Get_Capability_Ind
2月 17 14:41:47 workstation bluetoothd[17672]: profiles/audio/avdtp.c:session_cb()
2月 17 14:41:47 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_parse_cmd() Received  GET_ALL_CAPABILITIES_CMD
2月 17 14:41:47 workstation bluetoothd[17672]: profiles/audio/a2dp.c:endpoint_getcap_ind() Source 0xaaab0f5c0720: Get_Capability_Ind
2月 17 14:41:47 workstation bluetoothd[17672]: profiles/audio/avdtp.c:session_cb()
2月 17 14:41:47 workstation bluetoothd[17672]: profiles/audio/avdtp.c:avdtp_pa

相关文章:

Linux 系统蓝牙音频服务实现分析

Linux 系统蓝牙音频服务实现分析 蓝牙音频设备连接管理Linux 系统中,蓝牙音频服务实现为系统音频服务 PulseAudio 的可加载模块,它用来以 PulseAudio 标准的方式描述蓝牙音频设备,将其嵌入 PulseAudio 的音频处理流水线,并呈现给用户,支持用户切换音频设备,如蓝牙耳机。 …...

PyTorch 深度学习实战(14):Deep Deterministic Policy Gradient (DDPG) 算法

在上一篇文章中,我们介绍了 Proximal Policy Optimization (PPO) 算法,并使用它解决了 CartPole 问题。本文将深入探讨 Deep Deterministic Policy Gradient (DDPG) 算法,这是一种用于连续动作空间的强化学习算法。我们将使用 PyTorch 实现 D…...

craftjs的示例landing项目改成APP路由

下载项目 项目地址是:https://github.com/prevwong/craft.js 示例项目在examples文件夹下面landing文件夹 修改 1.修改依赖包 由于craftjs使用的多包管理,示例项目中craftjs/core和craftjs/layers使用的是工作区路径,这里需要修改版本 …...

java -jar 执行基于Maven构建的Java应用的方法总结

一、Maven pom.xml文件未指定主类的情况 1、用Maven打包 mvn clean package -DskipTests 2、用java命令执行jar包 java -cp maven-allin-mainclass-demo-1.0-SNAPSHOT.jar org.example.Main 二、Maven pom.xml文件指定主类的情况 1、pom.xml文件指定主类,有两种…...

前端发布缓存导致白屏解决方案

解决发布H5后因为本地缓存白屏方案 一、 核心配置优化(前提是访问网站的请求能抵达服务器) 方案一:前端项目设置全局不缓存方案 运行逻辑:在H5服务器配置中增加Cache-Control: no-cache或max-age0响应头,禁用静态资…...

【后端】【django】Django 自带的用户系统与 RBAC 机制

Django 自带的用户系统与 RBAC 机制 Django 自带的用户系统(django.contrib.auth)提供了 身份验证(Authentication) 和 权限管理(Authorization),能够快速实现 用户管理、权限控制、管理员后台…...

SpringBoot MCP 入门使用

随着AI的火爆,最近发现MCP在未来确实大有可为,作为一名javaer怎么可以落后在历史洪流呢,根据官网和cursor也从零开始体验一下自定义mcp server。以后可以根据自己业务场景做出各种适合自身业务的工具。 至于什么是MCP 可以到https://modelcon…...

Java使用JDBC连接操作Sqlite 笔记250314

Java使用JDBC连接操作Sqlite 以下是使用 Java JDBC 连接和操作 SQLite 数据库的详细步骤&#xff1a; 1. 添加 SQLite JDBC 驱动 在项目中引入 SQLite JDBC 驱动依赖。 Maven 项目在 pom.xml 中添加&#xff1a;<dependency><groupId>org.xerial</groupId>…...

每日一题---腐烂的苹果(广度优先搜索)

腐烂的苹果 给定一个 nm nm 的网格&#xff0c;其中每个单元格中可能有三种值中的一个 0 , 1 , 2。 其中 0 表示这个格子为空、1 表示这个格子有一个完好的苹果&#xff0c;2 表示这个格子有一个腐烂的苹果。 腐烂的苹果每分钟会向上下左右四个方向的苹果传播一次病菌&…...

Visual Studio里的调试(debugging)功能介绍

参考 1- Introduction to Debugging | Basic Visual Studio Debugging&#xff08;这是一位印度博主视频&#xff0c;我下面做到笔记也主要参考她的视频&#xff0c;但不得不说口音太重了&#xff0c;一股咖喱味&#xff09; 目录 个人对调试浅显的认识和对调试的介绍逐行调…...

10.2linux内核定时器实验(详细编程)_csdn

我尽量讲的更详细&#xff0c;为了关注我的粉丝&#xff01;&#xff01;&#xff01; 本章使用通过设置一个定时器来实现周期性的闪烁 LED 灯&#xff0c;因此本章例程就使用到了一个LED 灯。 这里我们以毫秒为单位&#xff0c;所以要用msecs_to_jiffies这个函数。 如果是2s就…...

机器学习——正则化、欠拟合、过拟合、学习曲线

过拟合&#xff08;overfitting&#xff09;:模型只能拟合训练数据的状态。即过度训练。 避免过拟合的几种方法&#xff1a; ①增加全部训练数据的数量&#xff08;最为有效的方式&#xff09; ②使用简单的模型&#xff08;简单的模型学不够&#xff0c;复杂的模型学的太多&am…...

Java多线程与高并发专题——阻塞和非阻塞队列的并发安全原理是什么?

引入 之前我们探究了常见的阻塞队列的特点&#xff0c;在本文我们就以 ArrayBlockingQueue 为例&#xff0c;首先分析 BlockingQueue &#xff0c;也就是阻塞队列的线程安全原理&#xff0c;然后再看看它的兄弟——非阻塞队列的并发安全原理。 ArrayBlockingQueue 源码分析 …...

git 撤销某次提交的上交到远程服务器的commit提交,此提交后面的commit需要保留【deeepseek生成】

核心思路 使用 git rebase -i 重写提交历史&#xff0c;删除目标提交后强制推送到远程&#xff08;需谨慎操作&#xff09;。 操作步骤 1. 确认要删除的提交位置 # 查看提交历史&#xff08;找到要删除的提交哈希&#xff0c;例如 a1b2c3d&#xff09; git log --oneline查看提…...

docker composeyaml文件,什么是swap-space,内存不足硬盘来凑,--ipc=host,yaml文件、环境变量、容器报警健康检查

--swap-space 参数明确针对的是系统内存&#xff08;RAM&#xff09;&#xff0c;与显存&#xff08;GPU Memory&#xff09;无关。以下是关键区分&#xff1a; 内存&#xff08;RAM&#xff09; vs 显存&#xff08;GPU Memory&#xff09; 类型内存&#xff08;RAM&#xff…...

tsfresh:时间序列特征自动提取与应用

tsfresh&#xff1a;时间序列特征自动提取与应用 本文系统介绍了 tsfresh 技术在 A 股市场数据分析与量化投资中的应用。从基础特征提取到高级策略开发&#xff0c;结合实战案例&#xff0c;详细讲解了如何利用 tsfresh 构建量化投资策略&#xff0c;并优化风险控制&#xff0c…...

【A2DP】深入解读A2DP中通用访问配置文件(GAP)的互操作性要求

目录 一、模式支持要求 1.1 发现模式 1.2 连接模式 1.3 绑定模式 1.4 模式间依赖关系总结 1.5 注意事项 1.6 协议设计深层逻辑 二、安全机制&#xff08;Security Aspects&#xff09; 三、空闲模式操作&#xff08;Idle Mode Procedures&#xff09; 3.1 支持要求 …...

CUDA编程之内存

CUDA的内存类型有全局内存、共享内存、常量内存、纹理内存、本地内存、寄存器等。我们需要分别了解它们的特点和使用场景。在CUDA编程中&#xff0c;合理利用各种内存类型对性能优化至关重要。 1. ‌全局内存&#xff08;Global Memory&#xff09;‌ ‌特点‌&#xff1a;设…...

【Agent实战】货物上架位置推荐助手(RAG方式+结构化prompt(CoT)+API工具结合ChatGPT4o能力Agent项目实践)

本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 结论 效果图示 1.prompt 2. API工具封…...

ffmpeg面试题整理

1. 基础概念 问题&#xff1a;FFmpeg 是什么&#xff1f;它的核心功能有哪些&#xff1f; 编解码&#xff1a;支持几乎所有音视频格式&#xff08;如 H.264, AAC, MP3&#xff09;。转换&#xff1a;在不同容器格式之间转换&#xff08;如 MP4 → MKV&#xff09;。流处理&…...

Idea运行项目报错:java.lang.OutOfMemoryError: Java heap space 解决方法

问题描述 Maven构建的时候&#xff0c;一直报错java.lang.OutOfMemoryError: Java heap space 尝试解决 找了几个JAVA高级小伙伴&#xff0c;一起去百度了各种可能&#xff0c;设置内存大小&#xff0c;发现都不行&#xff0c;还不断的重装了IDEA&#xff0c;以为是这个版本…...

解决 Linux /dev/mapper/ubuntu--vg-ubuntu--lv 磁盘空间不足的问题

解决 Linux /dev/mapper/ubuntu–vg-ubuntu–lv 磁盘空间不足的问题 https://blog.csdn.net/weixin_47908992/article/details/139882219 查看LVM卷组的信息 vgdisplay rootubuntu:~# vgdisplay--- Volume group ---VG Name ubuntu-vgSystem ID Fo…...

前端UI编程基础知识:基础三要素(结构→表现→行为)

以下是重新梳理的前端UI编程基础知识体系&#xff0c;结合最新技术趋势与实战要点&#xff0c;以更适合快速掌握的逻辑结构呈现&#xff1a; 一、基础三要素&#xff08;结构→表现→行为&#xff09; 1. HTML5 核心能力 • 语义化标签&#xff1a;<header>, <nav&g…...

Trae:与AI结伴,开启编程新体验

Trae&#xff1a;与AI结伴&#xff0c;开启编程新体验 在数字化时代&#xff0c;编程已经成为推动技术发展的核心力量。然而&#xff0c;随着项目复杂度的增加&#xff0c;开发者面临着诸多挑战&#xff0c;例如代码编写效率低下、代码质量难以把控等。如今&#xff0c;Trae作…...

如何用正则表达式爬取古诗文网中的数据(python爬虫)

一、了解正则表达式的基本内容&#xff1a; 什么是正则表达式 正则表达式&#xff08;Regular Expression&#xff0c;简称 regex&#xff09;是一种用于匹配字符串的模式。它通过特定的语法规则&#xff0c;可以高效地搜索、替换和提取文本中的特定内容。正则表达式广泛应用于…...

深度学习 Deep Learning 第1章 深度学习简介

第1章 深度学习简介 概述 本章介绍人工智能&#xff08;AI&#xff09;和深度学习领域&#xff0c;讨论其历史发展、关键概念和应用。解释深度学习如何从早期的AI和机器学习方法演变而来&#xff0c;以及如何有效解决之前方法无法应对的挑战。 关键概念 1. 人工智能的演变 …...

ByteByteGo学习笔记:通知系统设计

引言 在当今这个信息爆炸的时代&#xff0c;通知系统已经成为了现代应用程序中不可或缺的重要组成部分。无论是突发新闻的即时推送、产品更新的及时告知、促销活动的精准触达&#xff0c;还是用户交互的实时反馈&#xff0c;通知都扮演着至关重要的角色。一个高效、可靠、可扩…...

[设计模式]1_设计模式概览

摘要&#xff1a;设计模式原则、设计模式的划分与简要概括&#xff0c;怎么使用重构获得设计模式并改善代码的坏味道。 本篇作概览与检索用&#xff0c;后续结合源码进行具体模式深入学习。 目录 1、设计模式原理 核心原则&#xff08;语言无关&#xff09; 本质原理图 原…...

Python + Qt Designer构建多界面GUI应用程序:Python如何调用多个界面文件

引言 Qt Designer是一个用户友好的图形用户界面设计工具&#xff0c;它可以帮助开发人员通过拖放的方式快速创建界面。在实际开发中&#xff0c;往往需要设计多个界面文件&#xff0c;并在Python代码中进行统一管理和使用。本文将介绍如何在Python中使用Qt Designer设计好的多…...

AGI大模型(7):提示词应用

1 生成数据 LLM具有⽣成连贯⽂本的强⼤能⼒。使⽤有效的提示策略可以引导模型产⽣更好、更⼀致和更真实的响应。LLMs还可以特别有⽤地⽣成数据,这对于运⾏各种实验和评估⾮常有⽤。例如,我们可以使⽤它来为情感分类器⽣成快速样本,如下所示: 提示: ⽣成10个情感分析的范…...

【倒霉bug2025】找不到vc_runtimeMinimum_x64.msi

今天是倒霉的一天&#xff0c;当喉咙痛到无法出门玩耍的我打开steam准备开始玩《冰封世界》时&#xff0c;游戏启动直接报错 在选择安装之后弹出一个经典窗口 然后在C:\ProgramData\PackageCache中找msi到位置点击确定继续报错说msi版本不对 上网一搜&#xff0c;找不到vc_ru…...

什么是强哈希算法pbkdf2(Password-Based Key Derivation Function)

文章目录 什么是pbkdf2使用场景 在线工具 什么是pbkdf2 维基百科&#xff1a;https://zh.wikipedia.org/zh-cn/PBKDF2 PBKDF2&#xff08;Password-Based Key Derivation Function 2&#xff09;是一种基于密码的密钥派生函数。它的主要作用是从密码和盐&#xff08;salt&…...

Python 基础语法详解

一、变量和数据类型 变量 在 Python 中&#xff0c;变量无需声明类型&#xff0c;直接赋值即可。变量名区分大小写。 # 整数类型 age 25 print(age) # 输出&#xff1a;25# 浮点数类型 height 1.75 print(height) # 输出&#xff1a;1.75# 字符串类型 name "张三&…...

AI Agent 时代开幕-Manus AI与OpenAI Agent SDK掀起新风暴

【本周AI新闻: AI Agent 时代开幕-Manus AI与OpenAI Agent SDK掀起新风暴】 https://www.bilibili.com/video/BV1bkQyYCEvQ/?share_sourcecopy_web&vd_source32ed33e1165d68429b2e2eb4749f3f26 最近AI圈子里最火的话题非Manus莫属&#xff01;这款由中国武汉创业公司“蝴…...

为什么会出现redis数据库?redis是什么?

什么是 Redis? 为什么要用 Redis? 下面我将从 Redis 出现的背景、Redis 的解决方案个来回答。 1、Redis 出现的背景 互联网的应用越来越多&#xff0c;例如社交网络、电商、实时服务发展的十分迅速&#xff0c;这就导致了传统技术栈&#xff08;如关系型数据库&#xff09;…...

每日一题---dd爱框框(Java中输入数据过多)

dd爱框框 实例&#xff1a; 输入&#xff1a; 10 20 1 1 6 10 9 3 3 5 3 7 输出&#xff1a; 3 5 这道题要解决Java中输入的数过多时&#xff0c;时间不足的的问题。 应用这个输入模板即可解决&#xff1a; Java中输入大量数据 import java.util.*; import java.io.*;pu…...

Flink-学习路线

最近想学习一下Flink&#xff0c;公司的实时需求还是不少的&#xff0c;因此结合ai整理了一份学习路线&#xff0c;记录一下。 当然&#xff0c;公司也有Scala版本Flink框架&#xff0c;也学习了一下。这里只说Java版本 1. Java基础 目标: 掌握Java编程语言的基础知识。 内容…...

一次Milvus迁移的记录

前言 希望把Linux上生产环境中使用docker compose运行的milvus迁移到本地(mac os)的docker compose中 操作过程 找到了官方有两个相关的项目&#xff1a; https://github.com/zilliztech/milvus-backup https://github.com/zilliztech/vts 但是…我都没用&#xff0c;因为使…...

矩阵的转置

对于的矩阵&#xff0c;使用两个指针变量&#xff0c;可以方便实现(i,j)处元素与(j,i)处元素交换位置。令指针Arow&A[i][0]&#xff0c;则Arow[j]可实现对第i行j列元素的访问。令指针Bptr&A[0][i]&#xff0c;则*Bptr就可以访问(0,i)处元素&#xff0c;然后&#xff0c…...

使用 VLOOKUP 和条件格式在 Excel 中查找并标红匹配的串号

使用 VLOOKUP 和条件格式在 Excel 中查找并标红匹配的串号 你的步骤非常详细且清晰&#xff0c;能够帮助用户在 Excel 中通过 VLOOKUP 和条件格式来查找并标红匹配的串号。以下是对你提供的步骤的简要总结和补充说明&#xff1a; 1. 添加“是否匹配”列 在 a.xlsx 中新增一列…...

Python Matplotlib面试题精选及参考答案

目录 绘制函数 y=2x+5 在区间 [1,10] 的折线图,设置标题和坐标轴标签 在同一图中绘制 sin (x) 和 cos (x) 曲线,添加图例和网格线(x∈[0,2π]) 绘制分段函数:当 x<0 时 y=0,x≥0 时 y=x,设置不同线段颜色 绘制带数据点的折线图,使用红色虚线样式和圆形标记(数据…...

在线 SQL 转 SQLAlchemy:一键生成 Python 数据模型

一款高效的在线 SQL 转 SQLAlchemy 工具&#xff0c;支持自动解析 SQL 语句并生成 Python SQLAlchemy 模型代码&#xff0c;适用于数据库管理、后端开发和 ORM 结构映射。无需手写 SQLAlchemy 模型&#xff0c;一键转换 SQL 结构&#xff0c;提升开发效率&#xff0c;简化数据库…...

基于自定义线程池手写一个异步任务管理器

我们在后端执行某些耗时逻辑操作时往往会导致长时间的线程阻塞&#xff0c;在这种情况之下&#xff0c;我们往往会引一条异步线程去处理这些异步任务&#xff0c;如果每次都创建新的线程来处理这些任务&#xff0c;不仅会增加代码冗余&#xff0c;还可能造成线程管理混乱&#…...

基恩士PLC编程小技巧八:脚本过长如何实现换行及替换

基恩士PLC编程小技巧八&#xff1a;脚本过长如何实现换行&#xff1f; 一、问题点 我们在使用基恩士编程软件KV STUDIO 进行脚本编程时&#xff0c;经常遇到这样的问题&#xff1a;脚本的一行过长&#xff0c;程序不好阅读及维护。 IF MR1000 OR MR1001 OR MR1002 OR MR1003 OR…...

每日一题---数组中两个字符串的最小距离

数组中两个字符串的最小距离 给定一个字符串数组strs&#xff0c;再给定两个字符串str1和str2&#xff0c;返回在strs中str1和str2的最小距离&#xff0c;如果str1或str2为null&#xff0c;或不在strs中&#xff0c;返回-1。 链接&#xff1a;数组中两个字符串的最小距离__牛…...

【PTA题目解答】7-1利用STL比较数据大小并排序(15分)c++

1.题目&#xff1a; 2.算法原理 根据题目要求&#xff0c;模拟即可&#xff0c;set容器会帮我们把插入的数自动排序好 题目说输入非整型数据就停止&#xff0c;不用特意判断输入的数据是整型还是非整型&#xff0c;如果用户输入的是字符&#xff08;例如 a&#xff09;&#…...

如何用Deepseek制作流程图?

使用Deepseek制作流程图&#xff0c;本质上是让AI根据你的需求&#xff0c;生成相关流程图的代码&#xff0c;然后在流程图编辑器中渲染&#xff0c;类似于Python一样&#xff0c;ChatGPT可以生成代码&#xff0c;但仍需在IDE中执行。 你知道绘制流程图最高效的工具是什么吗&a…...

【09】单片机编程核心技巧:变量赋值,从定义到存储的底层逻辑

【09】单片机编程核心技巧&#xff1a;变量赋值&#xff0c;从定义到存储的底层逻辑 &#x1f31f; 核心概念 单片机变量的定义与赋值是程序设计的基础&#xff0c;其本质是通过 RAM&#xff08;随机存储器&#xff09; 和 ROM&#xff08;只读存储器&#xff09; 的协作实现…...

vscode python相对路径的问题

vscode python相对路径的问题 最近使用使用vscode连接wsl2写python时&#xff0c;经常遇到找不到包中的方法的问题&#xff0c;最终发现vscode在执行python代码时目录不是从当前python文件开始算起&#xff0c;而是从当前工作区的目录开始算起&#xff0c;比如说我打开的是/ho…...

C语言中的指针与函数

引言 在C语言编程中,指针是一个非常重要且强大的概念。它不仅帮助我们高效地管理内存,还能提升程序的灵活性和性能。而指针与函数的结合使用,是C语言中非常常见且极具挑战性的一个话题。正确理解和使用指针与函数的关系,不仅能帮助程序员提高代码质量,还能优化程序的执行…...