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

mn-video-player-v2

v1.5.6

Published

MN Video Player for WebSocket streaming

Readme

MnVideoPlayer Multi 多路视频播放器

概述

MnVideoPlayerMulti 在单个容器内管理多路实时/回放视频:统一控制(播放/暂停/倍速/音频)、多路同步播放、布局切换、窗口拖拽交换、窗口全屏、顶部通道信息栏,且每个窗口自带完整控制条(播放、码流切换、倍速、截屏、录屏、关闭、全屏等)。

import { MnVideoPlayerMulti } from './src/index'        // 源码(开发调试)
// import { MnVideoPlayerMulti } from 'mn-video-player-v2'  // 或发布产物

const multi = new MnVideoPlayerMulti()
multi.init({
  el: document.getElementById('multiPlayer'),
  layout: 4,
  urls: [
    { channelNo: 1, streamType: 1, channelInfoCustom: '前门摄像头' },
    { channelNo: 4, streamType: 1 },
  ],
  playerOptions: {
    host: '172.16.0.144',
    port: 16202,
    ws: 'ws',
    userId: '1',
    tenantId: '1',
    accessToken: 'your-access-token',
    phone: '260300380002',
    renderType: 'wasm',
    beginTime: '',            // 回放时间(YYYY-MM-DD HH:mm:ss)
    streamType: 1,
  },
})
multi.playAll()

Vite 项目中使用发布产物时,建议排除依赖预加载,避免 Worker 资源获取失败:

// vite.config.js
optimizeDeps: { exclude: ['mn-video-player-v2'] }

init(options) 配置项

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | el | HTMLElement | - | 多路容器元素(必填) | | num | number | urls 长度,缺省 1 | 初始路数 | | layout | number | 4 | 初始布局,取值 1/4/6/8/9/16/24/36 | | urls | (string | object)[] | [] | 每一路的配置(见「每路配置」) | | syncMode | boolean | false | 单路音频模式:仅允许一路发声(一路开启音频时其余路自动静音) | | unifiedTimer | boolean | true | 是否使用统一时钟(syncMode=true 时多路共用一个时钟调度播放节奏);置 false 各路用自身时钟 | | strictSync | boolean | false | 严格同步(需 unifiedTimer=true):任一通道断流无缓存且落后超过 syncThreshold 时暂停所有通道等待恢复 | | syncThreshold | number | 100 | 严重不同步判定阈值(ms),运行时可用 setSyncThreshold(ms) 动态修改 | | showToolbar | boolean | false | 是否显示顶部工具条(布局切换/统一控制) | | fullscreenMode | 'window' | 'browser' | 'window' | 全屏方式:'window' 窗口全屏(该窗口占满整个网格,双击/按钮均走窗口全屏);'browser' 网页全屏(requestFullscreen) | | playerOptions | object | - | 公共播放器参数,与每路配置合并后传给各子播放器(见下) | | controlCallback | (from, type) => boolean | void | - | 通道控制监听:用户操作某一路控制条时触发;返回 false 可阻止该操作后续的联动/默认行为 |

playerOptions(公共子播放器参数)

与每路配置合并后传给每个窗口的播放器。常用字段:

