--- /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;
+} ln_story_player_simple_t;
+
+
+static bool ln_story_player_simple_play_file(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");
+ 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\n");
+ impl->player->Stop();
+ impl->player->Reset();
+ }
+ else if (player_state == PLAYER_PAUSED || player_state == PLAYER_IDLE)
+ {
+ printf("[SIMPLE_PLAYER] resetting player\n");
+ impl->player->Reset();
+ }
+ else
+ {
+ printf("[SIMPLE_PLAYER] unknown state, forcing reset\n");
+ impl->player->Stop();
+ impl->player->Reset();
+ }
+
+ // 设置回调函数
+ g_simple_player.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;
+ }
+
+ int32_t ret = impl->player->SetSource(*impl->source);
+ if (ret != 0)
+ {
+ printf("[SIMPLE_PLAYER] set source failed: %d\n", ret);
+ impl->player->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();
+ return false;
+ }
+
+ // 设置音频会话和中断监听
+ AudioManager &amInstance = AudioManager::GetInstance();
+
+ // 如果已经激活过音频中断,先清理
+ if (impl->is_activate_audio_interrupt && impl->interrupt && impl->sessionId != AUDIO_SESSION_ID_NONE)
+ {
+ amInstance.DeactivateAudioInterrupt(*impl->interrupt);
+ impl->is_activate_audio_interrupt = false;
+ printf("[SIMPLE_PLAYER] previous audio interrupt deactivated\n");
+ }
+
+ // 创建新的会话和中断
+ 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 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();
+ amInstance.DeactivateAudioInterrupt(*impl->interrupt);
+ return false;
+ }
+
+ // 开始播放
+ ret = impl->player->Play();
+ if (ret != 0)
+ {
+ printf("[SIMPLE_PLAYER] play failed: %d\n", ret);
+ impl->player->Stop();
+ impl->player->Reset();
+ amInstance.DeactivateAudioInterrupt(*impl->interrupt);
+ return false;
+ }
+
+ impl->is_playing = true;
+ 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] stopping playback\n");
+
+ g_simple_player.should_stop = true;
+ g_simple_player.is_playing = false;
+
+ if (g_simple_player.player)
+ {
+ g_simple_player.player->Stop();
+ g_simple_player.player->Reset();
+ }
+
+ // 清理音频中断
+ ClearActivateAudioInterrupt();
+
+ // 清理资源
+ CleanupResources();
+
+ 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;
+}
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __cplusplus */
#include <soc_osal.h>
#include "stdio.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);
+
static app_task_definition_t ln_send_taskcfg = {0};
static app_task_definition_t ln_recv_taskcfg = {0};
-#define LN_HTTP_BOARD_TASK_SIZE 0x4000
+#define LN_HTTP_BOARD_TASK_SIZE 0x3000
static unsigned long g_ln_send_queue = 0xFFFFFFFF;
static unsigned long g_ln_recv_queue = 0xFFFFFFFF;
printf("LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD:下载OTA升级包\n");
ln_ota_download_flow_http(uapi_ln_get_device_id(), uapi_ln_get_device_token(), uapi_ln_get_device_version());
}
+ 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
+ }
if (mail.data != NULL)
{
printf("free(mail.data)\n");
{
case LN_HTTP_SERVICE_CMD_SSE_RECV:
snprintf(save_dir, sizeof(save_dir), "%s/speech", LN_DATA_PATH);
- snprintf(filename, sizeof(filename), "speech_audio_%d.mp3", segment_index);
+ snprintf(filename, sizeof(filename), "%d.mp3", segment_index);
break;
case LN_HTTP_RECV_STORY_CHOOSE:
snprintf(save_dir, sizeof(save_dir), "%s/story/choose", LN_DATA_PATH);
- snprintf(filename, sizeof(filename), "story_choose_%d.mp3", segment_index);
+ snprintf(filename, sizeof(filename), "%d.mp3", segment_index);
break;
case LN_HTTP_RECV_STORY_RANDOM:
snprintf(save_dir, sizeof(save_dir), "%s/story/random", LN_DATA_PATH);
- snprintf(filename, sizeof(filename), "story_random_%d.mp3", segment_index);
+ snprintf(filename, sizeof(filename), "%d.mp3", segment_index);
break;
default:
snprintf(save_dir, sizeof(save_dir), "%s", LN_DATA_PATH);
- snprintf(filename, sizeof(filename), "audio_%d.mp3", segment_index);
+ snprintf(filename, sizeof(filename), "%d.mp3", segment_index);
break;
}