本地 TWS 与音频同步
本地 TWS(True Wireless Stereo)与音频同步是杰理 AC63 系列蓝牙 SDK 中,让左右耳两只耳机通过蓝牙互相连接、由主设备接收手机音频并编码转发、从设备解码播放,并保证两侧播放节奏一致的关键机制。本文以 localtws.h 公开接口与 SDK 中注册的 TWS 同步函数族为主线,说明其架构、控制流、配置与扩展方式。
Purpose and Scope
本文覆盖:
- 本地 TWS 模块(localtws):主设备侧编码端(
localtws_enc_api_*)与从设备侧解码端(localtws_dec_*)的接口职责,以及音频数据从 A2DP 接收到对端播放的完整链路。 - TWS 音频同步机制:SDK 中以
tws_*_sync命名的同步函数族(媒体同步、ACL 数据同步、事件同步、连接同步、LMP slot 同步、AFH 同步等)在sdk_used_list.c中的注册关系与作用。 - 相关配置开关与资源:
TCFG_USER_TWS_ENABLE、TCFG_EQ_ONLINE_ENABLE条件编译,以及 TWS 连接/断开提示音资源(tws_conn.wtg/tws_dconn.wtg)。
以下主题属于兄弟页面,不在本文展开:TWS 固件升级流程(见 apps/common/include/update_tws.h、update_tws_new.h)、TWS 连接与配对管理(见各应用目录下的 apps/*/include/bt_tws.h)、EQ 在线调音(TCFG_EQ_ONLINE_ENABLE 相关)。
Overview
在真无线立体声应用中,左右耳(或主从耳)之间通过蓝牙 ACL 链路互联。其中一只作为主设备(活动设备)与手机建立 A2DP/HFP 连接,另一只作为从设备。要让两只耳机同时播放同一段音频且听感同步,必须解决两个问题:
- 音频数据转发:主设备把 A2DP 收到的音频流按
audio_fmt描述的格式,通过localtws编码器压缩/组帧,再经蓝牙协议栈的 TWS 链路转发给从设备;从设备用localtws解码器还原成 PCM 播放。 - 播放节奏同步:两侧播放进度必须对齐。SDK 通过一组
tws_*_sync同步函数在底层协调媒体时钟(media clock)、ACL 数据时序、LMP slot、事件与跳频(AFH)信息,保证左右声道/左右耳听感一致。
SDK 的 localtws.h 是这一能力在应用层的唯一公开接口:它把所有底层实现(编码器、解码器、蓝牙同步)封装为少量 C 函数,应用层只需按"打开 → 写入/读取 → 关闭"的模型使用。底层实现在 include_lib/media/media_develop/media/application/audio_localtws.h 与 media/localtws_decoder.h 声明的库中,SDK 侧头文件仅暴露接口契约。
Architecture
下图展示本地 TWS 音频同步的整体架构:手机作为 A2DP 源,主设备完成接收与编码,通过蓝牙协议栈的 TWS 链路转发,从设备解码输出;底层同步函数族贯穿两侧,负责节奏对齐。
flowchart TD
subgraph sg_Phone["手机(A2DP 源)"]
PHONE["A2DP Sink(主设备侧接收)"]
end
subgraph sg_Master["主设备(活动设备 / 编码端)"]
A2DP["A2DP 接收 + audio_fmt 格式"]
ENC["localtws 编码器<br/>localtws_enc_api_open / write / close"]
BT_STACK["蓝牙协议栈(ACL / LMP / AFH)"]
end
subgraph sg_Slave["从设备(解码端)"]
DEC["localtws 解码器<br/>localtws_dec_open / resume / pause"]
PLAY["PCM 播放输出"]
end
subgraph sg_Sync["TWS 同步函数族(sdk_used_list.c 注册)"]
MEDIA_SYNC["tws_local_media_sync / tws_media_sync"]
ACL_SYNC["tws_acl_data_sync / tws_conn_sync"]
SLOT_SYNC["tws_lmp_slot_sync / tws_afh_sync"]
EVENT_SYNC["tws_event_sync / tws_sync_call / tws_link_sync"]
end
PHONE -->|"蓝牙链路"| A2DP
A2DP --> ENC
ENC --> BT_STACK
BT_STACK <-->|"TWS ACL 数据转发"| DEC
DEC --> PLAY
MEDIA_SYNC --> ENC
MEDIA_SYNC --> DEC
ACL_SYNC --> BT_STACK
SLOT_SYNC --> BT_STACK
EVENT_SYNC --> BT_STACK
各组成部分的职责:
- localtws 编码器(主设备侧):把 A2DP 解出的
s16PCM 数据编码为适合 TWS 链路传输的帧。localtws_enc_api_open(struct audio_fmt *pfmt, u32 flag)指定编码格式与流标志,localtws_enc_api_write(s16 *data, int len)逐块喂入音频数据。 - localtws 解码器(从设备侧):从 TWS 链路接收编码帧并解码播放。
localtws_dec_open/localtws_dec_resume/localtws_dec_pause控制解码生命周期,localtws_media_dat_abandon用于主动丢弃积压数据以追平两侧节奏。 - 蓝牙协议栈同步层:
tws_lmp_slot_sync与tws_afh_sync让两侧在物理时序上对齐(LMP 时隙与跳频),tws_acl_data_sync与tws_conn_sync保证 ACL 数据与连接状态一致,tws_event_sync/tws_sync_call负责事件与呼叫类同步,tws_media_sync/tws_local_media_sync负责媒体播放进度的对齐。 - 应用层:通过
localtws.h中少量接口完成启停、蓝牙事件分发(localtws_bt_event_deal)与使能检测(localtws_check_enable)。
架构说明:
localtws.h头文件明确指出编码器、解码器的实现分别来自application/audio_localtws.h与media/localtws_decoder.h(见 localtws.h#L4-L5),即底层以库形式提供,SDK 侧只暴露接口,这与include_lib/media目录的组织方式一致。
localtws 模块职责与接口详解
cpu/br25/localtws/localtws.h 与 cpu/br23/localtws/localtws.h 是各 CPU 平台本地 TWS 的统一入口头文件(br23 与 br25 两平台声明一致)。该头文件把整套本地 TWS 能力按"编码端 / 解码端 / 控制端"三类接口组织,并定义了数据源标志:
#ifndef __LOCALTWS_H_
#define __LOCALTWS_H_
#include "application/audio_localtws.h"
#include "media/localtws_decoder.h"
#define LOCALTWS_ENC_FLAG_STREAM BIT(0) // 数据源是流数据
// localtws检测是否使能
int localtws_check_enable(void);
// localtws蓝牙事件处理
int localtws_bt_event_deal(struct bt_event *evt);
// 打开localtws编码
int localtws_enc_api_open(struct audio_fmt *pfmt, u32 flag);
// 关闭localtws编码
void localtws_enc_api_close(void);
// localtws编码写入
int localtws_enc_api_write(s16 *data, int len);
// localtws设置等待a2dp状态
void localtws_set_wait_a2dp_start(u8 flag);
// localtws启动(活动设备主动调用)
void localtws_start(struct audio_fmt *pfmt);
// localtws停止(活动设备主动调用)
void localtws_stop(void);
// 打开localtws解码
int localtws_dec_open(u32 value);
// 关闭localtws解码
int localtws_dec_close(u8 drop_frame_start);
// localtws已经打开
u8 localtws_dec_is_open(void);
// localtws解码激活
void localtws_dec_resume(void);
// localtws抛弃数据
int localtws_media_dat_abandon(void);
// localtws暂停
void localtws_dec_pause(void);
// localtws已经开始解码
int localtws_dec_out_is_start(void);
// localtws暂停控制
void localtws_decoder_pause(u8 pause);
#endif /*__LOCALTWS_H_*/
Source: localtws.h#L1-L51
编码端(主设备侧)接口
| 函数 | 作用 | 设计意图 |
|---|---|---|
localtws_start(struct audio_fmt *pfmt) | 活动设备主动启动本地 TWS | 应用层在 A2DP 即将开始时调用,把音频格式传给编码链路 |
localtws_stop(void) | 活动设备主动停止本地 TWS | 与 start 配对,保证编码与转发链路有序释放 |
localtws_enc_api_open(struct audio_fmt *pfmt, u32 flag) | 打开编码器 | flag 携带 LOCALTWS_ENC_FLAG_STREAM 表示数据源为流式数据(而非文件/预存数据) |
localtws_enc_api_write(s16 *data, int len) | 写入 PCM 数据 | 逐块喂入 A2DP 解码后的 s16 采样,编码器内部组帧后经 TWS 链路发送 |
localtws_enc_api_close(void) | 关闭编码器 | 释放编码资源 |
localtws_set_wait_a2dp_start(u8 flag) | 设置"等待 A2DP 启动"状态 | 编码链路在 A2DP 未就绪前避免提前发送,防止从设备侧无源可播 |
解码端(从设备侧)接口
| 函数 | 作用 | 设计意图 |
|---|---|---|
localtws_dec_open(u32 value) | 打开解码器 | 从设备收到 TWS 连接后启用解码通道 |
localtws_dec_close(u8 drop_frame_start) | 关闭解码器;drop_frame_start 非 0 时丢弃起始帧 | 起始帧往往携带不完整数据,丢弃可避免爆音/杂音 |
localtws_dec_is_open(void) | 查询解码器是否已打开 | 供应用层判断链路状态,避免重复打开 |
localtws_dec_resume(void) | 激活(恢复)解码 | 收到可播放数据或 A2DP 启动后从暂停态恢复 |
localtws_dec_pause(void) | 暂停解码 | 暂停时不丢弃数据,恢复后继续播放 |
localtws_decoder_pause(u8 pause) | 解码器暂停控制(带参) | 更细粒度的暂停控制,供播放通路直接调用 |
localtws_media_dat_abandon(void) | 抛弃积压数据 | 当两侧播放进度偏差过大时丢弃缓冲数据以重新对齐——这是本地 TWS 音频同步的核心手段 |
localtws_dec_out_is_start(void) | 查询解码输出是否已开始 | 区分"已打开"与"已出数据",用于超时/卡顿检测 |
事件与使能
localtws_check_enable(void):检测本地 TWS 功能是否使能(内部通常与TCFG_USER_TWS_ENABLE及角色配置相关)。localtws_bt_event_deal(struct bt_event *evt):把蓝牙协议栈事件(连接、断开、A2DP 启停等)分发给 localtws 内部状态机,是"音频同步跟随蓝牙状态"的桥梁。
TWS 音频同步函数族
SDK 在 sdk_used_list.c 中集中注册了整套 TWS 同步函数。以 br23 平台为例:
#if TCFG_USER_TWS_ENABLE
tws_local_media_sync
#if TCFG_EQ_ONLINE_ENABLE
#endif /* #if TCFG_EQ_ONLINE_ENABLE */
tws_acl_data_sync
tws_event_sync
tws_conn_sync
tws_lmp_slot_sync
tws_media_sync
tws_sync_call
tws_link_sync
tws_afh_sync
Source: cpu/br23/sdk_used_list.c#L3-L15(bd19/bd29 平台结构相同,见 cpu/bd19/sdk_used_list.c#L3-L15)
这些符号被列入 sdk_used_list.c,说明它们属于需要链接进固件的底层同步实现(防止库裁剪时被移除)。各函数在本地 TWS 音频同步中的角色如下:
| 同步函数 | 同步对象 | 说明 |
|---|---|---|
tws_local_media_sync | 本地媒体播放 | 本地 TWS 场景下的媒体进度对齐,直接服务本文主题 |
tws_media_sync | 媒体时钟 | 两侧媒体时钟基准对齐,保证播放速率一致 |
tws_acl_data_sync | ACL 数据 | 主从之间 ACL 数据分组的时序同步,确保转发帧按序到达 |
tws_event_sync | 蓝牙事件 | 两侧对同一蓝牙事件(连接/断开/A2DP 启停)的感知一致 |
tws_conn_sync | 连接状态 | 主从设备连接关系的同步 |
tws_lmp_slot_sync | LMP 时隙 | 底层时隙级同步,是音频节奏同步的物理基础 |
tws_afh_sync | 跳频(AFH) | 跳频序列同步,保证链路可用性与转发时延稳定 |
tws_sync_call | 同步呼叫 | 电话/呼叫场景下的同步 |
tws_link_sync | 链路 | TWS 链路的整体同步控制 |
设计意图:音频同步不是单点问题——媒体层需要对齐播放进度(tws_media_sync / tws_local_media_sync),传输层需要对齐数据分组(tws_acl_data_sync),物理层需要对齐时隙与跳频(tws_lmp_slot_sync / tws_afh_sync),事件层需要对齐状态感知(tws_event_sync / tws_conn_sync)。SDK 把这一整套同步能力按层次拆分注册,让协议栈与音频通路可以独立演进。
条件编译与配套资源
TCFG_USER_TWS_ENABLE
tws_local_media_sync 等同步函数整体处于 #if TCFG_USER_TWS_ENABLE 之下(见 cpu/br23/sdk_used_list.c#L3),即用户 TWS 功能开关。关闭该宏时,整套本地 TWS 音频同步函数都不会链接进固件。
TCFG_EQ_ONLINE_ENABLE
EQ 在线调音开关在 sdk_used_list.c 中紧邻 tws_local_media_sync 出现(#if TCFG_EQ_ONLINE_ENABLE ... #endif,见 cpu/bd19/sdk_used_list.c#L3-L7),说明在线 EQ 参数同样需要走 TWS 链路同步到从设备,属于本地 TWS 数据通道的旁路能力。
提示音资源
各 CPU 的 config tool 均包含 TWS 专属提示音:
tws_conn.wtg:TWS 连接提示音(例如 cpu/br25/tools/AC696X_config_tool/conf/source/tone_file/tws_conn.wtg)tws_dconn.wtg:TWS 断开提示音(例如 cpu/br25/tools/AC696X_config_tool/conf/source/tone_file/tws_dconn.wtg)
这些提示音文件的存在说明本地 TWS 的"已连接/已断开"状态在设备端有明确的用户可感知反馈,事件同步(tws_event_sync)会驱动其播放。
Core Flow:音频数据端到端流转
本地 TWS 的核心流程是"主设备编码转发 → 从设备解码播放",两侧均受底层同步函数约束。整体时序如下:
sequenceDiagram
participant APP as 主设备应用层
participant ENC as localtws 编码器(主)
participant BT as 蓝牙协议栈(TWS 链路 + 同步)
participant DEC as localtws 解码器(从)
participant PLAY as 播放输出
APP->>BT: 蓝牙事件(TWS 连接 / A2DP 启动)
BT->>APP: localtws_bt_event_deal(evt)
APP->>APP: localtws_check_enable() 确认使能
APP->>APP: localtws_set_wait_a2dp_start(1) 等待 A2DP
APP->>ENC: localtws_start(pfmt)
APP->>ENC: localtws_enc_api_open(pfmt, LOCALTWS_ENC_FLAG_STREAM)
Note over BT: tws_lmp_slot_sync / tws_afh_sync 对齐物理时序
Note over BT: tws_media_sync / tws_local_media_sync 对齐媒体时钟
loop 音频持续播放
APP->>ENC: localtws_enc_api_write(s16 *data, len)
ENC->>BT: 编码帧(ACL 数据,tws_acl_data_sync 保序)
BT->>DEC: 转发帧
DEC->>PLAY: 解码后 PCM 播放
end
opt 两侧进度偏差过大
DEC->>DEC: localtws_media_dat_abandon() 抛弃积压数据
end
APP->>ENC: localtws_enc_api_close()
APP->>ENC: localtws_stop()
流程要点:
- 事件驱动启动:蓝牙事件先经
localtws_bt_event_deal分发,应用层随后用localtws_check_enable确认本地 TWS 使能,避免在非 TWS 场景误启动编码。 - 等待 A2DP 就绪:
localtws_set_wait_a2dp_start让编码端等到 A2DP 真正启动后再开始发送数据,防止从设备解码器无源可播。 - 编码-转发-解码:主设备把 A2DP 的
s16采样写入编码器(LOCALTWS_ENC_FLAG_STREAM标志流式数据),编码帧经 TWS ACL 链路转发;底层tws_lmp_slot_sync/tws_afh_sync/tws_acl_data_sync保证时隙、跳频与分组时序一致。 - 节奏纠偏:当两侧播放进度偏差过大时,从设备调用
localtws_media_dat_abandon丢弃缓冲数据追平主设备——这是"同步"在数据通路上的最终兜底手段。 - 有序停止:先关编码器再
localtws_stop,与启动顺序严格对称。
解码器状态流转
从 localtws.h 解码端接口(dec_open / dec_resume / dec_pause / decoder_pause / media_dat_abandon / dec_close(drop_frame_start))可以还原出解码端的生命周期状态机:
stateDiagram-v2
[*] --> IDLE
IDLE --> OPEN: localtws_dec_open(value)
OPEN --> PAUSED: localtws_dec_pause() / localtws_decoder_pause(1)
PAUSED --> PLAYING: localtws_dec_resume()
PLAYING --> PAUSED: localtws_dec_pause() / localtws_decoder_pause(1)
PLAYING --> PLAYING: localtws_media_dat_abandon() 追平进度
OPEN --> IDLE: localtws_dec_close(0)
PLAYING --> IDLE: localtws_dec_close(drop_frame_start=1) 丢弃起始帧
PAUSED --> IDLE: localtws_dec_close(0)
说明:该状态机由
localtws.h公开 API 的语义推导(如dec_close的drop_frame_start参数表明关闭时可选择丢弃起始帧、media_dat_abandon表明播放中存在主动丢数据路径)。底层库内部实现细节未在 SDK 侧头文件中展开,属于"接口可验证、内部实现以库形式提供"的部分。
Usage Examples
主设备侧:启动与编码
以下示例展示活动设备(主设备)侧的标准调用序列,接口声明摘自 localtws.h:
// 1. 蓝牙事件到达后,应用层先做使能与事件分发
if (localtws_check_enable()) {
localtws_bt_event_deal(&evt);
}
// 2. 等待 A2DP 就绪后,按音频格式启动本地 TWS 编码
struct audio_fmt fmt = { /* 由 A2DP 解码通路填充采样率/通道数等 */ };
localtws_set_wait_a2dp_start(1);
localtws_start(&fmt);
localtws_enc_api_open(&fmt, LOCALTWS_ENC_FLAG_STREAM);
// 3. 音频数据持续写入(A2DP 解码回调中调用)
s16 *pcm = /* 解码后的 PCM 数据 */;
localtws_enc_api_write(pcm, len);
// 4. 停止时按对称顺序关闭
localtws_enc_api_close();
localtws_stop();
Source: localtws.h#L7-L29(接口声明;调用序列为基于接口语义的标准用法)
从设备侧:解码与播放
从设备侧使用解码端接口,通过暂停/恢复与丢数据接口维持与主设备的同步:
// 1. TWS 链路建立后打开解码器
localtws_dec_open(0);
// 2. 有数据可播时激活解码输出
localtws_dec_resume();
// 3. 暂停场景(例如通话插入):暂停但不丢数据
localtws_dec_pause();
// 或带参暂停控制
localtws_decoder_pause(1);
// 4. 两侧进度偏差过大时,抛弃积压数据追平主设备
if (/* 进度偏差检测 */) {
localtws_media_dat_abandon();
}
// 5. 停止时按需丢弃起始帧,避免爆音
localtws_dec_close(1);
Source: localtws.h#L32-L47(接口声明;调用序列为基于接口语义的标准用法)
同步函数族注册(链接保留)
tws_local_media_sync 等底层同步实现通过 sdk_used_list.c 显式引用,防止被链接器裁剪:
#if TCFG_USER_TWS_ENABLE
tws_local_media_sync
#if TCFG_EQ_ONLINE_ENABLE
#endif /* #if TCFG_EQ_ONLINE_ENABLE */
tws_acl_data_sync
tws_event_sync
tws_conn_sync
tws_lmp_slot_sync
tws_media_sync
tws_sync_call
tws_link_sync
tws_afh_sync
Source: cpu/br23/sdk_used_list.c#L3-L15
Configuration Options
本地 TWS 与音频同步相关的配置开关集中在 SDK 的配置宏中(各应用工程的 board_config / 配置头文件定义):
| 配置项 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
TCFG_USER_TWS_ENABLE | 宏开关 | 由工程配置 | 用户 TWS 总开关;关闭后 tws_local_media_sync 等同步函数不链接进固件(见 cpu/br23/sdk_used_list.c#L3) |
TCFG_EQ_ONLINE_ENABLE | 宏开关 | 由工程配置 | EQ 在线调音开关;开启时其实现与 tws_local_media_sync 一起被保留(见 cpu/bd19/sdk_used_list.c#L5-L7) |
LOCALTWS_ENC_FLAG_STREAM | 位标志 | BIT(0) | 编码数据源标志:置位表示数据来自流(A2DP 实时流),用于编码器选择组帧/缓存策略(见 localtws.h#L7) |
注意:
TCFG_USER_TWS_ENABLE与TCFG_EQ_ONLINE_ENABLE的实际取值由各应用工程的配置头文件决定,本文仅依据sdk_used_list.c的引用关系说明其作用。
API Reference
以下为 cpu/br25/localtws/localtws.h 公开的全部接口(br23 平台声明一致)。
int localtws_check_enable(void)
检测本地 TWS 是否使能。
- Returns:非 0 表示使能,0 表示未使能。
int localtws_bt_event_deal(struct bt_event *evt)
处理本地 TWS 相关的蓝牙事件(连接/断开/A2DP 启停等)。
- Parameters:
evt(struct bt_event *)— 蓝牙协议栈事件结构。 - Returns:0 表示事件已处理。
int localtws_enc_api_open(struct audio_fmt *pfmt, u32 flag)
打开本地 TWS 编码器。
- Parameters:
pfmt(struct audio_fmt *)— 音频格式(采样率、位深、通道数等);flag(u32)— 数据源标志,可传LOCALTWS_ENC_FLAG_STREAM。 - Returns:0 表示成功,非 0 表示失败。
void localtws_enc_api_close(void)
关闭本地 TWS 编码器,释放编码资源。
int localtws_enc_api_write(s16 *data, int len)
向编码器写入 PCM 数据。
- Parameters:
data(s16 *)— PCM 采样数据;len(int)— 数据长度(采样点数)。 - Returns:0 表示成功,非 0 表示失败。
void localtws_set_wait_a2dp_start(u8 flag)
设置"等待 A2DP 启动"标志,控制编码端是否等待 A2DP 就绪后再发送。
- Parameters:
flag(u8)— 非 0 表示等待。
void localtws_start(struct audio_fmt *pfmt)
启动本地 TWS(活动设备主动调用)。
- Parameters:
pfmt(struct audio_fmt *)— 音频格式。
void localtws_stop(void)
停止本地 TWS(活动设备主动调用)。
int localtws_dec_open(u32 value)
打开本地 TWS 解码器。
- Parameters:
value(u32)— 打开参数(保留/预留)。 - Returns:0 表示成功,非 0 表示失败。
int localtws_dec_close(u8 drop_frame_start)
关闭本地 TWS 解码器。
- Parameters:
drop_frame_start(u8)— 非 0 时丢弃起始帧(避免不完整帧导致爆音)。 - Returns:0 表示成功。
u8 localtws_dec_is_open(void)
查询解码器是否已打开。
- Returns:非 0 表示已打开。
void localtws_dec_resume(void)
激活解码输出(从暂停恢复播放)。
int localtws_media_dat_abandon(void)
抛弃解码缓冲中的积压数据,用于追平与主设备的播放进度。
- Returns:0 表示成功,非 0 表示失败。
void localtws_dec_pause(void)
暂停解码(不丢数据,恢复后继续)。
int localtws_dec_out_is_start(void)
查询解码输出是否已经开始。
- Returns:非 0 表示已开始输出。
void localtws_decoder_pause(u8 pause)
带参解码暂停控制。
- Parameters:
pause(u8)— 非 0 暂停,0 恢复。
Failure Modes、边界情况与并发
时序偏差与丢数据
本地 TWS 两侧播放不同步时,从设备通过 localtws_media_dat_abandon 主动丢弃积压数据追平主设备。这是同步的最后兜底手段:如果频繁触发,说明底层 tws_lmp_slot_sync / tws_afh_sync / tws_media_sync 的节奏对齐已经出现较大偏差,应优先检查 TWS 链路质量与蓝牙时序配置,而不是依赖丢数据修正。
A2DP 未就绪时的启动
localtws_set_wait_a2dp_start 的存在表明:如果编码端在 A2DP 未启动时就开始发送,从设备将无源可播,表现为"从耳无声"。正确的调用顺序是先等待 A2DP 就绪、再 localtws_start + localtws_enc_api_open。
起始帧丢弃
localtws_dec_close(u8 drop_frame_start) 与 localtws_dec_out_is_start 两个接口揭示了起始帧处理的边界:解码刚打开时输出的帧可能不完整,关闭时按需丢弃起始帧可避免爆音;应用层可用 localtws_dec_out_is_start 判断是否已进入稳定输出。
暂停/恢复的并发语义
localtws_dec_pause / localtws_dec_resume 与 localtws_decoder_pause(pause) 并存:前者是无参的语义化接口,后者是带参的底层控制接口,二者面向不同调用层级。暂停只冻结输出、不丢弃数据,恢复后继续播放,因此播放通路与蓝牙事件通路(localtws_bt_event_deal)对解码器的并发访问需要遵循"事件驱动暂停/恢复、播放通路驱动写数据"的协作模型。
实现细节边界(诚实说明)
编码器/解码器的内部实现(编码格式、帧结构、缓冲管理)位于库中(application/audio_localtws.h、media/localtws_decoder.h),SDK 侧 localtws.h 仅公开接口契约。本文描述的调用顺序与状态流转均基于接口语义推导,未涉及库内实现细节。
Performance 与运维建议
- 流式标志:
LOCALTWS_ENC_FLAG_STREAM表明本地 TWS 的典型数据源是 A2DP 实时流,编码器按流模式处理缓存,避免为整段文件预留缓冲导致内存与延迟浪费。 - 同步函数链接保留:
sdk_used_list.c的注册方式意味着修改或裁剪同步函数会影响固件链接结果——新增平台或关闭 TWS 功能时,需同步维护该列表,防止出现"功能看似打开、实际未链接"的问题。 - 提示音反馈:
tws_conn.wtg/tws_dconn.wtg是 TWS 连接/断开状态的用户反馈资源;若更换提示音,需同时替换各 CPU config tool 下的conf/source/tone_file与conf/output/extra_tones两处,并重新生成配置。
Extension Points
- 角色区分:
localtws_start明确标注"活动设备主动调用",解码端接口则供从设备使用——新增应用时只需按角色调用对应接口组,无需改动库。 - 事件注入:
localtws_bt_event_deal(struct bt_event *evt)是外部蓝牙事件进入 localtws 的唯一入口,自定义事件(如新增连接场景)可通过该函数注入。 - 格式适配:
struct audio_fmt *pfmt贯穿编码与解码两端,接入新的音频源(如录音、提示音混音)时通过该结构描述格式即可。 - 平台头文件:
cpu/br23/localtws/localtws.h与cpu/br25/localtws/localtws.h声明一致,新平台可沿用同一接口模型。
Related Links
- cpu/br25/localtws/localtws.h — 本地 TWS 统一接口(本文核心源码)
- cpu/br23/localtws/localtws.h — br23 平台同款接口
- cpu/br23/sdk_used_list.c — TWS 同步函数族注册列表
- cpu/bd19/sdk_used_list.c — bd19 平台同步函数注册列表
- include_lib/media/media_develop/media/application/audio_localtws.h — localtws 底层库接口(库实现入口)
- apps/spp_and_le/include/bt_tws.h — TWS 连接/配对管理(兄弟页面主题)
- apps/common/include/update_tws.h — TWS 升级流程(兄弟页面主题)