| 参数 | 类型 | 必填 | 说明 | |------|------|--------|------| | host / port / ws | string / number / string | 是 | WebSocket 服务器地址与协议(ws/wss) | | userId / tenantId / accessToken | string | 是 | 平台鉴权信息 | | phone | string | 是 | 设备编码 | | lang | string | 否 | 界面语言(如 'zh') | | renderType | string | 否 | 解码方式:'wasm' / 'decode'(VideoDecoder),回放建议 decode | | beginTime | string | 否 | 统一回放时间(YYYY-MM-DD HH:mm:ss);某路可用 beginTime 单独覆盖 | | streamType | number | 否 | 统一码流类型(实时:0=主码流/1=子码流;回放:1=主码流/2=子码流);某路可用 streamType 单独覆盖 | | debug | boolean | 否 | 是否显示每路调试信息悬浮层(默认 false) | | showControlBar | boolean | 否 | 是否显示每路内置控制条(默认 true) | | controlItems | string[] | 否 | 控制条按钮白名单(见下),空数组/不配置表示全部显示 | | showChannelInfo | boolean | 否 | 是否显示顶部通道信息栏(默认 false) | | channelInfoTemplate / channelInfoCustom | string | 否 | 通道信息栏模板与自定义内容(见下) | | channelInfoPosition | 'top-left' | 'bottom-right' | 否 | 通道信息栏位置(默认 'top-left') | | onlineWorker | object | 否 | 在线解码 Worker 资源(见「特殊场景」) | | url | string | 否 | 直连播放地址,优先于 host/port/phone/channelNo 拼接 |

控制条按钮(controlItems)

| 值 | 按钮 | 说明 | |----|------|------| | play | ▶/⏸ | 播放/暂停 | | audio | 🔊/🔇 | 音频开关 | | speed | 倍速 | 回放时显示(- / 倍速 / +) | | refresh | ↻ | 刷新回放(回放时显示) | | stream | FHD/HD/SD | 码流切换(实时 HD/SD,回放 FHD/HD/SD) | | snapshot | ▲ | 截屏:当前画面导出 PNG 下载 | | record | ⏺/⏹ | 录屏:开始/停止(浏览器支持时输出 mp4(H.264+AAC),否则回退 webm),录制中右上角红点"REC 录制中" | | close | ✕ | 关闭该路:停止播放并清为空窗口 | | fullscreen | ⛶/▣ | 全屏(网页全屏或窗口全屏,随 fullscreenMode) |

顶部通道信息栏

开启 showChannelInfo: true 后,窗口内显示通道信息栏,内容由 channelInfoTemplate 模板定制:

| 占位符 | 含义 | |--------|------| | $custom | 自定义内容(摄像头名称等,来自 channelInfoCustom 或每路配置,未提供为空) | | $code | 设备号 | | $chn | 通道号 | | $net | 网速(KB/s,播放中动态刷新) |

playerOptions: {
  showChannelInfo: true,
  channelInfoTemplate: '$custom $code CHN:$chn $net',
}
// 每路也可单独覆盖:urls: [{ channelNo: 1, channelInfoCustom: '前门摄像头' }]

每路配置(urls 元素)

每路可为以下任一种:

  • 地址字符串'ws://host:port/mndp/stream/xxx'
  • 对象 { url }:同上
  • 结构化参数对象{ url?, phone?, channelNo?, streamType?, beginTime?, channelInfoCustom?, ... },其中 url 可选;未给 url 时由公共 host/port/phone/channelNo 拼接

未配置(自动补齐的空窗口)显示虚线占位;appendChns 追加时会优先填充最早的空窗口。

布局与通道上限

  • 布局取值 1/4/6/8/9/16/24/366/8 为异形布局(1 大窗 + 其余小窗)。
  • setLayout(路数) 自动取不小于输入值的最近布局(如 10 → 16);setNum/setChns/appendChns 也会自动匹配布局路数。
  • 上限 36 路appendChns 追加时,先填充最早空窗口 → 不足再新增 → 已满 36 时覆盖**最久未使用(LRU)**的窗口,轮流覆盖而非总覆盖同一个。
  • 超过布局路数的窗口会按 36 上限的多行网格排布。

controlCallback:通道控制监听

用户操作某一路控制条(播放/暂停/关闭/码流切换/全屏等)时触发,可用于外部状态同步、接管或拦截。

multi.init({
  // ...其他参数
  controlCallback: (from, type) => {
    console.log('通道操作', from.id, from.opts, 'type =', type)
    // 窗口被关闭后(内部已清空该路为占位窗口)读取最新通道列表同步外部缓存:
    if (type === 'close') {
      setTimeout(() => console.log(multi.getAll()), 0)
    }
    // 返回 false 可阻止该操作后续的联动处理(含当前路默认行为)
  },
})

