Files
Obsidian/项目文档/同力/蓝牙/HFP通话音频切换无声_BSP分析与修复验证_2026-07-28.md
T
2026-11-05 14:35:04 +08:00

195 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HFP 通话音频切换无声问题分析与修复验证
## 1. 问题现象
蓝牙通话过程中,先将通话音频切换到手机端,等待车机扬声器输出进入 standby 后,再切回车机端,车机喇叭没有声音。
## 2. 分析资料
- 异常日志:`C:\Users\Administrator\Desktop\同力日志\tongli_2026_07_28_15_59_31.log`
- 修复后日志:`C:\Users\Administrator\Desktop\同力日志\tongli_2026_07_28_19_50_02.log`
- 蓝牙 SCO PCM:`cq_blink_soc_read.pcm`
- AudioTrack 写入前 PCM:`cq_blink_play.pcm`
- 系统源码:`Y:\tongli\android14.0_rk3562`
## 3. 蓝牙侧确认
蓝牙厂商提供的两份 PCM 数据均正常:
- `cq_blink_soc_read.pcm`:蓝牙 SCO 声卡读取到的下行通话数据正常。
- `cq_blink_play.pcm`:经过蓝牙侧处理后,在调用 `AudioTrack.write()` 前的数据正常。
日志显示 AudioTrack 能正常创建、写入并累计 delivered frames。采样率和声道转换关系也符合当前实现:
```text
蓝牙 SCO 8000 Hz / 单声道 / S16LE
AudioTrack 16000 Hz / 双声道 / PCM 16-bit
Audio HAL 48000 Hz / 双声道 / S16LE
```
因此,蓝牙 PCM 输入和 AudioTrack 写入前的数据不是本次无声问题的主要原因,排查范围应落在 AudioTrack 后面的系统音频输出链路:
```text
AudioTrack
-> AudioFlinger
-> AudioPolicy
-> Rockchip Audio HAL
-> Speaker PCM
-> Codec / 功放 / 喇叭
```
## 4. 异常日志时间线
```text
16:00:15.147 HAL通过start_output_stream打开Speaker,device=0x2
16:00:18.464 Telecom设置adev_set_mode=2,即AUDIO_MODE_IN_CALL
16:00:27.886 音频切到手机端,蓝牙AudioTrack停止
16:00:30.915 AudioFlinger空闲约3秒,HAL进入standby并关闭Speaker PCM
16:00:36.907 音频切回车机,蓝牙重新创建AudioTrack
16:00:38.054 AudioTrack音量恢复为1.0
16:00:36.420
至16:01:24 HAL持续高频执行do_out_standby/close device,喇叭无声
```
异常期间,HAL每秒约90次执行以下流程:
```text
get_presentation_position: Operation not permitted
do_out_standby, device=0x2
close device
```
与此同时,蓝牙AudioTrack仍持续写入且帧数增长。这表明HAL虽然向上层返回了“数据已消费”,但数据没有真正送入Speaker PCM。
## 5. 系统源码根因
源码文件:
```text
Y:\tongli\android14.0_rk3562\hardware\rockchip\audio\tinyalsa_hal\audio_hw.c
```
目标函数:
```c
static int start_output_stream(struct stream_out *out)
```
原代码约位于第1374行:
```c
if (adev->mode == AUDIO_MODE_IN_CALL) {
return 0;
}
```
该代码只要发现系统处于 `AUDIO_MODE_IN_CALL`,就直接返回成功,但没有打开扬声器 PCM。
切换到手机端后,原有Speaker PCM会在空闲超时后关闭。再次切回车机时,`start_output_stream()` 因上述判断直接返回,导致 `out->pcm[]` 中没有有效的Speaker PCM。
后续 `out_write()` 的行为如下:
1. 没有找到有效的 `out->pcm[i]`,`ret` 保持为 `-1`。
2. 错误恢复分支调用 `do_out_standby()`,再次关闭输出流。
3. HAL最终仍向AudioTrack返回 `bytes`,所以上层认为写入成功。
4. 下一次写入重复同样过程,形成高频 `standby/close` 循环。
当前HFP Client通话下行链路为:
```text
蓝牙SCO PCM -> 蓝牙处理 -> AudioTrack -> Speaker PCM
```
该链路不属于Rockchip基带Voice Session。`AUDIO_MODE_IN_CALL` 只表示Framework处于通话模式,不能单独证明基带Voice Session正在占用声卡。
同一文件中的 `out_write()` 和 `do_out_standby()` 已使用 `voice_is_in_call(adev)` 判断真实Voice Session,只有 `start_output_stream()` 的判断条件不一致。
## 6. 修复方案
修改前:
```c
if (adev->mode == AUDIO_MODE_IN_CALL) {
return 0;
}
```
修改后:
```c
if ((adev->mode == AUDIO_MODE_IN_CALL) && voice_is_in_call(adev)) {
return 0;
}
```
修改含义:
- 系统处于通话模式,且真实Rockchip Voice Session正在运行时,保持原逻辑,不重复打开声卡。
- 系统处于通话模式,但HFP Client通过AudioTrack播放时,允许重新打开Speaker PCM。
## 7. 修复后验证
修复后已完成多轮“手机端/车机端”音频切换,实车确认切回车机后喇叭声音能够恢复。
关键验证时间线一:
```text
19:51:19.207 切到手机端,AudioTrack停止
19:51:22.237 Speaker输出进入standby
19:51:22.263 通话模式下成功执行start_output_stream,device=0x2
19:51:22.804 蓝牙重新创建AudioTrack
19:51:23.946 AudioTrack音量恢复为1.0
```
关键验证时间线二,Speaker关闭后等待约9秒再切回:
```text
19:52:40.288 adev_set_mode=2,即AUDIO_MODE_IN_CALL
19:52:44.501 切到手机端,AudioTrack停止
19:52:47.501 Speaker输出进入standby
19:52:56.608 成功重新执行start_output_stream,device=0x2
19:52:57.115 蓝牙重新创建AudioTrack
19:52:58.262 AudioTrack音量恢复为1.0
```
修复前后日志统计对比:
| 日志 | `do_out_standby` | `get_presentation_position`告警 | 完整`start_output_stream` |
| --- | ---------------: | ----------------------------: | ----------------------: |
| 修复前 | 18648次 | 19645次 | 19次 |
| 修复后 | 13次 | 13次 | 13次 |
修复后,高频约90次/秒的 `standby/close` 失败循环已经消失。剩余 `get_presentation_position` 告警只在输出流启动阶段出现一次,不再持续刷屏。
日志中的 `pcm_read error: File descriptor in bad state` 均出现在SCO关闭、蓝牙音频切到手机端之后,属于停止读取过程中的收尾现象,不影响本次修复结论。
## 8. 责任归属
本问题主责建议归属Android BSP/Rockchip Audio HAL层:
- Framework/Telecom设置 `AUDIO_MODE_IN_CALL` 符合通话流程。
- AudioTrack能够正常创建和写入。
- AudioPolicy输出设备为 `AUDIO_DEVICE_OUT_SPEAKER (0x2)`。
- 实际故障发生在Audio HAL重新打开Speaker PCM的状态判断中。
蓝牙厂商需继续配合提供SCO PCM、AudioTrack写入数据及切换时序,但本次无需修改蓝牙PCM处理逻辑。
## 9. 回归测试建议
1. 通话中执行至少5轮“车机端 -> 手机端 -> 等待超过3秒 -> 车机端”。
2. 确认每次切回车机后均出现 `start_output_stream device=0x2`。
3. 确认不再出现高频 `do_out_standby/close device` 循环。
4. 验证通话下行声音、上行麦克风和音量调节。
5. 回归蓝牙音乐、系统提示音和其他媒体播放。
6. 如果产品支持真实基带通话,回归基带Voice Session,确认 `voice_is_in_call(adev) == true` 时原有声卡独占逻辑不变。
## 10. 版本管理注意事项
当前可见的以下文件仍显示为修改前代码,Git状态也为干净:
```text
Y:\tongli\android14.0_rk3562\hardware\rockchip\audio\tinyalsa_hal\audio_hw.c
```
本次验证镜像可能来自另一份源码或构建目录,或者已验证修改尚未同步回正式仓库。正式提交和后续重新编译前,应确认实际构建源码路径,并将上述一行条件修改同步到目标仓库,避免修复丢失。