+++ /dev/null
-#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 <stdbool.h>
-
-/* ============================== 精简版故事播放器接口 ============================== */
-
-/**
- * @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
+++ /dev/null
-#include "leiniao/include/ln_story_player_simple.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <service/graphic_service.h>
-/* 引入播放器相关头文件 */
-#include "player.h"
-#include "source.h"
-#include "audio_manager.h"
-#include <audio_base_type.h>
-#include <audio_player_service.h>
-#include <custom_utils.h>
-#include <los_base.h>
-/* 引入消息队列相关头文件 */
-#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> player;
- std::shared_ptr<Source> source;
- std::map<std::string, std::string> header;
-
- /* 音频管理 */
- AudioSession sessionId;
- std::shared_ptr<AudioInterrupt> interrupt;
- std::shared_ptr<InterruptListener> 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<SimplePlayerCallback> g_player_callback = nullptr;
-static std::shared_ptr<SimplePlayerInterruptListener> 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<Player>();
- if (!g_simple_player.player)
- {
- printf("[SIMPLE_PLAYER] Player create failed\n");
- return false;
- }
-
- g_player_callback = std::make_shared<SimplePlayerCallback>(&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<Source>(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<SimplePlayerInterruptListener>(impl);
- impl->interrupt = std::make_shared<AudioInterrupt>();
- 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 */
+++ /dev/null
-#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 <stdbool.h>
-
-/* ============================== 精简版故事播放器接口 ============================== */
-
-/**
- * @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
+++ /dev/null
-#include "leiniao/include/ln_story_player_simple.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <service/graphic_service.h>
-/* 引入播放器相关头文件 */
-#include "player.h"
-#include "source.h"
-#include "audio_manager.h"
-#include <audio_base_type.h>
-#include <audio_player_service.h>
-#include <custom_utils.h>
-#include <los_base.h>
-/* 引入消息队列相关头文件 */
-#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> player;
- std::shared_ptr<Source> source;
- std::map<std::string, std::string> header;
-
- /* 音频管理 */
- AudioSession sessionId;
- std::shared_ptr<AudioInterrupt> interrupt;
- std::shared_ptr<InterruptListener> 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<SimplePlayerCallback> g_player_callback = nullptr;
-static std::shared_ptr<SimplePlayerInterruptListener> 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<Player>();
- if (!g_simple_player.player)
- {
- printf("[SIMPLE_PLAYER] Player create failed\n");
- return false;
- }
-
- g_player_callback = std::make_shared<SimplePlayerCallback>(&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<Source>(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<SimplePlayerInterruptListener>(impl);
- impl->interrupt = std::make_shared<AudioInterrupt>();
- 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 */
#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);
/* 获取设备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: 优先读本地缓存,判断过期,否则刷新 */
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);
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)
--- /dev/null
+#include "ln_api.h"
+#include <stdio.h>
+#include <string.h>
+#include "cJSON.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* 宏定义 */
+#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
"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;
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);
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)
{
}
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;
"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))
{
"timestamp": timestamp,
"nonce": nonce,
"signature": signature,
- "deviceModel": "abmybox_1",
+ "deviceModel": "AB101",
"deviceVersion": "1.0.0",
"Content-Type": "application/json"
}
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 # 心跳超时(秒)
"timestamp": timestamp,
"nonce": nonce,
"signature": signature,
- "deviceModel": "abmybox_1",
+ "deviceModel": "AB101",
"deviceVersion": "1.0.0"
}
"timestamp": timestamp,
"nonce": nonce,
"signature": signature,
- "deviceModel": "abmybox_1",
+ "deviceModel": "AB101",
"deviceVersion": "1.0.0",
"Content-Type": "application/json"
}
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"
# 资源与超时配置