622 lines
20 KiB
Markdown
622 lines
20 KiB
Markdown
# Kapi_Rtc项目火山RTC替换实现方案
|
||
|
||
## 1. 背景概述
|
||
|
||
当前Kapi_Rtc项目使用WebSocket协议进行音频数据传输和通信,但由于业务需求变化,需要替换为火山RTC(Byte RTC)协议。本方案基于对Airhub_Rtc_h项目中火山RTC实现的分析,结合Kapi_Rtc项目的现有架构,提供详细的替换实现方案。
|
||
|
||
## 2. 技术分析
|
||
|
||
### 2.1 现有WebSocket实现
|
||
|
||
Kapi_Rtc项目当前使用`WebsocketProtocol`类实现通信,主要功能包括:
|
||
|
||
- 建立和维护WebSocket连接
|
||
- 发送和接收音频数据
|
||
- 发送文本消息
|
||
- 处理连接状态变化
|
||
|
||
主要接口:
|
||
```cpp
|
||
void Start() override;
|
||
void SendAudio(const std::vector<uint8_t>& data) override;
|
||
bool OpenAudioChannel() override;
|
||
void CloseAudioChannel() override;
|
||
bool IsAudioChannelOpened() const override;
|
||
void SendText(const std::string& text) override;
|
||
```
|
||
|
||
### 2.2 火山RTC实现(来自Airhub_Rtc_h)
|
||
|
||
Airhub_Rtc_h项目提供了完整的火山RTC封装,主要功能包括:
|
||
|
||
- RTC引擎初始化和销毁
|
||
- 加入/离开房间
|
||
- 发送音频/视频/消息数据
|
||
- 处理RTC事件回调
|
||
|
||
主要接口:
|
||
```cpp
|
||
volc_rtc_t volc_rtc_create(const char* appid, void* context, cJSON* p_config, volc_msg_cb message_callback, volc_data_cb data_callback);
|
||
void volc_rtc_destroy(volc_rtc_t handle);
|
||
int volc_rtc_start(volc_rtc_t rtc, const char* bot_id, volc_iot_info_t* iot_info);
|
||
int volc_rtc_stop(volc_rtc_t handle);
|
||
int volc_rtc_send(volc_rtc_t handle, const void* data, int size, volc_data_info_t* data_info);
|
||
int volc_rtc_interrupt(volc_rtc_t rtc);
|
||
```
|
||
|
||
### 2.3 架构对比
|
||
|
||
| 特性 | WebSocket | 火山RTC |
|
||
|------|-----------|---------|
|
||
| 连接管理 | 简单的TCP连接 | 复杂的RTC连接管理(ICE/STUN/TURN) |
|
||
| 音频质量 | 依赖网络质量 | 自适应网络,支持丢包重传和抗抖动 |
|
||
| 延迟 | 相对较高 | 低延迟(适合实时通信) |
|
||
| 功能丰富度 | 基础的消息和二进制数据传输 | 提供完整的RTC功能(音频/视频/消息) |
|
||
| 集成复杂度 | 低 | 中等 |
|
||
|
||
## 3. 替换方案
|
||
|
||
### 3.1 架构设计
|
||
|
||
保留Kapi_Rtc项目现有的`Protocol`抽象接口,创建新的`VolcRtcProtocol`类继承自`Protocol`,实现与`WebsocketProtocol`相同的接口,从而实现无缝替换。
|
||
|
||
```
|
||
┌─────────────────┐
|
||
│ Application │
|
||
└────────┬────────┘
|
||
│
|
||
┌────────▼────────┐
|
||
│ Protocol │
|
||
└────────┬────────┘
|
||
├───────────────┐
|
||
┌────────▼────────┐ ┌─────▼──────────┐
|
||
│WebsocketProtocol│ │VolcRtcProtocol │
|
||
└─────────────────┘ └────────────────┘
|
||
```
|
||
|
||
### 3.2 实现步骤
|
||
|
||
#### 3.2.1 引入火山RTC依赖
|
||
|
||
1. 将Airhub_Rtc_h项目中的火山RTC相关文件复制到Kapi_Rtc项目:
|
||
- `/components/volc_engine_rtc_lite/` - 火山RTC SDK
|
||
- `/components/common/src/volc_rtc.c` - RTC封装实现
|
||
- `/components/common/include/volc_rtc.h` - RTC封装头文件
|
||
|
||
2. 更新CMakeLists.txt文件,添加火山RTC组件:
|
||
|
||
```cmake
|
||
# 添加火山RTC组件
|
||
set(COMPONENTS ${COMPONENTS} volc_engine_rtc_lite)
|
||
set(COMPONENTS ${COMPONENTS} common)
|
||
```
|
||
|
||
#### 3.2.2 SDK配置与鉴权
|
||
|
||
火山RTC需要以下关键配置项进行鉴权和连接:
|
||
|
||
1. 在`Kconfig.projbuild`中添加配置项:
|
||
|
||
```
|
||
config VOLC_INSTANCE_ID
|
||
string "volcano instance id"
|
||
default ""
|
||
help
|
||
Instance ID for Volc RTC service.
|
||
|
||
config VOLC_PRODUCT_KEY
|
||
string "volcano product key"
|
||
default ""
|
||
help
|
||
Product Key for Volc RTC service.
|
||
|
||
config VOLC_PRODUCT_SECRET
|
||
string "volcano product secret"
|
||
default ""
|
||
help
|
||
Product Secret for Volc RTC service.
|
||
|
||
config VOLC_DEVICE_NAME
|
||
string "volcano device name"
|
||
default ""
|
||
help
|
||
Device Name for Volc RTC service.
|
||
|
||
config VOLC_BOT_ID
|
||
string "volcano bot id"
|
||
default ""
|
||
help
|
||
Bot ID for Volc RTC service.
|
||
|
||
config USE_VOLC_RTC
|
||
bool "Use Volc RTC instead of WebSocket"
|
||
default n
|
||
help
|
||
Select to use Volc RTC protocol instead of WebSocket.
|
||
```
|
||
|
||
2. 在`sdkconfig.defaults`中设置默认值:
|
||
|
||
```
|
||
# Volc RTC Configuration
|
||
CONFIG_VOLC_INSTANCE_ID="your_instance_id"
|
||
CONFIG_VOLC_PRODUCT_KEY="your_product_key"
|
||
CONFIG_VOLC_PRODUCT_SECRET="your_product_secret"
|
||
CONFIG_VOLC_DEVICE_NAME="your_device_name"
|
||
CONFIG_VOLC_BOT_ID="your_bot_id"
|
||
CONFIG_USE_VOLC_RTC=y
|
||
```
|
||
|
||
3. 创建配置JSON模板:
|
||
|
||
```cpp
|
||
// 对话AI配置格式
|
||
#define CONV_AI_CONFIG_FORMAT "{\"ver\": 1,\"iot\":{\"instance_id\":\"%s\",\"product_key\":\"%s\",\"product_secret\":\"%s\",\"device_name\":\"%s\"},\"rtc\":{\"log_level\":1,\"audio\":{\"publish\":true,\"subscribe\":true,\"codec\":4},\"video\":{\"publish\":false,\"subscribe\":false,\"codec\":1},\"params\":[\"{\\\"debug\\\":{\\\"log_to_console\\\":1}}\",\"{\\\"audio\\\":{\\\"codec\\\":{\\\"internal\\\":{\\\"enable\\\":1}}}}\",\"{\\\"rtc\\\":{\\\"access\\\":{\\\"concurrent_requests\\\":1}}}\",\"{\\\"rtc\\\":{\\\"ice\\\":{\\\"concurrent_agents\\\":1}}}\"]}}"
|
||
```
|
||
|
||
#### 3.2.3 创建VolcRtcProtocol类
|
||
|
||
创建新的`volc_rtc_protocol.h`和`volc_rtc_protocol.cc`文件,实现`Protocol`接口:
|
||
|
||
```cpp
|
||
// volc_rtc_protocol.h
|
||
#ifndef _VOLC_RTC_PROTOCOL_H_
|
||
#define _VOLC_RTC_PROTOCOL_H_
|
||
|
||
#include "protocol.h"
|
||
#include "volc_rtc.h"
|
||
#include <mutex>
|
||
#include <cJSON.h>
|
||
|
||
class VolcRtcProtocol : public Protocol {
|
||
public:
|
||
VolcRtcProtocol();
|
||
~VolcRtcProtocol();
|
||
|
||
void Start() override;
|
||
void SendAudio(const std::vector<uint8_t>& data) override;
|
||
bool OpenAudioChannel() override;
|
||
void CloseAudioChannel() override;
|
||
bool IsAudioChannelOpened() const override;
|
||
|
||
private:
|
||
void SendText(const std::string& text) override;
|
||
void OnRtcMessage(volc_msg_t* msg);
|
||
void OnRtcData(const void* data, int data_len, volc_data_info_t* info);
|
||
|
||
volc_rtc_t rtc_handle_ = nullptr;
|
||
std::mutex rtc_mutex_;
|
||
bool is_audio_channel_opened_ = false;
|
||
cJSON* rtc_config_ = nullptr;
|
||
};
|
||
|
||
#endif
|
||
```
|
||
|
||
```cpp
|
||
// volc_rtc_protocol.cc
|
||
#include "volc_rtc_protocol.h"
|
||
#include "board.h"
|
||
#include "system_info.h"
|
||
#include "application.h"
|
||
#include "assets/lang_config.h"
|
||
|
||
#include <cstring>
|
||
#include <cJSON.h>
|
||
#include <esp_log.h>
|
||
|
||
#define TAG "VolcRTC"
|
||
|
||
// RTC消息回调函数
|
||
static void rtc_message_callback(void* context, volc_msg_t* msg) {
|
||
if (context) {
|
||
((VolcRtcProtocol*)context)->OnRtcMessage(msg);
|
||
}
|
||
}
|
||
|
||
// RTC数据回调函数
|
||
static void rtc_data_callback(void* context, const void* data, int data_len, volc_data_info_t* info) {
|
||
if (context) {
|
||
((VolcRtcProtocol*)context)->OnRtcData(data, data_len, info);
|
||
}
|
||
}
|
||
|
||
VolcRtcProtocol::VolcRtcProtocol() {
|
||
// 创建完整的火山RTC配置
|
||
char config_buf[1024] = {0};
|
||
|
||
// 使用配置模板构建完整配置
|
||
snprintf(config_buf, sizeof(config_buf), CONV_AI_CONFIG_FORMAT,
|
||
CONFIG_VOLC_INSTANCE_ID,
|
||
CONFIG_VOLC_PRODUCT_KEY,
|
||
CONFIG_VOLC_PRODUCT_SECRET,
|
||
CONFIG_VOLC_DEVICE_NAME);
|
||
|
||
// 解析配置JSON
|
||
rtc_config_ = cJSON_Parse(config_buf);
|
||
if (!rtc_config_) {
|
||
ESP_LOGE(TAG, "Failed to parse RTC config");
|
||
rtc_config_ = cJSON_CreateObject();
|
||
|
||
// 设置默认配置
|
||
cJSON_AddNumberToObject(rtc_config_, "audio.codec", AUDIO_CODEC_TYPE_OPUS);
|
||
cJSON_AddNumberToObject(rtc_config_, "video.codec", VIDEO_CODEC_TYPE_NONE);
|
||
cJSON_AddBoolToObject(rtc_config_, "audio.publish", true);
|
||
cJSON_AddBoolToObject(rtc_config_, "video.publish", false);
|
||
cJSON_AddBoolToObject(rtc_config_, "audio.subscribe", true);
|
||
cJSON_AddBoolToObject(rtc_config_, "video.subscribe", false);
|
||
cJSON_AddNumberToObject(rtc_config_, "log_level", BYTE_RTC_LOG_LEVEL_INFO);
|
||
}
|
||
}
|
||
|
||
VolcRtcProtocol::~VolcRtcProtocol() {
|
||
CloseAudioChannel();
|
||
|
||
if (rtc_config_) {
|
||
cJSON_Delete(rtc_config_);
|
||
}
|
||
}
|
||
|
||
void VolcRtcProtocol::Start() {
|
||
// RTC协议在OpenAudioChannel时启动,这里不需要额外操作
|
||
}
|
||
|
||
bool VolcRtcProtocol::OpenAudioChannel() {
|
||
std::lock_guard<std::mutex> lock(rtc_mutex_);
|
||
|
||
if (rtc_handle_ != nullptr) {
|
||
ESP_LOGW(TAG, "RTC handle already exists");
|
||
return true;
|
||
}
|
||
|
||
// 创建RTC实例 - 传入完整配置,包括鉴权信息
|
||
const char* app_id = CONFIG_VOLC_INSTANCE_ID; // 使用实例ID作为app_id
|
||
rtc_handle_ = volc_rtc_create(app_id, this, rtc_config_, rtc_message_callback, rtc_data_callback);
|
||
|
||
if (rtc_handle_ == nullptr) {
|
||
ESP_LOGE(TAG, "Failed to create RTC instance");
|
||
SetError(Lang::Strings::SERVER_ERROR);
|
||
return false;
|
||
}
|
||
|
||
// 构造IoT信息
|
||
volc_iot_info_t iot_info = {0};
|
||
|
||
// 设置设备ID
|
||
strncpy(iot_info.device_id, SystemInfo::GetMacAddress().c_str(), sizeof(iot_info.device_id));
|
||
|
||
// 设置产品信息用于鉴权
|
||
strncpy(iot_info.product_key, CONFIG_VOLC_PRODUCT_KEY, sizeof(iot_info.product_key));
|
||
strncpy(iot_info.product_secret, CONFIG_VOLC_PRODUCT_SECRET, sizeof(iot_info.product_secret));
|
||
strncpy(iot_info.device_name, CONFIG_VOLC_DEVICE_NAME, sizeof(iot_info.device_name));
|
||
|
||
// 设置房间信息
|
||
const char* bot_id = CONFIG_VOLC_BOT_ID;
|
||
|
||
// 启动RTC并加入房间 - 内部会处理token生成和鉴权
|
||
int ret = volc_rtc_start(rtc_handle_, bot_id, &iot_info);
|
||
if (ret != 0) {
|
||
ESP_LOGE(TAG, "Failed to start RTC: %d", ret);
|
||
volc_rtc_destroy(rtc_handle_);
|
||
rtc_handle_ = nullptr;
|
||
SetError(Lang::Strings::SERVER_ERROR);
|
||
return false;
|
||
}
|
||
|
||
is_audio_channel_opened_ = true;
|
||
|
||
if (on_audio_channel_opened_ != nullptr) {
|
||
on_audio_channel_opened_();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
void VolcRtcProtocol::CloseAudioChannel() {
|
||
std::lock_guard<std::mutex> lock(rtc_mutex_);
|
||
|
||
if (rtc_handle_ != nullptr) {
|
||
// 停止RTC
|
||
volc_rtc_stop(rtc_handle_);
|
||
|
||
// 销毁RTC实例
|
||
volc_rtc_destroy(rtc_handle_);
|
||
rtc_handle_ = nullptr;
|
||
}
|
||
|
||
is_audio_channel_opened_ = false;
|
||
|
||
if (on_audio_channel_closed_ != nullptr) {
|
||
on_audio_channel_closed_();
|
||
}
|
||
}
|
||
|
||
bool VolcRtcProtocol::IsAudioChannelOpened() const {
|
||
return is_audio_channel_opened_;
|
||
}
|
||
|
||
void VolcRtcProtocol::SendAudio(const std::vector<uint8_t>& data) {
|
||
std::lock_guard<std::mutex> lock(rtc_mutex_);
|
||
|
||
if (!is_audio_channel_opened_ || rtc_handle_ == nullptr) {
|
||
ESP_LOGD(TAG, "RTC not connected, dropping audio data");
|
||
return;
|
||
}
|
||
|
||
// 构造音频数据信息
|
||
volc_data_info_t data_info = {0};
|
||
data_info.type = VOLC_DATA_TYPE_AUDIO;
|
||
data_info.info.audio.data_type = VOLC_AUDIO_DATA_TYPE_OPUS;
|
||
|
||
// 发送音频数据
|
||
int ret = volc_rtc_send(rtc_handle_, data.data(), data.size(), &data_info);
|
||
if (ret != 0) {
|
||
ESP_LOGE(TAG, "Failed to send audio data: %d", ret);
|
||
}
|
||
}
|
||
|
||
void VolcRtcProtocol::SendText(const std::string& text) {
|
||
std::lock_guard<std::mutex> lock(rtc_mutex_);
|
||
|
||
if (!is_audio_channel_opened_ || rtc_handle_ == nullptr) {
|
||
ESP_LOGD(TAG, "RTC not connected, dropping text message");
|
||
return;
|
||
}
|
||
|
||
// 构造消息数据信息
|
||
volc_data_info_t data_info = {0};
|
||
data_info.type = VOLC_DATA_TYPE_MESSAGE;
|
||
data_info.info.message.is_binary = false;
|
||
|
||
// 发送文本消息
|
||
int ret = volc_rtc_send(rtc_handle_, text.c_str(), text.size(), &data_info);
|
||
if (ret != 0) {
|
||
ESP_LOGE(TAG, "Failed to send text message: %d", ret);
|
||
}
|
||
}
|
||
|
||
void VolcRtcProtocol::OnRtcMessage(volc_msg_t* msg) {
|
||
if (!msg) {
|
||
return;
|
||
}
|
||
|
||
switch (msg->code) {
|
||
case VOLC_MSG_CONNECTED:
|
||
ESP_LOGI(TAG, "RTC connected");
|
||
break;
|
||
case VOLC_MSG_DISCONNECTED:
|
||
ESP_LOGI(TAG, "RTC disconnected");
|
||
CloseAudioChannel();
|
||
break;
|
||
case VOLC_MSG_USER_JOINED:
|
||
ESP_LOGI(TAG, "Remote user joined");
|
||
break;
|
||
case VOLC_MSG_USER_OFFLINE:
|
||
ESP_LOGI(TAG, "Remote user offline");
|
||
break;
|
||
case VOLC_MSG_CONV_STATUS:
|
||
ESP_LOGI(TAG, "Conversation status: %d", msg->data.conv_status);
|
||
break;
|
||
default:
|
||
ESP_LOGD(TAG, "Unhandled RTC message: %d", msg->code);
|
||
break;
|
||
}
|
||
}
|
||
|
||
void VolcRtcProtocol::OnRtcData(const void* data, int data_len, volc_data_info_t* info) {
|
||
if (!data || !info) {
|
||
return;
|
||
}
|
||
|
||
switch (info->type) {
|
||
case VOLC_DATA_TYPE_AUDIO:
|
||
if (on_incoming_audio_ != nullptr) {
|
||
on_incoming_audio_(std::vector<uint8_t>((uint8_t*)data, (uint8_t*)data + data_len));
|
||
}
|
||
break;
|
||
case VOLC_DATA_TYPE_MESSAGE:
|
||
if (info->info.message.is_binary) {
|
||
// 处理二进制消息
|
||
ESP_LOGD(TAG, "Received binary message, length: %d", data_len);
|
||
} else {
|
||
// 处理文本消息
|
||
std::string text((char*)data, data_len);
|
||
auto root = cJSON_Parse(text.c_str());
|
||
if (root != nullptr) {
|
||
if (on_incoming_json_ != nullptr) {
|
||
on_incoming_json_(root);
|
||
}
|
||
cJSON_Delete(root);
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
ESP_LOGD(TAG, "Unhandled RTC data type: %d", info->type);
|
||
break;
|
||
}
|
||
|
||
last_incoming_time_ = std::chrono::steady_clock::now();
|
||
}
|
||
```
|
||
|
||
#### 3.2.4 更新Protocol工厂
|
||
|
||
修改创建协议实例的代码,根据配置选择使用WebSocket或火山RTC:
|
||
|
||
```cpp
|
||
// 在application.cc或相关文件中
|
||
#include "websocket_protocol.h"
|
||
#include "volc_rtc_protocol.h"
|
||
|
||
// 创建协议实例
|
||
#ifdef CONFIG_USE_VOLC_RTC
|
||
protocol_ = new VolcRtcProtocol();
|
||
#else
|
||
protocol_ = new WebsocketProtocol();
|
||
#endif
|
||
```
|
||
|
||
#### 3.2.5 添加语音打断支持
|
||
|
||
集成火山RTC的语音打断功能:
|
||
|
||
```cpp
|
||
// 在volc_rtc_protocol.h中添加
|
||
int SendInterrupt();
|
||
|
||
// 在volc_rtc_protocol.cc中实现
|
||
int VolcRtcProtocol::SendInterrupt() {
|
||
std::lock_guard<std::mutex> lock(rtc_mutex_);
|
||
|
||
if (!is_audio_channel_opened_ || rtc_handle_ == nullptr) {
|
||
ESP_LOGD(TAG, "RTC not connected, cannot send interrupt");
|
||
return -1;
|
||
}
|
||
|
||
return volc_rtc_interrupt(rtc_handle_);
|
||
}
|
||
```
|
||
|
||
在`Application`类中添加打断功能:
|
||
|
||
```cpp
|
||
// 在application.h中添加
|
||
void SendInterrupt();
|
||
|
||
// 在application.cc中实现
|
||
void Application::SendInterrupt() {
|
||
if (!protocol_) {
|
||
ESP_LOGE(TAG, "Protocol not initialized");
|
||
return;
|
||
}
|
||
|
||
#ifdef CONFIG_USE_VOLC_RTC
|
||
auto* rtc_protocol = dynamic_cast<VolcRtcProtocol*>(protocol_);
|
||
if (rtc_protocol) {
|
||
rtc_protocol->SendInterrupt();
|
||
}
|
||
#endif
|
||
}
|
||
```
|
||
|
||
## 4. 业务功能实现
|
||
|
||
### 4.1 完整RTC连接通讯实现链路
|
||
|
||
#### 4.1.1 初始化与鉴权流程
|
||
|
||
1. **配置加载**:从Kconfig和sdkconfig加载火山RTC配置(INSTANCE_ID, PRODUCT_KEY, PRODUCT_SECRET, DEVICE_NAME, BOT_ID)
|
||
2. **时间同步**:初始化SNTP服务,确保设备时间与服务器同步(RTC连接需要准确时间戳)
|
||
3. **配置构建**:使用配置模板构建完整的JSON配置字符串,包含鉴权信息
|
||
4. **引擎初始化**:调用`volc_rtc_create()`创建RTC实例,传入配置信息
|
||
5. **IoT信息准备**:构建包含设备ID、产品信息的IoT结构体
|
||
6. **加入房间**:调用`volc_rtc_start()`加入RTC房间,内部自动处理token生成和鉴权
|
||
|
||
#### 4.1.2 启动引擎对话
|
||
|
||
1. **用户触发**:用户通过唤醒词或按钮触发对话
|
||
2. **开始监听**:`Application`类调用`protocol_->SendStartListening()`
|
||
3. **指令发送**:通过火山RTC发送开始监听指令给AI引擎
|
||
4. **音频采集**:启动麦克风采集音频数据
|
||
5. **音频发送**:将处理后的音频数据通过`protocol_->SendAudio()`发送给火山RTC
|
||
6. **AI处理**:火山RTC将音频数据转发给AI引擎进行处理
|
||
7. **结果返回**:AI引擎处理结果通过火山RTC返回给设备
|
||
8. **音频播放**:接收到的音频数据通过`on_incoming_audio_`回调传递给`Application`类播放
|
||
|
||
#### 4.1.3 关闭引擎
|
||
|
||
1. **对话结束**:AI引擎返回对话结束信号或用户手动触发关闭
|
||
2. **离开房间**:`Application`类调用`protocol_->CloseAudioChannel()`
|
||
3. **停止RTC**:调用`volc_rtc_stop()`停止RTC服务
|
||
4. **销毁实例**:调用`volc_rtc_destroy()`销毁RTC实例,释放资源
|
||
|
||
### 4.2 鉴权机制详解
|
||
|
||
火山RTC使用基于产品密钥的鉴权机制:
|
||
|
||
1. **配置信息**:设备需要提前配置INSTANCE_ID, PRODUCT_KEY, PRODUCT_SECRET, DEVICE_NAME
|
||
2. **Token生成**:RTC SDK内部使用产品密钥生成临时token
|
||
3. **Token使用**:在`byte_rtc_join_room()`调用时传入token进行身份验证
|
||
4. **Token过期处理**:SDK监听`on_token_privilege_will_expire`事件,在token即将过期时自动更新
|
||
|
||
### 4.3 音频数据流程
|
||
|
||
1. **音频输入**:麦克风采集的音频数据经过`AudioProcessor`处理后,通过`protocol_->SendAudio()`发送给火山RTC
|
||
2. **音频传输**:火山RTC使用优化的RTC协议传输音频数据,支持丢包重传和抗抖动
|
||
3. **AI处理**:火山引擎接收音频数据,进行语音识别和AI处理
|
||
4. **结果返回**:AI处理结果(音频和文本)通过火山RTC返回给设备
|
||
5. **音频输出**:接收到的音频数据通过`on_incoming_audio_`回调传递给`Application`类,然后播放出来
|
||
|
||
### 4.4 消息处理
|
||
|
||
1. **文本消息**:通过`protocol_->SendText()`发送文本消息
|
||
2. **JSON消息**:接收到的JSON消息通过`on_incoming_json_`回调传递给`Application`类进行处理
|
||
3. **二进制消息**:支持发送和接收二进制消息,用于特殊功能扩展
|
||
|
||
## 5. 测试和验证
|
||
|
||
### 5.1 功能测试
|
||
|
||
1. **连接测试**:验证设备能否成功连接到火山RTC服务器
|
||
2. **音频传输测试**:验证音频数据能否正常发送和接收
|
||
3. **语音打断测试**:验证语音打断功能能否正常工作
|
||
4. **异常处理测试**:验证网络异常或服务器异常时的处理逻辑
|
||
|
||
### 5.2 性能测试
|
||
|
||
1. **延迟测试**:测量音频从采集到播放的延迟
|
||
2. **稳定性测试**:长时间运行测试,验证系统稳定性
|
||
3. **资源占用测试**:监控CPU和内存占用情况
|
||
|
||
### 5.3 兼容性测试
|
||
|
||
1. **设备兼容性**:测试在不同硬件设备上的运行情况
|
||
2. **网络兼容性**:测试在不同网络环境下的运行情况
|
||
|
||
## 6. 注意事项
|
||
|
||
1. **配置管理**:
|
||
- 确保火山RTC的INSTANCE_ID, PRODUCT_KEY, PRODUCT_SECRET, DEVICE_NAME和BOT_ID等配置正确
|
||
- 避免将敏感信息(如PRODUCT_SECRET)硬编码到代码中,应通过配置文件管理
|
||
- 定期更新密钥,确保安全性
|
||
|
||
2. **时间同步**:
|
||
- RTC连接需要准确的设备时间,确保SNTP服务正常工作
|
||
- 在网络不稳定时,考虑使用本地RTC作为备用时间源
|
||
|
||
3. **错误处理**:
|
||
- 完善异常处理逻辑,特别是网络异常和服务器异常情况
|
||
- 实现token过期自动更新机制
|
||
- 添加连接重试逻辑,提高系统稳定性
|
||
|
||
4. **资源管理**:
|
||
- 合理管理RTC资源,避免内存泄漏
|
||
- 在不需要RTC服务时及时关闭连接
|
||
- 监控系统资源占用情况,避免资源耗尽
|
||
|
||
5. **日志管理**:
|
||
- 添加详细的日志,便于调试和问题定位
|
||
- 分类管理日志级别,在生产环境中降低日志级别以减少性能开销
|
||
|
||
6. **版本兼容性**:
|
||
- 确保火山RTC SDK版本与项目兼容
|
||
- 定期更新SDK,获取最新的功能和安全修复
|
||
|
||
7. **网络环境**:
|
||
- 确保设备处于稳定的网络环境中
|
||
- 考虑网络切换场景(如WiFi/4G切换)的处理逻辑
|
||
|
||
## 7. 结论
|
||
|
||
本方案通过创建新的`VolcRtcProtocol`类实现了与`WebsocketProtocol`相同的接口,从而实现了无缝替换。火山RTC提供了更丰富的功能和更好的音频质量,能够满足实时通信的需求。同时,本方案保持了Kapi_Rtc项目的现有架构不变,降低了迁移风险和成本。
|
||
|
||
## 8. 后续支持
|
||
|
||
如果您需要进一步的帮助,我可以:
|
||
1. 协助实现文档中的方案,包括代码编写和配置
|
||
2. 回答关于RTC连接鉴权和SDK配置的具体问题
|
||
3. 提供完整的实时AI对话业务实现链路的技术支持
|
||
4. 帮助进行测试和调试,确保RTC连接和音频传输正常工作
|
||
5. 优化系统性能,提高实时对话的稳定性和响应速度
|
||
|
||
请随时告诉我您的需求。 |