910 lines
29 KiB
C
910 lines
29 KiB
C
#include "server/ai_server.h"
|
|
#include "system/os/os_api.h"
|
|
#include "server/server_core.h"
|
|
#include "version.h"
|
|
#include "echo_cloud.h"
|
|
#include "generic/circular_buf.h"
|
|
#include "server/upgrade_server.h"
|
|
#include "sock_api/sock_api.h"
|
|
|
|
#ifdef ECHO_CLOUD_SDK_VERSION
|
|
MODULE_VERSION_EXPORT(echo_cloud_sdk, ECHO_CLOUD_SDK_VERSION);
|
|
#endif
|
|
|
|
|
|
const struct ai_sdk_api echo_cloud_sdk_api;
|
|
|
|
static const int events[][2] = {
|
|
{ AI_EVENT_SPEAK_END, ECHO_CLOUD_SPEAK_END },
|
|
{ AI_EVENT_MEDIA_END, ECHO_CLOUD_MEDIA_END },
|
|
{ AI_EVENT_MEDIA_STOP, ECHO_CLOUD_MEDIA_STOP },
|
|
{ AI_EVENT_PREVIOUS_SONG, ECHO_CLOUD_PREVIOUS_SONG },
|
|
{ AI_EVENT_NEXT_SONG, ECHO_CLOUD_NEXT_SONG },
|
|
{ AI_EVENT_RECORD_START, ECHO_CLOUD_RECORD_START },
|
|
{ AI_EVENT_RECORD_STOP, ECHO_CLOUD_RECORD_STOP },
|
|
{ AI_EVENT_VOLUME_CHANGE, ECHO_CLOUD_VOLUME_CHANGE },
|
|
{ AI_EVENT_PLAY_PAUSE, ECHO_CLOUD_PLAY_PAUSE },
|
|
{ AI_EVENT_COLLECT_RES, ECHO_CLOUD_COLLECT_RESOURCE },
|
|
{ AI_EVENT_CHILD_LOCK, ECHO_CLOUD_CHILD_LOCK_CHANGE },
|
|
{ AI_EVENT_CUSTOM_FUN, ECHO_CLOUD_DEVICE_CODE_REPORT },
|
|
{ AI_EVENT_QUIT , ECHO_CLOUD_QUIT },
|
|
};
|
|
|
|
enum {
|
|
ECHO_CLOUD_SPEEK_URL,
|
|
ECHO_CLOUD_MEDIA_URL,
|
|
};
|
|
|
|
typedef enum {
|
|
RECORDER_STOP = 0,
|
|
RECORDER_START,
|
|
} echo_cloud_rec_state_t;
|
|
|
|
|
|
#define WECHAT_ENC_MAX_DURATION (60) //s
|
|
#define WECHAT_ENC_BUFFER_MIN_LEN (1600 + 6)
|
|
|
|
|
|
#if 1
|
|
#define ONE_CACHE_BUF_LEN 800 //MSS=1300
|
|
#define CACHE_BUF_LEN (4 * ONE_CACHE_BUF_LEN) //vad_buf 占用一半
|
|
#else
|
|
#define ONE_CACHE_BUF_LEN 1200 //MSS=1300
|
|
#define CACHE_BUF_LEN ONE_CACHE_BUF_LEN
|
|
#endif
|
|
|
|
|
|
static struct {
|
|
struct server *enc_server;
|
|
struct echo_cloud_hdl hdl;
|
|
echo_cloud_rec_state_t rec_state;
|
|
OS_SEM sem;
|
|
OS_MUTEX mutex;
|
|
cbuffer_t vad_cbuf;
|
|
int app_task_pid;
|
|
volatile u8 exit_flag;
|
|
u8 voice_mode;
|
|
u8 connect_status;
|
|
u8 vad_status;
|
|
u8 vad_enable;
|
|
u8 curr_url_type;
|
|
u8 msg_notify_disable;
|
|
u16 curr_offset;
|
|
u32 rec_total_len;
|
|
u8 enc_buffer[CACHE_BUF_LEN + 6];
|
|
} echo_cloud_api_hdl;
|
|
|
|
#define __this (&echo_cloud_api_hdl)
|
|
|
|
|
|
static int event2echo_cloud(int event)
|
|
{
|
|
for (int i = 0; i < ARRAY_SIZE(events); i++) {
|
|
if (events[i][0] == event) {
|
|
return events[i][1];
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int echo_cloud_sdk_do_event(int event, int arg)
|
|
{
|
|
int err;
|
|
int argc = 2;
|
|
int argv[4];
|
|
int echo_cloud_event;
|
|
|
|
echo_cloud_event = event2echo_cloud(event);
|
|
if (echo_cloud_event == -1) {
|
|
return 0;
|
|
}
|
|
|
|
printf("echo_cloud_event: %x\n", echo_cloud_event);
|
|
|
|
if (!__this->connect_status) {
|
|
return 0;
|
|
}
|
|
|
|
if (echo_cloud_event == ECHO_CLOUD_RECORD_START) {
|
|
if (__this->rec_state == RECORDER_START) {
|
|
return 0;
|
|
}
|
|
} else if (echo_cloud_event == ECHO_CLOUD_MEDIA_END) {
|
|
if (__this->curr_url_type == ECHO_CLOUD_SPEEK_URL) {
|
|
echo_cloud_event = ECHO_CLOUD_SPEAK_END;
|
|
}
|
|
}
|
|
|
|
if (echo_cloud_event != ECHO_CLOUD_RECORD_STOP &&
|
|
echo_cloud_event != ECHO_CLOUD_CHILD_LOCK_CHANGE &&
|
|
__this->msg_notify_disable) {
|
|
return 0;
|
|
}
|
|
|
|
argv[0] = echo_cloud_event;
|
|
argv[1] = arg;
|
|
|
|
err = os_taskq_post_type("echo_cloud_app_task", Q_USER, argc, argv);
|
|
if (err != OS_NO_ERR) {
|
|
printf("send msg to echo_cloud_app_task fail, event : %d\n", echo_cloud_event);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int echo_cloud_vfs_fwrite(void *file, void *data, u32 len)
|
|
{
|
|
u32 remain = 0;
|
|
u32 cplen = 0;
|
|
int err = 0;
|
|
u32 ret_len = len;
|
|
u8 *p = (u8 *)data;
|
|
u8 vad_status = __this->vad_status;
|
|
|
|
if (len == 6) {
|
|
return len; //跳过头部
|
|
}
|
|
|
|
if (__this->rec_state == RECORDER_STOP) {
|
|
return 0;
|
|
}
|
|
|
|
__this->rec_total_len += len;
|
|
|
|
if (__this->vad_enable) {
|
|
if (0 == vad_status) {
|
|
if (0 == cbuf_is_write_able(&__this->vad_cbuf, len)) {
|
|
if (len != cbuf_read(&__this->vad_cbuf, __this->enc_buffer + 6, len)) {
|
|
return 0;
|
|
}
|
|
}
|
|
cbuf_write(&__this->vad_cbuf, data, len);
|
|
return len;
|
|
}
|
|
|
|
u32 cbuf_data_len = cbuf_get_data_size(&__this->vad_cbuf);
|
|
|
|
while (cbuf_data_len > 0) {
|
|
if (cbuf_data_len > ONE_CACHE_BUF_LEN) {
|
|
__this->curr_offset = cbuf_read(&__this->vad_cbuf, __this->enc_buffer + 6, ONE_CACHE_BUF_LEN);
|
|
} else {
|
|
__this->curr_offset = cbuf_read(&__this->vad_cbuf, __this->enc_buffer + 6, cbuf_data_len);
|
|
}
|
|
cbuf_data_len -= __this->curr_offset;
|
|
//echo_cloud的服务器限制了每次发800个字节,加上头部是806个字节
|
|
if (__this->curr_offset == ONE_CACHE_BUF_LEN) {
|
|
if (__this->rec_state != RECORDER_START) {
|
|
return 0;
|
|
}
|
|
if (__this->enc_buffer[0]) {
|
|
err = os_taskq_post("echo_cloud_app_task", 4, ECHO_CLOUD_RECORD_SEND, (int)__this->enc_buffer,
|
|
ONE_CACHE_BUF_LEN + 6, &__this->sem);
|
|
} else {
|
|
err = os_taskq_post("echo_cloud_app_task", 4, ECHO_CLOUD_RECORD_SEND, (int)(__this->enc_buffer + 6),
|
|
ONE_CACHE_BUF_LEN, &__this->sem);
|
|
}
|
|
if (err != OS_NO_ERR) {
|
|
puts("echo_cloud_app_task msg full\n");
|
|
return 0;
|
|
}
|
|
|
|
if (OS_TIMEOUT == os_sem_pend(&__this->sem, 450)) {
|
|
return 0;
|
|
}
|
|
__this->enc_buffer[0] = 0;
|
|
|
|
__this->curr_offset = 0;
|
|
}
|
|
}
|
|
|
|
if (2 == vad_status) {
|
|
if (OS_NO_ERR != os_taskq_post("echo_cloud_app_task", 1, ECHO_CLOUD_RECORD_STOP)) {
|
|
puts("echo_cloud_app_task send msg ECHO_CLOUD_RECORD_STOP timeout ");
|
|
}
|
|
return ret_len;
|
|
}
|
|
}
|
|
|
|
do {
|
|
remain = ONE_CACHE_BUF_LEN - __this->curr_offset;
|
|
cplen = remain < len ? remain : len;
|
|
if (cplen == 0) {
|
|
if (__this->rec_state != RECORDER_START) {
|
|
return 0;
|
|
}
|
|
if (__this->enc_buffer[0]) {
|
|
err = os_taskq_post("echo_cloud_app_task", 4, ECHO_CLOUD_RECORD_SEND, (int)__this->enc_buffer,
|
|
ONE_CACHE_BUF_LEN + 6, &__this->sem);
|
|
} else {
|
|
err = os_taskq_post("echo_cloud_app_task", 4, ECHO_CLOUD_RECORD_SEND, (int)(__this->enc_buffer + 6),
|
|
ONE_CACHE_BUF_LEN, &__this->sem);
|
|
}
|
|
if (err != OS_NO_ERR) {
|
|
puts("echo_cloud_app_task msg full\n");
|
|
return 0;
|
|
}
|
|
|
|
if (OS_TIMEOUT == os_sem_pend(&__this->sem, 450)) {
|
|
return 0;
|
|
}
|
|
|
|
__this->enc_buffer[0] = 0;
|
|
__this->curr_offset = 0;
|
|
} else {
|
|
memcpy(__this->enc_buffer + __this->curr_offset + 6, p, cplen);
|
|
__this->curr_offset += cplen;
|
|
p += cplen;
|
|
len -= cplen;
|
|
}
|
|
|
|
} while (len);
|
|
|
|
return ret_len;
|
|
}
|
|
|
|
static int echo_cloud_vfs_fclose(void *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct audio_vfs_ops echo_cloud_vfs_ops = {
|
|
.fwrite = echo_cloud_vfs_fwrite,
|
|
.fclose = echo_cloud_vfs_fclose,
|
|
};
|
|
|
|
static void enc_server_event_handler(void *priv, int argc, int *argv)
|
|
{
|
|
switch (argv[0]) {
|
|
case AUDIO_SERVER_EVENT_ERR:
|
|
case AUDIO_SERVER_EVENT_END:
|
|
puts("echo_cloud rec: AUDIO_SERVER_EVENT_END\n");
|
|
if (__this->rec_state == RECORDER_START) {
|
|
if (OS_NO_ERR != os_taskq_post("echo_cloud_app_task", 1, ECHO_CLOUD_RECORD_ERR)) {
|
|
puts("echo_cloud_app_task send msg ECHO_CLOUD_RECORD_ERR timeout ");
|
|
}
|
|
}
|
|
break;
|
|
case AUDIO_SERVER_EVENT_SPEAK_START:
|
|
__this->vad_status = 1;
|
|
puts("speak start ! \n");
|
|
break;
|
|
case AUDIO_SERVER_EVENT_SPEAK_STOP:
|
|
__this->vad_status = 2;
|
|
puts("speak stop ! \n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int echo_cloud_send_last_packet(struct echo_cloud_http_hdl *p)
|
|
{
|
|
int err = 0;
|
|
|
|
if ((__this->vad_enable && !__this->vad_status) || (WECHAT_MODE != __this->voice_mode && !__this->rec_total_len)) {
|
|
ai_server_event_notify(&echo_cloud_sdk_api, NULL, AI_SERVER_EVENT_REC_ERR);
|
|
return 0;
|
|
}
|
|
|
|
if (__this->curr_offset > 0) {
|
|
if (__this->enc_buffer[0]) {
|
|
err = echo_cloud_enc_http_send(p, __this->enc_buffer, __this->curr_offset + 6);
|
|
__this->enc_buffer[0] = 0;
|
|
} else {
|
|
err = echo_cloud_enc_http_send(p, __this->enc_buffer + 6, __this->curr_offset);
|
|
}
|
|
__this->curr_offset = 0;
|
|
}
|
|
|
|
if (!err) {
|
|
err = echo_cloud_enc_http_send(p, NULL, 0);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int echo_cloud_recorder_start(u16 sample_rate, u8 __voice_mode, u8 enable_vad)
|
|
{
|
|
int err;
|
|
union audio_req req = {0};
|
|
OS_SEM mic_sem;
|
|
|
|
puts("echo_cloud_recorder_start\n");
|
|
|
|
if (__this->rec_state == RECORDER_START) {
|
|
return -1;
|
|
}
|
|
|
|
if (!__this->enc_server) {
|
|
__this->enc_server = server_open("audio_server", "enc");
|
|
}
|
|
|
|
server_register_event_handler(__this->enc_server, NULL, enc_server_event_handler);
|
|
|
|
u8 voice_mode = __voice_mode & 0x3;
|
|
__this->voice_mode = voice_mode;
|
|
__this->vad_enable = enable_vad;
|
|
__this->vad_status = 0;
|
|
__this->curr_offset = 0;
|
|
__this->rec_total_len = 0;
|
|
|
|
memcpy(__this->enc_buffer, "#!AMR\n", 6);
|
|
cbuf_init(&__this->vad_cbuf, __this->enc_buffer + 6 + 2 * ONE_CACHE_BUF_LEN, 2 * ONE_CACHE_BUF_LEN);
|
|
os_sem_set(&__this->sem, 0);
|
|
|
|
req.enc.cmd = AUDIO_ENC_OPEN;
|
|
req.enc.channel = 1;
|
|
req.enc.volume = 100;
|
|
req.enc.priority = 0;
|
|
req.enc.output_buf = NULL;
|
|
req.enc.output_buf_len = 6 * 1024;
|
|
if (voice_mode == WECHAT_MODE) {
|
|
req.enc.msec = WECHAT_ENC_MAX_DURATION * 1000;
|
|
} else {
|
|
req.enc.msec = 15 * 1000;
|
|
}
|
|
req.enc.sample_rate = sample_rate;
|
|
req.enc.format = "amr";
|
|
req.enc.frame_size = 4 * 1024;
|
|
req.enc.sample_source = "mic";
|
|
req.enc.vfs_ops = &echo_cloud_vfs_ops;
|
|
req.enc.channel_bit_map = (__voice_mode & 0x70) ? BIT(((__voice_mode & 0x70) >> 4) - 1) : 0;
|
|
if (voice_mode != WECHAT_MODE && enable_vad) {
|
|
req.enc.use_vad = 1;
|
|
}
|
|
|
|
os_sem_create(&mic_sem, 0);
|
|
ai_server_event_notify(&echo_cloud_sdk_api, &mic_sem, AI_SERVER_EVENT_MIC_OPEN);
|
|
os_sem_pend(&mic_sem, 0);
|
|
os_sem_del(&mic_sem, 1);
|
|
|
|
__this->rec_state = RECORDER_START;
|
|
|
|
err = server_request(__this->enc_server, AUDIO_REQ_ENC, &req);
|
|
if (err == 0) {
|
|
puts("--------echo_cloud_start_rec: exit\n");
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int echo_cloud_recorder_stop(void)
|
|
{
|
|
int err;
|
|
union audio_req req;
|
|
|
|
if (!__this->enc_server || __this->rec_state == RECORDER_STOP) {
|
|
return 0;
|
|
}
|
|
__this->rec_state = RECORDER_STOP;
|
|
os_sem_post(&__this->sem);
|
|
|
|
req.enc.cmd = AUDIO_ENC_CLOSE;
|
|
|
|
err = server_request(__this->enc_server, AUDIO_REQ_ENC, &req);
|
|
if (err == 0) {
|
|
ai_server_event_notify(&echo_cloud_sdk_api, NULL, AI_SERVER_EVENT_MIC_CLOSE);
|
|
}
|
|
|
|
if (__this->voice_mode == WECHAT_MODE && __this->rec_total_len < WECHAT_ENC_BUFFER_MIN_LEN) {
|
|
ai_server_event_notify(&echo_cloud_sdk_api, NULL, AI_SERVER_EVENT_REC_TOO_SHORT);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void JL_echo_cloud_media_speak_play(const char *url)
|
|
{
|
|
__this->curr_url_type = ECHO_CLOUD_SPEEK_URL;
|
|
ai_server_event_url(&echo_cloud_sdk_api, url, AI_SERVER_EVENT_URL);
|
|
}
|
|
|
|
void JL_echo_cloud_wechat_speak_play(const char *url)
|
|
{
|
|
char *save_url = NULL;
|
|
|
|
if (url && strlen(url) > 0) {
|
|
save_url = (char *)malloc(strlen(url) + 1);
|
|
if (!save_url) {
|
|
return;
|
|
}
|
|
strcpy(save_url, url);
|
|
if (ai_server_event_notify(&echo_cloud_sdk_api, save_url, AI_SERVER_EVENT_RECV_CHAT)) {
|
|
free(save_url);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JL_echo_cloud_media_audio_play(const char *url)
|
|
{
|
|
__this->curr_url_type = ECHO_CLOUD_MEDIA_URL;
|
|
ai_server_event_url(&echo_cloud_sdk_api, url, AI_SERVER_EVENT_URL);
|
|
}
|
|
|
|
void JL_echo_cloud_media_audio_continue(const char *url)
|
|
{
|
|
ai_server_event_url(&echo_cloud_sdk_api, url, AI_SERVER_EVENT_CONTINUE);
|
|
}
|
|
|
|
void JL_echo_cloud_media_audio_pause(const char *url)
|
|
{
|
|
ai_server_event_url(&echo_cloud_sdk_api, url, AI_SERVER_EVENT_PAUSE);
|
|
}
|
|
|
|
void JL_echo_cloud_upgrade_notify(int event, void *arg)
|
|
{
|
|
ai_server_event_notify(&echo_cloud_sdk_api, arg, event);
|
|
}
|
|
|
|
void JL_echo_cloud_volume_change_notify(int volume)
|
|
{
|
|
ai_server_event_notify(&echo_cloud_sdk_api, (void *)volume, AI_SERVER_EVENT_VOLUME_CHANGE);
|
|
}
|
|
|
|
void JL_echo_cloud_child_lock_change_notify(int value)
|
|
{
|
|
ai_server_event_notify(&echo_cloud_sdk_api, (void *)value, AI_SERVER_EVENT_CHILD_LOCK_CHANGE);
|
|
}
|
|
|
|
void JL_echo_cloud_light_change_notify(int value)
|
|
{
|
|
ai_server_event_notify(&echo_cloud_sdk_api, (void *)value, AI_SERVER_EVENT_LIGHT_CHANGE);
|
|
}
|
|
|
|
__attribute__((weak)) int get_echo_cloud_device_id(char *device_id, u32 len)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
__attribute__((weak)) int get_echo_cloud_hash_key(char *hash_key, u32 len)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
__attribute__((weak)) int get_echo_cloud_uuid(char *uuid, u32 len)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
__attribute__((weak)) const char *get_echo_cloud_firmware_version(void)
|
|
{
|
|
return "0.0.0.0";
|
|
}
|
|
|
|
__attribute__((weak)) int check_echo_cloud_firmware_version(const char *url, const char *new_ver, const char *checksum)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
__attribute__((weak)) int get_app_music_volume(void)
|
|
{
|
|
return 100;
|
|
}
|
|
|
|
__attribute__((weak)) int get_app_music_playtime(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
__attribute__((weak)) int get_app_music_light_intensity(void)
|
|
{
|
|
return 100;
|
|
}
|
|
|
|
__attribute__((weak)) int get_app_music_battery_power(void)
|
|
{
|
|
return 100;
|
|
}
|
|
|
|
__attribute__((weak)) int ai_platform_if_support_poweron_recommend(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static int get_echo_cloud_profile(struct echo_cloud_hdl *echo)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = get_echo_cloud_device_id(echo->auth.device_id, DEVICE_ID_LEN + 1);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = get_echo_cloud_hash_key(echo->auth.hash, HASH_LEN + 1);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = get_echo_cloud_uuid(echo->auth.channel_uuid, UUID_LEN + 1);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void echo_cloud_heart_beat(void *priv)
|
|
{
|
|
struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv;
|
|
json_object *cmd_data = NULL;
|
|
|
|
if (p->media_play_state != ECHO_CLOUD_MEDIA_STATE_STOP) {
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token));
|
|
json_object_object_add(cmd_data, "offset", json_object_new_int(get_app_music_playtime() * 1000));
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_PROGRESS_CHANGER, cmd_data);
|
|
return;
|
|
}
|
|
if (!p->is_record) {
|
|
cmd_data = json_object_new_object();
|
|
echo_cloud_msg_http_req(&p->http, DEVICE_HEARTBEAT, cmd_data);
|
|
}
|
|
}
|
|
|
|
static void echo_cloud_device_status_sync(void *priv)
|
|
{
|
|
struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv;
|
|
json_object *cmd_data = NULL;
|
|
|
|
if (!p->is_record) {
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "light", json_object_new_int(get_app_music_light_intensity()));
|
|
echo_cloud_msg_http_req(&p->http, LIGHT_CHANGED, cmd_data);
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "battery", json_object_new_int(get_app_music_battery_power()));
|
|
echo_cloud_msg_http_req(&p->http, BATTERY_CHANGED, cmd_data);
|
|
}
|
|
}
|
|
|
|
static void get_device_mac_address(char *mac_address, u32 str_len)
|
|
{
|
|
char *p = mac_address;
|
|
char mac_addr[6];
|
|
|
|
extern int wifi_get_mac(char *mac_addr);
|
|
wifi_get_mac(mac_addr);
|
|
|
|
for (int i = 0; i < 6 && i < str_len / 3; i++) {
|
|
*(p++) = *((char *)"0123456789ABCDEF" + ((mac_addr[i] >> 4) & 0x0f));
|
|
*(p++) = *((char *)"0123456789ABCDEF" + (mac_addr[i] & 0x0f));
|
|
*(p++) = ':';
|
|
}
|
|
*(--p) = '\0';
|
|
}
|
|
|
|
static void echo_cloud_media_stop_notify(struct echo_cloud_hdl *p, int offset)
|
|
{
|
|
if (p->media_play_state != ECHO_CLOUD_MEDIA_STATE_STOP) {
|
|
json_object *cmd_data = json_object_new_object();
|
|
p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP;
|
|
json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token));
|
|
json_object_object_add(cmd_data, "offset", json_object_new_int(offset * 1000));
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_STOPPED, cmd_data);
|
|
}
|
|
}
|
|
|
|
static void echo_cloud_update_info(struct echo_cloud_hdl *p)
|
|
{
|
|
json_object *cmd_data = NULL;
|
|
|
|
char mac_address[18];
|
|
get_device_mac_address(mac_address, 18);
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "firmware_version", json_object_new_string(get_echo_cloud_firmware_version()));
|
|
json_object_object_add(cmd_data, "mac_address", json_object_new_string(mac_address));
|
|
json_object_object_add(cmd_data, "reconnect", json_object_new_boolean(true));
|
|
echo_cloud_msg_http_req(&p->http, DEVICE_ONLINE, cmd_data);
|
|
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "volume", json_object_new_int(get_app_music_volume()));
|
|
echo_cloud_msg_http_req(&p->http, VOLUME_CHANGED, cmd_data);
|
|
|
|
static const char input_text[] = {
|
|
0xE6, 0x92, 0xAD, 0xE6, 0x94, 0xBE, 0xE5, 0x84, 0xBF, 0xE6, 0xAD, 0x8C, 0x00
|
|
};
|
|
if (ai_platform_if_support_poweron_recommend()) {
|
|
cmd_data = json_object_new_object();
|
|
/* json_object_object_add(cmd_data, "text", json_object_new_string("播放黄凯芹的歌")); */
|
|
json_object_object_add(cmd_data, "text", json_object_new_string(input_text));
|
|
p->media_play_opt = MEDIA_IGNORE_SPEEK;
|
|
echo_cloud_msg_http_req(&p->http, AI_DIALOG_INPUT, cmd_data);
|
|
p->media_play_opt = MEDIA_HAS_PALY;
|
|
}
|
|
|
|
p->heartbeat_timer = sys_timer_add(p, echo_cloud_heart_beat, 5000);
|
|
p->sync_timer = sys_timer_add(p, echo_cloud_device_status_sync, 90000);
|
|
echo_cloud_device_status_sync(p);
|
|
}
|
|
|
|
static void echo_cloud_app_task(void *priv)
|
|
{
|
|
struct echo_cloud_hdl *p = (struct echo_cloud_hdl *)priv;
|
|
int msg[32];
|
|
int err;
|
|
json_object *cmd_data = NULL;
|
|
|
|
memset(p, 0, sizeof(struct echo_cloud_hdl));
|
|
|
|
#if 0
|
|
strcpy(p->auth.device_id, "AI000000000000000000000000000000");
|
|
strcpy(p->auth.channel_uuid, "2900a1f8-d1d0-41ad-90af-b59779210b28");
|
|
strcpy(p->auth.hash, "BKsY0nCiD6tpng4sldALH1kjWuV3BnfysuVzypbC");
|
|
#else
|
|
while (0 != get_echo_cloud_profile(p)) {
|
|
puts("echo_cloud get profile fail ! \n");
|
|
os_time_dly(50);
|
|
if (__this->exit_flag) {
|
|
__this->exit_flag = 0;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
p->http.auth = &p->auth;
|
|
p->http.priv = p;
|
|
p->http.timeout_ms = 5000;
|
|
p->http.out_cb = echo_cloud_rcv_json_handler;
|
|
p->media_play_opt = MEDIA_HAS_PALY;
|
|
p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP;
|
|
|
|
os_mutex_pend(&__this->mutex, 0);
|
|
if (__this->exit_flag || echo_cloud_mqtt_init(&p->auth)) {
|
|
os_mutex_post(&__this->mutex);
|
|
__this->exit_flag = 0;
|
|
return;
|
|
}
|
|
os_mutex_post(&__this->mutex);
|
|
|
|
echo_cloud_update_info(p);
|
|
|
|
__this->connect_status = 1;
|
|
|
|
while (1) {
|
|
err = os_taskq_pend("taskq", msg, ARRAY_SIZE(msg));
|
|
if (err != OS_TASKQ || msg[0] != Q_USER) {
|
|
continue;
|
|
}
|
|
|
|
if (__this->exit_flag) {
|
|
if (msg[1] == ECHO_CLOUD_MQTT_MSG_DEAL) {
|
|
free((void *)msg[2]);
|
|
continue;
|
|
} else if (msg[1] != ECHO_CLOUD_QUIT) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (msg[1]) {
|
|
case ECHO_CLOUD_RECORD_START:
|
|
if (!p->is_record) {
|
|
u8 voice_mode = msg[2] & 0x3;
|
|
if (0 == echo_cloud_enc_http_start(&p->http, voice_mode)) {
|
|
echo_cloud_recorder_start(8000, msg[2], msg[2] & VAD_ENABLE);
|
|
__this->msg_notify_disable = 1;
|
|
p->is_record = true;
|
|
}
|
|
}
|
|
break;
|
|
case ECHO_CLOUD_RECORD_SEND:
|
|
if (p->is_record) {
|
|
err = echo_cloud_enc_http_send(&p->http, (void *)msg[2], msg[3]);
|
|
os_sem_post((OS_SEM *)msg[4]);
|
|
if (err) {
|
|
echo_cloud_recorder_stop();
|
|
echo_cloud_enc_http_close(&p->http);
|
|
p->is_record = false;
|
|
__this->msg_notify_disable = 0;
|
|
}
|
|
}
|
|
break;
|
|
case ECHO_CLOUD_RECORD_STOP:
|
|
if (p->is_record) {
|
|
echo_cloud_recorder_stop();
|
|
echo_cloud_send_last_packet(&p->http);
|
|
echo_cloud_enc_http_close(&p->http);
|
|
p->is_record = false;
|
|
__this->msg_notify_disable = 0;
|
|
}
|
|
break;
|
|
case ECHO_CLOUD_RECORD_ERR:
|
|
if (p->is_record) {
|
|
echo_cloud_recorder_stop();
|
|
echo_cloud_enc_http_close(&p->http);
|
|
p->is_record = false;
|
|
__this->msg_notify_disable = 0;
|
|
if (__this->voice_mode != WECHAT_MODE) {
|
|
ai_server_event_notify(&echo_cloud_sdk_api, NULL, AI_SERVER_EVENT_REC_ERR);
|
|
}
|
|
}
|
|
break;
|
|
case ECHO_CLOUD_PREVIOUS_SONG:
|
|
puts("===========ECHO_CLOUD_PREVIOUS_SONG\n");
|
|
p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP;
|
|
p->media_play_opt = MEDIA_PLAY_IMMEDIATELY;
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token));
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_PREVIOUS_DEVICE, cmd_data);
|
|
p->media_play_opt = MEDIA_HAS_PALY;
|
|
break;
|
|
case ECHO_CLOUD_MEDIA_END:
|
|
case ECHO_CLOUD_NEXT_SONG:
|
|
puts("===========ECHO_CLOUD_NEXT_SONG\n");
|
|
p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP;
|
|
p->media_play_opt = MEDIA_PLAY_IMMEDIATELY;
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token));
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_NEXT_DEVICE, cmd_data);
|
|
p->media_play_opt = MEDIA_HAS_PALY;
|
|
break;
|
|
case ECHO_CLOUD_MEDIA_STOP:
|
|
echo_cloud_media_stop_notify(p, msg[2]);
|
|
break;
|
|
case ECHO_CLOUD_SPEAK_END:
|
|
puts("===========ECHO_CLOUD_SPEAK_END\n");
|
|
/* cmd_data = json_object_new_object(); */
|
|
/* json_object_object_add(cmd_data, "token", json_object_new_string(p->token.ai_dialog_token)); */
|
|
/* echo_cloud_msg_http_req(&p->http, AI_DIALOG_FINISHED, cmd_data); */
|
|
if (MEDIA_WAIT_PLAY == p->media_play_opt && p->media_url) {
|
|
p->media_play_opt = MEDIA_HAS_PALY;
|
|
JL_echo_cloud_media_audio_play(p->media_url);
|
|
p->media_play_state = ECHO_CLOUD_MEDIA_STATE_START;
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token));
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_STARTED, cmd_data);
|
|
}
|
|
break;
|
|
case ECHO_CLOUD_PLAY_PAUSE:
|
|
if (p->media_play_state != ECHO_CLOUD_MEDIA_STATE_STOP) {
|
|
int is_pause = msg[2];
|
|
cmd_data = json_object_new_object();
|
|
if (is_pause) {
|
|
p->media_play_state = ECHO_CLOUD_MEDIA_STATE_PAUSE;
|
|
json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token));
|
|
json_object_object_add(cmd_data, "offset", json_object_new_int(1000 * get_app_music_playtime()));
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_PAUSED, cmd_data);
|
|
} else {
|
|
p->media_play_state = ECHO_CLOUD_MEDIA_STATE_START;
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_RESUMED, cmd_data);
|
|
}
|
|
}
|
|
break;
|
|
case ECHO_CLOUD_VOLUME_CHANGE:
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "volume", json_object_new_int(msg[2]));
|
|
echo_cloud_msg_http_req(&p->http, VOLUME_CHANGED, cmd_data);
|
|
break;
|
|
case ECHO_CLOUD_COLLECT_RESOURCE:
|
|
;
|
|
int is_play_collect = msg[2];
|
|
if (!is_play_collect && p->media_url) {
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "token", json_object_new_string(p->token.media_play_token));
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_FAVORITE_DEVICE, cmd_data);
|
|
}
|
|
if (is_play_collect) {
|
|
p->media_play_state = ECHO_CLOUD_MEDIA_STATE_STOP;
|
|
p->media_play_opt = MEDIA_PLAY_IMMEDIATELY;
|
|
cmd_data = json_object_new_object();
|
|
echo_cloud_msg_http_req(&p->http, MEDIA_PLAYER_PLAY_FAVORITE_DEVICE, cmd_data);
|
|
p->media_play_opt = MEDIA_HAS_PALY;
|
|
}
|
|
break;
|
|
case ECHO_CLOUD_CHILD_LOCK_CHANGE:
|
|
cmd_data = json_object_new_object();
|
|
json_object_object_add(cmd_data, "child_lock", json_object_new_boolean(msg[2]));
|
|
echo_cloud_msg_http_req(&p->http, CHILD_LOCK_CHANGED, cmd_data);
|
|
break;
|
|
case ECHO_CLOUD_DEVICE_CODE_REPORT:
|
|
echo_cloud_media_stop_notify(p, msg[2]);
|
|
cmd_data = json_object_new_object();
|
|
echo_cloud_msg_http_req(&p->http, DEVICE_REQUEST_CODE, cmd_data);
|
|
break;
|
|
case ECHO_CLOUD_MQTT_MSG_DEAL:
|
|
if (p->media_play_opt != MEDIA_WAIT_PLAY) {
|
|
p->media_play_opt = MEDIA_PLAY_IMMEDIATELY;
|
|
p->http.out_cb(p, (const char *)msg[2]);
|
|
p->media_play_opt = MEDIA_HAS_PALY;
|
|
} else {
|
|
p->http.out_cb(p, (const char *)msg[2]);
|
|
}
|
|
free((void *)msg[2]);
|
|
break;
|
|
case ECHO_CLOUD_QUIT:
|
|
sys_timer_del(p->heartbeat_timer);
|
|
sys_timer_del(p->sync_timer);
|
|
if (p->is_record) {
|
|
echo_cloud_recorder_stop();
|
|
echo_cloud_enc_http_close(&p->http);
|
|
p->is_record = false;
|
|
}
|
|
echo_cloud_msg_http_close(&p->http);
|
|
/* p->http.timeout_ms = 1000; */
|
|
/* cmd_data = json_object_new_object(); */
|
|
/* echo_cloud_msg_http_req(&p->http, DEVICE_OFFLINE, cmd_data); */
|
|
free(p->media_url);
|
|
__this->msg_notify_disable = 0;
|
|
__this->exit_flag = 0;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int echo_cloud_app_init(void)
|
|
{
|
|
__this->exit_flag = 0;
|
|
__this->connect_status = 0;
|
|
|
|
return thread_fork("echo_cloud_app_task", 22, 1024, 128, &__this->app_task_pid, echo_cloud_app_task, &__this->hdl);
|
|
}
|
|
|
|
static void echo_cloud_app_uninit(void)
|
|
{
|
|
__this->exit_flag = 1;
|
|
__this->connect_status = 0;
|
|
|
|
os_mutex_pend(&__this->mutex, 0);
|
|
echo_cloud_mqtt_uninit();
|
|
os_mutex_post(&__this->mutex);
|
|
|
|
do {
|
|
if (OS_Q_FULL != os_taskq_post("echo_cloud_app_task", 1, ECHO_CLOUD_QUIT)) {
|
|
break;
|
|
}
|
|
puts("echo_cloud_app_task send msg QUIT timeout \n");
|
|
http_cancel_dns(&__this->hdl.http.ctx);
|
|
http_cancel_dns(&__this->hdl.http.rec_ctx);
|
|
os_time_dly(2);
|
|
} while (1);
|
|
|
|
while (__this->exit_flag) {
|
|
http_cancel_dns(&__this->hdl.http.ctx);
|
|
http_cancel_dns(&__this->hdl.http.rec_ctx);
|
|
}
|
|
|
|
thread_kill(&__this->app_task_pid, KILL_WAIT);
|
|
}
|
|
|
|
static int echo_cloud_sdk_open(void)
|
|
{
|
|
os_sem_create(&__this->sem, 0);
|
|
os_mutex_create(&__this->mutex);
|
|
return echo_cloud_app_init();
|
|
}
|
|
|
|
static int echo_cloud_sdk_check(void)
|
|
{
|
|
if (__this->connect_status) {
|
|
return AI_STAT_CONNECTED;
|
|
}
|
|
|
|
return AI_STAT_DISCONNECTED;
|
|
}
|
|
|
|
static int echo_cloud_sdk_disconnect(void)
|
|
{
|
|
echo_cloud_recorder_stop();
|
|
|
|
echo_cloud_app_uninit();
|
|
|
|
if (__this->enc_server) {
|
|
server_close(__this->enc_server);
|
|
__this->enc_server = NULL;
|
|
}
|
|
os_sem_del(&__this->sem, 1);
|
|
os_mutex_del(&__this->mutex, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
REGISTER_AI_SDK(echo_cloud_sdk_api) = {
|
|
.name = "echo_cloud",
|
|
.connect = echo_cloud_sdk_open,
|
|
.state_check = echo_cloud_sdk_check,
|
|
.do_event = echo_cloud_sdk_do_event,
|
|
.disconnect = echo_cloud_sdk_disconnect,
|
|
};
|