Skip to content

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 主要由 SpeakInterruptAndSpeakSpeakWithNoInterruptSilence 的完成或中断回调原样带回

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
}

注意:

  • 同步响应只代表“事件已被接收并进入处理流程”
  • 业务完成结果通常通过后续异步事件返回,例如 RecognitionCompleteSpeakComplete

3. 通用约定

3.1 data 的使用方式

  • SpeakInterruptAndSpeakSpeakWithNoInterruptdata 直接放要播报的文本
  • Silencedata 放静音时长,单位毫秒
  • DetectSpeechClientConnectdata 放 JSON 字符串
  • PauseDetectSpeechResumeDetectSpeechInterruptClientDisConnectdata 可为空

3.2 eventId 的建议用法

建议为每次独立的 TTS 请求都生成一个新的 eventId。这样可以在收到 SpeakCompleteSpeakInterrupted 时准确知道是哪一次播放结束或被打断。

3.3 事件方向

当前代码中的事件可以分为两类:

  • 客户端主动发送:ClientConnectClientDisConnectDetectSpeechPauseDetectSpeechResumeDetectSpeechSpeakInterruptAndSpeakSpeakWithNoInterruptSilenceInterrupt
  • 服务端主动回调:ClientConnectRecognitionCompleteNoInputTimeoutSpeakCompleteSpeakInterruptedAsrRealTimeResult

其中 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
}

字段说明:

字段类型是否必填说明
Typestring常用值为 callspycall 表示普通通话接入约定,spy 表示监听模式。
TtsEnginestring指定 TTS 引擎。不填时走服务端默认配置。当前内置可见值包括 aliyunkokoroxfyuntencent-cloudexample-tts
Voicestring指定发音人,实际可选值由具体 TTS 厂商决定。
PushAsrRealtimeResultboolean是否开启实时 ASR 中间结果推送。

实现注意:

  • 当前代码里,VoicePushAsrRealtimeResult 的处理放在 TtsEngine 同一个分支下。
  • 如果只传 Voice 或只传 PushAsrRealtimeResult,但没有传 TtsEngine,这两个字段当前实现不会生效。
  • 因此如果你希望稳定设置 VoicePushAsrRealtimeResult,建议同时传 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
}

字段说明:

字段类型是否必填说明
StartInputTimersboolean建议必填是否立即启动输入超时计时器。
NoInputTimeoutnumber单位毫秒。从开始识别起,多长时间内没有检测到任何语音则触发 NoInputTimeout
SpeechCompleteTimeoutnumber单位毫秒。检测到语音后,静音持续多久视为一句话结束。
AutomaticInterruptionboolean开启后,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:建议填写,便于和后续 SpeakCompleteSpeakInterrupted 对应

说明:

  • 同一会话下多次 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

用途:

  • 播放一段静音音频

参数:

字段类型是否必填说明
datanumber/string静音时长,单位毫秒,例如 500
eventIdstring建议填写,便于和完成事件对应。

说明:

  • 服务端会生成对应时长的静音 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 初始化完成
  • 收到这个事件后,再发 SpeakDetectSpeech 更稳妥

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 自身被中断,通常会带回原请求的 eventId
  • data:当前实现通常为 "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": "正在识别中的文本"
}

字段说明:

字段类型说明
asrEnginestringASR 引擎名,由具体实现填充,例如 aliyun-funasrxfyun
asrResultstring中间识别文本。

说明:

  • 这是实时中间结果,不代表最终识别完成
  • 最终结果仍以 RecognitionComplete 为准

示例:

json
{
  "id": "call-001",
  "event": "AsrRealTimeResult",
  "data": "{\"asrEngine\":\"aliyun-funasr\",\"asrResult\":\"帮我查一下\"}"
}

7. 推荐接入流程

7.1 普通通话模式

  1. 建立 TCP 连接。
  2. 发送 ClientConnectid 使用通话 UUID。
  3. 等待服务端异步 ClientConnect,通常 data{"msg":"SipInitSuccess"}
  4. 按需发送 SpeakDetectSpeech
  5. 通过 RecognitionCompleteAsrRealTimeResultSpeakCompleteSpeakInterrupted 处理后续业务。
  6. 结束时发送 ClientDisConnect 或直接断开 TCP。

7.2 spy 监听模式

  1. 建立 TCP 连接。
  2. 发送 ClientConnect,并在 data 中传 {"Type":"spy"}
  3. 等待服务端异步 ClientConnect,从 data 里取出 rtpPort
  4. 把外部 RTP 音频流发送到该端口。
  5. 发送 DetectSpeech 启动识别。
  6. 接收 RecognitionComplete 等识别回调。

8. 对接注意事项

  • event 名称区分大小写,必须与服务端枚举一致。
  • id 不能为空,否则服务端会直接返回错误响应。
  • 结构化参数要先序列化成 JSON 字符串再放进 data
  • 如果要跟踪某次 TTS 的完成状态,务必给请求带上 eventId
  • DetectSpeech 建议始终显式传 StartInputTimers
  • SpeakWithNoInterrupt 只保证屏蔽 ASR 自动打断,不保证屏蔽显式 Interrupt
  • PushAsrRealtimeResult 当前建议与 TtsEngine 一起传;spy 模式下当前实现会强制关闭该开关。