Browse Source
Cover hls.js playback, phase polling, playUrl renewal, error rebuild, and stop-vs-leave controls for monitor live.main
1 changed files with 207 additions and 0 deletions
@ -0,0 +1,207 @@ |
|||||
|
# 监控直播 HLS 真实播放补齐 |
||||
|
|
||||
|
日期:2026-09-02 |
||||
|
状态:已确认(待实现计划) |
||||
|
|
||||
|
## 背景 |
||||
|
|
||||
|
后端直播播放地址是 HLS: |
||||
|
|
||||
|
```text |
||||
|
https://vpull.jiagutech.com/dock-live/<streamSessionId>.m3u8?auth_key=... |
||||
|
``` |
||||
|
|
||||
|
当前前端缺口: |
||||
|
|
||||
|
1. `LivePlayer.vue` 把 `.m3u8` 直接赋给原生 `<video src>`,项目无 `hls.js`。Safari 通常可播,Chrome/Edge/Firefox 通常不能。 |
||||
|
2. `useMonitorLive` 创建会话后只靠观看租约心跳刷新状态。默认租约约 30s,第一次刷新约 15s;即使流已就绪,页面也会长时间停在「启动中」。 |
||||
|
3. `GET play-url` 返回 `expiresAt`(默认约 300s),前端只在「尚无 playUrl」时拉一次;过期后播放失败,仅 toast,不换新地址。 |
||||
|
4. 播放器错误只向上抛,没有重建路径。 |
||||
|
5. 关闭按钮语义只有「退出观看」(`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` → 停止直播推流 |
||||
|
|
||||
|
## 目标 |
||||
|
|
||||
|
监控详情实时画面: |
||||
|
|
||||
|
1. Chrome/Edge 用 `hls.js` 播放 HLS;Safari / 原生可播 HLS 时走原生 `<video>`。 |
||||
|
2. join 后每 2–3 秒查询会话,直到 `streaming` / `failed` / 超时,再尽快取 playUrl。 |
||||
|
3. 记录 playUrl `expiresAt`,到期前约 30 秒自动刷新地址并重建播放器。 |
||||
|
4. HLS 网络/鉴权/解码错误时限流重试:重新拉 play-url 并重建播放器。 |
||||
|
5. 区分: |
||||
|
- **退出观看**:现有中心按钮 / `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` |
||||
|
|
||||
|
新增: |
||||
|
|
||||
|
```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` 与 hls `ERROR`(`fatal`)→ emit `error` |
||||
|
- 中心按钮语义不变:emit `toggle`(由上层解释为退出观看 / 打开) |
||||
|
|
||||
|
不在 LivePlayer 内请求 API。 |
||||
|
|
||||
|
### 4. useMonitorLive |
||||
|
|
||||
|
文件:`src/composables/useMonitorLive.js` |
||||
|
|
||||
|
状态扩展: |
||||
|
|
||||
|
```js |
||||
|
live = { |
||||
|
session, dockId, playUrl, |
||||
|
playUrlExpiresAt, // unix 秒;缺省时用拿到时刻 + 270s 兜底 |
||||
|
heartbeatTimer, |
||||
|
phasePollTimer, |
||||
|
playRefreshTimer, |
||||
|
playRetryCount, |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
对外新增:`stopLiveStream`。 |
||||
|
|
||||
|
#### 4.1 打开 |
||||
|
|
||||
|
1. `joinLive(dockId)` |
||||
|
2. 写 `session` / `dockId` |
||||
|
3. 启动观看租约心跳(逻辑保留;与状态轮询分离) |
||||
|
4. 若已 `streaming` → `refreshPlayURL()` |
||||
|
5. 否则启动 phase 轮询:每 **2.5s** `getLiveSession` |
||||
|
- 响应经 http 解包后直接是 `LiveSession`(含 `phase`/`id`),不是 `{ session }` 包装 |
||||
|
- `streaming` → 停轮询,`refreshPlayURL()` |
||||
|
- `failed` / `stopped` → toast,清本地态(不强制 stop) |
||||
|
- 超过 **60s** 仍非终态 → toast「直播启动超时」,清本地态 |
||||
|
|
||||
|
#### 4.2 playUrl 刷新 |
||||
|
|
||||
|
`refreshPlayURL()`: |
||||
|
|
||||
|
1. 调 `getLivePlayURL` |
||||
|
2. 写 `live.playUrl` |
||||
|
3. 写 `live.playUrlExpiresAt = play.expiresAt || nowSec + 270` |
||||
|
4. 调度续期:`delay = max(5000, expiresAt*1000 - Date.now() - 30000)` |
||||
|
5. 到期触发时再次 `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()`: |
||||
|
|
||||
|
1. 无 active session 直接 return |
||||
|
2. `ui.confirm` 确认(文案:停止后所有观看者失去画面) |
||||
|
3. `POST /v1/live/:dockId/stop` |
||||
|
4. 成功:清本地播放态(等同 close 本地部分);可再尝试 leave(忽略错误) |
||||
|
5. 失败:toast,保留当前播放与心跳 |
||||
|
|
||||
|
### 5. 详情面板 UI |
||||
|
|
||||
|
文件:`src/components/MonitorDetailPanel.vue` + `MonitorView` 接线 |
||||
|
|
||||
|
在「实时画面」标题行(状态文案 / 全屏旁)增加: |
||||
|
|
||||
|
- 按钮文案:`停止直播` |
||||
|
- 仅 `liveView.active` 时可见/可点 |
||||
|
- emit `stop-live` → MonitorView → `useMonitorLive.stopLiveStream` |
||||
|
|
||||
|
中心播放按钮保持 toggle:有 session 时退出观看。 |
||||
|
|
||||
|
### 6. 验收 |
||||
|
|
||||
|
1. Chrome/Edge:join → streaming 后页内可见真实 HLS 画面(非仅黑框) |
||||
|
2. Safari:原生路径可播,不强制挂 hls.js |
||||
|
3. join 后非 streaming:约 2.5s 内开始轮询;流就绪后数秒内出现画面,不再干等 ~15s 心跳 |
||||
|
4. playUrl 到期前约 30s 自动换新地址,播放不中断或短暂重建后恢复 |
||||
|
5. 人为弄坏/过期 URL 后,最多自动重建 3 次 |
||||
|
6. 「停止直播」调 `/stop`;中心关闭只 leave,不 stop |
||||
|
7. 切设备 / 关面板:销毁 hls、清定时器,无残留请求风暴 |
||||
|
8. `fake://` 行为与现网一致 |
||||
|
9. `npm run build` 通过 |
||||
|
|
||||
|
## 风险 |
||||
|
|
||||
|
- 后端 `GetLiveSession` 返回解包后的 `LiveSession` 本体(handler `OKWithData(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 | |
||||
Loading…
Reference in new issue