初版
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
#include "sparkdesk_main.h"
|
||||
#include "authentication.h"
|
||||
#include "stdlib.h"
|
||||
#include "app_config.h"
|
||||
#include "app_task.h"
|
||||
#include "system/timer.h"
|
||||
#include "app_main.h"
|
||||
#include "init.h"
|
||||
#include "key_event_deal.h"
|
||||
#include "device/device.h"
|
||||
#include "app_power_manage.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#include "asm/charge.h"
|
||||
#include "cJSON.h"
|
||||
#include "media/includes.h"
|
||||
#include "audio_config.h"
|
||||
#include "ai_interaction/ai_audio.h"
|
||||
#include "websocket_define.h"
|
||||
#include "ifly_socket.h"
|
||||
#include "ui/ui_api.h"
|
||||
#include "ifly_common.h"
|
||||
|
||||
#if TCFG_IFLYTEK_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST NET_IFLY
|
||||
#define LOG_TAG "[IFLY_AI]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#ifdef TCFG_IFLYTEK_APP_ID
|
||||
#define APP_ID TCFG_IFLYTEK_APP_ID
|
||||
#else
|
||||
#define APP_ID "123"
|
||||
#endif
|
||||
|
||||
#define MAX_SPARKDESK_TOKENS ((MAX_SPARKDESK_LEN - 100) / 6) //1个tokens相当于1.5个汉字,1个汉字最大4个byte,即1个tokens约6个byte
|
||||
#define RECV_LAST_FRAME 2
|
||||
|
||||
typedef enum {
|
||||
IFLY_AI_STATUS_NULL = 0,
|
||||
IFLY_AI_STATUS_START, // 启动
|
||||
IFLY_AI_STATUS_SEND, // 数据已经发给socket
|
||||
IFLY_AI_STATUS_RECV, // 有接受到数据
|
||||
IFLY_AI_STATUS_RECV_END,// 接受完成
|
||||
IFLY_AI_STATUS_RECV_ERROR,// 接受错误
|
||||
IFLY_AI_STATUS_EXIT, // 已经退出
|
||||
} ifly_ai_status;
|
||||
|
||||
struct sparkdesk_info_t {
|
||||
u8 force_stop;
|
||||
u8 recv_finish;
|
||||
ifly_ai_status status;
|
||||
ifly_ai_param *param;
|
||||
};
|
||||
|
||||
static struct sparkdesk_info_t sparkdesk_info;
|
||||
static struct ifly_websocket_struct sparkdesk_socket;
|
||||
|
||||
//json解析函数_ai
|
||||
static void ifly_sparkdesk_recv_cb(u8 *j_str, u32 len, u8 type)
|
||||
{
|
||||
if (sparkdesk_info.force_stop) {
|
||||
return;
|
||||
}
|
||||
if (sparkdesk_info.status >= IFLY_AI_STATUS_RECV_END) {
|
||||
return;
|
||||
}
|
||||
cJSON *cjson_root = cJSON_Parse((char *)j_str);
|
||||
if (cjson_root == NULL) {
|
||||
log_error("cjson error...\r\n");
|
||||
if (sparkdesk_info.status <= IFLY_AI_STATUS_RECV_END) {
|
||||
sparkdesk_info.status = IFLY_AI_STATUS_RECV_ERROR;
|
||||
}
|
||||
return;
|
||||
}
|
||||
sparkdesk_info.status = IFLY_AI_STATUS_RECV;
|
||||
|
||||
cJSON *cjson_header = cJSON_GetObjectItem(cjson_root, "header");
|
||||
cJSON *cjson_status = cJSON_GetObjectItem(cjson_header, "status");
|
||||
cJSON *cjson_payload = cJSON_GetObjectItem(cjson_root, "payload");
|
||||
cJSON *cjson_choices = cJSON_GetObjectItem(cjson_payload, "choices");
|
||||
cJSON *cjson_text = cJSON_GetObjectItem(cjson_choices, "text");
|
||||
|
||||
int arr_size = cJSON_GetArraySize(cjson_text);
|
||||
cJSON *arr_item = cjson_text->child;
|
||||
if (!sparkdesk_info.param->ai_res) {
|
||||
return;
|
||||
}
|
||||
u32 ai_res_len = strlen(sparkdesk_info.param->ai_res);
|
||||
for (int i = 0; i < arr_size; i++) {
|
||||
cJSON *cjson_content = cJSON_GetObjectItem(arr_item, "content");
|
||||
char *cjson_str = cJSON_Print(cjson_content);
|
||||
u32 json_len = strlen(cjson_str);
|
||||
if ((ai_res_len + json_len + 1) > sparkdesk_info.param->ai_res_len) {
|
||||
log_error("len error\n");
|
||||
} else {
|
||||
strcpy(&sparkdesk_info.param->ai_res[ai_res_len], cjson_str);
|
||||
ai_res_len += json_len;
|
||||
}
|
||||
arr_item = arr_item->next;
|
||||
cJSON_free(cjson_str);
|
||||
}
|
||||
|
||||
int res_len = strlen(sparkdesk_info.param->ai_res);
|
||||
|
||||
str_remove_quote(sparkdesk_info.param->ai_res, res_len);
|
||||
|
||||
log_info("final res:%s\n", sparkdesk_info.param->ai_res);
|
||||
|
||||
if (cjson_status->valueint == RECV_LAST_FRAME) {
|
||||
sparkdesk_info.status = IFLY_AI_STATUS_RECV_END;
|
||||
sparkdesk_info.recv_finish = 1;
|
||||
sparkdesk_info.param->event_cb(IFLY_AI_EVT_RECV_OK, sparkdesk_info.param);
|
||||
}
|
||||
|
||||
cJSON_Delete(cjson_root);
|
||||
}
|
||||
|
||||
|
||||
//ai数据模块
|
||||
static char *ifly_sparkdesk_format_audio_data(void)
|
||||
{
|
||||
char *data_str = NULL;
|
||||
cJSON *cjson_test = NULL;
|
||||
cJSON *cjson_header = NULL;
|
||||
cJSON *cjson_parameter = NULL;
|
||||
cJSON *cjson_payload = NULL;
|
||||
cJSON *cjson_chat = NULL;
|
||||
cJSON *cjson_message = NULL;
|
||||
cJSON *cjson_text = NULL;
|
||||
cJSON *cjson_texts = NULL;
|
||||
|
||||
cjson_test = cJSON_CreateObject();
|
||||
cjson_header = cJSON_CreateObject();
|
||||
cjson_parameter = cJSON_CreateObject();
|
||||
cjson_payload = cJSON_CreateObject();
|
||||
cjson_chat = cJSON_CreateObject();
|
||||
cjson_message = cJSON_CreateObject();
|
||||
cjson_text = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(cjson_header, "app_id", APP_ID);
|
||||
cJSON_AddItemToObject(cjson_test, "header", cjson_header);
|
||||
|
||||
cJSON_AddStringToObject(cjson_chat, "domain", "generalv3");
|
||||
cJSON_AddNumberToObject(cjson_chat, "max_tokens", MAX_SPARKDESK_TOKENS);
|
||||
cJSON_AddItemToObject(cjson_parameter, "chat", cjson_chat);
|
||||
cJSON_AddItemToObject(cjson_test, "parameter", cjson_parameter);
|
||||
|
||||
cjson_texts = cJSON_AddArrayToObject(cjson_message, "text");
|
||||
cJSON_AddStringToObject(cjson_text, "role", "user");
|
||||
cJSON_AddStringToObject(cjson_text, "content", sparkdesk_info.param->content);
|
||||
cJSON_AddItemToArray(cjson_texts, cjson_text);
|
||||
cJSON_AddItemToObject(cjson_payload, "message", cjson_message);
|
||||
cJSON_AddItemToObject(cjson_test, "payload", cjson_payload);
|
||||
|
||||
data_str = cJSON_Print(cjson_test);
|
||||
|
||||
cJSON_Delete(cjson_test);
|
||||
|
||||
return data_str;
|
||||
}
|
||||
|
||||
static bool ifly_sparkdesk_get_send(u8 **buf, u32 *len)
|
||||
{
|
||||
if (sparkdesk_info.force_stop) {
|
||||
log_info("ai task kill!\n");
|
||||
return false;
|
||||
}
|
||||
if (sparkdesk_info.status >= IFLY_AI_STATUS_RECV_END) {
|
||||
log_info("ai task kill!\n");
|
||||
return false;
|
||||
}
|
||||
if (sparkdesk_info.status < IFLY_AI_STATUS_SEND) {
|
||||
sparkdesk_info.status = IFLY_AI_STATUS_SEND;
|
||||
char *input_src_json = ifly_sparkdesk_format_audio_data();
|
||||
if (input_src_json == NULL) {
|
||||
log_error("get json err \n");
|
||||
return false;
|
||||
}
|
||||
*buf = (u8 *)input_src_json;
|
||||
*len = strlen(input_src_json);
|
||||
return true;
|
||||
}
|
||||
os_time_dly(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ifly_sparkdesk_event_cb(ifly_socket_event_enum evt, void *param)
|
||||
{
|
||||
switch (evt) {
|
||||
case IFLY_SOCKET_EVT_SEND_OK:
|
||||
cJSON_free(param);
|
||||
os_time_dly(5);
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_SEND_ERROR:
|
||||
cJSON_free(param);
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_INIT_OK:
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_INIT_ERROR:
|
||||
case IFLY_SOCKET_EVT_HANSHACK_ERROR:
|
||||
case IFLY_SOCKET_EVT_ACCIDENT_END:
|
||||
case IFLY_SOCKET_EVT_END:
|
||||
case IFLY_SOCKET_EVT_FORCE_END:
|
||||
if ((evt != IFLY_SOCKET_EVT_END) && (evt != IFLY_SOCKET_EVT_FORCE_END)) {
|
||||
if (!sparkdesk_info.recv_finish) {
|
||||
sparkdesk_info.param->event_cb(IFLY_AI_EVT_NETWORK_FAIL, sparkdesk_info.param);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_EXIT:
|
||||
sparkdesk_info.status = IFLY_AI_STATUS_EXIT;
|
||||
sparkdesk_info.param->event_cb(IFLY_AI_EVT_EXIT, sparkdesk_info.param);
|
||||
if (sparkdesk_socket.auth) {
|
||||
printf("@@@@@@ free sparkdesk auth!!!\n");
|
||||
net_iflytek_free(sparkdesk_socket.auth);
|
||||
sparkdesk_socket.auth = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ifly_sparkdesk_start(ifly_ai_param *param)
|
||||
{
|
||||
memset(&sparkdesk_info, 0, sizeof(struct sparkdesk_info_t));
|
||||
memset(&sparkdesk_socket, 0, sizeof(struct ifly_websocket_struct));
|
||||
|
||||
#if TCFG_IFLYTEK_USE_PSRAM
|
||||
cJSON_Hooks hooks;
|
||||
hooks.malloc_fn = net_iflytek_malloc;
|
||||
hooks.free_fn = net_iflytek_free;
|
||||
cJSON_InitHooks(&hooks);
|
||||
#endif
|
||||
|
||||
sparkdesk_info.param = param;
|
||||
sparkdesk_socket.auth = (u8 *)ifly_authentication("wss://spark-api.xf-yun.com/v3.1/chat",
|
||||
"spark-api.xf-yun.com",
|
||||
"GET /v3.1/chat HTTP/1.1", 20);
|
||||
if (!sparkdesk_socket.auth) {
|
||||
sparkdesk_info.param->event_cb(IFLY_AI_EVT_NETWORK_FAIL, sparkdesk_info.param);
|
||||
return false;
|
||||
}
|
||||
|
||||
sparkdesk_socket.task_name = "ifly_ai";
|
||||
sparkdesk_socket.socket_mode = WEBSOCKET_MODE;
|
||||
sparkdesk_socket.recv_cb = ifly_sparkdesk_recv_cb;
|
||||
sparkdesk_socket.get_send = ifly_sparkdesk_get_send;
|
||||
sparkdesk_socket.event_cb = ifly_sparkdesk_event_cb;
|
||||
|
||||
sparkdesk_info.status = IFLY_AI_STATUS_START;
|
||||
|
||||
//创建链接
|
||||
bool ret = ifly_websocket_client_create(&sparkdesk_socket);
|
||||
if (ret == false) {
|
||||
sparkdesk_info.status = IFLY_AI_STATUS_NULL;
|
||||
net_iflytek_free(sparkdesk_socket.auth);
|
||||
sparkdesk_socket.auth = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ifly_sparkdesk_stop(u8 force_stop, u32 to_ms)
|
||||
{
|
||||
log_info("ai stop!\n");
|
||||
sparkdesk_info.force_stop = force_stop;
|
||||
while (sparkdesk_socket.auth) { // 结束时auth会自动释放
|
||||
os_time_dly(1);
|
||||
if (to_ms <= 10) {
|
||||
break;
|
||||
}
|
||||
to_ms -= 10;
|
||||
}
|
||||
if (to_ms < 1000) {
|
||||
to_ms = 1000;
|
||||
}
|
||||
sparkdesk_info.force_stop = 1;
|
||||
ifly_websocket_client_release(&sparkdesk_socket, to_ms);
|
||||
}
|
||||
|
||||
bool ifly_sparkdesk_is_work()
|
||||
{
|
||||
if ((sparkdesk_info.status != IFLY_AI_STATUS_NULL) && (sparkdesk_info.status != IFLY_AI_STATUS_EXIT)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef __SPARKDESK_MAIN_H
|
||||
#define __SPARKDESK_MAIN_H
|
||||
|
||||
#include "generic/includes.h"
|
||||
|
||||
#define MAX_SPARKDESK_LEN 2000
|
||||
|
||||
typedef enum {
|
||||
IFLY_AI_EVT_RECV_OK = 1, // AI对话完成。*param: ifly_ai_param*
|
||||
IFLY_AI_EVT_NETWORK_FAIL, // 结束。*param: ifly_ai_param*
|
||||
IFLY_AI_EVT_EXIT, // 结束。*param: ifly_ai_param*
|
||||
} ifly_ai_event_enum ;
|
||||
|
||||
typedef struct ifly_ai_struct {
|
||||
// 参数信息,所有参数都需要赋值
|
||||
char *content; // 输入对话数据
|
||||
char *ai_res; // 输出数据
|
||||
u32 ai_res_len; // 输出数据buf长度,最大MAX_SPARKDESK_LEN
|
||||
int (*event_cb)(ifly_ai_event_enum evt, void *param); // 事件回调
|
||||
} ifly_ai_param;
|
||||
|
||||
|
||||
// AI对话启动。*param参数句柄需要在stop之后才能释放
|
||||
bool ifly_sparkdesk_start(ifly_ai_param *param);
|
||||
// AI对话结束。
|
||||
void ifly_sparkdesk_stop(u8 force_stop, u32 to_ms);
|
||||
//判断是否正在运行
|
||||
bool ifly_sparkdesk_is_work(void);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,212 @@
|
||||
#include "app_config.h"
|
||||
#include "ui/ui_api.h"
|
||||
#include "jlui/ui.h"
|
||||
#include "ui_style.h"
|
||||
#include "app_task.h"
|
||||
#include "system/timer.h"
|
||||
#include "app_main.h"
|
||||
#include "init.h"
|
||||
#include "key_event_deal.h"
|
||||
#include "device/device.h"
|
||||
#include "app_power_manage.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#include "asm/charge.h"
|
||||
#include "cat1/cat1_common.h"
|
||||
#include "cJSON.h"
|
||||
#include "media/includes.h"
|
||||
#include "audio_config.h"
|
||||
#include "res/resfile.h"
|
||||
#include "res_config.h"
|
||||
#include "circular_buf.h"
|
||||
#include "ntp/ntp.h"
|
||||
#include "mbedtls/base64.h"
|
||||
#include "mbedtls/md.h"
|
||||
#include "authentication.h"
|
||||
#include "rtc.h"
|
||||
#include "ifly_common.h"
|
||||
|
||||
#if TCFG_IFLYTEK_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST NET_IFLY
|
||||
#define LOG_TAG "[IFLY_AUTH]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#ifdef TCFG_IFLYTEK_APP_SECRET
|
||||
#define API_SECRET TCFG_IFLYTEK_APP_SECRET
|
||||
#else
|
||||
#define API_SECRET "123"
|
||||
#endif
|
||||
|
||||
#ifdef TCFG_IFLYTEK_APP_KEY
|
||||
#define API_KEY TCFG_IFLYTEK_APP_KEY
|
||||
#else
|
||||
#define API_KEY "123"
|
||||
#endif
|
||||
|
||||
|
||||
// 鉴权信息结构体
|
||||
struct auth_info_t {
|
||||
char sig_ori[100];
|
||||
char sig_hmac[100];
|
||||
char sig_hmac_base64[50];
|
||||
char author_ori[200];
|
||||
char author[300];
|
||||
char date_str[50];
|
||||
struct sys_time curtime;
|
||||
struct tm pt;
|
||||
};
|
||||
|
||||
extern void ntp_client_get_time(const char *host);
|
||||
extern size_t strftime_2(char *ptr, size_t maxsize, const char *format, const struct tm *timeptr);
|
||||
|
||||
/* void *_calloc_r(struct _reent *r, size_t a, size_t b) */
|
||||
/* { */
|
||||
/* return calloc(a, b); */
|
||||
/* } */
|
||||
|
||||
static char dec2hex(short int c)
|
||||
{
|
||||
if (0 <= c && c <= 9) {
|
||||
return c + '0';
|
||||
} else if (10 <= c && c <= 15) {
|
||||
return c + 'A' - 10;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void urlencode(char url[])
|
||||
{
|
||||
int i = 0;
|
||||
int len = strlen(url);
|
||||
int res_len = 0;
|
||||
char res[100];
|
||||
for (i = 0; i < len; ++i) {
|
||||
char c = url[i];
|
||||
if (('0' <= c && c <= '9') ||
|
||||
('a' <= c && c <= 'z') ||
|
||||
('A' <= c && c <= 'Z') ||
|
||||
c == '/' || c == '.') {
|
||||
res[res_len++] = c;
|
||||
} else {
|
||||
int j = (short int)c;
|
||||
if (j < 0) {
|
||||
j += 256;
|
||||
}
|
||||
int i1, i0;
|
||||
i1 = j / 16;
|
||||
i0 = j - i1 * 16;
|
||||
res[res_len++] = '%';
|
||||
res[res_len++] = dec2hex(i1);
|
||||
res[res_len++] = dec2hex(i0);
|
||||
}
|
||||
}
|
||||
res[res_len] = '\0';
|
||||
strcpy(url, res);
|
||||
}
|
||||
|
||||
static int ifly_get_sys_time(struct sys_time *time)//获取时间
|
||||
{
|
||||
rtc_read_time(time);
|
||||
int week = caculate_weekday_by_time(time);
|
||||
|
||||
return week;
|
||||
/* void *fd = dev_open("rtc", NULL); */
|
||||
/* if (!fd) { */
|
||||
/* return ; */
|
||||
/* } */
|
||||
/* dev_ioctl(fd, IOCTL_GET_SYS_TIME, (u32)time); */
|
||||
/* dev_close(fd); */
|
||||
}
|
||||
|
||||
char *ifly_authentication(char *host_name, char *host, char *path, int retry)
|
||||
{
|
||||
//初始化
|
||||
struct auth_info_t *auth = net_iflytek_malloc(sizeof(struct auth_info_t));
|
||||
char *url_xf = net_iflytek_malloc(400);
|
||||
int retry_cnt = 0;
|
||||
|
||||
__retry:
|
||||
//获取网络时间
|
||||
/* ntp_client_get_time("s2c.time.edu.cn"); */
|
||||
ntp_client_get_time("ntp.ntsc.ac.cn");
|
||||
/* int ntp = ntp_client_get_time("s2c.time.edu.cn"); */
|
||||
/* if (ntp == -1) { */
|
||||
/* log_error("get ntp error!!\n"); */
|
||||
/* return NULL; */
|
||||
/* } */
|
||||
int week = ifly_get_sys_time(&(auth->curtime));
|
||||
auth->pt.tm_year = auth->curtime.year - 1900;
|
||||
auth->pt.tm_mon = auth->curtime.month - 1;
|
||||
auth->pt.tm_mday = auth->curtime.day;
|
||||
auth->pt.tm_hour = auth->curtime.hour;
|
||||
auth->pt.tm_min = auth->curtime.min;
|
||||
auth->pt.tm_sec = auth->curtime.sec;
|
||||
if (week == 7) {
|
||||
auth->pt.tm_wday = 0;
|
||||
} else {
|
||||
auth->pt.tm_wday = week;
|
||||
}
|
||||
strftime_2(auth->date_str, 40, "", &auth->pt);
|
||||
log_info("%s\n", auth->date_str);
|
||||
|
||||
//拼接sig原始字符
|
||||
sprintf(auth->sig_ori, "%s%s\ndate: %s\n%s", "host: ", host, auth->date_str, path);
|
||||
//通过hmac_sha256加密
|
||||
mbedtls_md_context_t ctx;
|
||||
const mbedtls_md_info_t *info;
|
||||
mbedtls_md_init(&ctx);
|
||||
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
mbedtls_md_setup(&ctx, info, 1);
|
||||
mbedtls_md_hmac_starts(&ctx, (unsigned char *)API_SECRET, strlen(API_SECRET));
|
||||
mbedtls_md_hmac_update(&ctx, (unsigned char *)auth->sig_ori, strlen(auth->sig_ori));
|
||||
mbedtls_md_hmac_finish(&ctx, (unsigned char *)auth->sig_hmac);
|
||||
mbedtls_md_free(&ctx);
|
||||
//进行base64编码
|
||||
int res;
|
||||
mbedtls_base64_encode((unsigned char *)auth->sig_hmac_base64, 50, (size_t *)&res, (unsigned char *)auth->sig_hmac, strlen(auth->sig_hmac));
|
||||
if (strlen(auth->sig_hmac_base64) != 44) { // 根据科大讯飞文档,hamc base64编码后,正常为44bytes
|
||||
log_info("base64 encode error!!\n");
|
||||
if (retry_cnt < retry) {
|
||||
memset(auth, 0, sizeof(struct auth_info_t));
|
||||
memset((char *)url_xf, 0, sizeof(url_xf));
|
||||
retry_cnt++;
|
||||
goto __retry;
|
||||
}
|
||||
}
|
||||
//拼接authorization_ori字符串
|
||||
strcat(auth->author_ori, "api_key=\"");
|
||||
strcat(auth->author_ori, API_KEY);
|
||||
strcat(auth->author_ori, "\",");
|
||||
strcat(auth->author_ori, "algorithm=\"hmac-sha256\",");
|
||||
strcat(auth->author_ori, "headers=\"host date request-line\",");
|
||||
strcat(auth->author_ori, "signature=\"");
|
||||
strcat(auth->author_ori, auth->sig_hmac_base64);
|
||||
strcat(auth->author_ori, "\"");
|
||||
//对autho_ori进行base64编码
|
||||
int author_res;
|
||||
mbedtls_base64_encode((unsigned char *)auth->author, 300, (size_t *)&author_res, (unsigned char *)auth->author_ori, strlen(auth->author_ori));
|
||||
//拼接url
|
||||
strcat(url_xf, host_name);
|
||||
strcat(url_xf, "?");
|
||||
urlencode(auth->date_str);
|
||||
strcat(url_xf, "authorization=");
|
||||
strcat(url_xf, auth->author);
|
||||
strcat(url_xf, "&date=");
|
||||
strcat(url_xf, auth->date_str);
|
||||
strcat(url_xf, "&host=");
|
||||
strcat(url_xf, host);
|
||||
log_info("url_xf:%s\n", url_xf);
|
||||
|
||||
net_iflytek_free(auth);
|
||||
|
||||
return url_xf;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef __AUTHENTICATION_H
|
||||
#define __AUTHENTICATION_H
|
||||
|
||||
#include "generic/includes.h"
|
||||
|
||||
// 鉴权接口。内存需要释放
|
||||
char *ifly_authentication(char *host_name, char *host, char *path, int retry);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "ifly_socket.h"
|
||||
#include "authentication.h"
|
||||
#include "sparkdesk_main.h"
|
||||
#include "tts_main.h"
|
||||
#include "vad_main.h"
|
||||
#include "system/timer.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_IFLYTEK_ENABLE
|
||||
|
||||
#define IFLY_DEMO_TEST 0
|
||||
|
||||
#if IFLY_DEMO_TEST
|
||||
|
||||
struct ifly_net_struct {
|
||||
// vad
|
||||
ifly_vad_param vad_param;
|
||||
|
||||
// ai
|
||||
ifly_ai_param ai_param;
|
||||
|
||||
// tts
|
||||
ifly_tts_param tts_param;
|
||||
|
||||
// other
|
||||
char local_text[MAX_VAD_LEN]; // 对话文本。近端
|
||||
char ai_text[MAX_SPARKDESK_LEN]; // 对话文本。远端
|
||||
int vad_timer;
|
||||
};
|
||||
static struct ifly_net_struct *p_ifly_net = NULL;
|
||||
|
||||
|
||||
static int ifly_tts_event_cb(ifly_tts_event_enum evt, void *param) //tts任务状态callback
|
||||
{
|
||||
switch (evt) {
|
||||
case IFLY_TTS_EVT_PLAY_START:
|
||||
break;
|
||||
case IFLY_TTS_EVT_PLAY_STOP:
|
||||
break;
|
||||
case IFLY_TTS_EVT_PLAY_FAIL_STOP:
|
||||
break;
|
||||
case IFLY_TTS_EVT_EXIT:
|
||||
if (p_ifly_net) {
|
||||
free(p_ifly_net);
|
||||
printf("free!!\n");
|
||||
p_ifly_net = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ifly_ai_event_cb(ifly_ai_event_enum evt, void *param) //ai任务状态callback
|
||||
{
|
||||
switch (evt) {
|
||||
case IFLY_AI_EVT_RECV_OK:
|
||||
ifly_sparkdesk_stop(10);
|
||||
|
||||
p_ifly_net->tts_param.event_cb = ifly_tts_event_cb;
|
||||
p_ifly_net->tts_param.text_res = p_ifly_net->ai_text;
|
||||
ifly_tts_start(&p_ifly_net->tts_param); //创建tts任务
|
||||
|
||||
break;
|
||||
case IFLY_AI_EVT_EXIT:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ifly_vad_event_cb(ifly_vad_event_enum evt, void *param) //vad任务状态callback
|
||||
{
|
||||
switch (evt) {
|
||||
case IFLY_VAD_EVT_AUDIO_START:
|
||||
break;
|
||||
case IFLY_VAD_EVT_RECV_OK:
|
||||
break;
|
||||
case IFLY_VAD_EVT_EXIT: //vad结束,开启ai
|
||||
p_ifly_net->ai_param.ai_res = p_ifly_net->ai_text;
|
||||
p_ifly_net->ai_param.ai_res_len = MAX_SPARKDESK_LEN;
|
||||
p_ifly_net->ai_param.event_cb = ifly_ai_event_cb;
|
||||
p_ifly_net->ai_param.content = p_ifly_net->local_text;
|
||||
p_ifly_net->ai_param.ai_res[0] = 0;
|
||||
ifly_sparkdesk_start(&p_ifly_net->ai_param); //创建ai任务
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void stop_rec(void *priv) //8s结束录音,并关闭vad任务
|
||||
{
|
||||
ifly_vad_stop(10);
|
||||
if (p_ifly_net->vad_timer) {
|
||||
sys_timer_del(p_ifly_net->vad_timer);
|
||||
p_ifly_net->vad_timer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ifly_net_demo(void)
|
||||
{
|
||||
if (p_ifly_net == NULL) {
|
||||
p_ifly_net = zalloc(sizeof(struct ifly_net_struct));
|
||||
ASSERT(p_ifly_net);
|
||||
}
|
||||
p_ifly_net->vad_param.vad_res = p_ifly_net->local_text;
|
||||
p_ifly_net->vad_param.vad_res_len = MAX_VAD_LEN;
|
||||
p_ifly_net->vad_param.event_cb = ifly_vad_event_cb;
|
||||
p_ifly_net->vad_param.vad_res[0] = 0;
|
||||
|
||||
if (!p_ifly_net->vad_timer) {
|
||||
p_ifly_net->vad_timer = sys_timer_add(NULL, stop_rec, 8000);
|
||||
}
|
||||
ifly_vad_start(&p_ifly_net->vad_param); //创建vad任务
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "ifly_common.h"
|
||||
#include "app_config.h"
|
||||
|
||||
|
||||
static void str_remove_quote(char *str, int len)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0, j = 0; i < len; i++) {
|
||||
if (str[i] != '\"') {
|
||||
str[j++] = str[i];
|
||||
}
|
||||
}
|
||||
str[j] = '\0';
|
||||
}
|
||||
|
||||
|
||||
void *net_iflytek_malloc(size_t size)
|
||||
{
|
||||
#if TCFG_IFLYTEK_USE_PSRAM && TCFG_PSRAM_DEV_ENABLE
|
||||
void *p = malloc_psram(size);
|
||||
if (p) {
|
||||
memset(p, 0, size);
|
||||
}
|
||||
return p;
|
||||
#else
|
||||
return zalloc(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void net_iflytek_free(void *pv)
|
||||
{
|
||||
#if TCFG_IFLYTEK_USE_PSRAM && TCFG_PSRAM_DEV_ENABLE
|
||||
return free_psram(pv);
|
||||
#else
|
||||
return free(pv);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef __IFLY_COMMON__H
|
||||
#define __IFLY_COMMON__H
|
||||
|
||||
#include "generic/includes.h"
|
||||
|
||||
// 引号去除函数
|
||||
void str_remove_quote(char *str, int len);
|
||||
|
||||
void *net_iflytek_malloc(size_t size);
|
||||
void net_iflytek_free(void *pv);
|
||||
#endif
|
||||
@@ -0,0 +1,276 @@
|
||||
#include "websocket_api.h"
|
||||
#include "os/os_compat.h"
|
||||
#ifdef WEBSOCKET_MEMHEAP_DEBUG
|
||||
#include "mem_leak_test.h"
|
||||
#endif
|
||||
#include "ifly_socket.h"
|
||||
#include "ui/ui_api.h"
|
||||
#include "cJSON.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_IFLYTEK_ENABLE
|
||||
|
||||
|
||||
#define LOG_TAG_CONST NET_IFLY
|
||||
#define LOG_TAG "[IFLY_SOCKET]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#define IFLY_SOCKET_KILL_TASK_NAME "ifly_socket_kill"
|
||||
|
||||
struct ifly_socket_info {
|
||||
struct websocket_struct socket_info;
|
||||
u8 force_stop; // 强制结束
|
||||
};
|
||||
|
||||
extern int task_kill(const char *name);
|
||||
extern void websockets_sleep(unsigned int ms);
|
||||
|
||||
static void task_kill_callback(char *buf)
|
||||
{
|
||||
log_info("[msg]>>>>>>>>>>>*buf=%s", buf);
|
||||
task_kill(buf);
|
||||
}
|
||||
|
||||
static void ifly_socket_kill_task_main(void *priv)
|
||||
{
|
||||
int msg[16];
|
||||
while (1) {
|
||||
os_taskq_pend(NULL, msg, ARRAY_SIZE(msg));
|
||||
}
|
||||
}
|
||||
|
||||
int ifly_socket_kill_task_create(void)
|
||||
{
|
||||
int err = os_task_create(ifly_socket_kill_task_main,
|
||||
NULL,
|
||||
5,
|
||||
256,
|
||||
256,
|
||||
IFLY_SOCKET_KILL_TASK_NAME);
|
||||
return err;
|
||||
}
|
||||
|
||||
void ifly_socket_kill_task_release(void)
|
||||
{
|
||||
if (!strcmp(os_current_task(), IFLY_SOCKET_KILL_TASK_NAME)) {
|
||||
task_kill(IFLY_SOCKET_KILL_TASK_NAME);
|
||||
return ;
|
||||
}
|
||||
int msg[4];
|
||||
msg[0] = (int)task_kill_callback;
|
||||
msg[1] = 1;
|
||||
msg[2] = (int)IFLY_SOCKET_KILL_TASK_NAME;
|
||||
os_taskq_post_type("app_core", Q_CALLBACK, 3, msg);
|
||||
}
|
||||
|
||||
|
||||
static void websockets_client_reg(struct websocket_struct *websockets_info, ifly_socket_param *ifly_socket)
|
||||
{
|
||||
memset(websockets_info, 0, sizeof(struct websocket_struct));
|
||||
websockets_info->_init = websockets_client_socket_init;
|
||||
websockets_info->_exit = websockets_client_socket_exit;
|
||||
websockets_info->_handshack = webcockets_client_socket_handshack;
|
||||
websockets_info->_send = websockets_client_socket_send;
|
||||
websockets_info->_recv_thread = websockets_client_socket_recv_thread;
|
||||
websockets_info->_heart_thread = websockets_client_socket_heart_thread;
|
||||
websockets_info->_recv_cb = ifly_socket->recv_cb;
|
||||
websockets_info->_recv = NULL;
|
||||
|
||||
websockets_info->websocket_mode = ifly_socket->socket_mode;
|
||||
websockets_info->kill_task = IFLY_SOCKET_KILL_TASK_NAME;
|
||||
}
|
||||
|
||||
|
||||
static int websockets_client_init(struct websocket_struct *websockets_info, u8 *url, const char *origin_str, const char *user_agent_str)
|
||||
{
|
||||
websockets_info->ip_or_url = url;
|
||||
websockets_info->origin_str = origin_str;
|
||||
websockets_info->user_agent_str = user_agent_str;
|
||||
websockets_info->recv_time_out = 1000;
|
||||
|
||||
//应用层和库的版本检测,结构体不一样则返回出错
|
||||
int err = websockets_struct_check(sizeof(struct websocket_struct));
|
||||
if (err == FALSE) {
|
||||
return err;
|
||||
}
|
||||
return websockets_info->_init(websockets_info);
|
||||
}
|
||||
|
||||
static int websockets_client_handshack(struct websocket_struct *websockets_info)
|
||||
{
|
||||
return websockets_info->_handshack(websockets_info);
|
||||
}
|
||||
|
||||
static int websockets_client_send(struct websocket_struct *websockets_info, u8 *buf, int len, char type)
|
||||
{
|
||||
//SSL加密时一次发送数据不能超过16K,用户需要自己分包
|
||||
return websockets_info->_send(websockets_info, buf, len, type);
|
||||
}
|
||||
|
||||
static void websockets_client_exit(struct websocket_struct *websockets_info)
|
||||
{
|
||||
websockets_info->_exit(websockets_info);
|
||||
}
|
||||
|
||||
static void ifly_socket_task_main(void *priv)
|
||||
{
|
||||
int err;
|
||||
ifly_socket_event_enum ifly_evt = IFLY_SOCKET_EVT_END;
|
||||
ifly_socket_param *ifly_socket = (ifly_socket_param *)priv;
|
||||
struct ifly_socket_info *info = (struct ifly_socket_info *)ifly_socket->socket_hdl;
|
||||
const char *origin_str = "http://coolaf.com";
|
||||
|
||||
log_info(" . ----------------- Client Websocket ------------------\r\n");
|
||||
|
||||
/* 0 . malloc buffer */
|
||||
struct websocket_struct *websockets_info = &info->socket_info;
|
||||
/* 1 . register */
|
||||
websockets_client_reg(websockets_info, ifly_socket);
|
||||
|
||||
/* 2 . init */
|
||||
err = websockets_client_init(websockets_info, ifly_socket->auth, origin_str, NULL);
|
||||
if (FALSE == err) {
|
||||
log_error(" . ! Cilent websocket init error !!!\r\n");
|
||||
ifly_evt = IFLY_SOCKET_EVT_INIT_ERROR;
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
/* 3 . hanshack */
|
||||
err = websockets_client_handshack(websockets_info);
|
||||
if (FALSE == err) {
|
||||
log_error(" . ! Handshake error !!!\r\n");
|
||||
ifly_evt = IFLY_SOCKET_EVT_HANSHACK_ERROR;
|
||||
goto exit_ws;
|
||||
}
|
||||
log_info(" . Handshake success \r\n");
|
||||
|
||||
/* 4 . CreateThread */
|
||||
err = os_task_create(websockets_info->_heart_thread,
|
||||
websockets_info,
|
||||
3,
|
||||
1024,
|
||||
0,
|
||||
"websocket_client_heart");
|
||||
if (err == 0) {
|
||||
websockets_info->ping_thread_id = 1;
|
||||
} else {
|
||||
websockets_info->ping_thread_id = 0;
|
||||
}
|
||||
err = os_task_create(websockets_info->_recv_thread,
|
||||
websockets_info,
|
||||
2,
|
||||
1024,
|
||||
0,
|
||||
"websocket_client_recv");
|
||||
if (err == 0) {
|
||||
websockets_info->recv_thread_id = 1;
|
||||
} else {
|
||||
websockets_info->recv_thread_id = 0;
|
||||
}
|
||||
|
||||
ifly_socket->event_cb(IFLY_SOCKET_EVT_INIT_OK, ifly_socket);
|
||||
|
||||
/* 5 . recv or send data */
|
||||
while (1) {
|
||||
u8 *send_buf = NULL;
|
||||
u32 send_len = 0;
|
||||
if (websockets_info->websocket_valid == INVALID_ESTABLISHED) {
|
||||
//websocket底层断开连接
|
||||
ifly_evt = IFLY_SOCKET_EVT_ACCIDENT_END;
|
||||
goto exit_ws;
|
||||
}
|
||||
if (info->force_stop) {
|
||||
ifly_evt = IFLY_SOCKET_EVT_FORCE_END;
|
||||
goto exit_ws;
|
||||
}
|
||||
err = ifly_socket->get_send(&send_buf, &send_len);
|
||||
if (err == false) {
|
||||
// 获取不到数据,正常退出
|
||||
goto exit_ws;
|
||||
}
|
||||
if ((send_buf == NULL) || (send_len == 0)) {
|
||||
continue;
|
||||
}
|
||||
err = websockets_client_send(websockets_info, send_buf, send_len, WCT_TXTDATA);
|
||||
if (FALSE == err) {
|
||||
log_error(" . ! send err !!!\r\n");
|
||||
ifly_socket->event_cb(IFLY_SOCKET_EVT_SEND_ERROR, send_buf);
|
||||
goto exit_ws;
|
||||
}
|
||||
ifly_socket->event_cb(IFLY_SOCKET_EVT_SEND_OK, send_buf);
|
||||
}
|
||||
|
||||
exit_ws:
|
||||
/* 6 . exit */
|
||||
ifly_socket->event_cb(ifly_evt, ifly_socket);
|
||||
|
||||
if (websockets_info->ping_thread_id) {
|
||||
websockets_info->ping_kill_flag = 1;
|
||||
}
|
||||
if (websockets_info->recv_thread_id) {
|
||||
websockets_info->recv_kill_flag = 1;
|
||||
}
|
||||
|
||||
while (websockets_info->recv_kill_flag || websockets_info->ping_kill_flag) {
|
||||
os_time_dly(1);
|
||||
}
|
||||
websockets_client_exit(websockets_info);
|
||||
|
||||
ifly_socket->event_cb(IFLY_SOCKET_EVT_EXIT, ifly_socket);
|
||||
|
||||
free(ifly_socket->socket_hdl);
|
||||
ifly_socket->socket_hdl = NULL;
|
||||
|
||||
int msg[5];
|
||||
msg[0] = (int)task_kill_callback;
|
||||
msg[1] = 1;
|
||||
msg[2] = (int)os_current_task();
|
||||
err = os_taskq_post_type("app_core", Q_CALLBACK, 3, msg);
|
||||
|
||||
os_time_dly(-1);
|
||||
}
|
||||
|
||||
bool ifly_websocket_client_create(ifly_socket_param *ifly_socket)
|
||||
{
|
||||
ifly_socket->socket_hdl = zalloc(sizeof(struct ifly_socket_info));
|
||||
if (ifly_socket->socket_hdl == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
os_task_create(ifly_socket_task_main,
|
||||
ifly_socket,
|
||||
3,
|
||||
2048,
|
||||
0,
|
||||
ifly_socket->task_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ifly_websocket_client_release(ifly_socket_param *ifly_socket, u32 to_ms)
|
||||
{
|
||||
if (ifly_socket && ifly_socket->socket_hdl) {
|
||||
struct ifly_socket_info *info = (struct ifly_socket_info *)ifly_socket->socket_hdl;
|
||||
info->force_stop = 1;
|
||||
|
||||
if (!strcmp(os_current_task(), ifly_socket->task_name)) {
|
||||
// 任务内部调用
|
||||
return ;
|
||||
}
|
||||
// 其他任务调用
|
||||
while (ifly_socket->socket_hdl) {
|
||||
os_time_dly(1);
|
||||
if (to_ms <= 10) {
|
||||
break;
|
||||
}
|
||||
to_ms -= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef __IFLY_SOCKET_H__
|
||||
#define __IFLY_SOCKET_H__
|
||||
|
||||
#include "generic/includes.h"
|
||||
|
||||
typedef enum {
|
||||
IFLY_SOCKET_EVT_SEND_OK = 1, // socket发数成功。*param: 发数buf
|
||||
IFLY_SOCKET_EVT_SEND_ERROR, // socket发数失败。*param: 发数buf
|
||||
|
||||
IFLY_SOCKET_EVT_INIT_ERROR, // socket初始化失败。*param: ifly_socket_param*
|
||||
IFLY_SOCKET_EVT_HANSHACK_ERROR, // socket握手失败。*param: ifly_socket_param*
|
||||
|
||||
IFLY_SOCKET_EVT_INIT_OK, // socket初始化成功。*param: ifly_socket_param*
|
||||
|
||||
IFLY_SOCKET_EVT_ACCIDENT_END, //意外结束。。*param: ifly_socket_param*
|
||||
|
||||
IFLY_SOCKET_EVT_END, // 结束。socket释放资源之前。*param: ifly_socket_param*
|
||||
IFLY_SOCKET_EVT_FORCE_END, // 强制结束。socket释放资源之前。*param: ifly_socket_param*
|
||||
IFLY_SOCKET_EVT_EXIT, // 结束。socket释放资源之后。*param: ifly_socket_param*
|
||||
} ifly_socket_event_enum ;
|
||||
|
||||
typedef struct ifly_websocket_struct {
|
||||
// 参数信息,所有参数都需要赋值
|
||||
char *task_name; // 任务名
|
||||
u8 *auth; // 鉴权buf
|
||||
u8 socket_mode; // 是否加密。WEBSOCKET_MODE, WEBSOCKETS_MODE
|
||||
void (*recv_cb)(u8 *buf, u32 len, u8 type); // 收数回调
|
||||
bool (*get_send)(u8 **buf, u32 *len); // 获取发数buf
|
||||
int (*event_cb)(ifly_socket_event_enum evt, void *param); // 事件回调
|
||||
// 模块内部记录信息,不需要赋值
|
||||
void *socket_hdl; // socket句柄
|
||||
} ifly_socket_param;
|
||||
|
||||
|
||||
// socket创建。*ifly_socket参数句柄需要在release之后才能释放
|
||||
bool ifly_websocket_client_create(ifly_socket_param *ifly_socket);
|
||||
// socket释放。
|
||||
void ifly_websocket_client_release(ifly_socket_param *ifly_socket, u32 to_ms);
|
||||
|
||||
// 用于处理socket收发任务的删除,需要在所有socket创建之前创建,所有socket释放之后释放
|
||||
int ifly_socket_kill_task_create(void);
|
||||
void ifly_socket_kill_task_release(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "circular_buf.h"
|
||||
#include "app_config.h"
|
||||
/* #include "audio_dec.h" */
|
||||
/* #include "media/pcm_decoder.h" */
|
||||
#include "tts_main.h"
|
||||
#include "jlstream.h"
|
||||
#include "ai_rx_player.h"
|
||||
|
||||
#if TCFG_IFLYTEK_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST NET_IFLY
|
||||
#define LOG_TAG "[IFLY_SOCKET]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
extern struct ai_rx_cb ai_rx_cb_t;
|
||||
enum stream_node_state iflytek_tts_ai_rx_get_frame(struct ai_rx_file_handle *hdl, struct stream_frame **pframe)
|
||||
{
|
||||
#define FIX_LEN 1024
|
||||
extern int iflytek_tts_cbuf_data_len();
|
||||
int available = iflytek_tts_cbuf_data_len();
|
||||
int read_size = MIN(available, FIX_LEN);
|
||||
if (read_size <= 0) {
|
||||
*pframe = NULL;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
struct stream_frame *frame = jlstream_get_frame(hdl->node->oport, read_size);
|
||||
if (!frame) {
|
||||
*pframe = NULL;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
extern int iflytek_tts_cbuf_read_data(u8 * data, int size);
|
||||
int rlen = iflytek_tts_cbuf_read_data(frame->data, read_size);
|
||||
frame->len = MAX(rlen, 0);
|
||||
if (rlen != read_size) {
|
||||
printf("cbuf_read mismatch: req=%d, act=%d\n", read_size, rlen);
|
||||
}
|
||||
*pframe = frame;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
|
||||
static void iflytek_tts_audio_begin(void)
|
||||
{
|
||||
struct ai_rx_player_param param = {0};
|
||||
param.coding_type = AUDIO_CODING_STREAM_MP3;
|
||||
param.sample_rate = 16000;
|
||||
param.channel_mode = AUDIO_CH_MIX;
|
||||
param.type = AI_SERVICE_VOICE;
|
||||
ai_rx_player_open(NULL, 0, ¶m);
|
||||
extern void audio_app_volume_set(u8 state, s16 volume, u8 fade);
|
||||
audio_app_volume_set(1, 100, 1);
|
||||
}
|
||||
|
||||
static void iflytek_tts_audio_start(void *arg)
|
||||
{
|
||||
iflytek_tts_audio_begin();
|
||||
}
|
||||
|
||||
void iflytek_tts_audio_play()
|
||||
{
|
||||
if (ai_rx_cb_t.get_frame_event_cb == NULL) {
|
||||
ai_rx_cb_t.get_frame_event_cb = iflytek_tts_ai_rx_get_frame;
|
||||
}
|
||||
iflytek_tts_audio_start(NULL);
|
||||
}
|
||||
|
||||
void iflytek_tts_audio_stop(void *arg)
|
||||
{
|
||||
ai_rx_player_close(0);
|
||||
if (ai_rx_cb_t.get_frame_event_cb) {
|
||||
ai_rx_cb_t.get_frame_event_cb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool ifly_net_tts_dec_check_run(void)
|
||||
{
|
||||
extern bool ai_rx_player_runing(u8 source);
|
||||
if (ai_rx_player_runing(0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef __IFLY_DEC_FILE_H
|
||||
#define __IFLY_DEC_FILE_H
|
||||
|
||||
bool ifly_net_tts_dec_check_run(void);
|
||||
void iflytek_tts_audio_stop(void *arg);
|
||||
void iflytek_tts_audio_play();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,527 @@
|
||||
#include "tts_main.h"
|
||||
#include "cJSON.h"
|
||||
#include "circular_buf.h"
|
||||
#include "stdlib.h"
|
||||
#include "app_config.h"
|
||||
#include "authentication.h"
|
||||
#include "ifly_dec_file.h"
|
||||
#include "sparkdesk_main.h"
|
||||
#include "websocket_define.h"
|
||||
#include "ifly_socket.h"
|
||||
#include "authentication.h"
|
||||
#include "stdlib.h"
|
||||
#include "app_config.h"
|
||||
#include "app_task.h"
|
||||
#include "system/timer.h"
|
||||
#include "app_main.h"
|
||||
#include "init.h"
|
||||
#include "key_event_deal.h"
|
||||
#include "device/device.h"
|
||||
#include "app_power_manage.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#include "asm/charge.h"
|
||||
#include "cJSON.h"
|
||||
#include "media/includes.h"
|
||||
#include "audio_config.h"
|
||||
#include "circular_buf.h"
|
||||
#include "ui/ui_api.h"
|
||||
#include "ifly_common.h"
|
||||
|
||||
#if TCFG_IFLYTEK_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST NET_IFLY
|
||||
#define LOG_TAG "[IFLY_TTS]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#define TTS_AUDIO_SAVE_TEST 0
|
||||
#if TTS_AUDIO_SAVE_TEST
|
||||
static FILE *save_file = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef TCFG_IFLYTEK_APP_ID
|
||||
#define APP_ID TCFG_IFLYTEK_APP_ID
|
||||
#else
|
||||
#define APP_ID "123"
|
||||
#endif
|
||||
|
||||
#define RECV_LAST_FRAME 2
|
||||
|
||||
#define IFLY_TTS_PKG_MAX (1024 * 10) //经测试,最长一包下发数据的长度为8500bytes左右
|
||||
#define IFLY_TTS_CBUF_LEN (IFLY_TTS_PKG_MAX + 4096)
|
||||
#define TTS_TIMEOUT_TIME 8 //防止tts在播放过程中收不到消息而卡住,8s没接收消息,就关闭任务
|
||||
|
||||
|
||||
typedef enum {
|
||||
IFLY_TTS_STATUS_NULL = 0,
|
||||
IFLY_TTS_STATUS_START, // 启动
|
||||
IFLY_TTS_STATUS_SEND, // 数据已经发给socket
|
||||
IFLY_TTS_STATUS_RECV, // 有接受到数据
|
||||
IFLY_TTS_STATUS_RECV_SEC, // 接收到第二包数据
|
||||
IFLY_TTS_STATUS_RECV_END,// 接受完成
|
||||
IFLY_TTS_STATUS_PLAY_END,// 播放完
|
||||
IFLY_TTS_STATUS_RECV_ERROR,// 接受错误
|
||||
IFLY_TTS_STATUS_EXIT, // 已经退出
|
||||
} ifly_tts_status;
|
||||
|
||||
struct tts_info_t {
|
||||
u8 force_stop; // 强制结束
|
||||
u8 recv_finish; // 接受完毕
|
||||
u8 tts_to_cnt; // 超时计数
|
||||
u16 tts_timer; // 超时用的timer ID
|
||||
cbuffer_t dec_cbuf; // 用于tts音频播放的cbuf
|
||||
char *dec_out_buf;
|
||||
ifly_tts_status status;
|
||||
ifly_tts_param *param;
|
||||
};
|
||||
|
||||
static struct tts_info_t tts_info;
|
||||
static struct ifly_websocket_struct tts_socket;
|
||||
static void *ifly_tts_usr_task = "app_core";
|
||||
|
||||
extern int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
|
||||
extern int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
|
||||
|
||||
|
||||
#if 0
|
||||
static int test_read(void *priv, void *buf, u32 len, u8 tmp_flag, int tmp_offset)
|
||||
{
|
||||
int rlen = 0;
|
||||
|
||||
if (tts_info.status > IFLY_TTS_STATUS_RECV_END) {
|
||||
return 0;
|
||||
}
|
||||
if (tmp_flag) {
|
||||
// 格式检查时读数,不保留读取记录
|
||||
void *rbuf = cbuf_read_alloc(&tts_info.dec_cbuf, &rlen);
|
||||
if (rlen) {
|
||||
if (rlen > len) {
|
||||
rlen = len;
|
||||
}
|
||||
memcpy(buf, rbuf, rlen);
|
||||
}
|
||||
} else {
|
||||
// 正常读数
|
||||
rlen = cbuf_read(&tts_info.dec_cbuf, buf, len);
|
||||
if (rlen != len) {
|
||||
if (tts_info.status >= IFLY_TTS_STATUS_RECV_END) {
|
||||
rlen = cbuf_get_data_len(&tts_info.dec_cbuf);
|
||||
rlen = cbuf_read(&tts_info.dec_cbuf, buf, rlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tts_info.status < IFLY_TTS_STATUS_RECV_END) {
|
||||
if (rlen == 0) {
|
||||
rlen = -1; // 没结束时返回-1挂起解码,不结束解码
|
||||
}
|
||||
}
|
||||
return rlen;
|
||||
}
|
||||
#endif
|
||||
|
||||
int iflytek_tts_cbuf_data_len()
|
||||
{
|
||||
int rlen = cbuf_get_data_len(&tts_info.dec_cbuf);
|
||||
return rlen;
|
||||
}
|
||||
|
||||
int iflytek_tts_cbuf_read_data(u8 *data, int size)
|
||||
{
|
||||
int rlen = cbuf_read(&tts_info.dec_cbuf, data, size);
|
||||
return rlen;
|
||||
}
|
||||
|
||||
static void ifly_net_tts_stop(u32 dat)
|
||||
{
|
||||
int to = dat;
|
||||
while (tts_info.dec_out_buf && (tts_info.status < IFLY_TTS_STATUS_PLAY_END)) {
|
||||
/* if (cbuf_get_data_len(&tts_info.dec_cbuf) == 0) { */
|
||||
/* break; */
|
||||
/* } */
|
||||
os_time_dly(1);
|
||||
if (to < 10) {
|
||||
break;
|
||||
}
|
||||
to -= 10;
|
||||
}
|
||||
if (tts_info.status < IFLY_TTS_STATUS_PLAY_END) {
|
||||
tts_info.status = IFLY_TTS_STATUS_PLAY_END;
|
||||
}
|
||||
extern void iflytek_tts_audio_stop(void *arg);
|
||||
iflytek_tts_audio_stop(NULL);
|
||||
}
|
||||
|
||||
static void ifly_net_tts_start(u32 dat)
|
||||
{
|
||||
log_info("ifly_net_tts_start");
|
||||
|
||||
// 开启ai_rx节点
|
||||
extern void iflytek_tts_audio_play();
|
||||
iflytek_tts_audio_play();
|
||||
}
|
||||
|
||||
static void ifly_tts_dec_func(void(*func)(u32 dat), int to)
|
||||
{
|
||||
int argv[3];
|
||||
argv[0] = (int)func;
|
||||
argv[1] = 1;
|
||||
argv[2] = to;
|
||||
int ret = os_taskq_post_type(ifly_tts_usr_task, Q_CALLBACK, 3, argv);
|
||||
if ((ret == OS_ERR_POST_NULL_PTR) || (ret == OS_TASK_NOT_EXIST)) {
|
||||
os_taskq_post_type("app_core", Q_CALLBACK, 3, argv);
|
||||
}
|
||||
}
|
||||
|
||||
//tts数据模块
|
||||
char *ifly_tts_format_text_data(void)
|
||||
{
|
||||
char *data_str = NULL;
|
||||
int out_len = 0;
|
||||
cJSON *cjson_test = NULL;
|
||||
cJSON *cjson_common = NULL;
|
||||
cJSON *cjson_business = NULL;
|
||||
cJSON *cjson_data = NULL;
|
||||
|
||||
char *buf = net_iflytek_malloc(MAX_SPARKDESK_LEN);
|
||||
mbedtls_base64_encode((unsigned char *)buf, MAX_SPARKDESK_LEN, (size_t *)&out_len, (unsigned char *)tts_info.param->text_res, strlen(tts_info.param->text_res) + 1);
|
||||
//定义最长回答
|
||||
|
||||
cjson_test = cJSON_CreateObject();
|
||||
cjson_common = cJSON_CreateObject();
|
||||
cjson_business = cJSON_CreateObject();
|
||||
cjson_data = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(cjson_common, "app_id", APP_ID);
|
||||
cJSON_AddItemToObject(cjson_test, "common", cjson_common);
|
||||
|
||||
cJSON_AddStringToObject(cjson_business, "aue", "lame");
|
||||
cJSON_AddNumberToObject(cjson_business, "sfl", 1);
|
||||
cJSON_AddStringToObject(cjson_business, "auf", "audio/L16;rate=16000");
|
||||
cJSON_AddStringToObject(cjson_business, "vcn", "xiaoyan");
|
||||
cJSON_AddStringToObject(cjson_business, "tte", "UTF8");
|
||||
cJSON_AddItemToObject(cjson_test, "business", cjson_business);
|
||||
|
||||
cJSON_AddNumberToObject(cjson_data, "status", 2);
|
||||
cJSON_AddStringToObject(cjson_data, "text", buf);
|
||||
cJSON_AddItemToObject(cjson_test, "data", cjson_data);
|
||||
|
||||
data_str = cJSON_Print(cjson_test);
|
||||
net_iflytek_free(buf);
|
||||
|
||||
cJSON_Delete(cjson_test);
|
||||
|
||||
log_info("tts content:%s\n", data_str);
|
||||
|
||||
return data_str;
|
||||
}
|
||||
|
||||
static void tts_recv_timeout_timer(void *priv)
|
||||
{
|
||||
if (tts_info.tts_to_cnt < TTS_TIMEOUT_TIME) {
|
||||
tts_info.tts_to_cnt++;
|
||||
if (tts_info.tts_to_cnt == TTS_TIMEOUT_TIME) {
|
||||
log_info("tts timeout!");
|
||||
if (tts_info.status < IFLY_TTS_STATUS_RECV_ERROR) {
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//tts解析播放模块
|
||||
static void ifly_tts_recv_cb(u8 *j_str, u32 len, u8 type)
|
||||
{
|
||||
tts_info.tts_to_cnt = 0; //收到数据重置定时器
|
||||
|
||||
if (tts_info.force_stop) {
|
||||
return;
|
||||
}
|
||||
if (tts_info.status >= IFLY_TTS_STATUS_RECV_END) {
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *cjson_root = cJSON_Parse((char *)j_str);
|
||||
char *audio = NULL;
|
||||
char *res_tts = NULL;
|
||||
if (cjson_root == NULL) {
|
||||
log_error("cjson error...\r\n");
|
||||
if (tts_info.status <= IFLY_TTS_STATUS_RECV_END) {
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *cjson_data = cJSON_GetObjectItem(cjson_root, "data");
|
||||
cJSON *cjson_status = cJSON_GetObjectItem(cjson_data, "status");
|
||||
cJSON *cjson_audio = cJSON_GetObjectItem(cjson_data, "audio");
|
||||
|
||||
audio = cJSON_Print(cjson_audio);
|
||||
if (!audio) {
|
||||
log_error("audio is null!!\n");
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
str_remove_quote(audio, strlen(audio));
|
||||
|
||||
res_tts = net_iflytek_malloc(IFLY_TTS_PKG_MAX);
|
||||
if (!res_tts) {
|
||||
log_error("malloc fail!!\n");
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
|
||||
return;
|
||||
}
|
||||
int olen = 0;
|
||||
int ret = mbedtls_base64_decode((unsigned char *)res_tts, IFLY_TTS_PKG_MAX, (size_t *)&olen, (unsigned char *)audio, strlen(audio));
|
||||
if (ret || (olen > IFLY_TTS_PKG_MAX)) {
|
||||
log_error("mbedtls_base64_decode fail!!\n");
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#if TTS_AUDIO_SAVE_TEST
|
||||
if (save_file) {
|
||||
int wlen = fwrite(save_file, res_tts, olen);
|
||||
if (wlen != olen) {
|
||||
log_error("save file err: %d, %d\n", wlen, olen);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//若cbuf满了,需要先while住,等待播放
|
||||
int to = 1500;
|
||||
while (!cbuf_write(&tts_info.dec_cbuf, res_tts, olen)) {
|
||||
if (tts_info.force_stop) {
|
||||
break;
|
||||
}
|
||||
os_time_dly(1);
|
||||
if (to < 10) {
|
||||
log_info(">>>>>>>>>>>>>>>>>>>>>>>cbuf full");
|
||||
break;
|
||||
}
|
||||
to -= 10;
|
||||
}
|
||||
|
||||
if (tts_info.status == IFLY_TTS_STATUS_RECV) {
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_SEC; // 如果有多帧语音数据,第二帧才开始播放,避免mp3_dec获取不到数据提前结束解码
|
||||
log_info("tts start play!\n");
|
||||
ifly_tts_dec_func(ifly_net_tts_start, 0);
|
||||
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_START, tts_info.param);
|
||||
}
|
||||
if (tts_info.status < IFLY_TTS_STATUS_RECV) {
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV;
|
||||
if (cjson_status->valueint == RECV_LAST_FRAME) { // 如果只有一帧语音数据,直接播放
|
||||
log_info("tts start play!\n");
|
||||
ifly_tts_dec_func(ifly_net_tts_start, 0);
|
||||
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_START, tts_info.param);
|
||||
}
|
||||
}
|
||||
if (cjson_status->valueint == RECV_LAST_FRAME) {
|
||||
log_info("tts last frame!\n");
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_END;
|
||||
tts_info.recv_finish = 1;
|
||||
|
||||
ifly_tts_dec_func(ifly_net_tts_stop, 3000);
|
||||
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_STOP, tts_info.param);
|
||||
}
|
||||
|
||||
cJSON_free(audio);
|
||||
cJSON_Delete(cjson_root);
|
||||
net_iflytek_free(res_tts);
|
||||
|
||||
/* ifly_net_tts_dec_resume(NULL); */
|
||||
|
||||
}
|
||||
|
||||
static bool ifly_tts_get_send(u8 **buf, u32 *len)
|
||||
{
|
||||
if (tts_info.force_stop) {
|
||||
log_info("tts task kill!\n");
|
||||
return false;
|
||||
}
|
||||
if (tts_info.status >= IFLY_TTS_STATUS_PLAY_END) {
|
||||
log_info("tts task kill!\n");
|
||||
return false;
|
||||
}
|
||||
if (tts_info.status < IFLY_TTS_STATUS_SEND) {
|
||||
tts_info.status = IFLY_TTS_STATUS_SEND;
|
||||
char *input_src_json = ifly_tts_format_text_data();
|
||||
if (input_src_json == NULL) {
|
||||
log_error("get json err \n");
|
||||
return false;
|
||||
}
|
||||
*buf = (u8 *)input_src_json;
|
||||
*len = strlen(input_src_json);
|
||||
return true;
|
||||
}
|
||||
os_time_dly(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ifly_tts_event_cb(ifly_socket_event_enum evt, void *param)
|
||||
{
|
||||
switch (evt) {
|
||||
case IFLY_SOCKET_EVT_SEND_OK:
|
||||
if (!tts_info.tts_timer) {
|
||||
tts_info.tts_timer = sys_timer_add(NULL, tts_recv_timeout_timer, 1000);
|
||||
}
|
||||
cJSON_free(param);
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_SEND_ERROR:
|
||||
cJSON_free(param);
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_INIT_OK:
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_INIT_ERROR:
|
||||
case IFLY_SOCKET_EVT_HANSHACK_ERROR:
|
||||
case IFLY_SOCKET_EVT_ACCIDENT_END:
|
||||
case IFLY_SOCKET_EVT_END:
|
||||
case IFLY_SOCKET_EVT_FORCE_END:
|
||||
if (ifly_net_tts_dec_check_run()) { // 正常结束会自动关闭,这里为异常或者强制关闭处理
|
||||
int to = 10;
|
||||
if (evt == IFLY_SOCKET_EVT_ACCIDENT_END) { //如果是意外退出,先播放完cbuf的内容
|
||||
to = 300;
|
||||
} else {
|
||||
if (tts_info.status < IFLY_TTS_STATUS_RECV_ERROR) {
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
|
||||
}
|
||||
}
|
||||
ifly_tts_dec_func(ifly_net_tts_stop, to * 10);
|
||||
while (ifly_net_tts_dec_check_run()) {
|
||||
os_time_dly(1);
|
||||
to -= 1;
|
||||
if (to <= 0) {
|
||||
break;
|
||||
}
|
||||
if (tts_info.force_stop) {
|
||||
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((evt != IFLY_SOCKET_EVT_END) && (evt != IFLY_SOCKET_EVT_FORCE_END)) {
|
||||
if (!tts_info.recv_finish) {
|
||||
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_FAIL_STOP, tts_info.param);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_EXIT:
|
||||
tts_info.status = IFLY_TTS_STATUS_EXIT;
|
||||
tts_info.param->event_cb(IFLY_TTS_EVT_EXIT, tts_info.param);
|
||||
if (tts_socket.auth) {
|
||||
net_iflytek_free(tts_socket.auth);
|
||||
tts_socket.auth = NULL;
|
||||
}
|
||||
if (tts_info.tts_timer) {
|
||||
sys_timer_del(tts_info.tts_timer);
|
||||
tts_info.tts_timer = 0;
|
||||
}
|
||||
if (tts_info.dec_out_buf) {
|
||||
net_iflytek_free(tts_info.dec_out_buf);
|
||||
tts_info.dec_out_buf = NULL;;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ifly_tts_start(ifly_tts_param *param)
|
||||
{
|
||||
memset(&tts_info, 0, sizeof(tts_info));
|
||||
memset(&tts_socket, 0, sizeof(struct ifly_websocket_struct));
|
||||
|
||||
#if TCFG_IFLYTEK_USE_PSRAM
|
||||
cJSON_Hooks hooks;
|
||||
hooks.malloc_fn = net_iflytek_malloc;
|
||||
hooks.free_fn = net_iflytek_free;
|
||||
cJSON_InitHooks(&hooks);
|
||||
#endif
|
||||
|
||||
tts_info.param = param;
|
||||
|
||||
tts_info.dec_out_buf = net_iflytek_malloc(sizeof(char) * IFLY_TTS_CBUF_LEN);
|
||||
cbuf_init(&tts_info.dec_cbuf, tts_info.dec_out_buf, IFLY_TTS_CBUF_LEN);
|
||||
|
||||
tts_socket.auth = (u8 *)ifly_authentication("wss://tts-api.xfyun.cn/v2/tts",
|
||||
"tts-api.xfyun.cn",
|
||||
"GET /v2/tts HTTP/1.1", 20);
|
||||
if (!tts_socket.auth) {
|
||||
net_iflytek_free(tts_info.dec_out_buf);
|
||||
tts_info.dec_out_buf = NULL;;
|
||||
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_FAIL_STOP, tts_info.param);
|
||||
return false;
|
||||
}
|
||||
tts_socket.task_name = "ifly_tts";
|
||||
tts_socket.socket_mode = WEBSOCKET_MODE;
|
||||
tts_socket.recv_cb = ifly_tts_recv_cb;
|
||||
tts_socket.get_send = ifly_tts_get_send;
|
||||
tts_socket.event_cb = ifly_tts_event_cb;
|
||||
|
||||
tts_info.status = IFLY_TTS_STATUS_START;
|
||||
|
||||
//创建链接
|
||||
bool ret = ifly_websocket_client_create(&tts_socket);
|
||||
if (ret == false) {
|
||||
tts_info.status = IFLY_TTS_STATUS_NULL;
|
||||
net_iflytek_free(tts_socket.auth);
|
||||
tts_socket.auth = NULL;
|
||||
net_iflytek_free(tts_info.dec_out_buf);
|
||||
tts_info.dec_out_buf = NULL;;
|
||||
}
|
||||
|
||||
#if TTS_AUDIO_SAVE_TEST
|
||||
if (save_file) {
|
||||
fclose(save_file);
|
||||
save_file = NULL;
|
||||
}
|
||||
save_file = fopen("storage/sd0/C/sf.mp3", "w+");
|
||||
if (!save_file) {
|
||||
log_error("fopen err \n\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ifly_tts_stop(u8 force_stop, u32 to_ms)
|
||||
{
|
||||
log_info("tts close!\n");
|
||||
tts_info.force_stop = force_stop;
|
||||
while (tts_socket.auth) { // 结束时auth会自动释放
|
||||
os_time_dly(1);
|
||||
if (to_ms <= 10) {
|
||||
break;
|
||||
}
|
||||
to_ms -= 10;
|
||||
}
|
||||
if (to_ms < 1000) {
|
||||
to_ms = 1000;
|
||||
}
|
||||
tts_info.force_stop = 1;
|
||||
ifly_websocket_client_release(&tts_socket, to_ms);
|
||||
|
||||
|
||||
#if TTS_AUDIO_SAVE_TEST
|
||||
if (save_file) {
|
||||
fclose(save_file);
|
||||
save_file = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ifly_tts_is_work()
|
||||
{
|
||||
if ((tts_info.status != IFLY_TTS_STATUS_NULL) && (tts_info.status != IFLY_TTS_STATUS_EXIT)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef __TTS_MAIN_H
|
||||
#define __TTS_MAIN_H
|
||||
|
||||
#include "generic/includes.h"
|
||||
|
||||
typedef enum {
|
||||
IFLY_TTS_EVT_PLAY_START = 1, // 播放开始。*param: ifly_tts_param*
|
||||
IFLY_TTS_EVT_PLAY_STOP, // 播放结束。*param: ifly_tts_param*
|
||||
IFLY_TTS_EVT_PLAY_FAIL_STOP, // 播放非正常结束。例如超时、强制结束等。*param: ifly_tts_param*
|
||||
IFLY_TTS_EVT_EXIT, // 结束。*param: ifly_tts_param*
|
||||
} ifly_tts_event_enum ;
|
||||
|
||||
typedef struct ifly_tts_struct {
|
||||
// 参数信息,所有参数都需要赋值
|
||||
char *text_res; // 文本数据
|
||||
int (*event_cb)(ifly_tts_event_enum evt, void *param); // 事件回调
|
||||
} ifly_tts_param;
|
||||
|
||||
|
||||
// TTS启动。*param参数句柄需要在stop之后才能释放
|
||||
bool ifly_tts_start(ifly_tts_param *param);
|
||||
// TTS结束。
|
||||
void ifly_tts_stop(u8 force_stop, u32 to_ms);
|
||||
// 判断tts是否正在运行
|
||||
bool ifly_tts_is_work(void);
|
||||
#endif
|
||||
@@ -0,0 +1,476 @@
|
||||
#include "string.h"
|
||||
#include "vad_main.h"
|
||||
#include "ifly_dec_file.h"
|
||||
#include "sparkdesk_main.h"
|
||||
#include "authentication.h"
|
||||
#include "websocket_define.h"
|
||||
#include "ifly_socket.h"
|
||||
#include "app_config.h"
|
||||
#include "app_task.h"
|
||||
#include "system/timer.h"
|
||||
#include "app_main.h"
|
||||
#include "init.h"
|
||||
#include "key_event_deal.h"
|
||||
#include "device/device.h"
|
||||
#include "app_power_manage.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#include "asm/charge.h"
|
||||
#include "cJSON.h"
|
||||
#include "audio_config.h"
|
||||
#include "third_party_profile/interface/app_protocol_common.h"
|
||||
#include "circular_buf.h"
|
||||
#include "ui/ui_api.h"
|
||||
#include "ifly_common.h"
|
||||
|
||||
#if TCFG_IFLYTEK_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST NET_IFLY
|
||||
#define LOG_TAG "[IFLY_VAD]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#ifdef TCFG_IFLYTEK_APP_ID
|
||||
#define APP_ID TCFG_IFLYTEK_APP_ID
|
||||
#else
|
||||
#define APP_ID "123"
|
||||
#endif
|
||||
|
||||
#if TCFG_ENC_SPEEX_ENABLE
|
||||
#define AI_AUDIO_CODING_TYPE AUDIO_CODING_SPEEX // 编码格式
|
||||
#else
|
||||
#error "ONLY SUPPORT SPEEX"
|
||||
#endif
|
||||
|
||||
#define AI_AUDIO_CODING_SR 16000 // 采样率。和audio_mic_enc_open()函数中的对应
|
||||
|
||||
#define PCM_OUT_BUF_LEN (AI_AUDIO_CODING_SR*2/1000 * 30)
|
||||
|
||||
#define SPEEX_SIZE 42
|
||||
|
||||
#define AUDIO_LEN 168
|
||||
#define BASE63_AUDIO_LEN 256
|
||||
|
||||
#define STATUS_FIRST_FRAME 0
|
||||
#define STATUS_CONTINUE_FRAME 1
|
||||
#define STATUS_LAST_FRAME 2
|
||||
#define STATUS_NED_FRAME 3
|
||||
|
||||
#define HEART_BEAT_REQ "client ping" // 服务器下发的心跳保持请求
|
||||
|
||||
typedef enum {
|
||||
IFLY_VAD_STATUS_NULL = 0,
|
||||
IFLY_VAD_STATUS_START, // 启动
|
||||
IFLY_VAD_STATUS_PCM_START, // 音频启动发数
|
||||
IFLY_VAD_STATUS_SENDING, // 音频数据发数中
|
||||
IFLY_VAD_STATUS_SEND_END, // 音频数据发数完毕
|
||||
IFLY_VAD_STATUS_RECV, // 有接受到数据
|
||||
IFLY_VAD_STATUS_RECV_END, // 接受完成
|
||||
IFLY_VAD_STATUS_RECV_ERROR, // 接受错误
|
||||
IFLY_VAD_STATUS_EXIT, // 已经退出
|
||||
} ifly_vad_status;
|
||||
|
||||
struct vad_info_t {
|
||||
u8 force_stop; // 强制结束
|
||||
u8 recv_finish; // 接收结束
|
||||
u8 frame_status;
|
||||
char *pcm_out_buf;
|
||||
cbuffer_t pcm_cbuf;
|
||||
ifly_vad_status status;
|
||||
ifly_vad_param *param;
|
||||
};
|
||||
|
||||
static struct vad_info_t vad_info;
|
||||
static struct ifly_websocket_struct vad_socket;
|
||||
|
||||
|
||||
#define AI_AUDIO_SAVE_TEST 0
|
||||
#if AI_AUDIO_SAVE_TEST
|
||||
static FILE *save_file = NULL;
|
||||
#endif
|
||||
|
||||
extern int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
|
||||
|
||||
//录音编码模块
|
||||
static u16 vad_audio_send_data(u8 *voice_buf, u16 voice_len)
|
||||
{
|
||||
#if AI_AUDIO_SAVE_TEST
|
||||
if (save_file) {
|
||||
int wlen = fwrite(save_file, voice_buf, voice_len);
|
||||
if (wlen != voice_len) {
|
||||
log_error("save file err: %d, %d\n", wlen, voice_len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//上传数据到服务器
|
||||
int wlen = cbuf_write(&vad_info.pcm_cbuf, voice_buf, voice_len);
|
||||
if (wlen != voice_len) {
|
||||
log_error("pcm out err: %d, %d\n", wlen, voice_len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vad_audio_stop(int cancel)
|
||||
{
|
||||
if (!ai_mic_is_busy()) {
|
||||
log_error("ai_mic_is_null \n\n");
|
||||
return true;
|
||||
}
|
||||
ai_mic_rec_close();
|
||||
|
||||
#if AI_AUDIO_SAVE_TEST
|
||||
if (save_file) {
|
||||
fclose(save_file);
|
||||
save_file = NULL;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static int vad_audio_start(void)
|
||||
{
|
||||
if (ai_mic_is_busy()) {
|
||||
log_error("ai_mic_is_busy \n\n");
|
||||
return false;
|
||||
}
|
||||
#if AI_AUDIO_SAVE_TEST
|
||||
if (save_file) {
|
||||
fclose(save_file);
|
||||
save_file = NULL;
|
||||
}
|
||||
save_file = fopen("storage/sd0/C/sf.bin", "w+");
|
||||
if (!save_file) {
|
||||
log_error("fopen err \n\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
mic_rec_pram_init(AI_AUDIO_CODING_TYPE, 0, vad_audio_send_data, 4, 1024);
|
||||
|
||||
ai_mic_rec_start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ifly_vad_recv_cb(u8 *j_str, u32 len, u8 type)
|
||||
{
|
||||
log_info("recv:%s\n", j_str);
|
||||
|
||||
if (vad_info.force_stop) {
|
||||
return;
|
||||
}
|
||||
if (vad_info.status >= IFLY_VAD_STATUS_RECV_END) {
|
||||
return;
|
||||
}
|
||||
if (!strcmp((char *)j_str, HEART_BEAT_REQ)) { // 保持心跳请求,不解析
|
||||
return;
|
||||
}
|
||||
cJSON *cjson_root = cJSON_Parse((char *)j_str); //json解析错误
|
||||
if (cjson_root == NULL) {
|
||||
log_error("cjson error...\r\n");
|
||||
if (vad_info.status <= IFLY_VAD_STATUS_RECV_END) {
|
||||
vad_info.status = IFLY_VAD_STATUS_RECV_ERROR;
|
||||
}
|
||||
vad_info.param->event_cb(IFLY_VAD_EVT_NETWORK_RECV_ERROR, vad_info.param);
|
||||
return;
|
||||
}
|
||||
vad_info.status = IFLY_VAD_STATUS_RECV;
|
||||
|
||||
cJSON *cjson_data = cJSON_GetObjectItem(cjson_root, "data");
|
||||
cJSON *cjson_status = cJSON_GetObjectItem(cjson_data, "status");
|
||||
cJSON *cjson_result = cJSON_GetObjectItem(cjson_data, "result");
|
||||
cJSON *cjson_ws = cJSON_GetObjectItem(cjson_result, "ws");
|
||||
|
||||
int arr_size = cJSON_GetArraySize(cjson_ws);
|
||||
cJSON *arr_item = cjson_ws->child;
|
||||
u32 vad_res_len = strlen(vad_info.param->vad_res);
|
||||
for (int i = 0; i < arr_size; i++) {
|
||||
cJSON *cjson_cw = cJSON_GetObjectItem(arr_item, "cw");
|
||||
int arr_size_cw = cJSON_GetArraySize(cjson_cw);
|
||||
cJSON *arr_item_cw = cjson_cw->child;
|
||||
for (int j = 0; j < arr_size_cw; j++) {
|
||||
cJSON *cjson_w = cJSON_GetObjectItem(arr_item_cw, "w");
|
||||
char *cjson_str = cJSON_Print(cjson_w);
|
||||
u32 json_len = strlen(cjson_str);
|
||||
if ((vad_res_len + json_len + 1) > vad_info.param->vad_res_len) {
|
||||
log_error("len error\n");
|
||||
} else {
|
||||
strcpy(&vad_info.param->vad_res[vad_res_len], cjson_str);
|
||||
vad_res_len += json_len;
|
||||
}
|
||||
arr_item_cw = arr_item_cw->next;
|
||||
cJSON_free(cjson_str);
|
||||
}
|
||||
|
||||
arr_item = arr_item->next;
|
||||
}
|
||||
|
||||
int res_len = strlen(vad_info.param->vad_res);
|
||||
|
||||
str_remove_quote(vad_info.param->vad_res, res_len);
|
||||
|
||||
log_info("final res:%s\n", vad_info.param->vad_res);
|
||||
|
||||
if (cjson_status->valueint == STATUS_LAST_FRAME) {
|
||||
vad_info.status = IFLY_VAD_STATUS_RECV_END;
|
||||
vad_info.recv_finish = 1;
|
||||
vad_info.param->event_cb(IFLY_VAD_EVT_RECV_OK, vad_info.param);
|
||||
}
|
||||
|
||||
cJSON_Delete(cjson_root);
|
||||
}
|
||||
|
||||
|
||||
//语音听写数据模块
|
||||
char *ifly_vad_format_audio_data(void)
|
||||
{
|
||||
char *data_str = NULL;
|
||||
cJSON *cjson_test = NULL;
|
||||
cJSON *cjson_common = NULL;
|
||||
cJSON *cjson_business = NULL;
|
||||
cJSON *cjson_data = NULL;
|
||||
|
||||
int out_len = 0;
|
||||
char *buf = net_iflytek_malloc(BASE63_AUDIO_LEN);
|
||||
char *audio_data = net_iflytek_malloc(AUDIO_LEN);
|
||||
ASSERT(buf);
|
||||
ASSERT(audio_data);
|
||||
|
||||
int rlen = cbuf_read(&vad_info.pcm_cbuf, audio_data, AUDIO_LEN);
|
||||
if (rlen != AUDIO_LEN) {
|
||||
net_iflytek_free(buf);
|
||||
net_iflytek_free(audio_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vad_info.frame_status == STATUS_FIRST_FRAME) {
|
||||
|
||||
mbedtls_base64_encode((unsigned char *)buf, BASE63_AUDIO_LEN, (size_t *)&out_len, (unsigned char *)audio_data, AUDIO_LEN);
|
||||
|
||||
//编码第一帧
|
||||
cjson_test = cJSON_CreateObject();
|
||||
cjson_common = cJSON_CreateObject();
|
||||
cjson_business = cJSON_CreateObject();
|
||||
cjson_data = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(cjson_common, "app_id", APP_ID);
|
||||
cJSON_AddItemToObject(cjson_test, "common", cjson_common);
|
||||
|
||||
cJSON_AddStringToObject(cjson_business, "language", "zh_cn");
|
||||
cJSON_AddStringToObject(cjson_business, "domain", "iat");
|
||||
cJSON_AddStringToObject(cjson_business, "accent", "mandarin");
|
||||
cJSON_AddNumberToObject(cjson_business, "speex_size", SPEEX_SIZE);
|
||||
cJSON_AddItemToObject(cjson_test, "business", cjson_business);
|
||||
|
||||
cJSON_AddNumberToObject(cjson_data, "status", 0);
|
||||
cJSON_AddStringToObject(cjson_data, "format", "audio/L16;rate=16000");
|
||||
cJSON_AddStringToObject(cjson_data, "encoding", "speex-wb");
|
||||
cJSON_AddStringToObject(cjson_data, "audio", buf);
|
||||
cJSON_AddItemToObject(cjson_test, "data", cjson_data);
|
||||
|
||||
data_str = cJSON_Print(cjson_test);
|
||||
|
||||
cJSON_Delete(cjson_test);
|
||||
|
||||
vad_info.frame_status = STATUS_CONTINUE_FRAME;
|
||||
|
||||
} else if (vad_info.frame_status == STATUS_CONTINUE_FRAME) {
|
||||
|
||||
mbedtls_base64_encode((unsigned char *)buf, BASE63_AUDIO_LEN, (size_t *)&out_len, (unsigned char *)audio_data, AUDIO_LEN);
|
||||
|
||||
//编码
|
||||
cjson_test = cJSON_CreateObject();
|
||||
cjson_data = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddNumberToObject(cjson_data, "status", 1);
|
||||
cJSON_AddStringToObject(cjson_data, "format", "audio/L16;rate=16000");
|
||||
cJSON_AddStringToObject(cjson_data, "encoding", "speex-wb");
|
||||
cJSON_AddStringToObject(cjson_data, "audio", buf);
|
||||
cJSON_AddItemToObject(cjson_test, "data", cjson_data);
|
||||
|
||||
data_str = cJSON_Print(cjson_test);
|
||||
|
||||
cJSON_Delete(cjson_test);
|
||||
|
||||
} else {
|
||||
//编码最后一帧
|
||||
mbedtls_base64_encode((unsigned char *)buf, BASE63_AUDIO_LEN, (size_t *)&out_len, (unsigned char *)audio_data, AUDIO_LEN);
|
||||
|
||||
cjson_test = cJSON_CreateObject();
|
||||
cjson_data = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddNumberToObject(cjson_data, "status", 2);
|
||||
cJSON_AddStringToObject(cjson_data, "format", "audio/L16;rate=16000");
|
||||
cJSON_AddStringToObject(cjson_data, "encoding", "speex-wb");
|
||||
cJSON_AddStringToObject(cjson_data, "audio", buf);
|
||||
cJSON_AddItemToObject(cjson_test, "data", cjson_data);
|
||||
|
||||
data_str = cJSON_Print(cjson_test);
|
||||
|
||||
cJSON_Delete(cjson_test);
|
||||
|
||||
if (vad_info.status < IFLY_VAD_STATUS_SEND_END) {
|
||||
vad_info.status = IFLY_VAD_STATUS_SEND_END;
|
||||
vad_audio_stop(1);
|
||||
cbuf_clear(&vad_info.pcm_cbuf);
|
||||
}
|
||||
}
|
||||
|
||||
net_iflytek_free(buf);
|
||||
net_iflytek_free(audio_data);
|
||||
|
||||
return data_str;
|
||||
}
|
||||
|
||||
static bool ifly_vad_get_send(u8 **buf, u32 *len)
|
||||
{
|
||||
if (vad_info.force_stop) {
|
||||
log_info("vad task kill!\n");
|
||||
return false;
|
||||
}
|
||||
if (vad_info.status >= IFLY_VAD_STATUS_RECV_END) {
|
||||
log_info("recv end vad task kill!\n");
|
||||
return false;
|
||||
}
|
||||
if (vad_info.status < IFLY_VAD_STATUS_PCM_START) {
|
||||
vad_info.status = IFLY_VAD_STATUS_PCM_START;
|
||||
vad_audio_start();
|
||||
vad_info.param->event_cb(IFLY_VAD_EVT_AUDIO_START, vad_info.param);
|
||||
}
|
||||
if (cbuf_get_data_len(&vad_info.pcm_cbuf) >= AUDIO_LEN) {
|
||||
char *input_src_json = ifly_vad_format_audio_data();
|
||||
if (input_src_json == NULL) {
|
||||
log_error("get json err \n");
|
||||
return false;
|
||||
}
|
||||
*buf = (u8 *)input_src_json;
|
||||
*len = strlen(input_src_json);
|
||||
return true;
|
||||
}
|
||||
os_time_dly(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ifly_vad_event_cb(ifly_socket_event_enum evt, void *param)
|
||||
{
|
||||
switch (evt) {
|
||||
case IFLY_SOCKET_EVT_SEND_OK:
|
||||
cJSON_free(param);
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_SEND_ERROR:
|
||||
cJSON_free(param);
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_INIT_OK:
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_INIT_ERROR:
|
||||
case IFLY_SOCKET_EVT_HANSHACK_ERROR:
|
||||
case IFLY_SOCKET_EVT_ACCIDENT_END:
|
||||
case IFLY_SOCKET_EVT_END:
|
||||
case IFLY_SOCKET_EVT_FORCE_END:
|
||||
vad_audio_stop(1);
|
||||
if ((evt != IFLY_SOCKET_EVT_END) && (evt != IFLY_SOCKET_EVT_FORCE_END)) {
|
||||
if (!vad_info.recv_finish) {
|
||||
vad_info.param->event_cb(IFLY_VAD_EVT_NETWORK_FAIL, vad_info.param);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IFLY_SOCKET_EVT_EXIT:
|
||||
vad_info.status = IFLY_VAD_STATUS_EXIT;
|
||||
vad_info.param->event_cb(IFLY_VAD_EVT_EXIT, vad_info.param);
|
||||
if (vad_socket.auth) {
|
||||
net_iflytek_free(vad_socket.auth);
|
||||
vad_socket.auth = NULL;
|
||||
}
|
||||
if (vad_info.pcm_out_buf) {
|
||||
net_iflytek_free(vad_info.pcm_out_buf);
|
||||
vad_info.pcm_out_buf = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ifly_vad_start(ifly_vad_param *param)
|
||||
{
|
||||
memset(&vad_info, 0, sizeof(struct vad_info_t));
|
||||
memset(&vad_socket, 0, sizeof(struct ifly_websocket_struct));
|
||||
|
||||
#if TCFG_IFLYTEK_USE_PSRAM
|
||||
cJSON_Hooks hooks;
|
||||
hooks.malloc_fn = net_iflytek_malloc;
|
||||
hooks.free_fn = net_iflytek_free;
|
||||
cJSON_InitHooks(&hooks);
|
||||
#endif
|
||||
|
||||
vad_info.pcm_out_buf = net_iflytek_malloc(PCM_OUT_BUF_LEN);
|
||||
ASSERT(vad_info.pcm_out_buf);
|
||||
cbuf_init(&vad_info.pcm_cbuf, vad_info.pcm_out_buf, PCM_OUT_BUF_LEN);
|
||||
|
||||
vad_info.param = param;
|
||||
vad_socket.auth = (u8 *)ifly_authentication("wss://iat-api.xfyun.cn/v2/iat",
|
||||
"iat-api.xfyun.cn",
|
||||
"GET /v2/iat HTTP/1.1", 20);
|
||||
if (!vad_socket.auth) {
|
||||
net_iflytek_free(vad_info.pcm_out_buf);
|
||||
vad_info.pcm_out_buf = NULL;
|
||||
vad_info.param->event_cb(IFLY_VAD_EVT_NETWORK_FAIL, vad_info.param);
|
||||
return false;
|
||||
}
|
||||
vad_socket.task_name = "ifly_vad";
|
||||
vad_socket.socket_mode = WEBSOCKET_MODE;
|
||||
vad_socket.recv_cb = ifly_vad_recv_cb;
|
||||
vad_socket.get_send = ifly_vad_get_send;
|
||||
vad_socket.event_cb = ifly_vad_event_cb;
|
||||
|
||||
vad_info.status = IFLY_VAD_STATUS_START;
|
||||
|
||||
//创建链接
|
||||
bool ret = ifly_websocket_client_create(&vad_socket);
|
||||
if (ret == false) {
|
||||
vad_info.status = IFLY_VAD_STATUS_NULL;
|
||||
net_iflytek_free(vad_socket.auth);
|
||||
vad_socket.auth = NULL;
|
||||
net_iflytek_free(vad_info.pcm_out_buf);
|
||||
vad_info.pcm_out_buf = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ifly_vad_stop(u8 force_stop, u32 to_ms)
|
||||
{
|
||||
log_info("vad stop!\n");
|
||||
ifly_vad_audio_stop();
|
||||
vad_info.force_stop = force_stop;
|
||||
while (vad_socket.auth) { // 结束时auth会自动释放
|
||||
os_time_dly(1);
|
||||
if (to_ms <= 10) {
|
||||
break;
|
||||
}
|
||||
to_ms -= 10;
|
||||
}
|
||||
if (to_ms < 1000) {
|
||||
to_ms = 1000;
|
||||
}
|
||||
vad_info.force_stop = 1;
|
||||
ifly_websocket_client_release(&vad_socket, to_ms);
|
||||
}
|
||||
|
||||
void ifly_vad_audio_stop(void)
|
||||
{
|
||||
vad_info.frame_status = STATUS_LAST_FRAME; // 停止语音发送
|
||||
}
|
||||
|
||||
bool ifly_vad_is_work()
|
||||
{
|
||||
if ((vad_info.status != IFLY_VAD_STATUS_NULL) && (vad_info.status != IFLY_VAD_STATUS_EXIT)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef __VAD_MAIN_H
|
||||
#define __VAD_MAIN_H
|
||||
|
||||
#include "generic/includes.h"
|
||||
|
||||
#define MAX_VAD_LEN 1000
|
||||
|
||||
typedef enum {
|
||||
IFLY_VAD_EVT_AUDIO_START = 1, // 音频启动。*param: ifly_vad_param*
|
||||
IFLY_VAD_EVT_RECV_OK, // VAD接受数据完毕。*param: ifly_vad_param*
|
||||
IFLY_VAD_EVT_NETWORK_FAIL, // 网络错误。*param: ifly_ai_param*
|
||||
IFLY_VAD_EVT_NETWORK_RECV_ERROR, // 网络接收错误。*param: ifly_ai_param*
|
||||
IFLY_VAD_EVT_EXIT, // 结束。*param: ifly_ai_param*
|
||||
} ifly_vad_event_enum ;
|
||||
|
||||
typedef struct ifly_vad_struct {
|
||||
// 参数信息,所有参数都需要赋值
|
||||
char *vad_res; // 输出数据
|
||||
u32 vad_res_len; // 输出数据buf长度,最大MAX_VAD_LEN
|
||||
int (*event_cb)(ifly_vad_event_enum evt, void *param); // 事件回调
|
||||
} ifly_vad_param;
|
||||
|
||||
|
||||
// VAD启动。*param参数句柄需要在stop之后才能释放
|
||||
bool ifly_vad_start(ifly_vad_param *param);
|
||||
// VAD结束。
|
||||
void ifly_vad_stop(u8 force_stop, u32 to_ms);
|
||||
|
||||
// VAD停止发送语音。
|
||||
void ifly_vad_audio_stop(void);
|
||||
|
||||
// 判断vad是否正在运行
|
||||
bool ifly_vad_is_work(void);
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".intelligent_duer_auth.data.bss")
|
||||
#pragma data_seg(".intelligent_duer_auth.data")
|
||||
#pragma const_seg(".intelligent_duer_auth.text.const")
|
||||
#pragma code_seg(".intelligent_duer_auth.text")
|
||||
#endif
|
||||
|
||||
#include "duer_auth_algorithm.h"
|
||||
#if INTELLIGENT_DUER
|
||||
|
||||
#define LOG_TAG_CONST NET_DUER
|
||||
#define LOG_TAG "[DUER_AUTH]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
|
||||
#define CST_OFFSET_SECONDS (28800) // 北京时间时差(秒)
|
||||
#define UUID_BYTE_LENGTH (16) // UUID字节数
|
||||
#define STRIPPED_UUID_LEN (32) // 无横杠UUID长度
|
||||
#define RANDOM_PART_LEN (8) // 随机部分长度
|
||||
#define CHARSET_SIZE (62) // 字符集大小
|
||||
#define UUID_BUFFER_SIZE (33) // UUID缓冲区大小
|
||||
#define REQUEST_ID_BUFFER_SIZE (60) // 请求ID缓冲区大小
|
||||
#define RANDOM_STRING_LENGTH (6) // 随机字符串长度
|
||||
|
||||
// 生成随机字节函数
|
||||
static void duer_get_random_bytes(unsigned char *buf, int nbytes)
|
||||
{
|
||||
while (nbytes--) {
|
||||
*buf = random32(0);
|
||||
++buf;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成UUID字符串函数
|
||||
static void generate_uuid_string_without_hyphens(char *uuid_buffer)
|
||||
{
|
||||
unsigned char rand_bytes[UUID_BYTE_LENGTH];
|
||||
duer_get_random_bytes(rand_bytes, UUID_BYTE_LENGTH);
|
||||
|
||||
snprintf(uuid_buffer, UUID_BUFFER_SIZE,
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x"
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
|
||||
rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7],
|
||||
rand_bytes[8], rand_bytes[9], rand_bytes[10], rand_bytes[11],
|
||||
rand_bytes[12], rand_bytes[13], rand_bytes[14], rand_bytes[15]);
|
||||
}
|
||||
|
||||
// 生成对话请求ID函数
|
||||
void duer_generate_dialog_request_id(char *request_id)
|
||||
{
|
||||
struct sys_time curtime;
|
||||
net_get_sys_time(&curtime);
|
||||
|
||||
// 时间参数日志
|
||||
log_info("Current Time Parameters:");
|
||||
log_info("Year: %d", curtime.year);
|
||||
log_info("Month: %d", curtime.month);
|
||||
log_info("Day: %d", curtime.day);
|
||||
log_info("Hour: %d", curtime.hour);
|
||||
log_info("Minute:%d", curtime.min);
|
||||
log_info("Second:%d", curtime.sec);
|
||||
|
||||
// 时间戳计算
|
||||
long long utc_seconds = timestamp_mytime_2_utc_sec(&curtime) - CST_OFFSET_SECONDS;
|
||||
long long milliseconds = utc_seconds * 1000; // 转换为毫秒
|
||||
log_info(">>>info: %s %d %s utc_s %lld utc_ms %lld \n", __FUNCTION__, __LINE__, __FILE__, utc_seconds, milliseconds);
|
||||
char stripped_uuid[STRIPPED_UUID_LEN + 1] = {0};
|
||||
generate_uuid_string_without_hyphens(stripped_uuid);
|
||||
|
||||
char random_part[RANDOM_PART_LEN + 1] = {0};
|
||||
strncpy(random_part, stripped_uuid, RANDOM_PART_LEN);
|
||||
|
||||
snprintf(request_id, REQUEST_ID_BUFFER_SIZE, "%lld_%s", milliseconds, random_part);
|
||||
}
|
||||
|
||||
// 生成随机字符串函数
|
||||
void duer_generate_random_string(char *output, int length)
|
||||
{
|
||||
const char charset[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
const int charset_size = sizeof(charset) - 1; // 字符集实际大小
|
||||
|
||||
unsigned char rand_bytes[length];
|
||||
duer_get_random_bytes(rand_bytes, length);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
output[i] = charset[rand_bytes[i] % charset_size];
|
||||
}
|
||||
output[length] = '\0'; // 确保字符串终止符
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef _DUER_AUTH_ALGORITHM_H_
|
||||
#define _DUER_AUTH_ALGORITHM_H_
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "sys_time.h"
|
||||
#include "duer_common.h"
|
||||
#include "app_config.h"
|
||||
|
||||
extern unsigned int random32(int type);
|
||||
|
||||
// 生成对话请求ID函数
|
||||
extern void duer_generate_dialog_request_id(char *request_id);
|
||||
|
||||
|
||||
// 生成6个字符的随机字符串
|
||||
extern void duer_generate_random_string(char *output, int length);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef _DUER_COMMON_H_
|
||||
#define _DUER_COMMON_H_
|
||||
|
||||
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "duer_auth_algorithm.h"
|
||||
#include "duer_http_req.h"
|
||||
#include "duer_socket.h"
|
||||
#include "duer_json_parse.h"
|
||||
#include "duer_json_request.h"
|
||||
#include "duer_task.h"
|
||||
#include "board_config.h"
|
||||
#include "cJSON.h"
|
||||
#include "net_includes.h"
|
||||
|
||||
|
||||
#if TCFG_INTELLIGENT_DUER && TCFG_NETAPPLICATION_ENABLE
|
||||
#define INTELLIGENT_DUER TCFG_INTELLIGENT_DUER
|
||||
#else
|
||||
#define INTELLIGENT_DUER 0
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#define DUER_CLIENT_AK "xxxxxx"
|
||||
#define DUER_CLIENT_SK "xxxxxx"
|
||||
#define DUER_APP_PID "xxxxxx"
|
||||
#define DUER_APP_FORMAT "opus"
|
||||
#define DUER_APP_SAMPLE 16000
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".intelligent_duer_network.data.bss")
|
||||
#pragma data_seg(".intelligent_duer_network.data")
|
||||
#pragma const_seg(".intelligent_duer_network.text.const")
|
||||
#pragma code_seg(".intelligent_duer_network.text")
|
||||
#endif
|
||||
#include "duer_common.h"
|
||||
|
||||
#if INTELLIGENT_DUER
|
||||
|
||||
#define LOG_TAG_CONST NET_DUER
|
||||
#define LOG_TAG "[DUER_HTTP_REQ]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
|
||||
static TokenData *duer_get_access_token_child()
|
||||
{
|
||||
char url_buffer[256];
|
||||
snprintf(url_buffer, sizeof(url_buffer),
|
||||
"https://openapi.baidu.com/oauth/2.0/token?"
|
||||
"grant_type=client_credentials&"
|
||||
"client_id=%s&"
|
||||
"client_secret=%s",
|
||||
DUER_CLIENT_AK, DUER_CLIENT_SK);
|
||||
char *response = NULL;
|
||||
net_http_get_request(url_buffer, &response);
|
||||
if (!response) {
|
||||
log_error("API request failed\n");
|
||||
return NULL;
|
||||
}
|
||||
TokenData *token = duer_parse_token_json(response);
|
||||
net_interface_free(response); // 无论成功与否都要释放响应
|
||||
if (!token) {
|
||||
log_error("JSON parsing failed\n");
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
TokenData *duer_get_access_token()
|
||||
{
|
||||
TokenData *token = duer_get_access_token_child();
|
||||
if (!token) {
|
||||
log_error("Failed to get access token\n");
|
||||
return NULL;
|
||||
}
|
||||
log_debug("refresh_token: %s\n", token->refresh_token);
|
||||
log_debug("expires_in: %d\n", token->expires_in);
|
||||
log_debug("session_key: %s\n", token->session_key);
|
||||
log_debug("access_token: %s\n", token->access_token);
|
||||
log_debug("scope: %s\n", token->scope);
|
||||
log_debug("session_secret: %s\n", token->session_secret);
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef _DUER_HTTP_REQ_H_
|
||||
#define _DUER_HTTP_REQ_H_
|
||||
|
||||
|
||||
|
||||
#include "duer_common.h"
|
||||
#include "duer_json_parse.h"
|
||||
#include "time.h"
|
||||
|
||||
|
||||
extern TokenData *duer_get_access_token();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,254 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".intelligent_duer_network.data.bss")
|
||||
#pragma data_seg(".intelligent_duer_network.data")
|
||||
#pragma const_seg(".intelligent_duer_network.text.const")
|
||||
#pragma code_seg(".intelligent_duer_network.text")
|
||||
#endif
|
||||
|
||||
#include "duer_socket.h"
|
||||
#if INTELLIGENT_DUER
|
||||
|
||||
#define LOG_TAG_CONST NET_DUER
|
||||
#define LOG_TAG "[DUER_SOCKET]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#define OBJ_URL "ws://duer-kids.baidu.com/sandbox/sota/realtime_asr?sn=%s"
|
||||
#define CUID_LEN 6
|
||||
#define MAX_URL_LEN 256
|
||||
#define MAX_TOKEN_LEN 256
|
||||
|
||||
static void task_kill_callback(char *buf);
|
||||
static u8 force_kill = 0; //结束标志位
|
||||
|
||||
static void websockets_callback(u8 *buf, u32 len, u8 type)
|
||||
{
|
||||
log_info("wbs recv msg : %s\n", buf);
|
||||
InsideRCResponse *response = duer_parse_inside_rc_json((char *)buf);
|
||||
if (response &&
|
||||
response->data &&
|
||||
response->data->is_end == 1) {
|
||||
force_kill = 1;
|
||||
duer_free_inside_rc_response(response);
|
||||
duer_netdownload_msg();
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Websocket Client api
|
||||
*******************************************************************************/
|
||||
static void websockets_client_reg(struct websocket_struct *websockets_info, char mode)
|
||||
{
|
||||
memset(websockets_info, 0, sizeof(struct websocket_struct));
|
||||
websockets_info->_init = websockets_client_socket_init;
|
||||
websockets_info->_exit = websockets_client_socket_exit;
|
||||
websockets_info->_handshack = webcockets_client_socket_handshack;
|
||||
websockets_info->_send = websockets_client_socket_send;
|
||||
websockets_info->_recv_thread = websockets_client_socket_recv_thread;
|
||||
websockets_info->_heart_thread = websockets_client_socket_heart_thread;
|
||||
websockets_info->_recv_cb = websockets_callback;
|
||||
websockets_info->_recv = NULL;
|
||||
websockets_info->websocket_mode = mode;
|
||||
}
|
||||
|
||||
static int websockets_client_init(struct websocket_struct *websockets_info, u8 *url, const char *origin_str, const char *user_agent_str)
|
||||
{
|
||||
websockets_info->ip_or_url = url;
|
||||
websockets_info->origin_str = origin_str;
|
||||
websockets_info->user_agent_str = user_agent_str;
|
||||
websockets_info->recv_time_out = 1000;
|
||||
|
||||
//应用层和库的版本检测,结构体不一样则返回出错
|
||||
int err = websockets_struct_check(sizeof(struct websocket_struct));
|
||||
if (err == FALSE) {
|
||||
return err;
|
||||
}
|
||||
return websockets_info->_init(websockets_info);
|
||||
}
|
||||
|
||||
static int websockets_client_handshack(struct websocket_struct *websockets_info)
|
||||
{
|
||||
log_info("myurl %s \n", websockets_info->ip_or_url);
|
||||
return websockets_info->_handshack(websockets_info);
|
||||
}
|
||||
|
||||
static int websockets_client_send(struct websocket_struct *websockets_info, u8 *buf, int len, char type)
|
||||
{
|
||||
//SSL加密时一次发送数据不能超过16K,用户需要自己分包
|
||||
return websockets_info->_send(websockets_info, buf, len, type);
|
||||
}
|
||||
|
||||
static void websockets_client_exit(struct websocket_struct *websockets_info)
|
||||
{
|
||||
websockets_info->_exit(websockets_info);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Websocket Client.c
|
||||
* Just one example for test
|
||||
*******************************************************************************/
|
||||
static void websockets_client_main_thread(void *priv)
|
||||
{
|
||||
|
||||
int err = 0;
|
||||
char mode = WEBSOCKET_MODE;
|
||||
char access_token[MAX_TOKEN_LEN] = {0};
|
||||
char cuid_str[CUID_LEN + 1] = {0};
|
||||
char url[MAX_URL_LEN] = {0};
|
||||
duer_generate_random_string(cuid_str, sizeof(cuid_str) - 1);
|
||||
log_debug("Generated CUID: %s", cuid_str);
|
||||
|
||||
if (snprintf(url, sizeof(url), OBJ_URL, cuid_str) >= sizeof(url)) {
|
||||
log_error("URL buffer overflow");
|
||||
return;
|
||||
}
|
||||
|
||||
if (snprintf(access_token, sizeof(access_token), "%s", (char *)priv) >= sizeof(access_token)) {
|
||||
log_error("Token buffer overflow");
|
||||
return;
|
||||
}
|
||||
log_debug("WebSocket URL: %s, Access Token: %s", url, access_token);
|
||||
|
||||
const char *origin_str = "http://coolaf.com";
|
||||
/* 0 . malloc buffer */
|
||||
struct websocket_struct *websockets_info = net_interface_malloc(sizeof(struct websocket_struct));
|
||||
if (!websockets_info) {
|
||||
return;
|
||||
}
|
||||
/* 1 . register */
|
||||
websockets_client_reg(websockets_info, mode);
|
||||
|
||||
/* 2 . init */
|
||||
err = websockets_client_init(websockets_info, (u8 *)url, origin_str, NULL);
|
||||
if (FALSE == err) {
|
||||
log_error(" . ! Cilent websocket init error !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
/* 3 . hanshack */
|
||||
err = websockets_client_handshack(websockets_info);
|
||||
if (FALSE == err) {
|
||||
log_error(" . ! Handshake error !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
log_debug(" . Handshake success \r\n");
|
||||
|
||||
/* 4 . CreateThread */
|
||||
err = os_task_create(websockets_info->_heart_thread,
|
||||
websockets_info,
|
||||
19,
|
||||
512,
|
||||
0,
|
||||
"websocket_client_heart");
|
||||
if (err == 0) {
|
||||
websockets_info->ping_thread_id = 1;
|
||||
} else {
|
||||
|
||||
websockets_info->ping_thread_id = 0;
|
||||
}
|
||||
err = os_task_create(websockets_info->_recv_thread,
|
||||
websockets_info,
|
||||
18,
|
||||
512,
|
||||
0,
|
||||
"websocket_client_recv");
|
||||
if (err == 0) {
|
||||
websockets_info->recv_thread_id = 1;
|
||||
} else {
|
||||
|
||||
websockets_info->recv_thread_id = 0;
|
||||
}
|
||||
os_time_dly(100);
|
||||
|
||||
//server_log_id
|
||||
char dialog_id[60];
|
||||
duer_generate_dialog_request_id(dialog_id);// 生成对话请求ID
|
||||
log_debug("Generated Dialog Request ID: %s\n", dialog_id);
|
||||
|
||||
//start_frame
|
||||
char *start_str = duer_start_frame(DUER_CLIENT_AK, DUER_CLIENT_SK, DUER_CLIENT_AK, DUER_APP_PID,
|
||||
cuid_str, DUER_APP_FORMAT, 1, DUER_APP_SAMPLE,
|
||||
cuid_str, access_token, 1,
|
||||
1, dialog_id, 1,
|
||||
"1");
|
||||
log_debug(">>>start_str %s \n", start_str);
|
||||
err = websockets_client_send(websockets_info, (u8 *)start_str, strlen(start_str), WCT_TXTDATA);
|
||||
if (FALSE == err) {
|
||||
log_debug(" . ! send err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
os_time_dly(40);
|
||||
/* char *finish = build_finish_frame(); */
|
||||
net_rec_start();
|
||||
u8 send_buf[40];
|
||||
u32 buf_size = sizeof(send_buf);
|
||||
while (1) {
|
||||
int available = net_rec_data_len();
|
||||
if (available == 0) {
|
||||
os_time_dly(1);
|
||||
continue;
|
||||
}
|
||||
int bytes_read = net_rec_read_data(send_buf, buf_size);
|
||||
if (bytes_read == 0) {
|
||||
os_time_dly(1);
|
||||
continue;
|
||||
}
|
||||
printf("Read %d bytes from cbuf\n", bytes_read);
|
||||
err = websockets_client_send(websockets_info, send_buf, bytes_read, WCT_BINDATA);
|
||||
if (false == err) {
|
||||
log_error(" . ! send err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
if (force_kill) {
|
||||
goto exit_ws;
|
||||
}
|
||||
os_time_dly(10);
|
||||
}
|
||||
exit_ws:
|
||||
/* 6 . exit */
|
||||
net_record_stop_with_clean();
|
||||
if (websockets_info->ping_thread_id) {
|
||||
websockets_info->ping_kill_flag = 1;
|
||||
}
|
||||
if (websockets_info->recv_thread_id) {
|
||||
websockets_info->recv_kill_flag = 1;
|
||||
}
|
||||
while (websockets_info->recv_kill_flag || websockets_info->ping_kill_flag) {
|
||||
os_time_dly(10);
|
||||
}
|
||||
websockets_client_exit(websockets_info);
|
||||
net_interface_free(websockets_info);
|
||||
net_interface_free(start_str);
|
||||
/* my_free(finish); */
|
||||
int msg[5];
|
||||
msg[0] = (int)task_kill_callback;
|
||||
msg[1] = 1;
|
||||
msg[2] = (int)os_current_task();
|
||||
err = os_taskq_post_type("app_core", Q_CALLBACK, 3, msg);
|
||||
os_time_dly(-1);
|
||||
}
|
||||
|
||||
|
||||
static void task_kill_callback(char *buf)
|
||||
{
|
||||
log_info("[msg]>>>>>>>>>>>*buf=%s", buf);
|
||||
task_kill(buf);
|
||||
}
|
||||
|
||||
void duer_websocket_client_thread_create(void *priv)
|
||||
{
|
||||
net_url_list_init();
|
||||
force_kill = 0;
|
||||
os_task_create(websockets_client_main_thread,
|
||||
priv,
|
||||
15,
|
||||
512 * 5,
|
||||
0,
|
||||
"websockets_client_main");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef _DUER_SOCKET_H_
|
||||
#define _DUER_SOCKET_H_
|
||||
|
||||
|
||||
|
||||
#include "websocket_api.h"
|
||||
#include "duer_common.h"
|
||||
|
||||
|
||||
|
||||
extern void duer_websocket_client_thread_create(void *priv);
|
||||
|
||||
extern int task_kill(const char *name);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,488 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".intelligent_duer_parse.data.bss")
|
||||
#pragma data_seg(".intelligent_duer_parse.data")
|
||||
#pragma const_seg(".intelligent_duer_parse.text.const")
|
||||
#pragma code_seg(".intelligent_duer_parse.text")
|
||||
#endif
|
||||
#include "duer_common.h"
|
||||
|
||||
#if INTELLIGENT_DUER
|
||||
#define LOG_TAG_CONST NET_DUER
|
||||
#define LOG_TAG "[DUER_JSON_PARSE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
TokenData *duer_parse_token_json(const char *json_str)
|
||||
{
|
||||
cJSON *root = cJSON_Parse(json_str);
|
||||
if (!root) {
|
||||
log_error("JSON parse error:\n");
|
||||
return NULL;
|
||||
}
|
||||
TokenData *data = (TokenData *)net_interface_malloc(sizeof(TokenData));
|
||||
if (!data) {
|
||||
log_error("Memory allocation failed");
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
memset(data, 0, sizeof(TokenData));
|
||||
cJSON *item;
|
||||
if ((item = cJSON_GetObjectItem(root, "refresh_token")) && cJSON_IsString(item)) {
|
||||
data->refresh_token = strdup(item->valuestring);
|
||||
}
|
||||
if ((item = cJSON_GetObjectItem(root, "expires_in")) && cJSON_IsNumber(item)) {
|
||||
data->expires_in = item->valueint;
|
||||
}
|
||||
if ((item = cJSON_GetObjectItem(root, "session_key")) && cJSON_IsString(item)) {
|
||||
data->session_key = strdup(item->valuestring);
|
||||
}
|
||||
if ((item = cJSON_GetObjectItem(root, "access_token")) && cJSON_IsString(item)) {
|
||||
data->access_token = strdup(item->valuestring);
|
||||
}
|
||||
if ((item = cJSON_GetObjectItem(root, "scope")) && cJSON_IsString(item)) {
|
||||
data->scope = strdup(item->valuestring);
|
||||
}
|
||||
if ((item = cJSON_GetObjectItem(root, "session_secret")) && cJSON_IsString(item)) {
|
||||
data->session_secret = strdup(item->valuestring);
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
void duer_free_token_data(TokenData *data)
|
||||
{
|
||||
if (data) {
|
||||
net_interface_free(data->refresh_token);
|
||||
net_interface_free(data->session_key);
|
||||
net_interface_free(data->access_token);
|
||||
net_interface_free(data->scope);
|
||||
net_interface_free(data->session_secret);
|
||||
net_interface_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
InsideRCResponse *duer_parse_inside_rc_json(const char *json_string)
|
||||
{
|
||||
InsideRCResponse *response = net_interface_calloc(1, sizeof(InsideRCResponse));
|
||||
if (!response) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *root = cJSON_Parse(json_string);
|
||||
if (!root) {
|
||||
log_error("Error parsing JSON: %s\n", cJSON_GetErrorPtr());
|
||||
net_interface_free(response);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *type = cJSON_GetObjectItem(root, "type");
|
||||
if (cJSON_IsString(type)) {
|
||||
response->type = strdup(type->valuestring);
|
||||
}
|
||||
|
||||
if (!response->type || strcmp(response->type, "inside_rc") != 0) {
|
||||
cJSON_Delete(root);
|
||||
return response;
|
||||
}
|
||||
|
||||
cJSON *status = cJSON_GetObjectItem(root, "status");
|
||||
if (cJSON_IsString(status)) {
|
||||
response->status = strdup(status->valuestring);
|
||||
}
|
||||
|
||||
cJSON *sn = cJSON_GetObjectItem(root, "sn");
|
||||
if (cJSON_IsString(sn)) {
|
||||
response->sn = strdup(sn->valuestring);
|
||||
}
|
||||
|
||||
cJSON *end = cJSON_GetObjectItem(root, "end");
|
||||
if (cJSON_IsNumber(end)) {
|
||||
response->end = end->valueint;
|
||||
}
|
||||
|
||||
cJSON *data_obj = cJSON_GetObjectItem(root, "data");
|
||||
if (data_obj && cJSON_IsObject(data_obj)) {
|
||||
response->data = net_interface_calloc(1, sizeof(Data));
|
||||
if (!response->data) {
|
||||
cJSON_Delete(root);
|
||||
duer_free_inside_rc_response(response);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *code = cJSON_GetObjectItem(data_obj, "code");
|
||||
if (cJSON_IsNumber(code)) {
|
||||
response->data->code = code->valueint;
|
||||
}
|
||||
|
||||
cJSON *msg = cJSON_GetObjectItem(data_obj, "msg");
|
||||
if (cJSON_IsString(msg)) {
|
||||
response->data->msg = strdup(msg->valuestring);
|
||||
}
|
||||
|
||||
cJSON *logid = cJSON_GetObjectItem(data_obj, "logid");
|
||||
if (cJSON_IsString(logid)) {
|
||||
response->data->logid = strdup(logid->valuestring);
|
||||
}
|
||||
|
||||
cJSON *qid = cJSON_GetObjectItem(data_obj, "qid");
|
||||
if (cJSON_IsString(qid)) {
|
||||
response->data->qid = strdup(qid->valuestring);
|
||||
}
|
||||
|
||||
cJSON *is_end = cJSON_GetObjectItem(data_obj, "is_end");
|
||||
if (cJSON_IsNumber(is_end)) {
|
||||
response->data->is_end = is_end->valueint;
|
||||
}
|
||||
|
||||
cJSON *need_clear_history = cJSON_GetObjectItem(data_obj, "need_clear_history");
|
||||
if (cJSON_IsNumber(need_clear_history)) {
|
||||
response->data->need_clear_history = need_clear_history->valueint;
|
||||
}
|
||||
|
||||
cJSON *assistant_answer = cJSON_GetObjectItem(data_obj, "assistant_answer");
|
||||
if (assistant_answer) {
|
||||
response->data->assistant_answer = net_interface_calloc(1, sizeof(AssistantAnswer));
|
||||
if (response->data->assistant_answer) {
|
||||
if (cJSON_IsString(assistant_answer)) {
|
||||
cJSON *aa_root = cJSON_Parse(assistant_answer->valuestring);
|
||||
if (aa_root) {
|
||||
cJSON *content = cJSON_GetObjectItem(aa_root, "content");
|
||||
if (cJSON_IsString(content)) {
|
||||
response->data->assistant_answer->content = strdup(content->valuestring);
|
||||
}
|
||||
|
||||
cJSON *nlu = cJSON_GetObjectItem(aa_root, "nlu");
|
||||
if (cJSON_IsString(nlu)) {
|
||||
response->data->assistant_answer->nlu = strdup(nlu->valuestring);
|
||||
}
|
||||
|
||||
cJSON *aa_is_end = cJSON_GetObjectItem(aa_root, "is_end");
|
||||
if (cJSON_IsNumber(aa_is_end)) {
|
||||
response->data->assistant_answer->is_end = aa_is_end->valueint;
|
||||
}
|
||||
|
||||
cJSON *metadata = cJSON_GetObjectItem(aa_root, "metadata");
|
||||
if (metadata && cJSON_IsObject(metadata)) {
|
||||
response->data->assistant_answer->metadata = net_interface_calloc(1, sizeof(Metadata));
|
||||
if (response->data->assistant_answer->metadata) {
|
||||
cJSON *multi_round_info = cJSON_GetObjectItem(metadata, "multi_round_info");
|
||||
if (multi_round_info && cJSON_IsObject(multi_round_info)) {
|
||||
response->data->assistant_answer->metadata->multi_round_info = net_interface_calloc(1, sizeof(MultiRoundInfo));
|
||||
if (response->data->assistant_answer->metadata->multi_round_info) {
|
||||
cJSON *is_in_multi = cJSON_GetObjectItem(multi_round_info, "is_in_multi");
|
||||
if (cJSON_IsBool(is_in_multi)) {
|
||||
response->data->assistant_answer->metadata->multi_round_info->is_in_multi = is_in_multi->valueint;
|
||||
}
|
||||
|
||||
cJSON *target_bot_id = cJSON_GetObjectItem(multi_round_info, "target_bot_id");
|
||||
if (cJSON_IsString(target_bot_id)) {
|
||||
response->data->assistant_answer->metadata->multi_round_info->target_bot_id = strdup(target_bot_id->valuestring);
|
||||
}
|
||||
|
||||
cJSON *intent = cJSON_GetObjectItem(multi_round_info, "intent");
|
||||
if (cJSON_IsString(intent)) {
|
||||
response->data->assistant_answer->metadata->multi_round_info->intent = strdup(intent->valuestring);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(aa_root);
|
||||
} else {
|
||||
response->data->assistant_answer->content = strdup(assistant_answer->valuestring);
|
||||
}
|
||||
} else if (cJSON_IsObject(assistant_answer)) {
|
||||
cJSON *content = cJSON_GetObjectItem(assistant_answer, "content");
|
||||
if (cJSON_IsString(content)) {
|
||||
response->data->assistant_answer->content = strdup(content->valuestring);
|
||||
}
|
||||
|
||||
cJSON *nlu = cJSON_GetObjectItem(assistant_answer, "nlu");
|
||||
if (cJSON_IsString(nlu)) {
|
||||
response->data->assistant_answer->nlu = strdup(nlu->valuestring);
|
||||
}
|
||||
|
||||
cJSON *aa_is_end = cJSON_GetObjectItem(assistant_answer, "is_end");
|
||||
if (cJSON_IsNumber(aa_is_end)) {
|
||||
response->data->assistant_answer->is_end = aa_is_end->valueint;
|
||||
}
|
||||
|
||||
cJSON *metadata = cJSON_GetObjectItem(assistant_answer, "metadata");
|
||||
if (metadata && cJSON_IsObject(metadata)) {
|
||||
response->data->assistant_answer->metadata = net_interface_calloc(1, sizeof(Metadata));
|
||||
if (response->data->assistant_answer->metadata) {
|
||||
cJSON *multi_round_info = cJSON_GetObjectItem(metadata, "multi_round_info");
|
||||
if (multi_round_info && cJSON_IsObject(multi_round_info)) {
|
||||
response->data->assistant_answer->metadata->multi_round_info = net_interface_calloc(1, sizeof(MultiRoundInfo));
|
||||
if (response->data->assistant_answer->metadata->multi_round_info) {
|
||||
cJSON *is_in_multi = cJSON_GetObjectItem(multi_round_info, "is_in_multi");
|
||||
if (cJSON_IsBool(is_in_multi)) {
|
||||
response->data->assistant_answer->metadata->multi_round_info->is_in_multi = is_in_multi->valueint;
|
||||
}
|
||||
|
||||
cJSON *target_bot_id = cJSON_GetObjectItem(multi_round_info, "target_bot_id");
|
||||
if (cJSON_IsString(target_bot_id)) {
|
||||
response->data->assistant_answer->metadata->multi_round_info->target_bot_id = strdup(target_bot_id->valuestring);
|
||||
}
|
||||
|
||||
cJSON *intent = cJSON_GetObjectItem(multi_round_info, "intent");
|
||||
if (cJSON_IsString(intent)) {
|
||||
response->data->assistant_answer->metadata->multi_round_info->intent = strdup(intent->valuestring);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *data_array = cJSON_GetObjectItem(data_obj, "data");
|
||||
if (data_array && cJSON_IsArray(data_array)) {
|
||||
int array_size = cJSON_GetArraySize(data_array);
|
||||
response->data->data_items_count = array_size;
|
||||
|
||||
if (array_size > 0) {
|
||||
response->data->data_items = net_interface_calloc(array_size, sizeof(DataItem *));
|
||||
if (!response->data->data_items) {
|
||||
cJSON_Delete(root);
|
||||
duer_free_inside_rc_response(response);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < array_size; i++) {
|
||||
DataItem *item = net_interface_calloc(1, sizeof(DataItem));
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
response->data->data_items[i] = item;
|
||||
|
||||
cJSON *array_item = cJSON_GetArrayItem(data_array, i);
|
||||
|
||||
cJSON *header = cJSON_GetObjectItem(array_item, "header");
|
||||
if (header && cJSON_IsObject(header)) {
|
||||
item->header = net_interface_calloc(1, sizeof(Header));
|
||||
if (item->header) {
|
||||
cJSON *namespace = cJSON_GetObjectItem(header, "namespace");
|
||||
if (cJSON_IsString(namespace)) {
|
||||
item->header->namespace = strdup(namespace->valuestring);
|
||||
}
|
||||
|
||||
cJSON *name = cJSON_GetObjectItem(header, "name");
|
||||
if (cJSON_IsString(name)) {
|
||||
item->header->name = strdup(name->valuestring);
|
||||
}
|
||||
|
||||
cJSON *messageId = cJSON_GetObjectItem(header, "messageId");
|
||||
if (cJSON_IsString(messageId)) {
|
||||
item->header->messageId = strdup(messageId->valuestring);
|
||||
}
|
||||
|
||||
cJSON *dialogRequestId = cJSON_GetObjectItem(header, "dialogRequestId");
|
||||
if (cJSON_IsString(dialogRequestId)) {
|
||||
item->header->dialogRequestId = strdup(dialogRequestId->valuestring);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *payload = cJSON_GetObjectItem(array_item, "payload");
|
||||
if (payload && cJSON_IsObject(payload)) {
|
||||
item->payload = net_interface_calloc(1, sizeof(Payload));
|
||||
if (item->payload) {
|
||||
cJSON *rolling = cJSON_GetObjectItem(payload, "rolling");
|
||||
if (cJSON_IsBool(rolling)) {
|
||||
item->payload->rolling = rolling->valueint;
|
||||
}
|
||||
|
||||
cJSON *text = cJSON_GetObjectItem(payload, "text");
|
||||
if (cJSON_IsString(text)) {
|
||||
item->payload->text = strdup(text->valuestring);
|
||||
}
|
||||
|
||||
cJSON *content = cJSON_GetObjectItem(payload, "content");
|
||||
if (cJSON_IsString(content)) {
|
||||
item->payload->content = strdup(content->valuestring);
|
||||
}
|
||||
|
||||
cJSON *format = cJSON_GetObjectItem(payload, "format");
|
||||
if (cJSON_IsString(format)) {
|
||||
item->payload->format = strdup(format->valuestring);
|
||||
}
|
||||
|
||||
cJSON *token = cJSON_GetObjectItem(payload, "token");
|
||||
if (cJSON_IsString(token)) {
|
||||
item->payload->token = strdup(token->valuestring);
|
||||
}
|
||||
|
||||
cJSON *url = cJSON_GetObjectItem(payload, "url");
|
||||
if (cJSON_IsString(url)) {
|
||||
item->payload->url = strdup(url->valuestring);
|
||||
net_url_set(item->payload->url);
|
||||
net_print_urls();
|
||||
}
|
||||
|
||||
cJSON *type = cJSON_GetObjectItem(payload, "type");
|
||||
if (cJSON_IsString(type)) {
|
||||
item->payload->type = strdup(type->valuestring);
|
||||
}
|
||||
|
||||
cJSON *answer = cJSON_GetObjectItem(payload, "answer");
|
||||
if (cJSON_IsString(answer)) {
|
||||
item->payload->answer = strdup(answer->valuestring);
|
||||
}
|
||||
|
||||
cJSON *id = cJSON_GetObjectItem(payload, "id");
|
||||
if (cJSON_IsString(id)) {
|
||||
item->payload->id = strdup(id->valuestring);
|
||||
}
|
||||
|
||||
cJSON *index = cJSON_GetObjectItem(payload, "index");
|
||||
if (cJSON_IsNumber(index)) {
|
||||
item->payload->index = index->valueint;
|
||||
}
|
||||
|
||||
cJSON *payload_is_end = cJSON_GetObjectItem(payload, "is_end");
|
||||
if (cJSON_IsNumber(payload_is_end)) {
|
||||
item->payload->payload_is_end = payload_is_end->valueint;
|
||||
}
|
||||
|
||||
cJSON *part = cJSON_GetObjectItem(payload, "part");
|
||||
if (cJSON_IsString(part)) {
|
||||
item->payload->part = strdup(part->valuestring);
|
||||
}
|
||||
|
||||
cJSON *reasoning_part = cJSON_GetObjectItem(payload, "reasoning_part");
|
||||
if (cJSON_IsString(reasoning_part)) {
|
||||
item->payload->reasoning_part = strdup(reasoning_part->valuestring);
|
||||
}
|
||||
|
||||
cJSON *tts = cJSON_GetObjectItem(payload, "tts");
|
||||
if (cJSON_IsString(tts)) {
|
||||
item->payload->tts = strdup(tts->valuestring);
|
||||
}
|
||||
|
||||
cJSON *timeoutInMilliseconds = cJSON_GetObjectItem(payload, "timeoutInMilliseconds");
|
||||
if (cJSON_IsNumber(timeoutInMilliseconds)) {
|
||||
item->payload->timeoutInMilliseconds = timeoutInMilliseconds->valueint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *property = cJSON_GetObjectItem(array_item, "property");
|
||||
if (property && cJSON_IsObject(property)) {
|
||||
cJSON *serviceCategory = cJSON_GetObjectItem(property, "serviceCategory");
|
||||
if (cJSON_IsString(serviceCategory)) {
|
||||
item->serviceCategory = strdup(serviceCategory->valuestring);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *lj_thread_id = cJSON_GetObjectItem(data_obj, "lj_thread_id");
|
||||
if (cJSON_IsString(lj_thread_id)) {
|
||||
response->data->lj_thread_id = strdup(lj_thread_id->valuestring);
|
||||
}
|
||||
|
||||
cJSON *ab_conversation_id = cJSON_GetObjectItem(data_obj, "ab_conversation_id");
|
||||
if (cJSON_IsString(ab_conversation_id)) {
|
||||
response->data->ab_conversation_id = strdup(ab_conversation_id->valuestring);
|
||||
}
|
||||
|
||||
cJSON *xiaoice_session_id = cJSON_GetObjectItem(data_obj, "xiaoice_session_id");
|
||||
if (cJSON_IsString(xiaoice_session_id)) {
|
||||
response->data->xiaoice_session_id = strdup(xiaoice_session_id->valuestring);
|
||||
}
|
||||
|
||||
cJSON *dialog_request_id = cJSON_GetObjectItem(data_obj, "dialog_request_id");
|
||||
if (cJSON_IsString(dialog_request_id)) {
|
||||
response->data->dialog_request_id = strdup(dialog_request_id->valuestring);
|
||||
}
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
return response;
|
||||
}
|
||||
|
||||
void duer_free_inside_rc_response(InsideRCResponse *response)
|
||||
{
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
|
||||
net_interface_free(response->status);
|
||||
net_interface_free(response->type);
|
||||
net_interface_free(response->sn);
|
||||
|
||||
if (response->data) {
|
||||
net_interface_free(response->data->msg);
|
||||
net_interface_free(response->data->logid);
|
||||
net_interface_free(response->data->qid);
|
||||
net_interface_free(response->data->lj_thread_id);
|
||||
net_interface_free(response->data->ab_conversation_id);
|
||||
net_interface_free(response->data->xiaoice_session_id);
|
||||
net_interface_free(response->data->dialog_request_id);
|
||||
|
||||
if (response->data->assistant_answer) {
|
||||
net_interface_free(response->data->assistant_answer->content);
|
||||
net_interface_free(response->data->assistant_answer->nlu);
|
||||
|
||||
if (response->data->assistant_answer->metadata) {
|
||||
if (response->data->assistant_answer->metadata->multi_round_info) {
|
||||
net_interface_free(response->data->assistant_answer->metadata->multi_round_info->target_bot_id);
|
||||
net_interface_free(response->data->assistant_answer->metadata->multi_round_info->intent);
|
||||
net_interface_free(response->data->assistant_answer->metadata->multi_round_info);
|
||||
}
|
||||
net_interface_free(response->data->assistant_answer->metadata);
|
||||
}
|
||||
net_interface_free(response->data->assistant_answer);
|
||||
}
|
||||
|
||||
if (response->data->data_items) {
|
||||
for (int i = 0; i < response->data->data_items_count; i++) {
|
||||
DataItem *item = response->data->data_items[i];
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->header) {
|
||||
net_interface_free(item->header->namespace);
|
||||
net_interface_free(item->header->name);
|
||||
net_interface_free(item->header->messageId);
|
||||
net_interface_free(item->header->dialogRequestId);
|
||||
net_interface_free(item->header);
|
||||
}
|
||||
|
||||
if (item->payload) {
|
||||
net_interface_free(item->payload->text);
|
||||
net_interface_free(item->payload->content);
|
||||
net_interface_free(item->payload->format);
|
||||
net_interface_free(item->payload->token);
|
||||
net_interface_free(item->payload->url);
|
||||
net_interface_free(item->payload->type);
|
||||
net_interface_free(item->payload->answer);
|
||||
net_interface_free(item->payload->id);
|
||||
net_interface_free(item->payload->part);
|
||||
net_interface_free(item->payload->reasoning_part);
|
||||
net_interface_free(item->payload->tts);
|
||||
net_interface_free(item->payload);
|
||||
}
|
||||
|
||||
net_interface_free(item->serviceCategory);
|
||||
net_interface_free(item);
|
||||
}
|
||||
net_interface_free(response->data->data_items);
|
||||
}
|
||||
|
||||
net_interface_free(response->data);
|
||||
}
|
||||
|
||||
net_interface_free(response);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
#ifndef _DUER_JSON_PARSE_H_
|
||||
#define _DUER_JSON_PARSE_H_
|
||||
|
||||
|
||||
#include "duer_common.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
char *refresh_token;
|
||||
int expires_in;
|
||||
char *session_key;
|
||||
char *access_token;
|
||||
char *scope;
|
||||
char *session_secret;
|
||||
} TokenData;
|
||||
|
||||
|
||||
extern TokenData *duer_parse_token_json(const char *json_str);
|
||||
|
||||
extern void duer_free_token_data(TokenData *data);
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *namespace;
|
||||
char *name;
|
||||
char *messageId;
|
||||
char *dialogRequestId;
|
||||
} Header;
|
||||
|
||||
typedef struct {
|
||||
char *text;
|
||||
char *content;
|
||||
char *format;
|
||||
char *token;
|
||||
char *url;
|
||||
int rolling;
|
||||
char *type;
|
||||
char *answer;
|
||||
char *id;
|
||||
int index;
|
||||
int payload_is_end;
|
||||
char *part;
|
||||
char *reasoning_part;
|
||||
char *tts;
|
||||
int timeoutInMilliseconds;
|
||||
} Payload;
|
||||
|
||||
typedef struct {
|
||||
Header *header;
|
||||
Payload *payload;
|
||||
char *serviceCategory;
|
||||
} DataItem;
|
||||
|
||||
typedef struct {
|
||||
int is_in_multi;
|
||||
char *target_bot_id;
|
||||
char *intent;
|
||||
} MultiRoundInfo;
|
||||
|
||||
typedef struct {
|
||||
MultiRoundInfo *multi_round_info;
|
||||
} Metadata;
|
||||
|
||||
typedef struct {
|
||||
char *content;
|
||||
char *nlu;
|
||||
Metadata *metadata;
|
||||
int is_end;
|
||||
} AssistantAnswer;
|
||||
|
||||
typedef struct {
|
||||
int code;
|
||||
char *msg;
|
||||
char *logid;
|
||||
char *qid;
|
||||
int is_end;
|
||||
AssistantAnswer *assistant_answer;
|
||||
int need_clear_history;
|
||||
DataItem **data_items;
|
||||
int data_items_count;
|
||||
char *lj_thread_id;
|
||||
char *ab_conversation_id;
|
||||
char *xiaoice_session_id;
|
||||
char *dialog_request_id;
|
||||
} Data;
|
||||
|
||||
typedef struct {
|
||||
char *status;
|
||||
char *type;
|
||||
Data *data;
|
||||
char *sn;
|
||||
int end;
|
||||
} InsideRCResponse;
|
||||
|
||||
|
||||
extern InsideRCResponse *duer_parse_inside_rc_json(const char *json_string);
|
||||
|
||||
extern void duer_free_inside_rc_response(InsideRCResponse *response);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".intelligent_duer_parse.data.bss")
|
||||
#pragma data_seg(".intelligent_duer_parse.data")
|
||||
#pragma const_seg(".intelligent_duer_parse.text.const")
|
||||
#pragma code_seg(".intelligent_duer_parse.text")
|
||||
#endif
|
||||
#include "duer_common.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
#if INTELLIGENT_DUER
|
||||
|
||||
char *duer_start_frame(const char *appid, const char *appkey, const char *client_id, const char *pid,
|
||||
const char *cuid, const char *format, int support_dcs, int sample,
|
||||
const char *user_id, const char *access_token, int support_tts,
|
||||
int support_text2dcs, const char *dialog_request_id, int access_rc,
|
||||
const char *rc_version)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
if (root == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(root, "type", cJSON_CreateString("start"));
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
if (data == NULL) {
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(root, "data", data);
|
||||
cJSON_AddItemToObject(data, "appid", cJSON_CreateString(appid));
|
||||
cJSON_AddItemToObject(data, "appkey", cJSON_CreateString(appkey));
|
||||
cJSON_AddItemToObject(data, "client_id", cJSON_CreateString(client_id));
|
||||
cJSON_AddItemToObject(data, "pid", cJSON_CreateString(pid));
|
||||
cJSON_AddItemToObject(data, "cuid", cJSON_CreateString(cuid));
|
||||
cJSON_AddItemToObject(data, "format", cJSON_CreateString(format));
|
||||
char support_dcs_str[32];
|
||||
snprintf(support_dcs_str, sizeof(support_dcs_str), "%d", support_dcs);
|
||||
cJSON_AddRawToObject(data, "support_dcs", support_dcs_str);
|
||||
char sample_str[32];
|
||||
snprintf(sample_str, sizeof(sample_str), "%d", sample);
|
||||
cJSON_AddRawToObject(data, "sample", sample_str);
|
||||
cJSON_AddItemToObject(data, "user_id", cJSON_CreateString(user_id));
|
||||
cJSON_AddItemToObject(data, "access_token", cJSON_CreateString(access_token));
|
||||
cJSON_AddItemToObject(data, "support_tts", cJSON_CreateBool(support_tts));
|
||||
cJSON_AddItemToObject(data, "support_text2dcs", cJSON_CreateBool(support_text2dcs));
|
||||
cJSON_AddItemToObject(data, "dialog_request_id", cJSON_CreateString(dialog_request_id));
|
||||
cJSON_AddItemToObject(data, "access_rc", cJSON_CreateBool(access_rc));
|
||||
cJSON_AddItemToObject(data, "rc_version", cJSON_CreateString(rc_version));
|
||||
char *json_string = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
return json_string;
|
||||
}
|
||||
|
||||
|
||||
char *build_finish_frame()
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
if (!root) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddStringToObject(root, "type", "finish");
|
||||
char *json_str = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
return json_str;
|
||||
}
|
||||
|
||||
char *duer_text_info_frame(const char *CUID,
|
||||
const char *current_dialog_request_id,
|
||||
const char *text_query)
|
||||
{
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
if (!root) {
|
||||
return NULL;
|
||||
}
|
||||
cJSON *event = cJSON_CreateObject();
|
||||
cJSON *header = cJSON_CreateObject();
|
||||
cJSON *payload = cJSON_CreateObject();
|
||||
cJSON *initiator = cJSON_CreateObject();
|
||||
if (!event || !header || !payload || !initiator) {
|
||||
cJSON_Delete(root);
|
||||
if (event) {
|
||||
cJSON_Delete(event);
|
||||
}
|
||||
if (header) {
|
||||
cJSON_Delete(header);
|
||||
}
|
||||
if (payload) {
|
||||
cJSON_Delete(payload);
|
||||
}
|
||||
if (initiator) {
|
||||
cJSON_Delete(initiator);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddStringToObject(root, "status", "ok");
|
||||
cJSON_AddStringToObject(root, "type", "dcs_decide");
|
||||
cJSON_AddStringToObject(root, "sn", CUID ? CUID : "");
|
||||
cJSON_AddNumberToObject(root, "end", 1);
|
||||
cJSON_AddItemToObject(root, "event", event);
|
||||
cJSON_AddItemToObject(event, "header", header);
|
||||
cJSON_AddItemToObject(event, "payload", payload);
|
||||
cJSON_AddStringToObject(header, "namespace", "ai.dueros.device_interface.screen");
|
||||
cJSON_AddStringToObject(header, "name", "LinkClicked");
|
||||
cJSON_AddStringToObject(header, "dialogRequestId", current_dialog_request_id ? current_dialog_request_id : "");
|
||||
char url[1024];
|
||||
if (text_query) {
|
||||
snprintf(url, sizeof(url), "dueros://server.dueros.ai/query?q=%s", text_query);
|
||||
} else {
|
||||
snprintf(url, sizeof(url), "dueros://server.dueros.ai/query");
|
||||
}
|
||||
cJSON_AddStringToObject(payload, "url", url);
|
||||
cJSON_AddItemToObject(payload, "initiator", initiator);
|
||||
cJSON_AddStringToObject(initiator, "type", "USER_CLICK");
|
||||
char *json_str = cJSON_Print(root);
|
||||
cJSON_Delete(root);
|
||||
return json_str;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef _DUER_JSON_REQUEST_H_
|
||||
#define _DUER_JSON_REQUEST_H_
|
||||
|
||||
#include "duer_common.h"
|
||||
|
||||
|
||||
extern char *duer_start_frame(const char *appid, const char *appkey, const char *client_id, const char *pid,
|
||||
const char *cuid, const char *format, int support_dcs, int sample,
|
||||
const char *user_id, const char *access_token, int support_tts,
|
||||
int support_text2dcs, const char *dialog_request_id, int access_rc,
|
||||
const char *rc_version);
|
||||
|
||||
extern char *build_finish_frame() ;
|
||||
|
||||
|
||||
extern char *duer_text_info_frame(const char *CUID,
|
||||
const char *current_dialog_request_id,
|
||||
const char *text_query);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".intelligent_duer_task.data.bss")
|
||||
#pragma data_seg(".intelligent_duer_task.data")
|
||||
#pragma const_seg(".intelligent_duer_task.text.const")
|
||||
#pragma code_seg(".intelligent_duer_task.text")
|
||||
#endif
|
||||
#include "duer_task.h"
|
||||
#include "duer_http_req.h"
|
||||
#include "os_api.h"
|
||||
|
||||
#if INTELLIGENT_DUER
|
||||
#define LOG_TAG_CONST NET_DUER
|
||||
#define LOG_TAG "[DUER_TASK]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#define INTELLIGENT_DUER_NAME "intelligent_duer"
|
||||
|
||||
static DUER_DATA *duer = NULL;
|
||||
|
||||
static TokenData *token = NULL;
|
||||
|
||||
void ntp_sync_callback(int result, const struct tm *sync_time, const char *msg)
|
||||
{
|
||||
if (result == 0) {
|
||||
char time_str[64];
|
||||
strftime_2(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", sync_time);
|
||||
log_info("NTP同步成功: %s | %s\n", time_str, msg);
|
||||
os_taskq_post(INTELLIGENT_DUER_NAME, 1, DUER_TOKEN_GET); //同步时间成功,下一步鉴权
|
||||
} else {
|
||||
log_info("NTP同步失败 (错误码: %d): %s\n", result, msg);
|
||||
os_taskq_post(INTELLIGENT_DUER_NAME, 1, DUER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static void duer_data_init()
|
||||
{
|
||||
if (duer == NULL) {
|
||||
duer = net_interface_malloc(sizeof(DUER_DATA));
|
||||
}
|
||||
}
|
||||
|
||||
static void duer_data_destory()
|
||||
{
|
||||
if (duer->access_token) {
|
||||
net_interface_free(duer->access_token);
|
||||
}
|
||||
if (duer != NULL) {
|
||||
net_interface_free(duer);
|
||||
}
|
||||
}
|
||||
|
||||
static void duer_ask_access_token()
|
||||
{
|
||||
token = duer_get_access_token();
|
||||
if (token) {
|
||||
size_t len = strlen(token->access_token);
|
||||
duer->access_token = net_interface_malloc(len + 1);
|
||||
if (duer->access_token == NULL) {
|
||||
os_taskq_post(INTELLIGENT_DUER_NAME, 1, DUER_ERROR);
|
||||
}
|
||||
snprintf(duer->access_token, len + 1, "%s", token->access_token);
|
||||
log_info(">>>>>>>>>>duer->access_token %s \n", duer->access_token);
|
||||
duer_free_token_data(token);
|
||||
os_taskq_post(INTELLIGENT_DUER_NAME, 1, DUER_WS_CONNECT);
|
||||
} else {
|
||||
os_taskq_post(INTELLIGENT_DUER_NAME, 1, DUER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static void task_kill_callback()
|
||||
{
|
||||
task_kill(INTELLIGENT_DUER_NAME);
|
||||
}
|
||||
|
||||
static void intelligent_duer_task(void *priv)
|
||||
{
|
||||
int msg[3] = {0};
|
||||
int res = 0;
|
||||
while (1) {
|
||||
res = os_taskq_pend(NULL, msg, ARRAY_SIZE(msg));
|
||||
if (res == OS_TASKQ) {
|
||||
switch (msg[1]) {
|
||||
case DUER_NTP_SYNC:
|
||||
ntp_get_time_to_sync_rtc_with_callback();
|
||||
break;
|
||||
case DUER_TOKEN_GET:
|
||||
duer_ask_access_token();
|
||||
break;
|
||||
case DUER_WS_CONNECT://ws通信、opus数据交互、url数据接收
|
||||
duer_websocket_client_thread_create(duer->access_token);
|
||||
break;
|
||||
case DUER_NETDOWN://URL索引下载,解码播放
|
||||
net_url_download();
|
||||
break;
|
||||
case DUER_ERROR:
|
||||
case DUER_CLOSING:
|
||||
goto err; //error和close都会关闭
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
err:
|
||||
duer_data_destory();
|
||||
msg[0] = (int)task_kill_callback;
|
||||
msg[1] = 1;
|
||||
os_taskq_post_type("app_core", Q_CALLBACK, 2, msg);
|
||||
os_time_dly(-1);
|
||||
}
|
||||
|
||||
void intelligent_duer_task_create()
|
||||
{
|
||||
duer_data_init();
|
||||
os_task_create(intelligent_duer_task,
|
||||
NULL,
|
||||
30,
|
||||
512 * 2,
|
||||
16,
|
||||
INTELLIGENT_DUER_NAME);
|
||||
}
|
||||
|
||||
void duer_sync_time_msg()
|
||||
{
|
||||
os_taskq_post(INTELLIGENT_DUER_NAME, 1, DUER_NTP_SYNC);
|
||||
}
|
||||
|
||||
void duer_netdownload_msg()
|
||||
{
|
||||
os_taskq_post(INTELLIGENT_DUER_NAME, 1, DUER_NETDOWN);
|
||||
}
|
||||
|
||||
void duer_netdownload_close_msg()
|
||||
{
|
||||
os_taskq_post(INTELLIGENT_DUER_NAME, 1, DUER_CLOSING);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef _DUER_TASK
|
||||
#define _DUER_TASK
|
||||
|
||||
#include "duer_common.h"
|
||||
#include "time.h"
|
||||
|
||||
// 状态枚举
|
||||
typedef enum {
|
||||
DUER_NTP_SYNC,
|
||||
DUER_TOKEN_GET,
|
||||
DUER_WS_CONNECT,
|
||||
DUER_NETDOWN,
|
||||
DUER_ERROR,
|
||||
DUER_CLOSING
|
||||
} SystemState;
|
||||
|
||||
// 错误码枚举
|
||||
typedef enum {
|
||||
ERROR_NONE = 0,
|
||||
ERROR_NTP_FAILED,
|
||||
ERROR_HTTP_FAILED,
|
||||
ERROR_TOKEN_INVALID,
|
||||
ERROR_WS_CONNECT_FAILED,
|
||||
ERROR_SERVER_REJECT,
|
||||
ERROR_RECORD_FAILED,
|
||||
ERROR_UPLOAD_FAILED,
|
||||
ERROR_TIMEOUT
|
||||
} ErrorCode;
|
||||
|
||||
typedef struct {
|
||||
char *access_token;
|
||||
u8 run;
|
||||
} DUER_DATA;
|
||||
|
||||
extern void ntp_get_time_to_sync_rtc_with_callback();
|
||||
|
||||
extern int task_kill(const char *name);
|
||||
|
||||
extern void intelligent_duer_task_create();
|
||||
|
||||
extern void duer_sync_time_msg();
|
||||
|
||||
extern void duer_netdownload_msg();
|
||||
|
||||
extern size_t strftime_2(char *ptr, size_t maxsize, const char *format, const struct tm *timeptr);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".intelligent_duer_voice.data.bss")
|
||||
#pragma data_seg(".intelligent_duer_voice.data")
|
||||
#pragma const_seg(".intelligent_duer_voice.text.const")
|
||||
#pragma code_seg(".intelligent_duer_voice.text")
|
||||
#endif
|
||||
#include "duer_ai_rx_get_frame.h"
|
||||
#include "ai_rx_player.h"
|
||||
#include "cpu/includes.h"
|
||||
|
||||
#if INTELLIGENT_DUER
|
||||
|
||||
#define FIX_LEN 1024
|
||||
enum stream_node_state ai_rx_get_frame_duer(struct ai_rx_file_handle *hdl, struct stream_frame **pframe)
|
||||
{
|
||||
int available = net_cbuf_data_len();
|
||||
int read_size = MIN(available, FIX_LEN);
|
||||
if (read_size <= 0) {
|
||||
*pframe = NULL;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
struct stream_frame *frame = jlstream_get_frame(hdl->node->oport, read_size);
|
||||
if (!frame) {
|
||||
*pframe = NULL;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
int rlen = net_cbuf_read_data(frame->data, read_size);
|
||||
frame->len = MAX(rlen, 0);
|
||||
if (rlen != read_size) {
|
||||
printf("cbuf_read mismatch: req=%d, act=%d\n", read_size, rlen);
|
||||
}
|
||||
*pframe = frame;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
|
||||
static void duer_audio_begin(void)
|
||||
{
|
||||
struct ai_rx_player_param param = {0};
|
||||
param.coding_type = AUDIO_CODING_STREAM_MP3;
|
||||
param.sample_rate = 16000;
|
||||
param.channel_mode = AUDIO_CH_MIX;
|
||||
param.type = AI_SERVICE_VOICE;
|
||||
ai_rx_player_open(NULL, 0, ¶m);
|
||||
extern void audio_app_volume_set(u8 state, s16 volume, u8 fade);
|
||||
audio_app_volume_set(1, 100, 1);
|
||||
}
|
||||
|
||||
static void duer_audio_start(void *arg)
|
||||
{
|
||||
duer_audio_begin();
|
||||
}
|
||||
|
||||
void duer_audio_play()
|
||||
{
|
||||
duer_audio_start(NULL);
|
||||
}
|
||||
|
||||
void duer_audio_stop(void *arg)
|
||||
{
|
||||
ai_rx_player_close(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef _DUER_AI_GET_FRAME_H_
|
||||
#define _DUER_AI_GET_FRAME_H_
|
||||
|
||||
#include "duer_common.h"
|
||||
#include "jlstream.h"
|
||||
#include "ai_rx_player.h"
|
||||
|
||||
#if INTELLIGENT_DUER
|
||||
|
||||
extern enum stream_node_state ai_rx_get_frame_duer(struct ai_rx_file_handle *hdl, struct stream_frame **pframe);
|
||||
|
||||
extern void my_duer_audio_play();
|
||||
|
||||
extern void my_duer_audio_stop(void *arg);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".net_interface.data.bss")
|
||||
#pragma data_seg(".net_interface.data")
|
||||
#pragma const_seg(".net_interface.text.const")
|
||||
#pragma code_seg(".net_interface.text")
|
||||
#endif
|
||||
#include "net_http.h"
|
||||
|
||||
#if NET_INTERFACE_EN
|
||||
#define LOG_TAG_CONST NET_INTERFACE
|
||||
#define LOG_TAG "[NET_INTERFACE_HTTP]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
int net_http_get_request(char *url, char **response)
|
||||
{
|
||||
int error = 0;
|
||||
http_body_obj http_body_buf;
|
||||
log_info("wt get url: %s\n", url);
|
||||
httpcli_ctx *ctx = (httpcli_ctx *)net_interface_calloc(1, sizeof(httpcli_ctx));
|
||||
if (!ctx) {
|
||||
log_error("calloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
memset(&http_body_buf, 0x0, sizeof(http_body_obj));
|
||||
http_body_buf.recv_len = 0;
|
||||
http_body_buf.buf_len = 2 * 1024;
|
||||
http_body_buf.buf_count = 1;
|
||||
http_body_buf.p = (char *)net_interface_malloc(http_body_buf.buf_len * http_body_buf.buf_count);
|
||||
if (!http_body_buf.p) {
|
||||
net_interface_free(ctx);
|
||||
ctx = NULL;
|
||||
return -1;
|
||||
}
|
||||
ctx->url = url;
|
||||
ctx->timeout_millsec = 5000;
|
||||
ctx->priv = &http_body_buf;
|
||||
ctx->connection = "close";
|
||||
ctx->data_format = "application/json";
|
||||
error = httpcli_get(ctx);
|
||||
if (error != HERROR_OK) {
|
||||
log_error("get failed\n");
|
||||
} else {
|
||||
if (http_body_buf.recv_len > 0) {
|
||||
log_info("\nreceive %d bytes from(%s)\n", http_body_buf.recv_len, url);
|
||||
log_info("%s\n", http_body_buf.p);
|
||||
if (response != NULL) {
|
||||
*response = net_interface_malloc(http_body_buf.recv_len + 1);
|
||||
if (*response == NULL) {
|
||||
log_error("Memory allocation failed for response\n");
|
||||
error = -1;
|
||||
} else {
|
||||
memcpy(*response, http_body_buf.p, http_body_buf.recv_len);
|
||||
(*response)[http_body_buf.recv_len] = '\0';
|
||||
log_info("Response copied to external pointer, length: %d\n", http_body_buf.recv_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
httpcli_close(ctx);
|
||||
if (http_body_buf.p) {
|
||||
net_interface_free(http_body_buf.p);
|
||||
}
|
||||
if (ctx) {
|
||||
net_interface_free(ctx);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
int net_http_post_request(char *url, char **response)
|
||||
{
|
||||
int ret = 0;
|
||||
http_body_obj http_body_buf;
|
||||
httpcli_ctx *ctx = (httpcli_ctx *)net_interface_calloc(1, sizeof(httpcli_ctx));
|
||||
if (!ctx) {
|
||||
log_error("calloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
memset(&http_body_buf, 0x0, sizeof(http_body_obj));
|
||||
http_body_buf.recv_len = 0;
|
||||
http_body_buf.buf_len = 2 * 1024;
|
||||
http_body_buf.buf_count = 1;
|
||||
http_body_buf.p = (char *)net_interface_malloc(http_body_buf.buf_len * http_body_buf.buf_count);
|
||||
if (!http_body_buf.p) {
|
||||
net_interface_free(ctx);
|
||||
return -1;
|
||||
}
|
||||
ctx->url = url;
|
||||
ctx->timeout_millsec = 5000;
|
||||
ctx->priv = &http_body_buf;
|
||||
ctx->connection = "close";
|
||||
ctx->data_format = "application/json";
|
||||
ret = httpcli_post(ctx);
|
||||
if (ret != HERROR_OK) {
|
||||
log_error("HTTP POST request failed\n");
|
||||
} else {
|
||||
if (http_body_buf.recv_len > 0) {
|
||||
log_info("\nReceived %d Bytes from (%s)\n", http_body_buf.recv_len, url);
|
||||
if (response != NULL) {
|
||||
*response = net_interface_malloc(http_body_buf.recv_len + 1);
|
||||
if (*response == NULL) {
|
||||
log_error("Memory allocation failed for response\n");
|
||||
ret = -1;
|
||||
} else {
|
||||
memcpy(*response, http_body_buf.p, http_body_buf.recv_len);
|
||||
(*response)[http_body_buf.recv_len] = '\0';
|
||||
log_info("Response copied to external pointer, length: %d\n", http_body_buf.recv_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
httpcli_close(ctx);
|
||||
if (http_body_buf.p) {
|
||||
net_interface_free(http_body_buf.p);
|
||||
}
|
||||
if (ctx) {
|
||||
net_interface_free(ctx);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif //NET_INTERFACE_EN
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef _NET_HTTP_H_
|
||||
#define _NET_HTTP_H_
|
||||
|
||||
#include "http/http_cli.h"
|
||||
#include "system/includes.h"
|
||||
#include "net_includes.h"
|
||||
|
||||
/**
|
||||
* @brief 执行HTTP GET请求
|
||||
*
|
||||
* @param[in] url 请求的URL地址
|
||||
* @param[out] response 响应数据指针的地址,需要外部释放内存
|
||||
*
|
||||
* @return int 执行结果
|
||||
* @retval 0 成功
|
||||
* @retval -1 失败
|
||||
*
|
||||
* @note 调用者需要负责释放response指向的内存
|
||||
* @warning URL参数不能为NULL
|
||||
*/
|
||||
extern int net_http_get_request(char *url, char **response);
|
||||
|
||||
/**
|
||||
* @brief 执行HTTP POST请求
|
||||
*
|
||||
* @param[in] url 请求的URL地址
|
||||
* @param[out] response 响应数据指针的地址,需要外部释放内存
|
||||
*
|
||||
* @return int 执行结果
|
||||
* @retval 0 成功
|
||||
* @retval -1 失败
|
||||
*
|
||||
* @note 调用者需要负责释放response指向的内存
|
||||
* @warning URL参数不能为NULL
|
||||
*/
|
||||
extern int net_http_post_request(char *url, char **response);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef _NET_INCLUDES_H_
|
||||
#define _NET_INCLUDES_H_
|
||||
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "board_config.h"
|
||||
#include "net_http.h"
|
||||
#include "net_mem.h"
|
||||
#include "net_time.h"
|
||||
#include "net_url_list.h"
|
||||
#include "net_record.h"
|
||||
#include "net_list_download.h"
|
||||
|
||||
|
||||
#define NET_INTERFACE_EN TCFG_NETAPPLICATION_ENABLE
|
||||
#endif
|
||||
@@ -0,0 +1,219 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".net_interface.data.bss")
|
||||
#pragma data_seg(".net_interface.data")
|
||||
#pragma const_seg(".net_interface.text.const")
|
||||
#pragma code_seg(".net_interface.text")
|
||||
#endif
|
||||
|
||||
#include "net_list_download.h"
|
||||
|
||||
#if NET_INTERFACE_EN
|
||||
|
||||
#define LOG_TAG_CONST NET_INTERFACE
|
||||
#define LOG_TAG "[NET_INTERFACE_LIST_DOWNLOAD]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
static net_buf_t net;
|
||||
/*=========================cbuf========================*/
|
||||
#define NET_RX_BUF_SIZE 1024*10 //网络缓存cbuf大小,可适当调整
|
||||
|
||||
static bool net_cbuf_init(void)
|
||||
{
|
||||
if (!net.buf) {
|
||||
net.buf = net_interface_malloc(NET_RX_BUF_SIZE);
|
||||
if (!net.buf) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
cbuf_init(&net.cbuf, net.buf, NET_RX_BUF_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void net_cbuf_write(void *buf, int len)
|
||||
{
|
||||
int requested_len = len;
|
||||
static u32 w_len;
|
||||
int wlen = cbuf_write(&net.cbuf, buf, requested_len);
|
||||
w_len += wlen;
|
||||
if (wlen != requested_len) {
|
||||
}
|
||||
}
|
||||
|
||||
static void net_cbuf_exit(void)
|
||||
{
|
||||
cbuf_clear(&(net.cbuf));
|
||||
net_interface_free(net.buf);
|
||||
net.buf = NULL;
|
||||
}
|
||||
|
||||
int net_cbuf_data_len()
|
||||
{
|
||||
return cbuf_get_data_len(&(net.cbuf));
|
||||
}
|
||||
|
||||
int net_cbuf_read_data(void *buf, u32 len)
|
||||
{
|
||||
return cbuf_read(&(net.cbuf), buf, len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*=======================================================================*/
|
||||
typedef struct {
|
||||
void *handle; // 传入指针以获取句柄
|
||||
int ret; // 返回值
|
||||
int download_status; // 网络下载状态[net_download.h]
|
||||
int http_err_status; // http链路状态
|
||||
struct net_download_parm parm; // 下载参数配置
|
||||
int file_len; // 获取下载文件大小
|
||||
int bytes_read; // 记每次下载的数据量
|
||||
int total_read; // 记录当前下载数据总量
|
||||
char buf[1024]; // 缓冲数据
|
||||
} my_net_download_variable; // 在download_demo里面所用
|
||||
|
||||
static int g_current_url_index = 0; //记录当前下载的URL索引
|
||||
static int g_total_urls = 0; //记录总共需要下载的URL数量
|
||||
|
||||
|
||||
static void download_demo(const char *url, const char *save_path, int url_index)
|
||||
{
|
||||
if (!url || !save_path) {
|
||||
log_error("错误: 参数为空\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("开始下载第%d个URL: %s\n", url_index + 1, url);
|
||||
|
||||
my_net_download_variable mynet;
|
||||
|
||||
mynet.parm.url = url;
|
||||
mynet.parm.cbuf_size = 10 * 1024; // 10KB 环形缓冲区
|
||||
mynet.parm.timeout_millsec = 1000; // 10秒连接超时
|
||||
mynet.parm.save_file = 1; // 保存到文件
|
||||
mynet.parm.file_dir = save_path; // 保存目录
|
||||
mynet.parm.dir_len = strlen(save_path);
|
||||
mynet.parm.seek_threshold = 1024; // 1024KB跳转阈值
|
||||
|
||||
mynet.ret = net_download_open(&mynet.handle, &mynet.parm);
|
||||
if (mynet.ret != 0) {
|
||||
log_error("net_download_open failed: %d\n", mynet.ret);
|
||||
return;
|
||||
}
|
||||
|
||||
mynet.file_len = net_download_get_file_len(mynet.handle);
|
||||
if (mynet.file_len > 0) {
|
||||
log_error("File length: %d bytes\n", mynet.file_len);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (net_download_exit_flag(mynet.handle)) {
|
||||
log_error("Download exit flag set\n");
|
||||
goto close;
|
||||
}
|
||||
|
||||
net_download_get_status(mynet.handle, &mynet.download_status, &mynet.http_err_status);
|
||||
if (mynet.download_status < 0) {
|
||||
log_info("Download failed: status=%d, http_err=%d\n",
|
||||
mynet.download_status, mynet.http_err_status);
|
||||
goto close;
|
||||
} else if (mynet.download_status == NET_DOWNLOAD_COMPLETE) {
|
||||
log_info("Download completed successfully\n");
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (net_download_check_ready(mynet.handle) >= 0) {
|
||||
mynet.bytes_read = net_download_read(mynet.handle, mynet.buf, sizeof(mynet.buf));//这里实际会缓存够buf_size
|
||||
if (mynet.bytes_read > 0) {
|
||||
mynet.total_read += mynet.bytes_read;
|
||||
log_info("Download progress: %d bytes\r", mynet.total_read);
|
||||
net_cbuf_write(mynet.buf, mynet.bytes_read);
|
||||
} else if (mynet.bytes_read < 0) {
|
||||
log_info("Read error: %d\n", mynet.bytes_read);
|
||||
goto close;
|
||||
}
|
||||
} else {
|
||||
os_time_dly(10);
|
||||
}
|
||||
}
|
||||
|
||||
close:
|
||||
mynet.ret = net_download_close(mynet.handle);
|
||||
if (mynet.ret != 0) {
|
||||
log_error("net_download_close failed: %d\n", mynet.ret);
|
||||
} else {
|
||||
log_info("Download closed normally\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void stop_all_downloads();
|
||||
static void net_download(void *priv)
|
||||
{
|
||||
int msg[3] = {0};
|
||||
net_url_reset_iterator();
|
||||
|
||||
g_total_urls = net_url_get_count();
|
||||
log_info("总共有 %d 个URL需要下载\n", g_total_urls);
|
||||
|
||||
if (g_total_urls == 0) {
|
||||
log_error("错误: URL列表为空\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char save_path[128];
|
||||
|
||||
for (g_current_url_index = 0; g_current_url_index < g_total_urls; g_current_url_index++) {
|
||||
char *url = net_get_url_by_index(g_current_url_index);
|
||||
if (url) {
|
||||
log_info("开始下载第%d个URL(共%d个): %s\n",
|
||||
g_current_url_index + 1, g_total_urls, url);
|
||||
snprintf(save_path, sizeof(save_path), "storage/sd0/C/913_%d.mp3", g_current_url_index);
|
||||
download_demo(url, save_path, g_current_url_index);
|
||||
|
||||
log_info("完成下载第%d个URL\n", g_current_url_index + 1);
|
||||
} else {
|
||||
log_error("错误: 获取第%d个URL失败\n", g_current_url_index + 1);
|
||||
}
|
||||
os_time_dly(50);
|
||||
}
|
||||
|
||||
log_info("所有URL下载完成!\n");
|
||||
msg[0] = (int)stop_all_downloads;
|
||||
msg[1] = 1;
|
||||
os_taskq_post_type("app_core", Q_CALLBACK, 2, msg);
|
||||
os_time_dly(-1);
|
||||
}
|
||||
|
||||
void net_url_download(void)
|
||||
{
|
||||
net_cbuf_init();
|
||||
os_task_create(net_download, NULL, 29, 512 * 5, 0, "dl_task");
|
||||
}
|
||||
|
||||
// 获取当前下载进度信息
|
||||
void get_download_progress(int *current_index, int *total_urls)
|
||||
{
|
||||
if (current_index) {
|
||||
*current_index = g_current_url_index;
|
||||
}
|
||||
if (total_urls) {
|
||||
*total_urls = g_total_urls;
|
||||
}
|
||||
log_info("current_download_url_index: %d total_urls %d ", current_index, total_urls);
|
||||
}
|
||||
|
||||
// 停止所有下载
|
||||
static void stop_all_downloads()
|
||||
{
|
||||
net_cbuf_exit();
|
||||
task_kill("dl_task");
|
||||
g_current_url_index = 0;
|
||||
g_total_urls = 0;
|
||||
log_info("所有下载已停止\n");
|
||||
}
|
||||
|
||||
#endif//NET_INTERFACE_EN
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef _NET_LIST_DOWNLOAD_H_
|
||||
#define _NET_LIST_DOWNLOAD_H_
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "circular_buf.h"
|
||||
#include "net_download.h"
|
||||
#include "http/http_cli.h"
|
||||
#include <stdlib.h>
|
||||
#include "net_includes.h"
|
||||
#include "os/os_api.h"
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
cbuffer_t cbuf;
|
||||
} net_buf_t;
|
||||
/**
|
||||
* @brief 获取网络cbuf缓存数据长度
|
||||
*
|
||||
*/
|
||||
extern int net_cbuf_data_len();
|
||||
/**
|
||||
* @brief 获取网络cbuf缓存数据
|
||||
*
|
||||
* @param[in] buf 外部传入buf地址
|
||||
* @param[in] len 外部传入buf长度
|
||||
* @param[out] response 响应数据指针的地址,需要外部释放内存
|
||||
*
|
||||
* @return int 执行结果
|
||||
* @retval 0 成功
|
||||
* @retval -1 失败
|
||||
*
|
||||
* @note 调用者需要负责释放buf指向的内存
|
||||
*/
|
||||
extern int net_cbuf_read_data(void *buf, u32 len);
|
||||
/**
|
||||
* @brief 执行url链式下载,需配合net_url_list执行,线程内部管理生存周期
|
||||
*/
|
||||
extern void net_url_download(void);
|
||||
/**
|
||||
* @brief url链式下载进度
|
||||
* @param[in] current_index 外部传入获取当前url索引的变量地址
|
||||
* @param[in] total_urls 外部传入获取总共url数量的变量地址
|
||||
|
||||
* @param[out] current_index 当前下载的url索引
|
||||
* @param[out] total_urls 总共的url数量
|
||||
*/
|
||||
extern void get_download_progress(int *current_index, int *total_urls);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".net_interface.data.bss")
|
||||
#pragma data_seg(".net_interface.data")
|
||||
#pragma const_seg(".net_interface.text.const")
|
||||
#pragma code_seg(".net_interface.text")
|
||||
#endif
|
||||
#include "net_mem.h"
|
||||
|
||||
#if NET_INTERFACE_EN
|
||||
|
||||
#define NET_INTERFACE_MEM_USE_PSRAM 1
|
||||
|
||||
#if NET_INTERFACE_MEM_USE_PSRAM
|
||||
#define NET_MALLOC(size) malloc_psram(size)
|
||||
#define NET_REALLOC(ptr,size) realloc_psram(ptr,size)
|
||||
#define NET_FREE(ptr) free_psram(ptr)
|
||||
#else
|
||||
#define NET_MALLOC(size) malloc(size)
|
||||
#define NET_REALLOC(ptr,size) realloc(ptr,size)
|
||||
#define NET_FREE(ptr) free(ptr)
|
||||
#endif
|
||||
|
||||
void *net_interface_malloc(size_t size)
|
||||
{
|
||||
return NET_MALLOC(size);
|
||||
}
|
||||
|
||||
void net_interface_free(void *pv)
|
||||
{
|
||||
if (pv != NULL) {
|
||||
NET_FREE(pv);
|
||||
}
|
||||
}
|
||||
|
||||
void *net_interface_calloc(unsigned long count, unsigned long size)
|
||||
{
|
||||
size_t total = count * size;
|
||||
void *p = NET_MALLOC(total);
|
||||
if (p) {
|
||||
memset(p, 0, total);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void *net_interface_realloc(void *ptr, size_t size)
|
||||
{
|
||||
return NET_REALLOC(ptr, size);
|
||||
}
|
||||
|
||||
_WEAK_
|
||||
void *calloc(unsigned long count, unsigned long size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(count * size);
|
||||
if (p) {
|
||||
memset(p, 0, count * size);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif//NET_INTERFACE_EN
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef _NET_MEM_H
|
||||
#define _NET_MEM_H
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "net_includes.h"
|
||||
|
||||
extern void *net_interface_malloc(size_t size);
|
||||
extern void net_interface_free(void *pv);
|
||||
extern void *net_interface_calloc(unsigned long count, unsigned long size);
|
||||
extern void *net_interface_realloc(void *ptr, size_t size);
|
||||
|
||||
#endif // _NET_MEM_H
|
||||
@@ -0,0 +1,146 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".net_interface.data.bss")
|
||||
#pragma data_seg(".net_interface.data")
|
||||
#pragma const_seg(".net_interface.text.const")
|
||||
#pragma code_seg(".net_interface.text")
|
||||
#endif
|
||||
#include "net_record.h"
|
||||
|
||||
#if NET_INTERFACE_EN
|
||||
|
||||
#define LOG_TAG_CONST NET_INTERFACE
|
||||
#define LOG_TAG "[NET_INTERFACE_RECORD]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#define NET_AUDIO_SAVE_TEST 1
|
||||
#if NET_AUDIO_SAVE_TEST
|
||||
static FILE *rec_file = NULL;
|
||||
#define __file rec_file
|
||||
#define FILE_SAVE_PATH "storage/sd0/C/record.bin"
|
||||
#endif
|
||||
|
||||
static net_rec_t rc;
|
||||
#define __buf rc.buf
|
||||
#define __cbuf rc.cbuf
|
||||
|
||||
static void net_rec_cbuf_init()
|
||||
{
|
||||
if (!__buf) {
|
||||
__buf = net_interface_malloc(NET_REC_CBUF_SIZE);
|
||||
}
|
||||
cbuf_init(&__cbuf, __buf, NET_REC_CBUF_SIZE);
|
||||
}
|
||||
|
||||
void net_rec_cbuf_exit(void)
|
||||
{
|
||||
cbuf_clear(&__cbuf);
|
||||
net_interface_free(__buf);
|
||||
__buf = NULL;
|
||||
}
|
||||
|
||||
static int my_fwrite(FILE *file, void *buf, u32 size)
|
||||
{
|
||||
int ret = fwrite(buf, size, 1, file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u16 net_rec_write_data(u8 *voice_buf, u16 voice_len)
|
||||
{
|
||||
#if NET_AUDIO_SAVE_TEST
|
||||
if (__file) {
|
||||
int wlen = my_fwrite(__file, voice_buf, voice_len);
|
||||
if (wlen != voice_len) {
|
||||
log_error("save file err: %d, %d\n", wlen, voice_len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
int wlen = cbuf_write(&__cbuf, voice_buf, voice_len);
|
||||
if (wlen != voice_len) {
|
||||
log_error("pcm out err: %d, %d\n", wlen, voice_len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int net_rec_stop(StopCompletedCallback callback)
|
||||
{
|
||||
if (!ai_mic_is_busy()) {
|
||||
log_info("ai_mic_is_null \n\n");
|
||||
return true;
|
||||
}
|
||||
ai_mic_rec_close();
|
||||
#if NET_AUDIO_SAVE_TEST
|
||||
if (__file) {
|
||||
fclose(__file);
|
||||
__file = NULL;
|
||||
}
|
||||
#endif
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int net_rec_start(void)
|
||||
{
|
||||
printf(">>>zwz info: %s %d %s\n", __FUNCTION__, __LINE__, __FILE__);
|
||||
net_rec_cbuf_init();
|
||||
if (ai_mic_is_busy()) {
|
||||
log_error("my_mic_is_busy \n\n");
|
||||
return false;
|
||||
}
|
||||
#if NET_AUDIO_SAVE_TEST
|
||||
if (__file) {
|
||||
fclose(__file);
|
||||
__file = NULL;
|
||||
}
|
||||
__file = fopen(FILE_SAVE_PATH, "w+");
|
||||
if (!__file) {
|
||||
log_error("fopen err \n\n");
|
||||
}
|
||||
#endif
|
||||
mic_rec_pram_init(NET_REC_TYPE, 0, net_rec_write_data, 1, 1024);
|
||||
ai_mic_rec_start();
|
||||
return true;
|
||||
}
|
||||
|
||||
int net_rec_data_len()
|
||||
{
|
||||
return cbuf_get_data_len(&__cbuf);
|
||||
}
|
||||
|
||||
int net_rec_read_data(void *buf, u32 len)
|
||||
{
|
||||
return cbuf_read(&__cbuf, buf, len);
|
||||
}
|
||||
|
||||
|
||||
void net_record_stop_with_clean()
|
||||
{
|
||||
net_rec_stop(net_rec_cbuf_exit);
|
||||
}
|
||||
|
||||
|
||||
void net_record_stop_without_clean()
|
||||
{
|
||||
net_rec_stop(NULL);
|
||||
}
|
||||
|
||||
void net_rec_test(void *priv)
|
||||
{
|
||||
net_record_stop_with_clean();
|
||||
}
|
||||
|
||||
void net_record_test()
|
||||
{
|
||||
printf(">>>zwz info: %s %d %s\n", __FUNCTION__, __LINE__, __FILE__);
|
||||
net_rec_start();
|
||||
sys_timeout_add(NULL, net_rec_test, 5000);
|
||||
|
||||
}
|
||||
#endif//NET_INTERFACE_EN
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef _MY_PLATFORM_RECORD_H_
|
||||
#define _MY_PLATFORM_RECORD_H_
|
||||
|
||||
|
||||
#include "net_includes.h"
|
||||
#include "fs.h"
|
||||
#include "os/os_api.h"
|
||||
#include "audio_def.h"
|
||||
|
||||
#define NET_REC_TYPE AUDIO_CODING_OPUS
|
||||
|
||||
extern int ai_mic_is_busy(void);
|
||||
extern int ai_mic_rec_close(void);
|
||||
extern int mic_rec_pram_init(/* const char **name, */u32 enc_type, u8 opus_type, u16(*speech_send)(u8 *buf, u16 len), u16 frame_num, u16 cbuf_size);
|
||||
extern int ai_mic_rec_start(void);
|
||||
|
||||
#define NET_REC_CBUF_SIZE 10*1024
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
cbuffer_t cbuf;
|
||||
} net_rec_t;
|
||||
|
||||
typedef void (*StopCompletedCallback)(void);
|
||||
/**
|
||||
* @brief 执行AI_TX录音
|
||||
*
|
||||
* @return int 执行结果
|
||||
* @retval 1 成功
|
||||
* @retval 0 失败
|
||||
*
|
||||
*/
|
||||
extern int net_rec_start(void);
|
||||
|
||||
/**
|
||||
* @brief 获取录音缓存数据长度
|
||||
*
|
||||
* @return int 缓存长度
|
||||
*
|
||||
*/
|
||||
extern int net_rec_data_len();
|
||||
|
||||
/**
|
||||
* @brief 获取录音缓存数据长度
|
||||
*
|
||||
* @retval 非0 长度
|
||||
* @retval 0 失败
|
||||
*
|
||||
*/
|
||||
extern int net_rec_read_data(void *buf, u32 len);
|
||||
|
||||
/**
|
||||
* @brief 清理缓存空间
|
||||
*/
|
||||
extern void net_rec_cbuf_exit(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 停止录音同时清理缓存空间
|
||||
*/
|
||||
extern void net_record_stop_with_clean();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 停止录音同时不清理缓存空间,需外部再去清理缓存空间
|
||||
*/
|
||||
extern void net_record_stop_without_clean();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".net_interface.data.bss")
|
||||
#pragma data_seg(".net_interface.data")
|
||||
#pragma const_seg(".net_interface.text.const")
|
||||
#pragma code_seg(".net_interface.text")
|
||||
#endif
|
||||
#include "net_time.h"
|
||||
|
||||
#if NET_INTERFACE_EN
|
||||
//获取rtc时间
|
||||
void net_get_sys_time(struct sys_time *time)
|
||||
{
|
||||
rtc_read_time(time);
|
||||
}
|
||||
#endif//NET_INTERFACE_EN
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef _NET_TIME_H_
|
||||
#define _NET_TIME_H_
|
||||
|
||||
|
||||
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "sys_time.h"
|
||||
#include <time.h>
|
||||
#include "net_includes.h"
|
||||
#include "timestamp.h"
|
||||
#include "rtc.h"
|
||||
|
||||
extern void net_get_sys_time(struct sys_time *time);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,211 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".net_interface.data.bss")
|
||||
#pragma data_seg(".net_interface.data")
|
||||
#pragma const_seg(".net_interface.text.const")
|
||||
#pragma code_seg(".net_interface.text")
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file net_url_list.c
|
||||
* @brief URL列表管理接口实现
|
||||
*
|
||||
* 该模块提供动态URL列表的存储、迭代和内存管理功能。列表使用动态数组实现,
|
||||
* 支持自动扩容、安全迭代和资源清理。适用于需要管理多个URL字符串的场景,
|
||||
* 支持最小单元测试。
|
||||
*/
|
||||
|
||||
#include "net_url_list.h"
|
||||
|
||||
#if NET_INTERFACE_EN
|
||||
|
||||
#define LOG_TAG_CONST NET_INTERFACE
|
||||
#define LOG_TAG "[NET_URL_LIST]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
static url_list_t g_url_list; // 全局URL列表实例
|
||||
|
||||
void net_url_list_init(void)
|
||||
{
|
||||
g_url_list.capacity = 5; // 初始容量设为5
|
||||
g_url_list.count = 0;
|
||||
g_url_list.iterator_index = 0;
|
||||
// 为URL指针数组申请初始空间
|
||||
g_url_list.urls = (char **)net_interface_malloc(sizeof(char *) * g_url_list.capacity);
|
||||
if (g_url_list.urls == NULL) {
|
||||
g_url_list.capacity = 0;
|
||||
log_error("错误: 初始化内存分配失败\n");
|
||||
}
|
||||
}
|
||||
|
||||
// 销毁URL列表,释放所有内存
|
||||
void net_url_list_destroy(void)
|
||||
{
|
||||
// 首先释放每个URL字符串本身的内存
|
||||
for (int i = 0; i < g_url_list.count; i++) {
|
||||
net_interface_free(g_url_list.urls[i]);
|
||||
}
|
||||
// 然后释放存放URL指针的数组
|
||||
net_interface_free(g_url_list.urls);
|
||||
// 重置所有状态
|
||||
g_url_list.urls = NULL;
|
||||
g_url_list.count = 0;
|
||||
g_url_list.capacity = 0;
|
||||
g_url_list.iterator_index = 0;
|
||||
}
|
||||
|
||||
// 设置(添加)一个URL到列表中
|
||||
void net_url_set(char *url)
|
||||
{
|
||||
if (url == NULL) {
|
||||
log_warn("警告: 尝试添加空URL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查容量是否足够,不足则扩容
|
||||
if (g_url_list.count >= g_url_list.capacity) {
|
||||
int new_capacity = g_url_list.capacity * 2; // 容量翻倍
|
||||
char **new_urls = (char **)net_interface_realloc(g_url_list.urls, sizeof(char *) * new_capacity);
|
||||
|
||||
if (new_urls == NULL) {
|
||||
log_error("错误: 内存扩容失败,无法添加URL: %s\n", url);
|
||||
return;
|
||||
}
|
||||
g_url_list.urls = new_urls;
|
||||
g_url_list.capacity = new_capacity;
|
||||
}
|
||||
|
||||
// 为新的URL字符串分配内存并复制内容
|
||||
g_url_list.urls[g_url_list.count] = (char *)net_interface_malloc(strlen(url) + 1); // +1 用于字符串结束符'\0'
|
||||
if (g_url_list.urls[g_url_list.count] == NULL) {
|
||||
log_error("错误: 无法为URL分配内存: %s\n", url);
|
||||
return;
|
||||
}
|
||||
strcpy(g_url_list.urls[g_url_list.count], url);
|
||||
g_url_list.count++;
|
||||
}
|
||||
|
||||
// 获取当前URL列表中的URL数量
|
||||
int net_url_get_count(void)
|
||||
{
|
||||
return g_url_list.count;
|
||||
}
|
||||
|
||||
// 根据索引获取URL,索引从0开始
|
||||
char *net_get_url_by_index(int index)
|
||||
{
|
||||
if (index < 0 || index >= g_url_list.count) {
|
||||
log_error("错误: 索引%d越界(总数:%d)\n", index, g_url_list.count);
|
||||
return NULL;
|
||||
}
|
||||
return g_url_list.urls[index];
|
||||
}
|
||||
|
||||
// 重置迭代器到列表开头
|
||||
void net_url_reset_iterator(void)
|
||||
{
|
||||
g_url_list.iterator_index = 0;
|
||||
}
|
||||
|
||||
// 获取迭代器当前指向的URL,并将迭代器移动到下一个位置
|
||||
char *net_url_get_next(void)
|
||||
{
|
||||
if (g_url_list.iterator_index >= g_url_list.count) {
|
||||
return NULL; // 已经遍历完所有URL
|
||||
}
|
||||
return g_url_list.urls[g_url_list.iterator_index++];
|
||||
}
|
||||
|
||||
// 获取第一个URL并重置迭代器
|
||||
char *net_url_get_first(void)
|
||||
{
|
||||
net_url_reset_iterator();
|
||||
return net_url_get_next();
|
||||
}
|
||||
|
||||
// 判断当前URL是否是最后一个(相对于迭代器位置)
|
||||
int net_url_is_last(void)
|
||||
{
|
||||
// 如果列表为空或迭代器已在最后一个或之后,返回1
|
||||
return (g_url_list.count == 0) || (g_url_list.iterator_index >= g_url_list.count - 1);
|
||||
}
|
||||
|
||||
// 打印所有URL
|
||||
void net_print_urls(void)
|
||||
{
|
||||
log_info("URL列表(共%d个):\n", g_url_list.count);
|
||||
for (int i = 0; i < g_url_list.count; i++) {
|
||||
log_info("%d: %s\n", i, g_url_list.urls[i]);
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
void test_url_management()
|
||||
{
|
||||
log_info("URL List Manager Test\n");
|
||||
log_info("=====================\n");
|
||||
|
||||
// 1. 初始化URL链表
|
||||
net_url_list_init();
|
||||
log_info("1. List initialized\n");
|
||||
|
||||
// 2. 添加URL
|
||||
net_url_set("https://www.example.com");
|
||||
net_url_set("https://www.github.com");
|
||||
net_url_set("https://www.openai.com");
|
||||
net_url_set("https://www.kernel.org");
|
||||
net_url_set("https://www.python.org");
|
||||
log_info("2. URLs added\n");
|
||||
|
||||
// 3. 打印所有URL
|
||||
net_print_urls();
|
||||
|
||||
// 4. 按索引获取URL
|
||||
log_info("\n4. Access by index:");
|
||||
for (int i = 0; i < net_url_get_count(); i++) {
|
||||
char *url = net_get_url_by_index(i);
|
||||
log_info("\n [%d] %s", i, url);
|
||||
}
|
||||
log_info("\n");
|
||||
|
||||
// 5. 迭代器测试
|
||||
log_info("\n5. Iterator test:");
|
||||
int count = 0;
|
||||
for (char *url = net_url_get_first();
|
||||
url != NULL;
|
||||
url = net_url_get_next()) {
|
||||
log_info("\n Iteration %d: %s", ++count, url);
|
||||
}
|
||||
|
||||
// 6. 重置迭代器
|
||||
net_url_reset_iterator();
|
||||
|
||||
// 7. 边缘测试:越界访问
|
||||
log_info("\n\n7. Boundary tests:");
|
||||
log_info("\n Index -1: %s",
|
||||
net_get_url_by_index(-1) ? "Found" : "NULL");
|
||||
log_info("\n Index %d: %s",
|
||||
net_url_get_count(),
|
||||
net_get_url_by_index(net_url_get_count()) ? "Found" : "NULL");
|
||||
|
||||
// 8. 销毁链表
|
||||
net_url_list_destroy();
|
||||
log_info("\n\n8. List destroyed\n");
|
||||
|
||||
// 9. 销毁后访问
|
||||
log_info("\n9. Post-destruction access:");
|
||||
log_info("\n Count: %d", net_url_get_count());
|
||||
net_print_urls();
|
||||
|
||||
// 10. 重新初始化并添加新URL
|
||||
net_url_list_init();
|
||||
net_url_set("https://www.new-start.com");
|
||||
log_info("\n10. Reinitialized list:\n");
|
||||
net_print_urls();
|
||||
net_url_list_destroy();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#endif//NET_INTERFACE_EN
|
||||
@@ -0,0 +1,150 @@
|
||||
#ifndef _NET_URL_LIST_H_
|
||||
#define _NET_URL_LIST_H_
|
||||
|
||||
#include "net_includes.h"
|
||||
|
||||
#include "list.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 定义URL列表的管理结构体
|
||||
typedef struct {
|
||||
char **urls; // 指向URL字符串指针数组的指针
|
||||
int capacity; // 指针数组当前的容量
|
||||
int count; // 当前存储的URL数量
|
||||
int iterator_index; // 用于外部迭代的当前位置
|
||||
} url_list_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 检查当前迭代位置是否指向最后一个URL
|
||||
*
|
||||
* 判断迭代器是否已到达最后一个URL(或越界)。
|
||||
*
|
||||
* @param None
|
||||
* @return int 1表示是最后一个或越界,0表示否
|
||||
*
|
||||
* @note 列表为空时返回1
|
||||
* @example if (net_url_is_last()) break;
|
||||
*/
|
||||
extern int net_url_is_last(void);
|
||||
/**
|
||||
* @brief 初始化URL列表结构
|
||||
*
|
||||
* 初始化全局URL列表,设置初始容量为5,并分配初始内存。
|
||||
* 如果内存分配失败,容量设为0并记录错误。
|
||||
*
|
||||
* @param None
|
||||
* @return None
|
||||
*
|
||||
* @note 初始容量选择5是为了平衡内存使用和扩容频率。
|
||||
* @example net_url_list_init();
|
||||
*/
|
||||
extern void net_url_list_init(void);
|
||||
/**
|
||||
* @brief 销毁URL列表并释放所有内存
|
||||
*
|
||||
* 安全释放所有URL字符串内存和指针数组内存,重置列表状态。
|
||||
* 防止内存泄漏的关键函数,必须在功能结束前调用。
|
||||
*
|
||||
* @param None
|
||||
* @return None
|
||||
*
|
||||
* @note 释放顺序:先释放每个URL字符串,再释放指针数组。
|
||||
* @example net_url_list_destroy();
|
||||
*/
|
||||
extern void net_url_list_destroy(void);
|
||||
/**
|
||||
* @brief 添加URL到列表
|
||||
*
|
||||
* 将URL字符串复制到列表末尾。如果容量不足,自动扩容至原容量2倍。
|
||||
* 支持空URL检查,扩容失败或内存分配失败时记录错误。
|
||||
*
|
||||
* @param url 要添加的URL字符串(需以空字符结尾)
|
||||
* @return None
|
||||
*
|
||||
* @example net_url_set("xxxxxx");
|
||||
*/
|
||||
|
||||
extern void net_url_set(char *url);
|
||||
/**
|
||||
* @brief 打印所有URL到日志
|
||||
*
|
||||
* 调试用途:将列表中所有URL按索引打印到日志。
|
||||
*
|
||||
* @param None
|
||||
* @return None
|
||||
*
|
||||
* @example net_print_urls();
|
||||
*/
|
||||
|
||||
extern void net_print_urls(void);
|
||||
/**
|
||||
* @brief 通过索引获取URL
|
||||
*
|
||||
* 根据索引(从0开始)返回对应的URL字符串。索引越界时返回NULL并记录错误。
|
||||
*
|
||||
* @param index URL的索引(0 ≤ index < count)
|
||||
* @return char* 成功返回URL字符串指针,失败返回NULL
|
||||
*
|
||||
* @note 返回的指针为列表内部数据,不应手动释放。
|
||||
* @example char *url = net_get_url_by_index(0);
|
||||
*/
|
||||
|
||||
extern char *net_get_url_by_index(int index);
|
||||
/**
|
||||
* @brief 获取第一个URL并重置迭代器
|
||||
*
|
||||
* 便捷函数:重置迭代器并返回第一个URL。列表为空时返回NULL。
|
||||
*
|
||||
* @param None
|
||||
* @return char* 第一个URL或NULL
|
||||
*
|
||||
* @example char *first = net_url_get_first();
|
||||
*/
|
||||
|
||||
extern char *net_url_get_first(void);
|
||||
/**
|
||||
* @brief 获取下一个URL(迭代器方式)
|
||||
*
|
||||
* 返回迭代器当前指向的URL,并将迭代器移动到下一个位置。
|
||||
* 遍历完成后返回NULL。
|
||||
*
|
||||
* @param None
|
||||
* @return char* URL字符串或NULL(遍历结束时)
|
||||
*
|
||||
* @note 与net_url_reset_iterator配合使用,实现安全迭代。
|
||||
* @example
|
||||
* net_url_reset_iterator();
|
||||
* while ((char *url = net_url_get_next()) != NULL) { ... }
|
||||
*/
|
||||
|
||||
extern char *net_url_get_next(void);
|
||||
/**
|
||||
* @brief 重置迭代器到列表开头
|
||||
*
|
||||
* 将内部迭代器位置重置为0,用于重新开始遍历。
|
||||
*
|
||||
* @param None
|
||||
* @return None
|
||||
*
|
||||
* @see net_url_get_next
|
||||
* @example net_url_reset_iterator();
|
||||
*/
|
||||
|
||||
extern void net_url_reset_iterator(void);
|
||||
/**
|
||||
* @brief 获取当前URL数量
|
||||
*
|
||||
* 返回列表中当前存储的URL数量,用于循环或状态检查。
|
||||
*
|
||||
* @param None
|
||||
* @return int URL数量(总为非负整数)
|
||||
*
|
||||
* @example int count = net_url_get_count();
|
||||
*/
|
||||
extern int net_url_get_count(void);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user