type 取值:'play' | 'stop' | 'audio' | 'speedDec' | 'speedInc' | 'fullscreen' | 'close' | 'refresh' | 'stream0' | 'stream1' | 'stream2' | 'windowFullscreen'

常用方法

| 方法 | 说明 | |------|------| | setLayout(n) | 切换布局(自动取最近不小于 n 的布局) | | setNum(n) | 设置路数(多于当前追加空位,少于当前移除多余) | | createChns(num, opts) | 重置为 num 路(用于整批重建) | | setChns(opts) | 批量替换每路配置(截取/补齐/重建播放器) | | appendChns(opts) | 追加路及配置(填充空位 → 新增 → 达上限 LRU 覆盖) | | removeChns(index \| index[]) | 按索引删除窗口 | | destroyChns() | 清空所有通道(保留网格空位);stop() 即调用它 | | getAll() | 返回当前所有路副本 MnVideoMultiChn[] | | getHasChnNum() | 已配置(可播放)的路数,不含空占位 | | playChn(id) | 播放指定路(按该路 streamType/beginTime,缺省取全局) | | playAll() | 播放全部已配置路 | | pause() / resume() | 统一暂停/恢复 | | stop() | 停止并清理所有路(销毁统一时钟、worker 等) | | resetAll() | 停止全部并按当前各路配置整体重建(stop + setChns,用于整批重置) | | refreshAll() | 刷新所有在播通道:回放路以当前全局时间作为新 beginTime 重播,实时路重连当前码流 | | reloadChn(chn) | 重载指定路(销毁并重建该路子播放器;chngetAll() 中的元素) | | switchStreamAll(chn, type) | 切换某路码流并联动刷新所有在播路(控制条码流切换内部使用) | | changeSpeed(spd) | 统一倍速(0.5–16,所有路同步) | | resetSpeed() | 恢复 1x | | enableAudio(enable) | 统一音频开关;syncMode 下仅一路发声 | | toggleAudio() | 切换统一音频开关 | | setSyncThreshold(ms) | 动态修改严重不同步判定阈值 | | setStrictSync(on) | 动态开关严格同步 | | swapChn(aId, bId) | 交换两路窗口(拖拽换位内部使用) | | setPlaybackTime(t) | 设置统一回放时间(更新全局 beginTime,对后续播放/重建生效) | | timeCallBack(fn) | 设置时间回调(继承自单路;统一时钟模式下回调为全局同步时间) | | setSyncTime(t) | 供统一时钟写入全局同步时间(一般内部使用) |

MnVideoMultiChn 结构(来自 getAll()):

| 字段 | 说明 | |------|------| | id | 窗口自增 ID | | url / opts | 该路播放地址 / 每路覆盖参数(phone/channelNo/... 或 null) | | streamType / beginTime | 该路独立码流/回放时间(缺省回退全局) | | lastUsed | LRU 序号(appendChns 覆盖时用到) | | el / body | 窗口容器 / 播放器挂载容器 | | player | 该路子播放器(MnVideoPlayer,播放中可用) |

示例

通道管理

// 追加两个通道(自动填充空位;已满 36 时覆盖最久未使用窗口)
multi.appendChns([
  { phone: '13695962142', channelNo: 2, streamType: 1 },
  { channelNo: 3 },
])

// 删除最后一路
multi.removeChns(multi.getHasChnNum() - 1)

// 读取当前通道(如持久化到 localStorage,供下次重建 urls)
const snapshot = multi.getAll().map(c => ({
  url: c.url,
  phone: c.opts?.phone,
  channelNo: c.opts?.channelNo,
  streamType: c.streamType,
}))

同步模式(多路帧级同步)

