KunSpace
返回博客

TV 切到 HDMI-IN:Audio Patch 代码流程梳理

2026年7月23日
4 次阅读

读完了?别空手走啊 😏

白嫖可以,但点个赞显得有教养

写评论

有想法?直接在这说,不用跑留言板

这篇写什么?

电视切到 HDMI-IN(外接机顶盒 / 游戏机等)时,声音往往不是「普通 App 播一段 AudioTrack」,而是 HAL 里建一条 Audio Patch:一边从 HDMI 输入读 PCM,一边写到本机输出。链路长、还要和 Dolby MS12、原有 mixer 线程打交道,log 一长串容易晕。

本文按调用顺序把这条路径捋直,方便对照 log 与源码。

一句话:

adev_create_audio_patch
  → create_patch / create_patch_l
  →(如需)清理 MS12 连续模式、usecase 校验
  → 启动 input 线程 + output 线程
  → ALSA 读入 → aml_ringbuffer → out_write_new → mixer / MS12 → 喇叭

HDMI-IN Audio Patch 主流程

DTV 一类实现结构类似(输入/输出线程 + 中间缓冲),可对照:

DTV Patch 双线程示意


一、入口:adev_create_audio_patch

作用:创建音频补丁,把「音频源」接到「输出设备」。

大致步骤:

  1. 初始化、检查设备类型与参数是否合法
  2. 把 Android 音频设备类型转成 HAL 内部类型
  3. 调用 create_patch 真正建 patch,并配好路由
  4. 相关格式按 PCM 等约定落下

可以把它理解成:「告诉 HAL——从现在起,HDMI-IN 的数据要送到本机出声口。」


二、create_patchcreate_patch_l

函数 做什么
create_patch 加互斥锁,保护共享状态,再调 create_patch_l
create_patch_l 分配并初始化 aml_audio_patch,按设备/格式填参数

参数配好后,拉起两条线程(任一步失败要释放资源):

  • audio_patch_input_threadloop:输入
  • audio_patch_output_threadloop:输出

中间用 aml_ringbuffer 解耦:输入只管往里写,输出只管往外读。


三、切 HDMI-IN 前:MS12 连续模式要先收一收

