toy-Kapi_Rtc/main/protocols/volc_rtc_protocol.h
Rdzleo f5a2777abf feat(rtc+ble): A+B+C 三件套 RTC 偶发连接失败修复 + BLE 配网 reboot 修复 (移植自 Baji)
============ 问题与修复 ============

### 问题 1: 配网模式按 BOOT 进入配网 → 设备 reboot
  日志:
    E BT_OSI: future_new unable to allocate memory for the semaphore.
    assert failed: future_ready future.c:64 (future != NULL)

  根因:
    BLE Bluedroid 协议栈初始化时, future_new 分配 semaphore 失败 → 后续 future_ready
    拿到 NULL → assert. 跟 Baji 的 vQueueDelete(NULL) 同性质 — DRAM 不足.
    Kapi 用 LVGL (~50-80 KB DRAM) + RTC SDK 静态 .bss (~30-50 KB),
    BLE Bluedroid stack 抢不到所需内存.

  修复 (sdkconfig):
    CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y
    让 BLE Bluedroid 全局变量动态分配优先走 PSRAM, 释放 ~30 KB DRAM
    给 controller / GATT / WiFi 使用. 修复后 BLE 配网正常.

### A+B+C 三件套 (移植自 Baji commit 70f0cdd, 解决 RTC 偶发连接失败)

  [A] sdkconfig: CONFIG_LWIP_MAX_SOCKETS=10 → 20
    根治火山 RTC SDK 启动时 lwIP socket fd 不足触发 SocketConnection-Lite.c:191
    bind local ip failed → ICE 协商失败 → wait connect bits=0x0 超时.

  [B] application.h/cc + volc_rtc_protocol.h/cc: 失败 3 次后销毁 + 重建 engine
    新增 VolcRtcProtocol::ForceRebuildEngine() public 方法.
    application.cc 加 audio_channel_retry_count_ 重试计数,
    OpenAudioChannel 连续失败 3 次时调用 ForceRebuildEngine 清理 SDK 状态.
    应对 A 修复后仍可能出现的 SDK 内部状态污染 (ICE Agent 异常等).

  [C] volc_rtc_protocol.cc: DIAG_RTC_BIND_ENABLE 一键开关诊断埋点
    在 join_room 前/后 + ForceRebuildEngine 前/后打印 lwIP socket fd / heap /
    psram / WiFi rssi / errno, 偶发失败时直接定位根因.
    验证完成后改 0 关闭, 编译器消除 #if 块, 零运行时开销.

============ 文件改动 ============

  sdkconfig:
    +CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=y    (BLE 配网修复)
    +CONFIG_LWIP_MAX_SOCKETS=10 → 20             (方案 A)

  main/application.h:
    +int audio_channel_retry_count_ = 0;         (RTC 重试计数)

  main/application.cc:
    OpenAudioChannel 失败处加重试计数 + 连续失败 3 次调 ForceRebuildEngine,
    static_cast<VolcRtcProtocol*>(protocol_.get()) (ESP-IDF 默认 -fno-rtti).

  main/protocols/volc_rtc_protocol.h:
    +void ForceRebuildEngine() 声明.

  main/protocols/volc_rtc_protocol.cc:
    +DIAG_RTC_BIND_ENABLE 开关 + diag_count_used_sockets() 工具函数,
    +OpenAudioChannel Pre-Join / Post-Fail DIAG 埋点,
    +ForceRebuildEngine() 实现 (volc_rtc_stop + volc_rtc_destroy + 等 2 秒 + 触发重建).

============ 关联资源 ============
  Baji commit 70f0cdd: feat(rtc): 偶发连接失败完整修复 (A+B+C 三件套)
  Baji commit bffd316: feat(provisioning): BLE 配网完整修复
  全局 CLAUDE.md "BLE 配网 DRAM 紧张完整排查流程" 章节
  项目记忆 project_ble_provisioning_issues.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:15:34 +08:00

77 lines
3.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef _VOLC_RTC_PROTOCOL_H_
#define _VOLC_RTC_PROTOCOL_H_
#include "protocol.h"
#include "volc_rtc.h"
#include "base/volc_device_manager.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <mutex>
#include <vector>
class VolcRtcProtocol : public Protocol {
public:
VolcRtcProtocol();
~VolcRtcProtocol();
void Start() override;
void SendAudio(const std::vector<uint8_t>& data) override;// 🔊 发送音频数据到RTC
void SendPcm(const std::vector<uint8_t>& data) override;// 🔊 发送PCM音频数据到RTC
void SendG711A(const std::vector<uint8_t>& data) override;// 🔊 发送G711A音频数据到RTC
bool OpenAudioChannel() override;// 🔊 打开音频通道
void CloseAudioChannel() override;// 🔊 关闭音频通道
void LeaveRoom(bool notify_closed = true) override;// Phase 6: 退出 RTC 房间stop保留 handle 供唤醒复用)
// 方案 B (移植自 Baji commit 70f0cdd): 强制销毁并重建 RTC engine.
// 当 OpenAudioChannel 连续失败 N 次时调用, 清理 SDK 内部错乱状态
// (lwIP socket fd 残留 / 内部缓存污染), 触发 Phase 6 的 rtc_handle_=nullptr → Start() 重建路径
void ForceRebuildEngine();
bool IsAudioChannelOpened() const override;// 🔊 检查音频通道是否已打开
void SendAbortSpeaking(AbortReason reason) override;// 🔊 发送中止通话请求
void SendStartListening(ListeningMode mode) override;// 🔊 发送开始监听请求
void SendTextMessage(const std::string& text) override;// 🔊 发送文本消息到RTC
void SendFunctionResult(const std::string& tool_call_id, const std::string& content) override;// 🔊 发送函数调用结果到RTC
/**
* @brief 设置Agent配置参数如音色、提示词等
* @param params JSON格式的配置参数字符串
*/
void SetAgentConfig(const std::string& params);
private:
EventGroupHandle_t event_group_handle_;
volc_rtc_t rtc_handle_ = nullptr;
std::mutex rtc_mutex_;
std::string extra_params_; // 存储额外的Agent配置参数
bool is_connected_ = false;
bool is_audio_channel_opened_ = false;
bool iot_ready_ = false;
volc_iot_info_t iot_info_ = {};
size_t opus_bytes_accum_ = 0;
size_t pcm_bytes_accum_ = 0;
size_t g711a_bytes_accum_ = 0;
size_t down_pcm_bytes_accum_ = 0;
size_t down_opus_bytes_accum_ = 0;
int opus_frames_accum_ = 0;
int pcm_frames_accum_ = 0;
int g711a_frames_accum_ = 0;
uint64_t uplink_last_log_us_ = 0;
std::vector<uint8_t> pcm_pending_;
std::vector<uint8_t> g711a_pending_;
bool first_downlink_logged_ = false;
static void MessageCallback(void* context, volc_msg_t* message);
static void DataCallback(void* context, const void* data, size_t len, volc_data_info_t* info);
void ParseServerMessage(const char* message);
void ProcessAudioData(const void* data, int size);
void SendText(const std::string& text) override;
void LogUplinkStatsMaybe();// 打印上传统计信息
void SendCtrl(const std::string& json);// 🔊 发送控制指令到RTC
void SendFunc(const std::string& json);// 🔊 发送函数调用指令到RTC
};
#endif