HowTo文档

如何在XSwitch中使用mod_unimrcp

MRCP VS UniMRCP

MRCP概述

MRCP(Media Resource Control Protocol)媒体资源控制协议,是语音服务器用来向客户端提供各种服务(比如我们熟悉的语音识别和语音合成)的通信协议。MRCP需要承载于其他协议之上,如RTSP (Real Time Streaming protocol)或SIP (Session Initiation protocol),MRCP协议有两个版本,版本v1依赖于RTSP协议来创建媒体流和数据传输,但版本V1兼容性较差,很难兼容不同厂家的扩展要求。版本v2使用了SIP来负责会话和媒体的创建,增加了扩展性,保证了兼容性,目前大多数使用的都是V2版本。

UniMRCP

UniMRCP是一个开源的、跨平台的MRCP协议实现,由C/C++语言编写,包含了MRCP客户端和服务端两部分,其封装了SIP、RTSP、SDP、MRCPv1、MRCPv2、RTP/RTCP堆栈,并为语音服务集成商提供了一个MRCP版本一致的API。MRCP和UniMRCP的关系有点像SIP和Sofia-SIP的关系。

MRCP交互流程

ASR(语音识别交互流程)

由上图可以看到MRCP首先会进行SIP协商,笔者测试的时候MRCP Client配的SIP端口是4090,MRCP Server的SIP端口是7010,具体的SIP INVITE内容展开如下:

INVITE sip:172.18.0.4:7010 SIP/2.0.
Via: SIP/2.0/UDP 172.18.0.3:4090;rport;branch=z9hG4bKtgUKjetBgg9vF.
Max-Forwards: 70.
From: <sip:172.18.0.3:4090>;tag=gp4313D5N660p.
To: <sip:172.18.0.4:7010>.
Call-ID: 48944478-331e-123d-17ad-0242ac120003.
CSeq: 78373748 INVITE.
User-Agent: FreeSWITCH.
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE.
Supported: timer, 100rel.
Content-Type: application/sdp.
Content-Disposition: session.
Content-Length: 336.
.
v=0.
o=FreeSWITCH 2187757640252386314 1175234853226465337 IN IP4 172.18.0.3.
s=-.
c=IN IP4 172.18.0.3.
t=0 0.
m=application 9 TCP/MRCPv2 1.
a=sendrecv.
a=setup:active.
a=connection:new.
a=resource:speechrecog.
a=cmid:1.
m=audio 4010 RTP/AVP 0 8 96.
a=rtpmap:0 PCMU/8000.
a=rtpmap:8 PCMA/8000.
a=rtpmap:96 L16/8000.
a=sendonly.
a=mid:1.

可以看到SIP INVITE SDP中带有resource:speechrecog,表示该请求为ASR请求,同时有一栏媒体属性a=sendonly表上只需要Client向Server单向发送媒体流。MRCP Server收到INVITE后会返回200 OK,SIP 200 OK的具体内容展开如下:

SIP/2.0 200 OK.
Via: SIP/2.0/UDP 172.18.0.3:4090;rport=4090;branch=z9hG4bKtgUKjetBgg9vF.
From: <sip:172.18.0.3:4090>;tag=gp4313D5N660p.
To: <sip:172.18.0.4:7010>;tag=HN3cHm99pHZDH.
Call-ID: 48944478-331e-123d-17ad-0242ac120003.
CSeq: 78373748 INVITE.
Contact: <sip:172.18.0.4:7010>.
User-Agent: UniMRCP SofiaSIP 1.5.0.
Accept: application/sdp.
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE.
Supported: timer, 100rel.
Session-Expires: 600;refresher=uac.
Min-SE: 120.
Content-Type: application/sdp.
Content-Disposition: session.
Content-Length: 299.
.
v=0.
o=AliMrcpServer 8710544278125709038 1637326555510876599 IN IP4 172.18.0.4.
s=-.
c=IN IP4 172.18.0.4.
t=0 0.
m=application 1544 TCP/MRCPv2 1.
a=setup:passive.
a=connection:new.
a=channel:231c9138dd2b4f83@speechrecog.
a=cmid:1.
m=audio 10012 RTP/AVP 0.
a=rtpmap:0 PCMU/8000.
a=recvonly.
a=mid:1.

可以看到MRCP Server回的200 OK中会携带MRCP协议的TCP端口以及MRCP CHANNEL等信息,还有相应接收媒体的RTP端口。可以看到本例中MRCP TCP的端口为1544。SIP协商完之后,就可以看到Client和Server之间会通过协商出来的TCP端口来发送MRCP消息。

比如DEFINE-GRAMMAR消息:

