toy-Kapi_Rtc/main/protocols/volc_rtc_protocol.h
Rdzleo a3a476f857 feat: Phase 6 软退出方案 C+ — 修复待命音无声 + 唤醒重连失败
问题(上次提交 d5239cf 之后实测):
1. EnterIdleHibernate 触发的 LeaveRoom() 顺手关闭了 codec output:
   LeaveRoom → on_audio_channel_closed_ → player_pipeline_close → EnableOutput(false)
   导致后续 PlaySound(P3_KAKA_DAIMING) 入队后 AudioLoop 写不出声音,
   WaitForAudioPlayback 3 秒超时退出, 用户听不到待命音。
2. LeaveRoom 调用 volc_rtc_destroy 后 rtc_handle_ = nullptr,
   WakeFromHibernate → ToggleChatState → OpenAudioChannel 直接返回 false,
   触发 2 秒重试循环, 同时每次失败回退 idle 都重新 PlaySound,
   codec 状态震荡产生杂音, 服务端 AI 任务也无法重新加入房间。

方案 C+ 修复:
- Protocol::LeaveRoom() 新增 bool notify_closed=true 参数 (默认行为不变)。
- VolcRtcProtocol::LeaveRoom(notify_closed):
  * 只 volc_rtc_stop, 不 volc_rtc_destroy, 保留 rtc_handle_ 供唤醒复用。
  * notify_closed=false 时跳过 on_audio_channel_closed_, 不连带关 codec。
- EnterIdleHibernate:
  * 调用 LeaveRoom(false) → codec output 保留。
  * 手动 background_task_->WaitForCompletion + 清空队列 + 关麦克风。
  * SetDeviceState(idle) 后 PlaySound 真正能播出来。
  * WaitForAudioPlayback 完才 player_pipeline_close (这里再正常关 output)。
- WakeFromHibernate:
  * 先放下 hibernating_ 让 AudioLoop guard 通过, 再 ToggleChatState。
  * 因 rtc_handle_ 仍有效, OpenAudioChannel 走 volc_rtc_start 重启路径,
    on_audio_channel_opened_ 回调重开 player_pipeline + 灌 200ms silence。

编译: kapi.bin 0x2e6330 (3.04MB), 分区 42% 空闲。

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

71 lines
2.9 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 供唤醒复用)
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