From 0c27f232681d21c032a6d9863c69828bbc7dfa2e Mon Sep 17 00:00:00 2001 From: cheng Date: Thu, 18 Sep 2025 19:52:16 +0800 Subject: [PATCH] =?utf8?q?=E5=88=A0=E9=99=A4=E5=86=97=E4=BD=99=E4=BB=A3?= =?utf8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../leiniao/include/ln_story_player_simple.h | 133 ---- .../leiniao/src/ln_story_player_simple.cpp | 731 ------------------ .../leiniao/include/ln_story_player_simple.h | 133 ---- .../leiniao/src/ln_story_player_simple.cpp | 731 ------------------ code/sdk/zhihui/leiniao/imsl_ln/ln_api.c | 61 +- .../sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c | 719 +++++++++++++++++ code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c | 8 +- .../sdk/zhihui/leiniao/imsl_ln/ln_api_token.c | 2 +- .../leiniao_python_test/device_info_apis_4.py | 2 +- .../device_websocket_client_6.py | 2 +- .../leiniao_python_test/get_device_token_1.py | 2 +- .../integrated_speech_chatstream_2.py | 2 +- .../leiniao_python_test/story_apis_test_3.py | 2 +- .../ln_api_cpp}/ln_story_player_simple.cpp | 0 .../ln_api_cpp}/ln_story_player_simple.h | 0 15 files changed, 732 insertions(+), 1796 deletions(-) delete mode 100644 code/sdk/application/wearable/fd272/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h delete mode 100644 code/sdk/application/wearable/fd272/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp delete mode 100644 code/sdk/application/wearable/n9/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h delete mode 100644 code/sdk/application/wearable/n9/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp create mode 100644 code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c rename code/sdk/{application/wearable/fd230/nativeapp/nativeui/leiniao/src => zhihui/leiniao/ln_api_cpp}/ln_story_player_simple.cpp (100%) rename code/sdk/{application/wearable/fd230/nativeapp/nativeui/leiniao/include => zhihui/leiniao/ln_api_cpp}/ln_story_player_simple.h (100%) diff --git a/code/sdk/application/wearable/fd272/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h b/code/sdk/application/wearable/fd272/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h deleted file mode 100644 index e5ba5d8d5..000000000 --- a/code/sdk/application/wearable/fd272/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef LN_STORY_PLAYER_SIMPLE_H -#define LN_STORY_PLAYER_SIMPLE_H - -#ifdef __cplusplus -#if __cplusplus -extern "C" -{ -#endif -#endif - -#include "stdint.h" -#include "errcode.h" -#include - -/* ============================== 精简版故事播放器接口 ============================== */ - -/** - * @brief 开始播放指定目录下的音频文件(顺序播放) - * @param story_dir 故事文件目录路径 - * @return true 成功,false 失败 - * - * 功能说明: - * - 自动扫描目录下的音频文件(支持mp3, wav, aac, m4a格式) - * - 按文件名顺序播放所有音频文件 - * - 播放完一个文件后自动播放下一个文件 - * - 音频系统只初始化一次,提高效率 - */ -bool ln_story_player_simple_start(const char *story_dir); - -/** - * @brief 停止播放并清理所有资源 - * @return 无返回值 - * - * 功能说明: - * - 立即停止当前播放 - * - 清理所有音频资源 - * - 重置播放器状态 - */ -void ln_story_player_simple_stop(void); - -/** - * @brief 检查是否正在播放 - * @return true 正在播放,false 未播放 - */ -bool ln_story_player_simple_is_playing(void); - -/** - * @brief 获取当前播放的文件索引 - * @return 当前播放的文件索引(从0开始),-1表示未播放 - */ -int ln_story_player_simple_get_current_index(void); - -/** - * @brief 获取总文件数 - * @return 扫描到的音频文件总数 - */ -int ln_story_player_simple_get_file_count(void); - -/** - * @brief 播放指定音频文件 - * @param file_path 音频文件完整路径 - * @return true 成功,false 失败 - * - * 功能说明: - * - 播放指定的单个音频文件 - * - 不会自动播放下一个文件 - * - 需要手动调用停止或暂停 - */ -bool ln_story_player_simple_play_file(const char *file_path); - -/** - * @brief 暂停播放 - * @return true 成功,false 失败 - * - * 功能说明: - * - 暂停当前正在播放的音频 - * - 保持播放器状态,可以恢复播放 - * - 不会清理音频资源 - */ -bool ln_story_player_simple_pause(void); - -/** - * @brief 恢复播放 - * @return true 成功,false 失败 - * - * 功能说明: - * - 恢复之前暂停的音频播放 - * - 只能在暂停状态下调用 - * - 如果当前没有暂停的音频,返回失败 - */ -bool ln_story_player_simple_resume(void); - -/** - * @brief 检查是否暂停 - * @return true 已暂停,false 未暂停 - */ -bool ln_story_player_simple_is_paused(void); - -/** - * @brief 处理播放器消息队列命令(内部使用) - * @param cmd 命令类型 - * @param data 数据指针 - * @param len 数据长度 - */ -void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len); - -/* ============================== 使用示例 ============================== */ - -/* -// 开始播放 -if (ln_story_player_simple_start("/user/leiniao/story/choose")) -{ - printf("播放开始成功\n"); -} - -// 检查播放状态 -if (ln_story_player_simple_is_playing()) -{ - printf("正在播放第 %d 个文件,共 %d 个文件\n", - ln_story_player_simple_get_current_index(), - ln_story_player_simple_get_file_count()); -} - -// 停止播放 -ln_story_player_simple_stop(); -*/ - -#ifdef __cplusplus -#if __cplusplus -} -#endif -#endif -#endif diff --git a/code/sdk/application/wearable/fd272/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp b/code/sdk/application/wearable/fd272/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp deleted file mode 100644 index 441ae6eca..000000000 --- a/code/sdk/application/wearable/fd272/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp +++ /dev/null @@ -1,731 +0,0 @@ -#include "leiniao/include/ln_story_player_simple.h" -#include -#include -#include -#include -#include -#include -#include -#include -/* 引入播放器相关头文件 */ -#include "player.h" -#include "source.h" -#include "audio_manager.h" -#include -#include -#include -#include -/* 引入消息队列相关头文件 */ -#include "ln_api.h" - -#ifdef __cplusplus -#if __cplusplus -extern "C" -{ -#endif /* __cplusplus */ -#endif /* __cplusplus */ - -using OHOS::Media::Player; -using OHOS::Media::Source; -using namespace OHOS::Media; -using namespace::Audio; -using Audio::AudioManager; - -/* ============================== 精简版播放器结构体 ============================== */ - -/* 播放器消息命令定义 */ - - -typedef struct -{ - /* 文件列表 */ - char file_paths[100][256]; // 最多100个文件 - int file_count; - int current_index; - - /* 播放器对象 */ - std::shared_ptr player; - std::shared_ptr source; - std::map header; - - /* 音频管理 */ - AudioSession sessionId; - std::shared_ptr interrupt; - std::shared_ptr interruptListener; - bool audio_initialized; - bool is_activate_audio_interrupt; // 音频中断激活标志 - - /* 状态标志 */ - bool is_playing; - bool should_stop; - bool is_paused; // 暂停状态标志 -} ln_story_player_simple_t; - - -static bool ln_story_player_simple_play_file(ln_story_player_simple_t *impl, int file_index); -static bool ln_story_player_simple_player_stat(ln_story_player_simple_t *impl, int file_index); - -/* ============================== 播放器回调类 ============================== */ - -class SimplePlayerCallback : public PlayerCallback -{ -public: - SimplePlayerCallback(ln_story_player_simple_t *impl) : impl_(impl) {} - - void OnPlaybackComplete() override - { - if (impl_ && !impl_->should_stop) - { - printf("[SIMPLE_PLAYER] playback completed, scheduling next file\n"); - // 通过消息队列播放下一个文件,避免在回调中直接调用 - impl_->current_index++; - if (impl_->current_index < impl_->file_count) - { - // 发送消息到 ln_http_server_send_task 线程 - do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT, NULL, 0); - } - else - { - printf("[SIMPLE_PLAYER] all files played, stopping(所有文件播放完成)\n"); - do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_STOP, NULL, 0); - impl_->is_playing = false; - } - } - } - - void OnError(int32_t errorType, int32_t errorCode) override - { - printf("[SIMPLE_PLAYER] playback error: type=%d, code=%d\n", errorType, errorCode); - if (impl_) - { - impl_->is_playing = false; - } - } - - void OnInfo(int32_t type, int32_t extra) override {} - void OnRewindToComplete() override {} - -private: - ln_story_player_simple_t *impl_; -}; - -/* ============================== 中断监听器类 ============================== */ - -class SimplePlayerInterruptListener : public InterruptListener -{ -public: - SimplePlayerInterruptListener(ln_story_player_simple_t *impl) : impl_(impl) {} - - void OnInterrupt(int32_t type, int32_t hint) override - { - if (!impl_) - return; - - printf("[SIMPLE_PLAYER] OnInterrupt: type=%d, hint=%d\n", type, hint); - - if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_PAUSE) - { - if (impl_->is_playing && impl_->player) - { - impl_->player->Pause(); - } - } - else if (type == INTERRUPT_TYPE_END && hint == INTERRUPT_HINT_RESUME) - { - if (impl_->is_playing && impl_->player) - { - impl_->player->Play(); - } - } - else if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_STOP) - { - if (impl_->is_playing) - { - impl_->player->Stop(); - impl_->is_playing = false; - } - } - } - -private: - ln_story_player_simple_t *impl_; -}; - -/* ============================== 全局变量 ============================== */ - -static ln_story_player_simple_t g_simple_player = {0}; -static std::shared_ptr g_player_callback = nullptr; -static std::shared_ptr g_interrupt_listener = nullptr; - -/* ============================== 内部函数 ============================== */ - -// 处理播放器消息队列命令 -void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len) -{ - if (cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT) - { - printf("[SIMPLE_PLAYER] received play next command\n"); - if (g_simple_player.is_playing && !g_simple_player.should_stop) - { - if (g_simple_player.current_index < g_simple_player.file_count) - { - ln_story_player_simple_play_file(&g_simple_player, g_simple_player.current_index); - } - } - } -} - -// 音频初始化(只初始化一次) -static bool AudioInit(void) -{ - if (g_simple_player.audio_initialized) - { - return true; - } - - AudioManager& amInstance = AudioManager::GetInstance(); - if (!amInstance.Initialize()) - { - printf("[SIMPLE_PLAYER] AudioManager Initialize failed\n"); - return false; - } - - g_simple_player.player = std::make_shared(); - if (!g_simple_player.player) - { - printf("[SIMPLE_PLAYER] Player create failed\n"); - return false; - } - - g_player_callback = std::make_shared(&g_simple_player); - if (!g_player_callback) - { - printf("[SIMPLE_PLAYER] PlayerCallback create failed\n"); - return false; - } - - g_simple_player.audio_initialized = true; - g_simple_player.is_activate_audio_interrupt = false; - - printf("[SIMPLE_PLAYER] Audio initialized successfully\n"); - return true; -} - -// 比较函数,用于文件名排序 -static int CompareFileNames(const void *a, const void *b) -{ - const char *file_a = (const char *)a; - const char *file_b = (const char *)b; - - // 提取文件名(去掉路径) - const char *name_a = strrchr(file_a, '/'); - const char *name_b = strrchr(file_b, '/'); - - if (name_a) name_a++; - else name_a = file_a; - if (name_b) name_b++; - else name_b = file_b; - - // 提取数字部分进行比较 - int num_a = atoi(name_a); - int num_b = atoi(name_b); - - return num_a - num_b; -} - -// 扫描目录获取音频文件 -static bool ScanAudioFiles(const char *story_dir) -{ - printf("[SIMPLE_PLAYER] scanning directory: %s\n", story_dir); - - // 检查目录是否存在 - if (access(story_dir, F_OK) == -1) - { - printf("[SIMPLE_PLAYER] directory does not exist: %s\n", story_dir); - return false; - } - - DIR *dir = opendir(story_dir); - if (!dir) - { - printf("[SIMPLE_PLAYER] failed to open directory: %s\n", story_dir); - return false; - } - - g_simple_player.file_count = 0; - struct dirent *entry; - - while ((entry = readdir(dir)) != NULL && g_simple_player.file_count < 100) - { - // 跳过 . 和 .. 目录 - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - { - continue; - } - - // 检查是否是音频文件 - const char *ext = strrchr(entry->d_name, '.'); - if (ext && (strcasecmp(ext, ".mp3") == 0 || strcasecmp(ext, ".wav") == 0 || - strcasecmp(ext, ".aac") == 0 || strcasecmp(ext, ".m4a") == 0)) - { - snprintf(g_simple_player.file_paths[g_simple_player.file_count], - sizeof(g_simple_player.file_paths[g_simple_player.file_count]), - "%s/%s", story_dir, entry->d_name); - g_simple_player.file_count++; - printf("[SIMPLE_PLAYER] found audio file: %s\n", entry->d_name); - } - } - - closedir(dir); - - // 对文件路径进行排序(按文件名中的数字排序) - if (g_simple_player.file_count > 1) - { - qsort(g_simple_player.file_paths, g_simple_player.file_count, - sizeof(g_simple_player.file_paths[0]), CompareFileNames); - printf("[SIMPLE_PLAYER] files sorted by name\n"); - } - - printf("[SIMPLE_PLAYER] found %d audio files\n", g_simple_player.file_count); - return g_simple_player.file_count > 0; -} - -// 播放指定文件 -static bool ln_story_player_simple_play_file(ln_story_player_simple_t *impl, int file_index) -{ - if (file_index < 0 || file_index >= impl->file_count) - { - printf("[SIMPLE_PLAYER] invalid file index: %d\n", file_index); - return false; - } - - printf("[SIMPLE_PLAYER] playing file %d: %s\n", file_index, impl->file_paths[file_index]); - - // 检查播放器状态 - int32_t player_state; - impl->player->GetPlayerState(player_state); - printf("[SIMPLE_PLAYER] current player state: %d\n", player_state); - - // 根据播放器状态进行处理 - if (player_state == PLAYER_STARTED || player_state == PLAYER_PLAYBACK_COMPLETE) - { - printf("[SIMPLE_PLAYER] stopping and resetting player before switching\n"); - impl->player->Stop(); - impl->player->Reset(); - // 然后调用PlayerStat来重新播放 - return ln_story_player_simple_player_stat(impl, file_index); - } - else if (player_state == PLAYER_PAUSED || player_state == PLAYER_IDLE) - { - printf("[SIMPLE_PLAYER] player is ready, calling PlayerStat\n"); - impl->player->Reset(); - return ln_story_player_simple_player_stat(impl, file_index); - } - else - { - printf("[SIMPLE_PLAYER] unknown state, forcing reset and calling PlayerStat\n"); - impl->player->Stop(); - impl->player->Reset(); - return ln_story_player_simple_player_stat(impl, file_index); - } -} - -// 播放器状态设置和播放(类似PlayersModel::PlayerStat) -static bool ln_story_player_simple_player_stat(ln_story_player_simple_t *impl, int file_index) -{ - printf("[SIMPLE_PLAYER] ln_story_player_simple_player_stat\n"); - int32_t ret; - - // 设置回调函数 - impl->player->SetPlayerCallback(g_player_callback); - - // 设置音频源 - impl->source = std::make_shared(impl->file_paths[file_index], impl->header); - if (!impl->source) - { - printf("[SIMPLE_PLAYER] failed to create source\n"); - return false; - } - - ret = impl->player->SetSource(*impl->source); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] set source failed: %d\n", ret); - impl->player->Reset(); // 只在SetSource失败时Reset - return false; - } - - // 配置播放器 - impl->player->SetAudioStreamType(AUDIO_STREAM_MUSIC); - - // 准备播放 - ret = impl->player->Prepare(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] prepare failed: %d\n", ret); - impl->player->Reset(); // 只在Prepare失败时Reset - return false; - } - - // 设置音频会话和中断监听(只在第一次播放时申请) - AudioManager &amInstance = AudioManager::GetInstance(); - - if (!impl->is_activate_audio_interrupt) - { - // 创建新的会话和中断(只在第一次播放时创建) - impl->sessionId = amInstance.MakeSessionId(); - if (impl->sessionId == AUDIO_SESSION_ID_NONE) - { - printf("[SIMPLE_PLAYER] failed to create session\n"); - impl->player->Reset(); - return false; - } - - impl->interruptListener = std::make_shared(impl); - impl->interrupt = std::make_shared(); - impl->interrupt->streamType = AUDIO_STREAM_MUSIC; - impl->interrupt->sessionID = impl->sessionId; - impl->interrupt->interruptListener = impl->interruptListener; - - if (amInstance.ActivateAudioInterrupt(*impl->interrupt) == INTERRUPT_FAILED) - { - printf("[SIMPLE_PLAYER] failed to activate audio interrupt\n"); - impl->player->Reset(); - return false; - } - - impl->is_activate_audio_interrupt = true; - printf("[SIMPLE_PLAYER] audio interrupt activated for first file\n"); - } - else - { - printf("[SIMPLE_PLAYER] reusing existing audio interrupt for file %d\n", file_index); - } - - // 设置音频会话ID - ret = impl->player->SetAudioSessionId(impl->sessionId); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] SetAudioSessionId failed: %d\n", ret); - impl->player->Reset(); - // 只在第一次播放失败时才清理音频中断 - if (impl->is_activate_audio_interrupt) - { - amInstance.DeactivateAudioInterrupt(*impl->interrupt); - impl->is_activate_audio_interrupt = false; - } - return false; - } - - // 开始播放 - ret = impl->player->Play(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] play failed: %d\n", ret); - impl->player->Stop(); - impl->player->Reset(); - // 只在第一次播放失败时才清理音频中断 - if (impl->is_activate_audio_interrupt) - { - amInstance.DeactivateAudioInterrupt(*impl->interrupt); - impl->is_activate_audio_interrupt = false; - } - return false; - } - - impl->is_playing = true; - impl->is_paused = false; // 重置暂停状态 - printf("[SIMPLE_PLAYER] started playing file %d\n", file_index); - return true; -} - -// 清理音频中断 -static void ClearActivateAudioInterrupt(void) -{ - if (g_simple_player.is_activate_audio_interrupt && g_simple_player.interrupt && g_simple_player.sessionId != AUDIO_SESSION_ID_NONE) - { - AudioManager &amInstance = AudioManager::GetInstance(); - amInstance.DeactivateAudioInterrupt(*g_simple_player.interrupt); - g_simple_player.is_activate_audio_interrupt = false; - printf("[SIMPLE_PLAYER] audio interrupt cleared\n"); - } -} - -// 清理资源 -static void CleanupResources(void) -{ - if (g_simple_player.player) - { - g_simple_player.player->Stop(); - g_simple_player.player->Reset(); - g_simple_player.player->Release(); - g_simple_player.player.reset(); - } - - if (g_simple_player.is_activate_audio_interrupt && g_simple_player.interrupt && g_simple_player.sessionId != AUDIO_SESSION_ID_NONE) - { - AudioManager &amInstance = AudioManager::GetInstance(); - amInstance.DeactivateAudioInterrupt(*g_simple_player.interrupt); - g_simple_player.is_activate_audio_interrupt = false; - printf("[SIMPLE_PLAYER] audio interrupt deactivated\n"); - } - - g_simple_player.interrupt.reset(); - g_simple_player.interruptListener.reset(); - g_player_callback.reset(); - - memset(&g_simple_player, 0, sizeof(g_simple_player)); - printf("[SIMPLE_PLAYER] resources cleaned up\n"); -} - -/* ============================== 公共接口 ============================== */ - -// 开始播放指定目录下的音频文件(顺序播放) -bool ln_story_player_simple_start(const char *story_dir) -{ - if (!story_dir) - { - printf("[SIMPLE_PLAYER] invalid story directory\n"); - return false; - } - - printf("[SIMPLE_PLAYER] starting playback: %s\n", story_dir); - - // 如果已经在播放,先停止 - if (g_simple_player.is_playing) - { - printf("[SIMPLE_PLAYER] stopping playback\n"); - ln_story_player_simple_stop(); - } - - // 初始化音频(只初始化一次) - if (!AudioInit()) - { - printf("[SIMPLE_PLAYER] audio init failed\n"); - return false; - } - - // 扫描音频文件 - if (!ScanAudioFiles(story_dir)) - { - printf("[SIMPLE_PLAYER] no audio files found\n"); - return false; - } - - // 开始播放第一个文件 - g_simple_player.current_index = 0; - g_simple_player.should_stop = false; - - if (!ln_story_player_simple_play_file(&g_simple_player, 0)) - { - printf("[SIMPLE_PLAYER] failed to start playing\n"); - return false; - } - - printf("[SIMPLE_PLAYER] playback started successfully\n"); - return true; -} - -// 停止播放 -void ln_story_player_simple_stop(void) -{ - printf("[SIMPLE_PLAYER] ln_story_player_simple_stop\n"); - - g_simple_player.should_stop = true; - g_simple_player.is_playing = false; - g_simple_player.is_paused = false; - - if (!g_simple_player.player) - { - printf("[SIMPLE_PLAYER] player is null, nothing to stop\n"); - return; - } - - // 检查播放器状态 - int32_t player_state; - int32_t ret = g_simple_player.player->GetPlayerState(player_state); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] get player state failed: %d\n", ret); - // 即使获取状态失败,也要尝试清理 - } - else - { - printf("[SIMPLE_PLAYER] current player state: %d\n", player_state); - } - - // 根据播放器状态进行处理(参考PlayersModel::PlayerStop) - if (player_state == PLAYER_STARTED || player_state == PLAYER_PLAYBACK_COMPLETE) - { - printf("[SIMPLE_PLAYER] stopping and resetting player\n"); - ret = g_simple_player.player->Stop(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] stop failed: %d\n", ret); - } - - ret = g_simple_player.player->Reset(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] reset failed: %d\n", ret); - } - } - else if (player_state == PLAYER_PAUSED) - { - printf("[SIMPLE_PLAYER] resetting paused player\n"); - ret = g_simple_player.player->Reset(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] reset failed: %d\n", ret); - } - } - else if (player_state == PLAYER_STOPPED) - { - printf("[SIMPLE_PLAYER] player already stopped\n"); - } - else - { - printf("[SIMPLE_PLAYER] unknown state, forcing stop and reset\n"); - g_simple_player.player->Stop(); - g_simple_player.player->Reset(); - } - // 清理音频中断 - ClearActivateAudioInterrupt(); - - - printf("[SIMPLE_PLAYER] playback stopped\n"); -} - -// 检查是否正在播放 -bool ln_story_player_simple_is_playing(void) -{ - return g_simple_player.is_playing; -} - -// 获取当前播放的文件索引 -int ln_story_player_simple_get_current_index(void) -{ - return g_simple_player.current_index; -} - -// 获取总文件数 -int ln_story_player_simple_get_file_count(void) -{ - return g_simple_player.file_count; -} - -// 播放指定音频文件 -bool ln_story_player_simple_play_file(const char *file_path) -{ - if (!file_path) - { - printf("[SIMPLE_PLAYER] invalid file path\n"); - return false; - } - - printf("[SIMPLE_PLAYER] playing single file: %s\n", file_path); - - // 如果已经在播放,先停止 - if (g_simple_player.is_playing) - { - printf("[SIMPLE_PLAYER] stopping current playback\n"); - ln_story_player_simple_stop(); - } - - // 初始化音频(只初始化一次) - if (!AudioInit()) - { - printf("[SIMPLE_PLAYER] audio init failed\n"); - return false; - } - - // 设置单个文件播放模式 - g_simple_player.file_count = 1; - g_simple_player.current_index = 0; - g_simple_player.should_stop = false; - g_simple_player.is_paused = false; - strncpy(g_simple_player.file_paths[0], file_path, sizeof(g_simple_player.file_paths[0]) - 1); - g_simple_player.file_paths[0][sizeof(g_simple_player.file_paths[0]) - 1] = '\0'; - - // 播放文件 - if (!ln_story_player_simple_play_file(&g_simple_player, 0)) - { - printf("[SIMPLE_PLAYER] failed to start playing single file\n"); - return false; - } - - printf("[SIMPLE_PLAYER] single file playback started successfully\n"); - return true; -} - -// 暂停播放 -bool ln_story_player_simple_pause(void) -{ - if (!g_simple_player.player) - { - printf("[SIMPLE_PLAYER] player not initialized\n"); - return false; - } - - if (!g_simple_player.is_playing) - { - printf("[SIMPLE_PLAYER] not currently playing\n"); - return false; - } - - if (g_simple_player.is_paused) - { - printf("[SIMPLE_PLAYER] already paused\n"); - return false; - } - - int32_t ret = g_simple_player.player->Pause(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] pause failed: %d\n", ret); - return false; - } - - g_simple_player.is_paused = true; - printf("[SIMPLE_PLAYER] playback paused\n"); - return true; -} - -// 恢复播放 -bool ln_story_player_simple_resume(void) -{ - if (!g_simple_player.player) - { - printf("[SIMPLE_PLAYER] player not initialized\n"); - return false; - } - - if (!g_simple_player.is_paused) - { - printf("[SIMPLE_PLAYER] not currently paused\n"); - return false; - } - - int32_t ret = g_simple_player.player->Play(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] resume failed: %d\n", ret); - return false; - } - - g_simple_player.is_paused = false; - printf("[SIMPLE_PLAYER] playback resumed\n"); - return true; -} - -// 检查是否暂停 -bool ln_story_player_simple_is_paused(void) -{ - return g_simple_player.is_paused; -} - -#ifdef __cplusplus -#if __cplusplus -} -#endif /* __cplusplus */ -#endif /* __cplusplus */ diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h b/code/sdk/application/wearable/n9/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h deleted file mode 100644 index e5ba5d8d5..000000000 --- a/code/sdk/application/wearable/n9/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef LN_STORY_PLAYER_SIMPLE_H -#define LN_STORY_PLAYER_SIMPLE_H - -#ifdef __cplusplus -#if __cplusplus -extern "C" -{ -#endif -#endif - -#include "stdint.h" -#include "errcode.h" -#include - -/* ============================== 精简版故事播放器接口 ============================== */ - -/** - * @brief 开始播放指定目录下的音频文件(顺序播放) - * @param story_dir 故事文件目录路径 - * @return true 成功,false 失败 - * - * 功能说明: - * - 自动扫描目录下的音频文件(支持mp3, wav, aac, m4a格式) - * - 按文件名顺序播放所有音频文件 - * - 播放完一个文件后自动播放下一个文件 - * - 音频系统只初始化一次,提高效率 - */ -bool ln_story_player_simple_start(const char *story_dir); - -/** - * @brief 停止播放并清理所有资源 - * @return 无返回值 - * - * 功能说明: - * - 立即停止当前播放 - * - 清理所有音频资源 - * - 重置播放器状态 - */ -void ln_story_player_simple_stop(void); - -/** - * @brief 检查是否正在播放 - * @return true 正在播放,false 未播放 - */ -bool ln_story_player_simple_is_playing(void); - -/** - * @brief 获取当前播放的文件索引 - * @return 当前播放的文件索引(从0开始),-1表示未播放 - */ -int ln_story_player_simple_get_current_index(void); - -/** - * @brief 获取总文件数 - * @return 扫描到的音频文件总数 - */ -int ln_story_player_simple_get_file_count(void); - -/** - * @brief 播放指定音频文件 - * @param file_path 音频文件完整路径 - * @return true 成功,false 失败 - * - * 功能说明: - * - 播放指定的单个音频文件 - * - 不会自动播放下一个文件 - * - 需要手动调用停止或暂停 - */ -bool ln_story_player_simple_play_file(const char *file_path); - -/** - * @brief 暂停播放 - * @return true 成功,false 失败 - * - * 功能说明: - * - 暂停当前正在播放的音频 - * - 保持播放器状态,可以恢复播放 - * - 不会清理音频资源 - */ -bool ln_story_player_simple_pause(void); - -/** - * @brief 恢复播放 - * @return true 成功,false 失败 - * - * 功能说明: - * - 恢复之前暂停的音频播放 - * - 只能在暂停状态下调用 - * - 如果当前没有暂停的音频,返回失败 - */ -bool ln_story_player_simple_resume(void); - -/** - * @brief 检查是否暂停 - * @return true 已暂停,false 未暂停 - */ -bool ln_story_player_simple_is_paused(void); - -/** - * @brief 处理播放器消息队列命令(内部使用) - * @param cmd 命令类型 - * @param data 数据指针 - * @param len 数据长度 - */ -void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len); - -/* ============================== 使用示例 ============================== */ - -/* -// 开始播放 -if (ln_story_player_simple_start("/user/leiniao/story/choose")) -{ - printf("播放开始成功\n"); -} - -// 检查播放状态 -if (ln_story_player_simple_is_playing()) -{ - printf("正在播放第 %d 个文件,共 %d 个文件\n", - ln_story_player_simple_get_current_index(), - ln_story_player_simple_get_file_count()); -} - -// 停止播放 -ln_story_player_simple_stop(); -*/ - -#ifdef __cplusplus -#if __cplusplus -} -#endif -#endif -#endif diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp b/code/sdk/application/wearable/n9/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp deleted file mode 100644 index 441ae6eca..000000000 --- a/code/sdk/application/wearable/n9/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp +++ /dev/null @@ -1,731 +0,0 @@ -#include "leiniao/include/ln_story_player_simple.h" -#include -#include -#include -#include -#include -#include -#include -#include -/* 引入播放器相关头文件 */ -#include "player.h" -#include "source.h" -#include "audio_manager.h" -#include -#include -#include -#include -/* 引入消息队列相关头文件 */ -#include "ln_api.h" - -#ifdef __cplusplus -#if __cplusplus -extern "C" -{ -#endif /* __cplusplus */ -#endif /* __cplusplus */ - -using OHOS::Media::Player; -using OHOS::Media::Source; -using namespace OHOS::Media; -using namespace::Audio; -using Audio::AudioManager; - -/* ============================== 精简版播放器结构体 ============================== */ - -/* 播放器消息命令定义 */ - - -typedef struct -{ - /* 文件列表 */ - char file_paths[100][256]; // 最多100个文件 - int file_count; - int current_index; - - /* 播放器对象 */ - std::shared_ptr player; - std::shared_ptr source; - std::map header; - - /* 音频管理 */ - AudioSession sessionId; - std::shared_ptr interrupt; - std::shared_ptr interruptListener; - bool audio_initialized; - bool is_activate_audio_interrupt; // 音频中断激活标志 - - /* 状态标志 */ - bool is_playing; - bool should_stop; - bool is_paused; // 暂停状态标志 -} ln_story_player_simple_t; - - -static bool ln_story_player_simple_play_file(ln_story_player_simple_t *impl, int file_index); -static bool ln_story_player_simple_player_stat(ln_story_player_simple_t *impl, int file_index); - -/* ============================== 播放器回调类 ============================== */ - -class SimplePlayerCallback : public PlayerCallback -{ -public: - SimplePlayerCallback(ln_story_player_simple_t *impl) : impl_(impl) {} - - void OnPlaybackComplete() override - { - if (impl_ && !impl_->should_stop) - { - printf("[SIMPLE_PLAYER] playback completed, scheduling next file\n"); - // 通过消息队列播放下一个文件,避免在回调中直接调用 - impl_->current_index++; - if (impl_->current_index < impl_->file_count) - { - // 发送消息到 ln_http_server_send_task 线程 - do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT, NULL, 0); - } - else - { - printf("[SIMPLE_PLAYER] all files played, stopping(所有文件播放完成)\n"); - do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_STOP, NULL, 0); - impl_->is_playing = false; - } - } - } - - void OnError(int32_t errorType, int32_t errorCode) override - { - printf("[SIMPLE_PLAYER] playback error: type=%d, code=%d\n", errorType, errorCode); - if (impl_) - { - impl_->is_playing = false; - } - } - - void OnInfo(int32_t type, int32_t extra) override {} - void OnRewindToComplete() override {} - -private: - ln_story_player_simple_t *impl_; -}; - -/* ============================== 中断监听器类 ============================== */ - -class SimplePlayerInterruptListener : public InterruptListener -{ -public: - SimplePlayerInterruptListener(ln_story_player_simple_t *impl) : impl_(impl) {} - - void OnInterrupt(int32_t type, int32_t hint) override - { - if (!impl_) - return; - - printf("[SIMPLE_PLAYER] OnInterrupt: type=%d, hint=%d\n", type, hint); - - if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_PAUSE) - { - if (impl_->is_playing && impl_->player) - { - impl_->player->Pause(); - } - } - else if (type == INTERRUPT_TYPE_END && hint == INTERRUPT_HINT_RESUME) - { - if (impl_->is_playing && impl_->player) - { - impl_->player->Play(); - } - } - else if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_STOP) - { - if (impl_->is_playing) - { - impl_->player->Stop(); - impl_->is_playing = false; - } - } - } - -private: - ln_story_player_simple_t *impl_; -}; - -/* ============================== 全局变量 ============================== */ - -static ln_story_player_simple_t g_simple_player = {0}; -static std::shared_ptr g_player_callback = nullptr; -static std::shared_ptr g_interrupt_listener = nullptr; - -/* ============================== 内部函数 ============================== */ - -// 处理播放器消息队列命令 -void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len) -{ - if (cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT) - { - printf("[SIMPLE_PLAYER] received play next command\n"); - if (g_simple_player.is_playing && !g_simple_player.should_stop) - { - if (g_simple_player.current_index < g_simple_player.file_count) - { - ln_story_player_simple_play_file(&g_simple_player, g_simple_player.current_index); - } - } - } -} - -// 音频初始化(只初始化一次) -static bool AudioInit(void) -{ - if (g_simple_player.audio_initialized) - { - return true; - } - - AudioManager& amInstance = AudioManager::GetInstance(); - if (!amInstance.Initialize()) - { - printf("[SIMPLE_PLAYER] AudioManager Initialize failed\n"); - return false; - } - - g_simple_player.player = std::make_shared(); - if (!g_simple_player.player) - { - printf("[SIMPLE_PLAYER] Player create failed\n"); - return false; - } - - g_player_callback = std::make_shared(&g_simple_player); - if (!g_player_callback) - { - printf("[SIMPLE_PLAYER] PlayerCallback create failed\n"); - return false; - } - - g_simple_player.audio_initialized = true; - g_simple_player.is_activate_audio_interrupt = false; - - printf("[SIMPLE_PLAYER] Audio initialized successfully\n"); - return true; -} - -// 比较函数,用于文件名排序 -static int CompareFileNames(const void *a, const void *b) -{ - const char *file_a = (const char *)a; - const char *file_b = (const char *)b; - - // 提取文件名(去掉路径) - const char *name_a = strrchr(file_a, '/'); - const char *name_b = strrchr(file_b, '/'); - - if (name_a) name_a++; - else name_a = file_a; - if (name_b) name_b++; - else name_b = file_b; - - // 提取数字部分进行比较 - int num_a = atoi(name_a); - int num_b = atoi(name_b); - - return num_a - num_b; -} - -// 扫描目录获取音频文件 -static bool ScanAudioFiles(const char *story_dir) -{ - printf("[SIMPLE_PLAYER] scanning directory: %s\n", story_dir); - - // 检查目录是否存在 - if (access(story_dir, F_OK) == -1) - { - printf("[SIMPLE_PLAYER] directory does not exist: %s\n", story_dir); - return false; - } - - DIR *dir = opendir(story_dir); - if (!dir) - { - printf("[SIMPLE_PLAYER] failed to open directory: %s\n", story_dir); - return false; - } - - g_simple_player.file_count = 0; - struct dirent *entry; - - while ((entry = readdir(dir)) != NULL && g_simple_player.file_count < 100) - { - // 跳过 . 和 .. 目录 - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - { - continue; - } - - // 检查是否是音频文件 - const char *ext = strrchr(entry->d_name, '.'); - if (ext && (strcasecmp(ext, ".mp3") == 0 || strcasecmp(ext, ".wav") == 0 || - strcasecmp(ext, ".aac") == 0 || strcasecmp(ext, ".m4a") == 0)) - { - snprintf(g_simple_player.file_paths[g_simple_player.file_count], - sizeof(g_simple_player.file_paths[g_simple_player.file_count]), - "%s/%s", story_dir, entry->d_name); - g_simple_player.file_count++; - printf("[SIMPLE_PLAYER] found audio file: %s\n", entry->d_name); - } - } - - closedir(dir); - - // 对文件路径进行排序(按文件名中的数字排序) - if (g_simple_player.file_count > 1) - { - qsort(g_simple_player.file_paths, g_simple_player.file_count, - sizeof(g_simple_player.file_paths[0]), CompareFileNames); - printf("[SIMPLE_PLAYER] files sorted by name\n"); - } - - printf("[SIMPLE_PLAYER] found %d audio files\n", g_simple_player.file_count); - return g_simple_player.file_count > 0; -} - -// 播放指定文件 -static bool ln_story_player_simple_play_file(ln_story_player_simple_t *impl, int file_index) -{ - if (file_index < 0 || file_index >= impl->file_count) - { - printf("[SIMPLE_PLAYER] invalid file index: %d\n", file_index); - return false; - } - - printf("[SIMPLE_PLAYER] playing file %d: %s\n", file_index, impl->file_paths[file_index]); - - // 检查播放器状态 - int32_t player_state; - impl->player->GetPlayerState(player_state); - printf("[SIMPLE_PLAYER] current player state: %d\n", player_state); - - // 根据播放器状态进行处理 - if (player_state == PLAYER_STARTED || player_state == PLAYER_PLAYBACK_COMPLETE) - { - printf("[SIMPLE_PLAYER] stopping and resetting player before switching\n"); - impl->player->Stop(); - impl->player->Reset(); - // 然后调用PlayerStat来重新播放 - return ln_story_player_simple_player_stat(impl, file_index); - } - else if (player_state == PLAYER_PAUSED || player_state == PLAYER_IDLE) - { - printf("[SIMPLE_PLAYER] player is ready, calling PlayerStat\n"); - impl->player->Reset(); - return ln_story_player_simple_player_stat(impl, file_index); - } - else - { - printf("[SIMPLE_PLAYER] unknown state, forcing reset and calling PlayerStat\n"); - impl->player->Stop(); - impl->player->Reset(); - return ln_story_player_simple_player_stat(impl, file_index); - } -} - -// 播放器状态设置和播放(类似PlayersModel::PlayerStat) -static bool ln_story_player_simple_player_stat(ln_story_player_simple_t *impl, int file_index) -{ - printf("[SIMPLE_PLAYER] ln_story_player_simple_player_stat\n"); - int32_t ret; - - // 设置回调函数 - impl->player->SetPlayerCallback(g_player_callback); - - // 设置音频源 - impl->source = std::make_shared(impl->file_paths[file_index], impl->header); - if (!impl->source) - { - printf("[SIMPLE_PLAYER] failed to create source\n"); - return false; - } - - ret = impl->player->SetSource(*impl->source); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] set source failed: %d\n", ret); - impl->player->Reset(); // 只在SetSource失败时Reset - return false; - } - - // 配置播放器 - impl->player->SetAudioStreamType(AUDIO_STREAM_MUSIC); - - // 准备播放 - ret = impl->player->Prepare(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] prepare failed: %d\n", ret); - impl->player->Reset(); // 只在Prepare失败时Reset - return false; - } - - // 设置音频会话和中断监听(只在第一次播放时申请) - AudioManager &amInstance = AudioManager::GetInstance(); - - if (!impl->is_activate_audio_interrupt) - { - // 创建新的会话和中断(只在第一次播放时创建) - impl->sessionId = amInstance.MakeSessionId(); - if (impl->sessionId == AUDIO_SESSION_ID_NONE) - { - printf("[SIMPLE_PLAYER] failed to create session\n"); - impl->player->Reset(); - return false; - } - - impl->interruptListener = std::make_shared(impl); - impl->interrupt = std::make_shared(); - impl->interrupt->streamType = AUDIO_STREAM_MUSIC; - impl->interrupt->sessionID = impl->sessionId; - impl->interrupt->interruptListener = impl->interruptListener; - - if (amInstance.ActivateAudioInterrupt(*impl->interrupt) == INTERRUPT_FAILED) - { - printf("[SIMPLE_PLAYER] failed to activate audio interrupt\n"); - impl->player->Reset(); - return false; - } - - impl->is_activate_audio_interrupt = true; - printf("[SIMPLE_PLAYER] audio interrupt activated for first file\n"); - } - else - { - printf("[SIMPLE_PLAYER] reusing existing audio interrupt for file %d\n", file_index); - } - - // 设置音频会话ID - ret = impl->player->SetAudioSessionId(impl->sessionId); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] SetAudioSessionId failed: %d\n", ret); - impl->player->Reset(); - // 只在第一次播放失败时才清理音频中断 - if (impl->is_activate_audio_interrupt) - { - amInstance.DeactivateAudioInterrupt(*impl->interrupt); - impl->is_activate_audio_interrupt = false; - } - return false; - } - - // 开始播放 - ret = impl->player->Play(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] play failed: %d\n", ret); - impl->player->Stop(); - impl->player->Reset(); - // 只在第一次播放失败时才清理音频中断 - if (impl->is_activate_audio_interrupt) - { - amInstance.DeactivateAudioInterrupt(*impl->interrupt); - impl->is_activate_audio_interrupt = false; - } - return false; - } - - impl->is_playing = true; - impl->is_paused = false; // 重置暂停状态 - printf("[SIMPLE_PLAYER] started playing file %d\n", file_index); - return true; -} - -// 清理音频中断 -static void ClearActivateAudioInterrupt(void) -{ - if (g_simple_player.is_activate_audio_interrupt && g_simple_player.interrupt && g_simple_player.sessionId != AUDIO_SESSION_ID_NONE) - { - AudioManager &amInstance = AudioManager::GetInstance(); - amInstance.DeactivateAudioInterrupt(*g_simple_player.interrupt); - g_simple_player.is_activate_audio_interrupt = false; - printf("[SIMPLE_PLAYER] audio interrupt cleared\n"); - } -} - -// 清理资源 -static void CleanupResources(void) -{ - if (g_simple_player.player) - { - g_simple_player.player->Stop(); - g_simple_player.player->Reset(); - g_simple_player.player->Release(); - g_simple_player.player.reset(); - } - - if (g_simple_player.is_activate_audio_interrupt && g_simple_player.interrupt && g_simple_player.sessionId != AUDIO_SESSION_ID_NONE) - { - AudioManager &amInstance = AudioManager::GetInstance(); - amInstance.DeactivateAudioInterrupt(*g_simple_player.interrupt); - g_simple_player.is_activate_audio_interrupt = false; - printf("[SIMPLE_PLAYER] audio interrupt deactivated\n"); - } - - g_simple_player.interrupt.reset(); - g_simple_player.interruptListener.reset(); - g_player_callback.reset(); - - memset(&g_simple_player, 0, sizeof(g_simple_player)); - printf("[SIMPLE_PLAYER] resources cleaned up\n"); -} - -/* ============================== 公共接口 ============================== */ - -// 开始播放指定目录下的音频文件(顺序播放) -bool ln_story_player_simple_start(const char *story_dir) -{ - if (!story_dir) - { - printf("[SIMPLE_PLAYER] invalid story directory\n"); - return false; - } - - printf("[SIMPLE_PLAYER] starting playback: %s\n", story_dir); - - // 如果已经在播放,先停止 - if (g_simple_player.is_playing) - { - printf("[SIMPLE_PLAYER] stopping playback\n"); - ln_story_player_simple_stop(); - } - - // 初始化音频(只初始化一次) - if (!AudioInit()) - { - printf("[SIMPLE_PLAYER] audio init failed\n"); - return false; - } - - // 扫描音频文件 - if (!ScanAudioFiles(story_dir)) - { - printf("[SIMPLE_PLAYER] no audio files found\n"); - return false; - } - - // 开始播放第一个文件 - g_simple_player.current_index = 0; - g_simple_player.should_stop = false; - - if (!ln_story_player_simple_play_file(&g_simple_player, 0)) - { - printf("[SIMPLE_PLAYER] failed to start playing\n"); - return false; - } - - printf("[SIMPLE_PLAYER] playback started successfully\n"); - return true; -} - -// 停止播放 -void ln_story_player_simple_stop(void) -{ - printf("[SIMPLE_PLAYER] ln_story_player_simple_stop\n"); - - g_simple_player.should_stop = true; - g_simple_player.is_playing = false; - g_simple_player.is_paused = false; - - if (!g_simple_player.player) - { - printf("[SIMPLE_PLAYER] player is null, nothing to stop\n"); - return; - } - - // 检查播放器状态 - int32_t player_state; - int32_t ret = g_simple_player.player->GetPlayerState(player_state); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] get player state failed: %d\n", ret); - // 即使获取状态失败,也要尝试清理 - } - else - { - printf("[SIMPLE_PLAYER] current player state: %d\n", player_state); - } - - // 根据播放器状态进行处理(参考PlayersModel::PlayerStop) - if (player_state == PLAYER_STARTED || player_state == PLAYER_PLAYBACK_COMPLETE) - { - printf("[SIMPLE_PLAYER] stopping and resetting player\n"); - ret = g_simple_player.player->Stop(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] stop failed: %d\n", ret); - } - - ret = g_simple_player.player->Reset(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] reset failed: %d\n", ret); - } - } - else if (player_state == PLAYER_PAUSED) - { - printf("[SIMPLE_PLAYER] resetting paused player\n"); - ret = g_simple_player.player->Reset(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] reset failed: %d\n", ret); - } - } - else if (player_state == PLAYER_STOPPED) - { - printf("[SIMPLE_PLAYER] player already stopped\n"); - } - else - { - printf("[SIMPLE_PLAYER] unknown state, forcing stop and reset\n"); - g_simple_player.player->Stop(); - g_simple_player.player->Reset(); - } - // 清理音频中断 - ClearActivateAudioInterrupt(); - - - printf("[SIMPLE_PLAYER] playback stopped\n"); -} - -// 检查是否正在播放 -bool ln_story_player_simple_is_playing(void) -{ - return g_simple_player.is_playing; -} - -// 获取当前播放的文件索引 -int ln_story_player_simple_get_current_index(void) -{ - return g_simple_player.current_index; -} - -// 获取总文件数 -int ln_story_player_simple_get_file_count(void) -{ - return g_simple_player.file_count; -} - -// 播放指定音频文件 -bool ln_story_player_simple_play_file(const char *file_path) -{ - if (!file_path) - { - printf("[SIMPLE_PLAYER] invalid file path\n"); - return false; - } - - printf("[SIMPLE_PLAYER] playing single file: %s\n", file_path); - - // 如果已经在播放,先停止 - if (g_simple_player.is_playing) - { - printf("[SIMPLE_PLAYER] stopping current playback\n"); - ln_story_player_simple_stop(); - } - - // 初始化音频(只初始化一次) - if (!AudioInit()) - { - printf("[SIMPLE_PLAYER] audio init failed\n"); - return false; - } - - // 设置单个文件播放模式 - g_simple_player.file_count = 1; - g_simple_player.current_index = 0; - g_simple_player.should_stop = false; - g_simple_player.is_paused = false; - strncpy(g_simple_player.file_paths[0], file_path, sizeof(g_simple_player.file_paths[0]) - 1); - g_simple_player.file_paths[0][sizeof(g_simple_player.file_paths[0]) - 1] = '\0'; - - // 播放文件 - if (!ln_story_player_simple_play_file(&g_simple_player, 0)) - { - printf("[SIMPLE_PLAYER] failed to start playing single file\n"); - return false; - } - - printf("[SIMPLE_PLAYER] single file playback started successfully\n"); - return true; -} - -// 暂停播放 -bool ln_story_player_simple_pause(void) -{ - if (!g_simple_player.player) - { - printf("[SIMPLE_PLAYER] player not initialized\n"); - return false; - } - - if (!g_simple_player.is_playing) - { - printf("[SIMPLE_PLAYER] not currently playing\n"); - return false; - } - - if (g_simple_player.is_paused) - { - printf("[SIMPLE_PLAYER] already paused\n"); - return false; - } - - int32_t ret = g_simple_player.player->Pause(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] pause failed: %d\n", ret); - return false; - } - - g_simple_player.is_paused = true; - printf("[SIMPLE_PLAYER] playback paused\n"); - return true; -} - -// 恢复播放 -bool ln_story_player_simple_resume(void) -{ - if (!g_simple_player.player) - { - printf("[SIMPLE_PLAYER] player not initialized\n"); - return false; - } - - if (!g_simple_player.is_paused) - { - printf("[SIMPLE_PLAYER] not currently paused\n"); - return false; - } - - int32_t ret = g_simple_player.player->Play(); - if (ret != 0) - { - printf("[SIMPLE_PLAYER] resume failed: %d\n", ret); - return false; - } - - g_simple_player.is_paused = false; - printf("[SIMPLE_PLAYER] playback resumed\n"); - return true; -} - -// 检查是否暂停 -bool ln_story_player_simple_is_paused(void) -{ - return g_simple_player.is_paused; -} - -#ifdef __cplusplus -#if __cplusplus -} -#endif /* __cplusplus */ -#endif /* __cplusplus */ diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c index e93d2a753..1777e2b43 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c @@ -13,11 +13,6 @@ #include "stdio.h" #include "audio_stream_player.h" - -#ifdef BOARD_N9 -#include "leiniao/include/ln_story_player_simple.h" -#endif - // 声明播放器消息处理函数 extern void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len); @@ -60,13 +55,13 @@ char *uapi_ln_get_device_iccid(void) /* 获取设备model */ char *uapi_ln_get_device_model(void) { - g_device_model = "abmybox_1"; + g_device_model = "AB101"; return g_device_model; } /* 获取设备固件版本version */ char *uapi_ln_get_device_version(void) { - g_device_version = "1.0.0"; + g_device_version = "1.3.107"; return g_device_version; } /* 获取设备token: 优先读本地缓存,判断过期,否则刷新 */ @@ -438,6 +433,7 @@ static void handle_ota_download_msg(const OtaDownloadMsg *msg) printf("[OTA_DOWNLOAD] MD5校验成功,准备重启升级\n"); push_ota_status(OTA_UI_STATUS_SUCCESS); /* 外部可在此触发升级动作 */ + uaip_cmd_update_request(); } else if (md5_ok == 0) { printf("[OTA_DOWNLOAD] MD5校验失败,建议重新下载\n"); push_ota_status(OTA_UI_STATUS_FAILED); @@ -1068,78 +1064,27 @@ static void ln_http_server_send_task(void const *arg) else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY) { printf("LN_HTTP_SERVICE_CMD_AUDIO_PLAY:播放音频\n"); - #ifdef BOARD_N9 - if (ln_story_player_simple_start("/user/leiniao/story/choose")) - { - printf("播放开始成功\n"); - } - #endif } else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_STOP) { printf("LN_HTTP_SERVICE_CMD_AUDIO_STOP:停止播放音频\n"); - #ifdef BOARD_N9 - ln_story_player_simple_stop(); - #endif } else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT) { printf("LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT:播放下一首\n"); - #ifdef BOARD_N9 - ln_story_player_simple_handle_message(LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT, NULL, 0); - #endif } else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY_FILE) { printf("LN_HTTP_SERVICE_CMD_AUDIO_PLAY_FILE:播放指定文件\n"); - #ifdef BOARD_N9 - if (mail.data != NULL) - { - char *file_path = (char*)mail.data; - printf("Playing file: %s\n", file_path); - if (ln_story_player_simple_play_file(file_path)) - { - printf("File playback started successfully\n"); - } - else - { - printf("Failed to start file playback\n"); - } - } - else - { - printf("Error: No file path provided\n"); - } - #endif } else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PAUSE) { printf("LN_HTTP_SERVICE_CMD_AUDIO_PAUSE:暂停播放\n"); - // #ifdef BOARD_N9 - // if (ln_story_player_simple_pause()) - // { - // printf("Playback paused successfully\n"); - // } - // else - // { - // printf("Failed to pause playback\n"); - // } - // #endif uapi_ln_audio_download_is_cancel(1); } else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_RESUME) { printf("LN_HTTP_SERVICE_CMD_AUDIO_RESUME:恢复播放\n"); - // #ifdef BOARD_N9 - // if (ln_story_player_simple_resume()) - // { - // printf("Playback resumed successfully\n"); - // } - // else - // { - // printf("Failed to resume playback\n"); - // } - // #endif uapi_ln_audio_download_is_cancel(0); } if (mail.data != NULL) diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c new file mode 100644 index 000000000..2fea535e9 --- /dev/null +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c @@ -0,0 +1,719 @@ +#include "ln_api.h" +#include +#include +#include "cJSON.h" +#include +#include +#include +#include + +/* 宏定义 */ +#define EMOJI_CACHE_BASE_PATH "/emoji_cache/" // 表情缓存根目录 +#define EMOJI_DOWNLOAD_TASK_STACK 4096 // 下载任务栈大小 +#define EMOJI_DOWNLOAD_TASK_PRIO 25 // 下载任务优先级 +#define MAX_PATH_LEN 512 // 最大路径长度 + +/* 表情信息结构体定义 */ +typedef struct +{ + char emotion[32]; // 心情类型 + char emojiUrl[512]; // 表情文件URL +} EmotionEmoji; + +/* 表情包配置结构体 */ +typedef struct +{ + char characterId[64]; // 角色ID + char eyesColor[32]; // 瞳色 + char eyesShape[32]; // 眼型 + EmotionEmoji *emojis; // 表情列表 + int emojiCount; // 表情数量 +} EmojiPackage; + +/* 全局变量 */ +EmojiPackage g_emoji_package = {0}; +static volatile int g_emoji_downloading = 0; // 下载状态标志 + +/* 安全字符串复制函数 */ +static inline void ln_safe_strcpy(char *dst, size_t dst_size, const char *src) +{ + if (!dst || dst_size == 0) + return; + if (src == NULL) + { + dst[0] = '\0'; + return; + } + strncpy(dst, src, dst_size - 1); + dst[dst_size - 1] = '\0'; +} + +/* 释放表情列表资源 */ +static void ln_free_emoji_list() +{ + if (g_emoji_package.emojis) + { + free(g_emoji_package.emojis); + g_emoji_package.emojis = NULL; + } + g_emoji_package.emojiCount = 0; +} + +/* 工具函数:创建多级目录 */ +static int ln_mkdir_p(const char *path) +{ + if (!path || *path == '\0') + return -1; + + char dir[MAX_PATH_LEN] = {0}; + strncpy(dir, path, sizeof(dir) - 1); + int len = strlen(dir); + if (dir[len - 1] != '/') + { + strncat(dir, "/", sizeof(dir) - 1 - len); + len++; + } + + char *p = dir + 1; + while (*p) + { + if (*p == '/') + { + *p = '\0'; + if (mkdir(dir, 0777) != 0 && errno != EEXIST) + { + return -1; + } + *p = '/'; + } + p++; + } + + // 创建最后一级目录 + if (mkdir(dir, 0777) != 0 && errno != EEXIST) + { + return -1; + } + return 0; +} + +/* 工具函数:生成表情缓存路径 */ +static void ln_gen_emoji_cache_path(const EmojiPackage *pkg, const EmotionEmoji *emoji, char *path, size_t path_len) +{ + if (!pkg || !emoji || !path || path_len == 0) + return; + + // 路径格式:BASE/characterId/eyesColor_eyesShape/emotion.ext + char ext[32] = {0}; + const char *dot = strrchr(emoji->emojiUrl, '.'); + if (dot && strlen(dot) < sizeof(ext)) + { + strncpy(ext, dot, sizeof(ext) - 1); + } + + snprintf(path, path_len, + "%s%s/%s_%s/%s%s", + EMOJI_CACHE_BASE_PATH, + pkg->characterId, + pkg->eyesColor, + pkg->eyesShape, + emoji->emotion, + ext); +} + +/* 工具函数:检查表情是否已缓存 */ +static int ln_emoji_is_cached(const EmojiPackage *pkg, const EmotionEmoji *emoji) +{ + char path[MAX_PATH_LEN] = {0}; + ln_gen_emoji_cache_path(pkg, emoji, path, sizeof(path)); + struct stat st; + return (stat(path, &st) == 0 && S_ISREG(st.st_mode)) ? 1 : 0; +} + +/* 文件下载函数:从URL下载到指定路径 */ +static int ln_download_file(const char *url, const char *save_path) +{ + if (!url || !save_path) + return -1; + + // 创建保存目录 + char dir[MAX_PATH_LEN] = {0}; + strncpy(dir, save_path, sizeof(dir) - 1); + char *last_slash = strrchr(dir, '/'); + if (last_slash) + { + *last_slash = '\0'; + if (ln_mkdir_p(dir) != 0) + { + printf("[DOWNLOAD] create dir %s failed\n", dir); + return -1; + } + } + + // 初始化HTTP客户端 + HttpClient client = {0}; + HttpClientData clientData = {0}; + HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 32 * 1024); // 增大接收缓冲区 + if (prep != HTTP_SUCCESS) + { + printf("[DOWNLOAD] prepare failed\n"); + return -1; + } + + // 发送GET请求 + HTTPC_RESULT res = HttpClientGetRequest(&client, url, &clientData); + if (res != HTTP_SUCCESS || HttpClientGetResponseCode(&client) != 200) + { + printf("[DOWNLOAD] GET %s failed\n", url); + HttpClientUnprepare(&clientData); + return -1; + } + + // 写入文件 + int fd = open(save_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + printf("[DOWNLOAD] open %s failed\n", save_path); + HttpClientUnprepare(&clientData); + return -1; + } + + int write_len = write(fd, clientData.responseBuf, clientData.responseContentLen); + close(fd); + HttpClientUnprepare(&clientData); + + if (write_len != clientData.responseContentLen) + { + printf("[DOWNLOAD] write file failed\n"); + unlink(save_path); // 删除不完整文件 + return -1; + } + + printf("[DOWNLOAD] %s saved to %s\n", url, save_path); + return 0; +} + +/* 下载任务:批量下载表情并上报结果 */ +static void ln_emoji_download_task(void *arg) +{ + EmojiPackage *pkg = (EmojiPackage *)arg; + if (!pkg || pkg->emojiCount <= 0 || !pkg->emojis) + { + g_emoji_downloading = 0; + return; + } + + int all_success = 1; + + // 遍历下载所有表情 + for (int i = 0; i < pkg->emojiCount; i++) + { + // 跳过已缓存的文件 + if (ln_emoji_is_cached(pkg, &pkg->emojis[i])) + { + printf("[DOWNLOAD] emoji %s already cached\n", pkg->emojis[i].emotion); + continue; + } + + // 下载并缓存 + char save_path[MAX_PATH_LEN] = {0}; + ln_gen_emoji_cache_path(pkg, &pkg->emojis[i], save_path, sizeof(save_path)); + if (ln_download_file(pkg->emojis[i].emojiUrl, save_path) != 0) + { + all_success = 0; + printf("[DOWNLOAD] emoji %s download failed\n", pkg->emojis[i].emotion); + } + } + + // 下载完成后上报状态 + const char *status = all_success ? "success" : "failed"; + uint32_t report_res = ln_emoji_report_status_http( + get_device_id(), // 假设存在设备ID获取接口 + get_device_token(), // 假设存在设备Token获取接口 + pkg->characterId, + pkg->eyesColor, + pkg->eyesShape, + status); + + printf("[DOWNLOAD] report status %s, result: %d\n", status, report_res); + + g_emoji_downloading = 0; + LOS_TaskDelete(NULL); // 删除当前任务(根据实际系统接口调整) +} + +/* 启动下载任务 */ +static void ln_start_emoji_download() +{ + if (g_emoji_downloading) + { + printf("[DOWNLOAD] already downloading, skip\n"); + return; + } + + if (g_emoji_package.emojiCount <= 0 || !g_emoji_package.emojis) + { + printf("[DOWNLOAD] no emojis to download\n"); + return; + } + + g_emoji_downloading = 1; + + // 创建下载任务(基于LiteOS接口示例) + UINT32 task_id; + TSK_INIT_PARAM_S task_param = { + .pfnTaskEntry = (TSK_ENTRY_FUNC)ln_emoji_download_task, + .ulStackSize = EMOJI_DOWNLOAD_TASK_STACK, + .pcName = "emoji_download", + .usTaskPrio = EMOJI_DOWNLOAD_TASK_PRIO, + .pvArg = &g_emoji_package}; + + if (LOS_TaskCreate(&task_id, &task_param) != LOS_OK) + { + printf("[DOWNLOAD] create task failed\n"); + g_emoji_downloading = 0; + } +} + +/* 解析下载表情命令JSON */ +void ln_parse_download_emoji(const char *json_buf, int json_len) +{ + if (!json_buf || json_len <= 0) + { + printf("[DOWNLOAD_EMOJI] invalid json input\n"); + return; + } + + // 释放原有表情资源 + ln_free_emoji_list(); + + // 复制并终止字符串 + char *text = (char *)malloc((size_t)json_len + 1); + if (!text) + return; + memcpy(text, json_buf, (size_t)json_len); + text[json_len] = '\0'; + + // 解析JSON根对象 + cJSON *root = cJSON_Parse(text); + if (!root) + { + printf("[DOWNLOAD_EMOJI] json parse failed\n"); + free(text); + return; + } + + // 检查错误码 + cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode"); + if (errorCode && !cJSON_IsNull(errorCode)) + { + printf("[DOWNLOAD_EMOJI] server error: %s\n", + cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown"); + cJSON_Delete(root); + free(text); + return; + } + + // 获取data对象 + cJSON *data = cJSON_GetObjectItem(root, "data"); + if (!data || !cJSON_IsObject(data)) + { + printf("[DOWNLOAD_EMOJI] no data object\n"); + cJSON_Delete(root); + free(text); + return; + } + + // 解析基础字段 + cJSON *characterId = cJSON_GetObjectItem(data, "characterId"); + cJSON *eyesColor = cJSON_GetObjectItem(data, "eyesColor"); + cJSON *eyesShape = cJSON_GetObjectItem(data, "eyesShape"); + + ln_safe_strcpy(g_emoji_package.characterId, + sizeof(g_emoji_package.characterId), + (characterId && cJSON_IsString(characterId)) ? characterId->valuestring : ""); + + ln_safe_strcpy(g_emoji_package.eyesColor, + sizeof(g_emoji_package.eyesColor), + (eyesColor && cJSON_IsString(eyesColor)) ? eyesColor->valuestring : ""); + + ln_safe_strcpy(g_emoji_package.eyesShape, + sizeof(g_emoji_package.eyesShape), + (eyesShape && cJSON_IsString(eyesShape)) ? eyesShape->valuestring : ""); + + // 解析表情列表 + cJSON *emotionEmojis = cJSON_GetObjectItem(data, "emotionEmojis"); + if (emotionEmojis && cJSON_IsArray(emotionEmojis)) + { + int count = cJSON_GetArraySize(emotionEmojis); + if (count > 0) + { + g_emoji_package.emojiCount = count; + g_emoji_package.emojis = (EmotionEmoji *)malloc(sizeof(EmotionEmoji) * count); + if (!g_emoji_package.emojis) + { + printf("[DOWNLOAD_EMOJI] malloc failed for emojis\n"); + g_emoji_package.emojiCount = 0; + } + else + { + // 遍历解析每个表情 + for (int i = 0; i < count; i++) + { + cJSON *item = cJSON_GetArrayItem(emotionEmojis, i); + if (item && cJSON_IsObject(item)) + { + cJSON *emotion = cJSON_GetObjectItem(item, "emotion"); + cJSON *emojiUrl = cJSON_GetObjectItem(item, "emojiUrl"); + + ln_safe_strcpy(g_emoji_package.emojis[i].emotion, + sizeof(g_emoji_package.emojis[i].emotion), + (emotion && cJSON_IsString(emotion)) ? emotion->valuestring : ""); + + ln_safe_strcpy(g_emoji_package.emojis[i].emojiUrl, + sizeof(g_emoji_package.emojis[i].emojiUrl), + (emojiUrl && cJSON_IsString(emojiUrl)) ? emojiUrl->valuestring : ""); + } + } + } + } + } + + // 打印解析结果 + printf("[DOWNLOAD_EMOJI] parsed successfully:\n"); + printf(" characterId: %s\n", g_emoji_package.characterId); + printf(" eyesColor: %s\n", g_emoji_package.eyesColor); + printf(" eyesShape: %s\n", g_emoji_package.eyesShape); + printf(" emojiCount: %d\n", g_emoji_package.emojiCount); + + for (int i = 0; i < g_emoji_package.emojiCount; i++) + { + printf(" emoji %d: emotion=%s, url=%s\n", + i + 1, + g_emoji_package.emojis[i].emotion, + g_emoji_package.emojis[i].emojiUrl); + } + + // 解析完成后启动下载任务 + ln_start_emoji_download(); + + // 清理资源 + cJSON_Delete(root); + free(text); +} + +/* 工具函数:递归删除目录 */ +static int ln_rmdir_recursive(const char *path) +{ + if (!path) + return -1; + + struct stat st; + if (stat(path, &st) != 0) + return -1; + + if (S_ISREG(st.st_mode)) + { + return unlink(path); // 删除文件 + } + else if (S_ISDIR(st.st_mode)) + { + DIR *dir = opendir(path); + if (!dir) + return -1; + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + char subpath[MAX_PATH_LEN] = {0}; + snprintf(subpath, sizeof(subpath), "%s/%s", path, entry->d_name); + if (ln_rmdir_recursive(subpath) != 0) + { + closedir(dir); + return -1; + } + } + + closedir(dir); + return rmdir(path); // 删除空目录 + } + + return -1; +} + +/* 解析删除表情命令JSON */ +void ln_parse_delete_emoji(const char *json_buf, int json_len) +{ + if (!json_buf || json_len <= 0) + { + printf("[DELETE_EMOJI] invalid json input\n"); + return; + } + + // 复制并终止字符串 + char *text = (char *)malloc((size_t)json_len + 1); + if (!text) + return; + memcpy(text, json_buf, (size_t)json_len); + text[json_len] = '\0'; + + // 解析JSON根对象 + cJSON *root = cJSON_Parse(text); + if (!root) + { + printf("[DELETE_EMOJI] json parse failed\n"); + free(text); + return; + } + + // 检查错误码 + cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode"); + if (errorCode && !cJSON_IsNull(errorCode)) + { + printf("[DELETE_EMOJI] server error: %s\n", + cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown"); + cJSON_Delete(root); + free(text); + return; + } + + // 获取data对象 + cJSON *data = cJSON_GetObjectItem(root, "data"); + if (!data || !cJSON_IsObject(data)) + { + printf("[DELETE_EMOJI] no data object\n"); + cJSON_Delete(root); + free(text); + return; + } + + // 解析字段 + cJSON *characterId = cJSON_GetObjectItem(data, "characterId"); + cJSON *eyesColor = cJSON_GetObjectItem(data, "eyesColor"); + cJSON *eyesShape = cJSON_GetObjectItem(data, "eyesShape"); + + char del_characterId[64] = {0}; + char del_eyesColor[32] = {0}; + char del_eyesShape[32] = {0}; + + ln_safe_strcpy(del_characterId, sizeof(del_characterId), + (characterId && cJSON_IsString(characterId)) ? characterId->valuestring : ""); + + ln_safe_strcpy(del_eyesColor, sizeof(del_eyesColor), + (eyesColor && cJSON_IsString(eyesColor)) ? eyesColor->valuestring : ""); + + ln_safe_strcpy(del_eyesShape, sizeof(del_eyesShape), + (eyesShape && cJSON_IsString(eyesShape)) ? eyesShape->valuestring : ""); + + // 打印解析结果 + printf("[DELETE_EMOJI] parsed successfully:\n"); + printf(" characterId: %s\n", del_characterId); + printf(" eyesColor: %s\n", del_eyesColor); + printf(" eyesShape: %s\n", del_eyesShape); + + // 删除本地表情缓存 + char del_dir[MAX_PATH_LEN] = {0}; + snprintf(del_dir, sizeof(del_dir), + "%s%s/%s_%s", + EMOJI_CACHE_BASE_PATH, + del_characterId, + del_eyesColor, + del_eyesShape); + + if (ln_rmdir_recursive(del_dir) == 0) + { + printf("[DELETE_EMOJI] cached files in %s deleted\n", del_dir); + } + else + { + printf("[DELETE_EMOJI] failed to delete cached files in %s\n", del_dir); + } + + // 清理资源 + cJSON_Delete(root); + free(text); +} + +static inline int ln_fill_auth_header(HttpClient *client, + const char *device_id, + const char *device_token) +{ + if (!client || !device_id || !device_token) + return -1; + char header_buf[512]; + int hdr = snprintf(header_buf, sizeof(header_buf), + "deviceId: %s\r\n" + "deviceToken: %s\r\n" + "Content-Type: application/json\r\n", + device_id, device_token); + if (hdr <= 0 || hdr >= (int)sizeof(header_buf)) + return -1; + HttpClientSetCustomHeader(client, header_buf); + return 0; +} + +/* 7.1 上报角色表情组下载状态 */ +uint32_t ln_emoji_report_status_http(const char *device_id, + const char *device_token, + const char *character_id, + const char *eyes_color, + const char *eyes_shape, + const char *status) +{ + if (!device_id || !device_token || !character_id || !eyes_color || !eyes_shape || !status) + return ERRCODE_FAIL; + +#ifdef SUPPORT_CAT1_SERVICE + cat1_api_add_cat1_vote(); + cat1_api_wakeup_cat1(1); + cat1_api_keep_wakeup(1); + cat1_api_disable_cat1_cmd(1); + LOS_Msleep(200); +#endif + + HttpClient client = (HttpClient){0}; + HttpClientData clientData = (HttpClientData){0}; + + if (ln_fill_auth_header(&client, device_id, device_token) != 0) + return ERRCODE_FAIL; + + /* 组装 JSON */ + cJSON *root = cJSON_CreateObject(); + if (!root) + return ERRCODE_FAIL; + cJSON_AddStringToObject(root, "characterId", character_id); + cJSON_AddStringToObject(root, "eyesColor", eyes_color); + cJSON_AddStringToObject(root, "eyesShape", eyes_shape); + cJSON_AddStringToObject(root, "status", status); + char *body = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + if (!body) + return ERRCODE_FAIL; + + HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024); + if (prep != HTTP_SUCCESS) + { + free(body); + return ERRCODE_FAIL; + } + clientData.postBuf = body; + clientData.postBufLen = (int)strlen(body); + clientData.postContentType = (char *)"application/json"; + + HTTPC_RESULT res = HttpClientPostRequest(&client, LN_EMOJI_REPORT_URL, &clientData); + if (res != HTTP_SUCCESS) + { + free(body); + HttpClientUnprepare(&clientData); + return ERRCODE_FAIL; + } + + int code = HttpClientGetResponseCode(&client); + if (clientData.responseBuf && clientData.responseBufLen > 0) + { + int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; + do_ln_http_server_recv_data(LN_HTTP_RECV_EMOJI_REPORT, clientData.responseBuf, (uint32_t)real_len); + } + if (code != 200) + { + free(body); + HttpClientUnprepare(&clientData); + return ERRCODE_FAIL; + } + + free(body); + HttpClientUnprepare(&clientData); + return ERRCODE_SUCC; +} + +/* 7.2 上报设备所有表情组状态 */ +uint32_t ln_emoji_report_all_http(const char *device_id, + const char *device_token, + const LnEmojiGroup *groups, + int group_count) +{ + if (!device_id || !device_token || (!groups && group_count > 0) || group_count < 0) + return ERRCODE_FAIL; + +#ifdef SUPPORT_CAT1_SERVICE + cat1_api_add_cat1_vote(); + cat1_api_wakeup_cat1(1); + cat1_api_keep_wakeup(1); + cat1_api_disable_cat1_cmd(1); + LOS_Msleep(200); +#endif + + HttpClient client = (HttpClient){0}; + HttpClientData clientData = (HttpClientData){0}; + client.isHttp = false; + client.remotePort = 443; + + if (ln_fill_auth_header(&client, device_id, device_token) != 0) + return ERRCODE_FAIL; + + /* 组装 JSON 数组 */ + cJSON *root = cJSON_CreateObject(); + if (!root) + return ERRCODE_FAIL; + cJSON *arr = cJSON_CreateArray(); + if (!arr) + { + cJSON_Delete(root); + return ERRCODE_FAIL; + } + for (int i = 0; i < group_count; ++i) + { + cJSON *item = cJSON_CreateObject(); + if (!item) + { + continue; + } + cJSON_AddStringToObject(item, "characterId", groups[i].characterId ? groups[i].characterId : ""); + cJSON_AddStringToObject(item, "eyesColor", groups[i].eyesColor ? groups[i].eyesColor : ""); + cJSON_AddStringToObject(item, "eyesShape", groups[i].eyesShape ? groups[i].eyesShape : ""); + cJSON_AddStringToObject(item, "status", groups[i].status ? groups[i].status : ""); + cJSON_AddItemToArray(arr, item); + } + cJSON_AddItemToObject(root, "emojiGroups", arr); + char *body = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + if (!body) + return ERRCODE_FAIL; + + HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024); + if (prep != HTTP_SUCCESS) + { + free(body); + return ERRCODE_FAIL; + } + clientData.postBuf = body; + clientData.postBufLen = (int)strlen(body); + clientData.postContentType = (char *)"application/json"; + + HTTPC_RESULT res = HttpClientPostRequest(&client, LN_EMOJI_REPORT_ALL_URL, &clientData); + if (res != HTTP_SUCCESS) + { + free(body); + HttpClientUnprepare(&clientData); + return ERRCODE_FAIL; + } + + int code = HttpClientGetResponseCode(&client); + if (clientData.responseBuf && clientData.responseBufLen > 0) + { + int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; + do_ln_http_server_recv_data(LN_HTTP_RECV_EMOJI_REPORT_ALL, clientData.responseBuf, (uint32_t)real_len); + } + if (code != 200) + { + free(body); + HttpClientUnprepare(&clientData); + return ERRCODE_FAIL; + } + + free(body); + HttpClientUnprepare(&clientData); + return ERRCODE_SUCC; +} \ No newline at end of file diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c index 190b4d866..0301cb77f 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c @@ -31,7 +31,7 @@ uint32_t ln_ota_get_task_http(const char *device_id, "deviceVersion: %s\r\n" "Content-Type: application/json\r\n", device_id, device_token, - (uapi_ln_get_device_model() ? uapi_ln_get_device_model() : "abmybox_1"), + (uapi_ln_get_device_model() ? uapi_ln_get_device_model() : "AB101"), (uapi_ln_get_device_version() ? uapi_ln_get_device_version() : "1.0.0")); if (hdr <= 0 || hdr >= (int)sizeof(header_buf)) return ERRCODE_FAIL; @@ -130,7 +130,7 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path, return -1; } - HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 3); + HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 4); if (prep != HTTP_SUCCESS) { printf("[OTA] range prepare failed: %d\n", prep); @@ -263,7 +263,7 @@ uint32_t ln_ota_download_flow_http(const char *device_id, int dres; if (g_ln_last_package_size > 0) { - dres = ln_download_file_to_path(g_ln_last_package_url, save_path, g_ln_last_package_size); + dres = ln_download_file_to_path(g_ln_last_package_url, LN_OTA_SAVE_DIR, g_ln_last_package_size); } if (dres != 0) { @@ -275,7 +275,7 @@ uint32_t ln_ota_download_flow_http(const char *device_id, } char done[512]; - int m = snprintf(done, sizeof(done), "{\"status\":%d,\"filePath\":\"%s\",\"md5\":\"%s\"}", 1, save_path, g_ln_last_package_md5); + int m = snprintf(done, sizeof(done), "{\"status\":%d,\"filePath\":\"%s\",\"md5\":\"%s\"}", 1, LN_OTA_SAVE_DIR, g_ln_last_package_md5); if (m > 0) do_ln_http_server_recv_data(LN_HTTP_RECV_OTA_DOWNLOAD_MSG, done, (uint32_t)m); return ERRCODE_SUCC; diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_token.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_token.c index 1c6fa8d23..edec9b592 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_token.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_token.c @@ -265,7 +265,7 @@ uint32_t ln_get_device_token_http(const char *device_id, "deviceVersion: %s\r\n" "Content-Type: application/json\r\n", device_id, device_iccid, timestamp, nonce, signature, - (device_model && device_model[0]) ? device_model : "abmybox_1", + (device_model && device_model[0]) ? device_model : "AB101", (device_version && device_version[0]) ? device_version : "1.0.0"); if (hdr_len <= 0 || hdr_len >= (int)sizeof(header_buf)) { diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/device_info_apis_4.py b/code/sdk/zhihui/leiniao/leiniao_python_test/device_info_apis_4.py index 3a0cb0937..812e9b635 100644 --- a/code/sdk/zhihui/leiniao/leiniao_python_test/device_info_apis_4.py +++ b/code/sdk/zhihui/leiniao/leiniao_python_test/device_info_apis_4.py @@ -44,7 +44,7 @@ def get_device_token() -> Optional[str]: "timestamp": timestamp, "nonce": nonce, "signature": signature, - "deviceModel": "abmybox_1", + "deviceModel": "AB101", "deviceVersion": "1.0.0", "Content-Type": "application/json" } diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/device_websocket_client_6.py b/code/sdk/zhihui/leiniao/leiniao_python_test/device_websocket_client_6.py index 974358896..5c0987406 100644 --- a/code/sdk/zhihui/leiniao/leiniao_python_test/device_websocket_client_6.py +++ b/code/sdk/zhihui/leiniao/leiniao_python_test/device_websocket_client_6.py @@ -28,7 +28,7 @@ class Constants(Enum): TOKEN_API_URL = "https://ambybox-api.test.leiniao.com/ambybox-device-api/v1/auth/getDeviceToken" UPGRADE_TEST_URL = "https://ambybox-api.test.leiniao.com/ambybox-device-api/v1/upgrade/getTask" SIGN_SECRET = "vDdXVXgPUEUM6_piUjz5AEhMvYTSFzQdPaN-ogKnSus=" - DEVICE_MODEL = "abmybox_1" + DEVICE_MODEL = "AB101" DEVICE_VERSION = "1.0.0" HEARTBEAT_INTERVAL = 30 # 心跳间隔(秒) HEARTBEAT_TIMEOUT = 10 # 心跳超时(秒) diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/get_device_token_1.py b/code/sdk/zhihui/leiniao/leiniao_python_test/get_device_token_1.py index ab03e6c87..f8770a2ac 100644 --- a/code/sdk/zhihui/leiniao/leiniao_python_test/get_device_token_1.py +++ b/code/sdk/zhihui/leiniao/leiniao_python_test/get_device_token_1.py @@ -36,7 +36,7 @@ def generate_request_params(): "timestamp": timestamp, "nonce": nonce, "signature": signature, - "deviceModel": "abmybox_1", + "deviceModel": "AB101", "deviceVersion": "1.0.0" } diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/integrated_speech_chatstream_2.py b/code/sdk/zhihui/leiniao/leiniao_python_test/integrated_speech_chatstream_2.py index bde84f590..6b9b82aaf 100644 --- a/code/sdk/zhihui/leiniao/leiniao_python_test/integrated_speech_chatstream_2.py +++ b/code/sdk/zhihui/leiniao/leiniao_python_test/integrated_speech_chatstream_2.py @@ -40,7 +40,7 @@ def get_device_token(): "timestamp": timestamp, "nonce": nonce, "signature": signature, - "deviceModel": "abmybox_1", + "deviceModel": "AB101", "deviceVersion": "1.0.0", "Content-Type": "application/json" } diff --git a/code/sdk/zhihui/leiniao/leiniao_python_test/story_apis_test_3.py b/code/sdk/zhihui/leiniao/leiniao_python_test/story_apis_test_3.py index 4869b285e..7eafdaae6 100644 --- a/code/sdk/zhihui/leiniao/leiniao_python_test/story_apis_test_3.py +++ b/code/sdk/zhihui/leiniao/leiniao_python_test/story_apis_test_3.py @@ -31,7 +31,7 @@ class Config: SIGN_SECRET = "vDdXVXgPUEUM6_piUjz5AEhMvYTSFzQdPaN-ogKnSus=" DEVICE_ID = "DEVICE_PC_TEST_001" DEVICE_ICCID = "86123456789012345678" - DEVICE_MODEL = "abmybox_1" + DEVICE_MODEL = "AB101" DEVICE_VERSION = "1.0.0" # 资源与超时配置 diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_story_player_simple.cpp similarity index 100% rename from code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/src/ln_story_player_simple.cpp rename to code/sdk/zhihui/leiniao/ln_api_cpp/ln_story_player_simple.cpp diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_story_player_simple.h similarity index 100% rename from code/sdk/application/wearable/fd230/nativeapp/nativeui/leiniao/include/ln_story_player_simple.h rename to code/sdk/zhihui/leiniao/ln_api_cpp/ln_story_player_simple.h -- 2.17.1