wrplayer 1.0.0
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.
- package/README.md +413 -0
- package/adapters/react/index.js +272 -0
- package/adapters/vanilla/index.js +392 -0
- package/adapters/vue/index.js +378 -0
- package/core/PTZController.js +462 -0
- package/core/VoiceIntercomController.js +492 -0
- package/core/WRPlayer.js +924 -0
- package/core/ZLMRTCClient-v1.0.1.js +8227 -0
- package/core/ZLMRTCClient-v1.1.2.js +9474 -0
- package/dist/wrplayer.esm.js +11343 -0
- package/dist/wrplayer.esm.js.map +1 -0
- package/dist/wrplayer.umd.js +11351 -0
- package/dist/wrplayer.umd.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PTZ Controller - PTZ云台控制核心模块
|
|
3
|
+
* 支持方向控制、变焦、聚焦、预置点管理等功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class PTZController {
|
|
7
|
+
/**
|
|
8
|
+
* 创建PTZ控制器实例
|
|
9
|
+
* @param {Object} config - PTZ配置
|
|
10
|
+
* @param {string} config.apiUrl - SIP服务器地址
|
|
11
|
+
* @param {string} config.apiKey - API认证密钥
|
|
12
|
+
* @param {string} config.deviceId - 设备ID
|
|
13
|
+
* @param {string} config.channelId - 通道ID
|
|
14
|
+
* @param {boolean} [config.debug=false] - 是否启用调试
|
|
15
|
+
* @param {boolean} [config.useProxy=true] - 是否使用代理(避免CORS)
|
|
16
|
+
*/
|
|
17
|
+
constructor(config) {
|
|
18
|
+
this.config = {
|
|
19
|
+
debug: false,
|
|
20
|
+
useProxy: true,
|
|
21
|
+
...config
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (!this.config.apiUrl) {
|
|
25
|
+
throw new Error('PTZController: apiUrl is required');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.eventHandlers = {};
|
|
29
|
+
this.isMoving = false;
|
|
30
|
+
this.currentSpeed = 50; // 默认速度50%
|
|
31
|
+
|
|
32
|
+
// 预置点管理
|
|
33
|
+
this.preset = new PTZPresetManager(this);
|
|
34
|
+
// 巡航管理
|
|
35
|
+
this.cruise = new PTZCruiseManager(this);
|
|
36
|
+
// 扫描管理
|
|
37
|
+
this.scan = new PTZScanManager(this);
|
|
38
|
+
|
|
39
|
+
this._log('PTZ Controller initialized', this.config);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 发送PTZ API请求
|
|
44
|
+
* @private
|
|
45
|
+
* @param {string} endpoint - API端点
|
|
46
|
+
* @param {Object} params - 请求参数
|
|
47
|
+
* @returns {Promise} - API响应
|
|
48
|
+
*/
|
|
49
|
+
async _request(endpoint, params = {}) {
|
|
50
|
+
try {
|
|
51
|
+
let url;
|
|
52
|
+
|
|
53
|
+
if (this.config.useProxy && typeof window !== 'undefined') {
|
|
54
|
+
// 使用Next.js代理
|
|
55
|
+
url = `/api/ptz/${endpoint}`;
|
|
56
|
+
} else {
|
|
57
|
+
// 直接请求
|
|
58
|
+
url = `${this.config.apiUrl}/api/front-end/${endpoint}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 添加查询参数
|
|
62
|
+
const searchParams = new URLSearchParams(params);
|
|
63
|
+
const fullUrl = `${url}?${searchParams}`;
|
|
64
|
+
|
|
65
|
+
const headers = {
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// 添加API Key认证 - 只使用SIP服务器需要的格式
|
|
70
|
+
if (this.config.apiKey) {
|
|
71
|
+
headers['api-key'] = this.config.apiKey;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this._log('PTZ API Request:', fullUrl);
|
|
75
|
+
this._log('PTZ API Headers:', JSON.stringify(headers, null, 2));
|
|
76
|
+
|
|
77
|
+
const response = await fetch(fullUrl, {
|
|
78
|
+
method: 'GET',
|
|
79
|
+
headers
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(`PTZ API Error: ${response.status} ${response.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const data = await response.json();
|
|
87
|
+
this._log('PTZ API Response:', data);
|
|
88
|
+
|
|
89
|
+
// 触发成功事件
|
|
90
|
+
this._emit('request:success', { endpoint, params, data });
|
|
91
|
+
|
|
92
|
+
return data;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
this._log('PTZ API Error:', error);
|
|
95
|
+
this._emit('request:error', { endpoint, params, error });
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* PTZ方向控制
|
|
102
|
+
* @param {string} direction - 方向 ('up', 'down', 'left', 'right', 'stop')
|
|
103
|
+
* @param {number} [speed] - 速度 (1-100)
|
|
104
|
+
* @returns {Promise}
|
|
105
|
+
*/
|
|
106
|
+
async move(direction, speed = this.currentSpeed) {
|
|
107
|
+
const command = direction === 'stop' ? 'stop' : direction;
|
|
108
|
+
const horizonSpeed = direction === 'left' || direction === 'right' ?
|
|
109
|
+
Math.floor(speed * 255 / 100) : 0;
|
|
110
|
+
const verticalSpeed = direction === 'up' || direction === 'down' ?
|
|
111
|
+
Math.floor(speed * 255 / 100) : 0;
|
|
112
|
+
const zoomSpeed = 0;
|
|
113
|
+
|
|
114
|
+
this.isMoving = command !== 'stop';
|
|
115
|
+
|
|
116
|
+
await this._request(`ptz/${this.config.deviceId}/${this.config.channelId}`, {
|
|
117
|
+
command,
|
|
118
|
+
horizonSpeed,
|
|
119
|
+
verticalSpeed,
|
|
120
|
+
zoomSpeed
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
this._emit('move', { direction, speed });
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 停止所有PTZ动作
|
|
129
|
+
* @returns {Promise}
|
|
130
|
+
*/
|
|
131
|
+
async stop() {
|
|
132
|
+
return this.move('stop', 0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 变焦控制
|
|
137
|
+
* @param {string} direction - 'in' 或 'out'
|
|
138
|
+
* @param {number} [speed] - 速度 (1-100)
|
|
139
|
+
* @returns {Promise}
|
|
140
|
+
*/
|
|
141
|
+
async zoom(direction, speed = this.currentSpeed) {
|
|
142
|
+
const command = direction === 'in' ? 'zoomin' : 'zoomout';
|
|
143
|
+
const zoomSpeed = Math.floor(speed * 16 / 100);
|
|
144
|
+
|
|
145
|
+
await this._request(`ptz/${this.config.deviceId}/${this.config.channelId}`, {
|
|
146
|
+
command,
|
|
147
|
+
horizonSpeed: 0,
|
|
148
|
+
verticalSpeed: 0,
|
|
149
|
+
zoomSpeed
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
this._emit('zoom', { direction, speed });
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 聚焦控制
|
|
158
|
+
* @param {string} direction - 'near' 或 'far'
|
|
159
|
+
* @param {number} [speed] - 速度 (1-100)
|
|
160
|
+
* @returns {Promise}
|
|
161
|
+
*/
|
|
162
|
+
async focus(direction, speed = this.currentSpeed) {
|
|
163
|
+
const focusSpeed = Math.floor(speed * 255 / 100);
|
|
164
|
+
|
|
165
|
+
await this._request(`fi/focus/${this.config.deviceId}/${this.config.channelId}`, {
|
|
166
|
+
command: direction,
|
|
167
|
+
speed: focusSpeed
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
this._emit('focus', { direction, speed });
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 光圈控制
|
|
176
|
+
* @param {string} direction - 'in' 或 'out'
|
|
177
|
+
* @param {number} [speed] - 速度 (1-100)
|
|
178
|
+
* @returns {Promise}
|
|
179
|
+
*/
|
|
180
|
+
async iris(direction, speed = this.currentSpeed) {
|
|
181
|
+
const irisSpeed = Math.floor(speed * 255 / 100);
|
|
182
|
+
|
|
183
|
+
await this._request(`fi/iris/${this.config.deviceId}/${this.config.channelId}`, {
|
|
184
|
+
command: direction,
|
|
185
|
+
speed: irisSpeed
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
this._emit('iris', { direction, speed });
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 设置控制速度
|
|
194
|
+
* @param {number} speed - 速度 (1-100)
|
|
195
|
+
* @returns {PTZController}
|
|
196
|
+
*/
|
|
197
|
+
setSpeed(speed) {
|
|
198
|
+
this.currentSpeed = Math.max(1, Math.min(100, speed));
|
|
199
|
+
this._emit('speed:change', { speed: this.currentSpeed });
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 获取当前速度
|
|
205
|
+
* @returns {number}
|
|
206
|
+
*/
|
|
207
|
+
getSpeed() {
|
|
208
|
+
return this.currentSpeed;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 事件监听
|
|
213
|
+
* @param {string} event - 事件名
|
|
214
|
+
* @param {Function} handler - 事件处理函数
|
|
215
|
+
* @returns {PTZController}
|
|
216
|
+
*/
|
|
217
|
+
on(event, handler) {
|
|
218
|
+
if (!this.eventHandlers[event]) {
|
|
219
|
+
this.eventHandlers[event] = [];
|
|
220
|
+
}
|
|
221
|
+
this.eventHandlers[event].push(handler);
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 移除事件监听
|
|
227
|
+
* @param {string} event - 事件名
|
|
228
|
+
* @param {Function} [handler] - 事件处理函数
|
|
229
|
+
* @returns {PTZController}
|
|
230
|
+
*/
|
|
231
|
+
off(event, handler) {
|
|
232
|
+
if (!this.eventHandlers[event]) return this;
|
|
233
|
+
|
|
234
|
+
if (handler) {
|
|
235
|
+
const index = this.eventHandlers[event].indexOf(handler);
|
|
236
|
+
if (index > -1) {
|
|
237
|
+
this.eventHandlers[event].splice(index, 1);
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
delete this.eventHandlers[event];
|
|
241
|
+
}
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 触发事件
|
|
247
|
+
* @private
|
|
248
|
+
* @param {string} event - 事件名
|
|
249
|
+
* @param {*} data - 事件数据
|
|
250
|
+
*/
|
|
251
|
+
_emit(event, data) {
|
|
252
|
+
if (this.eventHandlers[event]) {
|
|
253
|
+
this.eventHandlers[event].forEach(handler => {
|
|
254
|
+
try {
|
|
255
|
+
handler(data);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
this._log('Event handler error:', error);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 调试日志
|
|
265
|
+
* @private
|
|
266
|
+
*/
|
|
267
|
+
_log(...args) {
|
|
268
|
+
if (this.config.debug) {
|
|
269
|
+
console.log('[PTZController]', ...args);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 销毁控制器
|
|
275
|
+
*/
|
|
276
|
+
destroy() {
|
|
277
|
+
this.eventHandlers = {};
|
|
278
|
+
this._log('PTZ Controller destroyed');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* PTZ预置点管理器
|
|
284
|
+
*/
|
|
285
|
+
class PTZPresetManager {
|
|
286
|
+
constructor(controller) {
|
|
287
|
+
this.controller = controller;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 查询预置点列表
|
|
292
|
+
* @returns {Promise<Array>}
|
|
293
|
+
*/
|
|
294
|
+
async list() {
|
|
295
|
+
const response = await this.controller._request(
|
|
296
|
+
`preset/query/${this.controller.config.deviceId}/${this.controller.config.channelId}`
|
|
297
|
+
);
|
|
298
|
+
this.controller._emit('preset:list', response);
|
|
299
|
+
return response;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 保存当前位置为预置点
|
|
304
|
+
* @param {number} presetId - 预置点ID (1-255)
|
|
305
|
+
* @returns {Promise}
|
|
306
|
+
*/
|
|
307
|
+
async save(presetId) {
|
|
308
|
+
await this.controller._request(
|
|
309
|
+
`preset/add/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
310
|
+
{ presetId }
|
|
311
|
+
);
|
|
312
|
+
this.controller._emit('preset:saved', { presetId });
|
|
313
|
+
return this;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* 调用预置点
|
|
318
|
+
* @param {number} presetId - 预置点ID
|
|
319
|
+
* @returns {Promise}
|
|
320
|
+
*/
|
|
321
|
+
async call(presetId) {
|
|
322
|
+
await this.controller._request(
|
|
323
|
+
`preset/call/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
324
|
+
{ presetId }
|
|
325
|
+
);
|
|
326
|
+
this.controller._emit('preset:called', { presetId });
|
|
327
|
+
return this;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* 删除预置点
|
|
332
|
+
* @param {number} presetId - 预置点ID
|
|
333
|
+
* @returns {Promise}
|
|
334
|
+
*/
|
|
335
|
+
async delete(presetId) {
|
|
336
|
+
await this.controller._request(
|
|
337
|
+
`preset/delete/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
338
|
+
{ presetId }
|
|
339
|
+
);
|
|
340
|
+
this.controller._emit('preset:deleted', { presetId });
|
|
341
|
+
return this;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* PTZ巡航管理器
|
|
347
|
+
*/
|
|
348
|
+
class PTZCruiseManager {
|
|
349
|
+
constructor(controller) {
|
|
350
|
+
this.controller = controller;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 开始巡航
|
|
355
|
+
* @param {number} cruiseId - 巡航组ID
|
|
356
|
+
* @returns {Promise}
|
|
357
|
+
*/
|
|
358
|
+
async start(cruiseId = 1) {
|
|
359
|
+
await this.controller._request(
|
|
360
|
+
`cruise/start/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
361
|
+
{ cruiseId }
|
|
362
|
+
);
|
|
363
|
+
this.controller._emit('cruise:started', { cruiseId });
|
|
364
|
+
return this;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* 停止巡航
|
|
369
|
+
* @param {number} cruiseId - 巡航组ID
|
|
370
|
+
* @returns {Promise}
|
|
371
|
+
*/
|
|
372
|
+
async stop(cruiseId = 1) {
|
|
373
|
+
await this.controller._request(
|
|
374
|
+
`cruise/stop/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
375
|
+
{ cruiseId }
|
|
376
|
+
);
|
|
377
|
+
this.controller._emit('cruise:stopped', { cruiseId });
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* 设置巡航速度
|
|
383
|
+
* @param {number} cruiseId - 巡航组ID
|
|
384
|
+
* @param {number} speed - 巡航速度
|
|
385
|
+
* @returns {Promise}
|
|
386
|
+
*/
|
|
387
|
+
async setSpeed(cruiseId, speed) {
|
|
388
|
+
await this.controller._request(
|
|
389
|
+
`cruise/speed/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
390
|
+
{ cruiseId, speed }
|
|
391
|
+
);
|
|
392
|
+
this.controller._emit('cruise:speed:set', { cruiseId, speed });
|
|
393
|
+
return this;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* PTZ扫描管理器
|
|
399
|
+
*/
|
|
400
|
+
class PTZScanManager {
|
|
401
|
+
constructor(controller) {
|
|
402
|
+
this.controller = controller;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 开始扫描
|
|
407
|
+
* @param {number} scanId - 扫描组ID
|
|
408
|
+
* @returns {Promise}
|
|
409
|
+
*/
|
|
410
|
+
async start(scanId = 1) {
|
|
411
|
+
await this.controller._request(
|
|
412
|
+
`scan/start/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
413
|
+
{ scanId }
|
|
414
|
+
);
|
|
415
|
+
this.controller._emit('scan:started', { scanId });
|
|
416
|
+
return this;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* 停止扫描
|
|
421
|
+
* @param {number} scanId - 扫描组ID
|
|
422
|
+
* @returns {Promise}
|
|
423
|
+
*/
|
|
424
|
+
async stop(scanId = 1) {
|
|
425
|
+
await this.controller._request(
|
|
426
|
+
`scan/stop/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
427
|
+
{ scanId }
|
|
428
|
+
);
|
|
429
|
+
this.controller._emit('scan:stopped', { scanId });
|
|
430
|
+
return this;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* 设置扫描左边界
|
|
435
|
+
* @param {number} scanId - 扫描组ID
|
|
436
|
+
* @returns {Promise}
|
|
437
|
+
*/
|
|
438
|
+
async setLeftBoundary(scanId = 1) {
|
|
439
|
+
await this.controller._request(
|
|
440
|
+
`scan/set/left/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
441
|
+
{ scanId }
|
|
442
|
+
);
|
|
443
|
+
this.controller._emit('scan:boundary:left', { scanId });
|
|
444
|
+
return this;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* 设置扫描右边界
|
|
449
|
+
* @param {number} scanId - 扫描组ID
|
|
450
|
+
* @returns {Promise}
|
|
451
|
+
*/
|
|
452
|
+
async setRightBoundary(scanId = 1) {
|
|
453
|
+
await this.controller._request(
|
|
454
|
+
`scan/set/right/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
|
|
455
|
+
{ scanId }
|
|
456
|
+
);
|
|
457
|
+
this.controller._emit('scan:boundary:right', { scanId });
|
|
458
|
+
return this;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export default PTZController;
|