?此接口为 API 2.0 版本,在参数风格、错误码等方面有区别于 API 3.0 版本,请知悉。
本接口服务对实时音频流进行识别,同步返回识别结果,达到“边说边出文字”的效果。接口是 HTTP RESTful 形式,在使用该接口前,需要在语音识别控制台开通服务,并进入 API 密钥管理页面 新建密钥,生成 AppID、SecretID 和 SecretKey,用于 API 调用时生成签名,签名将用来进行接口鉴权。
集成实时语音识别 API 时,需按照以下要求。
内容 | 说明 |
---|---|
支持语言 | 中文普通话、英语、粤语、韩语、日语、泰语、上海话、四川话、南京话、南昌话。其中,四川话、南京话、南昌话需填写 表单 申请 |
支持行业 | 通用、金融、游戏、教育、医疗 |
音频属性 | 采样率:16000Hz或8000Hz、采样精度:16bits、声道:单声道 |
音频格式 | wav、pcm、opus、speex、silk、mp3、m4a、aac |
数据长度 | 音频流中每个数据包的音频分片建议为200ms,8k采样率对应的音频分片大小为3200字节,16k采样率对应的音频分片大小为6400字节 |
请求协议 | HTTP |
请求地址 | http://asr.cloud.tencent.com/asr/v1/<appid>?{请求参数} |
接口鉴权 | 签名鉴权机制,详见 签名生成 |
响应格式 | 统一采用 JSON 格式 |
开发语言 | 任意,只要可以向腾讯云服务发起 HTTP 请求的均可 |
请求频率限制 | 50次/秒,如您有提高请求频率限制的需求,请提工单 进行咨询 |
请求结构主要由请求方法、请求 URL、请求头部、请求正文组成。
HTTPS 请求方法,实时语音识别的请求方法为 POST。
RESTful 形式的 URL 结构示例如下:
http://asr.cloud.tencent.com/asr/v1/<appid>?
projectid=xxx&
sub_service_type=xxx&
engine_model_type=xxx&
result_text_format=xxx&
res_type=xxx&
voice_format=xxx&
secretid=xxx&
timestamp=xxx&
expired=xxx&
needvad=xxx&
nonce=xxx&
seq=xxx&
end=xxx&
source=xxx&
voice_id=xxx&
timeout=xxx
URL 中各字段含义如下:
参数名称 | 必选 | 类型 | 描述 |
---|---|---|---|
AppId | 是 | Int | 用户在腾讯云注册账号的 AppId,可以进入 API 密钥管理页面 获取。 |
projectid | 否 | Int | 腾讯云项目 ID,语音识别目前不区分项目,所以填0即可。 |
secretid | 是 | String | 用户在腾讯云注册账号 AppId 对应的 SecretId,可以进入 API 密钥管理页面 获取。 |
sub_service_type | 是 | Int | 子服务类型。1:实时流式识别。 |
engine_model_type | 是 | String | 引擎模型类型。 电话场景: • 8k_en:电话 8k 英语; • 8k_zh:电话 8k 中文普通话通用; • 8k_zh_finance:电话 8k 金融领域模型; 非电话场景: • 16k_zh:16k 中文普通话通用; • 16k_en:16k 英语; • 16k_ca:16k 粤语; • 16k_ko:16k 韩语; • 16k_zh-TW:16k 中文普通话繁体; • 16k_ja:16k 日语; • 16k_wuu-SH:16k 上海话方言; • 16k_zh_medical 医疗; • 16k_en_game 英文游戏; • 16k_zh_court 法庭; • 16k_en_edu 英文教育; • 16k_zh_edu 中文教育; • 16k_th 泰语。 |
hotword_id | 否 | String | 热词 id。用于调用对应的热词表,如果在调用语音识别服务时,不进行单独的热词 id 设置,自动生效默认热词;如果进行了单独的热词 id 设置,那么将生效单独设置的热词 id。 |
customization_id | 否 | String | 自学习模型 id。用于调用对应的自学习模型,如果在调用语音识别服务时,不进行单独的自学习模型 id 设置,自动生效默认自学习模型;如果进行了单独的自学习模型 id 设置,那么将生效单独设置的自学习模型 id。 |
result_text_format | 否 | Int | 识别结果文本编码方式。0:UTF-8。 |
res_type | 否 | Int | 结果返回方式,默认值0。0:同步返回;1:尾包返回。 |
voice_format | 否 | Int | 语音编码方式,可选,默认值为4。1:pcm;4:speex(sp);6:silk;8:mp3;10:opus(opus 格式音频流封装说明);12:wav;14:m4a(每个分片须是一个完整的 m4a 音频);16:aac。 |
needvad | 否 | Int | 0:关闭 vad,1:开启 vad。 如果音频流总时长超过60秒,用户需开启 vad,默认值0。 |
vad_silence_time | 否 | Int | 语音断句检测阈值,静音时长超过该阈值会被认为断句(多用在智能客服场景,需配合 needvad = 1 使用),取值范围:240-2000,单位 ms,此参数建议不要随意调整,可能会影响识别效果,目前仅支持 8k_zh、8k_zh_finance、16k_zh 引擎模型。 |
seq | 是 | Int | 语音分片的序号,序号从0开始,每次请求递增1, 两个 seq 之间间隔不能超过6秒。 |
end | 是 | Int | 是否为最后一片,最后一片语音片为1,其余为0。 |
source | 否 | Int | 默认值为0。 |
voice_id | 是 | String | 16位 String 串作为每个音频的唯一标识,用户自己生成。 |
timestamp | 是 | Int | 当前 UNIX 时间戳,可记录发起 API 请求的时间。如果与当前时间相差过大,会引起签名过期错误。可以取值为当前请求的系统时间戳即可。 |
expired | 是 | Int | 签名的有效期,是一个符合 UNIX Epoch 时间戳规范的数值,单位为秒;Expired 必须大于 Timestamp 且 Expired - Timestamp 小于90天。 |
nonce | 是 | Int | 随机正整数。用户需自行生成,最长10位。 |
filter_dirty | 否 | Integer | 是否过滤脏词(目前支持中文普通话引擎)。默认为0。0:不过滤脏词;1:过滤脏词;2:将脏词替换为 * 。 |
filter_modal | 否 | Integer | 是否过滤语气词(目前支持中文普通话引擎)。默认为0。0:不过滤语气词;1:部分过滤;2:严格过滤 。 |
filter_punc | 否 | Integer | 是否过滤标点符号(目前支持中文普通话引擎)。0:不过滤,1:过滤句末标点,2:过滤所有标点。默认为0。 |
convert_num_mode | 否 | Int | 是否进行阿拉伯数字智能转换(目前支持中文普通话引擎)。0:不转换,直接输出中文数字,1:根据场景智能转换为阿拉伯数字,3: 打开数学相关数字转换。默认值为1。 |
word_info | 否 | Int | 是否显示词级别时间戳。0:不显示;1:显示,不包含标点时间戳,2:显示,包含标点时间戳。支持引擎 8k_en,8k_zh,8k_zh_finance,16k_zh,16k_en,16k_ca,16k_zh-TW,16k_ja,16k_wuu-SH,默认为0。 |
请求头部,包括 Host,Authorization,Content-Type,Content-Length 四个参数。
参数名称 | 必选 | 类型 | 描述 |
---|---|---|---|
Host | 是 | String | 语音识别服务域名,固定为 asr.cloud.tencent.com |
Authorization | 是 | String | 用户的有效签名,用于鉴权。对应签名鉴权中得到的签名字符串 |
Content-Type | 是 | String | application/octet-stream |
Content-Length | 是 | Int | 请求长度,此处对应语音数据字节数,单位:字节 |
请求正文主要包含实时语音识别的数据,每个数据包的音频分片最大不能超过200KB。
用户通过 签名生成 的签名为 qPHg2LZkMRZyC5pyVD8egRqxPc0=,上传语音分片 @/mnt/d/1.silk_down,分片为流式语音的第一个分片(seq=0),采样率为16k,请求实时语音识别服务16k通用(engine_model_type = 16k_0)引擎模型,结果返回方式为同步返回。具体的示例生成方式请参考下面 PHP 代码。
curl --data "@/mnt/d/1.silk_down" -H "Authorization:qPHg2LZkMRZyC5pyVD8egRqxPc0=" -X POST "http://asr.cloud.tencent.com/asr/v1/1259228442?end=0&engine_model_type=16k_0&expired=1561548973&needvad=0&nonce=1561&projectid=1013976&res_type=0&result_text_format=0&secretid=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO*****&seq=0&source=0&sub_service_type=1&timeout=100×tamp=1561462573&voice_format=1&voice_id=yp1fifJmEx4Lh3OX"
这里以 Appid=1259228442, SecretId=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO***** 为例拼接签名原文,则拼接的签名原文为:
POSTasr.cloud.tencent.com/asr/v1/1259228442?end=1&engine_model_type=16k_0&expired=1561022467&needvad=1&nonce=52811334&projectid=0&res_type=1&result_text_format=0&secretid=AKIDoQq1zhZMN8dv0psmvud6OUKuGPO*****&seq=0&source=0&sub_service_type=1&timeout=5000×tamp=1561016467&voice_format=4&voice_id=f658c689c3b4db74
对签名原文和 SecretKey=kFpwoX5RYQ2SkqpeHgqmSzHK7h3A2fni,使用 HmacSha1 算法进行加密处理:
签名串=Base64Encode(HmacSha1(签名原文,SecretKey))
最终得到签名串为:
LsgT3TbVPb2nS3ac1U34stZ/D5k=
压缩 FrameSize 固定640,即一次压缩640 short,否则解压会失败。传到服务端可以是多帧的拼接组合,每一帧需满足下面格式。 每一帧压缩数据封装如下:
OpusHead(4字节) | 帧数据长度(2字节) | Opus 一帧压缩数据 |
---|---|---|
opus | 长度 len | 对应 len 长的 opus decode data |
实时语音识别的 RESTfulAPI 请求返回结果如下表所示:
参数名称 | 描述 |
---|---|
code | 0:正常,其他,发生错误 |
message | 0:success;不为0:其他 |
voice_id | 表示这通音频的标记,同一个音频流这个标记一样 |
seq | 语音分片序号,与请求时设置的 seq 相同 |
text | 语音分片的识别结果 如果请求参数 needvad 为0的话,text 为完整识别结果。 如果请求参数 needvad 为1的话,text 为空,需要从 result_list 字段中获取分片识别结果。 |
result_number | 表示后面的 result_list 里面有几段结果,如果是0表示没有结果,遇到中间是静音。 如果是1表示 result_list 有一个结果, 在发给服务器分片很大的情况下可能会出现多个结果,正常情况下都是1个结果。 |
result_list | slice_type:返回分片类型标记, 0表示一小段话开始,1表示在小段话的进行中,2表示小段话的结束 index 表示第几段话 start_time 这个分片在整个音频流中的开始时间 end_time 这个分片在整个音频流中的结束时间 voice_text_str 识别结果 |
final | 0 表示还在整个音频流的中间部分 1 表示是整个音频流的最后一个包。 主要是在电信场景中,客户端发送完了之后,要知道是否返回的是最后一个包。 |
word_size | 表示后面的 word_list 的长度,即有多少个词。 |
word_list | 词时间戳列表: word 表示这个词的内容, start_time 表示该词在整个音频中的起始时间, end_time 表示该词在整个音频中的结束时间, stable_flag 表示词的稳态结果,0:该词在后续识别中可能发生变化,1:表示该词在后续识别过程中不会变化。 |
第一个分片:
{"code":0,"message":"success","voice_id":"8qiS3yeVnwHHbQ9F","seq":0,"text":"","result_number":1,"result_list":[{"slice_type":0,"index":1,"start_time":0,"end_time":256,"voice_text_str":""}],"final":0}
中间某个分片:
{"code":0,"message":"success","voice_id":"8qiS3yeVnwHHbQ9F","seq":2,"text":"吃饭了。","result_number":1,"result_list":[{"slice_type":0,"index":1,"start_time":512,"end_time":768,"voice_text_str":"吃饭了。"}],"final":0}
最后一个分片:
{"code":0,"message":"success","voice_id":"K3lRwC6tWkKjvnCL","seq":6,"text":"吃饭了吗。","result_number":1,"result_list":[{"slice_type":0,"index":1,"start_time":3072,"end_time":3093,"voice_text_str":"吃饭了吗。"}],"final":1}
数值 | 说明 |
---|---|
100 | 获取语音分片信息失败 |
101 | 语音分片过大 |
102 | 参数不合法,具体详情参考 message |
103 | 访问数据库失败 |
104 | AppID 服务未开通,请在控制台开通服务 |
105 | 模板不存在 |
106 | 模板停用 |
107 | 鉴权失败 |
108 | 拼接签名串失败 |
109 | l5获取 IP、port 失败 |
110 | 后台识别服务器故障,请从 seq=0重传 |
111 | 后台识别模块回包格式错误 |
112 | 语音分片为空 |
113 | 后台服务器识别超时 |
114 | 引擎编号不合法 |
115 | 时长计算时音频类型不合法 |
116 | 无可使用的免费额度 |
117 | 禁止访问 |
118 | 请求限流 |
119 | 账户欠费停止服务,请及时充值 |
120 | 获取 rpcClient 错误 |
121 | 后台识别服务器错误,请从seq=0重传 |
122 | 后台识别服务器收到的包格式错误 |
123 | 后台识别服务器音频解压失败,请从seq=0重传 |
124 | 后台识别服务器识别失败,请从seq=0重传 |
125 | 后台识别服务器识别失败,请重新尝试 |
126 | 后台识别服务器音频分片等待超时,请从seq=0重传 |
127 | 后台识别服务器音频分片重复 |
//filepath 音频文件路径
//printCutResponse 是否打印返回结果
function sendvoice($filepath, $printCutResponse) {
if (empty ($filepath)) {
echo "filepath can not be empty";
return -1;
}
$reqArr = array ();
$reqArr['appid'] = Config :: $APPID;
$reqArr['projectid'] = 0;
$reqArr['sub_service_type'] = 1;
$reqArr['engine_model_type'] = Config :: $ENGINE_MODEL_TYPE;
$reqArr['res_type'] = Config :: $RES_TYPE;
$reqArr['result_text_format'] = Config :: $RESULT_TEXT_FORMAT;
$reqArr['voice_id'] = randstr(16);
$reqArr['needvad'] = 0;
$reqArr['timeout'] = 20000;
$reqArr['source'] = 0;
$reqArr['secretid'] = Config :: $SECRET_ID;
$reqArr['timestamp'] = time();
$reqArr['expired'] = time() + 24 * 60 * 60;
$reqArr['nonce'] = rand(1, 10000);
$reqArr['voice_format'] = Config :: $VOICE_FORMAT;
$secretKey = Config :: $SECRET_KEY;
$voicedata = file_get_contents($filepath);
$datalen = strlen($voicedata);
$cutlegth = Config :: $CUTLENGTH;
$seq = 0;
while ($datalen > 0) {
$end = 0;
if ($datalen < $cutlegth)
$end = 1;
$serverUrl = "http://asr.cloud.tencent.com/asr/v1/";
$reqArr['end'] = $end;
$reqArr['seq'] = $seq;
$serverUrl .= $reqArr['appid'] . "?";
$serverUrl .= "projectid=" . $reqArr['projectid'] . "&";
if (isset ($reqArr['template_name'])) {
$serverUrl .= "template_name=" . $reqArr['template_name'] . "&";
}
$serverUrl .= "sub_service_type=" . $reqArr['sub_service_type'] . "&";
$serverUrl .= "engine_model_type=" . $reqArr['engine_model_type'] . "&";
$serverUrl .= "res_type=" . $reqArr['res_type'] . "&";
$serverUrl .= "result_text_format=" . $reqArr['result_text_format'] . "&";
$serverUrl .= "voice_id=" . $reqArr['voice_id'] . "&";
$serverUrl .= "seq=" . $seq . "&";
$serverUrl .= "end=" . $end . "&";
$serverUrl .= "timeout=" . $reqArr['timeout'] . "&";
$serverUrl .= "source=" . $reqArr['source'] . "&";
$serverUrl .= "secretid=" . $reqArr['secretid'] . "&";
$serverUrl .= "timestamp=" . $reqArr['timestamp'] . "&";
$serverUrl .= "expired=" . $reqArr['expired'] . "&";
$serverUrl .= "nonce=" . $reqArr['nonce'] . "&";
$serverUrl .= "needvad=" . $reqArr['needvad'] . "&";
$serverUrl .= "voice_format=" . $reqArr['voice_format'];
$autho = createSign($reqArr, "POST", "asr.cloud.tencent.com", "/asr/v1/", $secretKey);
if ($datalen < $cutlegth) {
$data = file_get_contents($filepath, NULL, NULL, $seq * $cutlegth, $cutlegth);
} else {
$data = file_get_contents($filepath, NULL, NULL, $seq * $cutlegth, $cutlegth);
}
$seq = $seq +1;
$datalen = $datalen - $cutlegth;
$header = array (
'Authorization: ' . $autho,
'Content-Length: ' . strlen($data),
);
$rsp_str = "";
$http_code = -1;
$ret = http_curl_exec($serverUrl, $data, $rsp_str, $http_code, 'POST', 10, array(), $header);
if ($ret != 0) {
echo "http_curl_exec failed \n";
return false;
}
if($printCutResponse)
echo "rsp_str : \n" . $rsp_str . "\n";
}
return $rsp_str;
}