# 语音通信 > 用户流程:按下 PTT 按钮 → 发送语音 → 松开 PTT 按钮 → 停止语音 > 对应程序流程:前置条件检查 → StartCapture → 循环 TsClient.sendVoice(clientID, codec, frame) → StopCapture --- ## 一、状态树 ### 语音状态 ```mermaid flowchart TD VOICE[语音状态
维护 Opus 帧发送过程] VOICE --> V0[静默 Silent
没有发送语音帧] VOICE --> V1[采集中 Capturing
上层正在生成 Opus 帧] VOICE --> V2[发送中 Sending
循环调用 sendVoice] VOICE --> V3[受阻 Blocked
未连接、采集失败或发送异常] ``` --- ## 二、时序:发送语音帧与异常停止 ```mermaid sequenceDiagram participant UI as PTT 控件
接收按下、松开和取消操作 participant VFSM as 语音发送状态机
检查连接与频道前置条件 participant CAP as 音频采集与编码器
生成原始 Opus 帧 participant BRIDGE as TSBridge
调用 TsClient API participant SDK as Go SDK (gomobile)
通过 SendVoice 发送语音 participant TS as TeamSpeak 服务器
转发语音给频道成员 UI->>VFSM: PttPressed VFSM->>VFSM: 检查 connected 和 joinedChannel alt 前置条件不满足 VFSM-->>UI: Blocked(reason) else 前置条件满足 VFSM->>CAP: StartCapture() CAP-->>VFSM: CaptureReady VFSM->>VFSM: 状态改为 transmitting loop 每个 Opus 帧 CAP-->>VFSM: opusFrame VFSM->>BRIDGE: sendVoice(clientID, codec, frame) BRIDGE->>SDK: gomobile SendVoice SDK->>TS: UDP voice frame end alt 用户松开或取消 UI->>VFSM: PttReleased / PointerCancelled VFSM->>CAP: StopCapture() VFSM->>VFSM: 状态恢复 silent else 连接断开 SDK-->>VFSM: onDisconnected(error) VFSM->>CAP: StopCapture() VFSM->>VFSM: 状态改为 blocked end end ``` --- ## 三、事件依赖 ### 语音前置依赖矩阵 | 操作或事件 | 必须依赖 | 建议依赖 | 依赖失败时的处理 | | --- | --- | --- | --- | | `TsClient.sendVoice` | 已连接、已有可发送的 Opus 帧 | 当前用户已在有效频道 | 任一前置失效立即停止发送 | ### 权威性划分 | 数据 | 推荐权威来源 | 原因 | | --- | --- | --- | | 语音发送结果 | `TsClient.sendVoice` 返回值 | 表示本次本地发送调用结果,不代表对方播放成功 | ### 统一实现原则(语音相关) - **语音只在连接和频道前置条件满足时发送。** 断开或被踢必须立即停止。