From da7a00c665d96270b42a51ce80addbb54405217c Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Wed, 10 Jun 2026 15:53:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../azure_speech/AstCallbacks.kt | 17 +++++++++++++++++ .../yunqiinnovation/ble_service/BleService.kt | 13 +++++++++++++ 2 files changed, 30 insertions(+) diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AstCallbacks.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AstCallbacks.kt index 00d07b236..1ff8552fa 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AstCallbacks.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AstCallbacks.kt @@ -526,6 +526,11 @@ class DoubaoAstCallback( private val tag = "DoubaoAstCallback" private val doubaoFinalSourceTextCache: MutableMap = mutableMapOf() + // 延迟追踪:每句只在"距上一帧>700ms"(视为新一句)时打一条 [LAT-TRACE],避免逐帧刷屏。 + // 同进程 currentTimeMillis 与 BleService 侧可直接相减,定位 app 内部各跳耗时。 + private var latLastTextTs = 0L + private var latLastAudioTs = 0L + override fun onSessionStarted(sessionId: String) { eventSender.send( mapOf( @@ -566,6 +571,12 @@ class DoubaoAstCallback( } override fun onPartialText(sessionId: String, text: String) { + // [LAT-TRACE] 点1:收到译文文字(每句首条,svc=B 即对方) + val latNow = System.currentTimeMillis() + if (latNow - latLastTextTs > 700) { + FileLogger.i(tag, "[LAT-TRACE] 1.译文文字到达 svc=$serviceId ts=$latNow text=${text.take(12)}") + } + latLastTextTs = latNow eventSender.send( mapOf( "type" to "translatedInterim", @@ -580,6 +591,12 @@ class DoubaoAstCallback( } override fun onPartialAudio(sessionId: String, data: ByteArray) { + // [LAT-TRACE] 点2:app 收到译音 PCM(每句首帧,svc=B 即对方) + val latNow = System.currentTimeMillis() + if (latNow - latLastAudioTs > 700) { + FileLogger.i(tag, "[LAT-TRACE] 2.app收到译音 svc=$serviceId ts=$latNow size=${data.size}") + } + latLastAudioTs = latNow audioWriter?.write(data) } diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index c34d01daf..2b410ea81 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt @@ -150,6 +150,9 @@ object BleService { private var lastRightSeq = -1L // 下行写入连续失败(底层发送缓冲拥塞)的计数,用于节流告警与恢复提示;写成功即清零 private var writeFailRetryCount = 0 + // 延迟追踪:右声道(对方译音)每句首包写到 BLE 的时刻,配合 azure_speech 侧 [LAT-TRACE] 点1/点2 + // 相减即得 app 内部"收到译音→写到耳机"耗时;间隔>700ms 视为新一句,避免逐包刷屏 + private var latLastBleRightTs = 0L // 写失败(拥塞)后的退避时长(ms):给底层发送缓冲腾空的时间,再重试同一包,期间绝不丢包 private const val WRITE_RETRY_BACKOFF_MS = 5L @@ -1484,6 +1487,15 @@ object BleService { Log.i(TAG, "音频下行发送 ch=$chTag seq=$seq Δ=${deltaMs}ms size=${audioChunk.size}B " + "lastSeq(L=$lastLeftSeq,R=$lastRightSeq)$gapTag " + "hold(L=${leftHoldBuffer.size},R=${rightHoldBuffer.size}) queue=${audioSendQueue.size}") + + // [LAT-TRACE] 点3:右声道(对方译音)首包写到 BLE 耳机(每句首包)。 + // 与 azure_speech 的 [LAT-TRACE] 点2(app收到译音) 同时钟相减 = app 内部下行耗时。 + if (channelByte == OpusAudioManager.CHANNEL_RIGHT) { + if (now - latLastBleRightTs > 700) { + Log.i(TAG, "[LAT-TRACE] 3.译音写到BLE(右/对方) ts=$now seq=$seq") + } + latLastBleRightTs = now + } } // 固定节拍:睡到 nextSendAt 这个绝对时刻,把 poll/发送耗时算进同一周期,间隔不随它们抖动。 @@ -1548,6 +1560,7 @@ object BleService { lastLeftSeq = -1L // 重置左右声道包序追踪 lastRightSeq = -1L writeFailRetryCount = 0 // 重置写入拥塞计数 + latLastBleRightTs = 0L // 重置延迟追踪计时 Log.i(TAG, "音频发送线程已停止,队列和缓冲区已清空") }