From b2e241a668a3869052a0e9c6e84e65d597c3e84d Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Tue, 9 Dec 2025 16:49:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AE=AF=E9=A3=9E=E8=AF=AD?= =?UTF-8?q?=E9=9F=B3=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../azure_speech/XunFeiAsrHelper.kt | 90 ++++++++++++++----- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/XunFeiAsrHelper.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/XunFeiAsrHelper.kt index 3c4253dd0..7f0919df0 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/XunFeiAsrHelper.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/XunFeiAsrHelper.kt @@ -17,12 +17,14 @@ class XunFeiAsrHelper(private val context: Context) { private val tag = "XunFeiAsrHelper" private val appId = "43d87d22" - private val accessKeyId = "83f063755e8d342670002f50a4471d42" + private val accessKeyId = "2bd01ce06ac35957fc0c082f9b6284ec" + private val accessKeySecret = "ODU3OTUyYjhjMjY5NDhkMmMxYWY2Zjlm" private var webSocket: WebSocket? = null private val client = OkHttpClient() private var currentSessionId = "" + private var sessionId = "" fun start(callback: AzureAsrHelper.ContinuousRecognizeCallback) { try { @@ -73,7 +75,7 @@ class XunFeiAsrHelper(private val context: Context) { fun stop() { try { webSocket?.let { ws: WebSocket -> - ws.send("{\"end\": true}") + ws.send("{\"end\": true, \"sessionId\": \"$sessionId\"}") ws.close(1000, "User stopped") } } catch (e: Exception) { @@ -89,10 +91,15 @@ class XunFeiAsrHelper(private val context: Context) { ) { try { val jsonData = JSONObject(resultData) - val action = jsonData.optString("action") - - when (action) { - "started" -> { + Log.d(tag, "-------------------$jsonData") + val msgType = jsonData.optString("msg_type") + + when (msgType) { + "action" -> { + val sid = jsonData.optString("sessionId") + if (!sid.isNullOrEmpty()) { + sessionId = sid + } Log.d(tag, "Handshake success") } @@ -127,8 +134,13 @@ class XunFeiAsrHelper(private val context: Context) { } "error" -> { - Log.e(tag, "Error: $resultData") callback.onError(currentSessionId, 1005, "Xunfei error: $resultData") + Log.e(tag, "Error: $resultData") + } + + else -> { + callback.onError(currentSessionId, 1006, "Xunfei unknown msg_type: $msgType") + Log.w(tag, "Unknown msg_type: $msgType") } } } catch (e: Exception) { @@ -137,27 +149,57 @@ class XunFeiAsrHelper(private val context: Context) { } private fun getWebSocketUrl(): String { - val url = "wss://rtasr.xfyun.cn/v1/ws" - val ts = (System.currentTimeMillis() / 1000).toString() - val baseString = appId + ts - val signa = md5(baseString) - val signatureSha = hmacSha1(signa, accessKeyId) - val signature = Base64.encodeToString(signatureSha, Base64.NO_WRAP) - val encodedSignature = URLEncoder.encode(signature, "UTF-8") - - return "$url?appid=$appId&ts=$ts&signa=$encodedSignature" + val baseWsUrl = "wss://office-api-ast-dx.iflyaisol.com/ast/communicate/v1" + + val params = java.util.TreeMap() + params["audio_encode"] = "pcm_s16le" + params["lang"] = "autodialect" + params["samplerate"] = "16000" + params["accessKeyId"] = accessKeyId + params["appId"] = appId + params["utc"] = getUtcTime() + + val signature = calculateSignature(params) + params["signature"] = signature + + val sb = StringBuilder() + var first = true + for ((key, value) in params) { + if (!first) { + sb.append("&") + } + sb.append(URLEncoder.encode(key, "UTF-8")).append("=").append(URLEncoder.encode(value, "UTF-8")) + first = false + } + + return "$baseWsUrl?$sb" } - private fun md5(string: String): String { - val md = MessageDigest.getInstance("MD5") - val digest = md.digest(string.toByteArray(StandardCharsets.UTF_8)) - return digest.joinToString("") { "%02x".format(it) } + private fun getUtcTime(): String { + val sdf = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + sdf.timeZone = java.util.TimeZone.getTimeZone("GMT+8") + return sdf.format(java.util.Date()) } - private fun hmacSha1(data: String, key: String): ByteArray { - val secretKeySpec = SecretKeySpec(key.toByteArray(StandardCharsets.UTF_8), "HmacSHA1") + private fun calculateSignature(params: Map): String { + val baseStr = StringBuilder() + var first = true + for ((key, value) in params) { + if ("signature" == key) continue + if (value.isEmpty()) continue + + if (!first) { + baseStr.append("&") + } + baseStr.append(URLEncoder.encode(key, "UTF-8")).append("=").append(URLEncoder.encode(value, "UTF-8")) + first = false + } + val mac = Mac.getInstance("HmacSHA1") - mac.init(secretKeySpec) - return mac.doFinal(data.toByteArray(StandardCharsets.UTF_8)) + val keySpec = SecretKeySpec(accessKeySecret.toByteArray(StandardCharsets.UTF_8), "HmacSHA1") + mac.init(keySpec) + val signBytes = mac.doFinal(baseStr.toString().toByteArray(StandardCharsets.UTF_8)) + + return Base64.encodeToString(signBytes, Base64.NO_WRAP) } }