Lisa BT Audio Framework
蓝牙音频框架 - 支持 A2DP/HFP 的模块化音频处理框架
📋 目录
架构概览
分层设计
┌─────────────────────────────────────────┐
│ 应用层 (Application) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 场景适配层 (bt_sink/bt_source) │
│ - 接收蓝牙事件 │
│ - 管理 session 生命周期 │
│ - 连接 session 与硬件(回调机制) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 核心框架层 (core/) │
│ - Session 管理 │
│ - 编解码处理 │
│ - 缓冲管理 │
│ - 线程调度 │
└─────────────────────────────────────────┘
↓ ↓
┌────────────────────┐ ┌──────────────────┐
│ 蓝牙协议栈适配 │ │ 硬件接口抽象 │
│ (bt_audio_adapter)│ │ (interfaces/) │
│ - A2DP/HFP 桥接 │ │ - lisa_audio │
└────────────────────┘ │ - null_audio │
└──────────────────┘
数据流向
Sink 模式(接收音频)
蓝牙数据 → bt_sink → session 解码 → 回调 → 硬件播放
Source 模式(发送音频)
应用数据 → bt_source → session 编码 → 定时器 → 蓝牙发送
核心特性
✨ 解耦设计
Session 独立:不依赖硬件,通过回调机制交互
场景适配:bt_sink/bt_source 连接 session 与硬件
硬件抽象:支持多种音频接口实现
🎯 编解码支持
SBC:A2DP 标准编解码
mSBC:HFP 宽带语音(16kHz)
CVSD:HFP 窄带语音(8kHz)
🔄 模式支持
编码模式:自动编解码,动态缓冲
透传模式:直接传输,零开销
📊 性能优化
动态缓冲分配(根据采样率计算)
预填充机制(防止启动卡顿)
零拷贝队列传输
快速开始
Sink 场景(蓝牙耳机/音箱)
接收并播放蓝牙音频(A2DP 音乐播放)
#include "bt_sink/bt_sink.h"
#include "bt_audio_adapter.h"
void app_init(void) {
// 1. 初始化框架
bt_audio_framework_init();
// 2. 初始化 bt_sink(会自动注册到 adapter)
bt_sink_init();
// 3. 对接蓝牙协议栈
os_task_cb_t *cb = bt_audio_adapter_get_os_task_cb();
aud_os_init(cb); // 将回调注册到蓝牙栈
// 完成!等待蓝牙连接,音频会自动播放
}
数据流:
蓝牙 A2DP → adapter → bt_sink → session 解码 → lisa_audio → 扬声器
Source 场景(蓝牙电话)
发送音频到蓝牙(A2DP/HFP 音频发送)
#include "bt_source/bt_audio_interface_virtual.h"
void app_init(void) {
// 1. 初始化虚拟接口
bt_vintf_init();
// 2. 打开播放流(A2DP)
vintf_open_info_t open_info = {
.type = VINTF_PROFILE_PLAYBACK,
.open_complete_callback = on_open_complete,
};
vintf_profile_open(&open_info);
}
void send_audio(void) {
// 写入 PCM 数据,框架自动编码并发送
uint8_t pcm_data[320]; // 16kHz, 16bit, mono, 10ms
vintf_playback_write(pcm_data, sizeof(pcm_data));
}
数据流:
应用 PCM → bt_source → session 编码 → 定时器发送 → 蓝牙
配置选项
基础配置
# 启用框架
CONFIG_LISA_BT_AUDIO_FRAMEWORK=y
# 最大会话数
CONFIG_LISA_BT_AUDIO_MAX_SESSIONS=2
# 线程配置
CONFIG_LISA_BT_AUDIO_DECODE_TASK_STACK_SIZE=4096
CONFIG_LISA_BT_AUDIO_DECODE_TASK_PRIORITY=5
CONFIG_LISA_BT_AUDIO_PLAYBACK_TASK_STACK_SIZE=2048
CONFIG_LISA_BT_AUDIO_PLAYBACK_TASK_PRIORITY=6
场景选择
# Sink 场景(接收音频)
CONFIG_LISA_BT_AUDIO_INTERFACE_LOCAL=y
# Source 场景(发送音频)
CONFIG_LISA_BT_AUDIO_INTERFACE_VIRTUAL=y
# 空接口(测试用)
CONFIG_LISA_BT_AUDIO_INTERFACE_NULL=y
Codec 支持
CONFIG_AUDIO_CODEC_SBC=y # A2DP 标准
CONFIG_AUDIO_CODEC_MSBC=y # HFP 宽带
缓冲区配置
# PCM 缓冲区时长(毫秒)
CONFIG_LISA_BT_AUDIO_PCM_RINGBUF_TIME_MS_DOWNLINK=300
CONFIG_LISA_BT_AUDIO_PCM_RINGBUF_TIME_MS_UPLINK=300
# 工作缓冲区时长(毫秒)
CONFIG_LISA_BT_AUDIO_WORK_BUFFER_TIME_MS=100
# 预填充时长(毫秒)
CONFIG_LISA_BT_AUDIO_PCM_PREFILL_TIME_MS=60
调试选项
CONFIG_LISA_BT_AUDIO_DEBUG=y # 启用详细日志
支持的 Codec
Codec |
采样率 |
比特率 |
应用场景 |
|---|---|---|---|
SBC |
16/32/44.1/48 kHz |
可变 |
A2DP 音乐播放 |
mSBC |
16 kHz |
固定 |
HFP 宽带语音 |
CVSD |
8 kHz |
64 kbps |
HFP 窄带语音 |
LC3 |
8-48 kHz |
可变 |
LE Audio |
使用场景
场景 1:蓝牙音箱(Sink)
需求:接收手机音乐,通过扬声器播放
配置:
CONFIG_LISA_BT_AUDIO_FRAMEWORK=y
CONFIG_LISA_BT_AUDIO_INTERFACE_LOCAL=y # 使用 lisa_audio 硬件
CONFIG_AUDIO_CODEC_SBC=y # 启用 SBC 解码
实现:
bt_audio_framework_init();
bt_sink_init(); // 自动处理蓝牙音频接收和播放
场景 2:蓝牙电话(Source)
需求:将麦克风音频发送到蓝牙耳机
配置:
CONFIG_LISA_BT_AUDIO_FRAMEWORK=y
CONFIG_LISA_BT_AUDIO_INTERFACE_VIRTUAL=y # 虚拟接口(Source模式)
CONFIG_AUDIO_CODEC_MSBC=y # HFP 宽带语音
实现:
bt_vintf_init();
vintf_profile_open(&open_info);
// 持续调用 vintf_playback_write 发送音频
场景 3:音频算法处理
需求:接收蓝牙音频,进行 AI 处理后播放
方案 1:使用回调获取 PCM
// bt_sink 会通过回调传递解码后的 PCM
static int pcm_callback(const void *pcm, size_t size, void *user_data) {
// 在这里进行 AI 处理
ai_process(pcm, size);
// 处理后写入硬件
lisa_audio_write(pcm, size);
return size;
}
方案 2:使用 null_audio 接口
CONFIG_LISA_BT_AUDIO_INTERFACE_NULL=y // 空接口,不自动播放
API 参考
核心框架 API
// bt_audio_framework.h
bt_audio_error_t bt_audio_framework_init(void);
bt_audio_error_t bt_audio_framework_deinit(void);
Sink 场景 API
// bt_sink/bt_sink.h
bt_audio_error_t bt_sink_init(void);
bt_audio_error_t bt_sink_deinit(void);
Source 场景 API
// bt_source/bt_audio_interface_virtual.h
// 初始化
bt_audio_error_t bt_vintf_init(void);
bt_audio_error_t bt_vintf_deinit(void);
// 打开/关闭音频流
bt_audio_error_t vintf_profile_open(vintf_open_info_t *info);
bt_audio_error_t vintf_profile_close(void);
// 发送音频数据
int vintf_playback_write(const void *buffer, size_t size);
// 模式配置
bt_audio_error_t bt_vintf_set_encode_mode(bool encode_pcm);
bt_audio_error_t bt_vintf_set_frame_params(uint32_t duration_us, size_t size_bytes);
Session API(高级用法)
// core/bt_audio_session.h
// 创建和销毁
bt_audio_error_t bt_audio_session_create(
const bt_audio_session_config_t *config,
bt_audio_session_handle_t *session);
bt_audio_error_t bt_audio_session_destroy(bt_audio_session_handle_t session);
// 启动和停止
bt_audio_error_t bt_audio_session_start(
bt_audio_session_handle_t session,
const bt_audio_codec_config_t *codec_config);
bt_audio_error_t bt_audio_session_stop(bt_audio_session_handle_t session);
// 数据接口
bt_audio_error_t bt_audio_session_playback_write(
bt_audio_session_handle_t session,
const void *data, size_t size);
bt_audio_error_t bt_audio_session_capture_write_pcm(
bt_audio_session_handle_t session,
const void *pcm_data, size_t size);
bt_audio_error_t bt_audio_session_capture_read_frame(
bt_audio_session_handle_t session,
void *buffer, size_t buffer_size, size_t *frame_size);
Adapter API
// bt_audio_adapter.h
bt_audio_error_t bt_audio_adapter_init(void);
os_task_cb_t *bt_audio_adapter_get_os_task_cb(void);
int bt_audio_adapter_send_frames(uint8_t conidx, const uint8_t *data,
size_t frame_count, size_t total_size);
文件结构
lisa_bt_audio_framework/
├── 📄 README.md # 本文档
├── 📄 Kconfig # 配置选项
├── 📄 CMakeLists.txt # 构建配置
│
├── 📄 bt_audio_adapter.c/.h # 蓝牙协议栈适配器
│
├── 📁 core/ # 🎯 核心框架
│ ├── bt_audio_types.h # 基础类型定义
│ ├── aud_common.h # 音频通用定义
│ ├── bt_audio_framework.c/.h # 框架初始化
│ ├── bt_audio_session_mgr.c # 会话管理(编解码+缓冲+线程)
│ ├── bt_audio_session.h # 会话 API
│ └── bt_audio_codec_mgr.c # Codec 注册管理
│
├── 📁 codecs/ # 🎵 编解码器
│ ├── bt_audio_codec.h # Codec 接口定义
│ ├── bt_audio_codec_sbc.c # SBC 编解码
│ ├── bt_audio_codec_msbc.c # mSBC 编解码
│ └── bt_audio_codec_lc3.c # LC3 编解码
│
├── 📁 bt_sink/ # 🔊 Sink 场景(接收音频)
│ ├── bt_sink.c/.h # Sink 适配器
│ └── CMakeLists.txt # 构建配置
│
├── 📁 bt_source/ # 🎤 Source 场景(发送音频)
│ ├── bt_audio_interface_virtual.c/.h # 虚拟接口实现
│ └── CMakeLists.txt # 构建配置
│
└── 📁 interfaces/ # 🔌 硬件接口抽象
├── bt_audio_interface.h # 接口定义
├── bt_audio_interface_mgr.c # 接口注册管理
├── lisa_audio_interface.c # lisa_audio 硬件封装
├── null_audio_interface.c # 空实现(测试用)
└── CMakeLists.txt # 构建配置
目录说明
目录 |
职责 |
依赖关系 |
|---|---|---|
core/ |
核心会话管理、编解码调度 |
不依赖硬件,通过回调交互 |
codecs/ |
各种 Codec 实现 |
独立模块,按需编译 |
bt_sink/ |
Sink 场景适配器 |
依赖 core + interfaces |
bt_source/ |
Source 场景适配器 |
依赖 core,无硬件依赖 |
interfaces/ |
硬件接口实现 |
可替换,支持多种实现 |
关键设计原则
1. 解耦设计
Session 独立于硬件:通过回调机制(
playback_data_callback,capture_data_callback)与外部交互场景适配层:bt_sink/bt_source 负责连接 session 和硬件
硬件抽象:通过
bt_audio_interface_ops_t支持多种硬件实现
2. 回调机制
// Session 配置时注册回调
bt_audio_session_config_t config = {
.playback_data_callback = on_pcm_data, // Session → 外部
.capture_data_callback = request_data, // 外部 → Session
};
3. 零拷贝传输
使用队列传输编码帧(帧对齐保证)
PCM 数据使用环形缓冲区
避免不必要的内存拷贝
4. 动态资源分配
缓冲区大小根据采样率动态计算
透传模式按需分配资源
PSRAM 用于大缓冲区
常见问题
Q: 如何选择 Sink 还是 Source?
Sink:接收蓝牙音频(耳机、音箱)
Source:发送音频到蓝牙(电话、对讲)
Q: 编码模式 vs 透传模式?
编码模式:框架自动编解码,适合标准应用
透传模式:直接传输已编码数据,适合特殊需求
Q: 如何获取解码后的 PCM?
在 session 配置中注册 playback_data_callback:
static int on_pcm(const void *pcm, size_t size, void *user_data) {
// 处理 PCM 数据
return size;
}
Q: 如何优化延迟?
减小缓冲区时长(
PCM_RINGBUF_TIME_MS)减小预填充时长(
PCM_PREFILL_TIME_MS)提高线程优先级
性能指标
指标 |
典型值 |
说明 |
|---|---|---|
启动延迟 |
60-100ms |
取决于预填充配置 |
内存占用 |
50-100KB |
包含缓冲区和线程栈 |
CPU 占用 |
5-15% |
SBC 解码,48kHz 立体声 |
线程数量 |
2-4个 |
decode/encode/playback 线程 |
示例项目
samples/network/bt_audio_sink- Sink 场景示例samples/network/bt_audio_source- Source 场景示例samples/network/bt_audio_hfp_source- HFP 全双工示例
最后更新:2026-02-10