跨端接续与资源抢占恢复
约 2879 字大约 10 分钟
2026-10-16
本页包含已接线核心流程及可选扩展;具体子能力必须按设备、平台与用户授权求交集。参见支持范围、场景索引和命令手册。图中S编号只用于本文定位,不放入实际JSON。
前置条件
原会话已established;同一用户可将会话接续到另一个已授权终端。发起呼叫或接续不直接打开目标麦克风。
优先级只比较已获业务和资源授权的请求。抢占记录focusId、原会话和用户意图版本;用户手动停止/换源/撤权后旧恢复令牌失效。设备休眠/失联时不得直接恢复采集;要先S19同步。跨端提交后终止旧腿而非新腿,未声明对应能力的端媒体因endpointEpoch变化被过滤。
D/D2是不同设备参与端,A是应用端,P是平台和媒体节点。各端分别使用鉴权Topic/登录态,不能冒用另一端身份。基础会话与媒体协商见S01/S06,复用时保留本场景sessionId并独立分配密钥和token。
完整Human资源接续的增量能力与邀请出处见命令01。候选本人接听、Peer连通及实际完整资源取得只是ready前置,提交之前业务门保持关闭,不能提前发送本腿sessionEstablished使设备误认为已完成移交。正式源释放、IM条件提交及保留端重绑定仍在实施;下图是设计时序,不代表现有完整Focus围栏已经撤除。旧省略controls路径仍按原实现,不因这份新设计改变设备行为。
完整时序
报文索引
编号按图阅读;编号保留历史引用,不要求数值连续。字段定义以命令链接为准。
| 编号 | 发送→接收 | Topic/接口 | 方法及契约 | 应答/等待 | 状态或作用 |
|---|---|---|---|---|---|
| S18-M01 | D→P | SIG↑ | endpointControl | endpointState;10秒受理,媒体协商15秒 | |
| S18-M02 | P→D | SIG↓ | endpointState | 应答/状态事件;不等待ACK | |
| S18-M20 | P→D2 | SIG↓ | sessionInvite | 应答或事件,无ACK | 核对补齐,不以受理替代完成 |
| S18-M21 | D2→P | SIG↑ | sessionAccept | 按契约等待10秒 | 核对补齐,不以受理替代完成 |
| S18-M22 | P→D2 | SIG↓ | requestAck | 应答或事件,无ACK | 核对补齐,不以受理替代完成 |
| S18-M23 | D2→P | SIG↑ | mediaOffer | 按契约等待10秒 | 核对补齐,不以受理替代完成 |
| S18-M24 | P→D2 | SIG↓ | mediaAnswer | 应答或事件,无ACK | 核对补齐,不以受理替代完成 |
| S18-M25 | D2→P | SIG↑ | mediaState | 按契约等待10秒 | 核对补齐,不以受理替代完成 |
| S18-M26 | P→D2 | SIG↓ | requestAck | 应答或事件,无ACK | 核对补齐,不以受理替代完成 |
| S18-M27 | P→D2 | SIG↓ | sessionEstablished | 应答或事件,无ACK | 核对补齐,不以受理替代完成 |
| S18-M03 | P→D | SIG↓ | endpointState | 应答/状态事件;不等待ACK | |
| S18-M04 | D→P | SIG↑ | endpointControl | endpointState;10秒受理,媒体协商15秒 | |
| S18-M05 | P→D | SIG↓ | endpointState | 应答/状态事件;不等待ACK | |
| S18-M06 | P→D | SIG↓ | sessionEnded | 应答/状态事件;不等待ACK | |
| S18-M07 | P→D | SIG↓ | endpointState | 应答/状态事件;不等待ACK | |
| S18-M08 | P→D | SIG↓ | focusControl | focusState;10秒受理,媒体协商15秒 | |
| S18-M09 | D→P | SIG↑ | focusState | 应答/状态事件;不等待ACK | |
| S18-M10 | P→D | SIG↓ | focusControl | focusState;10秒受理,媒体协商15秒 | |
| S18-M11 | D→P | SIG↑ | focusState | 应答/状态事件;不等待ACK | |
| S18-M12 | P→D | SIG↓ | focusControl | focusState;10秒受理,媒体协商15秒 | |
| S18-M13 | D→P | SIG↑ | focusState | 应答/状态事件;不等待ACK | |
| S18-M14 | P→D | SIG↓ | focusControl | focusState;10秒受理,媒体协商15秒 | |
| S18-M15 | D→P | SIG↑ | focusState | 应答/状态事件;不等待ACK | |
| S18-M16 | P→D | SIG↓ | focusControl | focusState;10秒受理,媒体协商15秒 | |
| S18-M17 | D→P | SIG↑ | focusState | 应答/状态事件;不等待ACK | |
| S18-M18 | D→P | SIG↑ | endpointControl | endpointState;10秒 | |
| S18-M19 | P→D | SIG↓ | endpointState | 实际状态应答;不等待ACK |
逐条JSON
S18-M01 · endpointControl
{
"msgToken": "s18-62",
"method": "endpointControl",
"sessionId": "s18-session",
"params": {
"operation": "prepare",
"expectedRevision": 0,
"handoffId": "handoff-1",
"target": {
"kind": "endpoint",
"id": "tablet-a"
}
}
}S18-M02 · endpointState
{
"msgToken": "s18-62",
"method": "endpointState",
"sessionId": "s18-session",
"data": {
"state": "preparing",
"revision": 1,
"handoffId": "handoff-1"
},
"code": 200
}S18-M20 · sessionInvite
{
"msgToken": "s18-s18-new-invite",
"method": "sessionInvite",
"sessionId": "s18-new",
"eventSeq": 1,
"params": {
"purpose": "humanCall",
"transport": "webrtc",
"admission": "accept",
"tracks": [
{
"trackId": "voice",
"kind": "audio",
"sourceId": "microphone",
"sourceType": "microphone",
"direction": "sendrecv",
"codec": "opus",
"state": "requested"
}
],
"expiresAt": 1900000000000,
"callType": "voice",
"callGroupId": "s18-group"
}
}S18-M21 · sessionAccept
{
"msgToken": "s18-s18-new-invite",
"method": "sessionAccept",
"sessionId": "s18-new",
"params": {
"acceptedTracks": [
"voice"
]
}
}S18-M22 · requestAck
{
"msgToken": "s18-s18-new-invite",
"method": "requestAck",
"sessionId": "s18-new",
"code": 200,
"data": {
"requestMethod": "sessionAccept",
"result": {
"state": "negotiating"
}
}
}S18-M23 · mediaOffer
{
"msgToken": "s18-s18-new-offer",
"method": "mediaOffer",
"sessionId": "s18-new",
"params": {
"mediaRevision": 1,
"webrtc": {
"iceMode": "complete",
"sdp": "<SDP offer>"
}
}
}S18-M24 · mediaAnswer
{
"msgToken": "s18-s18-new-offer",
"method": "mediaAnswer",
"sessionId": "s18-new",
"code": 200,
"data": {
"mediaRevision": 1,
"webrtc": {
"iceMode": "complete",
"sdp": "<SDP answer>"
}
}
}S18-M25 · mediaState
{
"msgToken": "s18-s18-new-connected",
"method": "mediaState",
"sessionId": "s18-new",
"params": {
"mediaRevision": 1,
"state": "connected"
}
}S18-M26 · requestAck
{
"msgToken": "s18-s18-new-connected",
"method": "requestAck",
"sessionId": "s18-new",
"code": 200,
"data": {
"requestMethod": "mediaState",
"result": {
"mediaRevision": 1,
"state": "connected"
}
}
}S18-M27 · sessionEstablished
{
"msgToken": "s18-s18-new-established",
"method": "sessionEstablished",
"sessionId": "s18-new",
"eventSeq": 2,
"data": {
"state": "established",
"mediaRevision": 1
}
}S18-M03 · endpointState
{
"msgToken": "s18-63",
"method": "endpointState",
"sessionId": "s18-session",
"data": {
"state": "ready",
"revision": 2,
"handoffId": "handoff-1",
"candidateSessionId": "s18-new"
},
"eventSeq": 1
}S18-M04 · endpointControl
{
"msgToken": "s18-64",
"method": "endpointControl",
"sessionId": "s18-session",
"params": {
"operation": "commit",
"expectedRevision": 2,
"handoffId": "handoff-1",
"candidateSessionId": "s18-new"
}
}S18-M05 · endpointState
{
"msgToken": "s18-64",
"method": "endpointState",
"sessionId": "s18-session",
"data": {
"state": "committed",
"revision": 3,
"handoffId": "handoff-1",
"candidateSessionId": "s18-new"
},
"code": 200
}S18-M06 · sessionEnded
{
"msgToken": "s18-65",
"method": "sessionEnded",
"sessionId": "s18-session",
"data": {
"state": "ended",
"reason": "handoffCommitted"
},
"eventSeq": 2
}S18-M07 · endpointState
{
"msgToken": "s18-66",
"method": "endpointState",
"sessionId": "s18-session",
"data": {
"state": "failed",
"revision": 2,
"handoffId": "handoff-1",
"reason": "handoffTimeout"
},
"eventSeq": 3
}S18-M08 · focusControl
{
"msgToken": "s18-67",
"method": "focusControl",
"sessionId": "s-ai",
"params": {
"operation": "suspend",
"expectedRevision": 0,
"focusId": "ai-focus",
"resources": [
"microphone",
"speaker"
],
"priority": "call",
"resumePolicy": "explicit"
},
"eventSeq": 1
}S18-M09 · focusState
{
"msgToken": "s18-67",
"method": "focusState",
"sessionId": "s-ai",
"data": {
"state": "suspended",
"revision": 1,
"focusId": "ai-focus",
"resources": [
"microphone",
"speaker"
]
},
"code": 200
}S18-M10 · focusControl
{
"msgToken": "s18-68",
"method": "focusControl",
"sessionId": "s-call",
"params": {
"operation": "acquire",
"expectedRevision": 1,
"focusId": "call-focus",
"resources": [
"microphone",
"speaker"
],
"priority": "call",
"resumePolicy": "never",
"expiresAt": 1900000000000
},
"eventSeq": 1
}S18-M11 · focusState
{
"msgToken": "s18-68",
"method": "focusState",
"sessionId": "s-call",
"data": {
"state": "acquired",
"revision": 2,
"focusId": "call-focus",
"resources": [
"microphone",
"speaker"
]
},
"code": 200
}S18-M12 · focusControl
{
"msgToken": "s18-69",
"method": "focusControl",
"sessionId": "s-call",
"params": {
"operation": "release",
"expectedRevision": 2,
"focusId": "call-focus",
"resources": [
"microphone",
"speaker"
],
"priority": "call",
"resumePolicy": "never"
},
"eventSeq": 2
}S18-M13 · focusState
{
"msgToken": "s18-69",
"method": "focusState",
"sessionId": "s-call",
"data": {
"state": "released",
"revision": 3,
"focusId": "call-focus",
"resources": [
"microphone",
"speaker"
]
},
"code": 200
}S18-M14 · focusControl
{
"msgToken": "s18-70",
"method": "focusControl",
"sessionId": "s-ai",
"params": {
"operation": "restore",
"expectedRevision": 3,
"focusId": "ai-focus",
"resources": [
"microphone",
"speaker"
],
"priority": "conversation",
"resumePolicy": "explicit",
"expiresAt": 1900000000000
},
"eventSeq": 2
}S18-M15 · focusState
{
"msgToken": "s18-70",
"method": "focusState",
"sessionId": "s-ai",
"data": {
"state": "restored",
"revision": 4,
"focusId": "ai-focus",
"resources": [
"microphone",
"speaker"
]
},
"code": 200
}S18-M16 · focusControl
{
"msgToken": "s18-71",
"method": "focusControl",
"sessionId": "s-ai",
"params": {
"operation": "restore",
"expectedRevision": 3,
"focusId": "ai-focus",
"resources": [
"microphone",
"speaker"
],
"priority": "conversation",
"resumePolicy": "explicit",
"expiresAt": 1900000000000
},
"eventSeq": 3
}S18-M17 · focusState
{
"msgToken": "s18-71",
"method": "focusState",
"sessionId": "s-ai",
"data": {
"state": "denied",
"revision": 4,
"focusId": "ai-focus",
"resources": [
"microphone",
"speaker"
],
"reason": "userIntentChanged"
},
"code": 409
}S18-M18 · endpointControl
{
"msgToken": "s18-extra-17",
"method": "endpointControl",
"sessionId": "s18-abort",
"params": {
"operation": "abort",
"expectedRevision": 1,
"handoffId": "handoff-abort"
}
}S18-M19 · endpointState
{
"msgToken": "s18-extra-17",
"method": "endpointState",
"sessionId": "s18-abort",
"code": 200,
"data": {
"state": "aborted",
"revision": 2,
"handoffId": "handoff-abort",
"reason": "userCancelled"
}
}媒体与本地动作
| 编号 | 路径 | 格式与约束 |
|---|---|---|
| S18-R01 | D2→P | WebRTC ICE/DTLS;独立Peer;15秒超时撤销候选腿 |
异常、状态与资源释放
路由与信封见公共约定;阶段性状态不代表最终完成。操作终态时限见命令手册;编号、超时等元信息不发送。
能力不足422、无权限403、资源/修订号冲突409、超时504按S11处理,不静默开启设备能力。同token只重放原结果;事件按eventSeq去重,旧会话/端代次不可恢复。验证本页正常路径、alt分支及支持范围断言。文档通过不代表代码或硬件已实现。
验证入口
更新日志
2026/10/16 16:04
查看所有更新日志
fe3a7-docs(AI与音视频): 按场景整理完整接入流程与命令契约于
