npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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.

Readme

hq-video-player(中文)

面向浏览器的多通道视频播放器,支持 ZLMediaKit WebRTC、WS-FLV/HTTP-FLV、 WebCodecs 与 FFmpeg-WASM 管线。每个 Player 实例管理一路视频,多个实例共享传输和 解码 Worker。

English documentation

安装

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>): void

screenshot() 和 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。

License

MIT