主题
XHIM 商用媒体消息与实时音视频架构
范围状态(2026-07-26):纯 IM 首发只承诺图片、文件等附件消息。实时语音/ 视频通话推迟到后续厂商 Provider 接入,不属于当前 Stable 发布门禁;本文的 RTC 章节作为未来独立可选包的设计与验收基线保留。
状态:Active Target Architecture
最近审查:2026-07-26
适用范围:iOS、macOS、Android、Windows、HarmonyOS/OpenHarmony
本文定义晞晗IM(XHIM)面向商业交付的媒体消息和实时音视频边界。它同时记录 当前已经落地的能力与尚未完成的发布门禁,不能把接口或设计文档解释为功能已经 生产可用。
1. 两条必须分离的能力
“音视频”包含两种生命周期完全不同的能力:
| 能力 | 例子 | 数据真相 | 延迟目标 | 核心路径 |
|---|---|---|---|---|
| 媒体消息 | 图片、语音条、短视频、文件 | SQLite + 服务端消息事件流 | 最终可靠送达 | 上传/下载 + MessageEnvelope + Outbox/Sync |
| 实时通话 | 1v1/群组语音、视频通话 | Call Actor + 服务端信令状态 | 实时、允许短暂丢包 | Call Signaling + RTC Provider + 平台系统能力 |
二者不得共用一套状态机:
- 媒体消息是持久业务数据,必须能够离线入队、重启恢复、ACK/Sync 幂等归并;
- RTC 信令包含响铃、接听、协商、成员变化和挂断,要求严格过期、顺序和通话 Epoch,不能排在普通消息 Outbox 后等待;
- 通话结束后可以生成一条持久的
xhim.call.summary消息,但它只是通话记录, 不是信令真相; - 系统 Push 只负责唤醒或展示来电,接收端仍必须向信令服务确认当前权威状态。
mermaid
flowchart LR
App["业务 App / UI Kit"]
Facade["平台 Facade"]
Message["可靠消息平面"]
Media["媒体传输平面"]
Call["实时通话控制平面"]
RTC["RTC Provider 插件"]
IMBackend["IM Backend"]
MediaBackend["Upload / Object Storage / CDN"]
CallBackend["Call Signaling / TURN / SFU"]
App --> Facade
Facade --> Message
Facade --> Media
Facade --> Call
Message --> IMBackend
Media --> MediaBackend
Call --> CallBackend
Call --> RTC
RTC --> CallBackend2. 当前实现边界
截至 2026-07-26,仓库已经实现:
- 通用、不可变且版本化的消息信封:
content_type + content_version + payload + fallback_text; - 旧文本消息在存储层规范化为
text/plain@1,旧 Wire 字段号和语义保留; - 未知类型和高版本二进制 Payload 可通过 Storage、Outbox、Sync、Engine 和
xhim_v1C ABI 原样往返,包括内嵌 NUL 字节; send_text保留为兼容便捷 API,send_message是通用发送入口;- C ABI 可稳定区分 text/image/audio/video/file/custom 内容大类,同时导出原始
content_type,平台 Wrapper 可以按注册表选择 Renderer。 xhim_media_v1.proto已固定MediaRef、可选密钥封装和图片/语音/视频/文件 v1 字段号;对应 C++ 模型会严格校验 32-byte Hash、MIME、大小、尺寸、时长、 波形、叶子文件名和密钥封装边界;- SQLite Schema v6 引入账号隔离的
media_tasks,支持上传/下载方向、幂等 创建、持久进度、合法状态转换、单调revisionfence、可重试失败、执行租约 以及完成后的稳定MediaRef;当前 Schema 为 v12,冻结 fixture 覆盖 v1-v12 到当前版本的迁移与重启恢复; - 供应商无关的阻塞式
MediaTransportSPI、MediaProcessor和专用 joinable I/O 线程MediaWorker已落地:支持进度持久化、有限指数退避抖动、Retry-After、凭证暂停、账号 Epoch 撤销排空、异常归类和过期租约恢复; - Go 服务端已实现
xhim_media_service_v1.proto的 Prepare/Complete/Download 控制面、PostgreSQL 媒体元数据、本地 HMAC 对象 Adapter 与生产 S3/S3-compatible 预签名 Adapter。Complete 会重新读取对象并校验 SHA-256/长度/MIME,下载授权会按绑定会话实时复核成员身份;真实 TLS E2E 覆盖 Alice 上传与 Bob 下载。本地 Adapter 对 Root identity、对象和 partial 普通文件类型做前后校验,使用固定分片锁与 hard-link no-replace 发布;受限于 跨平台 Go 标准库没有统一原子 no-follow 打开接口,它仍只属于开发/可信私有 驱动,不能替代生产 S3 的权限隔离。 XHIM::RTC已实现单写者CallActor、有界业务/保留控制队列、call epoch 和 signal sequence fence、邀请/接听/拒绝/挂断、RTC 连接/重连及麦克风/ 摄像头控制;- Go 服务端已实现
xhim_call_v1.proto、PostgreSQL 通话/参与者/幂等操作/ 账号信令表、Push 唤醒、审计和用户级短期签名 RTC Token;Token 不进入公共 信令持久化; - iOS/macOS/Android/Windows/HarmonyOS UI 源码已提供基础通话控制组件,业务状态仍由 Call Actor 和服务端信令决定。
当前仍未实现或尚未完成生产验收:
- Reference C++ Adapter 已实现媒体控制面 Codec、流式 PUT/PATCH 断点上传和 Complete,也已实现授权后严格 GET/Range 下载、
.partial持久恢复、 SHA-256/大小校验、fsync+ no-replace 原子发布、TTL/LRU 配额清理及稳定 C ABI 下载受理和不暴露路径的 scoped reader;真实 S3 multipart Session/Part 持久化与 CDN 策略尚未完成,Reference Windows 安全文件端口仍 fail closed; 服务端当前只支持最大 256 MiB 单对象; - 缩略图生成、转码、内容嗅探和波形提取;
- C++
SignalingPort到参考 HTTP API 的正式产品 Adapter,以及任一厂商 RTC/WebRTC 媒体 Adapter、TURN/SFU 部署; - CallKit、Android Telecom/ConnectionService、Windows、HarmonyOS 的系统通话 桥接;
- 真实网络、弱网、后台、锁屏、蓝牙路由和真机互通验证。
所以当前代码已完成媒体任务与通话控制面的可靠基础,但还不能把“控制面完成” 表述为“音视频媒体面已达到生产可用”。
3. 通用消息信封
3.1 稳定合同
text
MessageEnvelope
content_type ASCII 稳定命名空间,最长 256 bytes
content_version 该 content_type 内从 1 开始的版本
payload 不可变二进制内容,当前硬上限 1 MiB
fallback_text 有效 UTF-8,必须可用于摘要、通知和兜底 UI内置类型:
content_type | v1 Payload |
|---|---|
text/plain | UTF-8 文本 |
xhim.media.image | Image descriptor + 一个或多个稳定 MediaRef |
xhim.media.audio | Audio descriptor + 稳定 MediaRef |
xhim.media.video | Video descriptor + 视频/封面 MediaRef |
xhim.media.file | File descriptor + 稳定 MediaRef |
xhim.call.summary | 已结束通话的持久摘要,不含实时信令 |
第三方类型必须使用组织拥有的反向域名命名空间,例如 com.example.order。content_version 只在同一类型内递增,不能把版本拼进 字符串后又复用为另一套语义。
3.2 前向兼容
- Core 把 Payload 当作不透明字节,不根据未知内容决定 Cursor 是否推进;
- 未识别类型或更高版本必须原样落库、同步和导出;
- 平台插件负责
validate/render/conversationSummary/notificationSummary/actions; - 插件未安装、解析失败或抛出异常时必须使用
fallback_text和统一未知消息组件; - Renderer 失败不能回滚已经提交的 Sync、改变消息 Payload 或使会话不可打开;
- 同一
client_message_id的类型、版本、Payload 和 Fallback 都属于不可变幂等 内容,任一不同都必须返回冲突。
4. 媒体消息
4.1 MediaRef 只保存稳定身份
消息 Payload 不得保存本地路径、文件描述符、系统相册 URI、Bearer Token 或 临时签名 URL。建议的 v1 语义如下:
text
MediaRef
media_id 服务端稳定不透明 ID
content_hash 原始密文字节或明文字节的协商摘要
byte_size
mime_type
storage_revision
encryption_descriptor 可选;只引用算法套件和密钥封装,不含日志可见明文密钥
ImageContent
original: MediaRef
thumbnail: MediaRef?
pixel_width
pixel_height
AudioContent
media: MediaRef
duration_ms
codec_hint
waveform[] 归一化有界采样,不是原始音频
VideoContent
media: MediaRef
cover: MediaRef?
duration_ms
pixel_width
pixel_height
FileContent
media: MediaRef
display_namemime_type、扩展名、display_name 和远端 Metadata 都是不可信输入。下载后必须 使用实际内容检测、大小上限和 Hash 校验;展示名必须清理路径分隔符和控制字符。
4.2 上传状态机
mermaid
stateDiagram-v2
[*] --> Created
Pending --> Authorizing
Authorizing --> Transferring
Transferring --> Paused: 网络或宿主后台限制
Paused --> Authorizing: 授权过期
Paused --> Transferring: 会话仍有效
Transferring --> Verifying
Verifying --> Completed: 服务端提交 MediaRef
Pending --> Cancelled
Authorizing --> Failed
Transferring --> Failed
Verifying --> Failed
Failed --> Authorizing: 用户或策略重试商业实现必须满足:
- Core 先把媒体任务、选取来源和目标消息草稿可靠持久化;
- 上传授权只包含最小权限、对象范围和短有效期;
- 大文件使用分片上传;已确认 Part、上传 Session 和 Hash 状态可重启恢复;
- 授权过期刷新授权,不重新创建业务消息或改变
client_message_id; - Complete 由服务端校验大小、Hash、类型和租户归属后返回稳定
MediaRef; - 只有所有必需
MediaRef完成后,才冻结消息 Payload 并进入消息 Outbox; - 取消媒体任务必须与取消消息草稿使用显式状态转换,不能靠删除临时文件猜测;
- 相机、相册和文件 Provider 交付的平台 URI 必须尽快复制到 SDK 管理的受控 沙箱,不能假设授权可跨进程重启继续使用。
ClientEngine 已统一拥有 MediaWorker,并在上传完成后用稳定 MediaRef 冻结媒体 Payload、写入 Message Outbox;登录会恢复崩溃窗口内尚未入队的完成 任务。稳定 C ABI 的 create/get/cancel 返回持久任务快照, MEDIA_TASK_UPDATED@1 只携带 task_id 和状态,不携带本地路径或授权。 MediaTransport::transfer() 仍是产品侧必须实现或替换的阻塞式 Port。 Adapter 必须:
- 串行调用进度观察者,执行严格短于任务租约的硬超时,并让
cancel_account_epoch()线程安全且尽快返回; - 使用请求中的
resume_offset恢复传输,并只在服务端完成大小、Hash 和租户 校验后返回稳定MediaRef; - 不把临时签名 URL、Bearer Token、明文密钥或平台 URI 写入任务记录和日志;
- 将凭证过期、可重试网络故障、永久内容错误和主动取消映射到明确的
MediaTransferDisposition,不得用异常承载普通业务失败。
创建 API 的 OK 仅表示任务和依赖消息意图已经持久化;只有任务进入 Completed 且 Message Outbox 写入成功,才表示媒体发送链进入消息可靠投递。 iOS、macOS、Android、Windows 和 HarmonyOS 已完成 scoped reader 的原生封装。 现存商用门禁集中在 Windows 安全缓存端口、 真实对象存储 multipart/CDN、审核/派生媒体与五端真机弱网证据,不再是 Engine/Outbox 或平台 API 编排缺失。
4.3 下载与缓存
- 已使用
media_id + storage_revision向 Media API 获取短期下载授权,授权 回包的完整MediaRef必须与任务不可变输入完全一致; - 当前每个 Client 的
MediaWorker串行执行媒体任务;非零偏移强制Range+ 精确206 Content-Range,零偏移只接受200,不跟随重定向; - 临时文件写入
.partial,发布持久进度前先落盘;大小/Hash 校验后以 no-replace 原子方式切换为可读缓存,取消和崩溃按 SQLite revision/offset 恢复; - 缓存身份使用调用方从稳定
MediaRef派生的扁平键,不能使用会过期的 URL 或服务端文件名; - 缓存根由账号数据库沙箱派生;执行器只在已打开根句柄内按 TTL 优先/LRU 清理并执行 512 MiB 默认上限;
- App 使用稳定 scoped reader API 在 I/O 线程分块消费字节;reader 打开时复核 flat key、普通单链接文件、大小和 SHA-256,持有原生文件句柄消除路径替换 TOCTOU,且不暴露绝对缓存路径;
- 缩略图优先,原图/视频按用户动作和网络策略加载;
- 解码器在独立受限执行环境使用像素数、帧数、时长和解压尺寸上限;
- 业务日志只记录不透明任务 ID、分类和耗时,不记录 URL Query、Token、本地路径 或消息正文。
5. 实时音视频通话
5.1 不自研媒体引擎
XHIM Core 负责跨端一致的通话业务语义,但不自行实现 RTP、拥塞控制、回声消除、 编解码器或 SFU。媒体能力通过版本化 RtcProvider SPI 接入成熟 WebRTC 实现或 商业 RTC 服务。这样既能切换供应商,也不会把厂商对象穿过稳定 C ABI。
WebRTC 互操作以 W3C WebRTC Recommendation 和 IETF WebRTC RFC 集合为基线。 生产实现必须使用 ICE/STUN/TURN 完成连通性协商,并使用 DTLS-SRTP/SRTP 保护 媒体;不得提供回退到明文 RTP 的“兼容开关”。
5.2 组件边界
text
CallService
CallActor 单一通话状态写入者
CallSignalingClient 建立/恢复信令会话、命令幂等、事件排序
RtcProvider 供应商无关媒体 SPI
CallSystemBridge CallKit/Telecom/Windows/Harmony 平台桥
CallAudioSessionBridge 权限、音频焦点、路由、中断
CallPushBridge VoIP/高优先级 Push 唤醒
CallLogProjector 结束后投影 xhim.call.summary
CallDiagnostics 脱敏状态、质量和失败分类CallActor 是一次 Client 内唯一通话状态写入者。Provider 回调、Push、信令事件、 UI 动作、系统来电动作和应用生命周期变化都必须携带 fence 回到 Actor,不能各自 修改一份布尔状态。
5.3 通话身份与防重放
每条信令命令或事件至少包含:
text
call_id
call_epoch
participant_id
device_id
signal_id
signal_seq
expected_revision
created_at_server_ms
expires_at_server_ms
idempotency_key
trace_id
payload_type
payload_version
payload规则:
call_id是服务端生成或确认的稳定身份,不能用房间号代替;- 同一
call_id每次重新发起/恢复关键协商都推进call_epoch; - 服务端为通话状态机分配单调
signal_seq,客户端按序应用并通过 Range/Resume 补缺口;WebSocket 到达时间不是业务顺序; signal_id/idempotency_key建唯一约束,重复 invite/accept/hangup 是 no-op;- 所有瞬时信令都有服务端时间和严格过期时间,过期 invite 不得再次响铃;
- 旧 Epoch、旧 Provider Session、旧 Push 和迟到 SDP/ICE 不得改变新通话;
- 权限校验、参与者集合和通话状态由服务端裁决,客户端显示不能代替授权;
- 信令和媒体 Token 都限定 App、用户、设备、Call、权限和短有效期,不复用 IM 长期凭证直接加入房间。
5.4 1v1 通话状态机
mermaid
stateDiagram-v2
[*] --> Idle
Idle --> Creating: startCall
Creating --> OutgoingRinging: 服务端确认邀请
Idle --> IncomingRinging: 有效邀请 + 服务端复核
IncomingRinging --> Joining: accept
OutgoingRinging --> Joining: 对端接受
Joining --> ConnectingMedia: Token + Provider Session
ConnectingMedia --> Connected
Connected --> Reconnecting: 媒体或信令中断
Reconnecting --> Connected: 恢复成功
Creating --> Ending: 取消或失败
OutgoingRinging --> Ending: 拒绝/超时/取消
IncomingRinging --> Ending: 拒绝/超时/取消
Joining --> Ending: 失败/挂断
ConnectingMedia --> Ending: 失败/挂断
Connected --> Ending: 挂断/踢出
Reconnecting --> Ending: 超时/挂断
Ending --> Ended: 服务端终态或有界本地收束
Ended --> [*]首个商业版本建议默认每个账号只允许一个活动通话。呼叫碰撞、跨设备同时接听、 系统蜂窝来电和第二路 RTC 来电都由服务端策略与 Call Actor 共同裁决,不能仅靠 UI 禁用按钮。
5.5 RtcProvider SPI
Provider SPI 面向产品 Adapter,不属于公开稳定 C++ ABI。平台 Facade 对业务只 暴露 XHIM DTO:
text
RtcProviderFactory
capabilities()
createSession(config, eventSink)
RtcSession
join(joinToken)
publishAudio(enabled)
publishVideo(enabled, captureSource)
switchCamera()
setSpeakerRoute(route)
subscribeVideo(participant, quality)
setNetworkPreference(policy)
leave(reason)
close()
RtcEventSink
onConnectionState
onParticipantState
onTrackState
onAudioRoute
onQualitySample
onFatalError必须满足:
- 所有回调带
call_id/call_epoch/provider_session_generation; leave/close线程安全、幂等、可在任意中间状态执行;- Provider 不直接发送 IM 消息、不修改 SQLite、不自行重登录;
- Provider 错误先映射到稳定 domain/code/retryable,不把厂商错误文本暴露给 UI;
- 远端视频渲染面由平台 Wrapper 管理,C ABI 只传不透明 Track/View Handle 的受控 生命周期,不跨 ABI 复制原始视频帧;
- 能力协商显式表达 1v1/群组、屏幕共享、Simulcast/SVC、美颜、录制、转写和 E2EE insertable streams;缺失能力返回
UNSUPPORTED,不得静默降级。
5.6 平台桥接
| 平台 | 必须接入的系统能力 |
|---|---|
| iOS | CallKit、PushKit 合规路径、AVAudioSession、相机/麦克风权限、后台模式 |
| macOS | 系统音频会话、相机/麦克风权限、设备切换、窗口/屏幕采集授权 |
| Android | Telecom/ConnectionService 或受支持自管通话、前台服务、AudioFocus、运行时权限 |
| Windows | 音频端点/默认通信设备变化、相机/麦克风隐私权限、系统通知和电源状态 |
| HarmonyOS | ArkTS/Node-API Facade、音视频权限、音频打断与路由、后台/通知能力 |
平台桥只上报系统事实和执行 Actor 已决定的动作。它不能根据收到 Push 就直接 创建第二个 Provider Session,也不能在 UI 消失时自行挂断仍由系统托管的通话。
5.7 Push、锁屏与恢复
- Push Payload 只包含最小 call identity、显示所需的非敏感摘要和过期时间;
- 收到 Push 后先建立系统来电 UI,同时并发向信令服务复核通话仍可接听;
- 服务端已取消、已被其他设备接听或邀请过期时,立即结束系统来电 UI;
- 用户在系统 UI 接听后,动作进入 Call Actor,再取得短期 Join Token 并创建 Provider Session;
- App 被系统终止后,服务端通话状态和系统框架负责恢复;普通 IM 消息缓存不是 通话真相;
- 任何 Push Token、Join Token、SDP、ICE Candidate 和签名 URL 均不得进入普通 日志或 Crash 附件。
6. 安全、隐私和滥用防护
- 信令只允许 TLS/WSS,媒体安全遵循选定 WebRTC/RTC Provider 的加密基线;
- TURN 凭证和房间 Join Token 使用最小权限、短有效期和单 Call 范围;
- 服务端限制呼叫频率、并发邀请、群人数和异常设备,客户端限流不能替代服务端;
- 默认不录音录像;录制必须有明确产品能力、参与者可见提示、权限和留存策略;
- 质量指标只采集连接类型、抖动、丢包、RTT、码率、帧率和错误分类等最小数据, 不采集媒体内容;
- 用户 ID、IP、设备信息和质量数据属于隐私数据,必须进入数据地图、保留策略和 用户删除流程;
- 自定义媒体加密或 E2EE 必须使用经过评审的协议和密钥管理,不自行发明算法。
7. 商用质量目标
具体数值必须在首个正式 Provider 和目标地区完成基线测试后冻结,不能在没有 真实网络证据时承诺。至少建立以下 SLI/SLO:
- 邀请提交成功率、来电通知到达率、接听成功率;
- 首帧音频/视频时间、重连成功率和重连时长;
- 通话意外终止率、无声/黑屏率、音频路由错误率;
- RTT、抖动、丢包、卡顿率、码率和视频首帧;
- 媒体上传成功率、恢复成功率、校验失败率和 CDN 命中率;
- 按平台/系统版本/网络类型/地区/Provider 版本分桶;
- 所有指标具备 Trace ID,但不包含 Token、URL Query、SDP 或消息正文。
8. Provider 选择门禁
选择或切换 RTC Provider 前必须形成可复现的能力矩阵:
| 维度 | 验证内容 |
|---|---|
| 平台覆盖 | 五端目标系统、CPU ABI、最低版本、模拟器/真机限制 |
| 网络 | IPv4/IPv6、NAT64、UDP 被禁、代理/VPN、弱网、跨运营商、TURN 覆盖 |
| 音频 | AEC/ANS/AGC、蓝牙、听筒/扬声器、耳机插拔、系统打断 |
| 视频 | 编解码器、硬编硬解、旋转、前后台、Simulcast/SVC、屏幕共享 |
| 安全 | 传输加密、Token 模型、漏洞响应、数据驻留、合规材料 |
| 运维 | 区域 SLA、容量、限流、故障切换、状态页、支持响应 |
| 成本 | 峰值并发、分钟、带宽、录制/转写、TURN 和跨区费用 |
| 可迁移性 | Provider SPI 覆盖率、房间语义差异、双 Provider 灰度与回滚 |
不以 Demo 能通话作为选型结论。最终 Adapter 必须通过相同 XHIM 合同测试,业务 代码和 UI 不得 import 厂商 SDK 类型。
9. 发布门禁
9.1 媒体消息
- 内置 Payload Protobuf 字段冻结并生成各端 Codec;
- 上传、断点续传、授权刷新、完成校验和取消都有进程重启测试;
- 消息发送严格依赖稳定
MediaRef,数据库中无临时 URL; - 受损文件、超大文件、错误 MIME、解码炸弹和路径注入测试通过;
- 五端缓存隔离、配额、后台限制和真机验证通过;
- 未知 Payload、插件缺失和 Renderer 崩溃始终可显示 fallback。
9.2 实时通话
- Call Actor、信令幂等/过期/补序和 Provider generation fence 已实现;
- 至少一个 Provider Adapter 和测试 Provider 通过统一合同测试;
- iOS/macOS、Android、Windows、HarmonyOS 的权限、系统通话、后台、锁屏、 音频路由和中断测试通过;
- UDP 禁用、NAT64、VPN、网络切换、弱网、Push 延迟和进程终止 Chaos 通过;
- 1v1 呼叫碰撞、跨设备接听、取消/接听竞态、迟到事件和重复 Push 通过;
- Join/TURN Token、日志脱敏、录制提示、隐私材料和安全评审完成;
- 发布包、示例 App、服务端版本矩阵、Provider 版本矩阵、SLA 和回滚预案冻结。
任何一项未满足时,只能标记为 Preview/Alpha,不得对外宣称“商用音视频”。