Browse Source

上传闪退修复

newdev_shunjiawei
liwei1dao 7 months ago
parent
commit
f599ed768e
  1. 35
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/StsAgent.kt

35
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/StsAgent.kt

@ -2,9 +2,11 @@ package com.yunqiinnovation.agent_service
import android.content.Context import android.content.Context
import android.media.AudioFormat import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioRecord import android.media.AudioRecord
import android.media.MediaPlayer import android.media.MediaPlayer
import android.media.MediaRecorder import android.media.MediaRecorder
import android.media.ToneGenerator
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
@ -91,6 +93,9 @@ class StsAgent(
// ── 预缓冲(VAD 触发前 ~1.5s)──────────────────────────────────────────── // ── 预缓冲(VAD 触发前 ~1.5s)────────────────────────────────────────────
private val preBuffer = ArrayDeque<ByteArray>() private val preBuffer = ArrayDeque<ByteArray>()
private val preBufferLock = Any() private val preBufferLock = Any()
// ── 麦克风读写锁(防止 read() 与 stop()/release() 并发导致 native SIGABRT)──
private val micLock = Any()
private val PRE_BUFFER_MAX_CHUNKS = 75 private val PRE_BUFFER_MAX_CHUNKS = 75
// ── 本地 VAD ────────────────────────────────────────────────────────────── // ── 本地 VAD ──────────────────────────────────────────────────────────────
@ -406,8 +411,15 @@ class StsAgent(
recordingJob = launch { recordingJob = launch {
val buf = ByteArray(RECORD_CHUNK) val buf = ByteArray(RECORD_CHUNK)
while (isActive && (inCall || pttRecording)) { while (isActive && (inCall || pttRecording)) {
val read = audioRecord?.read(buf, 0, buf.size) ?: break // 使用非阻塞读 + micLock,确保与 stopMicHard() 中的 stop()/release() 互斥。
// READ_NON_BLOCKING 让 read() 立即返回(微秒级),锁持有时间极短,
// 彻底消除 releaseBuffer 并发导致的 native SIGABRT。
val read = synchronized(micLock) {
audioRecord?.read(buf, 0, buf.size, AudioRecord.READ_NON_BLOCKING) ?: -1
}
if (read < 0) break
if (read > 0) { micChunkCount++; onMicAudio(buf.copyOf(read)) } if (read > 0) { micChunkCount++; onMicAudio(buf.copyOf(read)) }
else delay(5) // 无数据时释放协程让出线程,避免忙等
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -417,8 +429,13 @@ class StsAgent(
private fun stopMicHard() { private fun stopMicHard() {
recordingJob?.cancel(); recordingJob = null recordingJob?.cancel(); recordingJob = null
try { audioRecord?.stop(); audioRecord?.release() } catch (_: Exception) {} // 拿 micLock 后再 stop()/release(),与录音循环的 read() 互斥,
audioRecord = null // 保证 stop() 永远不会在 read() 内部 releaseBuffer 期间被调用。
synchronized(micLock) {
val ar = audioRecord
audioRecord = null
try { ar?.stop(); ar?.release() } catch (_: Exception) {}
}
} }
private fun startMicWatchdog() { private fun startMicWatchdog() {
@ -504,12 +521,24 @@ class StsAgent(
} }
forwardingAudio = true; wsConnecting = false forwardingAudio = true; wsConnecting = false
playReadyBeep()
emit("recognition_started", emptyMap()) emit("recognition_started", emptyMap())
val cached = synchronized(preBufferLock) { preBuffer.toList().also { preBuffer.clear() } } val cached = synchronized(preBufferLock) { preBuffer.toList().also { preBuffer.clear() } }
for (c in cached) feedSendBuffer(c) for (c in cached) feedSendBuffer(c)
} }
/** 唤醒成功提示音:通知用户可以说话了 */
private fun playReadyBeep() {
try {
val toneGen = ToneGenerator(AudioManager.STREAM_MUSIC, 60)
toneGen.startTone(ToneGenerator.TONE_PROP_BEEP, 180)
mainHandler.postDelayed({ toneGen.release() }, 300)
} catch (e: Exception) {
Log.w(TAG, "playReadyBeep failed: ${e.message}")
}
}
// ═════════════════════════════════════════════════════════════════════════ // ═════════════════════════════════════════════════════════════════════════
// WebSocket // WebSocket
// ═════════════════════════════════════════════════════════════════════════ // ═════════════════════════════════════════════════════════════════════════

Loading…
Cancel
Save