Jingle 与 XMPP
What
Jingle(XEP-0166)在 XMPP IQ 上做会话协商,媒体仍走 RTP / ICE / DTLS:XML 信封里的 SIP+SDP。
Abstract
XMPP(RFC6120)先解决 IM 和 Presence:Message、Presence、IQ,地址是 JID。 Jitsi、Conversations、早期 Google Talk 走 Jingle;WebRTC 出现后内容对上了 ICE 和 DTLS-SRTP。 用 JSON 替换 XML(JMPP)只换信封,不换呼叫模型。
Why
对端已是 XMPP 客户端时,只开 SIP/Verto 接不上。
把 Presence 当成 Agent Available,路由会错(同 SIP Events 与对话状态)。
XML 冗长不等于“没有会话模型”;丢掉 Jingle 语义去重造 JSON 会失去 ICE/DTLS 对齐。
How
XMPP 只提供信封
三种 stanza,Jingle 只住在 IQ 里:
类型 |
作用 |
|---|---|
message |
聊天、群聊(MUC) |
presence |
在线、忙碌、离开 |
iq |
请求-响应; |
连接一般是 TCP + TLS + SASL。浏览器侧用 XMPP over WebSocket(RFC7395)或 BOSH。
IQ 信封 vs SIP INVITE
同一层信息,拆法不同。抓包时先找到 IQ,再读里面的 <jingle>:
flowchart LR
subgraph sipSide[SIP]
S1["INVITE sip:bob"]
S2["From / To / Call-ID / CSeq"]
S3["Content-Type: application/sdp"]
S4["v=0 / m=audio / a=rtpmap"]
S5["a=ice-ufrag / a=candidate"]
S6["a=fingerprint"]
end
subgraph jingleSide[Jingle]
J1["iq type=set to bob"]
J2["sid ≈ Call-ID"]
J3["jingle action=session-initiate"]
J4["description payload-type"]
J5["transport ICE-UDP"]
J6["fingerprint DTLS"]
end
S1 --- J1
S2 --- J2
S3 --- J3
S4 --- J4
S5 --- J5
S6 --- J6
action 常用:session-initiate / session-accept / session-terminate;transport-info 是 Trickle ICE;content-add / content-remove 加减媒体。
XEP |
内容 |
|---|---|
0166 |
Jingle 核心 |
0167 |
RTP 会话(相当于 SDP m= 行) |
0176 |
ICE-UDP |
0320 |
DTLS-SRTP |
0353 |
Jingle Message Initiation(用 message 振铃) |
和 SIP / JSEP 对照
SIP |
Jingle |
JSEP |
|
|---|---|---|---|
信封 |
SIP 文本 |
XMPP IQ |
无(应用自选) |
会话描述 |
SDP |
Jingle content/description |
SDP |
传输协商 |
可选 ICE |
ICE-UDP XEP |
强制 ICE |
典型实现 |
FS sofia、话机 |
Jitsi、Conversations |
所有浏览器 |
网关要做 Jingle 描述 ↔ SDP 互译。
FreeSWITCH 与呼叫中心
历史上有 mod_dingaling 对接 Google Talk / Jingle,现已不是主路径。
今天更常见:浏览器 ↔ FS 用 Verto 或 SIP-WS;Jitsi 内部用 Jingle / Colibri,与 FS 并列。
呼叫中心不必把 Jingle 当 ACD 信令。
JMPP:只换信封
XML stanza 冗长是 XMPP 在移动 IM 上被专有协议取代的原因之一。 JMPP 用 JSON 表达 同一套 Message / Presence / IQ,可减带宽。呼叫模型仍是 Jingle:谁发起、怎么协商媒体、ICE 怎么 Trickle。
XMPP: <iq type="set"> <jingle action="session-initiate" .../> </iq>
JMPP: { "iq": { "type": "set", "jingle": { "action": "session-initiate", ... } } }
变了:XML → JSON
没变:IQ 请求-响应、Jingle action、ICE/DTLS 字段含义
不会因此多出:REFER、History-Info、SIPREC
Web 侧若已有 WebSocket,多数项目会直接上自定义 JSON 或 Verto,而不是再实现一套 JMPP Jingle。 笔记:JMPP 让 XMPP 协议老树开新花。
实践注意
Jingle 没有 SIP 那么完整的 REFER / History-Info / SIPREC 生态。
MUC(XEP-0045)是聊天室,不是 SIP 会议焦点。
抓包看 5222(c2s)或 443 上的 XMPP-WS;Jingle 藏在 IQ 里,过滤
jingle或session-initiate。与 WebRTC 互通必须对齐 ICE 和 DTLS fingerprint。
Example
session-initiate 可与 SIP INVITE+SDP 对照验证(把 fingerprint/ufrag 换成 JSEP 里的真实值)。注意最外层是 IQ,不是 <message>:
<iq type="set" from="alice@example.com/phone" to="bob@example.com/phone" id="1">
<jingle xmlns="urn:xmpp:jingle:1" action="session-initiate" sid="abcd">
<content creator="initiator" name="audio">
<description xmlns="urn:xmpp:jingle:apps:rtp:1" media="audio">
<payload-type id="111" name="opus" clockrate="48000"/>
</description>
<transport xmlns="urn:xmpp:jingle:transports:ice-udp:1" ufrag="4ZcD" pwd="7P3n3jR5aK">
<fingerprint xmlns="urn:xmpp:jingle:apps:dtls:0" hash="sha-256">
7F:05:B2:C9:4F:0E:53:0A:B1:A4:7D:6B:0C:1E:88:2F:AA:3C:91:D4:6E:70:12:8B:5A:33:C0:9F:E1:4D:27:18
</fingerprint>
</transport>
</content>
</jingle>
</iq>
验证:用 Conversations 或 Gajim 打给支持 Jingle 的对端,Wireshark 过滤 xmpp 或 WebSocket 文本里的 session-initiate;对端 session-accept 后应有 ICE 连通。
若只看到 <presence> 或 <message> 而没有 <iq>...<jingle>,那是聊天/在线状态,不是呼叫信令。
JMPP 聊天室示例(非 Jingle 呼叫)见上述简书文的 FastAPI 代码,可单独跑通 presence/message —— 那只能证明信封换成了 JSON,不能证明转接/录音标准变了。
FS 侧 module_exists mod_dingaling 若为 false,不要假设主树仍支持 Jingle 终结。
Conclusion
Jingle = XMPP IQ 信封 + 与 SDP 同构的媒体描述。
呼叫中心主路径仍是 SIP/Verto;Jingle 做边缘网关。
换 JSON 信封(JMPP)不产生新的转接/录音标准。