Skip to content3.1
3.2
5.1
5.2
5.3
5.4
5.5
5.6
5.7
5.8
5.9
5.10
6.1
6.2
6.3
6.4
6.5
6.6
7.2
EasyMrcp 事件协议说明
EasyMrcp 客户端与服务端之间通过自定义 TCP 协议交互。当前对接主要围绕“事件”展开,客户端发起控制事件,服务端返回同步响应,并在后续通过异步事件推送 ASR/TTS 结果。
本文档基于当前代码实现整理,重点补齐以下内容:
- 统一消息结构
- 各事件的方向、参数、字段类型和触发时机
eventId的回传规则- 普通通话模式与
spy监听模式的差异
1. TCP 封包格式
每条消息都由固定头和 JSON 消息体组成:
text
+-------------------+------------------------+
| 消息头(8字节) | 消息体(变长) |
+-------------------+------------------------+
| 魔数(4字节) | 长度(4字节) | JSON数据 |
+-------------------+------------------------+- 魔数固定为
0x66AABB99 - 长度字段表示后续 JSON 消息体的字节长度
- JSON 编码为 UTF-8
2. 统一消息结构
2.1 客户端发送事件
json
{
"id": "f659db22-5c82-4f6c-aebb-3bba6f183411",
"eventId": "9a4d68f5-0b1e-44b0-a640-90d6a3c4a557",
"event": "Speak",
"data": "你好,欢迎使用 EasyMrcp"
}字段说明:
id:会话唯一标识。普通通话场景下通常是 PBX/FreeSWITCH 侧的通话 UUID,必须和 EasyMrcp 内部会话使用同一个 ID。eventId:可选。用于把某次请求和后续异步回调关联起来,主要用于Speak类事件。event:事件名,大小写敏感,必须与服务端枚举完全一致。data:字符串字段。简单参数可以直接放纯文本;复杂参数需要先序列化成 JSON 字符串再放入data。
2.2 服务端异步事件
json
{
"id": "f659db22-5c82-4f6c-aebb-3bba6f183411",
"eventId": "9a4d68f5-0b1e-44b0-a640-90d6a3c4a557",
"event": "SpeakComplete",
"data": "completed"
}说明:
- 只有部分事件会带
eventId - 当前实现里,
eventId主要由Speak、InterruptAndSpeak、SpeakWithNoInterrupt、Silence的完成或中断回调原样带回
2.3 服务端同步响应
客户端每发一条事件,服务端都会先回一个同步响应:
json
{
"id": "f659db22-5c82-4f6c-aebb-3bba6f183411",
"code": 200,
"message": "Success",
"data": "success"
}错误时格式如下:
json
{
"id": "f659db22-5c82-4f6c-aebb-3bba6f183411",
"code": 500,
"message": "错误描述",
"data": null
}注意:
- 同步响应只代表“事件已被接收并进入处理流程”
- 业务完成结果通常通过后续异步事件返回,例如
RecognitionComplete、SpeakComplete
3. 通用约定
3.1 data 的使用方式
Speak、InterruptAndSpeak、SpeakWithNoInterrupt:data直接放要播报的文本Silence:data放静音时长,单位毫秒DetectSpeech、ClientConnect:data放 JSON 字符串PauseDetectSpeech、ResumeDetectSpeech、Interrupt、ClientDisConnect:data可为空
3.2 eventId 的建议用法
建议为每次独立的 TTS 请求都生成一个新的 eventId。这样可以在收到 SpeakComplete 或 SpeakInterrupted 时准确知道是哪一次播放结束或被打断。
3.3 事件方向
当前代码中的事件可以分为两类:
- 客户端主动发送:
ClientConnect、ClientDisConnect、DetectSpeech、PauseDetectSpeech、ResumeDetectSpeech、Speak、InterruptAndSpeak、SpeakWithNoInterrupt、Silence、Interrupt - 服务端主动回调:
ClientConnect、RecognitionComplete、NoInputTimeout、SpeakComplete、SpeakInterrupted、AsrRealTimeResult
其中 ClientConnect 是双向事件:
- 客户端发
ClientConnect表示建立 EasyMrcp 逻辑会话并下发连接参数 - 服务端回
ClientConnect表示会话已具备后续处理条件
4. 事件总览
| 事件名 | 方向 | 用途 |
|---|---|---|
ClientConnect | 双向 | 建立逻辑会话,或通知会话已就绪 |
ClientDisConnect | 客户端 -> 服务端 | 主动断开 |
DetectSpeech | 客户端 -> 服务端 | 开始或更新 ASR 识别参数 |
PauseDetectSpeech | 客户端 -> 服务端 | 暂停 ASR 接收音频 |
ResumeDetectSpeech | 客户端 -> 服务端 | 恢复 ASR 接收音频 |
RecognitionComplete | 服务端 -> 客户端 | ASR 最终识别结果 |
NoInputTimeout | 服务端 -> 客户端 | 指定时间内没有检测到输入语音 |
Speak | 客户端 -> 服务端 | 普通 TTS 播放 |
InterruptAndSpeak | 客户端 -> 服务端 | 打断当前及排队 TTS,然后播报新文本 |
SpeakWithNoInterrupt | 客户端 -> 服务端 | 播放期间禁止 ASR 自动打断 |
Silence | 客户端 -> 服务端 | 播放一段静音 |
Interrupt | 客户端 -> 服务端 | 强制打断当前 TTS |
SpeakComplete | 服务端 -> 客户端 | TTS 播放完成 |
SpeakInterrupted | 服务端 -> 客户端 | TTS 被打断 |
AsrRealTimeResult | 服务端 -> 客户端 | 实时 ASR 中间结果推送 |
5. 客户端发送事件
5.1 ClientConnect
用途:
- 注册 TCP 客户端和 EasyMrcp 会话的绑定关系
- 可同时下发 TTS 引擎、发音人、实时 ASR 推送开关等参数
spy模式下用于申请监听端口
data 内部结构:
json
{
"Type": "call",
"TtsEngine": "xfyun",
"Voice": "x4_yezi",
"PushAsrRealtimeResult": true
}字段说明:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
Type | string | 否 | 常用值为 call 或 spy。call 表示普通通话接入约定,spy 表示监听模式。 |
TtsEngine | string | 否 | 指定 TTS 引擎。不填时走服务端默认配置。当前内置可见值包括 aliyun、kokoro、xfyun、tencent-cloud、example-tts。 |
Voice | string | 否 | 指定发音人,实际可选值由具体 TTS 厂商决定。 |
PushAsrRealtimeResult | boolean | 否 | 是否开启实时 ASR 中间结果推送。 |
实现注意:
- 当前代码里,
Voice和PushAsrRealtimeResult的处理放在TtsEngine同一个分支下。 - 如果只传
Voice或只传PushAsrRealtimeResult,但没有传TtsEngine,这两个字段当前实现不会生效。 - 因此如果你希望稳定设置
Voice或PushAsrRealtimeResult,建议同时传TtsEngine。 spy模式下当前实现会强制关闭PushAsrRealtimeResult。
请求示例:
json
{
"id": "call-001",
"event": "ClientConnect",
"data": "{\"Type\":\"call\",\"TtsEngine\":\"xfyun\",\"Voice\":\"x4_yezi\",\"PushAsrRealtimeResult\":true}"
}spy 模式说明
当 Type=spy 时,服务端会为该会话创建一个本地 RTP 接收端口,并通过后续异步 ClientConnect 事件返回:
json
{
"id": "call-001",
"event": "ClientConnect",
"data": "{\"rtpPort\":40000}"
}拿到 rtpPort 后,外部系统把 RTP 音频流发到该端口,再配合 DetectSpeech 启动识别。
5.2 ClientDisConnect
用途:
- 主动通知服务端当前 TCP 客户端准备断开
参数:
data:无
说明:
- 当前实现主要对
spy模式做额外清理,会关闭对应 ASR/RTP 资源 - 普通通话场景的会话释放更多依赖 SIP 侧结束流程
请求示例:
json
{
"id": "call-001",
"event": "ClientDisConnect",
"data": null
}5.3 DetectSpeech
用途:
- 启动 ASR 识别
- 设置无输入超时、句尾静音超时、自动打断等参数
data 内部结构:
json
{
"StartInputTimers": true,
"NoInputTimeout": 60000,
"SpeechCompleteTimeout": 800,
"AutomaticInterruption": true
}字段说明:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
StartInputTimers | boolean | 建议必填 | 是否立即启动输入超时计时器。 |
NoInputTimeout | number | 否 | 单位毫秒。从开始识别起,多长时间内没有检测到任何语音则触发 NoInputTimeout。 |
SpeechCompleteTimeout | number | 否 | 单位毫秒。检测到语音后,静音持续多久视为一句话结束。 |
AutomaticInterruption | boolean | 否 | 开启后,ASR 在检测到可打断内容时会自动中断当前 TTS。 |
实现注意:
- 当前实现里,如果
data是一个 JSON 对象字符串,建议显式传StartInputTimers。 - 因为处理代码会直接读取这个字段,缺失时容易造成空值问题。
SpeechCompleteTimeout主要对需要 VAD 断句的一句话识别模式生效。AutomaticInterruption是否能真正触发,还依赖具体 ASR 引擎是否会上报中间打断时机。- 服务端收到
DetectSpeech后,会重新绑定本次识别的回调逻辑,并更新实时结果推送开关。
请求示例:
json
{
"id": "call-001",
"event": "DetectSpeech",
"data": "{\"StartInputTimers\":true,\"NoInputTimeout\":60000,\"SpeechCompleteTimeout\":800,\"AutomaticInterruption\":true}"
}5.4 PauseDetectSpeech
用途:
- 暂停 ASR 接收音频
参数:
data:无
说明:
- 暂停时会取消当前输入超时计时器
- 暂停期间不会触发
NoInputTimeout
请求示例:
json
{
"id": "call-001",
"event": "PauseDetectSpeech",
"data": null
}5.5 ResumeDetectSpeech
用途:
- 恢复 ASR 接收音频
参数:
data:无
说明:
- 恢复时会重新启动输入超时计时器
- 该行为相当于重新开始本轮
NoInputTimeout计时
请求示例:
json
{
"id": "call-001",
"event": "ResumeDetectSpeech",
"data": null
}5.6 Speak
用途:
- 发起一段普通 TTS 播放
参数:
data:要播报的文本内容eventId:建议填写,便于和后续SpeakComplete、SpeakInterrupted对应
说明:
- 同一会话下多次
Speak会按队列顺序执行 - 当前一条正在播放时,后续
Speak会进入排队
请求示例:
json
{
"id": "call-001",
"eventId": "speak-001",
"event": "Speak",
"data": "您好,欢迎致电"
}5.7 InterruptAndSpeak
用途:
- 立即打断当前播放中的 TTS
- 清空队列中尚未执行的 TTS
- 直接播报新的文本
参数:
data:新的播报文本eventId:建议填写
说明:
- 这是“抢播”语义,不是简单入队
请求示例:
json
{
"id": "call-001",
"eventId": "speak-urgent-001",
"event": "InterruptAndSpeak",
"data": "检测到新的任务,请立即处理"
}5.8 SpeakWithNoInterrupt
用途:
- 播放一段不希望被 ASR 自动打断的 TTS
参数:
data:播报文本eventId:建议填写
实现注意:
- 当前实现中,它的核心效果是把会话的
interruptEnable置为false - 这样可以屏蔽 ASR 中间结果触发的自动打断
- 但它并不保证屏蔽显式发送的
Interrupt事件 - 播放结束后,服务端会自动把
interruptEnable恢复为true
请求示例:
json
{
"id": "call-001",
"eventId": "speak-protected-001",
"event": "SpeakWithNoInterrupt",
"data": "以下内容非常重要,请完整收听"
}5.9 Silence
用途:
- 播放一段静音音频
参数:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
data | number/string | 是 | 静音时长,单位毫秒,例如 500。 |
eventId | string | 否 | 建议填写,便于和完成事件对应。 |
说明:
- 服务端会生成对应时长的静音 PCM 并按普通 TTS 播放流程发送
- 静音播放结束后同样会收到
SpeakComplete
请求示例:
json
{
"id": "call-001",
"eventId": "silence-001",
"event": "Silence",
"data": "500"
}5.10 Interrupt
用途:
- 强制中断当前正在播放的 TTS
- 清空排队中的 TTS 任务
参数:
data:无
说明:
- 发送后服务端会立即回推
SpeakInterrupted - 这个
SpeakInterrupted当前实现通常不带eventId
请求示例:
json
{
"id": "call-001",
"event": "Interrupt",
"data": null
}6. 服务端异步回调事件
6.1 ClientConnect
用途:
- 通知客户端当前会话已经具备后续处理条件
常见返回内容:
普通通话模式
json
{
"id": "call-001",
"event": "ClientConnect",
"data": "{\"msg\":\"SipInitSuccess\"}"
}说明:
- 表示 SIP/MRCP/RTP 初始化完成
- 收到这个事件后,再发
Speak、DetectSpeech更稳妥
spy 模式
json
{
"id": "call-001",
"event": "ClientConnect",
"data": "{\"rtpPort\":40000}"
}说明:
- 表示监听模式所需的 RTP 端口已经分配完成
6.2 RecognitionComplete
用途:
- 返回本轮 ASR 的最终识别结果
参数:
data:最终识别文本,纯字符串
说明:
- 当前实现只在最终结果非空时推送
- 推送完成后,服务端会重新启动输入计时器,准备下一轮识别
示例:
json
{
"id": "call-001",
"event": "RecognitionComplete",
"data": "帮我查询今天的订单状态"
}6.3 NoInputTimeout
用途:
- 在指定时间内没有检测到任何语音输入
参数:
data:当前固定为"no-input-timeout"
说明:
- 当前实现触发一次超时后,会取消旧计时器并重新启动输入计时器
- 因此如果用户持续保持静默,可能会周期性重复收到该事件
示例:
json
{
"id": "call-001",
"event": "NoInputTimeout",
"data": "no-input-timeout"
}6.4 SpeakComplete
用途:
- 表示某次 TTS 或静音播放完成
参数:
eventId:如果原请求带了eventId,这里会原样返回data:当前固定为"completed"
说明:
- 收到该事件后,服务端内部会继续执行下一条排队中的
Speak
示例:
json
{
"id": "call-001",
"eventId": "speak-001",
"event": "SpeakComplete",
"data": "completed"
}6.5 SpeakInterrupted
用途:
- 表示某次 TTS 被打断
参数:
eventId:如果是某条排队/播放中的Speak自身被中断,通常会带回原请求的eventIddata:当前实现通常为"interrupt"
说明:
- 直接发送
Interrupt触发的SpeakInterrupted当前一般不带eventId
示例:
json
{
"id": "call-001",
"eventId": "speak-001",
"event": "SpeakInterrupted",
"data": "interrupt"
}6.6 AsrRealTimeResult
用途:
- 推送 ASR 中间识别结果
触发条件:
ClientConnect时开启PushAsrRealtimeResult- 当前使用的 ASR 引擎支持中间结果推送
- 中间结果非空
data 内部结构:
json
{
"asrEngine": "aliyun-funasr",
"asrResult": "正在识别中的文本"
}字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
asrEngine | string | ASR 引擎名,由具体实现填充,例如 aliyun-funasr、xfyun。 |
asrResult | string | 中间识别文本。 |
说明:
- 这是实时中间结果,不代表最终识别完成
- 最终结果仍以
RecognitionComplete为准
示例:
json
{
"id": "call-001",
"event": "AsrRealTimeResult",
"data": "{\"asrEngine\":\"aliyun-funasr\",\"asrResult\":\"帮我查一下\"}"
}7. 推荐接入流程
7.1 普通通话模式
- 建立 TCP 连接。
- 发送
ClientConnect,id使用通话 UUID。 - 等待服务端异步
ClientConnect,通常data为{"msg":"SipInitSuccess"}。 - 按需发送
Speak或DetectSpeech。 - 通过
RecognitionComplete、AsrRealTimeResult、SpeakComplete、SpeakInterrupted处理后续业务。 - 结束时发送
ClientDisConnect或直接断开 TCP。
7.2 spy 监听模式
- 建立 TCP 连接。
- 发送
ClientConnect,并在data中传{"Type":"spy"}。 - 等待服务端异步
ClientConnect,从data里取出rtpPort。 - 把外部 RTP 音频流发送到该端口。
- 发送
DetectSpeech启动识别。 - 接收
RecognitionComplete等识别回调。
8. 对接注意事项
event名称区分大小写,必须与服务端枚举一致。id不能为空,否则服务端会直接返回错误响应。- 结构化参数要先序列化成 JSON 字符串再放进
data。 - 如果要跟踪某次 TTS 的完成状态,务必给请求带上
eventId。 DetectSpeech建议始终显式传StartInputTimers。SpeakWithNoInterrupt只保证屏蔽 ASR 自动打断,不保证屏蔽显式Interrupt。PushAsrRealtimeResult当前建议与TtsEngine一起传;spy模式下当前实现会强制关闭该开关。