若当前在用 Dolby MS12,且处于 continuous(连续)音频模式,建 patch 前通常要:

  1. get_dolby_ms12_cleanup:把 MS12 设成 非 continuous,并清理(停相关线程、重置格式、释放资源,continuous_audio_mode = 0
  2. 更新设备状态,记录退出 MS12 的时间点
  3. usecase_change_validate_l:校验/更新输出流用例;常会把当前 mixer 线程相关路径置到合适状态(如 standby),避免和 HDMI-IN 新路径抢写

流程图里常强调:clean MS12 要发生在 output 侧某些写入之前,顺序错了容易卡音或状态错乱。

同时系统里往往还有一条 一直在跑的 mixer 线程(按键音等)。HDMI-IN patch 起来后,可能出现 两条输出路径并存,后面会靠 main / aux mixer 写入函数分工。


四、输入线程:audio_patch_input_threadloop

职责:从 HDMI-IN(ALSA)把数据读进环形缓冲。

adev_open_input_stream
  → while (!input_thread_exit)
       start_input_stream(按需)
       aml_alsa_input_read
         · 信号不稳 → ring_buffer_clear,等待
         · 有数据   → ring_buffer_write → aml_ringbuffer
  → adev_close_input_stream

要点:

  • input_thread_exit 控制退出
  • 不稳定时先清缓冲,避免脏数据把输出线程拖垮
  • 输入线程 不直接碰喇叭,只负责「采进来」

五、输出线程:audio_patch_output_threadloop

职责:从环形缓冲取数,写到输出通路。

5.1 启动

  1. 若有活跃 direct 流 → do_output_standby_l;若仍挂着 MS12 → 再清理
  2. adev_open_output_stream_new 打开输出流,分配 buffer
  3. 设线程名、优先级,必要时绑核

5.2 循环主体

while (!output_thread_exit)
  根据格式 / 游戏低延迟模式决定 write_bytes
  若 ringbuffer 数据不够 → 按格式等待
  若够 → ring_buffer_read
       →(可选)格式转换等
       → out_write_new(...)
→ do_output_standby_l
→ adev_close_output_stream_new

5.3 out_write_new 里常看的几步

调用 作用
get_sink_format / MS12 相关查询 看当前 sink、MS12 状态
usecase_change_validate_l 用例是否变化,要不要换写入函数
write_func_p(...) 真正写入(指向不同 mixer/process 函数)
update_audio_format 更新内部格式(常用于信息条显示等)

六、usecase_change_validate_l:谁来写硬件?

用例(usecase)一变,写入函数可能切换,例如:

写入函数 典型场景
mixer_main_buffer_write 主缓冲路径
mixer_aux_buffer_write 辅助缓冲(多路并存时常见)
process_buffer_write 其它处理路径

待机时会重置用例掩码,并给 MS12 发相应消息。
没有用例变化时,则沿用已选好的 write 函数,保证行为稳定。

HDMI-IN 起来后若与原 mixer 并存,log 里常能看到 双 output 线程 / aux 写入 相关痕迹——对照本节看会清晰很多。


七、mixer_main_buffer_writeconfig_output

主缓冲写入时大致会:

  1. 有数据 → 写硬件、恢复定时器等;无数据 → 按空转策略处理
  2. 若是 HDMI / SPDIF输入格式变化(例如编码类型从一种变成另一种)→ 重新配输出,必要时 重建 MS12(create ms12)
  3. 特殊格式(如 DTS CD)走专用分支
  4. 按是否 MS12 库选择渲染/同步路径

config_output() 是重配核心,尤其判断是否 eDolbyMS12Lib

  • 不支持当前压缩格式 → 关旧解码器 → 按新配置再 init
  • 设置 continuous / OTT 输入等 MS12 参数
  • 非 MS12 也有类似「重置 + 初始化」,只是没有 MS12 专有项
  • 需要时做 AV 同步相关处理

因此:切 HDMI-IN 后若中途片源格式变了,log 里出现 recreate MS12,往往是预期行为,不是偶然报错。


八、和 DTV Patch 对照(同一套骨架)

DTV 流程(create_dtv_patch_l 等)同样是:

主控线程
  ├─ 输入线程:取 ES → 打包 → 写入 Audio Data 缓冲
  └─ 输出线程:取出 → 解码 → out_write_new → HAL out

和 HDMI-IN 的「input loop / output loop / 中间 buffer」是同一类设计;差别主要在 数据从哪来(HDMI ALSA PCM vs 数字电视 ES 包)以及解码落在哪一层。


九、对照 log 时怎么抓重点

建议按时间线搜这些关键字(名称因分支略有差异):

  1. create_audio_patch / create_patch
  2. ms12 + cleanup / continuous
  3. usecase_change_validate
  4. input_thread / output_thread / ring_buffer
  5. out_write_new / mixer_main / mixer_aux / config_output / create.*ms12

异常时可快速判断卡在哪一段:

现象 优先怀疑
有 patch 无声音 输出线程没起来、ringbuffer 一直空、write 函数选错
一切换就卡死/爆音 MS12 continuous 未清干净,或与 mixer 双写冲突
中途格式变后无声 config_output / 重建 MS12 失败
只有按键音没有 HDMI HDMI 路径走了 standby,或 aux/main 混错

总结

TV 切到 HDMI-IN 的音频核心,不是单函数「打开设备」,而是:

  1. 建 patch(源 → 宿)
  2. 必要时拆掉 MS12 连续模式,usecase 让路
  3. 双线程 + 环形缓冲 搬数
  4. out_write_new → usecase → mixer/MS12 → config_output 真正出声

把这条链记熟,再去啃上千行 HDMI-IN log,会顺很多。


相关阅读

评论

还没有评论,来做第一个吧

有想法?直接在这说,不用跑留言板