MRCP/2.0 516 DEFINE-GRAMMAR 1.
Channel-Identifier: 231c9138dd2b4f83@speechrecog.
Content-Type: application/srgs+xml.
Content-Id: default.
Content-Length: 355.
.
<?xml version="1.0" encoding="utf-8"?>
<grammar xmlns="http://www.w3.org/2001/06/grammar" xml:lang="en-US" version="1.0" mode="continuous" root="service">
<rule id="service">
<one-of>
<item><ruleref uri="#speech-to-text"/></item>
</one-of>
</rule>
<rule id="speech-to-text">
<one-of>
<item>telecom</item>
<item>banking</item>
</one-of>
</rule>
</grammar>

再比如START-OF-INPUT消息:

MRCP/2.0 94 START-OF-INPUT 2 IN-PROGRESS.
Channel-Identifier: 231c9138dd2b4f83@speechrecog.
.

当Client要停止识别的时候,需要向Server发送STOP消息:

MRCP/2.0 72 STOP 3.
Channel-Identifier: 231c9138dd2b4f83@speechrecog.
.

同时Client会向Server发送SIP BYE:

BYE sip:172.18.0.4:7010 SIP/2.0.
Via: SIP/2.0/UDP 172.18.0.3:4090;rport;branch=z9hG4bKv2D5N4Uja2N2p.
Max-Forwards: 70.
From: <sip:172.18.0.3:4090>;tag=gp4313D5N660p.
To: <sip:172.18.0.4:7010>;tag=HN3cHm99pHZDH.
Call-ID: 48944478-331e-123d-17ad-0242ac120003.
CSeq: 78373749 BYE.
User-Agent: FreeSWITCH.
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE.
Supported: timer, 100rel.
Content-Length: 0.
.

TTS(语音合成交互流程)

和ASR一样,TTS的交互中,首先也会进行SIP协商,笔者测试的TTS交互中具体的SIP INVITE内容如下:

INVITE sip:172.18.0.4:7010 SIP/2.0.
Via: SIP/2.0/UDP 172.18.0.3:4090;rport;branch=z9hG4bKgQ7mS1cSgmKre.
Max-Forwards: 70.
From: <sip:172.18.0.3:4090>;tag=S772HQ54UpF9e.
To: <sip:172.18.0.4:7010>.
Call-ID: 4a08eeb7-332e-123d-17ad-0242ac120003.
CSeq: 78377185 INVITE.
User-Agent: FreeSWITCH.
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE.
Supported: timer, 100rel.
Content-Type: application/sdp.
Content-Disposition: session.
Content-Length: 336.
.
v=0.
o=FreeSWITCH 5842879550813741697 1676426280523992390 IN IP4 172.18.0.3.
s=-.
c=IN IP4 172.18.0.3.
t=0 0.
m=application 9 TCP/MRCPv2 1.
a=sendrecv.
a=setup:active.
a=connection:new.
a=resource:speechsynth.
a=cmid:1.
m=audio 4026 RTP/AVP 0 8 96.
a=rtpmap:0 PCMU/8000.
a=rtpmap:8 PCMA/8000.
a=rtpmap:96 L16/8000.
a=recvonly.
a=mid:1.

可以看到SIP INVITE SDP中带有resource:speechsynth,表示该请求为语音识别请求,同时有一栏媒体属性a=recvonly,表示Client只负责接收媒体。MRCP Server收到INVITE后会返回200 OK,SIP 200 OK的具体内容如下:

SIP/2.0 200 OK.
Via: SIP/2.0/UDP 172.18.0.3:4090;rport=4090;branch=z9hG4bKgQ7mS1cSgmKre.
From: <sip:172.18.0.3:4090>;tag=S772HQ54UpF9e.
To: <sip:172.18.0.4:7010>;tag=1853F0r8190QH.
Call-ID: 4a08eeb7-332e-123d-17ad-0242ac120003.
CSeq: 78377185 INVITE.
Contact: <sip:172.18.0.4:7010>.
User-Agent: UniMRCP SofiaSIP 1.5.0.
Accept: application/sdp.
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE.
Supported: timer, 100rel.
Session-Expires: 600;refresher=uac.
Min-SE: 120.
Content-Type: application/sdp.
Content-Disposition: session.
Content-Length: 299.
.
v=0.
o=AliMrcpServer 8396542301222437832 1081701497237101738 IN IP4 172.18.0.4.
s=-.
c=IN IP4 172.18.0.4.
t=0 0.
m=application 1544 TCP/MRCPv2 1.
a=setup:passive.
a=connection:new.
a=channel:9e429f6dcfc54283@speechsynth.
a=cmid:1.
m=audio 10004 RTP/AVP 0.
a=rtpmap:0 PCMU/8000.
a=sendonly.
a=mid:1.

