You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7.3 KiB
7.3 KiB
监控直播 HLS 真实播放补齐
日期:2026-09-02
状态:已确认(待实现计划)
背景
后端直播播放地址是 HLS:
https://vpull.jiagutech.com/dock-live/<streamSessionId>.m3u8?auth_key=...
当前前端缺口:
LivePlayer.vue把.m3u8直接赋给原生<video src>,项目无hls.js。Safari 通常可播,Chrome/Edge/Firefox 通常不能。useMonitorLive创建会话后只靠观看租约心跳刷新状态。默认租约约 30s,第一次刷新约 15s;即使流已就绪,页面也会长时间停在「启动中」。GET play-url返回expiresAt(默认约 300s),前端只在「尚无 playUrl」时拉一次;过期后播放失败,仅 toast,不换新地址。- 播放器错误只向上抛,没有重建路径。
- 关闭按钮语义只有「退出观看」(
leaveLive);后端已有POST /v1/live/:dockId/stop,前端未接。
相关后端契约:
GET /v1/live/:dockId/sessions/:streamSessionId→ 会话状态GET /v1/live/:dockId/play-url?streamSessionId=→{ playUrl, expiresAt, streamName }POST /v1/live/:dockId/sessions/:id/heartbeat→ 续观看租约DELETE .../viewers/me→ 退出观看POST /v1/live/:dockId/stop→ 停止直播推流
目标
监控详情实时画面:
- Chrome/Edge 用
hls.js播放 HLS;Safari / 原生可播 HLS 时走原生<video>。 - join 后每 2–3 秒查询会话,直到
streaming/failed/ 超时,再尽快取 playUrl。 - 记录 playUrl
expiresAt,到期前约 30 秒自动刷新地址并重建播放器。 - HLS 网络/鉴权/解码错误时限流重试:重新拉 play-url 并重建播放器。
- 区分:
- 退出观看:现有中心按钮 /
leaveLive - 停止直播:详情面板新增按钮,调
POST /stop
- 退出观看:现有中心按钮 /
非目标
- 不改 Media 页(仍可
window.open) - 不改后端协议 / 鉴权
- 不做 WebRTC / FLV 播放器
- 不把停止直播做成播放器内复杂手势
- 不在本轮处理多路同屏播放
方案
采用:LivePlayer 负责媒体;useMonitorLive 负责会话/轮询/续期/退出与停止;详情面板加停止按钮。
1. 依赖
package.json 增加 hls.js(运行时依赖)。
2. API
文件:src/api/live.js、src/config/urls.js
新增:
export function getLiveSession(dockId, sessionId) {
return request.get(`/v1/live/${dockId}/sessions/${sessionId}`)
}
export function stopLive(dockId) {
return request.post(`/v1/live/${dockId}/stop`)
}
现有 getLivePlayURL(dockId, sessionId) 保持;调用方必须消费返回的 expiresAt。
3. LivePlayer
文件:src/components/LivePlayer.vue
职责:
- props 仍接收
playUrl/phase/active/cameraLabel playable判定不变:有playUrl且非fake://- 当
playable:- 若
video.canPlayType('application/vnd.apple.mpegurl')真 → 原生src = playUrl - 否则动态/静态引入
hls.js:Hls.isSupported()时new Hls()→loadSource→attachMedia - 不支持则 emit
error,展示失败态
- 若
playUrl变化或组件卸载:destroy 旧 Hls 实例,清空video.src/removeAttribute('src')+load()- 监听
video.error与 hlsERROR(fatal)→ emiterror - 中心按钮语义不变:emit
toggle(由上层解释为退出观看 / 打开)
不在 LivePlayer 内请求 API。
4. useMonitorLive
文件:src/composables/useMonitorLive.js
状态扩展:
live = {
session, dockId, playUrl,
playUrlExpiresAt, // unix 秒;缺省时用拿到时刻 + 270s 兜底
heartbeatTimer,
phasePollTimer,
playRefreshTimer,
playRetryCount,
}
对外新增:stopLiveStream。
4.1 打开
joinLive(dockId)- 写
session/dockId - 启动观看租约心跳(逻辑保留;与状态轮询分离)
- 若已
streaming→refreshPlayURL() - 否则启动 phase 轮询:每 2.5s
getLiveSession- 响应经 http 解包后直接是
LiveSession(含phase/id),不是{ session }包装 streaming→ 停轮询,refreshPlayURL()failed/stopped→ toast,清本地态(不强制 stop)- 超过 60s 仍非终态 → toast「直播启动超时」,清本地态
- 响应经 http 解包后直接是
4.2 playUrl 刷新
refreshPlayURL():
- 调
getLivePlayURL - 写
live.playUrl - 写
live.playUrlExpiresAt = play.expiresAt || nowSec + 270 - 调度续期:
delay = max(5000, expiresAt*1000 - Date.now() - 30000) - 到期触发时再次
refreshPlayURL();失败走播放错误重试路径
仅当本地仍持有同一 session.id 时回填,避免竞态。
4.3 播放错误重建
详情面板 @live-error → composable onPlayError():
playRetryCount < 3:递增,重新refreshPlayURL()(新 URL 触发 LivePlayer 重建)- 达到 3:toast「播放失败」,重置计数但不 leave;心跳可继续
成功拿到并切换新 playUrl 后把 playRetryCount 置 0。
4.4 退出观看
现有 closeLive(true):
- 清所有定时器
- 清
session/playUrl/expires leaveLive
切换设备 / 关详情 / 卸载:仍走退出观看。
4.5 停止直播
stopLiveStream():
- 无 active session 直接 return
ui.confirm确认(文案:停止后所有观看者失去画面)POST /v1/live/:dockId/stop- 成功:清本地播放态(等同 close 本地部分);可再尝试 leave(忽略错误)
- 失败:toast,保留当前播放与心跳
5. 详情面板 UI
文件:src/components/MonitorDetailPanel.vue + MonitorView 接线
在「实时画面」标题行(状态文案 / 全屏旁)增加:
- 按钮文案:
停止直播 - 仅
liveView.active时可见/可点 - emit
stop-live→ MonitorView →useMonitorLive.stopLiveStream
中心播放按钮保持 toggle:有 session 时退出观看。
6. 验收
- Chrome/Edge:join → streaming 后页内可见真实 HLS 画面(非仅黑框)
- Safari:原生路径可播,不强制挂 hls.js
- join 后非 streaming:约 2.5s 内开始轮询;流就绪后数秒内出现画面,不再干等 ~15s 心跳
- playUrl 到期前约 30s 自动换新地址,播放不中断或短暂重建后恢复
- 人为弄坏/过期 URL 后,最多自动重建 3 次
- 「停止直播」调
/stop;中心关闭只 leave,不 stop - 切设备 / 关面板:销毁 hls、清定时器,无残留请求风暴
fake://行为与现网一致npm run build通过
风险
- 后端
GetLiveSession返回解包后的LiveSession本体(handlerOKWithData(session)),不是LiveSessionVO - 云端 phase 推进依赖后端 reconcile;前端轮询只能反映后端状态,不能替代设备推流
- 多标签页同时观看:停止直播会影响所有观看者——必须确认框
- hls.js:LivePlayer 内 动态 import,避免拖大非监控首屏 chunk
文件清单
| 文件 | 变更 |
|---|---|
package.json / lock |
加 hls.js |
src/api/live.js |
getLiveSession / stopLive |
src/config/urls.js |
对应常量 |
src/components/LivePlayer.vue |
HLS 适配 + 销毁 |
src/composables/useMonitorLive.js |
轮询 / 续期 / 重试 / stop |
src/components/MonitorDetailPanel.vue |
停止按钮 |
src/views/MonitorView/MonitorView.vue |
接线 stop / live-error |