From ea186f8a5c6fc352e5695f290dadf72490d4e52f Mon Sep 17 00:00:00 2001 From: cheng Date: Tue, 16 Sep 2025 12:10:41 +0800 Subject: [PATCH] =?utf8?q?=E9=9B=B7=E9=B8=9F=EF=BC=9A=E5=AE=9E=E7=8E=B0?= =?utf8?q?=E9=9F=B3=E9=A2=91=E6=B5=81=E6=92=AD=E6=94=BE=EF=BC=8C=E8=BF=98?= =?utf8?q?=E6=9C=89bug=EF=BC=8C=E6=92=AD=E6=94=BE=E4=B8=8D=E6=B5=81?= =?utf8?q?=E7=95=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../config/target_config/brandy/config.py | 1 + code/sdk/zhihui/CMakeLists.txt | 1 + .../leiniao/audio_stream/CMakeLists.txt | 64 ++ .../audio_stream/audio_stream_player.cpp | 688 ++++++++++++++++++ .../audio_stream/audio_stream_player.h | 157 ++++ .../sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt | 1 + code/sdk/zhihui/leiniao/imsl_ln/ln_api.c | 13 + 7 files changed, 925 insertions(+) create mode 100644 code/sdk/zhihui/leiniao/audio_stream/CMakeLists.txt create mode 100644 code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.cpp create mode 100644 code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.h diff --git a/code/sdk/build/config/target_config/brandy/config.py b/code/sdk/build/config/target_config/brandy/config.py index 8d0386815..9e557023c 100644 --- a/code/sdk/build/config/target_config/brandy/config.py +++ b/code/sdk/build/config/target_config/brandy/config.py @@ -1150,6 +1150,7 @@ target = { "vibrator_service", # "-:lwip", "thunderbird_toys",#雷鸟 + "leiniaoMediaService", # "cat1Service", # "cat1Test", "ppp", diff --git a/code/sdk/zhihui/CMakeLists.txt b/code/sdk/zhihui/CMakeLists.txt index 874d31c7f..e598e4894 100644 --- a/code/sdk/zhihui/CMakeLists.txt +++ b/code/sdk/zhihui/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory_if_exist(config) add_subdirectory_if_exist(UI) add_subdirectory_if_exist(AI) add_subdirectory_if_exist(leiniao) +add_subdirectory_if_exist(leiniao/audio_stream) if("TEST_4G_SDK" IN_LIST DEFINES) add_subdirectory_if_exist(4gsdk) diff --git a/code/sdk/zhihui/leiniao/audio_stream/CMakeLists.txt b/code/sdk/zhihui/leiniao/audio_stream/CMakeLists.txt new file mode 100644 index 000000000..853ccc5f4 --- /dev/null +++ b/code/sdk/zhihui/leiniao/audio_stream/CMakeLists.txt @@ -0,0 +1,64 @@ +#=============================================================================== +# @brief cmake file +# Copyright (c) @CompanyNameMagicTag 2022-2022. All rights reserved. +#=============================================================================== + +set(COMPONENT_NAME "leiniaoMediaService") + +set(PUBLIC_HEADER +) + + +set(PRIVATE_HEADER +${ROOT_DIR}/middleware/services/media/foundation/audiomanager/interfaces/kits/audiomanager +${ROOT_DIR}/middleware/services/media/foundation/audiomanager/interfaces/kits/audiocommon +${ROOT_DIR}/middleware/services/media/foundation/audiomanager/frameworks/audiostream/include +${ROOT_DIR}/middleware/services/media/foundation/sample/wrapper +${ROOT_DIR}/middleware/services/media/foundation/utils/include +) + +set(AI_SOURCES +${CMAKE_CURRENT_SOURCE_DIR}/audio_stream_player.cpp +) + +set(SOURCES + ${AI_SOURCES} +) + +set(COMPONENT_PUBLIC_CCFLAGS +) + +set(COMPONENT_CCFLAGS + -std=c++11 + -Wno-error + -Wno-unused-parameter + -fno-exceptions + -fno-unwind-tables + -fno-asynchronous-unwind-tables + -Wno-undef + -Wno-bitwise-op-parentheses + -Wno-shift-op-parentheses + -nostdlibinc + -nostdinc + -nostdlib + -Wno-c++11-narrowing + -Wno-error=unused-function +) + +set(WHOLE_LINK + true +) + +set(MAIN_COMPONENT + false +) + +set(PUBLIC_DEFINES +SUPPORT_AI_MEDIA_SERVICE +) + +set(PRIVATE_DEFINES +LOCAL_VOICE_CALL +) + +build_component() diff --git a/code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.cpp b/code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.cpp new file mode 100644 index 000000000..e74ca6f16 --- /dev/null +++ b/code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.cpp @@ -0,0 +1,688 @@ +/* + * Copyright (c) CompanyNameMagicTag. 2023-2023. All rights reserved. + * Description: 流式音频播放器实现 + * Author: Media Software Group + * Create: 2023-12-01 + */ + +#include "audio_stream_player.h" +#include +#include +#include +#include +#include + +// 包含音频管理相关头文件 +#include "audio_manager_c_wrapper.h" +#include "media_log.h" +#include "media_thread_adapt.h" +#include "audio_base_type.h" +#include "audio_manager.h" +#include "audio_stream_out.h" + +#ifdef __cplusplus +extern "C" +{ +#endif +// 线程/队列接口 +#include "thread_init.h" +#include "errcode.h" +#include "wearable_log.h" + +// 音频数据队列消息结构 +typedef struct +{ + uint8_t *data; + uint32_t size; + uint32_t timestamp; +} AudioQueueMessage; + +// 音频流播放器上下文 +typedef struct +{ + AudioStreamConfig config; // 音频配置 + AudioStreamState state; // 当前状态 + AudioDataCallback dataCallback; // 数据回调 + PlayStatusCallback statusCallback; // 状态回调 + void *userData; // 用户数据 + + // 线程/队列 + osMessageQueueId_t inputQueue; // 输入播放数据队列 + uint32_t queueMsgCount; // 队列消息数量 + MediaThreadIdHandle taskHandle; // 线程句柄 + char threadName[32]; // 线程名称 + bool isStopped; // 停止标志 + + // 统计信息 + AudioStreamStats stats; // 统计信息 + + // 音频相关 + void *audioStreamHandle; // 音频流句柄 + float volume; // 音量 + float speed; // 播放速度 +} AudioStreamPlayerContext; + +// 默认配置 +static const AudioStreamConfig DEFAULT_CONFIG = { + .sampleRate = 16000, + .channels = 1, + .bitsPerSample = 16, + .bufferSize = 2048, + .queueSize = 10}; + +// 使用全局指针以配合uapi_create_osThread_custom +static AudioStreamPlayerContext *g_audioStreamPlayerCtx = nullptr; + +// 队列创建函数(在线程创建时由系统调用) +static void CreateAudioStreamQueue(void) +{ + if (g_audioStreamPlayerCtx != nullptr) + { + g_audioStreamPlayerCtx->inputQueue = osMessageQueueNew(g_audioStreamPlayerCtx->config.queueSize, + sizeof(AudioQueueMessage), nullptr); + if (g_audioStreamPlayerCtx->inputQueue == nullptr) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"Failed to create audio stream queue"); + } + else + { + g_audioStreamPlayerCtx->queueMsgCount = 0; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"Audio stream queue created successfully"); + } + } +} + +// 播放线程函数(与ai_media_service_ext风格保持一致) +static void AudioStreamPlayThread(void *arg) +{ + (void)arg; + AudioStreamPlayerContext *ctx = g_audioStreamPlayerCtx; + if (ctx == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayThread: context is null"); + return; + } + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayThread: started"); + + while (!ctx->isStopped) + { + if (ctx->inputQueue != nullptr) + { + AudioQueueMessage msg; + osStatus_t status = osMessageQueueGet(ctx->inputQueue, &msg, nullptr, 100); // 100ms超时 + if (status == osOK) + { + if (msg.data != nullptr && msg.size > 0) + { + // 写入音频数据到输出流(重试逻辑) + WRITE_RETRY: + { + int32_t ret = AudioStreamOutStreamWrite(msg.data, msg.size); + if (ret != 0) + { + ctx->stats.retryCount++; + usleep(100000); + goto WRITE_RETRY; + } + ctx->stats.totalBytesPlayed += msg.size; + } + + if (ctx->dataCallback != nullptr) + { + ctx->dataCallback(msg.data, msg.size, ctx->userData); + } + } + + if (msg.data != nullptr) + { + free(msg.data); + } + + if (ctx->queueMsgCount > 0) + { + ctx->queueMsgCount--; + } + ctx->stats.queueCurrentSize = ctx->queueMsgCount; + } + else if (status == osErrorTimeout) + { + continue; + } + else + { + MEDIA_ERR_LOG("AudioStreamPlayThread: queue get error=%d", status); + usleep(10000); + } + } + else + { + usleep(10000); + } + } + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayThread: stopped"); +} + +// 创建音频流播放器 +int32_t AudioStreamPlayerCreate(const AudioStreamConfig *config, AudioStreamPlayerHandle *handle) +{ + if (handle == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerCreate: handle is null"); + return -1; + } + + // 分配上下文内存 + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)malloc(sizeof(AudioStreamPlayerContext)); + if (ctx == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerCreate: failed to allocate memory"); + return -1; + } + + // 初始化上下文 + memset(ctx, 0, sizeof(AudioStreamPlayerContext)); + + // 设置配置 + if (config != nullptr) + { + ctx->config = *config; + } + else + { + ctx->config = DEFAULT_CONFIG; + } + + // 初始化状态 + ctx->state = AUDIO_STREAM_STATE_IDLE; + ctx->volume = 1.0f; + ctx->speed = 1.0f; + + // 队列/线程相关初值 + ctx->inputQueue = nullptr; + ctx->queueMsgCount = 0; + ctx->taskHandle = nullptr; + ctx->threadName[0] = '\0'; + ctx->isStopped = true; + + *handle = (AudioStreamPlayerHandle)ctx; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerCreate: success"); + return 0; +} + +// 初始化音频流播放器 +int32_t AudioStreamPlayerInit(AudioStreamPlayerHandle handle, + AudioDataCallback dataCallback, + PlayStatusCallback statusCallback, + void *userData) +{ + if (handle == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerInit: handle is null"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + + // 设置回调函数 + ctx->dataCallback = dataCallback; + ctx->statusCallback = statusCallback; + ctx->userData = userData; + + // 初始化音频管理器 + if (!AudioManagerInit()) + { + MEDIA_ERR_LOG("AudioStreamPlayerInit: failed to init audio manager"); + return -1; + } + + // 获取音频会话ID + AudioSession sessionId = AudioManagerMakeSessionId(); + if (sessionId == AUDIO_SESSION_ID_NONE) + { + MEDIA_ERR_LOG("AudioStreamPlayerInit: failed to get audio session ID"); + return -1; + } + + // 激活音频中断 + if (AudioManagerActivateInterrupt(AUDIO_STREAM_VOICE_ASSISTANT, sessionId) == INTERRUPT_FAILED) + { + MEDIA_ERR_LOG("AudioStreamPlayerInit: failed to activate audio interrupt"); + return -1; + } + + // 配置音频流 - 参考ai_media_service_ext.cpp的实现 + AudioRendererConfig renderConfig = {}; + renderConfig.audioFormat = MP3; // 使用MP3格式,与原始代码一致 + renderConfig.channelCount = ctx->config.channels; + renderConfig.linkDir = AUDIO_UP_LINK; // 上行链路,用于播放 + renderConfig.sampleRate = ctx->config.sampleRate; + renderConfig.sessionID = sessionId; + renderConfig.streamType = AUDIO_STREAM_VOICE_ASSISTANT; + + // 初始化音频输出流 + int32_t ret = AudioStreamOutInit(renderConfig); + if (ret != 0) + { + MEDIA_ERR_LOG("AudioStreamPlayerInit: failed to init audio stream, ret=%d", ret); + AudioManagerDeactivateInterrupt(); + return -1; + } + + // 保存会话ID + ctx->audioStreamHandle = (void *)(uintptr_t)sessionId; + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerInit: success"); + return 0; +} + +// 开始播放 +int32_t AudioStreamPlayerStart(AudioStreamPlayerHandle handle) +{ + if (handle == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerStart: handle is null"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + + if (ctx->state == AUDIO_STREAM_STATE_PLAYING) + { + MEDIA_WARNING_LOG("AudioStreamPlayerStart: already playing"); + return 0; + } + + // 启动音频流 - 参考ai_media_service_ext.cpp的实现 + int32_t ret = AudioStreamOutPlay(); + if (ret != 0) + { + MEDIA_ERR_LOG("AudioStreamPlayerStart: failed to start audio stream, ret=%d", ret); + return -1; + } + + // 启动播放线程(uapi_create_osThread_custom) + ctx->isStopped = false; + ctx->state = AUDIO_STREAM_STATE_PLAYING; + g_audioStreamPlayerCtx = ctx; + app_task_definition_t task_cfg; + memset(&task_cfg, 0, sizeof(task_cfg)); + task_cfg.create_queue_handle = CreateAudioStreamQueue; + task_cfg.attr.name = "AudioStreamPlayThread"; + task_cfg.attr.priority = (osPriority_t)16; + task_cfg.attr.stack_size = 0x2000; // 8KB + task_cfg.func = AudioStreamPlayThread; + task_cfg.use_mem = USE_EXTEND_MEM; + extern errcode_t uapi_create_osThread_custom(app_task_definition_t * task_cfg); + errcode_t ret_errcode_t = uapi_create_osThread_custom(&task_cfg); + if (ret_errcode_t != ERRCODE_SUCC) + { + MEDIA_ERR_LOG("AudioStreamPlayerStart: failed to create thread: %d", ret_errcode_t); + ctx->isStopped = true; + ctx->state = AUDIO_STREAM_STATE_ERROR; + return -1; + } + ctx->taskHandle = (void *)task_cfg.task_handle; + strncpy(ctx->threadName, task_cfg.attr.name, sizeof(ctx->threadName) - 1); + ctx->threadName[sizeof(ctx->threadName) - 1] = '\0'; + + // 调用状态回调 + if (ctx->statusCallback != nullptr) + { + ctx->statusCallback(AUDIO_STREAM_STATE_PLAYING, ctx->userData); + } + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerStart: success"); + return 0; +} + +// 暂停播放 +int32_t AudioStreamPlayerPause(AudioStreamPlayerHandle handle) +{ + if (handle == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerPause: handle is null"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + + if (ctx->state != AUDIO_STREAM_STATE_PLAYING) + { + MEDIA_WARNING_LOG("AudioStreamPlayerPause: not playing"); + return -1; + } + + ctx->state = AUDIO_STREAM_STATE_PAUSED; + + // 调用状态回调 + if (ctx->statusCallback != nullptr) + { + ctx->statusCallback(AUDIO_STREAM_STATE_PAUSED, ctx->userData); + } + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerPause: success"); + return 0; +} + +// 恢复播放 +int32_t AudioStreamPlayerResume(AudioStreamPlayerHandle handle) +{ + if (handle == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerResume: handle is null"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + + if (ctx->state != AUDIO_STREAM_STATE_PAUSED) + { + MEDIA_WARNING_LOG("AudioStreamPlayerResume: not paused"); + return -1; + } + + ctx->state = AUDIO_STREAM_STATE_PLAYING; + + // 调用状态回调 + if (ctx->statusCallback != nullptr) + { + ctx->statusCallback(AUDIO_STREAM_STATE_PLAYING, ctx->userData); + } + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerResume: success"); + return 0; +} + +// 停止播放 +int32_t AudioStreamPlayerStop(AudioStreamPlayerHandle handle) +{ + if (handle == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerStop: handle is null"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + + if (ctx->state == AUDIO_STREAM_STATE_STOPPED) + { + MEDIA_WARNING_LOG("AudioStreamPlayerStop: already stopped"); + return 0; + } + + // 停止播放线程 + ctx->isStopped = true; + ctx->state = AUDIO_STREAM_STATE_STOPPED; + + // 等待线程结束(若支持) + if (ctx->taskHandle != nullptr) + { + osThreadId_t threadId = (osThreadId_t)ctx->taskHandle; + osThreadJoin(threadId); + ctx->taskHandle = nullptr; + } + + // 停止音频流 - 参考ai_media_service_ext.cpp的实现 + AudioStreamOutStop(); + + // 清理队列 + if (ctx->inputQueue != nullptr) + { + AudioQueueMessage msg; + while (osMessageQueueGet(ctx->inputQueue, &msg, nullptr, 0) == osOK) + { + if (msg.data != nullptr) + { + free(msg.data); + } + } + osMessageQueueDelete(ctx->inputQueue); + ctx->inputQueue = nullptr; + ctx->queueMsgCount = 0; + ctx->stats.queueCurrentSize = 0; + } + + // 停用音频中断 + AudioManagerDeactivateInterrupt(); + + // 调用状态回调 + if (ctx->statusCallback != nullptr) + { + ctx->statusCallback(AUDIO_STREAM_STATE_STOPPED, ctx->userData); + } + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerStop: success"); + return 0; +} + +// 写入音频数据到播放队列 +int32_t AudioStreamPlayerWrite(AudioStreamPlayerHandle handle, const uint8_t *data, uint32_t size) +{ + if (handle == nullptr || data == nullptr || size == 0) + { + MEDIA_ERR_LOG("AudioStreamPlayerWrite: invalid parameters"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + + if (ctx->state != AUDIO_STREAM_STATE_PLAYING && ctx->state != AUDIO_STREAM_STATE_PAUSED) + { + MEDIA_ERR_LOG("AudioStreamPlayerWrite: player not in playing/paused state"); + return -1; + } + + // 分配内存并复制数据 + uint8_t *dataCopy = (uint8_t *)malloc(size); + if (dataCopy == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerWrite: failed to allocate memory"); + return -1; + } + + memcpy(dataCopy, data, size); + + // 发送到消息队列 + if (ctx->inputQueue == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerWrite: queue not ready"); + free(dataCopy); + return -1; + } + + AudioQueueMessage msg; + msg.data = dataCopy; + msg.size = size; + msg.timestamp = (uint32_t)time(nullptr); + + osStatus_t status = osMessageQueuePut(ctx->inputQueue, &msg, 0, 0); + if (status == osOK) + { + ctx->queueMsgCount++; + ctx->stats.queueCurrentSize = ctx->queueMsgCount; + MEDIA_DEBUG_LOG("AudioStreamPlayerWrite: queued %u bytes", size); + return 0; + } + else + { + MEDIA_WARNING_LOG("AudioStreamPlayerWrite: queue full or error=%d, dropping frame", status); + ctx->stats.droppedFrames++; + free(dataCopy); + return -1; + } +} + +// 清空播放队列 +int32_t AudioStreamPlayerFlush(AudioStreamPlayerHandle handle) +{ + if (handle == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerFlush: handle is null"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + if (ctx->inputQueue != nullptr) + { + AudioQueueMessage msg; + while (osMessageQueueGet(ctx->inputQueue, &msg, nullptr, 0) == osOK) + { + if (msg.data != nullptr) + { + free(msg.data); + } + if (ctx->queueMsgCount > 0) + { + ctx->queueMsgCount--; + } + } + ctx->stats.queueCurrentSize = ctx->queueMsgCount; + } + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerFlush: success"); + return 0; +} + +// 获取播放器状态 +AudioStreamState AudioStreamPlayerGetState(AudioStreamPlayerHandle handle) +{ + if (handle == nullptr) + { + return AUDIO_STREAM_STATE_ERROR; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + return ctx->state; +} + +// 获取播放器统计信息 +int32_t AudioStreamPlayerGetStats(AudioStreamPlayerHandle handle, AudioStreamStats *stats) +{ + if (handle == nullptr || stats == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerGetStats: invalid parameters"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + *stats = ctx->stats; + return 0; +} + +// 设置音量 +int32_t AudioStreamPlayerSetVolume(AudioStreamPlayerHandle handle, float volume) +{ + if (handle == nullptr || volume < 0.0f || volume > 1.0f) + { + MEDIA_ERR_LOG("AudioStreamPlayerSetVolume: invalid parameters"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + ctx->volume = volume; + // 设置音频流音量 - 使用AudioManagerSetVolume + AudioManagerSetVolume(AUDIO_STREAM_VOICE_ASSISTANT, (int32_t)(volume * 100)); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerSetVolume: set to %.2f", volume); + return 0; +} + +// 销毁音频流播放器 +int32_t AudioStreamPlayerDestroy(AudioStreamPlayerHandle handle) +{ + if (handle == nullptr) + { + MEDIA_ERR_LOG("AudioStreamPlayerDestroy: handle is null"); + return -1; + } + + AudioStreamPlayerContext *ctx = (AudioStreamPlayerContext *)handle; + + // 停止播放 + AudioStreamPlayerStop(handle); + + // 清空队列 + AudioStreamPlayerFlush(handle); + + // 销毁音频流 - 参考ai_media_service_ext.cpp的实现 + AudioStreamOutDeinit(); + + // 队列已在Stop中销毁 + + // 释放上下文内存 + free(ctx); + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"AudioStreamPlayerDestroy: success"); + return 0; +} + + +/* 音频播放器全局句柄与初始化函数 */ +static AudioStreamPlayerHandle g_ln_audio_player = nullptr; + +/* 音频播放器状态回调函数 */ +static void ln_audio_player_status_cb(int32_t status, void *user) +{ + (void)user; + printf("[LN_AUDIO] status=%d\n", status); +} + +/* 音频数据回调函数 */ +static void ln_audio_player_data_cb(const uint8_t *data, uint32_t size, void *user) +{ + (void)data; + (void)size; + (void)user; +} + +/* 音频播放器初始化函数 */ +uint32_t ln_audio_player_init(void) +{ + if (g_ln_audio_player != nullptr) { + return ERRCODE_SUCC; /* 已初始化 */ + } + + AudioStreamConfig cfg; + cfg.sampleRate = 16000; + cfg.channels = 1; + cfg.bitsPerSample = 16; + cfg.bufferSize = 2048; + cfg.queueSize = 10; + + if (AudioStreamPlayerCreate(&cfg, &g_ln_audio_player) != 0) { + printf("[LN_AUDIO] create failed\n"); + g_ln_audio_player = nullptr; + return ERRCODE_FAIL; + } + + if (AudioStreamPlayerInit(g_ln_audio_player, ln_audio_player_data_cb, ln_audio_player_status_cb, nullptr) != 0) { + printf("[LN_AUDIO] init failed\n"); + AudioStreamPlayerDestroy(g_ln_audio_player); + g_ln_audio_player = nullptr; + return ERRCODE_FAIL; + } + + if (AudioStreamPlayerStart(g_ln_audio_player) != 0) { + printf("[LN_AUDIO] start failed\n"); + AudioStreamPlayerDestroy(g_ln_audio_player); + g_ln_audio_player = nullptr; + return ERRCODE_FAIL; + } + + return ERRCODE_SUCC; +} + +int32_t ln_audio_player_write(const uint8_t* data, uint32_t size) +{ + if (g_ln_audio_player == nullptr) { + if (ln_audio_player_init() != ERRCODE_SUCC) { + return -1; + } + } + return AudioStreamPlayerWrite(g_ln_audio_player, data, size); +} + +#ifdef __cplusplus +}; +#endif \ No newline at end of file diff --git a/code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.h b/code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.h new file mode 100644 index 000000000..76f300c94 --- /dev/null +++ b/code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) CompanyNameMagicTag. 2023-2023. All rights reserved. + * Description: 流式音频播放器接口定义 + * Author: Media Software Group + * Create: 2023-12-01 + */ + +#ifndef AUDIO_STREAM_PLAYER_H +#define AUDIO_STREAM_PLAYER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// 音频播放器句柄 +typedef void* AudioStreamPlayerHandle; + +// 音频数据回调函数类型 +typedef void (*AudioDataCallback)(const uint8_t* data, uint32_t size, void* userData); + +// 播放状态回调函数类型 +typedef void (*PlayStatusCallback)(int32_t status, void* userData); + +// 音频配置结构体 +typedef struct { + uint32_t sampleRate; // 采样率 + uint32_t channels; // 声道数 + uint32_t bitsPerSample; // 位深度 + uint32_t bufferSize; // 缓冲区大小 + uint32_t queueSize; // 队列大小 +} AudioStreamConfig; + +// 播放器状态 +typedef enum { + AUDIO_STREAM_STATE_IDLE = 0, // 空闲状态 + AUDIO_STREAM_STATE_PLAYING, // 播放中 + AUDIO_STREAM_STATE_PAUSED, // 暂停 + AUDIO_STREAM_STATE_STOPPED, // 停止 + AUDIO_STREAM_STATE_ERROR // 错误状态 +} AudioStreamState; + +// 播放器统计信息 +typedef struct { + uint32_t totalBytesPlayed; // 总播放字节数 + uint32_t queueCurrentSize; // 队列当前大小 + uint32_t queueMaxSize; // 队列最大大小 + uint32_t droppedFrames; // 丢帧数 + uint32_t retryCount; // 重试次数 +} AudioStreamStats; + +/** + * @brief 创建音频流播放器 + * @param config 音频配置 + * @param handle 输出参数:播放器句柄 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerCreate(const AudioStreamConfig* config, AudioStreamPlayerHandle* handle); + +/** + * @brief 初始化音频流播放器 + * @param handle 播放器句柄 + * @param dataCallback 音频数据回调函数(可选) + * @param statusCallback 播放状态回调函数(可选) + * @param userData 用户数据 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerInit(AudioStreamPlayerHandle handle, + AudioDataCallback dataCallback, + PlayStatusCallback statusCallback, + void* userData); + +/** + * @brief 开始播放 + * @param handle 播放器句柄 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerStart(AudioStreamPlayerHandle handle); + +/** + * @brief 暂停播放 + * @param handle 播放器句柄 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerPause(AudioStreamPlayerHandle handle); + +/** + * @brief 恢复播放 + * @param handle 播放器句柄 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerResume(AudioStreamPlayerHandle handle); + +/** + * @brief 停止播放 + * @param handle 播放器句柄 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerStop(AudioStreamPlayerHandle handle); + +/** + * @brief 写入音频数据到播放队列 + * @param handle 播放器句柄 + * @param data 音频数据指针 + * @param size 音频数据大小 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerWrite(AudioStreamPlayerHandle handle, const uint8_t* data, uint32_t size); + +/** + * @brief 清空播放队列 + * @param handle 播放器句柄 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerFlush(AudioStreamPlayerHandle handle); + +/** + * @brief 获取播放器状态 + * @param handle 播放器句柄 + * @return 播放器状态 + */ +AudioStreamState AudioStreamPlayerGetState(AudioStreamPlayerHandle handle); + +/** + * @brief 获取播放器统计信息 + * @param handle 播放器句柄 + * @param stats 输出参数:统计信息 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerGetStats(AudioStreamPlayerHandle handle, AudioStreamStats* stats); + +/** + * @brief 设置音量 + * @param handle 播放器句柄 + * @param volume 音量值 (0.0 - 1.0) + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerSetVolume(AudioStreamPlayerHandle handle, float volume); + +/** + * @brief 销毁音频流播放器 + * @param handle 播放器句柄 + * @return 0 成功,-1 失败 + */ +int32_t AudioStreamPlayerDestroy(AudioStreamPlayerHandle handle); + + +uint32_t ln_audio_player_init(void); +int32_t ln_audio_player_write(const uint8_t* data, uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif // AUDIO_STREAM_PLAYER_H diff --git a/code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt b/code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt index cb1e08d00..fb1a58722 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt +++ b/code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt @@ -23,6 +23,7 @@ set(SOURCES set(PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/include # 包含imsl_vad.h和imsl_kws.h的目录 ${ROOT_DIR}/zhihui/4g/cat1Service/include/ + ${ROOT_DIR}/zhihui/leiniao/audio_stream/ ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/include ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include ${ROOT_DIR}/open_source/openharmony/third_party/cJSON/ diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c index bcb0a6f55..c5f9df596 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c @@ -11,6 +11,7 @@ #include #include #include "stdio.h" +#include "audio_stream_player.h" #ifdef BOARD_N9 #include "leiniao/include/ln_story_player_simple.h" @@ -650,6 +651,13 @@ errcode_t uapi_ln_http_server_init(void) return ERRCODE_FAIL; } + /* 初始化音频播放器(流式输出) */ + if (ln_audio_player_init() != ERRCODE_SUCC) + { + printf("[LN_AUDIO] init failed in http server init\n"); + /* 不中断HTTP服务,按需可返回失败 */ + } + return ERRCODE_SUCC; } @@ -795,6 +803,7 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char { if (client_data.contentBlockLen > 0) { + /* 一边下载到文件,一边推送到播放器队列,尝试边下边播 */ size_t written = fwrite(client_data.responseBuf, 1, client_data.contentBlockLen, fd); if (written != client_data.contentBlockLen) { @@ -805,6 +814,10 @@ static uint32_t ln_download_mp3_file_advanced(const char *audio_url, const char HttpClientUnprepare(&client_data); return ERRCODE_FAIL; } + + /* 非阻塞入队,队列满则忽略该块 */ + (void)ln_audio_player_write((const uint8_t*)client_data.responseBuf, (uint32_t)client_data.contentBlockLen); + total_written += written; } } -- 2.17.1