跳转至

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/probe
  • POST /v1/play/outcome
  • POST /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。

鉴权

二选一:

  1. 请求体 appSecret
  2. 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 渗透率和播放质量。

鉴权

二选一:

  1. 请求体 appSecret
  2. 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. 播放会话

用于控制台“在线拉流”列表和卡顿统计。推荐时序:

  1. 首帧显示后调用 start
  2. 每约 20 秒调用 heartbeat
  3. 页面卸载或播放结束时调用 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),否则会触发混合内容拦截。