同样,Server返回的200 OK中携带本次MRCP协议的TCP端口和CHANNEL等信息。SIP协商结束后,就是MRCP消息的交互,相较于ASR,TTS的MRCP交互过程较简单,首先Client发送一个SPEAK消息给Server,该消息会携带本次合成语音需要的文本,Server返回IN-PROGRESS,表上开始向Client返回语音流,Client可以开始接收语音流,最后Server返回SPEAK-COMPLETE,表示本次TTS请求语音流传送完毕。最后Client会发送SIP BYE来结束整个TTS过程。

XSwitch中使用MRCP

XSwitch中使用mod_unimrcp模块来和MRCP Server进行交互,mod_unimrcp模块集成了UniMRCP库,相当于一个MRCP Client, 由于FreeSWITCH官方从git里移除了mod_unimrcp,为了同步官方,我们也从XSwitch代码树里移除了该模块和相关依赖的库。当然XSwitch在官方的基础上,对该模块做了一些优化。同时对该模块也提供了动态的页面配置,登陆XSwitch WEB管理界面,点击页面菜单的【高级】⇨【模块配置】⇨【UniMRCP】,就可以添加MRCP相应的Profile,可以通过添加多个MRCP Profile来对接不同的厂商。笔者测试的是Ali的MRCP Sever,配置界面如下:

上面的测试数据也是使用该配置产生的。

ASR测试

在XSwitch中注册SIP分机,账号1001,将下面的lua代码存储到/tmp/asr.lua中

function onAsrInput(s, type, obj)
    if type == "event" then
        s:consoleLog("INFO", obj:serialize())
    end
    if obj:getHeader("Speech-Type") == "detected-speech" then
        session:execute("detect_speech", "resume")
        session:execute("detect_speech", "start-input-timers")
    end
    return ""
end

session:setInputCallback("onAsrInput");
session:set_tts_params("unimrcp:ali", "xiaoyun")
session:answer()
session:execute("detect_speech", "unimrcp:ali default default")
session:execute("detect_speech", "param start-input-timers true")
session:execute("detect_speech", "param no-input-timeout 5000")
session:execute("detect_speech", "param speech-timeout 10000")
session:execute("detect_speech", "param auto-resume true")
session:execute("detect_speech", "start-input-timers")

session:streamFile("silence_stream://90000000")

接着在XSwitch服务器上执行下面命令:

fs_cli
originate user/1001 &lua(/tmp/asr.lua)

并开始讲话,可以看到控制台上有MRCP交互的消息和识别的文本内容。

TTS测试

同样地注册SIP分机1001,然后将下面的内容保存到XSwitch服务器的/tmp/tts.lua中,

session:set_tts_params("unimrcp:ali", "xiaoyun")
session:answer()
text = "欢迎使用小樱桃智能语音产品"
session:speak(text)
session:streamFile("silence_stream://2000")

接着在XSwitch服务器上执行下面命令:

fs_cli
originate user/1001 &lua(/tmp/tts.lua)

通话建立后,就可以听到欢迎使用小樱桃智能语音产品的语音播报。 上述asr.lua脚本的中的unimrp:ali表示使用的ASR引擎为unimrcp,profile为ali,和页面配置保持一致。同样tts.lua脚本的中的unimrp:ali表示使用的TTS引擎为unimrcp,profile为ali

Tips

  1. mod_unimrcp默认的ASR是一句话识别模式,所以如果在高并发的通话中去调用该模块去识别,是非常耗资源的。这种需要连续识别的场景可以通过设置通道变量cache_speech_handlestrue来实现ASR资源的复用。同样的如果是在一次通话中要频繁调用TTS,可以通过设置通道变量play_and_detect_speech_close_asrfalse来实现TTS资源复用。通话结束后,ASR/TTS所申请的资源会被释放掉。

  2. UniMRCP由于各种原因,版本一直比较旧,随着技术的不断发展,各语音厂商也提供了直接面向用户的WS/HTTP等协议的语音接口,用户可以不用只依赖于MRCP协议,而是直接面向语音服务。不过随之而来的问题就是没有一个统一的接口去面向不同的语音厂商,为此XSwitch集成了多家语音厂商的接口,编写了对应的模块,比如mod_ali、mod_xunfei、mod_baidu等等,在XSwitch中使用各家ASR/TTS只需要设置对应的语音引擎即可。

参考

  • MRCPV2:https://www.rfc-editor.org/rfc/rfc6787?ref=lowbibi.com
  • UniMRCP:https://www.unimrcp.org/
  • MRCP:http://en.wikipedia.org/wiki/Media_Resource_Control_Protocol
如何在 XSwitch 中实现互联互通