From 90a523736e50b6fbf89787f49b9d445d763ffa99 Mon Sep 17 00:00:00 2001 From: joywayer Date: Sat, 27 Jun 2026 12:07:20 +0800 Subject: [PATCH] =?UTF-8?q?3.A/3.F=20=E4=BF=AE=E5=A4=8D=EF=BC=9AvoicePlayi?= =?UTF-8?q?ng=20=E5=85=A5=E5=8F=82=E7=B1=BB=E5=9E=8B=E5=85=BC=E5=AE=B9=20+?= =?UTF-8?q?=20mediaTypeAudio=20canPlay=20=E8=AF=8A=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 发现的根因 实测日志 mediaTypeAudio 进入了 native(amrFileCodec frame=107 打印两次确认 转码运行),但缺 gameui_play_voice 触发 log → canPlay=false 被短路。 原因是 voicePlaying handler 解析 H5 入参时类型不容忍。 之前实现: let on = (data?.asInt ?? 0) == 1 asInt 只对 BridgeData.number case 返回值。若 H5 传 string "1"(实测确实 会传)→ asInt 返回 nil → `?? 0 = 0` → `0 == 1 = false` → 开关被关闭。 daoqi 用 ObjC `[data intValue]`(NSString/NSNumber 都能转),永远能 正确处理两种类型。 ## 修复 VoicePlayingHandler:改用 asLooseString 比较 "1" 字符串,兼容 H5 传: - string "1" → asLooseString → "1" → on=true ✓ - number 1 → asLooseString → "1" → on=true ✓ - string "0" → asLooseString → "0" → on=false ✓ - number 0 → asLooseString → "0" → on=false ✓ - 其它 → on=false(保守拒绝) 同时加 log.debug 记录每次 voicePlaying 调用的 raw + 解析后值,便于后续 诊断 H5 业务行为。 ## 诊断辅助 RemoteAudioHandler.handleMediaTypeAudio 增加 log.debug: mediaTypeAudio user=X transcoded=Y canPlay=Z 让真机测试能直接看到 canPlay 实际值,避免再次盲猜。 ## 验证 BuildProject 通过 0 错误。真机重试 mediaTypeAudio 应能看到: voicePlaying H5 → raw=1 → isEnabled=true mediaTypeAudio user=0 transcoded=true canPlay=true gameui_play_voice user=0 --- .../Bridge/Handlers/RemoteAudioHandler.swift | 1 + .../Bridge/Handlers/VoicePlayingHandler.swift | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ylgamehall/Source/Bridge/Handlers/RemoteAudioHandler.swift b/ylgamehall/Source/Bridge/Handlers/RemoteAudioHandler.swift index e2c788f..3460d4f 100644 --- a/ylgamehall/Source/Bridge/Handlers/RemoteAudioHandler.swift +++ b/ylgamehall/Source/Bridge/Handlers/RemoteAudioHandler.swift @@ -321,6 +321,7 @@ public enum RemoteAudioHandler { // (仅转码成功 + 开关 enabled 时触发,对齐 daoqi // NewRootVC.m:343-353 if (canbofang) { play + callHandler }) let canPlay = await VoiceCenter.shared.isEnabled + log.debug("mediaTypeAudio user=\(user, privacy: .public) transcoded=\(transcoded, privacy: .public) canPlay=\(canPlay, privacy: .public)") if transcoded, canPlay { do { try VoicePlayer.shared.play(wavLocal) diff --git a/ylgamehall/Source/Bridge/Handlers/VoicePlayingHandler.swift b/ylgamehall/Source/Bridge/Handlers/VoicePlayingHandler.swift index 307db18..b984e71 100644 --- a/ylgamehall/Source/Bridge/Handlers/VoicePlayingHandler.swift +++ b/ylgamehall/Source/Bridge/Handlers/VoicePlayingHandler.swift @@ -5,14 +5,18 @@ // H5 → Native handler:voicePlaying — 远端语音播放总开关 // 契约:docs/H5-Native-Contract.md §3.1 [6] // -// Phase 2.4 仅实现"开关位",真正的 mediaTypeAudio 播放在 Phase 3 AudioKit -// 完整时接入。开关位由 actor VoiceCenter 持有,跨 handler 共享。 +// 开关位由 actor VoiceCenter 持有,跨 handler 共享。Phase 3.F +// mediaTypeAudio handler 读 isEnabled 决定是否播放。 // import Foundation +import os.log /// 远端语音播放总开关(actor 隔离,跨 bridge handler 安全共享)。 -/// Phase 3 mediaTypeAudio handler 读此 isEnabled 决定是否播放。 +/// +/// 默认值 `true` 对齐 daoqi NewRootVC.m:255 / gameController.m:343 viewDidLoad +/// 早期 `canbofang=YES` 行为(ObjC BOOL 默认是 NO,但 daoqi 在 VC init 时主动 +/// set YES)。 public actor VoiceCenter { public static let shared = VoiceCenter() public private(set) var isEnabled: Bool = true @@ -21,13 +25,19 @@ public actor VoiceCenter { public enum VoicePlayingHandler { + nonisolated private static let log = Logger(subsystem: "ylgamehall", category: "VoicePlaying") + public static func register(on bridge: any BridgeProtocol) { // 【6】voicePlaying // 入参 int 1=允许 / 其它=静默 // cb:"voicePlaying"(字面与 handler 名相同,不是 "Response from voicePlaying") bridge.register("voicePlaying") { data, callback in - let on = (data?.asInt ?? 0) == 1 + // 兼容 H5 传 number 或 string("1" / 1 / 0 / "0")。原 asInt 实现遇到 + // string 类型会返回 nil,导致 H5 调 voicePlaying("1") 时被当作 0 关闭开关。 + let raw = data?.asLooseString ?? "" + let on = raw == "1" Task { await VoiceCenter.shared.setEnabled(on) } + log.debug("voicePlaying H5 → raw=\(raw, privacy: .public) → isEnabled=\(on, privacy: .public)") // 注意回包字面是 "voicePlaying",不是 "Response from voicePlaying"。 // 与其它 handler 的 "Response from XXX" 模式不一致是原项目历史包袱 // (daoqi/msext gameController.m:573、NewRootVC.m:451 都是 responseCallback(@"voicePlaying"))