Baji_Rtc_Toy/main/protocols/volc_rtc_protocol.h
Rdzleo 22b7a70d7d fix: 同步 Kapi 软 RTC 退出五连修到数字人项目(待命音 + 欢迎语杂音)
从 Kapi commit b1577d8 / a3a476f 完整移植 5 个修复,覆盖三类问题:
1. 开机/唤醒后按 BOOT 进 RTC 房间,欢迎语前 1-3 秒杂音
2. 软 RTC 退出(41s 无对话触发 Dialog watchdog)后待命音"卡卡正在待命"无声/杂音/被截
3. 软退出后按 BOOT 唤醒,欢迎语前杂音

【修复 1】OnAudioChannelOpened EnableOutput(true) 后立刻灌 200ms silence
  - 防止 I2S DMA 启动后到 RTC 真实 PCM 到达 1-3s 空窗的杂音

【修复 2】LeaveRoom 加 notify_closed 参数(默认 true 不变老路径)
  - hibernate 路径传 false 跳过 on_audio_channel_closed_ 回调
  - 避免回调链 player_pipeline_close → EnableOutput(false) 误关 codec
    导致待命音无声

【修复 3】LeaveRoom 不再 volc_rtc_destroy, 保留 rtc_handle_
  - 唤醒时 OpenAudioChannel 直接 volc_rtc_start 复用 handle, 不死循环
  - 服务端 AI 任务无需 destroy 也会按 180s 兜底机制清理

【修复 4 - 最隐蔽】LeaveRoom 末尾重置 downlink_is_pcm_ = false
  - 火山 RTC 下行是 PCM, DataCallback 设 downlink_is_pcm_=true
  - 不重置 → PlaySound 的 Opus 包被 OnAudioOutput 当成 raw PCM 字节流
    直接写 codec → 杂音而非待命音
  - 唤醒重连后 DataCallback 收下一包会自动重置, 不影响欢迎语

【修复 5】OnAudioInput 入口加 hibernating_ guard
  - hibernate 期间禁用输入侧, 防止访问关闭的 codec → std::bad_alloc abort
  - 不冻结 OnAudioOutput, 让待命音队列正常被消费

【EnterIdleHibernate 重写】套用 Kapi 新顺序:
  Step 0: hibernating_=true + 50ms (让 OnAudioInput guard 生效)
  Step 1: LeaveRoom(false) (codec output 保留)
  Step 2: background_task->WaitForCompletion
  Step 3: 清空 audio_decode_queue_
  Step 4: EnableInput(false) + close recorder_pipeline
  Step 5: 强制 esp_pm 禁用 Light Sleep
  Step 5.5: EnableOutput(false→true) + 200ms silence (清 LeaveRoom 副作用)
  Step 6: SetDeviceState(idle) → PlaySound 待命音
  Step 7: WaitForAudioPlayback (队列消费完毕)
  Step 7.5: background_task->WaitForCompletion + vTaskDelay(1000)
            (DMA + ES8311 FIFO + 功放尾音衰减, 防尾音截断)
  Step 8: player_pipeline_close
  Step 9: NVS idle_cycles_++
  Step 10: 显示字幕"已自动退出RTC对话..."(数字人特有, 保留)

【WakeFromHibernate】调整 hibernating_=false 顺序
  - 先放下 hibernating_, 让 ToggleChatState 期间 OnAudioInput guard 通过
  - 否则 ToggleChatState 期间音频上行迟迟不开

编译: kapi.bin 0x41c000 (4.21MB), 分区 25% 空闲。
实测三项全通: 欢迎语干净 + 待命音清晰完整 + 唤醒欢迎语干净。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 10:11:36 +08:00

76 lines
3.1 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;// 🔊 关闭音频通道(仅 stop 媒体流,不退出房间)
// Phase 6+:退出 RTC 房间(仅 stop保留 rtc_handle_ 供唤醒复用)
// notify_closed=false 时跳过 on_audio_channel_closed_hibernate 路径用,避免 codec 被回调链关
// 与 CloseAudioChannel 区别CloseAudioChannel 只停媒体流,房间仍占用
void LeaveRoom(bool notify_closed = true) override;
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