hq-video-player
v0.4.5
Published
Multi-channel monitor-wall player with structured error codes, deterministic reconnect jitter and ZLMediaKit WebRTC + WS-FLV/MSE pipelines.
Maintainers
Readme
hq-video-player(中文)
面向浏览器的多通道视频播放器,支持 ZLMediaKit WebRTC、WS-FLV/HTTP-FLV、
WebCodecs 与 FFmpeg-WASM 管线。每个 Player 实例管理一路视频,多个实例共享传输和
解码 Worker。
安装
pnpm add hq-video-player包内提供 ESM、CommonJS 和 TypeScript 声明,不再导出独立样式文件,也不内置控制条或 键盘快捷键。控制 UI、快捷键和页面布局由业务侧实现。
仓库使用 pnpm workspace,固定版本的 mpegts.js fork 位于
packages/mpegts.js,构建库时会一起打包,使用方无需另外安装。
快速开始
import { Player } from 'hq-video-player';
const player = new Player({
container: '#app',
decoderBackend: 'auto',
isLive: true,
muted: true,
});
player.on('error', ({ code, message }) => console.error(code, message));
player.on('state', (state) => console.log('state:', state));
player.play('wss://zlm.example/live/cam01.live.flv');
// 页面离开或通道被移除时释放管线、媒体节点、定时器和监听器。
player.destroy();传入 URL 字符串时会自动识别 ws-flv、http-flv 或 webrtc。需要明确指定
协议、录像时间范围、音频或重连参数时,请传 SourceOptions 对象。
ZLM 录像回放时间
GB28181 录像由 ZLM 转为 FLV 后,可在数据源中传入请求的绝对时间范围:
player.play({
protocol: 'ws-flv',
url: 'wss://zlm.example/record/device-01.live.flv',
playback: {
startTime: '2026-09-24T00:20:00+08:00',
endTime: '2026-09-24T00:30:00+08:00',
},
receiveAudio: true,
});
player.on('timeUpdate', (time) => {
updateTimeline(time.currentTime, time.duration);
console.debug({
rawFlvPtsMs: time.rawFlvPtsMs,
displayTimeMs: time.displayTimeMs,
recordingTime: time.recordingTime,
});
});
player.on('rawFlvPts', ({ rawFlvPtsMs }) => {
// 每个收到的 FLV 视频包都会触发,可用于记录时间戳跳变。
console.debug('FLV packet PTS:', rawFlvPtsMs);
});各时间字段的含义不同:
| 字段 | 含义 |
| --- | --- |
| rawFlvPtsMs | 最近收到的 FLV 视频包原始 PTS,可能领先于当前显示帧;非 FLV 数据源为 null。 |
| displayTimeMs | 当前实际显示帧的媒体时间,回放 UI 应优先使用该字段。 |
| currentTime / currentTimeMs | 相对第一帧显示画面的播放位置,单位分别为秒/毫秒。 |
| recordingTimeMs / recordingTime | playback.startTime + currentTime;只有传入绝对开始时间时才存在。 |
| duration / durationMs | 请求时间范围的时长;只有同时传入起止时间时才存在。 |
FLV PTS 通常从 0 开始,本身不包含 SIP/SDP 的绝对时间。如果请求从 00:20:00
开始,但设备第一帧实际是 00:20:30,浏览器无法只根据 PTS 推出缺失的 30 秒。
业务侧必须用服务端或设备提供的可信元数据修正时间锚点。rawFlvPts 可以发现接收后的
PTS 回退或跳变,但无法还原第一包之前未知的时间差。
数据源
interface SourceOptions {
protocol: 'ws-flv' | 'http-flv' | 'ws-h264' | 'ws-h265' | 'webrtc';
url: string;
playback?: { startTime?: number | string; endTime?: number | string };
receiveAudio?: boolean;
autoReconnect?: boolean;
maxReconnectAttempts?: number;
reconnectDelayMs?: number;
headers?: Record<string, string>;
// 仅 WebRTC
signaling?: 'zlm-sdk' | 'whep';
preferredCodec?: 'h264' | 'h265';
iceServers?: Array<{ urls: string | string[]; username?: string; credential?: string }>;
iceDisconnectGraceMs?: number;
maxIceRestartAttempts?: number;
firstFrameTimeoutMs?: number;
fallback?: SourceOptions;
}webrtc 数据源始终选择 WebRTC 管线。FLV 数据源使用
decoderBackend: 'auto' 时,依次尝试 MSE、WebCodecs、FFmpeg-WASM。
WebRTC 自动回退到 WS-FLV
player.play({
protocol: 'webrtc',
url: 'webrtc://zlm.example/index/api/webrtc?app=live&stream=cam01',
signaling: 'zlm-sdk',
preferredCodec: 'h265',
firstFrameTimeoutMs: 8000,
fallback: {
protocol: 'ws-flv',
url: 'wss://zlm.example/live/cam01.live.flv',
},
});Player 配置项
src/core/PlayerOptions.ts 中的 PlayerOptions
是配置项的唯一准确定义。
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| container | string \| HTMLElement | 必填 | 挂载节点。 |
| width, height | number | 容器尺寸 | 初始渲染尺寸。 |
| fitMode | 'contain' \| 'stretch' | 'contain' | 视频适配方式。 |
| decoderBackend | 'auto' \| 'mse' \| 'webrtc' \| 'webcodecs-hw' \| 'webcodecs-sw' \| 'wasm-ffmpeg' | 'auto' | 渲染管线。 |
| decoderMode | 'auto' \| 'hardware' \| 'software' | 'auto' | WebCodecs 解码偏好。 |
| wcsUseVideoRender | boolean | false | WebCodecs 是否通过 video 元素显示。 |
| source | SourceOptions \| string | - | 初始数据源。 |
| autoplay | boolean | false | 构造后自动播放初始数据源。 |
| isLive | boolean | true | 是否按直播模式配置 MSE。 |
| videoBuffer | number | 0.2 | MSE 直播目标延迟,单位秒。 |
| muted | boolean | true | 初始静音状态。 |
| volume | number | 1 | 初始音量,范围 0 到 1。 |
| audioMode | 'auto' \| 'native' \| 'wasm' \| 'disabled' | 'auto' | 音频输出路径。 |
| heartbeat | number | 2000 | 健康检查间隔(毫秒),0 表示关闭。 |
| timeout | number | 5000 | 无新画面超时(毫秒)。 |
| rotate | number | 0 | 初始旋转角度。 |
| scale | number | 1 | 初始缩放;运行时限制在 0.1..4。 |
| decoder | { decoderUrl?, wasmUrl? } | - | FFmpeg-WASM 资源地址。 |
| debug | boolean | false | 是否输出诊断日志。 |
| zIndex | number | 100 | 播放器根节点层级。 |
Player API
play(source: string | SourceOptions): void
pause(): void
resume(): void
destroy(): void
isDestroyed(): boolean
fullscreen(): Promise<void>
quitFullscreen(): Promise<void>
isFullscreen(): boolean
screenshot(name?: string): Promise<PlayerScreenshotResult | null>
startRecord(name?: string): PlayerRecordHandle | null
stopRecord(): Promise<PlayerRecordResult | null>
setVolume(value: number): void
getVolume(): number
isMuted(): boolean
mute(): void
unmute(): void
toggleMute(): void
setRotate(degrees: number): void
getRotate(): number
setScale(value: number): void
getScale(): number
getStats(): SlotStats
getCurrentTime(): number
getPlaybackTime(): PlaybackTimeSnapshot
on<E>(event: E, listener: PlayerEventListener<E>): () => void
off<E>(event: E, listener: PlayerEventListener<E>): voidscreenshot() 和 stopRecord() 会创建可下载的对象 URL。业务侧长期保存返回值时,
不再使用后应调用 URL.revokeObjectURL()。
事件
| 事件 | Payload |
| --- | --- |
| play, pause, destroy | void |
| state | SlotStatus |
| error | { code, message, detail?, cause? } |
| stats | SlotStats & { currentSource } |
| timeUpdate | PlaybackTimeSnapshot |
| rawFlvPts | 每个 FLV 视频包触发 { rawFlvPtsMs } |
| fullscreen | { fullscreen } |
| volume | { volume, muted } |
| recordStart | PlayerRecordHandle |
| recordStop | PlayerRecordResult |
| screenshot | PlayerScreenshotResult |
| rotate | { rotate } |
| scale | { scale } |
| timeout | { code, message } |
播放器本身不会注册键盘快捷键。仓库中的演示页面自行安装了 keydown 监听器,用来展示
业务侧可以如何接入。
多路播放
每个通道创建一个实例,移除通道时销毁对应实例:
const players = sources.map((source, index) => {
const cell = document.querySelector(`#cell-${index}`)!;
const player = new Player({ container: cell, source, autoplay: true });
player.on('error', ({ code }) => reportChannelError(index, code));
return player;
});
players.forEach((player) => player.destroy());多个实例共享 Worker Hub,但每个实例拥有独立的状态、事件、媒体节点和生命周期。
已移除的兼容 API
当前版本不再提供旧的静态 hqVideoPlayer API、useMSE、useWCS、水印配置和方法、
showBandwidth、getSource() 或 getBackend()。请分别使用 Player、
decoderBackend、业务侧叠加层和 getStats()。
开发
pnpm install
pnpm run dev
pnpm run typecheck
pnpm run lint
pnpm run build
pnpm run build:package发布流程见 PUBLISHING.md,版本记录见 CHANGELOG.md。
