mn-video-player-v2 1.5.3 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/readme.md +207 -509
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mn-video-player-v2",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "MN Video Player for WebSocket streaming",
5
5
  "main": "./dist/mn-video-player-v2.umd.js",
6
6
  "module": "./dist/mn-video-player-v2.es.js",
package/readme.md CHANGED
@@ -1,582 +1,280 @@
1
- # MnVideoPlayer API 文档
1
+ # MnVideoPlayer Multi 多路视频播放器
2
2
 
3
3
  ## 概述
4
4
 
5
- MnVideoPlayer 是一个基于 WebSocket 的视频播放器,支持实时播放、历史回放、对讲等功能。
6
-
7
- 在项目中使用时,需要排除 `mn-video-player-v2` 模块的预加载,以避免与 Worker文件资源获取失败。
5
+ `MnVideoPlayerMulti` 在单个容器内管理**多路实时/回放视频**:统一控制(播放/暂停/倍速/音频)、多路同步播放、布局切换、窗口拖拽交换、窗口全屏、顶部通道信息栏,且每个窗口自带完整控制条(播放、码流切换、倍速、截屏、录屏、关闭、全屏等)。
8
6
 
9
7
  ```javascript
10
- // 示例:Vue 项目配置
11
- module.exports = {
12
- // ... 其他配置
13
- optimizeDeps: {
14
- exclude: ['mn-video-player-v2']
15
- },
16
- };
17
- ```
18
-
19
- ## 初始化
20
-
21
- ### init(options)
22
-
23
- 初始化播放器实例。
24
-
25
- **参数:**
8
+ import { MnVideoPlayerMulti } from './src/index' // 源码(开发调试)
9
+ // import { MnVideoPlayerMulti } from 'mn-video-player-v2' // 或发布产物
26
10
 
