Skip to content

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 --> CallBackend

2. 当前实现边界

截至 2026-07-26,仓库已经实现:

  • 通用、不可变且版本化的消息信封: content_type + content_version + payload + fallback_text
  • 旧文本消息在存储层规范化为 text/plain@1,旧 Wire 字段号和语义保留;
  • 未知类型和高版本二进制 Payload 可通过 Storage、Outbox、Sync、Engine 和 xhim_v1 C 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,支持上传/下载方向、幂等 创建、持久进度、合法状态转换、单调 revision fence、可重试失败、执行租约 以及完成后的稳定 MediaRef;当前 Schema 为 v12,冻结 fixture 覆盖 v1-v12 到当前版本的迁移与重启恢复;
  • 供应商无关的阻塞式 MediaTransport SPI、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_typev1 Payload
text/plainUTF-8 文本
xhim.media.imageImage descriptor + 一个或多个稳定 MediaRef
xhim.media.audioAudio descriptor + 稳定 MediaRef
xhim.media.videoVideo descriptor + 视频/封面 MediaRef
xhim.media.fileFile descriptor + 稳定 MediaRef
xhim.call.summary已结束通话的持久摘要,不含实时信令

第三方类型必须使用组织拥有的反向域名命名空间,例如 com.example.ordercontent_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_name

mime_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: 用户或策略重试

商业实现必须满足:

  1. Core 先把媒体任务、选取来源和目标消息草稿可靠持久化;
  2. 上传授权只包含最小权限、对象范围和短有效期;
  3. 大文件使用分片上传;已确认 Part、上传 Session 和 Hash 状态可重启恢复;
  4. 授权过期刷新授权,不重新创建业务消息或改变 client_message_id
  5. Complete 由服务端校验大小、Hash、类型和租户归属后返回稳定 MediaRef
  6. 只有所有必需 MediaRef 完成后,才冻结消息 Payload 并进入消息 Outbox;
  7. 取消媒体任务必须与取消消息草稿使用显式状态转换,不能靠删除临时文件猜测;
  8. 相机、相册和文件 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 平台桥接

平台必须接入的系统能力
iOSCallKit、PushKit 合规路径、AVAudioSession、相机/麦克风权限、后台模式
macOS系统音频会话、相机/麦克风权限、设备切换、窗口/屏幕采集授权
AndroidTelecom/ConnectionService 或受支持自管通话、前台服务、AudioFocus、运行时权限
Windows音频端点/默认通信设备变化、相机/麦克风隐私权限、系统通知和电源状态
HarmonyOSArkTS/Node-API Facade、音视频权限、音频打断与路由、后台/通知能力

平台桥只上报系统事实和执行 Actor 已决定的动作。它不能根据收到 Push 就直接 创建第二个 Provider Session,也不能在 UI 消失时自行挂断仍由系统托管的通话。

5.7 Push、锁屏与恢复

  1. Push Payload 只包含最小 call identity、显示所需的非敏感摘要和过期时间;
  2. 收到 Push 后先建立系统来电 UI,同时并发向信令服务复核通话仍可接听;
  3. 服务端已取消、已被其他设备接听或邀请过期时,立即结束系统来电 UI;
  4. 用户在系统 UI 接听后,动作进入 Call Actor,再取得短期 Join Token 并创建 Provider Session;
  5. App 被系统终止后,服务端通话状态和系统框架负责恢复;普通 IM 消息缓存不是 通话真相;
  6. 任何 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,不得对外宣称“商用音视频”。

10. 标准基线

XHIM 客户端 SDK 与服务端文档