KunSpace
返回博客

Dolby FlexConnect:从硬件 RF 到校准的六层链路

2026年7月5日
3 次阅读

读完了?别空手走啊 😏

写到这了还不点个赞?键盘会痛的

写评论

读完了不评论,作者会以为你在沉思

这篇写什么?

做 Soundbar + Dolby FlexConnect / 校准时,最容易混在一起的三件事是:

  1. modelName 怎么对应到 JSON?
  2. FlexConnect 初始化谁调?
  3. 音箱连没连怎么报给 Dolby?

下面用「六层分层图」把整条链路摊开:左右是谁定的、JSON 管不管左右、两套 API 各在哪一层

一句话:

硬件 RF → 串口 → App Bridge 定角色/modelName → IDeviceConnection 报名单
→ Dolby 按型号加载 JSON → FlexCalibrator.setDevices 初始化 → 可校准 / UI 显示

「分层图」:从下到上共 6 层

内容
用户看到的校准结果 / UI
Dolby Calibration + FlexConnect(读 JSON、初始化、开校准)
IDeviceConnection(AIDL:App 上报 Device[]
校准 App Bridge(定角色、贴 modelName
串口协议(AT+GET_RFSTATUS
硬件 RF(左 / 右 / 低音炮真连接状态)

左右是谁定的?在第 ①②③ 层就定完了。

第 ⑤ 层的 JSON 不负责判断左右,只负责「这种型号的音箱声学参数怎么配」。


整条链路详解

第 ① 层:硬件

Soundbar 主机上有 RF 模块。无线左环绕 / 右环绕 / 低音炮连上或断开,硬件是知道的

第 ② 层:串口问硬件(Bridge 去查)

Bridge 绑 OemSerialPortRouter,周期发:

AT+GET_RFSTATUS

MCU / RF 回类似:

GET_RFSTATUS_SBAR51_101
                      ███
                      ││└─ Z = 低音炮(1=连, 0=断)
                      │└── Y = 右环绕
                      └─── X = 左环绕

例:101 → 左连、右断、低音炮连。

到这里,App 只知道「三个开关」:左 / 右 / 低音炮通不通。
还不知道 Dolby 的 modelName,也还没碰 JSON。

第 ③ 层:App Bridge 定身份

Bridge 用写死的规则(或读可选的 device_role_map.json):

RF 结果 OEM 内部 ID 角色 贴给 Dolby 的 modelName
主机 soundbar_main 主机 Controller OEM_5ch_ISS
左=1 rear_left 左环绕 OEM_1ch_WS_L
右=1 rear_right 右环绕 OEM_1ch_WS_R
低音炮=1 subwoofer_main 低音炮 OEM_SUB

对 RF=101,Bridge 组装出的名单是:

  1. soundbar_main → OEM / OEM_5ch_ISS(主机,必报)
  2. rear_left → OEM / OEM_1ch_WS_L(左,连了才报)
  3. subwoofer_main → OEM / OEM_SUB(低音炮,连了才报)
  4. (右环绕不报,因为右=0)

每个设备填成 Dolby 规定的 Device 结构:

Device {
  address.id        = "rear_left"        // 起的唯一名
  isMovable         = true               // 卫星/低音炮可移动;主机 false
  hardwareId.manufacturerName = "OEM"
  hardwareId.modelName        = "OEM_1ch_WS_L"   // ★ 身份钥匙
}

左右是在这一步由「RF 位 + OEM 角色表」定死的。
App 不会打开 OEM_OEM_1ch_WS_L.json 来判断「这是不是左环绕」。

同一份 RF 状态还会更新 App 主页音箱列表、断连弹窗——那是 UI,和 Dolby 并行。

第 ④ 层:交给 Dolby(AIDL 上报)

Manifest 里挂 Dolby 约定的 Action:

com.dolby.android.audio.intent.action.AUDIO_DEVICE_CONNECTED

Dolby 的 DolbyAudioCalibrationService 启动后:

  1. 用这个 Intent 找到 App 的 Bridge
  2. bindService
  3. addCallback(listener)
  4. 之后有变化时立刻调:listener.onDevicesChanged(devices)

这就是 IDeviceConnection 这一层:只做「报名单」,不做声学初始化。

第 ⑤ 层:Dolby 加载 JSON + 初始化 FlexConnect

Calibration 收到 Device[] 后,对每一个设备:

  1. 取出 manufacturerName + modelName
    例:OEM + OEM_1ch_WS_L
  2. 拼文件名:OEM_OEM_1ch_WS_L.json
  3. 到目录找:/vendor/etc/dolby/audio/device_descriptors/
  4. 核对 JSON 里的 identifier 是否等于 modelName(必须一致)
  5. 读描述符内容(声学 / 渲染参数),例如:
    • 主机 JSON:render_outputs 有 5 路(对应主机 5 声道)
    • 左环绕 JSON:render_outputs 有 1 路
    • 低音炮 JSON:render_outputs 有 1 路
  6. 内部走类似 setDevices / device connector 初始化(AIDK 里 FlexCalibrator 那套)

第 ⑥ 层:和音频输出怎么对上

平台还有 8 声道 ALSA 布局(和角色表一致):

ch0~4 = 主机 FL/FR/FC/LS/RS
ch5   = 左环绕
ch6   = 右环绕
ch7   = 低音炮 LFE

device_role_map 里也写了:

  • 左 → ALSA ch5
  • 右 → ALSA ch6
  • 低音炮 → ALSA ch7

含义是:FlexConnect / 校准认为「左环绕」时,声音应走到 ch5;
所以 App 必须把「物理左 RF」稳定映射成 OEM_1ch_WS_L不能左右填反

上报顺序也建议固定:主机 → 左 → 右 → 低音炮(与 init_order 一致),因为 Dolby 文档里设备列表顺序会影响 flexible rendering 声道编排。


完整例子串起来

假设某时刻:左环绕连上、右环绕没连、低音炮连上。

  1. ① 硬件:左 RF 通,右 RF 断,Sub RF 通
  2. ② 串口GET_RFSTATUS_SBAR51_101
  3. ③ App Bridge:认定 → 主机 + 左 + 低音炮;填 modelNameOEM_5ch_ISS, OEM_1ch_WS_L, OEM_SUB(此时仍未读 JSON)
  4. onDevicesChanged([主机, 左, 低音炮])
  5. ⑤ Dolby:打开 OEM_OEM_5ch_ISS.jsonOEM_OEM_1ch_WS_L.jsonOEM_OEM_SUB.json,各自做 device connector / FlexConnect 初始化 → 可以开始校准
  6. ⑥ UI:主页也显示主机 / 左 / 低音炮在线,右不显示

若下一步右环绕也连上(RF 变成 111):

  • Bridge 重新上报 4 个设备
  • Dolby 再多加载 OEM_OEM_1ch_WS_R.json,并重新初始化

两层 API 在整条链里的位置

①②③④  ←── IDeviceConnection(App 侧实现:报「连了谁、叫什么型号」)
        │
        ▼
   ⑤     ←── device connector / FlexConnect 初始化
              (Dolby Calibration 内部:按型号加载 JSON 并开工)

不是同一个 API:

  • 前者 = 报名单
  • 后者 = 按名单给每个设备做初始化

三个问题的直接回答

总览:谁负责哪一段?

Dolby 要确认的事 实际接口 谁写 / 谁调
① modelName 怎么加载 JSON DeviceEventHandler.loadDescriptor(manufacturer, modelName) Dolby Calibration 内部
② 初始化 FlexConnect FlexCalibrator.setDevices(List<Device>) → native setDevice(...) Dolby Calibration 内部
③ 音箱连接状态 IDeviceConnection + onDevicesChanged(Device[]) OEM Bridge 实现并上报;Dolby bind 后收回调

1. modelName → JSON(代码级)

不是 OEM 调的 API。 在 Dolby 的 DolbyAudioCalibrationService.apk 里:

OEM Bridge
  └─ onDevicesChanged(Device[])          // IDeviceConnectionListener
        ↓
DeviceEventHandler.onDevicesChanged()
  ├─ 读 device.hardwareId.manufacturerName
  ├─ 读 device.hardwareId.modelName
  └─ loadDescriptor(manufacturerName, modelName) → byte[]
        ↓
CalibrationDirector 侧再把 descriptor 放进 FlexCalibrator$Device

loadDescriptor 实际逻辑(类:com.dolby.android.audio.calibration.DeviceEventHandler):

fileName = manufacturer + "_" + modelName + ".json"
例:OEM + OEM_1ch_WS_L → OEM_OEM_1ch_WS_L.json

在 DEVICE_DESCRIPTOR_PATHS 里依次找,优先:
  /vendor/etc/dolby/audio/device_descriptors/<fileName>

找到则 Files.readAllBytes(path)
找不到则 log:"Device descriptor for <manuf> <model> not found."
  并跳过该设备("Skipping device ... with invalid device descriptor")

对应 AIDL 字段:

com.dolby.android.audio.deviceconnection.HardwareIdentifiers
  - manufacturerName
  - modelName

OEM 侧要做的: 上报正确的 manufacturerName / modelName,并保证板子上有
/vendor/etc/dolby/audio/device_descriptors/{manufacturer}_{modelName}.json

2. FlexConnect 初始化(代码级)

同样是 Dolby 内部调用,OEM 不直接调:

DeviceEventHandler.onDevicesChanged(...)
  └─ notifyDeviceChangedListeners()
        ↓
CalibrationDirector.onDevicesChanged()
  ├─ getFlexCalibratorDevices()   // 含已加载的 descriptor 内容
  ├─ 组装 List<FlexCalibrator.Device>
  └─ mFlexCalibrator.setDevices(list)     // ★ FlexConnect 设备初始化入口
        ↓
FlexCalibratorNative.setDevices(list)
  └─ 对每个设备调用 native:
       setDevice(deviceId, descriptorJson, connected, ignoreMapping,
                 isFront, x, y, angle, isFirst, isLast, handle)
        ↓
libflexcalibrator.dolby.so

接口签名(AIDK):

com.dolby.android.audio.flexcalibrator.FlexCalibrator
  boolean setDevices(List<FlexCalibrator.Device> devices)

FlexCalibrator.Device 构造时带:deviceIddescriptor(JSON 字符串内容)、connected / front / position 等。
失败时 Calibration 会打:onDevicesChanged() setDevices failed

和 MS12 IMs12 的关系:

运行时还有另一套(播放侧 Flexible Rendering):

vendor.dolby.audio.ms12.IMs12
  enableFlexibleRendering(boolean)
  setFlexibleRendererConfiguration(sconf, dconf)

校准前「按每个设备 connector 初始化」对应的是上面的 FlexCalibrator.setDevices
IMs12 多在校准产出 sconf / dconf 之后给播放链路用。

3. 音箱连接状态(代码级)

Dolby 侧怎么拿连接状态:

com.dolby.android.audio.deviceconnection.IDeviceConnection
  addCallback(IDeviceConnectionListener)
  removeCallback()

IDeviceConnectionListener
  onDevicesChanged(in Device[] devices)

发现 / 绑定方式(Calibration 内):

Intent action:
  com.dolby.android.audio.intent.action.AUDIO_DEVICE_CONNECTED

DeviceEventHandler.connectToDeviceConnectionService()
  → bindService
  → IDeviceConnection.addCallback(...)
  → 立刻收到 onDevicesChanged(规范要求 addCallback 时立即回调)

OEM 侧怎么提供连接状态:

DeviceConnectionBridgeService(OEM)
  Manifest 声明 action: AUDIO_DEVICE_CONNECTED
  onBind → 返回 IDeviceConnection.Stub

  内部:
    绑 OemSerialPortRouter
    发 AT+GET_RFSTATUS
    解析 GET_RFSTATUS_SBAR51_XYZ  → 左/右/低音炮
    按角色表填 Device[]
      hardwareId.manufacturerName = "OEM"
      hardwareId.modelName = "OEM_5ch_ISS" / "OEM_1ch_WS_L" / ...
    callback.onDevicesChanged(devices)
层次 接口 / 手段 作用
硬件 → App 串口 AT+GET_RFSTATUS 物理连没连
App → Dolby IDeviceConnection.onDevicesChanged 把连接列表(含 modelName)报给 Calibration

Dolby 不读 RF;只认 IDeviceConnection 上报的 Device[]


三件事串成一条

【③ 连接状态 - OEM】
  RF/串口 → Bridge 组 Device[](含 modelName)
       → IDeviceConnection.onDevicesChanged()

【① 加载 JSON - Dolby】
  DeviceEventHandler.loadDescriptor(manuf, model)
       → 读 /vendor/etc/dolby/audio/device_descriptors/{manuf}_{model}.json

【② 初始化 FlexConnect - Dolby】
  CalibrationDirector
       → FlexCalibrator.setDevices(...)
       → native setDevice(deviceId, descriptor, ...)
       → 各设备 connector / FlexConnect 就绪,再开校准

小结(给自己贴墙上)

  1. 左右身份:RF 位 + Bridge 角色表(①②③),不是 JSON。
  2. JSON:只描述「这个型号怎么响 / 怎么渲染」,由 Dolby loadDescriptor{manufacturer}_{modelName}.json 加载。
  3. FlexConnect 开工:Dolby 内部 FlexCalibrator.setDevices,App 只负责把名单报对、文件预置对。
  4. 报名单接口初始化接口;搞混就会以为「JSON 决定左右」或「App 要自己调 setDevices」。

记录于 Soundbar / Dolby FlexConnect 联调实践。

评论

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

读完了不评论,作者会以为你在沉思