雷鸟:实现音频流播放,还有bug,播放不流畅
authorcheng <chengyf@zhihuishixun.com>
Tue, 16 Sep 2025 04:10:41 +0000 (12:10 +0800)
committercheng <chengyf@zhihuishixun.com>
Tue, 16 Sep 2025 04:10:41 +0000 (12:10 +0800)
code/sdk/build/config/target_config/brandy/config.py
code/sdk/zhihui/CMakeLists.txt
code/sdk/zhihui/leiniao/audio_stream/CMakeLists.txt [new file with mode: 0644]
code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.cpp [new file with mode: 0644]
code/sdk/zhihui/leiniao/audio_stream/audio_stream_player.h [new file with mode: 0644]
code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c

index 8d03868..9e55702 100644 (file)
@@ -1150,6 +1150,7 @@ target = {
                           "vibrator_service",
                         #   "-:lwip",
                           "thunderbird_toys",#雷鸟
+                          "leiniaoMediaService",
                         #   "cat1Service",
                         #   "cat1Test",
                           "ppp",
index 874d31c..e598e48 100644 (file)
@@ -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 (file)
index 0000000..853ccc5
--- /dev/null
@@ -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 (file)
index 0000000..e74ca6f
--- /dev/null
@@ -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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+// 包含音频管理相关头文件
+#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 (file)
index 0000000..76f300c
--- /dev/null
@@ -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 <stdint.h>
+#include <stdbool.h>
+
+#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
index cb1e08d..fb1a587 100644 (file)
@@ -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/
index bcb0a6f..c5f9df5 100644 (file)
@@ -11,6 +11,7 @@
 #include <osal_msgqueue.h>
 #include <soc_osal.h>
 #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;
             }
         }