27
- | 参数 | 类型 | 必填 | 说明 |
28
- |------|------|--------|------|
29
- | el | HTMLElement | 是 | 容器元素,用于显示视频 |
30
- | host | string | 是 | 主机地址 |
31
- | port | number | 是 | 端口号 |
32
- | ws | string | 是 | WebSocket 协议(ws 或 wss) |
33
- | userId | string | 是 | 用户 ID |
34
- | tenantId | string | 是 | 租户 ID |
35
- | accessToken | string | 是 | 访问令牌 |
36
- | phone | string | 是 | 车辆编码 |
37
- | channelNo | number | 是 | 通道号 |
38
- | beginTime | string | 否 | 开始时间(历史回放时使用) 格式为 'YYYY-MM-DD HH:mm:ss' |
39
- | streamType | number | 否 | 码流类型 实时:(0=主码流,1=子码流) 回放:(1=主码流,2=子码流) |
40
- | renderType | string | 否 | 渲染类型(wasm 或 decode) 使用wasm解码 回放时建议使用decode |
41
- | showControlBar | boolean | 否 | 是否显示内置控制栏(默认 true) |
42
- | debug | boolean | 否 | 是否显示调试信息悬浮层(时间戳、fps、延迟、网速等,每 1 秒刷新,默认 false) |
43
- | url | string | 否 | 直连播放地址(如 ws://host:port/mndp/stream/xxx),设置后优先于 host/port/phone/channelNo 拼接 |
44
- | showChannelInfo | boolean | 否 | 是否显示顶部通道信息栏(通道号/网速/设备号,默认 false) |
45
- | channelInfoTemplate | string | 否 | 顶部信息栏内容模板,支持占位符 `$custom`(自定义内容)/ `$code`(设备号)/ `$chn`(通道号)/ `$net`(网速,动态刷新),其余字符原样显示;未配置默认 `设备:$code CHN:$chn 网速:$net` |
46
- | channelInfoCustom | string | 否 | 模板中 `$custom` 占位符的内容(如摄像头名称),由使用者提供,未提供显示为空 |
47
- | controlItems | MnVideoPlayerControlItem[] | 否 | 控制条功能项白名单,未配置或空数组时全部显示。可选值:`play` 播放/暂停、`audio` 音频、`speed` 倍速(回放)、`refresh` 刷新(回放)、`stream` 码流切换、`snapshot` 截屏、`record` 录屏、`close` 关闭、`fullscreen` 全屏(网页或窗口全屏) |
48
- | enableDoubleClickFullscreen | boolean | 否 | 是否启用双击全屏(默认 true) |
49
- | controlCallbackFn | (type: MnVideoPlayerControlType) => boolean | 否 | 控制回调函数,返回 false 则阻止默认行为 ,返回 true 则执行默认行为 MnVideoPlayerControlType 枚举值 : refresh 刷新 close 关闭 fullscreen 全屏/退出全屏 stream 切换码流 speedDec 降低倍速 speedInc 增加倍速 |
50
-
51
- **示例:**
52
-
53
- ```javascript
54
- const player = new MnVideoPlayer();
55
- player.init({
56
- el: document.getElementById('player'),
57
- host: '172.16.0.150',
11
+ const multi = new MnVideoPlayerMulti()
12
+ multi.init({
13
+ el: document.getElementById('multiPlayer'),
14
+ layout: 4,
15
+ urls: [
16
+ { channelNo: 1, streamType: 1, channelInfoCustom: '前门摄像头' },
17
+ { channelNo: 4, streamType: 1 },
18
+ ],
19
+ playerOptions: {
20
+ host: '172.16.0.144',
58
21
  port: 16202,
59
22
  ws: 'ws',
60
23
  userId: '1',
61
24
  tenantId: '1',
62
25
  accessToken: 'your-access-token',
63
- phone: '13695962147',
64
- channelNo: 5,
65
- beginTime: '2026-01-14 13:30:00',
26
+ phone: '260300380002',
27
+ renderType: 'wasm',
28
+ beginTime: '', // 回放时间(YYYY-MM-DD HH:mm:ss)
66
29
  streamType: 1,
67
- showControlBar:true,
68
- renderType:'wasm' // 使用wasm解码 回放时建议使用decode
69
- });
30
+ },
31
+ })
32
+ multi.playAll()
70
33
  ```
71
34
 
72
- ## 播放控制
73
-
74
- ### play(data)
35
+ > Vite 项目中使用发布产物时,建议排除依赖预加载,避免 Worker 资源获取失败:
36
+ >
37
+ > ```javascript
38
+ > // vite.config.js
39
+ > optimizeDeps: { exclude: ['mn-video-player-v2'] }
40
+ > ```
75
41
 
76
- 开始播放视频。
42
+ ## init(options) 配置项
77
43
 
78
- **参数:**
79
-
80
- | 参数 | 类型 | 必填 | 说明 |
44
+ | 参数 | 类型 | 默认值 | 说明 |
81
45
  |------|------|--------|------|
82
- | streamType | number | 否 | 码流类型(0=主码流,1=子码流) |
83
- | beginTime | string | 否 | 开始时间(历史回放时使用) |
84
- **示例:**
85
-
86
- ```javascript
87
- // 实时播放
88
- player.play({
89
- beginTime: ''
90
- });
91
-
92
- // 历史回放
93
- player.play({
94
- streamType: 0,
95
- beginTime: '2026-01-14 13:30:00'
96
- });
97
- ```
98
-
99
- ### pause()
100
-
101
- 暂停播放。
102
-
103
- **参数:** 无
104
-
105
- **示例:**
106
-
107
- ```javascript
108
- player.pause();
109
- ```
110
-
111
- ### resume()
112
-
113
- 恢复播放。
114
-
115
- **参数:** 无
116
-
117
- **说明:** 用于在暂停后恢复视频播放。
118
-
119
- **示例:**
120
-
121
- ```javascript
122
- // 恢复播放
123
- player.resume();
124
- ```
125
-
126
- ### stop()
127
-
128
- 停止播放。
129
-
130
- **参数:** 无
131
-
132
- **示例:**
133
-
134
- ```javascript
135
- player.stop();
136
- ```
137
-
138
-
139
- ### changeSpeed(number)
140
-
141
- 倍速播放 0.5-16。
142
-
143
- **参数:** number 数字
144
-
145
- **示例:**
146
-
147
- ```javascript
148
- player.changeSpeed(2);
149
- ```
150
-
151
-
152
- ### switchStream(streamType)
46
+ | el | HTMLElement | - | 多路容器元素(必填) |
47
+ | num | number | urls 长度,缺省 1 | 初始路数 |
48
+ | layout | number | 4 | 初始布局,取值 `1/4/6/8/9/16/24/36` |
49
+ | urls | (string \| object)[] | [] | 每一路的配置(见「每路配置」) |
50
+ | syncMode | boolean | false | 单路音频模式:仅允许一路发声(一路开启音频时其余路自动静音) |
51
+ | unifiedTimer | boolean | true | 是否使用统一时钟(syncMode=true 时多路共用一个时钟调度播放节奏);置 false 各路用自身时钟 |
52
+ | strictSync | boolean | false | 严格同步(需 unifiedTimer=true):任一通道断流无缓存且落后超过 `syncThreshold` 时暂停所有通道等待恢复 |
53
+ | syncThreshold | number | 100 | 严重不同步判定阈值(ms),运行时可用 `setSyncThreshold(ms)` 动态修改 |
54
+ | showToolbar | boolean | false | 是否显示顶部工具条(布局切换/统一控制) |
55
+ | fullscreenMode | 'window' \| 'browser' | 'window' | 全屏方式:'window' 窗口全屏(该窗口占满整个网格,双击/按钮均走窗口全屏);'browser' 网页全屏(requestFullscreen) |
56
+ | playerOptions | object | - | 公共播放器参数,与每路配置合并后传给各子播放器(见下) |
57
+ | controlCallback | (from, type) => boolean \| void | - | 通道控制监听:用户操作某一路控制条时触发;返回 `false` 可阻止该操作后续的联动/默认行为 |
153
58
 
154
- 切换码流类型。
59
+ ## playerOptions(公共子播放器参数)
155
60
 
156
- **参数:**
61
+ 与每路配置合并后传给每个窗口的播放器。常用字段:
157
62
 
158
63
  | 参数 | 类型 | 必填 | 说明 |
159
64
  |------|------|--------|------|
160
- | streamType | number | 是 | 码流类型 实时:(0=主码流,1=子码流) 回放:(1=主码流,2=子码流) |
65
+ | host / port / ws | string / number / string | 是 | WebSocket 服务器地址与协议(ws/wss) |
66
+ | userId / tenantId / accessToken | string | 是 | 平台鉴权信息 |
67
+ | phone | string | 是 | 设备编码 |
68
+ | lang | string | 否 | 界面语言(如 'zh') |
69
+ | renderType | string | 否 | 解码方式:'wasm' / 'decode'(VideoDecoder),回放建议 decode |
70
+ | beginTime | string | 否 | 统一回放时间(`YYYY-MM-DD HH:mm:ss`);某路可用 `beginTime` 单独覆盖 |
71
+ | streamType | number | 否 | 统一码流类型(实时:0=主码流/1=子码流;回放:1=主码流/2=子码流);某路可用 `streamType` 单独覆盖 |
72
+ | debug | boolean | 否 | 是否显示每路调试信息悬浮层(默认 false) |
73
+ | showControlBar | boolean | 否 | 是否显示每路内置控制条(默认 true) |
74
+ | controlItems | string[] | 否 | 控制条按钮白名单(见下),空数组/不配置表示全部显示 |
75
+ | showChannelInfo | boolean | 否 | 是否显示顶部通道信息栏(默认 false) |
76
+ | channelInfoTemplate / channelInfoCustom | string | 否 | 通道信息栏模板与自定义内容(见下) |
77
+ | channelInfoPosition | 'top-left' \| 'bottom-right' | 否 | 通道信息栏位置(默认 'top-left') |
78
+ | onlineWorker | object | 否 | 在线解码 Worker 资源(见「特殊场景」) |
79
+ | url | string | 否 | 直连播放地址,优先于 host/port/phone/channelNo 拼接 |
80
+
81
+ ### 控制条按钮(controlItems)
82
+
83
+ | 值 | 按钮 | 说明 |
84
+ |----|------|------|
85
+ | play | ▶/⏸ | 播放/暂停 |
86
+ | audio | 🔊/🔇 | 音频开关 |
87
+ | speed | 倍速 | 回放时显示(- / 倍速 / +) |
88
+ | refresh | ↻ | 刷新回放(回放时显示) |
89
+ | stream | FHD/HD/SD | 码流切换(实时 HD/SD,回放 FHD/HD/SD) |
90
+ | snapshot | ▲ | 截屏:当前画面导出 PNG 下载 |
91
+ | record | ⏺/⏹ | 录屏:开始/停止(浏览器支持时输出 mp4(H.264+AAC),否则回退 webm),录制中右上角红点"REC 录制中" |
92
+ | close | ✕ | 关闭该路:停止播放并清为空窗口 |
93
+ | fullscreen | ⛶/▣ | 全屏(网页全屏或窗口全屏,随 fullscreenMode) |
94
+
95
+ ### 顶部通道信息栏
96
+
97
+ 开启 `showChannelInfo: true` 后,窗口内显示通道信息栏,内容由 `channelInfoTemplate` 模板定制:
161
98
 
162
- **示例:**
99
+ | 占位符 | 含义 |
100
+ |--------|------|
101
+ | `$custom` | 自定义内容(摄像头名称等,来自 `channelInfoCustom` 或每路配置,未提供为空) |
102
+ | `$code` | 设备号 |
103
+ | `$chn` | 通道号 |
104
+ | `$net` | 网速(KB/s,播放中动态刷新) |
163
105
 
164
106
  ```javascript
165
- // 切换到主码流
166
- player.switchStream(0);
167
-
168
- // 切换到子码流
169
- player.switchStream(1);
170
-
171
- // 切换码流(使用变量)
172
- let streamType = 1;
173
- player.switchStream(streamType);
174
- streamType = streamType === 1 ? 0 : 1;
107
+ playerOptions: {
108
+ showChannelInfo: true,
109
+ channelInfoTemplate: '$custom $code CHN:$chn $net',
110
+ }
111
+ // 每路也可单独覆盖:urls: [{ channelNo: 1, channelInfoCustom: '前门摄像头' }]
175
112
  ```
176
- ### controlCallback(callback)
177
113
 
178
- 设置控制栏按钮点击回调,返回 `false` 可阻止默认行为。
114
+ ## 每路配置(urls 元素)
179
115
 
180
- **参数:**
181
-
182
- | 参数 | 类型 | 必填 | 说明 |
183
- |------|------|--------|------|
184
- | callback | function | 是 | 回调函数,参数为操作类型 `'play' \| 'stop' \| 'audio' \| 'speedDec' \| 'speedInc' \| 'refresh' \| 'close' \| 'fullscreen'`,返回 `true` 继续默认行为,`false` 阻止 |
185
-
186
- **示例:**
187
-
188
- ```javascript
189
- player.controlCallback((type) => {
190
- console.log('控制操作:', type);
191
- return true;
192
- });
193
- ```
116
+ 每路可为以下任一种:
194
117
 
195
- ## 内置控制栏
118
+ - **地址字符串**:`'ws://host:port/mndp/stream/xxx'`
119
+ - **对象 `{ url }`**:同上
120
+ - **结构化参数对象**:`{ url?, phone?, channelNo?, streamType?, beginTime?, channelInfoCustom?, ... }`,其中 `url` 可选;未给 `url` 时由公共 `host/port/phone/channelNo` 拼接
196
121
 
197
- 播放器内置控制栏,鼠标移入视频区域时显示,位于视频底部,高度 20px。
122
+ 未配置(自动补齐的空窗口)显示虚线占位;`appendChns` 追加时会优先填充最早的空窗口。
198
123
 
199
- 从左到右按钮依次:
124
+ ## 布局与通道上限
200
125
 
201
- | 按钮 | 图标 | 说明 |
202
- |------|------|------|
203
- | 播放/暂停 | ▶ / ⏸ | 切换播放状态 |
204
- | 音频开关 | 🔊 / 🔇 | 切换音频开/关 |
205
- | 降低倍速 | < | 降低播放倍速(回放时显示) |
206
- | 当前倍速 | 0.5x ~ 32x | 显示当前倍速(回放时显示) |
207
- | 提高倍速 | > | 提高播放倍速(回放时显示) |
208
- | 刷新 | ↻ | 重新加载回放(回放时显示) |
209
- | 码流切换 | FHD/HD/SD | 切换主/子码流(实时显示 HD/SD,回放显示 FHD/HD/SD) |
210
- | 截屏 | ▲ | 当前画面导出 PNG 下载 |
211
- | 录屏 | ⏺ / ⏹ | 开始/停止录制(webm 或 mp4),录制中右上角显示红点"REC 录制中" |
212
- | 关闭 | ✕ | 停止播放并关闭 |
213
- | 全屏 | ⛶ / ▣ | 网页全屏(⛶)或窗口全屏(▣,多路模式默认) |
126
+ - 布局取值 `1/4/6/8/9/16/24/36`;`6/8` 为异形布局(1 大窗 + 其余小窗)。
127
+ - `setLayout(路数)` 自动取**不小于**输入值的最近布局(如 10 → 16);`setNum`/`setChns`/`appendChns` 也会自动匹配布局路数。
128
+ - **上限 36 路**:`appendChns` 追加时,先填充最早空窗口 → 不足再新增 → 已满 36 时覆盖**最久未使用(LRU)**的窗口,轮流覆盖而非总覆盖同一个。
129
+ - 超过布局路数的窗口会按 36 上限的多行网格排布。
214
130
 
215
- 可通过 `showControlBar: false` 禁用内置控制栏。默认支持双击视频全屏,可通过 `player.enableDoubleClickFullscreen = false` 关闭。
131
+ ## controlCallback:通道控制监听
216
132
 
217
- 按钮组可通过 `controlItems` 配置裁剪,例如只显示播放/码流/录屏/全屏:
133
+ 用户操作某一路控制条(播放/暂停/关闭/码流切换/全屏等)时触发,可用于外部状态同步、接管或拦截。
218
134
 
219
135
  ```javascript
220
- player.init({
136
+ multi.init({
221
137
  // ...其他参数
222
- controlItems: ['play', 'stream', 'record', 'fullscreen']
223
- });
224
- ```
225
-
226
- ### 截屏与录屏
227
-
228
- - **截屏**:点击截屏按钮将当前画面导出为 PNG 并下载,无需额外配置。
229
- - **录屏**:点击录屏按钮开始录制(画面 + 播放声音),再次点击停止并下载视频文件。浏览器支持 mp4(H.264+AAC)时优先输出 mp4,否则回退 webm(vp9/vp8)。
230
- - 录制过程中右上角显示红点"REC 录制中"提示,控制栏录制按钮变 ⏹ 高亮。
231
- - 播放器停止(`stop()`)时若仍在录制,会保存已录制的部分片段。
232
- - 多路模式下各窗口独立录制,互不影响。
233
-
234
- ### enableAudio(enable)
235
-
236
- 开启或关闭音频。
237
-
238
- **参数:**
239
-
240
- | 参数 | 类型 | 必填 | 说明 |
241
- |------|------|--------|------|
242
- | enable | boolean | 是 | true=开启音频,false=关闭音频 |
243
-
244
- **示例:**
245
-
246
- ```javascript
247
- // 开启音频
248
- player.enableAudio(true);
249
-
250
- // 关闭音频
251
- player.enableAudio(false);
252
-
253
- // 切换音频状态(使用变量)
254
- let audioEnabled = false;
255
- player.enableAudio(!audioEnabled);
256
- audioEnabled = !audioEnabled;
257
- ```
258
-
259
- ### resetCanvas()
260
-
261
- 重置画布大小,使其适应容器元素的当前尺寸。
262
-
263
- **参数:** 无
264
-
265
- **说明:** 当容器元素大小改变时,调用此方法可以重新调整画布大小,确保视频显示正常。
266
-
267
- **示例:**
268
-
269
- ```javascript
270
- // 重置画布大小
271
- player.resetCanvas();
272
-
273
- // 窗口大小改变时重置画布
274
- window.addEventListener('resize', () => {
275
- player.resetCanvas();
276
- });
277
- ```
278
-
279
- ### timeCallBack(callback)
280
-
281
- 设置视频时间回调函数。
282
-
283
- **参数:**
284
-
285
- | 参数 | 类型 | 必填 | 说明 |
286
- |------|------|--------|------|
287
- | callback | function | 是 | 时间回调函数,参数为视频时间(毫秒) |
288
-
289
- **示例:**
290
-
291
- ```javascript
292
- player.timeCallBack((time) => {
293
- console.log('当前视频时间:', time);
294
- });
295
- ```
296
-
297
- ### netSpeedCallback 属性
298
-
299
- 设置网速回调函数,播放时周期性收到网速数据。与 `timeCallBack` 不同,该回调直接赋值给 player 实例。
300
-
301
- **参数:**
302
-
303
- | 参数 | 类型 | 说明 |
304
- |------|------|------|
305
- | kbPerSec | number | 网速(KB/s) |
306
- | frame | object | 原始网速帧对象,含 `d`(本周期字节数)、`len`(缓存帧数)等 |
307
-
308
- **示例:**
309
-
310
- ```javascript
311
- player.netSpeedCallback = (kbPerSec, frame) => {
312
- console.log('当前网速:', kbPerSec, 'KB/s');
313
- console.log('缓存帧数:', frame.len);
314
- };
315
- ```
316
-
317
- ## 调试功能
318
-
319
- ### debug 选项
320
-
321
- 初始化时设置 `debug: true`,会在视频左上角悬浮显示实时调试信息(每 1 秒刷新):
322
-
323
- - **时间**:当前播放时间戳
324
- - **fps**:实际出帧率
325
- - **延迟**:帧输出相对应播时刻的平均/最大滞后(ms),流畅时接近 0
326
- - **网速**:KB/s 及缓存帧数
327
- - **帧数 / 缓存队列**:本秒输出帧数与播放队列排队帧数
328
- - **模式**:wasm / VideoDecoder
329
-
330
- **示例:**
331
-
332
- ```javascript
333
- player.init({
334
- // ...其他参数
335
- debug: true
336
- });
138
+ controlCallback: (from, type) => {
139
+ console.log('通道操作', from.id, from.opts, 'type =', type)
140
+ // 窗口被关闭后(内部已清空该路为占位窗口)读取最新通道列表同步外部缓存:
141
+ if (type === 'close') {
142
+ setTimeout(() => console.log(multi.getAll()), 0)
143
+ }
144
+ // 返回 false 可阻止该操作后续的联动处理(含当前路默认行为)
145
+ },
146
+ })
337
147
  ```
338
148
 
339
- 如需自行展示,也可通过 `player.timerCtrl.perfSnapshot()` 获取 fps/延迟统计(返回 `{ fps, avgLatency, maxLatency, frames, cache }`),取完后调用 `player.timerCtrl.perfReset()` 重置统计窗口。
149
+ `type` 取值:`'play' | 'stop' | 'audio' | 'speedDec' | 'speedInc' | 'fullscreen' | 'close' | 'refresh' | 'stream0' | 'stream1' | 'stream2' | 'windowFullscreen'`
340
150
 
341
- ## 缓存流控(TimerCtrl)
151
+ ## 常用方法
342
152
 
343
- `timerCtrl` 维护播放缓存队列(`timeList`),当缓存帧数超过上限时会通知 worker **暂停解析**,低于下限时通知 worker **恢复解析**,保证播放流畅的同时控制缓存空间。阈值均为配置项:
153
+ | 方法 | 说明 |
154
+ |------|------|
155
+ | `setLayout(n)` | 切换布局(自动取最近不小于 n 的布局) |
156
+ | `setNum(n)` | 设置路数(多于当前追加空位,少于当前移除多余) |
157
+ | `createChns(num, opts)` | 重置为 num 路(用于整批重建) |
158
+ | `setChns(opts)` | 批量替换每路配置(截取/补齐/重建播放器) |
159
+ | `appendChns(opts)` | 追加路及配置(填充空位 → 新增 → 达上限 LRU 覆盖) |
160
+ | `removeChns(index \| index[])` | 按索引删除窗口 |
161
+ | `destroyChns()` | 清空所有通道(保留网格空位);`stop()` 即调用它 |
162
+ | `getAll()` | 返回当前所有路副本 `MnVideoMultiChn[]` |
163
+ | `getHasChnNum()` | 已配置(可播放)的路数,不含空占位 |
164
+ | `playChn(id)` | 播放指定路(按该路 streamType/beginTime,缺省取全局) |
165
+ | `playAll()` | 播放全部已配置路 |
166
+ | `pause()` / `resume()` | 统一暂停/恢复 |
167
+ | `stop()` | 停止并清理所有路(销毁统一时钟、worker 等) |
168
+ | `changeSpeed(spd)` | 统一倍速(0.5–16,所有路同步) |
169
+ | `resetSpeed()` | 恢复 1x |
170
+ | `enableAudio(enable)` | 统一音频开关;syncMode 下仅一路发声 |
171
+ | `toggleAudio()` | 切换统一音频开关 |
172
+ | `setSyncThreshold(ms)` | 动态修改严重不同步判定阈值 |
173
+ | `setStrictSync(on)` | 动态开关严格同步 |
174
+ | `swapChn(aId, bId)` | 交换两路窗口(拖拽换位内部使用) |
175
+ | `timeCallBack(fn)` | 设置时间回调(继承自单路;统一时钟模式下回调为全局同步时间) |
176
+ | `setSyncTime(t)` | 供统一时钟写入全局同步时间(一般内部使用) |
177
+
178
+ `MnVideoMultiChn` 结构(来自 `getAll()`):
179
+
180
+ | 字段 | 说明 |
181
+ |------|------|
182
+ | id | 窗口自增 ID |
183
+ | url / opts | 该路播放地址 / 每路覆盖参数(phone/channelNo/... 或 null) |
184
+ | streamType / beginTime | 该路独立码流/回放时间(缺省回退全局) |
185
+ | lastUsed | LRU 序号(appendChns 覆盖时用到) |
186
+ | el / body | 窗口容器 / 播放器挂载容器 |
187
+ | player | 该路子播放器(`MnVideoPlayer`,播放中可用) |
344
188
 
345
- - **maxCache**(默认 `60`):缓存帧数上限,超过则暂停 worker 解码
346
- - **minCache**(默认 `10`):缓存帧数下限,低于则恢复 worker 解码
189
+ ## 示例
347
190
 
348
- 可通过 `player.timerCtrl` 直接调整(或修改 `TimerCtrl` 构造参数 `{ maxCache, minCache, onCacheChange }`):
191
+ ### 通道管理
349
192
 
350
193
  ```javascript
351
- player.timerCtrl.maxCache = 90; // 提高缓存上限
352
- player.timerCtrl.minCache = 20; // 提高恢复阈值
194
+ // 追加两个通道(自动填充空位;已满 36 时覆盖最久未使用窗口)
195
+ multi.appendChns([
196
+ { phone: '13695962142', channelNo: 2, streamType: 1 },
197
+ { channelNo: 3 },
198
+ ])
199
+
200
+ // 删除最后一路
201
+ multi.removeChns(multi.getHasChnNum() - 1)
202
+
203
+ // 读取当前通道(如持久化到 localStorage,供下次重建 urls)
204
+ const snapshot = multi.getAll().map(c => ({
205
+ url: c.url,
206
+ phone: c.opts?.phone,
207
+ channelNo: c.opts?.channelNo,
208
+ streamType: c.streamType,
209
+ }))
353
210
  ```
354
211
 
355
- 销毁(`player.stop()` / `stop()`)时 `TimerCtrl` 会清空全部缓存队列以释放内存。
356
-
357
- ## 顶部通道信息栏
358
-
359
- 开启 `showChannelInfo: true` 后,在视频顶部显示通道信息栏(默认内容:设备号、通道号、网速)。
360
-
361
- 内容可通过 `channelInfoTemplate` 模板定制,支持以下占位符:
362
-
363
- | 占位符 | 含义 |
364
- |--------|------|
365
- | `$custom` | 自定义内容(如摄像头名称,来自 `channelInfoCustom`,未提供显示为空) |
366
- | `$code` | 设备号 |
367
- | `$chn` | 通道号 |
368
- | `$net` | 网速(KB/s,播放中动态刷新) |
369
-
370
- 其余字符原样显示。
212
+ ### 同步模式(多路帧级同步)
371
213
 
372
214
  ```javascript
373
- player.init({
215
+ multi.init({
374
216
  // ...其他参数
375
- showChannelInfo: true,
376
- channelInfoTemplate: '$custom $code CHN:$chn $net', // 如:前门摄像头 260300380002 CHN:1 124 KB/s
377
- channelInfoCustom: '前门摄像头',
378
- });
217
+ syncMode: true, // 统一倍速 / 单路音频
218
+ unifiedTimer: true, // 多路共用统一时钟,帧时间同步(< syncThreshold)
219
+ strictSync: true, // 某路断流无缓存且落后时暂停所有路等待
220
+ syncThreshold: 100, // 判定严重不同步的阈值(ms)
221
+ })
222
+
223
+ multi.setSyncThreshold(150) // 运行时动态调整阈值
224
+ multi.setStrictSync(false) // 或临时关闭严格同步
225
+ multi.changeSpeed(2) // 所有路同步 2x
379
226
  ```
380
227
 
381
- 多路模式下可在 `playerOptions` 中统一配置,也可在每路配置(`urls` 数组元素)中单独覆盖。
228
+ > 同步模式下任一路窗口上的播放/暂停、倍速、码流等**用户操作会联动到所有路**;程序化调用(playAll/changeSpeed 等)本身即面向全体。`unifiedTimer: false` 时各路使用自身时钟,但 syncMode 的联动/单路音频/统一倍速仍生效。
382
229
 
383
- ## 多路播放器(MnVideoPlayerMulti)
384
-
385
- 多路播放器在单个容器内管理多路画面,支持布局切换、窗口拖拽交换、统一控制、多路同步播放等。
230
+ ### 窗口全屏与网页全屏
386
231
 
387
232
  ```javascript
388
- import { MnVideoPlayerMulti } from './src/index' // 或 dist 产物
389
-
390
- const multi = new MnVideoPlayerMulti();
391
233
  multi.init({
392
- el: document.getElementById('multiPlayer'),
393
- layout: 4,
394
- urls: [
395
- { channelNo: 1, streamType: 1, channelInfoCustom: '前门摄像头' },
396
- { channelNo: 4, streamType: 1 },
397
- // ... 每路可为:地址字符串 / { url } / 结构化参数对象
398
- ],
399
- playerOptions: { // 公共播放器参数,与每路配置合并
400
- host: '172.16.0.144',
401
- port: 16202,
402
- ws: 'ws',
403
- userId: '1',
404
- tenantId: '1',
405
- accessToken: '...',
406
- phone: '260300380002',
407
- lang: 'zh',
408
- renderType: 'wasm',
409
- beginTime: '2026-08-21 11:34:00',
410
- streamType: 1,
411
- showChannelInfo: true,
412
- },
413
- });
234
+ // ...其他参数
235
+ fullscreenMode: 'window', // 默认:控制条 ▣ + 双击 → 该窗口占满整个网格
236
+ // fullscreenMode: 'browser', // 控制条 ⛶ + 双击 → 浏览器原生网页全屏
237
+ })
414
238
  ```
415
239
 
416
- ### init(options) 配置项
417
-
418
- | 参数 | 类型 | 默认值 | 说明 |
419
- |------|------|--------|------|
420
- | el | HTMLElement | - | 多路容器元素(必填) |
421
- | num | number | urls 长度 | 初始路数 |
422
- | layout | number | 4 | 布局:1/4/6/8/9/16/24/36(6/8 为异形:1 大窗 + 其余小窗) |
423
- | urls | (string \| { url } \| 结构化对象)[] | [] | 每一路的配置,可含 url/phone/channelNo/streamType/channelInfoCustom 等 |
424
- | syncMode | boolean | false | 多路联动:统一倍速、单路音频(仅一路发声)等联动行为 |
425
- | unifiedTimer | boolean | true | 是否使用统一时钟(多路共用一个时钟调度播放节奏,需 syncMode=true 才生效);置 false 时各路用自身时钟 |
426
- | strictSync | boolean | false | 严格同步(需统一时钟):任一通道断流无缓存且落后超过 syncThreshold 时暂停所有通道等待恢复 |
427
- | syncThreshold | number | 100 | 严重不同步判定阈值(ms),运行时可用 `setSyncThreshold(ms)` 动态修改 |
428
- | showToolbar | boolean | false | 是否显示顶部工具条(布局切换/统一控制) |
429
- | fullscreenMode | 'window' \| 'browser' | 'window' | 多路模式全屏方式:'window' 窗口全屏(该窗口占满网格,双击/按钮均走窗口全屏)/ 'browser' 网页全屏 |
430
- | playerOptions | MnVideoPlayerOptions(不含 el) | - | 公共播放器参数,与每路配置合并后传给各子播放器 |
431
-
432
- ### 常用方法
433
-
434
- | 方法 | 说明 |
435
- |------|------|
436
- | setLayout(n) / setNum(n) | 切换布局 / 设置路数(数字自动取不小于它的最近布局) |
437
- | setChns(chns) / appendChns(chns) / removeChns(n) / destroyChns() | 批量设置 / 追加 / 删除 / 清空通道 |
438
- | playAll() / pause() / resume() / stop() | 统一播放控制 |
439
- | changeSpeed(spd) | 统一倍速(所有路同步) |
440
- | enableAudio(enable) | 统一音频开关(syncMode 下仅一路发声) |
441
- | setSyncThreshold(ms) | 动态修改严重不同步阈值 |
442
- | setStrictSync(on) | 动态开关严格同步 |
443
-
444
- ### 注意事项
445
-
446
- - **窗口数上限**:最多 36 路。超出后 `appendChns` 不再新增,改为覆盖**最久未使用(LRU)**的窗口。
447
- - **联动行为**:控制条上的播放/暂停/码流切换/刷新/关闭等用户操作会联动到所有窗口;程序化调用(如 `playAll`)不联动。
448
- - **窗口全屏**:多路默认窗口全屏(▣,占满整个网格而非网页全屏),退出后自动恢复原布局(含 6/8 异形布局)。
449
-
240
+ - 窗口全屏退出后自动恢复原布局(含 6/8 异形)。
241
+ - 若某路正在窗口全屏,切换布局/销毁时会自动退出全屏。
450
242
 
451
-
452
- ## 完整示例
453
-
454
- ### 实时播放示例
243
+ ### 播放/暂停/恢复
455
244
 
456
245
  ```javascript
457
- import MnVideoPlayer from 'mn-video-player-v2'
458
-
459
- const player = new MnVideoPlayer();
460
-
461
- // 初始化播放器
462
- player.init({
463
- el: document.getElementById('player'),
464
- host: '172.16.0.150',
465
- port: 16202,
466
- ws: 'ws',
467
- userId: '1',
468
- tenantId: '1',
469
- accessToken: 'your-access-token',
470
- phone: '13695962147',
471
- channelNo: 5
472
- });
473
-
474
- // 开始播放
475
- player.play({
476
- streamType: 0
477
- });
246
+ multi.playAll() // 播放全部已配置路(空占位窗口跳过)
247
+ multi.pause() // 全部暂停
248
+ multi.resume() // 全部恢复
249
+ multi.stop() // 停止并清理(保留网格空位)
250
+ multi.playChn(3) // 只播放指定 id 的路
478
251
  ```
479
252
 
480
- ### 历史回放示例
481
-
482
- ```javascript
483
- import MnVideoPlayer from 'mn-video-player-v2'
484
-
485
- const player = new MnVideoPlayer();
486
-
487
- // 初始化播放器
488
- player.init({
489
- el: document.getElementById('player'),
490
- host: '172.16.0.150',
491
- port: 16202,
492
- ws: 'ws',
493
- userId: '1',
494
- tenantId: '1',
495
- accessToken: 'your-access-token',
496
- phone: '13695962147',
497
- channelNo: 5,
498
- renderType:"decode"
499
- });
500
-
501
- // 历史回放
502
- player.play({
503
- streamType: 0,
504
- beginTime: '2026-01-14 13:30:00'
505
- });
506
- ```
253
+ ## 特殊场景:file:// 直开等环境的解码 Worker(onlineWorker)
507
254
 
508
- ### 完整控制示例
255
+ 多路播放器各窗口的解码 Worker 默认与页面同源部署(相对路径加载 mn-common-c.js / mn-media-v2.js / mn-media-v2.wasm)。当插件运行在 `file://` 直开、离线壳等无法按相对路径加载的环境时,可通过 `playerOptions.onlineWorker` 传入这三份构建产物的**在线地址**(须为同一版本、允许 CORS),播放器将以「内置 worker 源码 + Blob URL」方式创建解码 Worker:
509
256
 
510
257
  ```javascript
511
- import MnVideoPlayer from 'mn-video-player-v2'
512
-
513
- const player = new MnVideoPlayer();
514
-
515
- // 初始化播放器
516
- player.init({
517
- el: document.getElementById('player'),
518
- host: '172.16.0.150',
519
- port: 16202,
520
- ws: 'ws',
521
- userId: '1',
522
- tenantId: '1',
523
- accessToken: 'your-access-token',
524
- phone: '13695962147',
525
- channelNo: 5
526
- });
527
-
528
- // 播放按钮
529
- document.getElementById('playBtn').addEventListener('click', () => {
530
- player.play({
531
- streamType: 0,
532
- beginTime: '2026-01-14 13:30:00'
533
- });
534
- });
535
-
536
- // 暂停按钮
537
- document.getElementById('pauseBtn').addEventListener('click', () => {
538
- player.pause();
539
- });
540
-
541
- // 停止按钮
542
- document.getElementById('stopBtn').addEventListener('click', () => {
543
- player.stop();
544
- });
545
-
546
- // 切换码流按钮
547
- let streamType = 1;
548
- document.getElementById('switchBtn').addEventListener('click', () => {
549
- player.switchStream(streamType);
550
- streamType = streamType === 1 ? 0 : 1
551
- });
552
-
553
- // 音频开关按钮
554
- let audioEnabled = false;
555
- document.getElementById('playAudioBtn').addEventListener('click', () => {
556
- player.enableAudio(!audioEnabled);
557
- audioEnabled = !audioEnabled
558
- });
559
-
258
+ playerOptions: {
259
+ onlineWorker: {
260
+ common: 'https://cdn.example.com/mn-common-c.js',
261
+ media: 'https://cdn.example.com/mn-media-v2.js',
262
+ wasm: 'https://cdn.example.com/mn-media-v2.wasm',
263
+ },
264
+ }
560
265
  ```
561
266
 
562
- ## 注意事项
267
+ > 说明:地址缺失或创建失败会自动回退内置加载方式;宿主环境若完全禁止创建 Worker(含 Blob),则该模式也无法生效。
563
268
 
564
- 1. **容器元素**:初始化时必须提供一个有效的 DOM 元素作为容器
565
- 2. **WebSocket 连接**:确保 WebSocket 服务器地址可访问
566
- 4. **码流切换**:实时播放和历史回放都支持码流切换
567
- 5. **时间格式**:历史回放的 beginTime 格式为 'YYYY-MM-DD HH:mm:ss'
568
- 7. **资源清理**:停止播放时会自动清理资源
269
+ ## 注意事项
569
270
 
271
+ 1. **容器元素**:init 时必须提供有效 DOM 元素;重建实例前先调用旧实例的 `stop()` 并清空容器。
272
+ 2. **联动范围**:控制条上的用户操作(播放/暂停/码流切换/关闭/全屏等)在 syncMode 下联动全体;程序化 API 不重复联动。
273
+ 3. **资源清理**:`stop()`/`destroyChns()` 会停止所有路播放器、销毁统一时钟并释放 worker 等资源;单路关闭(✕)只清空该窗口。
274
+ 4. **网速/调试**:各窗口独立统计,可通过 `multi.getAll()[i].player` 访问对应子播放器的 `netSpeedCallback`/`timerCtrl` 等。
275
+ 5. **录屏声音**:录屏时同步录制该路播放声音;多路各窗口独立录制。
570
276
 
571
277
  ## 浏览器兼容性
572
278
 
573
- - Chrome 90+
574
- - Firefox 88+
575
- - Safari 14+
576
- - Edge 90+
577
-
578
- 需要支持:
579
- - WebSocket API
580
- - Canvas API
581
- - Web Audio API
582
- - WebAssembly (WASM)
279
+ - Chrome 90+ / Edge 90+ / Firefox 88+ / Safari 14+
280
+ - 需要支持:WebSocket、Canvas、Web Audio、WebAssembly(WASM 解码时)、VideoDecoder(decode 解码时)