跳转到内容

Realtime API

Realtime API 是房间内的长连接协议。它用于聊天、播放控制、播放进度、WebRTC 信令,以及订阅房间内可缓存资源的变化。

如果你只需要一次性读取或修改资源,优先使用 HTTP/OpenAPI 或 gRPC。只有需要低延迟推送、减少轮询、或保持房间实时状态时,才使用 Realtime API。

WebSocket 地址:

wss://<host>/ws/rooms/<roomId>

认证方式:

方式适用客户端说明
Authorization: Bearer <accessToken>原生客户端、CLI、服务端 SDK登录用户
Authorization: Bearer <guest_token>原生客户端、CLI、服务端 SDK游客;先调用 POST /api/auth/guest-token
?ticket=<ticket>浏览器和不方便设置 header 的客户端先通过 POST /api/tickets 创建短期、一次性、房间绑定 ticket

WebSocket 只处理二进制 protobuf 帧。客户端发送 synctv.client.ClientMessage,服务端返回 synctv.client.ServerMessage。文本帧、ping、pong 等非业务帧不会被当作业务消息处理。

const roomId = 'room_1';
const ticket = ticketResponse.ticket;
const ws = new WebSocket(`wss://app.example.com/ws/rooms/${roomId}?ticket=${ticket}`);
ws.binaryType = 'arraybuffer';
ws.onmessage = async (event) => {
const bytes =
event.data instanceof Blob
? new Uint8Array(await event.data.arrayBuffer())
: new Uint8Array(event.data);
const message = ServerMessage.decode(bytes);
handleServerMessage(message);
};
function sendClientMessage(message) {
ws.send(ClientMessage.encode(message).finish());
}

游客不是登录用户。服务端不会为游客创建用户记录、成员记录或登录会话;游客身份只由房间绑定的 guest token 表示,对外 ID 使用 gst_ 前缀。

游客进入房间必须同时满足这些条件:

  • 全局 user.enable_guest 开启。
  • 房间 allowGuestJoin 开启。
  • 房间没有密码,且未关闭、未封禁。
  • guest token 的房间版本仍然有效;房间关闭游客、设置密码或触发游客吊销后,旧 token 失效。

游客默认只能接收基础房间实时状态,不能读取播放列表、发送聊天或管理媒体。房间可以通过 guestAddedPermissions 开放 guest 安全能力,例如成员列表、聊天历史或 WebRTC 信令;该上限独立于 member 权限。即使增加权限,游客仍不能调用账号、播放列表、媒体管理、成员管理、房间管理、Provider 凭据或管理面接口。

Terminal window
curl -sS "$BASE_URL/api/auth/guest-token" \
-H "Content-Type: application/json" \
-d '{"roomId":"room_1"}'

原生 WebSocket 客户端使用响应中的 token

Authorization: Bearer <guest_token>

gRPC MessageStream 使用同一个 token:

x-room-id: room_1
authorization: Bearer <guest_token>
场景使用 Realtime 的原因
同步播放播放、暂停、seek、切换媒体需要低延迟传播
聊天聊天消息事件通过显式订阅接收
播放信息URL 过期、Provider 凭据变化或媒体切换后需要刷新
房间成员列表成员加入、离开、权限变化需要同步 UI
WebRTC 信令offer、answer、ICE candidate 需要房间内转发;目标地址使用 public_actor_id:conn_id,登录用户是 usr_*,游客是 gst_*

WebRTC 客户端在发送信令前调用 GET /api/rooms/<roomId>/webrtc/ice-servers 获取 ICE servers。该接口和 Realtime 使用同一套房间 actor 权限:登录成员和游客都通过 Authorization: Bearer <token> 认证,并且都必须拥有 use_webrtc

常用客户端消息:

ClientMessage 字段用途
chat发送聊天消息
heartbeat客户端心跳
playback_progress上报当前播放位置和播放状态
playback_update发起播放状态更新命令
webrtc_offer / webrtc_answer / webrtc_ice_candidateWebRTC 信令转发
webrtc_join / webrtc_leave加入或离开 WebRTC 会话
observeResource订阅实时资源
unobserveResource取消订阅实时资源

常用服务端消息:

ServerMessage 字段用途
chat收到聊天消息
heartbeatAck心跳确认
error普通业务错误
playbackState / playingChanged播放状态或播放目标变化
roomSettings房间设置变更广播
mediaAdded / mediaUpdated / mediaRemoved媒体变更广播
playlistCreated / playlistUpdated / playlistDeleted播放列表变更广播
permissionChanged / userJoined / userLeft / roomMembers成员和权限变化
chatEventresourceChanged.chatEvent显式订阅后的聊天消息事件
resourceObserved / resourceChanged / resourceObserveError实时资源观察结果

字段的完整结构以 synctv-proto/proto/client.proto 为准。端到端示例见 SDK 与 API 示例

Realtime API 的所有业务消息都在 ClientMessage.messageServerMessage.messageoneof 中。客户端应该按 oneof 类型分发,而不是按文本字段或日志字符串判断。

function handleServerMessage(message) {
if (message.chatEvent) {
appendChatEvent(message.chatEvent);
return;
}
if (message.chat) {
appendChat(message.chat);
return;
}
if (message.playbackState) {
updatePlayback(message.playbackState);
return;
}
if (message.resourceChanged) {
if (message.resourceChanged.chatEvent) {
appendChatEvent(message.resourceChanged.chatEvent);
return;
}
handleResourceChanged(message.resourceChanged);
return;
}
if (message.error) {
handleRealtimeError(message.error);
}
}

连接建立后,先订阅当前页面需要的资源。资源观察见 Realtime 资源观察

聊天消息事件使用独立游标:双向流中发送 observeResource.chatEvents.afterEventSequence 可以补发指定事件之后的事件;HTTP SSE GET /api/rooms/<roomId>/watch/chat-events 支持 afterEventSequence 查询参数,并在重连时读取浏览器发送的 Last-Event-ID。服务端会在 SSE 事件上设置 id:,客户端保存该值即可恢复。

WebSocket 断开后,观察不会跨连接保留。客户端重连时应该:

  1. 重新获取 WebSocket ticket,或用有效 access token 重新连接。
  2. 为当前仍然打开的视图重新发送 observeResource
  3. 带上本地保存的 version,让服务端判断是否需要补发快照。
  4. playback 同时带上本地缓存的 mediaIdplaylistIdtargetplaybackClientProfile
  5. 收到 resourceObserved.changed=false 时保留本地缓存;收到 resourceChanged 时替换对应缓存。

如果连接长时间断开,客户端可以先用 HTTP/gRPC 拉取关键资源,再恢复观察,避免用户看到过旧 UI。

资源观察错误通过 ServerMessage.resourceObserveError 返回:

场景客户端动作
observeId 为空或超过 128 字符修正客户端代码,不要重试同一请求
未指定 resource修正客户端代码
超过每连接 64 个观察取消不需要的观察,或合并列表视图
快照服务不可用或加载失败展示临时错误,可按退避策略重试
权限、资源不存在或输入无效重新拉取房间状态,必要时引导用户返回

发送失败或连接被服务端断开时,不要假设观察仍然有效。重连后完整重建观察。