ppcenter 播放侧 API¶
| 属性 | 值 |
|---|---|
| 文档定位 | ppcenter 提供给播放端(pplayer / 自研播放器)的完整 API 参考 |
| 适用接口 | /v1/play/*、/v1/nat/probe、/ws/play、/v1/p2p/signal、legacy /api/play/* |
| 不包含 | ppcenter 管理后台 /admin/*、控制台用户接口、推流侧 /v1/publish/*、录制接口 |
| 生产示例 | https://api.pp-cdn.org |
| 数据格式 | HTTP JSON;WebSocket 为 JSON 文本帧 |
| 路由来源 | ppcenter internal/apis/route.go |
本文只描述播放侧。推流侧、控制台及管理后台接口不在本文范围内。
1. 通用约定¶
1.1 接口总览¶
| 方法 | 路径 | 用途 | 鉴权 |
|---|---|---|---|
| POST | /v1/play/link |
用 appId + streamName 生成短时 viewer token 和 pplayer 链接 |
无(可通过配置关闭) |
| POST | /v1/play/requests |
播放决策:选 Edge、判断 P2P、返回 WHEP/ERTSP 地址 | viewer token |
| POST | /v1/nat/probe |
上报播放端 NAT 探测结果,供 P2P 资格判断 | appSecret 或 viewer token |
| POST | /v1/play/outcome |
上报本次实际走 P2P 还是 Edge | appSecret 或 viewer token |
| POST | /v1/play/session/start |
首帧显示后开始播放会话 | 请求体中的 appSecret 或 txTime/txSecret |
| POST | /v1/play/session/heartbeat |
约每 20 秒刷新会话与卡顿统计 | 同上 |
| POST | /v1/play/session/stop |
结束播放会话 | 同上,支持 sendBeacon |
| GET | /ws/play |
对时、时延上报、兼容性 WHEP URL 查询、PTS 推送 | 无 |
| GET | /v1/p2p/signal |
P2P Offer/Answer/ICE 信令转发 | 播放决策返回的 P2P token |
| POST | /api/play/edgeNode |
legacy 选 Edge | 可选 MD5 token |
| POST | /api/play/start |
legacy 起播统计 | 无 |
| POST | /api/play/end |
legacy 结束统计 | 无 |
| POST | /api/play/lag |
legacy 卡顿统计 | 无 |
新接入应使用 /v1/*。legacy /api/play/* 仅为兼容旧客户端保留。
如果部署未启用 P2P coordinator,/v1/nat/probe 与 /v1/p2p/signal 不会注册(返回 404),/v1/play/requests 仍可正常返回纯 Edge 播放决策。
1.2 HTTP 响应格式¶
/v1/play/* 与 /v1/nat/probe 成功时直接返回业务 JSON,外面没有 {code,msg,data} 信封。
失败统一为:
{
"code": "invalid_request",
"message": "valid streamName and clientId are required"
}
HTTP 状态码表达大类,code 表达具体原因。
1.3 鉴权¶
viewer token¶
播放决策使用 viewer token:
Authorization: Bearer <appId>:<txTime>:<txSecret>
txTime:16 进制 Unix 秒,表示 token 过期时间。txSecret:使用appSecret对appId/streamName签名的 HMAC-SHA256。- 当前格式:
v1.<keyId>.<hex>,例如v1.default.4f...。 - ppcenter 同时接受当前密钥和轮换过渡期内的历史密钥。
签名原文:
<keyId>:<appId>/<streamName>:<TX_TIME_UPPER>
其中 TX_TIME_UPPER 是 txTime 的大写形式。
可以直接调用:
POST /v1/play/link
Content-Type: application/json
{"appId":"app_xxx","streamName":"stream_xxx"}
由 ppcenter 生成 txTime/txSecret,播放器无需持有 appSecret。
appSecret¶
以下接口允许在 JSON 请求体里传 appSecret:
POST /v1/nat/probePOST /v1/play/outcomePOST /v1/play/session/start|heartbeat|stop
其中 NAT、outcome 还接受 Authorization 里的 viewer token;session 接口只从请求体读取 txTime/txSecret,因为页面卸载时用 navigator.sendBeacon 无法自定义请求头。
1.4 限流与缓存¶
| 范围 | 限制 |
|---|---|
/v1/play/* |
每 IP 每分钟 180 次(play_api) |
/v1/nat/probe |
每 IP 每分钟 30 次(nat_probe) |
/api/play/* |
每 IP 每分钟 120 次(legacy_play) |
所有播放相关 JSON 响应都带:
Cache-Control: no-store
CORS 允许 *、POST/PUT/OPTIONS/GET,请求头允许 Content-Type、Authorization。
1.5 字段约束¶
| 字段 | 规则 |
|---|---|
appId |
^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ |
streamName |
^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ |
sessionId |
^[A-Za-z0-9][A-Za-z0-9._-]{7,63}$ |
path / connType |
p2p 或 edge |
codec |
h264 或 hevc |
2. 生成播放链接¶
POST /v1/play/link¶
把一个 appId + streamName 换成短时 viewer token 和可直接打开的 pplayer 链接。
该接口不需要鉴权,只签发读 token,不会泄露 appSecret,也不能用于推流。生产环境是否开放由 ppcenter 配置 play.linkGeneratorEnabled 控制;关闭时返回 404。
请求¶
{
"appId": "app_xxx",
"streamName": "stream_xxx"
}
响应 200¶
{
"appId": "app_xxx",
"streamName": "stream_xxx",
"txTime": "6923a1f0",
"txSecret": "v1.default.7d1f...",
"expiresAt": "2026-11-06T09:30:00Z",
"pplayerUrl": "https://pplayer.pp-cdn.org/?ppcenter=https%3A%2F%2Fapi.pp-cdn.org&appId=app_xxx&streamName=stream_xxx&txTime=6923a1f0&txSecret=v1.default.7d1f..."
}
| 字段 | 说明 |
|---|---|
txTime / txSecret |
可直接放入 Authorization: Bearer appId:txTime:txSecret |
expiresAt |
token 过期时间,RFC3339 |
pplayerUrl |
可选;ppcenter 配置了 play.playerBaseUrl 时返回 |
错误¶
| HTTP | code | 说明 |
|---|---|---|
| 400 | invalid_request |
JSON 非法,或 appId/streamName 格式错误 |
| 404 | not_found |
链接生成器被关闭,或 app 不存在 |
| 402 | account_in_arrears |
账户欠费或不可用 |
| 503 | credential_store_unavailable |
凭证存储不可用 |
| 503 | service_unavailable |
播放服务不可用 |
3. 播放决策¶
POST /v1/play/requests¶
播放器拿到 viewer token 后调用此接口,ppcenter 选择 Edge,并在条件允许时分配 P2P 会话。
请求头¶
Authorization: Bearer <appId>:<txTime>:<txSecret>
Content-Type: application/json
请求体¶
{
"streamName": "stream_xxx",
"clientId": "viewer-8f3c2a",
"requestRegion": "cn",
"capabilities": ["whep", "p2p-h264-opus"],
"natProbeId": "probe-xxxx",
"codec": "h264"
}
| 字段 | 必填 | 说明 |
|---|---|---|
streamName |
是 | 流名,不含 appId/ 前缀 |
clientId |
是 | 播放端稳定标识;建议同一播放器复用 |
requestRegion |
否 | 期望区域,用于 Edge 调度 |
capabilities |
是 | 至少包含 whep 或 ertsp |
natProbeId |
否 | /v1/nat/probe 返回的 probeId,P2P 必须 |
codec |
否 | h264 / hevc;显式指定时优先选择该编码管线 |
capabilities 常用值:
| 值 | 含义 |
|---|---|
whep |
浏览器/WebRTC 播放,支持 Edge 与 P2P 竞争 |
ertsp |
嵌入式 ERTSP 播放,只走 Edge,不尝试 P2P |
p2p |
支持 P2P |
p2p-h264-opus |
支持 H264 + Opus 的 P2P |
whep 与 ertsp 建议只传一个;两者同时出现时 ppcenter 走 ERTSP 分支。
响应 200:Edge 播放¶
{
"mode": "edge-only",
"edgeStreamUrl": "https://edge-1.edge.pp-cdn.org/app_xxx/stream_xxx?txTime=6923a1f0&txSecret=v1.default.7d1f...",
"expiresAt": "2026-11-06T17:30:00Z",
"edge": {
"nodeId": 12,
"nodeName": "edge-hk-1",
"region": "hk"
},
"reason": "symmetric_nat",
"publisherNat": "symmetric",
"playerNat": "restricted",
"p2pAvailable": false,
"p2pAvailability": {
"publisherConnected": true,
"publisherSlotsFree": 2,
"publisherSlotsMax": 3,
"reason": "symmetric_nat"
}
}
响应 200:P2P 可用¶
{
"mode": "p2p-connect",
"edgeStreamUrl": "https://edge-1.edge.pp-cdn.org/app_xxx/stream_xxx?txTime=6923a1f0&txSecret=v1.default.7d1f...",
"expiresAt": "2026-11-06T17:30:00Z",
"edge": {
"nodeId": 12,
"nodeName": "edge-hk-1",
"region": "hk"
},
"reason": "eligible",
"publisherNat": "cone",
"playerNat": "restricted",
"p2pAvailable": true,
"p2pAvailability": {
"publisherConnected": true,
"publisherSlotsFree": 2,
"publisherSlotsMax": 3,
"reason": "eligible"
},
"p2p": {
"sessionId": "sess_xxx",
"participantId": "part_xxx",
"signalUrl": "wss://api.pp-cdn.org/v1/p2p/signal",
"token": "p2p-token...",
"expiresAt": "2026-11-06T09:40:00Z",
"raceWindowMs": 5000,
"connectTimeoutMs": 10000,
"stunServers": ["stun:stun.pp-cdn.org:3478"]
}
}
响应 200:ERTSP¶
{
"mode": "edge-ertsp",
"edgeStreamUrl": "rtsp://txTime:[email protected]:8554/app_xxx/stream_xxx/h264",
"expiresAt": "2026-11-06T17:30:00Z",
"edge": {
"nodeId": 12,
"nodeName": "edge-hk-1",
"region": "hk"
},
"reason": "ertsp_capability"
}
ERTSP 固定单码率、H264,不返回 p2p。
响应字段¶
| 字段 | 说明 |
|---|---|
mode |
edge-only / p2p-connect / edge-ertsp |
edgeStreamUrl |
Edge 播放基址或 ERTSP URL |
expiresAt |
地址过期时间,RFC3339 |
edge.nodeId / edge.nodeName / edge.region |
选中的 Edge |
reason |
P2P 资格或决策原因 |
publisherNat / playerNat |
观察到的 NAT 类型,可选 |
p2pAvailable |
本次是否真的可以走 P2P;等价于 mode == "p2p-connect" |
p2pAvailability |
P2P 不可用时解释 publisher 在线/槽位状态 |
p2p |
仅 mode == "p2p-connect" 时返回 |
edgeStreamUrl 是裸流路径签名 URL,不含 codec 段和 /whep。播放器根据自身 HEVC 解码能力追加:
/hevc/whep
/h264/whep
签名是对裸 {appId}/{streamName} 计算的,追加 codec 段后仍可被 Edge 验证,因此同一个决策可服务两种编码。
p2p 字段¶
| 字段 | 说明 |
|---|---|
sessionId |
P2P 会话 ID |
participantId |
播放端参与者 ID |
signalUrl |
P2P 信令 WebSocket 地址 |
token |
信令 token |
expiresAt |
P2P 会话过期时间 |
raceWindowMs |
P2P 与 Edge 竞争窗口 |
connectTimeoutMs |
P2P 建连超时 |
stunServers |
传给 RTCPeerConnection.iceServers 的 STUN 列表;可能省略 |
错误¶
| HTTP | code | 说明 |
|---|---|---|
| 400 | invalid_request |
streamName/clientId 为空或格式错误,codec 非 h264/hevc |
| 400 | unsupported_capability |
capabilities 不含 whep 或 ertsp |
| 401 | invalid_viewer_token |
token 缺失、格式错误或签名不匹配 |
| 401 | viewer_token_expired |
txTime 已过期 |
| 402 | account_in_arrears |
账户欠费 |
| 503 | no_edge_available |
没有健康 Edge |
| 503 | invalid_edge_endpoint |
选中的 Edge 地址不可用 |
| 503 | credential_store_unavailable |
凭证存储不可用 |
| 503 | service_unavailable |
播放服务不可用 |
4. NAT 探测上报¶
POST /v1/nat/probe¶
播放端通过 STUN/ICE 得到自身公网映射后上报,ppcenter 用它判断 P2P 直连资格。返回的 probeId 作为 natProbeId 传给 /v1/play/requests。
鉴权¶
二选一:
- 请求体
appSecret Authorization: Bearer <appId>:<txTime>:<txSecret>
请求¶
{
"appId": "app_xxx",
"kind": "player",
"clientId": "viewer-8f3c2a",
"natType": "restricted",
"publicIp": "203.0.113.9",
"publicPort": 54321
}
| 字段 | 必填 | 说明 |
|---|---|---|
appId |
是 | 应用 ID |
appSecret |
否 | 与 viewer token 二选一 |
kind |
是 | player 或 publisher |
streamName |
publisher 必填 | 播放端探测可省略 |
clientId |
是 | 与播放决策保持一致 |
natType |
否 | public/open、cone/full_cone、restricted/port_restricted、symmetric、cgnat;未知值按 restricted 处理 |
publicIp |
是 | 必须是可解析的 IP 字面量,不能是 loopback 或 unspecified;RFC1918 私网地址允许 |
publicPort |
是 | 1–65535 |
响应 200¶
{
"probeId": "probe-5f3c...",
"natType": "restricted",
"directlyReachable": true
}
probeId 由 appId + clientId 稳定推导,同一对值重复上报会返回同一个 ID。观察记录有效期约 5 分钟。
错误¶
| HTTP | code | 说明 |
|---|---|---|
| 400 | invalid_request |
缺字段、publicIp 非法、端口越界、publisher 缺 streamName |
| 401 | invalid_credentials |
appSecret 或 viewer token 无效 |
| 503 | credential_store_unavailable |
凭证存储不可用 |
| 503 | nat_store_unavailable |
NAT 观察存储不可用 |
5. 播放结果上报¶
POST /v1/play/outcome¶
播放器在最终确定走 P2P 还是 Edge 后上报一次,用于统计真实 P2P 渗透率和播放质量。
鉴权¶
二选一:
- 请求体
appSecret Authorization: Bearer <appId>:<txTime>:<txSecret>
使用 viewer token 时必须提供 streamName,因为签名覆盖 appId/streamName。
请求¶
{
"appId": "app_xxx",
"streamName": "stream_xxx",
"path": "p2p",
"reason": "eligible",
"publisherNat": "cone",
"playerNat": "restricted",
"ipv4": "203.0.113.9",
"qualityLevel": "1080p",
"costTime": 860,
"isOK": true
}
| 字段 | 必填 | 说明 |
|---|---|---|
appId |
是 | 应用 ID |
appSecret |
否 | 与 viewer token 二选一 |
streamName |
视鉴权方式 | 使用 viewer token 时必填 |
path / connType |
是 | 实际播放路径:p2p 或 edge;两者任传其一,connType 优先 |
reason |
否 | 播放决策返回的 reason,失败时填失败原因 |
publisherNat / playerNat |
否 | 播放决策返回的 NAT 类型 |
ipv4 |
否 | 客户端公网 IPv4;缺省时用服务端看到的请求 IP |
qualityLevel |
否 | 实际播放分辨率,如 1080p |
costTime |
否 | 从请求连接到收到首帧的毫秒数;失败时会被置 0 |
isOK |
否 | 是否成功,缺省 true |
响应 200¶
{
"ok": true,
"path": "p2p"
}
特殊情况:
| 情况 | 响应 |
|---|---|
| 首次上报 404“流不存在” | {"ok":true,"path":"edge","counted":false} |
| 同一客户端重复上报 404 | {"ok":true,"path":"edge","skipped":"stream_not_found_already_reported"} |
| 流已离线时的失败上报 | 明细被跳过,返回 skipped: "stream_offline_failure" |
404 不计入 P2P 渗透率,也不携带加载耗时。
错误¶
| HTTP | code | 说明 |
|---|---|---|
| 400 | invalid_request |
appId 非法,或 path 不是 p2p/edge |
| 401 | invalid_credentials |
appSecret / viewer token 无效 |
| 503 | credential_store_unavailable |
凭证存储不可用 |
| 503 | play_outcome_store_unavailable |
结果存储不可用 |
6. 播放会话¶
用于控制台“在线拉流”列表和卡顿统计。推荐时序:
- 首帧显示后调用
start - 每约 20 秒调用
heartbeat - 页面卸载或播放结束时调用
stop
三个接口都只接受 JSON 请求体中的 appSecret 或 txTime/txSecret,不读取 Authorization,以便 sendBeacon 工作。
POST /v1/play/session/start¶
{
"sessionId": "viewer-8f3c2a-01",
"appId": "app_xxx",
"streamName": "stream_xxx",
"txTime": "6923a1f0",
"txSecret": "v1.default.7d1f...",
"path": "p2p",
"ipv4": "203.0.113.9",
"qualityLevel": "1080p",
"costTime": 860
}
必填:sessionId、appId、path,以及一种有效凭证。
响应:
{"ok": true}
POST /v1/play/session/heartbeat¶
{
"sessionId": "viewer-8f3c2a-01",
"appId": "app_xxx",
"streamName": "stream_xxx",
"txTime": "6923a1f0",
"txSecret": "v1.default.7d1f...",
"path": "p2p",
"p2pDelayMs": 82,
"lagCount": 1,
"lagDurationMs": 420
}
| 字段 | 说明 |
|---|---|
path |
当前实际路径;P2P 切 Edge 后应上报 edge |
p2pDelayMs |
P2P 时延;超过 60 秒的值会被丢弃 |
lagCount |
累计卡顿次数 |
lagDurationMs |
累计卡顿时长,毫秒 |
会话不存在不算错误,客户端重新发送 start 即可。
POST /v1/play/session/stop¶
{
"sessionId": "viewer-8f3c2a-01",
"appId": "app_xxx",
"streamName": "stream_xxx",
"txTime": "6923a1f0",
"txSecret": "v1.default.7d1f...",
"endReason": "page_unload",
"lagCount": 1,
"lagDurationMs": 420
}
会话接口错误¶
| HTTP | code | 说明 |
|---|---|---|
| 400 | invalid_request |
JSON 非法、sessionId/appId 格式错误;start 还要求 path 合法 |
| 401 | invalid_credentials |
appSecret 或 txTime/txSecret 无效 |
| 503 | credential_store_unavailable |
凭证存储不可用 |
| 503 | play_session_store_unavailable |
会话存储不可用 |
7. 播放 WebSocket:/ws/play¶
公共 WebSocket,用于:
- 客户端/服务端时钟同步
- 上报端到端时延
- 查询兼容性 WHEP URL
- 接收 publisher PTS 推送
连接¶
wss://api.pp-cdn.org/ws/play?streamPath=app_xxx/stream_xxx&workerId=12
| Query | 必填 | 说明 |
|---|---|---|
streamPath |
建议 | appId/streamName;时延统计按它归属 app |
workerId |
否 | Edge worker ID |
连接本身不鉴权。服务端每 90 秒内有 Ping/Pong 保活。
时钟同步¶
请求:
{"type":"TIME_SYNC","t1":1762412400000}
响应:
{"type":"TIME_SYNC_ACK","t1":1762412400000,"t2":1762412400031,"t3":1762412400032}
客户端可用 t1/t2/t3 计算时钟偏移。
时延上报¶
{"type":"LATENCY_REPORT","path":"p2p","delayMs":82}
path 只能是 edge 或 p2p。该消息没有响应;服务端按分钟聚合 P50/P80/P95/P99。
查询 WHEP URL¶
请求:
{"type":"GET_WHEP_URL","streamPath":"app_xxx/stream_xxx","region":"cn","codecType":"hevc"}
响应:
{
"type": "WHEP_URL",
"code": 0,
"msg": "OK",
"streamPath": "app_xxx/stream_xxx",
"codecType": "hevc",
"whepUrl": "https://edge-1.edge.pp-cdn.org/app_xxx/stream_xxx/hevc/whep",
"webrtcABRWSPath": "/app_xxx/stream_xxx"
}
| code | 说明 |
|---|---|
| 0 | 成功 |
| 400 | 缺 streamPath |
| 422 | codecType 非 h264/hevc |
| 500 | resolver 未就绪 |
| 1001 | 其他解析错误 |
| 1002 | 指定编码不可用 |
PTS 推送¶
服务端可能主动推送:
{"type":"PUB_PTS","ptsFnMs":123456789}
用于多路同步/首帧时间计算。
8. P2P 信令:/v1/p2p/signal¶
播放决策返回 p2p.signalUrl 和 p2p.token 后,播放器建立 WebSocket:
wss://api.pp-cdn.org/v1/p2p/signal
鉴权二选一:
Authorization: Bearer <token>
或:
Sec-WebSocket-Protocol: ppcdn-token.<token>
使用子协议时服务端返回 Sec-WebSocket-Protocol: ppcdn-p2p-v1。
消息格式¶
{
"v": 1,
"seq": 12,
"sentAt": 1762412400000,
"type": "offer",
"sessionId": "sess_xxx",
"streamPath": "app_xxx/stream_xxx",
"sdp": "v=0...",
"candidate": null,
"reason": "",
"expiresAt": "2026-11-06T09:40:00Z"
}
| 字段 | 说明 |
|---|---|
v |
协议版本,必须为 1 |
seq |
递增序号;重复或回退序号会被关闭 |
sentAt |
发送时间;与服务端偏差超过 120 秒会被拒绝 |
type |
offer / answer / ice 等 |
sessionId |
P2P 会话 ID |
sdp |
SDP,最大 96 KB |
candidate |
ICE candidate,最大 4 KB |
reason |
关闭/失败原因 |
连接建立后服务端先发:
{"v":1,"type":"ready","streamPath":"app_xxx/stream_xxx"}
限制:
| 项 | 限制 |
|---|---|
| 每 10 秒消息数 | 40 |
| ICE candidate 数 | 64 |
| candidate 大小 | 4 KB |
| SDP 大小 | 96 KB |
| 时钟偏差 | 120 秒 |
| 读超时 | 90 秒 |
9. Legacy /api/play/*¶
旧客户端兼容接口,返回 {code,msg,data} 信封。新接入请使用 /v1/play/*。
POST /api/play/edgeNode¶
{
"expTime": 1762412400,
"streamId": "app_xxx/stream_xxx",
"region": "cn"
}
Authorization 头可选。若 Authorization 或 expTime 任一存在,则二者必须同时存在,且:
Authorization = MD5(API_PLAYER_KEY + expTime + streamId)
成功:
{
"code": 0,
"msg": "OK",
"data": {
"streamId": "app_xxx/stream_xxx",
"edges": [],
"recommended": "https://...",
"webrtcABRWSPath": "/app_xxx/stream_xxx"
}
}
无 Edge 时返回 HTTP 422:
{"code":1001,"msg":"no edge found for app_xxx/stream_xxx"}
POST /api/play/start¶
{
"streamPath": "app_xxx/stream_xxx",
"loadTime": 860,
"protocol": "webrtc",
"quality": "1080p",
"isSucceed": true
}
成功返回 202:
{"code":0,"msg":"Accepted","data":{"accepted":true}}
失败的 404 上报会被忽略;其他失败会把 loadTime 置 0。
POST /api/play/end¶
{
"streamPath": "app_xxx/stream_xxx",
"playDuration": 120
}
POST /api/play/lag¶
{
"streamPath": "app_xxx/stream_xxx",
"lagDuration": 420
}
end 与 lag 成功均返回 202 {"accepted":true}。
10. 推荐接入流程¶
1. POST /v1/play/link 获取 txTime/txSecret(或业务后端自行签名)
2. POST /v1/nat/probe player NAT 探测,拿到 probeId
3. POST /v1/play/requests viewer token + clientId + natProbeId
4. 若 mode == p2p-connect:
- 连接 p2p.signalUrl,使用 p2p.token
- 在 raceWindowMs 内竞争 P2P
- connectTimeoutMs 内未成功则回退 Edge
若 mode == edge-only / edge-ertsp:
- 直接使用 edgeStreamUrl
5. WHEP:根据 HEVC 能力追加 /hevc/whep 或 /h264/whep
ERTSP:直接打开返回的 rtsp:// URL
6. 首帧显示后 POST /v1/play/session/start
7. 每约 20 秒 POST /v1/play/session/heartbeat
8. 路径确定后 POST /v1/play/outcome 一次
9. 结束或页面卸载时 POST /v1/play/session/stop
11. 注意事项¶
- P2P 是否可用由
p2pAvailable决定,不要仅凭p2p字段或reason字符串推断。 p2pAvailability用于解释 P2P 为什么不可用:publisher 未连接、槽位已满、NAT 不可穿越等。edgeStreamUrl是签名裸路径。不要自己拼接txSecret,只需追加 codec/WHEP 路径。- viewer token 过期后重新调用
/v1/play/requests,不要复用旧 URL。 /v1/play/outcome每个播放会话上报一次即可;失败重试可能重复计数。- 404“流不存在”只记录一次,且不计入 P2P 渗透率。
- session 接口的心跳间隔建议 20 秒;停止上报优先使用
sendBeacon。 - WebSocket
/ws/play与/v1/p2p/signal是两条独立连接,前者用于统计/对时,后者用于 P2P 媒体协商。 - 生产环境建议播放页面与 Edge 同协议(HTTPS 页面只能连 WSS/HTTPS),否则会触发混合内容拦截。