以手机本地播放
test1.mp3通过 LHDC V5 编码传输到蓝牙耳机并播放为例,详细说明从文件到声音的完整技术链路。
一、整体链路概览
┌───────────────────────────── 手机(Source / A2DP Source)─────────────────────────────┐
│ │
│ test1.mp3 文件 → MP3 解码 → PCM 重采样/位深转换 → LHDC V5 编码 → A2DP 封包 → 蓝牙射频 │
│ │
└──────────────────────────────────────────────────────────────────────────────────────┘
│
▼ 蓝牙无线传输(2.4GHz ISM 频段)
│
┌───────────────────────────── 耳机(Sink / A2DP Sink)──────────────────────────────────┐
│ │
│ 蓝牙射频接收 → A2DP 解包 → LHDC V5 解码 → PCM → I2S/TDM → DAC → 功放 → 喇叭发声 │
│ │
└───────────────────────────────────────────────────────────────────────────────────────┘
整个链路可以分为 7 个大阶段,下面逐一详细说明。
二、阶段 1:MP3 文件解码(手机端)
2.1 文件读取
手机操作系统(Android / iOS)从存储(UFS / eMMC / SSD)读取 test1.mp3 文件:
- MP3 文件结构:ID3 标签(元数据:歌名、艺术家、专辑封面)+ MPEG 音频帧序列
- 每个 MP3 帧:帧头(4 字节,含同步字 0xFFE、比特率、采样率、声道模式)+ 边信息 + 主数据(Huffman 编码的频谱系数)
- 典型 MP3 帧长:48kHz/320kbps 时约 1041 字节/帧,每帧 1152 采样 = 24ms 音频
2.2 MP3 解码流程
手机使用系统内置的 MP3 解码器(Android 的 MediaCodec / AudioTrack,iOS 的 AVAudioPlayer / CoreAudio):
MP3 比特流 → 同步字检测 → 帧头解析 → 边信息解码 → Huffman 解码
→ 逆量化 → 重排序 → 立体声解码(M/S / 强度立体声)
→ 抗混叠滤波 → IMDCT → 多相合成滤波器组 → PCM
输出:32-bit float 或 16/24-bit integer 的 PCM 数据,采样率通常为 44.1kHz 或 48kHz,立体声。
注意:MP3 是有损编码,解码后的 PCM 已经不是原始录音的精确复现,存在量化噪声和预回声等伪影。但这一步与 LHDC V5 无关------LHDC V5 接收到的就是这个"已经有损一次"的 PCM。
三、阶段 2:PCM 预处理(手机端)
3.1 音频系统路由
手机的音频系统(Android AudioFlinger / iOS CoreAudio)将解码后的 PCM 路由到蓝牙输出通道:
- 混音器(Mixer):如果有多个音频流(音乐 + 通知音),在此混合
- 音量控制:应用系统音量和媒体音量
- 重采样器(Resampler):如果 MP3 采样率与蓝牙协商的采样率不一致,进行重采样
3.2 采样率协商
手机与耳机通过 A2DP 的 SDP(Service Discovery Protocol) 和 AVDTP(Audio/Video Distribution Transport Protocol) 协商音频参数:
- 耳机在 SDP 记录中声明支持的编码格式和能力(包括 LHDC V5 的 Vendor ID
0x053a(Savitech)、Codec ID、支持的采样率/位深/码率) - 手机在 SET_CONFIGURATION 命令中选择双方都支持的配置
- 典型协商结果:48kHz / 24-bit / 立体声 / 900kbps(或 Auto 模式)
3.3 位深转换
如果协商为 24-bit 输出,而 MP3 解码输出为 16-bit,则需要位深转换:
- 16→24:左移 8 位(低 8 位补零),或使用抖动(dithering)提升主观听感
- 32-bit float→24-bit:缩放 + 截断/舍入
3.4 输入到 LHDC V5 编码器
最终送入 LHDC V5 编码器的是:
- 格式:交织立体声 PCM(L R L R ...)
- 位深:16-bit(S16LE,2 字节/采样)或 24-bit(packed S24LE,3 字节/采样)
- 采样率:44.1 / 48 / 96 / 192 kHz
- 每帧采样数:240(44.1/48k)/ 480(96k)/ 960(192k),对应 5ms 帧长
四、阶段 3:LHDC V5 编码(手机端)
4.1 编码器初始化
手机蓝牙协议栈(Android Bluedroid / iOS CoreBluetooth)在 A2DP 连接建立时初始化 LHDC V5 编码器:
c
lhdcv5_enc_t *enc = lhdcv5_enc_new(LHDC_VERSION_1);
lhdcv5_enc_init_encoder(enc,
48000, // 协商采样率
24, // 协商位深
LHDC_QUALITY_HIGH, // 码率档位(900 kbps)或 LHDC_QUALITY_AUTO
LHDC_MTU_2MBPS, // 蓝牙链路 MTU(660 字节,EDR 2Mbps)
LHDC_ENC_INTERVAL_20MS); // 编码间隔(攒帧周期)
4.2 逐帧编码
音频系统以 5ms 为单位向编码器喂入 PCM 数据:
c
// 每 5ms 调用一次,喂入一帧交织立体声 PCM
lhdcv5_enc_encode(enc, pcm_frame, pcm_len,
out_buf, out_buf_len,
&written_bytes, &written_frames);
编码内部流程 (详见《 LHDC V5 编解码流程与原理详解 》):
PCM 输入
│
├─→ LD-MDCT 时频变换(时域 → 频域频谱系数)
│
├─→ SNS 频谱噪声整形(ADPCM 编码频谱包络,逐段缩放)
│
├─→ 量化 + 码率控制循环(搜索最优全局量化步长,使输出比特数落在目标范围)
│ ├─ 系数量化
│ ├─ 比特数预测(coef3 / coef7 IIR 预测器)
│ ├─ 三进制符号生成
│ └─ 算术编码比特数估算
│
├─→ 比特流打包
│ ├─ 前导信息(global_gain, sns_mode, 方向位, nzc, flag2, resid)
│ ├─ FAC 自适应算术编码(三进制符号流 → 字节流)
│ ├─ 尾数 + 符号位平面(因果 IIR 预测比特分配)
│ └─ 填充到固定帧长
│
└─→ 帧头加扰(XOR + 字节重排,前 8 字节)
│
▼
LHDC V5 编码帧(每声道固定字节数,双声道等长)
4.3 帧攒包(Packetization)
LHDC V5 编码器内部维护一个帧队列。由于每帧编码输出较小(48kHz/900kbps 时每帧约 562 字节,双声道共 1125 字节),而蓝牙 MTU 通常为 660~1023 字节,编码器会攒够一个数据包的量后一次性输出:
编码间隔 20ms = 4 帧(每帧 5ms)
攒够 4 帧后组成一个 A2DP 媒体包输出
written_bytes 在大多数调用中为 0,每隔 N 次调用返回一个完整数据包。
4.4 自适应码率(ABR,可选)
如果使用 LHDC_QUALITY_AUTO 模式,手机蓝牙协议栈会根据 A2DP 发送队列深度动态调整码率:
- 队列积压(链路质量差,重传多)→ 降低码率(如 900k → 500k → 400k)
- 队列空闲(链路质量好)→ 提升码率
- 通过
lhdcv5_enc_set_bitrate_index()无缝切换,在下一帧队列排空后生效
五、阶段 4:A2DP 封包与蓝牙传输
5.1 A2DP 媒体封包
编码后的 LHDC V5 数据被封装为 A2DP 媒体包:
┌──────────────────────────────────────────────────────────────────┐
│ A2DP 媒体包(Media Packet) │
├──────────┬──────────┬────────────────────────────────────────────┤
│ RTP 头 │ 媒体载荷头 │ LHDC V5 编码数据 │
│ (12 字节) │ (可变) │ (多个 5ms 帧拼接) │
└──────────┴──────────┴────────────────────────────────────────────┘
RTP 头字段:
- V(版本)= 2
- P(填充)= 0
- X(扩展)= 0
- CC(CSRC 计数)= 0
- M(标记)= 帧边界标记
- PT(载荷类型)= 动态分配(Vendor Specific)
- 序列号:每包递增,用于丢包检测
- 时间戳:采样时钟驱动,用于播放同步
- SSRC:同步源标识
LHDC V5 媒体载荷头(Vendor Specific Header):
- Vendor ID:
0x053a(Savitech / HWA) - Codec ID:LHDC V5 标识
- 帧信息:码率索引、采样率、位深等(部分信息在 A2DP 协商时已确定,部分在每帧头中)
5.2 L2CAP 封包
A2DP 媒体包被送入 L2CAP(Logical Link Control and Adaptation Protocol)层:
- 加入 L2CAP 头(2 字节长度 + 2 字节 Channel ID = 0x0041,A2DP 信道)
- 如果超过 L2CAP MTU(通常 1024 字节),进行分片
5.3 基带与射频
L2CAP PDU 进入蓝牙基带(Baseband):
- 前向纠错(FEC):1/3 FEC 或 2/3 FEC(增强数据率 EDR 模式下可选)
- 白化(Whitening):与信道跳频序列同步的伪随机序列异或,避免长 0/长 1
- 跳频(Frequency Hopping):蓝牙经典(BR/EDR)在 79 个 1MHz 信道间每秒跳 1600 次
- GFSK 调制(BR 1Mbps)/ π/4-DQPSK(EDR 2Mbps)/ 8DPSK(EDR 3Mbps)
- 2.4GHz ISM 频段射频发射
5.4 无线传输
- 传输距离:典型 1 ~ 10 米(Class 2 设备)
- 干扰源:WiFi(同 2.4GHz 频段)、微波炉、其他蓝牙设备、USB 3.0 等
- 重传机制:ARQ(Automatic Repeat reQuest),接收方 CRC 校验失败则请求重传
- 延迟:空中传输时间约 1 ~ 3ms,加上重传和排队,端到端无线延迟通常 10 ~ 30ms
六、阶段 5:蓝牙接收与 A2DP 解包(耳机端)
6.1 射频接收与基带解调
耳机蓝牙芯片(如 Qualcomm QCC 系列、恒玄 BES 系列、杰理 AC 系列等)执行发射的逆过程:
- 2.4GHz 射频接收 → GFSK/DQPSK 解调 → 去白化 → FEC 纠错 → CRC 校验
- 如果 CRC 失败 → 通过 ACK/NAK 机制请求重传
- 跳频同步:与手机保持相同的跳频序列和时序
6.2 L2CAP 与 A2DP 解包
- L2CAP 层:去除 L2CAP 头,重组分片
- A2DP 层:去除 RTP 头和媒体载荷头,提取 LHDC V5 编码数据
- RTP 序列号检测丢包,时间戳用于播放同步和抖动缓冲(Jitter Buffer)
6.3 抖动缓冲(Jitter Buffer)
由于无线传输延迟不稳定(抖动),耳机通常维护一个抖动缓冲区:
- 缓存若干帧已解码/待解码的音频数据
- 吸收无线传输延迟波动,避免播放卡顿
- 典型缓冲深度:50 ~ 150ms(低延迟模式可降至 20 ~ 30ms)
- 缓冲过深 → 延迟增大;缓冲过浅 → 抖动导致欠载(under-run),出现爆音/断音
七、阶段 6:LHDC V5 解码(耳机端)
7.1 解码器初始化
耳机蓝牙协议栈在 A2DP 连接建立时,根据协商的参数初始化 LHDC V5 解码器:
c
size_t ws_size = lhdc_dec_get_workspace_size(48000, 5); // 48kHz/5ms
void *ws = malloc(ws_size); // 或静态分配
lhdc_dec_config_t cfg = {
.sample_rate = LHDC_DEC_SR_48000,
.bit_depth = LHDC_DEC_BITDEPTH_24,
.frame_duration = LHDC_DEC_FRAME_5MS,
.channels = 2,
.max_frame_bytes = 1024,
};
lhdc_decoder_t *dec = lhdc_dec_init(ws, &cfg);
7.2 逐帧解码
A2DP 解包后的 LHDC V5 帧数据送入解码器:
c
lhdc_dec_decode_frame(dec, frame_data, frame_len,
pcm_out, pcm_capacity,
&consumed, &generated, &frame_info);
解码内部流程 (详见《 LHDC V5 编解码流程与原理详解 》):
LHDC V5 编码帧
│
├─→ 帧解析(2B 帧头 → payload 长度 + 标志)
│
├─→ 逐声道独立解码(ch0 = L, ch1 = R)
│ │
│ ├─→ 解扰(逆字节重排 + XOR,前 8 字节)
│ │
│ ├─→ 前导信息读取
│ │ ├─ flag (1b), global_gain (9b), sns_mode (4b)
│ │ ├─ SNS 方向位 (num_sfb-1 b)
│ │ ├─ nzc (7~9b), flag2 (1b), resid (0/4b)
│ │
│ ├─→ SNS 参数解码(adsq 逆 DPCM → 每段缩放因子 → 后处理平滑)
│ │
│ ├─→ FAC 熵解码
│ │ ├─ 自适应范围解码(滑动窗口三符号模型)
│ │ ├─ Stream1(marker plane)+ Stream2(quotient)
│ │ └─ Rice 商解码 → 量化系数高位
│ │
│ ├─→ 尾数 + 符号位平面解码
│ │ ├─ 因果 IIR 预测器分配尾数位数
│ │ ├─ 拼接高位和尾数 → 完整系数量值
│ │ ├─ 读取符号位
│ │ └─ 系数逆序(恢复自然频序)
│ │
│ ├─→ 逆量化(spectrum = quant × 2^e(global_gain))
│ │
│ ├─→ SNS 合成(逐段乘以增益,逆频谱整形)
│ │
│ ├─→ 快速 IMDCT(频域 → 时域,FFT-based 480/960/1920 点)
│ │
│ └─→ 加窗重叠相加(KBD 窗 + 50% overlap-add → 时域 PCM)
│
├─→ 立体声交织(L/R 交织)
│
├─→ 输出缩放(位深转换 + 采样率归一化 + 电平校准)
│
└─→ 错误隐藏(如检测到 FAC 失同步 → 该帧静音 + 清空重叠缓冲)
│
▼
PCM 输出(16-bit int16 或 24-bit in int32 容器,交织立体声)
7.3 解扰选择器自动重试
解码器的一个重要鲁棒性设计:由于前导信息中第 9 字节最低位选择的解扰排列在少数帧上可能错误(导致 FAC 范围解码器失同步,产生巨大的虚假系数),解码器会:
- 先用默认选择器解码
- 检测解码后 PCM 峰值是否超过 2× 满量程
- 如果异常,恢复重叠缓冲区,用翻转的选择器重解码
- 保留峰值更小的结果
这确保了即使在个别帧解扰选择错误的情况下,也能正确解码,避免爆音。
八、阶段 7:PCM 输出与发声(耳机端)
8.1 I2S/TDM 接口
解码后的 PCM 数据通过数字音频接口发送到 DAC(数模转换器):
- I2S(Inter-IC Sound) :最常用的 3 线接口(BCLK 位时钟、LRCK 左右声道时钟、SD 串行数据)
- 48kHz/立体声/24-bit:BCLK = 48000 × 2 × 32 = 3.072 MHz(24-bit 在 32-bit 槽中传输)
- TDM(Time Division Multiplexing):多声道时分复用,高端耳机/降噪耳机可能使用
- DSD(Direct Stream Digital):少数高端设备支持,但 LHDC V5 输出为 PCM,不涉及
数据通过 DMA(Direct Memory Access)从解码器内存缓冲区自动搬运到 I2S 发送 FIFO,无需 CPU 干预。
8.2 DAC 数模转换
DAC 将数字 PCM 转换为模拟电信号:
- Δ-Σ DAC(Delta-Sigma) :绝大多数消费级音频 DAC 使用此架构
- 过采样(Oversampling):通常 32×~256× 过采样,将量化噪声推到高频
- 噪声整形(Noise Shaping):反馈环路将噪声推向人耳不敏感的高频段
- 1-bit 或 multi-bit 调制
- 信噪比(SNR):典型 95~120 dB
- 总谐波失真(THD):典型 -90~-105 dB
8.3 模拟信号处理
DAC 输出的模拟信号经过:
- 低通滤波(LPF):去除 Δ-Σ 调制产生的高频噪声和镜像频谱
- 音量控制:模拟电位器或数字音量控制(通常在 DAC 前端数字域完成)
- EQ 均衡器:部分耳机有自定义 EQ 调音(通过数字 IIR/FIR 滤波器实现,在 PCM 域处理)
- 主动降噪(ANC):如果耳机支持 ANC,在此阶段混合反相声波(通常通过独立的 ANC DSP 芯片处理)
8.4 功放与喇叭
- 耳机功放(Headphone Amplifier) :将模拟信号放大到足以驱动喇叭的功率
- 典型输出功率:1~10 mW(32Ω 负载)
- 类 AB 或 D 类功放
- 喇叭(Speaker Driver) :
- 动圈式(Dynamic):最常见,音圈在磁场中振动带动振膜
- 动铁式(Balanced Armature):高端入耳式耳机常用,体积小、解析力高
- 圈铁混合:低频动圈 + 高频动铁
- 发声原理:交变电流通过音圈 → 在磁场中受力振动 → 带动振膜推动空气 → 产生声波 → 人耳感知为声音
九、端到端延迟分析
| 环节 | 典型延迟 | 说明 |
|---|---|---|
| MP3 解码 | 5~20ms | 系统音频解码缓冲 |
| PCM 预处理 | <1ms | 重采样/位深转换 |
| LHDC V5 编码 | <1ms | 5ms 帧,编码计算 <1ms |
| 编码攒包 | 0~20ms | 取决于编码间隔(10/20ms) |
| A2DP 封包 | <1ms | 协议栈处理 |
| 蓝牙无线传输 | 10~30ms | 空中传输 + 重传 + 排队 |
| 抖动缓冲 | 20~150ms | 吸收抖动,可配置 |
| LHDC V5 解码 | <2ms | 5ms 帧,双声道 <2ms(ESP32) |
| I2S/DAC 缓冲 | 5~20ms | DMA 缓冲 + DAC FIFO |
| 模拟滤波/功放 | <1ms | 模拟电路延迟 |
| 总计 | 40~250ms | 低延迟模式可降至 ~40ms |
LHDC V5 的 5ms 帧长设计是其低延迟特性的关键------相比传统 SBC 的 20 ~ 40ms 帧长,编码/解码延迟大幅降低。
十、音质损失链路分析
从 test1.mp3 到耳机发声,信号经历了多次有损处理:
原始录音(模拟/高码率 PCM)
│
├─→ MP3 编码(第一次有损):320kbps CBR,典型 SNR ~95dB
│
├─→ MP3 解码 → PCM
│
├─→ LHDC V5 编码(第二次有损):900kbps,典型 THD ~-64dB(量化噪声底)
│
├─→ 蓝牙无线传输(可能丢包/误码):FEC + ARQ 保护,通常无误码
│
├─→ LHDC V5 解码 → PCM
│
├─→ DAC(非线性失真 + 量化噪声):SNR ~100dB
│
└─→ 喇叭(频响不平 + 失真):取决于耳机品质
关键观察:
- LHDC V5 在 900kbps 码率下的量化噪声底约 -64dB,远低于 MP3 320kbps 的失真水平,因此第二次有损编码的损伤通常被第一次 MP3 编码的损伤掩盖
- 如果源文件是无损格式(FLAC / WAV / ALAC),LHDC V5 900kbps 的音质损失才成为主导因素
- LHDC V5 支持最高 192kHz/24bit,对于高解析度音源(Hi-Res Audio),其编码质量明显优于 SBC / AAC
- 蓝牙无线传输本身通常不引入音质损失(FEC + ARQ 确保无差错),除非链路极差导致丢包隐藏(丢包帧用静音/插值替代)
十一、关键协议与标准对照
| 层级 | 协议/标准 | 作用 |
|---|---|---|
| 应用层 | A2DP(Advanced Audio Distribution Profile) | 蓝牙音频分发配置文件,定义音频流传输流程 |
| 应用层 | AVDTP(Audio/Video Distribution Transport Protocol) | 音视频分发传输协议,A2DP 的信令通道 |
| 表示层 | LHDC V5 Codec | 音频编解码标准(Savitech 私有,谷歌 AOSP 开源实现) |
| 会话层 | SDP(Service Discovery Protocol) | 服务发现,协商编码能力 |
| 传输层 | L2CAP(Logical Link Control and Adaptation Protocol) | 逻辑链路控制与适配,分片/重组 |
| 链路层 | Baseband + LMP | 基带控制 + 链路管理,跳频/功率控制 |
| 物理层 | Bluetooth BR/EDR Radio | 2.4GHz 射频,GFSK/DQPSK 调制 |
十二、与其他蓝牙编码链路的对比
| 编码 | 典型码率 | 帧长 | 编码延迟 | 音质(主观) | 支持平台 |
|---|---|---|---|---|---|
| SBC | 128~328kbps | 20~40ms | 高 | 一般 | 所有蓝牙设备(强制) |
| AAC | 128~256kbps | 20~40ms | 高 | 较好 | iOS / 部分 Android |
| aptX | 352kbps | 4~11ms | 低 | 较好 | 高通芯片设备 |
| aptX HD | 576kbps | 4~11ms | 低 | 好 | 高通芯片设备 |
| LDAC | 330~990kbps | 可配 | 中 | 很好 | 索尼 / Android 8.0+ |
| LHDC V5 | 64~1000kbps | 5ms 固定 | 很低 | 很好 | 华为/小米/OPPO 等 / Android |
LHDC V5 的核心优势在于固定 5ms 低延迟帧长 + 高码率高解析度支持的组合,在保证音质的同时实现了低延迟,适合游戏、视频等对延迟敏感的场景。