接口说明 / 语音+视频 WebRTC 对接
mode=1,mediaType=1)+ 两人视频(mode=2,mediaType=2)mode 3–6 与 room_* 现返回 code=501)https://api.menling.com(自有 IM 信令)| 项 | 值 |
|---|---|
| Base | https://api.menling.com |
| 路径前缀 | /message/call/*(短路径 /call/* 同源,建议用完整前缀) |
| Content-Type | application/json; charset=utf-8 |
| 鉴权 | 与发信相同:优先 Authorization: Bearer <JWT>;无效时回落 Cookie rong_auth_cookie |
| 用户 ID | body 里 to / destId 支持 门铃号数字 或 N3d 密文(与 send_private 一致) |
统一响应壳(与现网 IM 一致):
{ "code": 200, "msg": "ok", "result": "<JSON字符串或[]>" }
注意:result 多数是 JSON 字符串,客户端需再 JSON.parse(result)。
未登录:code 为 "10000"(字符串),msg: "Not loged in."。
| 值 | 含义 | 一期 |
|---|---|---|
| 1 | p2p_audio 两人语音 | ✅ 开放 |
| 2 | p2p_video 两人视频 | ✅ 开放 |
| 3 | group_audio 群语音 | 预留 → 501 |
| 4 | group_video 群视频 | 预留 → 501 |
| 5 | meeting 会议 | 预留 → 501 |
| 6 | live 直播 | 预留 → 501 |
| 值 | 含义 | 一期 |
|---|---|---|
| 1 | audio | ✅(配 mode=1) |
| 2 | video | ✅(配 mode=2) |
| 值 | 含义 |
|---|---|
| 0 | inviting 邀请中 |
| 1 | ringing 对方振铃 |
| 2 | accepted 已接通 |
| 3 | rejected 拒接 |
| 4 | hangup 挂断 |
| 5 | cancelled 主叫取消 |
| 6 | timeout 超时未接(默认 45s) |
| 7 | failed 失败 |
peer(一期)| host | participant | audience
POST /message/call/invite
语音:
{ "to": 88017, "mediaType": 1, "mode": 1 }
视频:
{ "to": 88017, "mediaType": 2, "mode": 2 }
可选:callId(客户端预生成)、roomId(P2P 可省略)、destId/toUserId(同 to)。
缺 mode 时按 mediaType 推断;缺 mediaType 时按 mode 推断;mode=1/2 会强制对齐 mediaType。
成功 code=200,result 解析后:
{
"callId": "c....",
"roomId": null,
"mode": 1,
"mediaType": 1,
"iceServers": [{ "urls": "stun:stun.l.google.com:19302" }],
"ttlMs": 45000,
"session": { "callId": "...", "from": 1, "to": 88017, "status": 0, "...": "..." }
}
主叫拿到 callId + iceServers 后:创建等 call.accept → 建 PeerConnection → 发 sdp(offer)**。
POST /message/call/ringing
{ "callId": "c...." }
仅被叫可调;推给主叫事件 call.ringing。
POST /message/call/accept
{ "callId": "c...." }
成功 result 含 session 字段等价信息 + iceServers。
被叫接听后:建 PC → 等 call.sdp(offer) → setRemoteDescription → 回 sdp(answer)。
POST /message/call/reject
{ "callId": "c....", "reason": "reject" }
POST /message/call/hangup
{ "callId": "c....", "reason": "hangup" }
call.cancel,status=5) call.hangup(status=4),duration 为秒 服务端会往私聊插一条可见 tip,如 [语音通话] 12秒 / [视频通话] 12秒(按 mediaType)。
POST /message/call/sdp
{
"callId": "c....",
"sdpType": "offer",
"sdp": "v=0\\r\\n..."
}
sdpType:offer | answer。
不入库(隐私);仅 WS 推给对端 call.sdp。
POST /message/call/ice
{
"callId": "c....",
"candidate": { "candidate": "candidate:...", "sdpMid": "0", "sdpMLineIndex": 0 },
"sdpMid": "0",
"sdpMLineIndex": 0
}
candidate 可为 WebRTC 的 RTCIceCandidateInit 对象或字符串;原样转给对端。
GET /message/call/status?callId=c....
GET /message/call/ice_servers
返回 { iceServers: [...], ttlMs: 3600000 }。
TURN/STUN 以 GET /message/call/ice_servers 返回为准,勿在客户端写死。
生产将换域名/短时凭证;客户端只消费 iceServers 数组。
均需登录;当前固定:
code=501,msg 含 reserved,result.reserved=true 并带回 acceptedFields。
| 方法 | 路径 | 预留字段 |
|---|---|---|
| POST | /message/call/room_create |
mode(3–6), mediaType, title, maxParticipants, audienceEnabled, members[] |
| POST | /message/call/room_join |
roomId, role, callId? |
| POST | /message/call/room_leave |
roomId, callId?, reason |
前端可先封装方法,内部遇 501 提示「暂未开放」。
在线通道:现有 IM WS(经 9527 HTTP push 注入)。
消息 content 为 JSON 字符串:
{
"dmkCall": 1,
"event": "call.invite",
"v": 1,
"payload": { }
}
识别:typeof content === 'object' ? content : JSON.parse(content) 后看 dmkCall === 1。
| event | 谁收到 | payload 要点 |
|---|---|---|
call.invite |
被叫 | 完整 session(含 from/to/mode/mediaType/callId) |
call.ringing |
主叫 | callId |
call.accept |
主叫 | session + iceServers |
call.reject |
对端 | callId, reason |
call.cancel |
被叫 | callId(主叫未接通取消) |
call.hangup |
对端 | callId, duration, reason |
call.timeout |
双方 | callId, reason=timeout |
call.sdp |
对端 | callId, sdpType, sdp |
call.ice |
对端 | callId, candidate, sdpMid, sdpMLineIndex |
离线:邀请/挂断会走现有厂商推送钩子,文案约 [语音通话邀请] / [视频通话邀请](在线优先)。
拉取历史:信令以 object_name=DMK:Call* 入库且 hide,会话列表不展示;挂断 tip 为普通文本。
| code | 含义 |
|---|---|
| 200 | 成功 |
| 400 | 参数错误(msg 说明) |
"10000" |
未登录 |
| 403 | 非通话参与者 / 无权操作 |
| 404 | call 不存在(进程重启后内存会话丢,以库为准或重新邀请) |
| 409 | 忙线 / 状态不允许(已接通再 accept 等) |
| 501 | 预留能力(群/会议/直播;非 P2P 语音/视频) |
| 500 | 服务异常 |
Idle
--invite--> Outgoing ----ringing----> OutgoingRinging
--收到invite--> Incoming --ringing--> IncomingRinging
Outgoing*/IncomingRinging --accept--> Connecting --ICE/SDP ok--> InCall
任意非 Idle --reject/hangup/cancel/timeout--> Ended --> Idle
超时:服务端 45s 未接自动 call.timeout;客户端可同时本地计时。
| 项 | 语音 mode=1 | 视频 mode=2 |
|---|---|---|
| getUserMedia | { audio: true } |
{ audio: true, video: true }(可设 facingMode / 分辨率) |
| PeerConnection tracks | 仅 audio | audio + video;offer/answer 须带 video m-line |
| 本地预览 | 无 | 接听前可选本地预览(前置摄像头);接听后再 addTrack / renegotiate 若预览流未入 PC |
| 远端渲染 | AudioTrack → 扬声器 | VideoTrack → RemoteView;AudioTrack → 扬声器 |
| 权限 | RECORD_AUDIO |
+ CAMERA;接通后建议前台服务 |
| UI 入口 | 资料页/聊天「语音通话」 | 「视频通话」;参数 mode=2,mediaType=2 |
| 切换 | 本期不做语音↔视频中途升级 | 同左;要升档需重新 invite |
权限:接通后建议前台服务(可复用旧 CallForegroundService 壳,换 WebRTC)。
POST invite {"to":<门铃号>,"mode":1,"mediaType":1} → 得 callId、iceServers call.invite → POST ringing → UI 振铃 POST accept call.accept → getUserMedia({audio:true}) → PeerConnection(iceServers) → addTrack → createOffer → POST sdp call.sdp(offer) → setRemote → createAnswer → POST sdp onicecandidate → POST ice;对端 addIceCandidate POST hangup av_call_session / av_log 有 invite→accept→hangupPOST invite {"to":<门铃号>,"mode":2,"mediaType":2} → 得 callId、iceServers(字段与语音相同,注意 mode/mediaType=2) call.invite(payload.mode=2)→ 视频来电 UI;可选本地预览 getUserMedia({audio:true,video:true}) POST accept getUserMedia({audio:true,video:true}) → 本地预览绑 LocalView → PC addTrack(audio+video) → createOffer(SDP 含 video)→ POST sdp createAnswer → POST sdp;远端 ontrack 绑 RemoteView POST hangup;tip 应为 [视频通话] … mode=3 或 room_create 须拿到 code=501# 需有效 JWT
TOK='Bearer eyJ...'
API=https://api.menling.com
# 语音
curl -sS -X POST "$API/message/call/invite" \
-H "Authorization: $TOK" -H "Content-Type: application/json" \
-d '{"to":88017,"mediaType":1,"mode":1}'
# 视频(期望 code=200 + mode=2 + iceServers)
curl -sS -X POST "$API/message/call/invite" \
-H "Authorization: $TOK" -H "Content-Type: application/json" \
-d '{"to":88017,"mediaType":2,"mode":2}'
curl -sS "$API/message/call/ice_servers" -H "Authorization: $TOK"
# 群/会议仍 501
curl -sS -X POST "$API/message/call/invite" \
-H "Authorization: $TOK" -H "Content-Type: application/json" \
-d '{"to":88017,"mediaType":2,"mode":4}'
curl -sS -X POST "$API/message/call/room_create" \
-H "Authorization: $TOK" -H "Content-Type: application/json" \
-d '{"mode":5,"title":"周会"}'
# 期望 code=501 reserved
im-webrtc-work/patches/ios-hide-callkit.md)。 org.webrtc:google-webrtc / iOS GoogleWebRTC)。 CallSignalingClient:封装上表 REST + 解析 WS dmkCall(按 payload.mode 分支语音/视频 UI)。 VoiceCallActivity:仅音频;VideoCallActivity:LocalView/RemoteView + 前后摄像头切换(可选)。 mode=1,mediaType=1;视频调 mode=2,mediaType=2;不再 RongVoIPIntent。 | 路径 | 说明 |
|---|---|
.196:[内部路径] |
信令实现 |
.196:[内部路径] |
挂载路由 |
av_call_session 表 |
可扩展会话 |
av_log |
兼容旧时长/风控 |
|
一期语音进度 |
|
两人视频进度 |
|
方案总览 |