通话控制

呼叫

RustPBX 支持 SIP 和 WebRTC 两种通话方式,在连接 RustPBX 时通过路径区分:

  • /call/sip: 使用 SIP 协议建立通话,RTP 传输音频。
  • /call/webrtc: 直接使用 SDP Offer 建立通话。

使用 Invite 命令发起呼叫。 当通话建立成功会收到 Answer 事件, 否则会收到 Reject 事件。

InviteFlow

呼叫和接听都使用 CallOption, 除设置被叫地址外,其他参数相同, 主要用于配置以下功能:

  • TTS: 文本转语音
  • ASR: 语音识别
  • VAD: 语音活动检测
  • 录音: 录音
  • denoise: 降噪

SIP 呼叫

SIP 呼叫需要在 CallOption 中设置主叫(caller)和被叫(callee) 的 SIP 地址,例:

{
  "command": "invite",
  "option": {
    "caller": "sip:6001@192.168.1.100",
    "callee": "sip:6002@192.168.1.100"
  }
}

caller 地址可以设置为 UserAgent 的 SIP 地址, 默认为 sip:{本机IP}:13050。

UserAgent 的 SIP 地址可以在 config.toml 中配置:

[ua]
addr = "0.0.0.0"
udp_port = 13050  # 避免使用 5060

UserAgent 相当于一个话机,因此他有自己的 SIP 地址。

WebRTC 呼叫

WebRTC 呼叫需要在 option.offer 字段中设置呼叫目标的 SDP Offer,例如:

{
  "command": "invite",
  "option": {
    "offer": "v=0\r\no=- 123456789 2 IN IP4 127.0.0.1\r\n..."
  }
}

接听/拒绝

接听比呼叫略微复杂,需要配置 Webhook 接收来电通知。有以下几个步骤:

  • 配置 Webhook
  • 当有来电时,RustPBX 会向 Webhook 地址发送请求
  • WebHook 服务根据请求中的 dialogId 连接 RustPBX
  • 发送 Accept 命令接听,或者 Reject 命令拒绝
AcceptFlow

配置 Webhook

在 config.toml 中配置 Webhook 地址:

[ua.handler]
type = "webhook"
url = "http://localhost:8090/webhook"
method = "POST"

接收通知

当有电话呼入时,RustPBX 会向 Webhook 地址发送 POST 请求, 例:

{
  "event": "invite",
  "dialogId": "9zUPiixVNO-xRTf4NqCV-dv9qE2iB",
  "caller": "sip:6001@192.168.139.103",
  "callee": "sip:192.168.3.197",
  "createdAt": "2025-10-23T06:33:26.623706+00:00"
}

dialogId 格式为: {call_id}-{tag1}-{tag2}, 其中 tag1 为 from_tag 和 to_tag 中的字典序较大者,tag2 为较小者。

连接 RustPBX

使用 dialogId 作为 id 参数的值,建立 WebSocket 连接

'ws://localhost:8080/call/sip?id=9zUPiixVNO-xRTf4NqCV-dv9qE2iB'

接听/拒绝

然后发送 Accept (接听) / Reject (拒绝) 命令 :

示例代码:

在 RustPBX Go SDK 中有 webhook 示例代码,可以参考:


func serveWebhook(parent context.Context, option CreateClientOption, addr, prefix string) {
	server := gin.Default()
	server.POST(prefix, func(c *gin.Context) {
		var form IncomingCall
		if err := c.ShouldBindJSON(&form); err != nil {
			c.JSON(400, gin.H{"error": err.Error()})
			return
		}

		client := createClient(parent, option, form.DialogID)

		go func() {
			ctx, cancel := context.WithCancel(parent)
			defer cancel()
			err := client.Connect("sip")
			if err != nil {
				option.Logger.Errorf("Failed to connect to server: %v", err)
			}
			defer client.Shutdown()

			client.Accept(option.CallOption)
			time.Sleep(300 * time.Millisecond)
			client.TTS(option.Greeting, "", "", true, false, nil, nil, false)
			<-ctx.Done()
		}()

		c.JSON(200, gin.H{"message": "OK"})
	})
	server.Run(addr)
}

参阅 RustPBX Go SDK 获取更多信息。

转接

在通话中使用 Refer 命令可以将通话转接到另一个号码,用于“转人工“场景。

转接流程:

  • 通话已经建立, 发送 Refer 命令
  • RustPBX 呼叫转接目标
  • 目标接听后,RustPBX 互相转发两端音频
ReferFlow

挂断

在通话建立后,使用 Hangup 命令挂断通话。示例:

{
  "command": "hangup",
  "reason": "Normal clearing"
}

事件

在通话的建立到结束整个过程中,会触发多种事件,如图示:

CallEvent

TrackStart

创建 RTP/WebRTC Track

Ringing

响铃事件, 对方已经收到呼叫,还未接听。

Answer

通话建立,对方已经接听。

Reject

对方拒绝,或其他 4xx 异常。

请求失败响应码列表: Request Failure 4xx

Hangup

通话建立后,对方挂断。

TrackEnd

RTP/WebRTC Track 结束。

TTS/Play 命令也会创建对应的 Track, 同样也会触发 TrackStart 和 TrackEnd 事件。可以用 TrackId 区分。

参见:TTS/Play。

其他事件

ASR 和 VAD 功能会触发相应的事件,包括:

  • AsrDelta: 中间识别结果,内容可能会变动。
  • AsrFinal: 最终识别结果,内容稳定。
  • Speaking: 语音活动检测,有语音输入。
  • Silence: 语音活动检测,一段时间没有语音输入。

详见:ASR 和 VAD。