预期结果:实时语音文字识别
三方功能支持:腾讯云语音识别
对接要求:在识别过程中,客户端持续上传 binary message 到后台,内容为音频流二进制数据。建议每40ms 发送40ms 时长(即1:1实时率)的数据包,对应 pcm 大小为:8k 采样率640字节,16k 采样率1280字节。音频发送速率过快超过1:1实时率或者音频数据包之间发送间隔超过6秒,可能导致引擎出错,后台将返回错误并主动断开连接。
音频流上传完成之后,客户端需发送以下内容的 text message,通知后台结束识别。
拼装请求url:
传入参数:engine_model_type【引擎模型类型】16k_zh_dialect:多方言,
expired【签名的有效期截止时间,UNIX 时间戳,单位为秒】System.currentTimeMillis() / 1000L + 86400L,
needvad【语音分片长】1:开启
nonce【随机正整数】RandomUtil.randomInt(1000, 99999);
timestamp【当前 UNIX 时间戳,单位为秒】,
secretid【密钥 】
voice_format【语音编码方式】1:pcm
voice_id【音频流识别全局唯一标识】AsrUtils.getVoiceId(asrConfig.getAppId())
signature【接口签名参数】
举例:wss://asr.cloud.tencent.com/asr/v2/1256841545?engine_model_type=16k_zh_dialect&expired=1685527216&needvad=1&nonce=37769&secretid=AKIDkHZbOtm7qKYu1ktrY0D9k6E6hfPdFIkx×tamp=1685440816&voice_format=8&voice_id=1256841545_1685440816950_byc53&signature=Qv1YDDYCP7skMsASStxFuAVMa0w=
签名生成:
1、对除 signature 之外的所有参数按字典序进行排序,拼接请求 URL 作为签名原文,这里以 Appid=125922***
,SecretId=*****Qq1zhZMN8dv0******
为例拼接签名原文,则拼接的签名原文为:
asr.cloud.tencent.com/asr/v2/125922***?engine_model_type=16k_zh&expired=1673494772&needvad=1&nonce=1673408372&secretid=*****Qq1zhZMN8dv0******×tamp=1673408372&voice_format=1&voice_id=c64385ee-3e5c-4fc5-bbfd-7c71addb35b0
实现方法:
private TreeMap<String, Object> getRequestParamMap(AsrConfig asrConfig, AsrRequest request, AsrRequestContent content) {
TreeMap<String, Object> treeMap = new TreeMap();
treeMap.put(TencentContents.SECRET_ID, asrConfig.getSecretId());
treeMap.put(TencentContents.ENGINE_MODEL_TYPE, request.getEngineModelType());
treeMap.put(TencentContents.VOICE_ID, content.getVoiceId());
treeMap.put(TencentContents.VOICE_FORMAT, request.getVoiceFormat());
treeMap.put(TencentContents.TIMESTAMP, request.getTimestamp());
treeMap.put(TencentContents.EXPIRED, request.getExpired());
treeMap.put(TencentContents.NONCE, request.getNonce());
treeMap.put(TencentContents.NEED_VAD, request.getNeedVad());
return treeMap;
}
private TreeMap<String, Object> getWsParams(AsrConfig asrConfig, AsrRequest request, AsrRequestContent content) {
TreeMap<String, Object> treeMap = this.getRequestParamMap(asrConfig, request, content);
if (request.getExtendsParam() != null) {
Iterator var5 = request.getExtendsParam().entrySet().iterator();
while (var5.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry) var5.next();
treeMap.put(entry.getKey(), entry.getValue());
}
}
return treeMap;
}
public static String createUrl(Map<String, Object> paramMap) {
StringBuilder sb = new StringBuilder();
sb.append("?");
Iterator var2 = paramMap.entrySet().iterator();
while(var2.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry)var2.next();
if (entry.getValue() != null && entry.getValue() != "") {
sb.append((String)entry.getKey());
sb.append('=');
sb.append(entry.getValue());
sb.append('&');
}
}
if (paramMap.size() > 0) {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
String signUrl = new StringBuilder().append(asrConfig.getWsSignUrl()).append(asrConfig.getAppId()).append(paramUrl).toString();
public AsrConfig(String appId, String secretKey, String secretId, Long waitTime, String realAsrUrl, String signUrl, String logUrl, String wsUrl, String token) {
super(secretId, secretKey, Long.valueOf(appId), token);
this.realAsrUrl = (String)Optional.ofNullable(realAsrUrl).orElse("https://asr.cloud.tencent.com/asr/v1/");
this.signUrl = (String)Optional.ofNullable(signUrl).orElse("asr.cloud.tencent.com/asr/v1/");
this.logUrl = (String)Optional.ofNullable(logUrl).orElse("https://asr.tencentcloudapi.com/");
this.wsUrl = (String)Optional.ofNullable(wsUrl).orElse("wss://asr.cloud.tencent.com/asr/v2/");
this.wsSignUrl = "asr.cloud.tencent.com/asr/v2/";
this.flashUrl =