multi.init({
  // ...其他参数
  syncMode: true,        // 统一倍速 / 单路音频
  unifiedTimer: true,    // 多路共用统一时钟,帧时间同步(< syncThreshold)
  strictSync: true,      // 某路断流无缓存且落后时暂停所有路等待
  syncThreshold: 100,    // 判定严重不同步的阈值(ms)
})

multi.setSyncThreshold(150)   // 运行时动态调整阈值
multi.setStrictSync(false)    // 或临时关闭严格同步
multi.changeSpeed(2)          // 所有路同步 2x

同步模式下任一路窗口上的播放/暂停、倍速、码流等用户操作会联动到所有路;程序化调用(playAll/changeSpeed 等)本身即面向全体。unifiedTimer: false 时各路使用自身时钟,但 syncMode 的联动/单路音频/统一倍速仍生效。

启动首帧对齐:同步播放启动时,各通道注册后标记"未出帧",出帧后标记"已出帧"。先就绪的通道出首帧前会等待最慢通道首帧到达(多因解码器初始化/等 I 帧较慢),待所有通道首帧就绪后尽量在同一时刻一起出帧,消除各路启动渲染的时间差;某通道始终无流时最多等 5 秒(对齐上限,超时强制出帧兜底),不会卡死其他路。

窗口全屏与网页全屏

multi.init({
  // ...其他参数
  fullscreenMode: 'window',   // 默认:控制条 ▣ + 双击 → 该窗口占满整个网格
  // fullscreenMode: 'browser', // 控制条 ⛶ + 双击 → 浏览器原生网页全屏
})
  • 窗口全屏退出后自动恢复原布局(含 6/8 异形)。
  • 若某路正在窗口全屏,切换布局/销毁时会自动退出全屏。

播放/暂停/恢复

multi.playAll()      // 播放全部已配置路(空占位窗口跳过)
multi.pause()        // 全部暂停
multi.resume()       // 全部恢复
multi.stop()         // 停止并清理(保留网格空位)
multi.playChn(3)     // 只播放指定 id 的路

特殊场景:file:// 直开等环境的解码 Worker(onlineWorker)

多路播放器各窗口的解码 Worker 默认与页面同源部署(相对路径加载 mn-common-c.js / mn-media-v2.js / mn-media-v2.wasm)。当插件运行在 file:// 直开、离线壳等无法按相对路径加载的环境时,可通过 playerOptions.onlineWorker 传入这三份构建产物的在线地址(须为同一版本、允许 CORS),播放器将以「内置 worker 源码 + Blob URL」方式创建解码 Worker:

playerOptions: {
  onlineWorker: {
    common: 'https://cdn.example.com/mn-common-c.js',
    media:  'https://cdn.example.com/mn-media-v2.js',
    wasm:   'https://cdn.example.com/mn-media-v2.wasm',
  },
}

说明:地址缺失或创建失败会自动回退内置加载方式;宿主环境若完全禁止创建 Worker(含 Blob),则该模式也无法生效。

注意事项

  1. 容器元素:init 时必须提供有效 DOM 元素;重建实例前先调用旧实例的 stop() 并清空容器。
  2. 联动范围:控制条上的用户操作(播放/暂停/码流切换/关闭/全屏等)在 syncMode 下联动全体;程序化 API 不重复联动。
  3. 资源清理stop()/destroyChns() 会停止所有路播放器、销毁统一时钟并释放 worker 等资源;单路关闭(✕)只清空该窗口。
  4. 网速/调试:各窗口独立统计,可通过 multi.getAll()[i].player 访问对应子播放器的 netSpeedCallback/timerCtrl 等。
  5. 录屏声音:录屏时同步录制该路播放声音;多路各窗口独立录制。

浏览器兼容性

  • Chrome 90+ / Edge 90+ / Firefox 88+ / Safari 14+
  • 需要支持:WebSocket、Canvas、Web Audio、WebAssembly(WASM 解码时)、VideoDecoder(decode 解码时)