--- /dev/null
+# AI Media Service - 音频录音和播放服务
+
+## 概述
+
+AI Media Service 是一个专为 AI 语音助手应用设计的音频录音和播放服务。它提供了完全分离的录音和播放功能,支持多种音频格式,具有线程安全、资源管理优化、完善的错误处理等特性。
+
+## 核心功能
+
+### 🎙️ 录音功能
+- **实时录音**: 支持连续音频录音
+- **回调机制**: 通过回调函数实时传递录音数据
+- **多格式支持**: PCM、MP3、OPUS 格式
+- **线程安全**: 独立录音线程,不阻塞主线程
+
+### 🔊 播放功能
+- **缓冲播放**: 支持音频缓冲区播放
+- **流式播放**: 支持连续音频流播放
+- **格式兼容**: 与录音格式保持一致
+- **状态控制**: 完整的播放状态管理
+
+### 🛡️ 系统特性
+- **音频焦点管理**: 完整的音频中断处理机制
+- **资源管理**: 自动资源分配和释放
+- **错误处理**: 完善的错误检查和恢复
+- **线程安全**: 多线程环境下的安全操作
+
+## 架构设计
+
+```
+AI Media Service
+├── 录音子系统 (Recording Subsystem)
+│ ├── AIRecordContext - 录音上下文
+│ ├── AIRecordInterruptListener - 录音中断监听器
+│ ├── RecordingThreadFunc - 录音线程函数
+│ └── 录音缓冲区管理
+├── 播放子系统 (Playback Subsystem)
+│ ├── AIPlaybackContext - 播放上下文
+│ ├── AIPlaybackInterruptListener - 播放中断监听器
+│ └── 播放状态管理
+├── 音频管理 (Audio Management)
+│ ├── AudioManager - 音频管理器
+│ ├── AudioInterrupt - 音频中断处理
+│ └── AudioSession - 音频会话管理
+└── 服务管理 (Service Management)
+ ├── 服务初始化/反初始化
+ ├── 全局资源管理
+ └── 线程安全保护
+```
+
+## 数据结构
+
+### 录音上下文 (AIRecordContext)
+```cpp
+typedef struct {
+ AudioSession sessionId; // 音频会话ID
+ AudioInterrupt interrupt; // 音频中断结构体
+ bool isRecordingStopped; // 录音停止标志
+ MediaMutexHandle mutex; // 互斥锁
+ MediaThreadCondHandle cond; // 条件变量
+ MediaThreadIdHandle recordThread; // 录音线程句柄
+ std::shared_ptr<AudioStreamIn> streamIn; // 音频输入流
+ uint8_t *recordBuffer; // 录音缓冲区
+ uint32_t bufferSize; // 缓冲区大小
+ AudioCodecFormat audioFormat; // 音频格式
+ ai_media_record_callback recordCallback; // 录音回调函数
+ bool isInitialized; // 初始化标志
+} AIRecordContext;
+```
+
+### 播放上下文 (AIPlaybackContext)
+```cpp
+typedef struct {
+ AudioSession sessionId; // 音频会话ID
+ AudioInterrupt interrupt; // 音频中断结构体
+ bool isPlaybackStopped; // 播放停止标志
+ MediaMutexHandle mutex; // 互斥锁
+ MediaThreadCondHandle cond; // 条件变量
+ MediaThreadIdHandle playThread; // 播放线程句柄
+ std::shared_ptr<AudioStreamOut> streamOut; // 音频输出流
+ AudioCodecFormat audioFormat; // 音频格式
+ bool isInitialized; // 初始化标志
+} AIPlaybackContext;
+```
+
+## API 接口
+
+### 服务管理接口
+
+#### `ai_media_service_init()`
+```cpp
+int32_t ai_media_service_init(void);
+```
+- **功能**: 初始化 AI 媒体服务
+- **返回值**: 0 成功,-1 失败
+- **说明**: 服务启动时自动调用,创建全局互斥锁
+
+#### `ai_media_service_deinit()`
+```cpp
+void ai_media_service_deinit(void);
+```
+- **功能**: 反初始化 AI 媒体服务
+- **说明**: 服务关闭时自动调用,清理所有资源
+
+### 录音接口
+
+#### `ai_media_record_start()`
+```cpp
+void ai_media_record_start(uint8_t* format);
+```
+- **功能**: 开始音频录音
+- **参数**:
+ - `format`: 音频格式字符串 ("pcm", "mp3", "opus")
+- **调用逻辑**:
+ 1. 检查录音状态,避免重复启动
+ 2. 创建录音上下文和资源
+ 3. 初始化 AudioManager 和音频会话
+ 4. 创建音频中断监听器
+ 5. 配置音频输入流 (16kHz, 单声道, 上行链路)
+ 6. 启动音频流和录音线程
+ 7. 设置录音状态为活跃
+
+#### `ai_media_record_stop()`
+```cpp
+void ai_media_record_stop(void);
+```
+- **功能**: 停止音频录音
+- **调用逻辑**:
+ 1. 检查录音状态
+ 2. 通知录音线程停止
+ 3. 等待录音线程结束
+ 4. 停止音频流并释放资源
+ 5. 释放音频中断和会话
+ 6. 清理录音上下文
+
+#### `ai_media_get_record_status()`
+```cpp
+uint8_t ai_media_get_record_status(void);
+```
+- **功能**: 获取录音状态
+- **返回值**: 0 停止,1 录音中
+
+#### `ai_media_set_record_callback()`
+```cpp
+void ai_media_set_record_callback(ai_media_record_callback callback);
+```
+- **功能**: 设置录音数据回调函数
+- **参数**:
+ - `callback`: 回调函数指针
+- **回调函数签名**:
+ ```cpp
+ typedef void (*ai_media_record_callback)(uint8_t *data, uint32_t size);
+ ```
+
+### 播放接口
+
+#### `ai_media_play_start()`
+```cpp
+void ai_media_play_start(uint8_t* format);
+```
+- **功能**: 开始音频播放
+- **参数**:
+ - `format`: 音频格式字符串 ("pcm", "mp3", "opus")
+- **调用逻辑**:
+ 1. 检查播放状态,避免重复启动
+ 2. 创建播放上下文和资源
+ 3. 初始化 AudioManager 和音频会话
+ 4. 创建音频中断监听器
+ 5. 配置音频输出流 (16kHz, 单声道, 下行链路)
+ 6. 启动音频流
+ 7. 设置播放状态为活跃
+
+#### `ai_media_play_stop()`
+```cpp
+void ai_media_play_stop(void);
+```
+- **功能**: 停止音频播放
+- **调用逻辑**:
+ 1. 检查播放状态
+ 2. 设置播放停止标志
+ 3. 停止音频流并释放资源
+ 4. 释放音频中断和会话
+ 5. 清理播放上下文
+
+#### `ai_media_get_play_status()`
+```cpp
+uint8_t ai_media_get_play_status(void);
+```
+- **功能**: 获取播放状态
+- **返回值**: 0 停止,1 播放中
+
+#### `ai_media_play_buffer()`
+```cpp
+void ai_media_play_buffer(uint8_t *data, uint32_t size);
+```
+- **功能**: 播放音频缓冲区数据
+- **参数**:
+ - `data`: 音频数据缓冲区
+ - `size`: 数据大小
+- **说明**: 需要先调用 `ai_media_play_start()` 启动播放
+
+## 调用逻辑和时序
+
+### 录音流程时序图
+```
+应用程序 AI Media Service AudioManager 录音线程
+ | | | |
+ |-- ai_media_record_start() -->| | |
+ | |-- Initialize() ----->| |
+ | |-- MakeSessionId() --->| |
+ | |-- ActivateAudioInterrupt() -->| |
+ | |-- AudioStreamInInit() ------>| |
+ | |-- AudioStreamInStart() ----->| |
+ | |-- 创建录音线程 ------------->| |
+ | | | |-- 录音循环 -->
+ | | | | 获取音频数据
+ | | | | 调用回调函数
+ |-- 录音回调 <---------------------------| | |
+ | 处理音频数据 | | | |
+ | | | |<-- 继续录音 --
+ |-- ai_media_record_stop() --->| | |
+ | |-- 通知线程停止 ------>| |
+ | |-- 等待线程结束 <------| |
+ | |-- AudioStreamInStop() ------>|
+ | |-- DeactivateAudioInterrupt() ->|
+ | |-- 清理资源 | |
+```
+
+### 播放流程时序图
+```
+应用程序 AI Media Service AudioManager
+ | | |
+ |-- ai_media_play_start() ---->| |
+ | |-- Initialize() ----->|
+ | |-- MakeSessionId() --->|
+ | |-- ActivateAudioInterrupt() -->|
+ | |-- AudioStreamOutInit() ----->|
+ | |-- AudioStreamOutPlay() ----->|
+ | | |
+ |-- ai_media_play_buffer() --->| |
+ | (音频数据) |-- AudioStreamOutStreamWrite() ->|
+ | | |
+ |-- ai_media_play_buffer() --->| |
+ | (更多数据) |-- AudioStreamOutStreamWrite() ->|
+ | | |
+ |-- ai_media_play_stop() ------>| |
+ | |-- AudioStreamOutStop() ------>|
+ | |-- DeactivateAudioInterrupt() ->|
+ | |-- 清理资源 |
+```
+
+## 使用示例
+
+### 基本录音示例
+```cpp
+#include "ai_media.h"
+
+// 录音数据回调函数
+void record_callback(uint8_t *data, uint32_t size) {
+ printf("收到录音数据: %u 字节\n", size);
+ // 处理录音数据 - 保存到文件、发送到网络等
+ // save_to_file(data, size);
+}
+
+int main() {
+ // 1. 设置录音回调
+ ai_media_set_record_callback(record_callback);
+
+ // 2. 开始录音 (PCM格式)
+ ai_media_record_start((uint8_t*)"pcm");
+
+ // 3. 检查录音状态
+ if (ai_media_get_record_status() == 1) {
+ printf("录音已开始\n");
+ }
+
+ // 4. 录音10秒
+ sleep(10);
+
+ // 5. 停止录音
+ ai_media_record_stop();
+ printf("录音已停止\n");
+
+ return 0;
+}
+```
+
+### 基本播放示例
+```cpp
+#include "ai_media.h"
+
+int main() {
+ // 1. 开始播放 (PCM格式)
+ ai_media_play_start((uint8_t*)"pcm");
+
+ // 2. 检查播放状态
+ if (ai_media_get_play_status() == 1) {
+ printf("播放已开始\n");
+ }
+
+ // 3. 播放音频数据
+ uint8_t audio_buffer[1024];
+ // ... 填充音频数据 ...
+ // load_audio_data(audio_buffer, 1024);
+
+ ai_media_play_buffer(audio_buffer, 1024);
+
+ // 4. 可以继续播放更多数据
+ for (int i = 0; i < 100; i++) {
+ // load_next_chunk(audio_buffer, 1024);
+ ai_media_play_buffer(audio_buffer, 1024);
+ usleep(50000); // 50ms 延迟
+ }
+
+ // 5. 停止播放
+ ai_media_play_stop();
+ printf("播放已停止\n");
+
+ return 0;
+}
+```
+
+### 录音播放循环示例
+```cpp
+#include "ai_media.h"
+#include <vector>
+
+std::vector<uint8_t> recorded_data;
+
+// 录音回调 - 收集录音数据
+void collect_callback(uint8_t *data, uint32_t size) {
+ recorded_data.insert(recorded_data.end(), data, data + size);
+ printf("累计录音数据: %zu 字节\n", recorded_data.size());
+}
+
+int record_and_playback_demo() {
+ printf("=== 录音播放演示 ===\n");
+
+ // 1. 录音阶段
+ printf("开始录音...\n");
+ ai_media_set_record_callback(collect_callback);
+ ai_media_record_start((uint8_t*)"pcm");
+
+ sleep(5); // 录音5秒
+
+ ai_media_record_stop();
+ printf("录音完成,共录制 %zu 字节\n", recorded_data.size());
+
+ // 2. 播放阶段
+ printf("开始播放录音...\n");
+ ai_media_play_start((uint8_t*)"pcm");
+
+ // 分块播放录音数据
+ const size_t chunk_size = 1024;
+ size_t bytes_played = 0;
+
+ while (bytes_played < recorded_data.size()) {
+ size_t current_chunk = std::min(chunk_size, recorded_data.size() - bytes_played);
+ ai_media_play_buffer(recorded_data.data() + bytes_played, current_chunk);
+ bytes_played += current_chunk;
+
+ printf("已播放: %zu/%zu 字节\n", bytes_played, recorded_data.size());
+ usleep(50000); // 50ms 延迟
+ }
+
+ ai_media_play_stop();
+ printf("播放完成\n");
+
+ return 0;
+}
+```
+
+## 配置参数
+
+### 音频配置
+- **采样率**: 16000 Hz (固定)
+- **通道数**: 1 (单声道,适合语音应用)
+- **音频流类型**: AUDIO_STREAM_VOICE_ASSISTANT
+- **录音缓冲区**: 2048 字节 (2KB)
+- **链路方向**:
+ - 录音: AUDIO_UP_LINK (上行链路)
+ - 播放: AUDIO_DOWN_LINK (下行链路)
+
+### 支持的音频格式
+| 格式 | 描述 | 用途 |
+|------|------|------|
+| PCM | 脉冲编码调制,未压缩 | 高质量录音,实时处理 |
+| MP3 | MPEG Audio Layer III | 压缩存储,网络传输 |
+| OPUS | 开源音频编解码器 | 低延迟,高质量语音 |
+
+## 中断处理机制
+
+### 录音中断处理
+```cpp
+class AIRecordInterruptListener : public InterruptListener {
+ void OnInterrupt(int32_t type, int32_t hint) override {
+ // INTERRUPT_TYPE_BEGIN + INTERRUPT_HINT_PAUSE: 暂停录音
+ // INTERRUPT_TYPE_END + INTERRUPT_HINT_RESUME: 恢复录音
+ // INTERRUPT_TYPE_END + INTERRUPT_HINT_STOP: 停止录音
+ }
+};
+```
+
+### 播放中断处理
+```cpp
+class AIPlaybackInterruptListener : public InterruptListener {
+ void OnInterrupt(int32_t type, int32_t hint) override {
+ // INTERRUPT_TYPE_BEGIN + INTERRUPT_HINT_PAUSE: 暂停播放
+ // INTERRUPT_TYPE_END + INTERRUPT_HINT_RESUME: 恢复播放
+ // INTERRUPT_TYPE_END + INTERRUPT_HINT_STOP: 停止播放
+ }
+};
+```
+
+## 错误处理
+
+### 常见错误类型
+1. **初始化失败**
+ - AudioManager 初始化失败
+ - 音频会话创建失败
+ - 资源分配失败
+
+2. **音频流错误**
+ - 音频流初始化失败
+ - 音频流启动失败
+ - 数据读写失败
+
+3. **线程错误**
+ - 录音线程创建失败
+ - 线程同步失败
+
+### 错误处理策略
+- **完整的错误检查**: 每个关键操作都有错误检查
+- **资源清理**: 错误发生时自动清理已分配的资源
+- **状态恢复**: 错误后恢复到安全状态
+- **详细日志**: 提供详细的错误信息用于调试
+
+## 性能优化
+
+### 内存优化
+- 固定大小的录音缓冲区 (2KB)
+- 智能指针管理音频流对象
+- 及时释放不需要的资源
+
+### 线程优化
+- 独立的录音线程,避免阻塞主线程
+- 合理的线程属性设置
+- 最小化锁竞争
+
+### 音频优化
+- 适中的缓冲区大小,平衡延迟和性能
+- 流式处理,减少内存占用
+- 高效的数据传输
+
+## 调试和监控
+
+### 日志系统
+```cpp
+MEDIA_INFO_LOG("一般信息日志");
+MEDIA_WARNING_LOG("警告信息日志");
+MEDIA_ERR_LOG("错误信息日志");
+```
+
+### 状态监控
+- 实时录音/播放状态查询
+- 资源使用情况监控
+- 线程状态监控
+
+### 调试技巧
+1. 启用详细日志输出
+2. 监控音频数据流
+3. 检查资源分配和释放
+4. 验证线程同步
+
+## 依赖项
+
+### 系统依赖
+- OpenHarmony 音频系统
+- AudioManager 和 AudioStream 接口
+- 线程和同步原语支持
+
+### 头文件依赖
+```cpp
+#include "audio_base_type.h" // 音频基础类型
+#include "cpp/audio_manager.h" // C++ 音频管理器
+#include "cpp/audio_stream_in.h" // C++ 音频输入流
+#include "cpp/audio_stream_out.h" // C++ 音频输出流
+#include "media_thread_adapt.h" // 线程适配层
+```
+
+## 总结
+
+AI Media Service 提供了一个完整、高效、易用的音频录音和播放解决方案:
+
+### ✅ 核心优势
+- **功能完整**: 支持录音、播放、格式转换等完整功能
+- **架构清晰**: 录音和播放功能完全分离,职责明确
+- **线程安全**: 完善的多线程同步机制
+- **资源管理**: 自动化的资源分配和释放
+- **错误处理**: 完善的错误检查和恢复机制
+- **易于使用**: 简洁的 API 接口,清晰的调用逻辑
+
+### 🎯 适用场景
+- AI 语音助手应用
+- 语音识别和合成
+- 音频录制和播放
+- 实时音频处理
+- 语音通话应用
+
+### 🚀 扩展性
+- 支持新的音频格式
+- 可扩展的音频处理算法
+- 灵活的配置参数
+- 模块化的架构设计
+
+这为 AI 应用提供了一个稳定、高效、功能完整的音频处理基础设施。
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) CompanyNameMagicTag. 2023-2023. All rights reserved.
+ * Description: AI Media Service - Refactored for separated recording and playback
+ * Author: Media Software Group
+ * Create: 2023-07-06
+ * Modified: 2024-12-28 - Separated recording and playback functionality
+ */
+
+#include "ai_media.h"
+
+#include <climits>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string>
+#include <iostream>
+#include <unistd.h>
+#include "securec.h"
+
+// Include audio base types first to avoid conflicts
+#include "audio_base_type.h"
+#include "cpp/audio_manager.h"
+#include "cpp/audio_stream_in.h"
+#include "cpp/audio_stream_out.h"
+
+#include "media_log.h"
+#include "media_thread_adapt.h"
+#include "audio_manager_c_wrapper.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+using namespace::Audio;
+
+#define MEDIA_CMD_LEN_MAX 128
+#define MEDIA_UNUSED(x) ((void)(x))
+#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))
+
+const int32_t NUMBER_BASE = 10;
+const int32_t CHANNEL_COUNT = 1; // Changed to 1 for AI applications
+const int32_t SAMPLERATE_16000 = 16000;
+const uint32_t RECORD_BUFFER_SIZE = 0x800; // 2KB buffer for recording
+
+/* ==================== AI Media Context Structures ==================== */
+
+// Recording context structure
+typedef struct {
+ AudioSession sessionId;
+ AudioInterrupt interrupt;
+ bool isRecordingStopped;
+ MediaMutexHandle mutex;
+ MediaThreadCondHandle cond;
+ MediaThreadIdHandle recordThread;
+ std::shared_ptr<AudioStreamIn> streamIn;
+ uint8_t *recordBuffer;
+ uint32_t bufferSize;
+ AudioCodecFormat audioFormat;
+ ai_media_record_callback recordCallback;
+ bool isInitialized;
+} AIRecordContext;
+
+// Playback context structure
+typedef struct {
+ AudioSession sessionId;
+ AudioInterrupt interrupt;
+ bool isPlaybackStopped;
+ MediaMutexHandle mutex;
+ MediaThreadCondHandle cond;
+ MediaThreadIdHandle playThread;
+ std::shared_ptr<AudioStreamOut> streamOut;
+ AudioCodecFormat audioFormat;
+ bool isInitialized;
+} AIPlaybackContext;
+
+/* ==================== Interrupt Listener Classes ==================== */
+
+class AIRecordInterruptListener : public InterruptListener {
+public:
+ explicit AIRecordInterruptListener(AIRecordContext *ctx) : context(ctx) {}
+ ~AIRecordInterruptListener() override {}
+
+ void OnInterrupt(int32_t type, int32_t hint) override
+ {
+ MEDIA_INFO_LOG("AI Record OnInterrupt, type:%d, hint:%d", type, hint);
+
+ if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_PAUSE) {
+ MEDIA_INFO_LOG("AI record pause signal");
+ }
+ if (type == INTERRUPT_TYPE_END && hint == INTERRUPT_HINT_RESUME) {
+ MEDIA_INFO_LOG("AI record resume signal");
+ }
+ if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_STOP) {
+ MEDIA_INFO_LOG("AI record stop begin signal");
+ }
+ if (type == INTERRUPT_TYPE_END && hint == INTERRUPT_HINT_STOP) {
+ MEDIA_INFO_LOG("AI record stop end signal");
+ if (context != nullptr) {
+ context->isRecordingStopped = true;
+ }
+ }
+ }
+
+private:
+ AIRecordContext *context;
+};
+
+class AIPlaybackInterruptListener : public InterruptListener {
+public:
+ explicit AIPlaybackInterruptListener(AIPlaybackContext *ctx) : context(ctx) {}
+ ~AIPlaybackInterruptListener() override {}
+
+ void OnInterrupt(int32_t type, int32_t hint) override
+ {
+ MEDIA_INFO_LOG("AI Playback OnInterrupt, type:%d, hint:%d", type, hint);
+
+ if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_PAUSE) {
+ MEDIA_INFO_LOG("AI playback pause signal");
+ }
+ if (type == INTERRUPT_TYPE_END && hint == INTERRUPT_HINT_RESUME) {
+ MEDIA_INFO_LOG("AI playback resume signal");
+ }
+ if (type == INTERRUPT_TYPE_BEGIN && hint == INTERRUPT_HINT_STOP) {
+ MEDIA_INFO_LOG("AI playback stop begin signal");
+ }
+ if (type == INTERRUPT_TYPE_END && hint == INTERRUPT_HINT_STOP) {
+ MEDIA_INFO_LOG("AI playback stop end signal");
+ if (context != nullptr) {
+ context->isPlaybackStopped = true;
+ }
+ }
+ }
+
+private:
+ AIPlaybackContext *context;
+};
+
+/* ==================== Global Variables ==================== */
+
+static MediaMutexHandle g_aiMediaMutex = nullptr;
+static AIRecordContext *g_recordContext = nullptr;
+static AIPlaybackContext *g_playbackContext = nullptr;
+static Audio::AudioManager& g_amInstance = Audio::AudioManager::GetInstance();
+
+// Recording status flags
+static uint8_t g_recordStatus = 0; // 0: stopped, 1: recording
+static uint8_t g_playStatus = 0; // 0: stopped, 1: playing
+
+/* ==================== Audio Format Helper Functions ==================== */
+
+/**
+ * @brief Convert string format to AudioCodecFormat enum
+ * @param format Format string ("pcm", "mp3", "opus")
+ * @return AudioCodecFormat enum value
+ */
+static AudioCodecFormat GetAudioFormatFromString(const uint8_t* format)
+{
+ if (format == nullptr) {
+ return PCM; // Default to PCM
+ }
+
+ if (strcmp((const char*)format, "mp3") == 0) {
+ return MP3;
+ } else if (strcmp((const char*)format, "opus") == 0) {
+ return OPUS;
+ } else {
+ return PCM; // Default to PCM for "pcm" or unknown formats
+ }
+}
+
+/* ==================== Recording Implementation ==================== */
+
+/**
+ * @brief Initialize recording resources
+ * @param context Recording context
+ * @return true on success, false on failure
+ */
+static bool InitRecordingResources(AIRecordContext *context)
+{
+ if (context == nullptr) {
+ MEDIA_ERR_LOG("Recording context is null");
+ return false;
+ }
+
+ // Create mutex and condition variable
+ context->mutex = MediaMutexCreate(nullptr);
+ if (context->mutex == nullptr) {
+ MEDIA_ERR_LOG("Failed to create recording mutex");
+ return false;
+ }
+
+ context->cond = MediaThreadCondCreate();
+ if (context->cond == nullptr) {
+ MEDIA_ERR_LOG("Failed to create recording condition variable");
+ MediaMutexDestroy(&context->mutex);
+ return false;
+ }
+
+ // Allocate recording buffer
+ context->recordBuffer = (uint8_t*)malloc(RECORD_BUFFER_SIZE);
+ if (context->recordBuffer == nullptr) {
+ MEDIA_ERR_LOG("Failed to allocate recording buffer");
+ MediaThreadCondDestroy(&context->cond);
+ MediaMutexDestroy(&context->mutex);
+ return false;
+ }
+
+ context->bufferSize = RECORD_BUFFER_SIZE;
+ context->isInitialized = true;
+ return true;
+}
+
+/**
+ * @brief Cleanup recording resources
+ * @param context Recording context
+ */
+static void CleanupRecordingResources(AIRecordContext *context)
+{
+ if (context == nullptr) {
+ return;
+ }
+
+ if (context->streamIn != nullptr) {
+ context->streamIn->Stop();
+ context->streamIn->Release();
+ context->streamIn.reset();
+ }
+
+ if (context->recordBuffer != nullptr) {
+ free(context->recordBuffer);
+ context->recordBuffer = nullptr;
+ }
+
+ if (context->cond != nullptr) {
+ MediaThreadCondDestroy(&context->cond);
+ context->cond = nullptr;
+ }
+
+ if (context->mutex != nullptr) {
+ MediaMutexDestroy(&context->mutex);
+ context->mutex = nullptr;
+ }
+
+ context->isInitialized = false;
+}
+
+/**
+ * @brief Recording thread function
+ * @param priv Recording context pointer
+ * @return nullptr
+ */
+static void* RecordingThreadFunc(void* priv)
+{
+ AIRecordContext *context = (AIRecordContext*)priv;
+ if (context == nullptr) {
+ MEDIA_ERR_LOG("Recording context is null in thread");
+ return nullptr;
+ }
+
+ MEDIA_INFO_LOG("Recording thread started");
+
+ while (true) {
+ MediaMutexLock(context->mutex);
+ if (context->isRecordingStopped) {
+ MediaMutexUnLock(context->mutex);
+ break;
+ }
+ MediaMutexUnLock(context->mutex);
+
+ // Obtain audio data from stream
+ uint32_t readSize = context->bufferSize;
+ int32_t ret = AudioStreamInObtainBuffer(context->recordBuffer, &readSize);
+
+ if (ret == 0 && readSize > 0) {
+ // Call callback function to deliver recorded data
+ if (context->recordCallback != nullptr) {
+ context->recordCallback(context->recordBuffer, readSize);
+ }
+ } else {
+ // Sleep briefly if no data available
+ usleep(10000); // 10ms
+ }
+ }
+
+ MEDIA_INFO_LOG("Recording thread stopped");
+ return nullptr;
+}
+
+/**
+ * @brief Start audio recording
+ * @param format Audio format string
+ */
+void ai_media_record_start(uint8_t* format)
+{
+ MEDIA_INFO_LOG("Starting AI media recording with format: %s", format ? (char*)format : "pcm");
+
+ MediaMutexLock(g_aiMediaMutex);
+
+ // Check if already recording
+ if (g_recordContext != nullptr && g_recordStatus == 1) {
+ MEDIA_WARNING_LOG("Recording already in progress");
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Create recording context
+ if (g_recordContext == nullptr) {
+ g_recordContext = (AIRecordContext*)malloc(sizeof(AIRecordContext));
+ if (g_recordContext == nullptr) {
+ MEDIA_ERR_LOG("Failed to allocate recording context");
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+ memset(g_recordContext, 0, sizeof(AIRecordContext));
+ }
+
+ // Initialize resources
+ if (!InitRecordingResources(g_recordContext)) {
+ MEDIA_ERR_LOG("Failed to initialize recording resources");
+ free(g_recordContext);
+ g_recordContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Set audio format
+ g_recordContext->audioFormat = GetAudioFormatFromString(format);
+
+ // Request audio focus using C++ API
+ bool success = g_amInstance.Initialize();
+ if (!success) {
+ MEDIA_ERR_LOG("AudioManager init failed");
+ CleanupRecordingResources(g_recordContext);
+ free(g_recordContext);
+ g_recordContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ AudioSession sessionId = g_amInstance.MakeSessionId();
+ if (sessionId == AUDIO_SESSION_ID_NONE) {
+ MEDIA_ERR_LOG("Invalid audio session ID");
+ CleanupRecordingResources(g_recordContext);
+ free(g_recordContext);
+ g_recordContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Create interrupt listener and setup interrupt
+ std::shared_ptr<AIRecordInterruptListener> interruptListener =
+ std::make_shared<AIRecordInterruptListener>(g_recordContext);
+ if (interruptListener == nullptr || interruptListener.get() == nullptr) {
+ MEDIA_ERR_LOG("AI record interrupt listener is nullptr");
+ CleanupRecordingResources(g_recordContext);
+ free(g_recordContext);
+ g_recordContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ AudioInterrupt interrupt = { AUDIO_STREAM_VOICE_ASSISTANT, sessionId, interruptListener };
+ if (g_amInstance.ActivateAudioInterrupt(interrupt) == INTERRUPT_FAILED) {
+ MEDIA_ERR_LOG("Failed to activate audio interrupt");
+ CleanupRecordingResources(g_recordContext);
+ free(g_recordContext);
+ g_recordContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ g_recordContext->interrupt = interrupt;
+
+ g_recordContext->sessionId = sessionId;
+
+ // Configure audio stream input
+ CapturerInputConfig captureConfig = {};
+ captureConfig.audioFormat = g_recordContext->audioFormat;
+ captureConfig.channelCount = CHANNEL_COUNT;
+ captureConfig.linkDir = AUDIO_UP_LINK;
+ captureConfig.sampleRate = SAMPLERATE_16000;
+ captureConfig.sessionID = sessionId;
+ captureConfig.streamType = AUDIO_STREAM_VOICE_ASSISTANT;
+
+ int32_t ret = AudioStreamInInit(captureConfig);
+ if (ret != 0) {
+ MEDIA_ERR_LOG("Audio stream in init failed");
+ g_amInstance.DeactivateAudioInterrupt(g_recordContext->interrupt);
+ CleanupRecordingResources(g_recordContext);
+ free(g_recordContext);
+ g_recordContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ ret = AudioStreamInStart();
+ if (ret != 0) {
+ MEDIA_ERR_LOG("Audio stream in start failed");
+ AudioStreamInDeinit();
+ g_amInstance.DeactivateAudioInterrupt(g_recordContext->interrupt);
+ CleanupRecordingResources(g_recordContext);
+ free(g_recordContext);
+ g_recordContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Start recording thread
+ g_recordContext->isRecordingStopped = false;
+ MediaThreadattr attr = { "AIRecordingThread", 0x2000, THREAD_SCHED_INVALID, 0, false };
+ g_recordContext->recordThread = MediaThreadCreate(RecordingThreadFunc, g_recordContext, &attr);
+
+ if (g_recordContext->recordThread == nullptr) {
+ MEDIA_ERR_LOG("Failed to create recording thread");
+ AudioStreamInStop();
+ AudioStreamInDeinit();
+ g_amInstance.DeactivateAudioInterrupt(g_recordContext->interrupt);
+ CleanupRecordingResources(g_recordContext);
+ free(g_recordContext);
+ g_recordContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ g_recordStatus = 1; // Set recording status
+ MediaMutexUnLock(g_aiMediaMutex);
+
+ MEDIA_INFO_LOG("AI media recording started successfully");
+}
+
+/**
+ * @brief Stop audio recording
+ */
+void ai_media_record_stop(void)
+{
+ MEDIA_INFO_LOG("Stopping AI media recording");
+
+ MediaMutexLock(g_aiMediaMutex);
+
+ if (g_recordContext == nullptr || g_recordStatus == 0) {
+ MEDIA_WARNING_LOG("Recording not in progress");
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Signal recording thread to stop
+ MediaMutexLock(g_recordContext->mutex);
+ g_recordContext->isRecordingStopped = true;
+ MediaThreadCondSignal(g_recordContext->cond);
+ MediaMutexUnLock(g_recordContext->mutex);
+
+ // Wait for thread to finish
+ MediaThreadJoin(&g_recordContext->recordThread);
+
+ // Stop audio stream
+ AudioStreamInStop();
+ AudioStreamInDeinit();
+ g_amInstance.DeactivateAudioInterrupt(g_recordContext->interrupt);
+
+ // Cleanup resources
+ CleanupRecordingResources(g_recordContext);
+ free(g_recordContext);
+ g_recordContext = nullptr;
+
+ g_recordStatus = 0; // Clear recording status
+ MediaMutexUnLock(g_aiMediaMutex);
+
+ MEDIA_INFO_LOG("AI media recording stopped successfully");
+}
+
+/**
+ * @brief Get recording status
+ * @return Recording status (0: stopped, 1: recording)
+ */
+uint8_t ai_media_get_record_status(void)
+{
+ return g_recordStatus;
+}
+
+/**
+ * @brief Set recording callback function
+ * @param callback Callback function for recorded data
+ */
+void ai_media_set_record_callback(ai_media_record_callback callback)
+{
+ MediaMutexLock(g_aiMediaMutex);
+ if (g_recordContext != nullptr) {
+ g_recordContext->recordCallback = callback;
+ }
+ MediaMutexUnLock(g_aiMediaMutex);
+
+ MEDIA_INFO_LOG("Recording callback set");
+}
+
+/* ==================== Playback Implementation ==================== */
+
+/**
+ * @brief Initialize playback resources
+ * @param context Playback context
+ * @return true on success, false on failure
+ */
+static bool InitPlaybackResources(AIPlaybackContext *context)
+{
+ if (context == nullptr) {
+ MEDIA_ERR_LOG("Playback context is null");
+ return false;
+ }
+
+ // Create mutex and condition variable
+ context->mutex = MediaMutexCreate(nullptr);
+ if (context->mutex == nullptr) {
+ MEDIA_ERR_LOG("Failed to create playback mutex");
+ return false;
+ }
+
+ context->cond = MediaThreadCondCreate();
+ if (context->cond == nullptr) {
+ MEDIA_ERR_LOG("Failed to create playback condition variable");
+ MediaMutexDestroy(&context->mutex);
+ return false;
+ }
+
+ context->isInitialized = true;
+ return true;
+}
+
+/**
+ * @brief Cleanup playback resources
+ * @param context Playback context
+ */
+static void CleanupPlaybackResources(AIPlaybackContext *context)
+{
+ if (context == nullptr) {
+ return;
+ }
+
+ if (context->streamOut != nullptr) {
+ context->streamOut->Stop();
+ context->streamOut->Release();
+ context->streamOut.reset();
+ }
+
+ if (context->cond != nullptr) {
+ MediaThreadCondDestroy(&context->cond);
+ context->cond = nullptr;
+ }
+
+ if (context->mutex != nullptr) {
+ MediaMutexDestroy(&context->mutex);
+ context->mutex = nullptr;
+ }
+
+ context->isInitialized = false;
+}
+
+/**
+ * @brief Start audio playback
+ * @param format Audio format string
+ */
+void ai_media_play_start(uint8_t* format)
+{
+ MEDIA_INFO_LOG("Starting AI media playback with format: %s", format ? (char*)format : "pcm");
+
+ MediaMutexLock(g_aiMediaMutex);
+
+ // Check if already playing
+ if (g_playbackContext != nullptr && g_playStatus == 1) {
+ MEDIA_WARNING_LOG("Playback already in progress");
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Create playback context
+ if (g_playbackContext == nullptr) {
+ g_playbackContext = (AIPlaybackContext*)malloc(sizeof(AIPlaybackContext));
+ if (g_playbackContext == nullptr) {
+ MEDIA_ERR_LOG("Failed to allocate playback context");
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+ memset(g_playbackContext, 0, sizeof(AIPlaybackContext));
+ }
+
+ // Initialize resources
+ if (!InitPlaybackResources(g_playbackContext)) {
+ MEDIA_ERR_LOG("Failed to initialize playback resources");
+ free(g_playbackContext);
+ g_playbackContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Set audio format
+ g_playbackContext->audioFormat = GetAudioFormatFromString(format);
+
+ // Request audio focus using C++ API
+ bool success = g_amInstance.Initialize();
+ if (!success) {
+ MEDIA_ERR_LOG("AudioManager init failed");
+ CleanupPlaybackResources(g_playbackContext);
+ free(g_playbackContext);
+ g_playbackContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ AudioSession sessionId = g_amInstance.MakeSessionId();
+ if (sessionId == AUDIO_SESSION_ID_NONE) {
+ MEDIA_ERR_LOG("Invalid audio session ID");
+ CleanupPlaybackResources(g_playbackContext);
+ free(g_playbackContext);
+ g_playbackContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Create interrupt listener and setup interrupt
+ std::shared_ptr<AIPlaybackInterruptListener> interruptListener =
+ std::make_shared<AIPlaybackInterruptListener>(g_playbackContext);
+ if (interruptListener == nullptr || interruptListener.get() == nullptr) {
+ MEDIA_ERR_LOG("AI playback interrupt listener is nullptr");
+ CleanupPlaybackResources(g_playbackContext);
+ free(g_playbackContext);
+ g_playbackContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ AudioInterrupt interrupt = { AUDIO_STREAM_VOICE_ASSISTANT, sessionId, interruptListener };
+ if (g_amInstance.ActivateAudioInterrupt(interrupt) == INTERRUPT_FAILED) {
+ MEDIA_ERR_LOG("Failed to activate audio interrupt");
+ CleanupPlaybackResources(g_playbackContext);
+ free(g_playbackContext);
+ g_playbackContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ g_playbackContext->interrupt = interrupt;
+
+ g_playbackContext->sessionId = sessionId;
+
+ // Configure audio stream output
+ AudioRendererConfig renderConfig = {};
+ renderConfig.audioFormat = g_playbackContext->audioFormat;
+ renderConfig.channelCount = CHANNEL_COUNT;
+ renderConfig.linkDir = AUDIO_DOWN_LINK;
+ renderConfig.sampleRate = SAMPLERATE_16000;
+ renderConfig.sessionID = sessionId;
+ renderConfig.streamType = AUDIO_STREAM_VOICE_ASSISTANT;
+
+ int32_t ret = AudioStreamOutInit(renderConfig);
+ if (ret != 0) {
+ MEDIA_ERR_LOG("Audio stream out init failed");
+ g_amInstance.DeactivateAudioInterrupt(g_playbackContext->interrupt);
+ CleanupPlaybackResources(g_playbackContext);
+ free(g_playbackContext);
+ g_playbackContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ ret = AudioStreamOutPlay();
+ if (ret != 0) {
+ MEDIA_ERR_LOG("Audio stream out play failed");
+ AudioStreamOutDeinit();
+ g_amInstance.DeactivateAudioInterrupt(g_playbackContext->interrupt);
+ CleanupPlaybackResources(g_playbackContext);
+ free(g_playbackContext);
+ g_playbackContext = nullptr;
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ g_playbackContext->isPlaybackStopped = false;
+ g_playStatus = 1; // Set playback status
+ MediaMutexUnLock(g_aiMediaMutex);
+
+ MEDIA_INFO_LOG("AI media playback started successfully");
+}
+
+/**
+ * @brief Stop audio playback
+ */
+void ai_media_play_stop(void)
+{
+ MEDIA_INFO_LOG("Stopping AI media playback");
+
+ MediaMutexLock(g_aiMediaMutex);
+
+ if (g_playbackContext == nullptr || g_playStatus == 0) {
+ MEDIA_WARNING_LOG("Playback not in progress");
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Signal playback to stop
+ MediaMutexLock(g_playbackContext->mutex);
+ g_playbackContext->isPlaybackStopped = true;
+ MediaThreadCondSignal(g_playbackContext->cond);
+ MediaMutexUnLock(g_playbackContext->mutex);
+
+ // Stop audio stream
+ AudioStreamOutStop();
+ AudioStreamOutDeinit();
+ g_amInstance.DeactivateAudioInterrupt(g_playbackContext->interrupt);
+
+ // Cleanup resources
+ CleanupPlaybackResources(g_playbackContext);
+ free(g_playbackContext);
+ g_playbackContext = nullptr;
+
+ g_playStatus = 0; // Clear playback status
+ MediaMutexUnLock(g_aiMediaMutex);
+
+ MEDIA_INFO_LOG("AI media playback stopped successfully");
+}
+
+/**
+ * @brief Get playback status
+ * @return Playback status (0: stopped, 1: playing)
+ */
+uint8_t ai_media_get_play_status(void)
+{
+ return g_playStatus;
+}
+
+/**
+ * @brief Play audio buffer
+ * @param data Audio data buffer
+ * @param size Size of audio data
+ */
+void ai_media_play_buffer(uint8_t *data, uint32_t size)
+{
+ if (data == nullptr || size == 0) {
+ MEDIA_WARNING_LOG("Invalid audio data or size");
+ return;
+ }
+
+ MediaMutexLock(g_aiMediaMutex);
+
+ if (g_playbackContext == nullptr || g_playStatus == 0) {
+ MEDIA_WARNING_LOG("Playback not started, call ai_media_play_start first");
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ // Check if playback is stopped
+ MediaMutexLock(g_playbackContext->mutex);
+ bool isStopped = g_playbackContext->isPlaybackStopped;
+ MediaMutexUnLock(g_playbackContext->mutex);
+
+ if (isStopped) {
+ MEDIA_WARNING_LOG("Playback is stopped");
+ MediaMutexUnLock(g_aiMediaMutex);
+ return;
+ }
+
+ MediaMutexUnLock(g_aiMediaMutex);
+
+ // Write audio data to stream
+ int32_t ret = AudioStreamOutStreamWrite(data, size);
+ if (ret != 0) {
+ MEDIA_ERR_LOG("Failed to write audio data to stream, ret=%d", ret);
+ }
+}
+
+/* ==================== Module Initialization ==================== */
+
+/**
+ * @brief Initialize AI media service
+ * @return 0 on success, -1 on failure
+ */
+int32_t ai_media_service_init(void)
+{
+ if (g_aiMediaMutex != nullptr) {
+ MEDIA_WARNING_LOG("AI media service already initialized");
+ return 0;
+ }
+
+ g_aiMediaMutex = MediaMutexCreate(nullptr);
+ if (g_aiMediaMutex == nullptr) {
+ MEDIA_ERR_LOG("Failed to create AI media mutex");
+ return -1;
+ }
+
+ MEDIA_INFO_LOG("AI media service initialized successfully");
+ return 0;
+}
+
+/**
+ * @brief Deinitialize AI media service
+ */
+void ai_media_service_deinit(void)
+{
+ // Stop recording if active
+ if (g_recordStatus == 1) {
+ ai_media_record_stop();
+ }
+
+ // Stop playback if active
+ if (g_playStatus == 1) {
+ ai_media_play_stop();
+ }
+
+ // Cleanup mutex
+ if (g_aiMediaMutex != nullptr) {
+ MediaMutexDestroy(&g_aiMediaMutex);
+ g_aiMediaMutex = nullptr;
+ }
+
+ MEDIA_INFO_LOG("AI media service deinitialized");
+}
+
+/* ==================== Module Constructor/Destructor ==================== */
+
+// Initialize the service when the module is loaded
+__attribute__((constructor))
+static void ai_media_service_constructor(void)
+{
+ ai_media_service_init();
+}
+
+// Cleanup when the module is unloaded
+__attribute__((destructor))
+static void ai_media_service_destructor(void)
+{
+ ai_media_service_deinit();
+}
+
+#ifdef __cplusplus
+};
+#endif