使用 C++ SDK 发送音频流,获取中间识别结果和句子级最终结果。本文的 Linux x86_64 示例使用 3.3.0b。
快速接入
准备 Linux 编译环境,获取 SDK,配置访问凭证,然后运行完整示例。请求参数和返回事件的详细定义,请参见实时语音识别接口说明。
原 API 2.0 已下线,与本文使用的 API 3.1 定义不同。
准备环境
Linux 编译工具最低版本要求:CMake 3.0、glibc 2.5、GCC 4.8.5。
本示例使用 Linux x86_64、GCC 10.2.1 和 glibc 2.32。
获取和安装 SDK
源码仓库
从 SDK 的 GitHub 仓库获取源码。仓库默认分支可能与下载包版本不同,编译方法以对应版本的 readme.md 为准。
git clone --depth 1 https://github.com/aliyun/alibabacloud-nls-cpp-sdk
下载包
源码包需要编译后生成库文件;平台预编译包包含库文件和头文件。
|
SDK 包 |
平台 |
MD5 |
|
SDK源码 |
7257c0998654e611cf2e8ca9867670ef |
|
|
Linux x86_64 |
9a93df607f26f1558bc1043a425af6d1 |
|
|
Linux aarch64 |
76c34a3ab397d7285963a139b9270ff4 |
alibabacloud-nls-cpp-sdk<version>-master_<github commit id>.zip 是源码包;NlsCppSdk_<平台>_<版本号>_<github commit id>.tar.gz 是平台预编译包,使用方法见包内 readme.md。
编译与集成
源码编译
在源码根目录执行以下命令,生成 SDK 库文件及 srDemo(一句话识别)、stDemo(实时语音识别)、syDemo(语音合成)、daDemo(语音对话)等示例程序。
./scripts/build_linux.sh
生成的头文件和库位于 build/install/NlsSdk3.X_LINUX。在 build/demo 目录运行 ./stDemo 可查看包内示例的使用方式。
预编译包
解压与 CPU 架构匹配的 SDK 包,再选择所需的 Debug 或 Release 包。使用与应用一致的 C++ ABI;CXX11 包对应 _GLIBCXX_USE_CXX11_ABI=1。
3.3.0b 的 Linux x86_64 Release CXX11 包内,demo/build_linux_demo.sh 仍指定 ABI=0。使用此包时,将示例编译参数调整为 ABI=1,避免链接错误。
配置访问凭证
运行示例前,为当前进程设置以下环境变量。AppKey 和 AccessKey 必须属于实际使用的服务账号。不要在代码或日志中写入凭证明文。
|
环境变量 |
说明 |
|
NLS_APPKEY_ENV |
项目的 AppKey。 |
|
NLS_AK_ENV |
AccessKey ID。 |
|
NLS_SK_ENV |
AccessKey Secret。 |
export NLS_APPKEY_ENV='<appkey>'
export NLS_AK_ENV='<accesskey-id>'
export NLS_SK_ENV='<accesskey-secret>'
在 IDE 中运行时,也需在运行配置中设置这些环境变量。示例通过 NlsToken 获取访问令牌,缓存令牌及过期时间,并在临近过期时更新;多个并发请求可共用有效令牌。
代码示例
以下完整示例包含客户端初始化、Token 获取、异步事件处理、音频发送及资源释放。使用 SDK 包中的 test0.wav,并将文件放在程序工作目录;并发示例还使用 test1.wav。
示例中使用的音频文件为16000 Hz采样率,控制台设置的模型为通用模型。如果使用其他音频,请设置为支持该音频场景的模型。关于模型设置,请参见管理项目。
示例等待启动事件后发送音频,收到失败事件后停止发送,并将失败状态传递为非零进程退出码。正常完成时,依次收到 TranscriptionCompleted 和通道关闭事件。
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <ctime>
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <sys/time.h>
#include <errno.h>
#include <atomic>
#include "nlsClient.h"
#include "nlsEvent.h"
#include "nlsToken.h"
#include "speechTranscriberRequest.h"
#define FRAME_SIZE 3200
#define SAMPLE_RATE 16000
using namespace AlibabaNlsCommon;
using AlibabaNls::NlsClient;
using AlibabaNls::NlsEvent;
using AlibabaNls::LogDebug;
using AlibabaNls::LogInfo;
using AlibabaNls::LogError;
using AlibabaNls::SpeechTranscriberRequest;
struct ParamStruct {
std::string fileName;
std::string appkey;
std::string token;
int result = -1;
};
struct ParamCallBack {
public:
ParamCallBack() {
pthread_mutex_init(&mtxWord, NULL);
pthread_cond_init(&cvWord, NULL);
};
~ParamCallBack() {
pthread_mutex_destroy(&mtxWord);
pthread_cond_destroy(&cvWord);
};
std::atomic<bool> started{false}, closed{false}, failed{false}, completed{false};
int userId;
char userInfo[8];
pthread_mutex_t mtxWord;
pthread_cond_t cvWord;
};
std::string g_akId = "";
std::string g_akSecret = "";
std::string g_token = "";
long g_expireTime = -1;
int g_sync_timeout = 0;
struct timeval tv;
struct timeval tv1;
int generateToken(std::string akId, std::string akSecret,
std::string* token, long* expireTime) {
NlsToken nlsTokenRequest;
nlsTokenRequest.setAccessKeyId(akId);
nlsTokenRequest.setKeySecret(akSecret);
int ret = nlsTokenRequest.applyNlsToken();
if (ret < 0) {
printf("generateToken Failed, error code:%d msg:%s\n",
ret, nlsTokenRequest.getErrorMsg());
return ret;
}
*token = nlsTokenRequest.getToken();
*expireTime = nlsTokenRequest.getExpireTime();
return 0;
}
unsigned int getSendAudioSleepTime(int dataSize,
int sampleRate,
int compressRate) {
const int sampleBytes = 16;
const int soundChannel = 1;
int bytes = (sampleRate * sampleBytes * soundChannel) / 8;
int bytesMs = bytes / 1000;
int sleepMs = (dataSize * compressRate) / bytesMs;
return sleepMs;
}
void onTranscriptionStarted(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
printf("onTranscriptionStarted: %d\n", tmpParam->userId);
printf("onTranscriptionStarted: status code=%d, task id=%s\n",
cbEvent->getStatusCode(), cbEvent->getTaskId());
pthread_mutex_lock(&(tmpParam->mtxWord));
tmpParam->started = true;
pthread_cond_signal(&(tmpParam->cvWord));
pthread_mutex_unlock(&(tmpParam->mtxWord));
}
void onSentenceBegin(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
printf("onSentenceBegin: %d\n", tmpParam->userId);
printf("onSentenceBegin: status code=%d, task id=%s, index=%d, time=%d\n",
cbEvent->getStatusCode(), cbEvent->getTaskId(),
cbEvent->getSentenceIndex(),
cbEvent->getSentenceTime()
);
}
void onSentenceEnd(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
printf("onSentenceEnd: %d\n", tmpParam->userId);
printf("onSentenceEnd: status code=%d, task id=%s, index=%d, time=%d, begin_time=%d, result=%s\n",
cbEvent->getStatusCode(),
cbEvent->getTaskId(),
cbEvent->getSentenceIndex(),
cbEvent->getSentenceTime(),
cbEvent->getSentenceBeginTime(),
cbEvent->getResult()
);
}
void onTranscriptionResultChanged(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
printf("onTranscriptionResultChanged: %d\n", tmpParam->userId);
printf("onTranscriptionResultChanged: status code=%d, task id=%s, index=%d, time=%d, result=%s\n",
cbEvent->getStatusCode(),
cbEvent->getTaskId(),
cbEvent->getSentenceIndex(),
cbEvent->getSentenceTime(),
cbEvent->getResult()
);
}
void onTranscriptionCompleted(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
tmpParam->completed = true;
printf("onTranscriptionCompleted: %d\n", tmpParam->userId);
printf("onTranscriptionCompleted: status code=%d, task id=%s\n",
cbEvent->getStatusCode(),
cbEvent->getTaskId());
}
void onTaskFailed(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
pthread_mutex_lock(&tmpParam->mtxWord);
tmpParam->failed = true;
pthread_cond_broadcast(&tmpParam->cvWord);
pthread_mutex_unlock(&tmpParam->mtxWord);
printf("onTaskFailed: %d\n", tmpParam->userId);
printf("onTaskFailed: status code=%d, task id=%s, error message=%s\n",
cbEvent->getStatusCode(),
cbEvent->getTaskId(),
cbEvent->getErrorMessage()
);
}
void onChannelClosed(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
printf("onChannelClosed: %d, %s\n", tmpParam->userId, tmpParam->userInfo);
printf("onChannelClosed: response=%s\n", cbEvent->getAllResponse());
pthread_mutex_lock(&(tmpParam->mtxWord));
tmpParam->closed = true;
pthread_cond_broadcast(&(tmpParam->cvWord));
pthread_mutex_unlock(&(tmpParam->mtxWord));
}
void* pthreadFunction(void* arg) {
int sleepMs = 0;
int ret = 0;
ParamCallBack *cbParam = NULL;
cbParam = new ParamCallBack();
cbParam->userId = rand() % 100;
strcpy(cbParam->userInfo, "User.");
ParamStruct *tst = (ParamStruct *) arg;
if (tst == NULL) {
printf("arg is not valid\n");
delete cbParam;
return NULL;
}
std::ifstream fs;
fs.open(tst->fileName.c_str(), std::ios::binary | std::ios::in);
if (!fs) {
printf("Cannot open %s\n", tst->fileName.c_str());
delete cbParam;
return NULL;
}
SpeechTranscriberRequest* request =
NlsClient::getInstance()->createTranscriberRequest();
if (request == NULL) {
printf("createTranscriberRequest failed\n");
delete cbParam;
return NULL;
}
request->setOnTranscriptionStarted(onTranscriptionStarted, cbParam);
request->setOnTranscriptionResultChanged(onTranscriptionResultChanged, cbParam);
request->setOnTranscriptionCompleted(onTranscriptionCompleted, cbParam);
request->setOnSentenceBegin(onSentenceBegin, cbParam);
request->setOnSentenceEnd(onSentenceEnd, cbParam);
request->setOnTaskFailed(onTaskFailed, cbParam);
request->setOnChannelClosed(onChannelClosed, cbParam);
request->setAppKey(tst->appkey.c_str());
request->setToken(tst->token.c_str());
request->setUrl("wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1");
request->setFormat("opus");
request->setSampleRate(SAMPLE_RATE);
request->setIntermediateResult(true);
request->setPunctuationPrediction(true);
request->setInverseTextNormalization(true);
struct timespec outtime;
struct timeval now;
ret = request->start();
if (ret < 0) {
printf("start() failed. may be can not connect server. please check network or firewalld\n");
NlsClient::getInstance()->releaseTranscriberRequest(request);
delete cbParam;
return NULL;
} else {
if (g_sync_timeout == 0) {
printf("wait started callback.\n");
gettimeofday(&now, NULL);
outtime.tv_sec = now.tv_sec + 5;
outtime.tv_nsec = now.tv_usec * 1000;
pthread_mutex_lock(&(cbParam->mtxWord));
while (!cbParam->started && !cbParam->failed && !cbParam->closed) {
if (ETIMEDOUT == pthread_cond_timedwait(&(cbParam->cvWord), &(cbParam->mtxWord), &outtime)) {
printf("start timeout.\n");
pthread_mutex_unlock(&(cbParam->mtxWord));
request->cancel();
NlsClient::getInstance()->releaseTranscriberRequest(request);
delete cbParam;
return NULL;
}
}
pthread_mutex_unlock(&(cbParam->mtxWord));
} else {
}
}
while (cbParam->started && !cbParam->failed && !cbParam->closed && !fs.eof()) {
uint8_t data[FRAME_SIZE] = {0};
fs.read((char *) data, sizeof(uint8_t) * FRAME_SIZE);
size_t nlen = fs.gcount();
if (nlen <= 0) {
continue;
}
ret = request->sendAudio(data, nlen, ENCODER_OPUS);
if (ret < 0) {
printf("send data fail.\n");
cbParam->failed = true;
break;
}
sleepMs = getSendAudioSleepTime(nlen, SAMPLE_RATE, 1);
usleep(sleepMs * 1000);
}
printf("sendAudio done.\n");
fs.close();
ret = cbParam->failed || cbParam->closed ? -1 : request->stop();
if (ret == 0) {
if (g_sync_timeout == 0) {
printf("wait closed callback.\n");
gettimeofday(&now, NULL);
outtime.tv_sec = now.tv_sec + 5;
outtime.tv_nsec = now.tv_usec * 1000;
pthread_mutex_lock(&(cbParam->mtxWord));
while (!cbParam->closed) {
if (ETIMEDOUT == pthread_cond_timedwait(&(cbParam->cvWord), &(cbParam->mtxWord), &outtime)) {
printf("stop timeout\n");
pthread_mutex_unlock(&(cbParam->mtxWord));
NlsClient::getInstance()->releaseTranscriberRequest(request);
delete cbParam;
return NULL;
}
}
pthread_mutex_unlock(&(cbParam->mtxWord));
} else {
}
} else {
printf("stop ret is %d\n", ret);
cbParam->failed = true;
if (!cbParam->closed) request->cancel();
}
NlsClient::getInstance()->releaseTranscriberRequest(request);
tst->result = (!cbParam->failed && cbParam->completed) ? 0 : -1;
delete cbParam;
return NULL;
}
int speechTranscriberFile(const char* appkey) {
std::time_t curTime = std::time(0);
if (g_expireTime - curTime < 10) {
printf("the token will be expired, please generate new token by AccessKey-ID and AccessKey-Secret.\n");
if (generateToken(g_akId, g_akSecret, &g_token, &g_expireTime) < 0) {
return -1;
}
}
ParamStruct pa;
pa.token = g_token;
pa.appkey = appkey;
pa.fileName = "test0.wav";
pthread_t pthreadId;
if (pthread_create(&pthreadId, NULL, &pthreadFunction, (void *)&pa) != 0) return -1;
pthread_join(pthreadId, NULL);
return pa.result;
}
#define AUDIO_FILE_NUMS 2
#define AUDIO_FILE_NAME_LENGTH 32
int speechTranscriberMultFile(const char* appkey) {
std::time_t curTime = std::time(0);
if (g_expireTime - curTime < 10) {
printf("the token will be expired, please generate new token by AccessKey-ID and AccessKey-Secret.\n");
if (generateToken(g_akId, g_akSecret, &g_token, &g_expireTime) < 0) {
return -1;
}
}
char audioFileNames[AUDIO_FILE_NUMS][AUDIO_FILE_NAME_LENGTH] = {
"test0.wav",
"test1.wav"
};
ParamStruct pa[AUDIO_FILE_NUMS];
for (int i = 0; i < AUDIO_FILE_NUMS; i ++) {
pa[i].token = g_token;
pa[i].appkey = appkey;
pa[i].fileName = audioFileNames[i];
}
std::vector<pthread_t> pthreadId(AUDIO_FILE_NUMS);
int created = 0;
for (; created < AUDIO_FILE_NUMS; ++created) {
if (pthread_create(&pthreadId[created], NULL, &pthreadFunction, &pa[created]) != 0) break;
}
int result = created == AUDIO_FILE_NUMS ? 0 : -1;
for (int j = 0; j < created; ++j) {
pthread_join(pthreadId[j], NULL);
if (pa[j].result != 0) result = -1;
}
return result;
}
int main(int argc, char* argv[]) {
const char* appkeyEnv = getenv("NLS_APPKEY_ENV");
const char* akEnv = getenv("NLS_AK_ENV");
const char* skEnv = getenv("NLS_SK_ENV");
if (!appkeyEnv || !*appkeyEnv || !akEnv || !*akEnv || !skEnv || !*skEnv) {
std::cerr << "Set NLS_APPKEY_ENV, NLS_AK_ENV, and NLS_SK_ENV." << std::endl;
return EXIT_FAILURE;
}
std::string appkey = appkeyEnv;
g_akId = akEnv;
g_akSecret = skEnv;
int ret = NlsClient::getInstance()->setLogConfig(
"log-transcriber", LogError, 10, 3);
if (ret < 0) {
printf("set log failed.\n");
NlsClient::releaseInstance();
return EXIT_FAILURE;
}
if (g_sync_timeout > 0) {
NlsClient::getInstance()->setSyncCallTimeout(g_sync_timeout);
}
NlsClient::getInstance()->startWorkThread(1);
int result = speechTranscriberFile(appkey.c_str());
// To transcribe two files concurrently, replace the preceding call with:
// int result = speechTranscriberMultFile(appkey.c_str());
NlsClient::releaseInstance();
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
本示例不启用预连接池。使用 3.3.0b 的 Linux x86_64 Release CXX11 预编译包时,启用预连接池(例如调用 setPreconnectedPool(2))可能触发内存释放异常,导致进程退出。使用此预编译包时,请勿启用预连接池。如需使用预连接池,可从源码构建 Debug 库。
将 SDK 的 include 和 lib 目录加入项目编译、链接配置。以当前目录下的 NlsSdk3.X_LINUX 为例,示例文件 demo.cpp 可使用以下命令编译:
g++ -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=1 demo.cpp -I NlsSdk3.X_LINUX/include -L NlsSdk3.X_LINUX/lib -lalibabacloud-idst-speech -lpthread -o demo
export LD_LIBRARY_PATH="$PWD/NlsSdk3.X_LINUX/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
使用说明
-
示例按 16000 Hz、16 位、单声道音频计算发送间隔。8 kHz PCM 每发送 1600 字节可等待 100 ms,16 kHz PCM 每发送 3200 字节可等待 100 ms。OPUS/OPU 模式传给 SDK 的仍是 PCM 数据;建议为每次 640 字节、等待 20 ms。实时录音按采集速率发送,文件模拟录音时才需要控制发送速率。
-
sendAudio返回负值时停止发送;收到TranscriptionCompleted或TaskFailed后,SDK 关闭通道,不再继续发送音频。 -
异步模式下,
start()返回不代表启动成功,应等待启动事件或失败事件;stop()返回不代表交互结束,应等待通道关闭事件。调用setSyncCallTimeout后启用同步模式。 -
SDK 多线程指一个音频源对应一个线程,不是多个线程同时发送同一音频源。完整示例提供单文件及双文件并发入口;所有请求结束后再释放全局客户端,
releaseInstance()非线程安全。
免费用户并发连接不能超过 2 个。工作线程数在 200 并发以下建议设置为 1,更高并发的建议配置见 SDK readme.md。
语音断句检测阈值为一句话之后的静音长度,取值范围为 200~2000 ms,默认 800 ms。启用语义断句后,语音断句检测不生效;语义断句需要同时开启中间识别结果。
通过 setPayloadParam 传递定制或高级参数,参数为 JSON 格式字符串,例如 {"vad_model":"farfield"}。
内网访问
在阿里云上海 ECS 上使用内网访问时,创建 SpeechTranscriberRequest 后设置以下 URL。
request->setUrl("ws://nls-gateway-cn-shanghai-internal.aliyuncs.com/ws/v1");
关键接口
NlsClient
NlsClient:语音处理客户端,利用该客户端可以进行一句话识别、实时语音识别和语音合成的语音处理任务。该客户端为线程安全,建议全局仅创建一个实例。
|
接口名 |
启用版本 |
功能描述 |
|
getInstance |
2.x |
获取(创建)NlsClient实例。 |
|
setLogConfig |
2.x |
设置日志文件与存储路径。 支持 LogDebug、LogInfo、LogWarning 和 LogError 日志级别,可设置单个日志文件大小和循环记录的文件数量。 |
|
setDirectHost |
3.x |
跳过DNS域名解析直接设置服务器ipv4地址,若调用则需要在startWorkThread之前。 |
|
setAddrInFamily |
3.1.12 |
设置套接口地址结构的类型,默认为AF_INET仅返回IPV4相关的地址信息,需要在startWorkThread之前调用。 |
|
setUseSysGetAddrInfo |
3.1.13 |
如果 libevent 无法完成 DNS 解析,可调用此接口切换为系统解析接口。需在 startWorkThread 之前调用。 |
|
setSyncCallTimeout |
3.1.17 |
设置同步调用模式的超时时间(ms)。默认值为0(即关闭同步模式)。 使用同步调用模式时:
|
|
setPreconnectedPool |
3.3.0 |
设置每个域名URL的预连接池。
|
|
startWorkThread |
3.x |
启动工作线程。默认值为 1,即启动一个线程;设置为 -1 时,启动与 CPU 核数相同数量的线程。高并发时建议设置为 -1。此接口用于初始化 NlsClient 实例,必须调用。 |
|
releaseInstance |
3.x |
销毁NlsClient对象实例。 |
|
getVersion |
2.x |
获取SDK版本号。 |
|
createTranscriberRequest |
2.x |
创建实时语音识别对象,线程安全,支持高并发请求。 |
|
releaseTranscriberRequest |
2.x |
销毁实时语音识别对象,需要在当前请求的closed事件后调用。 另外,请注意在代码中增加异常处理机制,并在异常发生时及时调用该方法以确保资源释放,否则将影响正常的使用,比如无识别结果返回。 |
NlsToken
NlsToken:创建Token对象,用于申请获取TokenId。使用 getExpireTime 获取过期时间戳,在临近过期时重新申请。有效期内申请新 Token 不会使已有 Token 自动失效。
|
接口名 |
功能描述 |
|
setAccessKeyId |
设置阿里云账号AccessKey ID。 |
|
setKeySecret |
设置阿里云账号AccessKey Secret。 |
|
setDomain |
设置域名,非必填。 |
|
setServerVersion |
设置API版本,非必填。 |
|
setServerResourcePath |
设置服务路径,非必填。 |
|
setRegionId |
设置服务地域 ID,非必填。 |
|
setAction |
设置功能,非必填。 |
|
applyNlsToken |
申请获取TokenId。 |
|
getToken |
获取TokenId。 |
|
getExpireTime |
获取Token有效期时间戳(秒)。 |
|
getErrorMsg |
获得错误信息。 |
NlsEvent
NlsEvent:事件对象,可以从中获取Request状态码、云端返回结果、失败信息等。
|
接口名 |
功能描述 |
|
getStatusCode |
获取状态码,正常情况为0或者20000000,失败时对应失败的错误码。 |
|
getErrorMessage |
在TaskFailed回调中,获取NlsRequest操作过程中出现失败时的错误信息。 |
|
getTaskId |
获取任务的TaskId。 |
|
getAllResponse |
获取云端返回的识别结果。 |
|
getResult |
获取中间识别结果和最终结果。 |
|
getSentenceIndex |
获取实时语音检测的句子编号。 |
|
getSentenceTime |
获取当前已处理的音频时间位置,单位是毫秒。 |
|
getSentenceBeginTime |
对应的SentenceBegin事件的时间,单位是毫秒。 |
SpeechTranscriberRequest
SpeechTranscriberRequest:实时语音识别请求对象,用于长语音实时识别。接口说明以speechTranscriberRequest.h内容为准。
|
接口名 |
启用版本 |
功能描述 |
|
setOnTaskFailed |
2.x |
设置错误回调函数。 |
|
setOnTranscriptionStarted |
2.x |
设置实时音频流识别开始回调函数。 |
|
setOnSentenceBegin |
2.x |
设置一句话开始回调。 |
|
setOnSentenceEnd |
2.x |
设置一句话结束回调函数。 |
|
setOnTranscriptionResultChanged |
2.x |
设置实时音频流识别中间结果回调函数。 |
|
setOnTranscriptionCompleted |
2.x |
设置服务端结束服务回调函数。 |
|
setOnChannelClosed |
2.x |
设置通道关闭回调函数。 |
|
setOnMessage |
3.1.16 |
设置服务端response message回调函数,所有回调从此回调输出由用户自行解析。非必填。设置后需setEnableOnMessage启动。 |
|
setAppKey |
2.x |
设置AppKey。 |
|
setToken |
2.x |
口令认证。所有的请求都必须通过setToken方法认证通过,才可以使用。 |
|
setUrl |
2.x |
设置服务URL地址。 |
|
setIntermediateResult |
2.x |
设置是否返回中间识别结果。 默认值为 false。 |
|
setPunctuationPrediction |
2.x |
设置是否在后处理中添加标点。 默认值为 false。 |
|
setInverseTextNormalization |
2.x |
设置是否在后处理中执行数字转换。 默认值为 false。 |
|
setFormat |
2.x |
设置音频数据编码格式(PCM、OPUS、OPU,默认是PCM,推荐OPUS)。 |
|
setSampleRate |
2.x |
音频采样率设置。 支持 16000 和 8000 Hz,默认值为 16000 Hz。 |
|
setSemanticSentenceDetection |
2.x |
设置是否使用语义断句。 |
|
setMaxSentenceSilence |
2.x |
设置vad阈值。 |
|
setCustomizationId |
2.x |
设置定制模型。 |
|
setVocabularyId |
2.x |
为当前请求设置热词表 ID。如果同时配置了控制台热词和 SDK 热词,则 SDK 配置优先,将覆盖控制台热词配置。 |
|
setTimeout |
2.x |
设置链接超时时间,默认5000ms。 |
|
setSessionId |
2.x |
设置Session ID。 |
|
setOutputFormat |
2.x |
设置输出文本的编码格式:UTF-8 或 GBK。 |
|
setPayloadParam |
2.x |
参数设置,入参为JSON格式字符串。 |
|
setContextParam |
2.x |
设置用户自定义参数,入参为JSON格式字符串。 |
|
AppendHttpHeaderParam |
2.x |
设置用户自定义ws阶段http header参数。 |
|
setSendTimeout |
3.1.14 |
设置发送超时时间,默认5000ms。 |
|
setRecvTimeout |
3.1.14 |
设置接收超时时间, 默认15000ms,需setEnableRecvTimeout开启后生效。 |
|
setEnableRecvTimeout |
3.1.16 |
设置是否启用接收超时检测,默认false,即默认关闭接收超时时间,启用后长时间未收到服务端数据则报错。 |
|
getOutputFormat |
3.1.16 |
获得设置的输出文本的编码格式。 |
|
setEnableOnMessage |
3.1.16 |
设置开启服务器返回消息回调。 |
|
getTaskId |
3.1.17 |
获得当前请求的task_id。 |
|
start |
2.x |
启动SpeechTranscriberRequest。 |
|
stop |
2.x |
会与服务端确认关闭,正常停止实时音频流识别操作。 |
|
cancel |
2.x |
不会与服务端确认关闭,直接关闭实时音频流识别过程。 |
|
control |
2.x |
要求服务端更新识别参数。 |
|
sendAudio |
2.x |
发送语音数据。建议一次发送音频数据640~16384字节。 |
SDK包文件说明
-
scripts/build_linux.sh:SDK源码中,以Linux平台为例的示例编译脚本。
-
CMakeLists.txt:SDK源码中,以Linux平台为例的示例代码工程CMakeList文件。
-
demo目录:SDK包中,集成示例代码,以Linux平台为例,如下表所示。
文件名
描述
speechRecognizerDemo.cpp
一句话识别示例。
speechSynthesizerDemo.cpp
语音合成示例。
speechTranscriberDemo.cpp
实时语音识别示例。
fileTransferDemo.cpp
录音文件识别示例。
-
resource目录:SDK源码中,语音服务范例音频,可用于功能测试,如下表所示。
文件名
描述
-
test0.wav
-
test1.wav
-
test2.wav
-
test3.wav
测试音频(16k采样频率、16bit采样位数的音频文件)。
-
-
include:SDK源码中的头文件,如下表所示。
文件名
描述
nlsClient.h
SDK实例。
nlsEvent.h
回调事件说明。
nlsGlobal.h
SDK全局头文件。
nlsToken.h
SDK Access Token实例。
iNlsRequest.h
NLS请求基础头文件。
speechRecognizerRequest.h
一句话识别。
speechSynthesizerRequest.h
语音合成、长文本语音合成。
speechTranscriberRequest.h
实时音频流识别。
FileTrans.h
录音文件识别。
-
lib:SDK库文件。
-
readme.md:SDK说明。
-
release.log:版本说明。
-
version:版本号。
C++ SDK 错误码
|
状态码 |
状态消息 |
原因 |
解决方案 |
|
0 |
Success |
成功 |
|
|
-10 |
DefaultError |
默认错误 |
暂未使用。 |
|
-11 |
JsonParseFailed |
错误的JSON格式 |
请检查传入的JSON字符串是否符合JSON格式。 |
|
-12 |
JsonObjectError |
错误的JSON对象 |
建议重新尝试。 |
|
-13 |
MallocFailed |
Malloc失败 |
请检查内存是否充足。 |
|
-14 |
ReallocFailed |
Realloc失败 |
请检查内存是否充足。 |
|
-15 |
InvalidInputParam |
传入无效的参数 |
暂未使用。 |
|
-50 |
InvalidLogLevel |
无效日志级别 |
请检查设置的Log级别。 |
|
-51 |
InvalidLogFileSize |
无效日志文件大小 |
请检查设置的Log文件大小参数。 |
|
-52 |
InvalidLogFileNum |
无效日志文件数量 |
请检查设置的Log文件数量参数。 |
|
-100 |
EncoderExistent |
NLS的编码器已存在 |
建议重新尝试。 |
|
-101 |
EncoderInexistent |
NLS的编码器不存在 |
建议重新初始化。 |
|
-102 |
OpusEncoderCreateFailed |
Opus编码器创建失败 |
建议重新初始化。 |
|
-103 |
OggOpusEncoderCreateFailed |
OggOpus编码器创建失败 |
建议重新初始化。 |
|
-104 |
InvalidEncoderType |
encoder类型无效 |
编译时可能关闭OPUS但是又使用,或请检查ENCODER_TYPE。 |
|
-150 |
EventClientEmpty |
主工作线程空指针,已释放 |
建议重新初始化,即startWorkThread()。 |
|
-151 |
SelectThreadFailed |
工作线程选择失败,未初始化 |
建议重新初始化,即startWorkThread()。 |
|
-160 |
StartCommandFailed |
发送start命令失败 |
建议重新尝试。 |
|
-161 |
InvokeStartFailed |
请求状态机不对,导致start失败 |
请检查当前请求是否未创建或者已经完成。 |
|
-162 |
InvokeSendAudioFailed |
请求状态机不对,导致sendAudio失败 |
请检查当前请求是否已经启动(即收到started事件回调)或者已经完成。 |
|
-163 |
InvalidOpusFrameSize |
opus帧长无效,默认为640字节 |
OPU编码模式下,sendAudio一帧只接收640字节数据。 |
|
-164 |
InvokeStopFailed |
请求状态机不对,导致stop失败 |
请检查当前请求是否未启动(即收到started事件回调)或者已经完成。 |
|
-165 |
InvokeCancelFailed |
请求状态机不对,导致cancel失败 |
请检查当前请求是否未启动(即收到started事件回调)或者已经完成。 |
|
-166 |
InvokeStControlFailed |
请求状态机不对,导致stControl失败 |
请检查当前请求是否未启动(即收到started事件回调)或者已经完成。 |
|
-200 |
NlsEventEmpty |
NLS事件为空 |
SDK内部使用,NlsEvent帧丢失。 |
|
-201 |
NewNlsEventFailed |
创建NlsEvent失败 |
SDK内部使用,NlsEvent帧创建失败。 |
|
-202 |
NlsEventMsgEmpty |
NLS事件中消息为空 |
parseJsonMsg()进行解析时发现消息字符串为空。 |
|
-203 |
InvalidNlsEventMsgType |
无效的NLS事件中消息类型 |
SDK内部使用,NlsEvent帧的事件类型不合法。 |
|
-204 |
InvalidNlsEventMsgStatusCode |
无效的NLS事件中消息状态码 |
SDK内部使用,NlsEvent帧的事件消息状态不合法。 |
|
-205 |
InvalidNlsEventMsgHeader |
无效的NLS事件中消息头 |
SDK内部使用,NlsEvent帧的事件消息头不合法。 |
|
-250 |
CancelledExitStatus |
已调用cancel |
暂未使用。 |
|
-251 |
InvalidWorkStatus |
无效的工作状态 |
SDK内部使用,当前请求内部状态不合法。 |
|
-252 |
InvalidNodeQueue |
workThread中NodeQueue无效 |
SDK内部使用,当前待运行的请求不合法,建议释放当前请求重新尝试。 |
|
-300 |
InvalidRequestParams |
请求的入参无效 |
sendAudio传入的数据为空。 |
|
-301 |
RequestEmpty |
请求是空指针 |
SDK内部使用,当前请求已经释放,建议释放当前请求重新尝试。 |
|
-302 |
InvalidRequest |
无效的请求 |
SDK内部使用,当前请求已经释放,建议释放当前请求重新尝试。 |
|
-303 |
SetParamsEmpty |
设置传入的参数为空 |
请检查传入的参数是否为空。 |
|
-350 |
GetHttpHeaderFailed |
获得http头失败 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-351 |
HttpGotBadStatus |
http错误的状态 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-352 |
WsResponsePackageFailed |
解析websocket返回包失败 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-353 |
WsResponsePackageEmpty |
解析websocket返回包为空 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-354 |
WsRequestPackageEmpty |
websocket请求包为空 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-355 |
UnknownWsFrameHeadType |
未知websocket帧头类型 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-356 |
InvalidWsFrameHeaderSize |
无效的websocket帧头大小 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-357 |
InvalidWsFrameHeaderBody |
无效的websocket帧头本体 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-358 |
InvalidWsFrameBody |
无效的websocket帧本体 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-359 |
WsFrameBodyEmpty |
帧数据为空,常见为收到了脏数据 |
SDK内部使用,根据日志中反馈信息详细定位。 |
|
-400 |
NodeEmpty |
node为空指针 |
建议释放当前请求重新尝试。 |
|
-401 |
InvaildNodeStatus |
node所处状态无效 |
SDK内部使用,建议释放当前请求重新尝试。 |
|
-402 |
GetAddrinfoFailed |
通过 DNS 解析地址失败 |
SDK内部使用,请检查当前环境的DNS是否可用。 |
|
-403 |
ConnectFailed |
联网失败 |
请检查当前网络环境是否可用。 |
|
-404 |
InvalidDnsSource |
当前设备无DNS |
SDK内部使用,请检查当前环境的DNS是否可用。 |
|
-405 |
ParseUrlFailed |
无效URL |
请检查设置的URL是否有效。 |
|
-406 |
SslHandshakeFailed |
SSL握手失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-407 |
SslCtxEmpty |
SSL_CTX 为空 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-408 |
SslNewFailed |
SSL_new失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-409 |
SslSetFailed |
SSL设置参数失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-410 |
SslConnectFailed |
SSL_connect失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-411 |
SslWriteFailed |
SSL发送数据失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-412 |
SslReadSysError |
SSL接收数据收到SYSCALL错误 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-413 |
SslReadFailed |
SSL接收数据失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-414 |
SocketFailed |
创建socket失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-415 |
SetSocketoptFailed |
设置socket参数失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-416 |
SocketConnectFailed |
进行socket链接失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-417 |
SocketWriteFailed |
socket发送数据失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-418 |
SocketReadFailed |
socket接收数据失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-430 |
NlsReceiveFailed |
NLS接收帧数据失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-431 |
NlsReceiveEmpty |
NLS接收帧数据为空 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-432 |
ReadFailed |
接收数据失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-433 |
NlsSendFailed |
NLS发送数据失败 |
SDK内部使用,请检查当前网络环境是否可用,并再次尝试。 |
|
-434 |
NewOutputBufferFailed |
创建buffer失败 |
SDK内部使用,请检查内存是否充足。 |
|
-435 |
NlsEncodingFailed |
音频编码失败 |
SDK内部使用,建议释放当前请求重新尝试。 |
|
-436 |
EventEmpty |
event为空 |
SDK内部使用,建议释放当前请求重新尝试。 |
|
-437 |
EvbufferTooMuch |
evbuffer中数据太多 |
SDK内部使用,发送数据缓存已满(16K音频最大缓存320000,8K音频最大缓存160000),请检查是否发送音频数据过频或一次发送过多数据。 |
|
-438 |
EvutilSocketFailed |
evutil设置参数失败 |
SDK内部使用,建议释放当前请求重新尝试。 |
|
-439 |
InvalidExitStatus |
无效的退出状态 |
请检查是否已经cancel了当前请求。 |
|
-450 |
InvalidAkId |
阿里云账号ak id无效 |
请检查阿里云账号ak id是否为空。 |
|
-451 |
InvalidAkSecret |
阿里云账号ak secret无效 |
请检查阿里云账号ak secret是否为空。 |
|
-452 |
InvalidAppKey |
项目appKey无效 |
请检查阿里云项目appKey是否为空。 |
|
-453 |
InvalidDomain |
domain无效 |
请检查输入的domain是否为空。 |
|
-454 |
InvalidAction |
action无效 |
请检查输入的action是否为空。 |
|
-455 |
InvalidServerVersion |
ServerVersion无效 |
请检查输入的ServerVersion是否为空。 |
|
-456 |
InvalidServerResource |
ServerResource无效 |
请检查输入的ServerResource是否为空。 |
|
-457 |
InvalidRegionId |
RegionId无效 |
请检查输入的RegionId是否为空。 |
|
-500 |
InvalidFileLink |
无效的录音文件链接 |
录音文件转写文件链接为空。 |
|
-501 |
ErrorStatusCode |
错误的状态码 |
录音文件转写返回错误,详见错误码。 |
|
-502 |
IconvOpenFailed |
申请转换描述失败 |
UTF8与GBK转换失败。 |
|
-503 |
IconvFailed |
编码转换失败 |
UTF8与GBK转换失败。 |
|
-504 |
ClientRequestFaild |
账号客户端请求失败 |
录音文件转写返回失败。 |
|
-999 |
NlsMaxErrorCode |
|
其他状态码 |
状态消息 |
原因 |
解决方案 |
|
10000001 |
NewSslCtxFailed |
SSL: couldn't create a context! |
建议重新初始化。 |
|
10000002 |
DefaultErrorCode |
return of SSL_read: error:00000000:lib(0):func(0):reason(0) |
建议重新尝试。 |
|
return of SSL_read: error:140E0197:SSL routines:SSL_shutdown:shutdown while in init |
|||
|
10000003 |
SysErrorCode |
系统错误。 |
根据系统反馈的错误信息进行处理。 |
|
10000004 |
EmptyUrl |
URL: The url is empty. |
传入的URL为空,请重新填写正确URL。 |
|
10000005 |
InvalidWsUrl |
Could not parse WebSocket url: |
传入的URL格式错误,请重新填写正确URL。 |
|
10000007 |
JsonStringParseFailed |
JSON: Json parse failed. |
JSON格式异常,请通过日志查看具体的错误点。 |
|
10000008 |
UnknownWsHeadType |
WEBSOCKET: unkown head type. |
联网失败,请检查本机DNS解析和URL是否有效。 |
|
10000009 |
HttpConnectFailed |
HTTP: connect failed. |
与云端连接失败,请检查网络后重试。 |
|
10000010 |
MemNotEnough |
内存不足。 |
请检查内存是否充足。 |
|
10000015 |
SysConnectFailed |
connect failed. |
联网失败,请检查本机DNS解析和URL是否有效。 |
|
10000100 |
HttpGotBadStatusWith403 |
Got bad status host=xxxxx line=HTTP/1.1 403 Forbidden |
链接被拒,请检查账号特别是token是否过期。 |
|
10000101 |
EvSendTimeout |
Send timeout. socket error: |
libevent发送event超时,请检查回调中是否有耗时任务,或并发过大导致无法及时处理事件。 |
|
10000102 |
EvRecvTimeout |
Recv timeout. socket error: |
libevent接收event超时,请检查回调中是否有耗时任务,或并发过大导致无法及时处理事件。 |
|
10000103 |
EvUnknownEvent |
Unknown event: |
未知的libevent事件,建议重新尝试。 |
|
10000104 |
OpNowInProgress |
Operation now in progress |
链接正在进行中,建议重新尝试。 |
|
10000105 |
BrokenPipe |
Broken pipe |
pipe处理不过来,建议重新尝试。 |
|
10000110 |
TokenHasExpired |
Gateway:ACCESS_DENIED:The token 'xxx' has expired! |
请更新Token。 |
|
10000111 |
TokenIsInvalid |
Meta:ACCESS_DENIED:The token 'xxx' is invalid! |
请检查token的有效性。 |
|
10000112 |
NoPrivilegeToVoice |
Gateway:ACCESS_DENIED:No privilege to this voice! (voice: zhinan, privilege: 0) |
此发音人无权使用。 |
|
10000113 |
MissAuthHeader |
Gateway:ACCESS_DENIED:Missing authorization header! |
请检查账号是否有权限,或并发是否在限度内。 |
|
10000120 |
Utf8ConvertError |
utf8ToGbk failed |
utf8转码失败,常为系统问题,建议重新尝试。 |
|
20000000 |
SuccessStatusCode |
成功 |
服务端响应状态码
关于服务状态码,请参见接口说明。
常见问题
场景问题
C++ SDK调用智能实时语音解析接口失败,提示 {"TaskFailed":"connect failed."} {"channeclClosed": "nls request finished."}如何解决?
-
C++ SDK3.0及以前的版本有小概率出现这个问题,无需特别关注;3.1及以后版本出现这个问题可能是运行环境的网络问题导致,建议检查本地网络环境。
-
如果没有返回TaskId,说明在连接过程中直接断开,实时语音交互不需要重复调用接口,重复调用会有并发上限和超时时间,并发超过限制会直接返回超过限制,WebSocket连接超过10秒没有音频就会自动断开,但是会返回TaskId。
C++ SDK实时语音识别报错,提示status_text:Gateway:IDLE_TIMEOUT:Websocket session is idle for too long time, the last directive is 'StartTranscription'!如何解决?
-
因为超过10秒没有发送数据到服务端,空闲超时自动断连。同时请检查URI是否正确,
wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1。如果发生这种情况,可以增加重试机制再次请求。 -
也可能服务端瞬间收到大量请求导致单个实例无法及时处理,建议重试。
C++ SDK调用实时语音识别,集成在mrcp里面使用,部分情况下创建多个识别通道时会返回10000002如何解决?
错误码 10000002 是 SDK 默认错误码,底层错误见 Resource temporarily unavailable。一般是建立连接后未向服务端发送数据,导致 WebSocket 超时;下一个发送的指令是StartTranscription。
C++ SDK(3.0及以后版本)使用语音合成和语音识别功能,可以提高GCC5.0以上的编译版本吗?
可以。Linux下支持GCC 4.8.5或以上版本。目前已验证且顺利编译运行的GCC版本包括4.8.5、5.5.0、8.4.0。
为什么连接不到framework?
framework中代码采用Objective-C和C++混合编写而成,所以需要使用.mm后缀文件进行调用,同时确保工程的头文件路径与库文件路径设置正确。
C++ SDK ASR请求有DNS解析失败的情况导致异常,报错“ali-recog-skd.log:AliSpeech_C++SDK(ERROR): GetInetAddressByHostname:252 DNS: resolved timeout.ali-recog-skd.log:AliSpeech_C++SDK(ERROR): start:76 start failed: DNS: resolved timeout..unimrcpserver_current.log: [ERROR] [[./ali/AliRecogChannel.cpp:772,onTaskFailed]]Ali Task start failed Msg :DNS: resolved timeout., start finised."”如何解决?
-
旧版(3.0及以前版本):在高并发或者电脑DNS忙碌的情况下容易出现以上问题,建议更新到3.1.X版本,或进行再次重启请求。
-
新版(3.0及以后版本):已经对此问题进行防御,若仍然偶现此问题,则为电脑DNS忙碌,需要再次重启请求。
C++ SDK(新)集成到其他项目中时,将CMakeLists.txt中的add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) 修改为add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)后编译不通过如何解决?
除了CMakeLists.txt,全工程都需要修改该参数,例如config/linux.thirdparty.debug.cmake和config/linux.thirdparty.release.cmake,请在全目录搜索_GLIBCXX_USE_CXX11_ABI进行修改。
C++ SDK旧版NlsSdkCpp2.0和新版NlsSdkCpp3.X的区别是什么?
NlsSdkCpp2.0版本的SDK每一个请求为一个线程,且接口为同步接口。
NlsSdkCpp3.X 版本由第三方库 libevent 统一处理事件消息,并发性能更强,接口默认异步;3.1.17 起可通过 setSyncCallTimeout 启用同步调用。
C++版的SDK不支持实现C11规范吗? 现在导致项目无法链接SDK该如何解决?
应用和 SDK 必须使用一致的 _GLIBCXX_USE_CXX11_ABI。3.3.0b 源码默认编译脚本会根据 GCC 主版本选择 ABI;CXX11 预编译包使用 ABI=1,不能直接与 ABI=0 的应用混用。
C++ SDK测试Demo成功,集成项目报错,DNS解析失败,报错“nls-gateway-cn-shanghai.aliyuncs.com dns failed: nodename nor servname provided, or not known”如何解决?
-
SDK中会查看当前设备开启的所有协议族(IPv4、IPv6)进行DNS解析请求,
nls-gateway-cn-shanghai.aliyuncs.com不支持 IPv6,返回解析错误,从而导致SDK DNS解析失败退出。可禁用当前设备的IPv6协议族,3.1.12 及以后版本可通过 setAddrInFamily 配置协议族。 -
建议升级到3.1.12及以后版本。
C++ SDK测试Demo可以成功,集成项目报错,网络链接失败,报错“[dnsEventCallback:465]Node:0x7f087c001030 ai_canonname: nls-gateway-cn-shanghai.aliyuncs.com.gds.alibabadns.com[dnsEventCallback:477]Node:0x7f087c001030 IpV4:106.15.XX.XX[connectProcess:1329]Node:0x7f087c001030 sockFd:41[connectProcess:1347]Node:0x7f087c001030 new Socket ip:106.15.XX.XX port:443 Fd:41.[socketConnect:1458]Node:0x7f087c001030 Connect failed:Network is unreachable. retry...”如何解决?
以上现象为无法连接网络,查看日志发现DNS域名解析出来的IP链接不成功,进一步通过Ping判断网络不通。由于本地拦截DNS解析,导致SDK内部libevent的evdns_getaddrinfo获得错误的IP。
解决办法:
-
3.1.12版本以前可将evdns_getaddrinfo()手动替换成系统的getaddrinfo()。
-
3.1.12版本可在
CMakeLists.txt中修改add_definitions(-DNLS_USE_NATIVE_GETADDRINFO)。 -
3.1.12及以后版本增加setDirectHost()接口,可以在SDK外部进行DNS解析,获取正确IP后通过该接口设入。
-
3.1.13及以后版本已解决此问题,若运行时仍存在上述问题,建议调用接口setUseSysGetAddrInfo(true)。
C++ SDK语音合成时传入的文本没有采用UTF-8编码会有什么错误信息?
如果传入的文本没有采用UTF-8编码,在文本中含有中文字符时,语音合成SDK调用start函数会失败,返回错误信息Socket recv failed, errorCode: 0。错误码为0表示服务端已经关闭了连接,此时应检查传入的文本是否采用UTF-8编码。