sample-xmonitor-js 1.0.10 → 2.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/lib/Monitor.d.ts +25 -2
- package/lib/Monitor.js +96 -7
- package/lib/index.d.ts +88 -2
- package/lib/index.js +13 -6
- package/lib/txrtc/index.d.ts +21 -0
- package/lib/txrtc/index.js +91 -7
- package/package.json +36 -36
package/lib/Monitor.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export declare const logger: {
|
|
2
|
+
trace: boolean;
|
|
3
|
+
debug(msg: string, ...args: any[]): void;
|
|
4
|
+
warn(msg: string, ...args: any[]): void;
|
|
5
|
+
info(msg: string, ...args: any[]): void;
|
|
6
|
+
error(msg: string, ...args: any[]): void;
|
|
7
|
+
success(msg: string, ...args: any[]): void;
|
|
8
|
+
};
|
|
1
9
|
export declare interface Setup {
|
|
2
10
|
/**
|
|
3
11
|
*控制视频截图清晰度
|
|
@@ -34,6 +42,10 @@ export declare class ScreenTypeError extends Error {
|
|
|
34
42
|
readonly code: string;
|
|
35
43
|
constructor(message: any, code?: any);
|
|
36
44
|
}
|
|
45
|
+
export declare class NetworkError extends Error {
|
|
46
|
+
readonly code: string;
|
|
47
|
+
constructor(message: any, code?: any);
|
|
48
|
+
}
|
|
37
49
|
export type Device = 'screen' | 'camera' | 'mobile';
|
|
38
50
|
export type Provider = 'default' | 'txrtc';
|
|
39
51
|
export type DeviceProvider = 'default:screen' | 'default:camera' | 'default:mobile' | 'txrtc:screen' | 'txrtc:camera' | 'txrtc:mobile';
|
|
@@ -52,8 +64,9 @@ export declare class MonitorType {
|
|
|
52
64
|
* stop 录制停止
|
|
53
65
|
* error 发生错误
|
|
54
66
|
* changed 状态改变
|
|
67
|
+
* statistics 通话质量统计
|
|
55
68
|
*/
|
|
56
|
-
export type MonitorEvent = 'cancel' | 'start' | 'playing' | 'pause' | 'shot' | 'stop' | 'error' | 'changed' | 'message';
|
|
69
|
+
export type MonitorEvent = 'cancel' | 'start' | 'playing' | 'pause' | 'shot' | 'stop' | 'error' | 'changed' | 'message' | 'statistics';
|
|
57
70
|
export declare const MonitorEvents: Array<MonitorEvent>;
|
|
58
71
|
export type MonitorEventObject = {
|
|
59
72
|
event: MonitorEvent;
|
|
@@ -90,7 +103,7 @@ export type MonitorOptions = {
|
|
|
90
103
|
export default class Monitor {
|
|
91
104
|
readonly options: MonitorOptions;
|
|
92
105
|
readonly no: string;
|
|
93
|
-
element: HTMLVideoElement;
|
|
106
|
+
element: HTMLVideoElement | HTMLCanvasElement;
|
|
94
107
|
readonly type: MonitorType;
|
|
95
108
|
readonly watcher: Watcher;
|
|
96
109
|
private eventPool;
|
|
@@ -115,4 +128,14 @@ export default class Monitor {
|
|
|
115
128
|
}): Promise<void>;
|
|
116
129
|
removeEventListener(event: MonitorEvent): void;
|
|
117
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* @returns 频次控制函数
|
|
133
|
+
* @param handle
|
|
134
|
+
* @param timeWindow
|
|
135
|
+
* @param threshold
|
|
136
|
+
*/
|
|
137
|
+
export declare function createFrequencyController<T extends any[]>(handle: (...args: T) => void, timeWindow?: number, threshold?: number): {
|
|
138
|
+
(...args: T): void;
|
|
139
|
+
reset(): void;
|
|
140
|
+
};
|
|
118
141
|
export {};
|
package/lib/Monitor.js
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
import Driver from 'xdriver';
|
|
2
|
+
function print(fn, ...args) {
|
|
3
|
+
if (logger.trace) {
|
|
4
|
+
console.trace(...args);
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
fn(...args);
|
|
8
|
+
}
|
|
9
|
+
function getFormatDate() {
|
|
10
|
+
const date = new Date();
|
|
11
|
+
return `[${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}] xmonitor`;
|
|
12
|
+
}
|
|
13
|
+
export const logger = {
|
|
14
|
+
trace: false,
|
|
15
|
+
debug(msg, ...args) {
|
|
16
|
+
print(console.debug, `%c${getFormatDate()} 🐞 ${msg}`, "color: gray;", ...args);
|
|
17
|
+
},
|
|
18
|
+
warn(msg, ...args) {
|
|
19
|
+
print(console.warn, `%c${getFormatDate()} 🔔 ${msg}`, "color: #FFCC00;", ...args);
|
|
20
|
+
},
|
|
21
|
+
info(msg, ...args) {
|
|
22
|
+
print(console.info, `${getFormatDate()} ℹ️ ${msg}`, ...args);
|
|
23
|
+
},
|
|
24
|
+
error(msg, ...args) {
|
|
25
|
+
print(console.error, `%c${getFormatDate()} ❌ ${msg}`, "color: #EF4444;", ...args);
|
|
26
|
+
},
|
|
27
|
+
success(msg, ...args) {
|
|
28
|
+
print(console.log, `%c${getFormatDate()} ✅ ${msg}`, "color: #10B981;", ...args);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
2
31
|
class Watcher {
|
|
3
32
|
constructor(monitor) {
|
|
4
33
|
this.monitor = monitor;
|
|
@@ -10,7 +39,7 @@ class Watcher {
|
|
|
10
39
|
autoIncrement: true
|
|
11
40
|
});
|
|
12
41
|
}).open().then(() => {
|
|
13
|
-
|
|
42
|
+
logger.debug('Shot');
|
|
14
43
|
});
|
|
15
44
|
this.driver = driver;
|
|
16
45
|
}
|
|
@@ -22,12 +51,22 @@ class Watcher {
|
|
|
22
51
|
sharpness = 1;
|
|
23
52
|
if (sharpness < 0)
|
|
24
53
|
sharpness = 0.1;
|
|
25
|
-
let { videoWidth, videoHeight } = this.monitor.element;
|
|
26
54
|
let name = Date.now() + '.jpg';
|
|
27
55
|
let canvas = document.createElement("canvas");
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
56
|
+
if (this.monitor.element instanceof HTMLVideoElement) {
|
|
57
|
+
let { videoWidth, videoHeight } = this.monitor.element;
|
|
58
|
+
canvas.width = videoWidth * sharpness;
|
|
59
|
+
canvas.height = videoHeight * sharpness;
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
canvas.getContext('2d').drawImage(this.monitor.element, 0, 0, canvas.width, canvas.height);
|
|
62
|
+
}
|
|
63
|
+
else if (this.monitor.element instanceof HTMLCanvasElement) {
|
|
64
|
+
let sourceCanvas = this.monitor.element;
|
|
65
|
+
canvas.width = sourceCanvas.width * sharpness;
|
|
66
|
+
canvas.height = sourceCanvas.height * sharpness;
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
canvas.getContext('2d').drawImage(sourceCanvas, 0, 0, canvas.width, canvas.height);
|
|
69
|
+
}
|
|
31
70
|
let img = { name };
|
|
32
71
|
Object.defineProperty(img, "blob", {
|
|
33
72
|
get() {
|
|
@@ -61,6 +100,12 @@ export class ScreenTypeError extends Error {
|
|
|
61
100
|
this.code = code;
|
|
62
101
|
}
|
|
63
102
|
}
|
|
103
|
+
export class NetworkError extends Error {
|
|
104
|
+
constructor(message, code) {
|
|
105
|
+
super(message);
|
|
106
|
+
this.code = code;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
64
109
|
export class MonitorType {
|
|
65
110
|
constructor(provider, device) {
|
|
66
111
|
this.provider = provider;
|
|
@@ -74,7 +119,7 @@ export class MonitorType {
|
|
|
74
119
|
return new MonitorType(type, device);
|
|
75
120
|
}
|
|
76
121
|
}
|
|
77
|
-
export const MonitorEvents = ['cancel', 'start', 'playing', 'pause', 'shot', 'stop', 'error', 'changed', 'message'];
|
|
122
|
+
export const MonitorEvents = ['cancel', 'start', 'playing', 'pause', 'shot', 'stop', 'error', 'changed', 'message', 'statistics'];
|
|
78
123
|
export class MObject {
|
|
79
124
|
constructor(room, id) {
|
|
80
125
|
this.room = room;
|
|
@@ -130,7 +175,7 @@ export default class Monitor {
|
|
|
130
175
|
if (listener) {
|
|
131
176
|
return listener({ event, target: this, data, error });
|
|
132
177
|
}
|
|
133
|
-
|
|
178
|
+
logger.debug(event, data, error);
|
|
134
179
|
}
|
|
135
180
|
async switch(option) {
|
|
136
181
|
throw new NotSupportError('不支持设备切换');
|
|
@@ -139,3 +184,47 @@ export default class Monitor {
|
|
|
139
184
|
this.eventPool.delete(event);
|
|
140
185
|
}
|
|
141
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* @returns 频次控制函数
|
|
189
|
+
* @param handle
|
|
190
|
+
* @param timeWindow
|
|
191
|
+
* @param threshold
|
|
192
|
+
*/
|
|
193
|
+
export function createFrequencyController(handle, timeWindow = 10000, threshold = 3) {
|
|
194
|
+
// 初始化状态:调用次数、首次调用时间
|
|
195
|
+
let callCount = 0;
|
|
196
|
+
let firstCallTime = null;
|
|
197
|
+
/**
|
|
198
|
+
* 频次控制的核心函数
|
|
199
|
+
* @param args 透传给触发回调的参数
|
|
200
|
+
*/
|
|
201
|
+
const frequencyController = (...args) => {
|
|
202
|
+
const now = Date.now();
|
|
203
|
+
// 1. 检查是否超出时间窗口,超出则重置状态
|
|
204
|
+
if (firstCallTime && now - firstCallTime > timeWindow) {
|
|
205
|
+
callCount = 0;
|
|
206
|
+
firstCallTime = null;
|
|
207
|
+
}
|
|
208
|
+
// 2. 首次调用时记录时间
|
|
209
|
+
if (firstCallTime === null) {
|
|
210
|
+
firstCallTime = now;
|
|
211
|
+
}
|
|
212
|
+
// 3. 累加调用次数
|
|
213
|
+
callCount += 1;
|
|
214
|
+
// 4. 判断是否达到触发条件(次数 > 阈值)
|
|
215
|
+
if (callCount > threshold) {
|
|
216
|
+
handle(...args);
|
|
217
|
+
// 可选:触发后重置状态(避免同一窗口内多次触发),根据需求可注释
|
|
218
|
+
callCount = 0;
|
|
219
|
+
firstCallTime = null;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
/**
|
|
223
|
+
* 手动重置状态(可选扩展)
|
|
224
|
+
*/
|
|
225
|
+
frequencyController.reset = () => {
|
|
226
|
+
callCount = 0;
|
|
227
|
+
firstCallTime = null;
|
|
228
|
+
};
|
|
229
|
+
return frequencyController;
|
|
230
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import Monitor, { DeviceProvider, MObject, MonitorEvent, MonitorEventListener, MonitorType, Setup } from "./Monitor";
|
|
1
|
+
import Monitor, { DeviceProvider, logger, MObject, MonitorEvent, MonitorEventListener, MonitorType, Setup } from "./Monitor";
|
|
2
2
|
import { DefaultSetup } from "./default";
|
|
3
3
|
import { TxrtcSetup } from "./txrtc";
|
|
4
|
+
import { LOG_LEVEL, PermissionOption } from "trtc-sdk-v5";
|
|
4
5
|
export default class MonitorFactory {
|
|
5
6
|
private readonly monitorMap;
|
|
6
7
|
private readonly pointer;
|
|
@@ -58,4 +59,89 @@ export declare function getSpeakerList(requestPermission?: boolean): Promise<Med
|
|
|
58
59
|
export declare function setCurrentSpeaker(speakerId: string): Promise<void>;
|
|
59
60
|
export declare function getMicrophoneList(requestPermission?: boolean): Promise<MediaDeviceInfo[]>;
|
|
60
61
|
export declare function getCameraList(requestPermission?: boolean): Promise<MediaDeviceInfo[]>;
|
|
61
|
-
export declare function isSupported(): Promise<
|
|
62
|
+
export declare function isSupported(): Promise<CheckSystemResult>;
|
|
63
|
+
export declare function getPermissions(option: PermissionOption): Promise<import("trtc-sdk-v5").PermissionResult>;
|
|
64
|
+
export declare function setLog(level: LOG_LEVEL, enableUploadLog?: boolean): void;
|
|
65
|
+
/**
|
|
66
|
+
* 系统能力检测结果的顶层接口
|
|
67
|
+
*/
|
|
68
|
+
interface CheckSystemResult {
|
|
69
|
+
/**
|
|
70
|
+
* 综合检测结果
|
|
71
|
+
* @type {boolean}
|
|
72
|
+
* @description 如果为 true,表示当前浏览器环境满足 SDK 运行的最低要求;
|
|
73
|
+
* 如果为 false,则意味着当前浏览器无法正常使用该 SDK 的核心功能。
|
|
74
|
+
*/
|
|
75
|
+
result: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* 详细的子项检测详情
|
|
78
|
+
* @type {CheckSystemDetail}
|
|
79
|
+
*/
|
|
80
|
+
detail: CheckSystemDetail;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 浏览器各项具体能力的详细检测结果
|
|
84
|
+
*/
|
|
85
|
+
interface CheckSystemDetail {
|
|
86
|
+
/**
|
|
87
|
+
* 浏览器支持度
|
|
88
|
+
* @type {boolean}
|
|
89
|
+
* @description 检测当前浏览器(及其版本)是否在 SDK 支持的列表中(例如是否为太旧的 IE 浏览器)。
|
|
90
|
+
*/
|
|
91
|
+
isBrowserSupported: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* WebRTC 核心支持
|
|
94
|
+
* @type {boolean}
|
|
95
|
+
* @description 检测浏览器是否原生支持 WebRTC 标准 API,这是音视频通话的基础。
|
|
96
|
+
*/
|
|
97
|
+
isWebRTCSupported: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* WebCodecs 支持
|
|
100
|
+
* @type {boolean}
|
|
101
|
+
* @description 检测是否支持 WebCodecs API,这通常用于更底层的音视频数据处理和高性能编解码。
|
|
102
|
+
*/
|
|
103
|
+
isWebCodecsSupported: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* 媒体设备访问支持
|
|
106
|
+
* @type {boolean}
|
|
107
|
+
* @description 检测是否支持通过 `navigator.mediaDevices` 获取摄像头和麦克风列表及音视频流。
|
|
108
|
+
*/
|
|
109
|
+
isMediaDevicesSupported: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* 屏幕共享支持
|
|
112
|
+
* @type {boolean}
|
|
113
|
+
* @description 检测当前浏览器是否支持屏幕共享功能(通常需要检测 `getDisplayMedia` API)。
|
|
114
|
+
*/
|
|
115
|
+
isScreenShareSupported: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* 小流支持
|
|
118
|
+
* @type {boolean}
|
|
119
|
+
* @description 通常指是否支持发送低分辨率、低码率的视频流(用于低性能设备或弱网环境)。
|
|
120
|
+
*/
|
|
121
|
+
isSmallStreamSupported: boolean;
|
|
122
|
+
/**
|
|
123
|
+
* H264 上行编码支持
|
|
124
|
+
* @type {boolean}
|
|
125
|
+
* @description 检测摄像头采集的视频是否能使用 H264 编码格式推送到服务器。
|
|
126
|
+
*/
|
|
127
|
+
isH264EncodeSupported: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* H264 下行解码支持
|
|
130
|
+
* @type {boolean}
|
|
131
|
+
* @description 检测浏览器是否能解码服务器下发的 H264 格式的视频流。
|
|
132
|
+
*/
|
|
133
|
+
isH264DecodeSupported: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* VP8 上行编码支持
|
|
136
|
+
* @type {boolean}
|
|
137
|
+
* @description 检测摄像头采集的视频是否能使用 VP8 编码格式推送到服务器。
|
|
138
|
+
*/
|
|
139
|
+
isVp8EncodeSupported: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* VP8 下行解码支持
|
|
142
|
+
* @type {boolean}
|
|
143
|
+
* @description 检测浏览器是否能解码服务器下发的 VP8 格式的视频流。
|
|
144
|
+
*/
|
|
145
|
+
isVp8DecodeSupported: boolean;
|
|
146
|
+
}
|
|
147
|
+
export { logger };
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MObject, MonitorEvents, MonitorType, NotSupportError } from "./Monitor";
|
|
1
|
+
import { logger, MObject, MonitorEvents, MonitorType, NotSupportError } from "./Monitor";
|
|
2
2
|
import DefaultMonitor from "./default";
|
|
3
3
|
import TxrtcMonitor from "./txrtc";
|
|
4
4
|
import TRTC from "trtc-sdk-v5";
|
|
@@ -65,7 +65,7 @@ export default class MonitorFactory {
|
|
|
65
65
|
}
|
|
66
66
|
clearInterval(this.intervalId);
|
|
67
67
|
// 启动timer定时截图
|
|
68
|
-
|
|
68
|
+
logger.info(`开启定时截图,时间间隔${interval}ms`);
|
|
69
69
|
this.intervalId = setInterval(async () => {
|
|
70
70
|
try {
|
|
71
71
|
// 调用所有监视器截图
|
|
@@ -76,15 +76,15 @@ export default class MonitorFactory {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
catch (e) {
|
|
79
|
-
|
|
79
|
+
logger.error('监视器启动出错', e);
|
|
80
80
|
}
|
|
81
81
|
}, interval);
|
|
82
82
|
// 启动所有监视器
|
|
83
83
|
for (let monitor of this.monitorMap.values()) {
|
|
84
|
-
|
|
84
|
+
logger.info(`启动监视器 >>> ${monitor.type.toString()}`);
|
|
85
85
|
monitor.start();
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
logger.info('所有监视器启动完成');
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
90
|
* 停用监视器
|
|
@@ -117,7 +117,7 @@ export function getInstance(options) {
|
|
|
117
117
|
}
|
|
118
118
|
return factory.init(MonitorType.parse(provider), element, setup);
|
|
119
119
|
})).then((monitors) => {
|
|
120
|
-
|
|
120
|
+
logger.info('监视器启动完成', monitors);
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
return factory;
|
|
@@ -143,3 +143,10 @@ export function getCameraList(requestPermission) {
|
|
|
143
143
|
export function isSupported() {
|
|
144
144
|
return TRTC.isSupported();
|
|
145
145
|
}
|
|
146
|
+
export function getPermissions(option) {
|
|
147
|
+
return TRTC.getPermissions(option);
|
|
148
|
+
}
|
|
149
|
+
export function setLog(level, enableUploadLog) {
|
|
150
|
+
TRTC.setLogLevel(level, enableUploadLog);
|
|
151
|
+
}
|
|
152
|
+
export { logger };
|
package/lib/txrtc/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Monitor, { Device, MObject, MonitorOptions, Setup } from "../Monitor";
|
|
2
2
|
import TRTC, { LocalVideoConfig, ScreenShareConfig } from "trtc-sdk-v5";
|
|
3
|
+
import { WatermarkOptions } from 'trtc-sdk-v5/plugins/video-effect/watermark';
|
|
3
4
|
export interface TxrtcSetup extends Setup {
|
|
4
5
|
userId: string;
|
|
5
6
|
roomId: string;
|
|
@@ -7,6 +8,26 @@ export interface TxrtcSetup extends Setup {
|
|
|
7
8
|
userSig: string;
|
|
8
9
|
userDefineRecordId?: string;
|
|
9
10
|
option?: LocalVideoConfig['option'] | ScreenShareConfig['option'];
|
|
11
|
+
/**
|
|
12
|
+
* trtc-sdk-v5/assets 目录放到cdn上 某些插件会依赖静态资源
|
|
13
|
+
* eg: 'https://xxxx/assets'
|
|
14
|
+
*/
|
|
15
|
+
assetsPath?: string;
|
|
16
|
+
deviceDetector?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 开启人脸检测
|
|
19
|
+
*/
|
|
20
|
+
faceDetection?: {
|
|
21
|
+
onFaceDetectionStateChanged: (hasFace: boolean) => void;
|
|
22
|
+
detectionInterval?: number;
|
|
23
|
+
minConfidence?: number;
|
|
24
|
+
missingTimeout?: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* 开启水印
|
|
28
|
+
* 设置水平图片路径
|
|
29
|
+
*/
|
|
30
|
+
watermark?: WatermarkOptions;
|
|
10
31
|
}
|
|
11
32
|
export default class TxrtcMonitor extends Monitor {
|
|
12
33
|
private active;
|
package/lib/txrtc/index.js
CHANGED
|
@@ -1,19 +1,48 @@
|
|
|
1
|
-
import Monitor, { MonitorType, NotSupportError, ScreenTypeError } from "../Monitor";
|
|
2
|
-
import TRTC
|
|
1
|
+
import Monitor, { createFrequencyController, logger, MonitorType, NetworkError, NotSupportError, ScreenTypeError } from "../Monitor";
|
|
2
|
+
import TRTC from "trtc-sdk-v5";
|
|
3
3
|
import { CDNStreaming, PublishMode } from 'trtc-sdk-v5/plugins/cdn-streaming';
|
|
4
|
+
import { FaceDetection } from 'trtc-sdk-v5/plugins/video-effect/face-detection';
|
|
5
|
+
import { Watermark } from 'trtc-sdk-v5/plugins/video-effect/watermark';
|
|
6
|
+
import { DeviceDetector } from 'trtc-sdk-v5/plugins/device-detector';
|
|
4
7
|
export default class TxrtcMonitor extends Monitor {
|
|
5
8
|
constructor(options) {
|
|
6
9
|
super(options);
|
|
7
10
|
this.active = false;
|
|
8
11
|
this.setup = options.setup;
|
|
9
|
-
|
|
12
|
+
const trtcOptions = {
|
|
13
|
+
plugins: [CDNStreaming, DeviceDetector]
|
|
14
|
+
};
|
|
15
|
+
if (this.setup?.deviceDetector) {
|
|
16
|
+
trtcOptions.plugins?.push(DeviceDetector);
|
|
17
|
+
}
|
|
18
|
+
if (this.setup?.faceDetection) {
|
|
19
|
+
if (!this.setup.assetsPath) {
|
|
20
|
+
throw new Error('Please set setup.assetsPath');
|
|
21
|
+
}
|
|
22
|
+
trtcOptions.assetsPath = this.setup.assetsPath;
|
|
23
|
+
trtcOptions.plugins?.push(FaceDetection);
|
|
24
|
+
}
|
|
25
|
+
if (this.setup?.watermark) {
|
|
26
|
+
trtcOptions.plugins?.push(Watermark);
|
|
27
|
+
}
|
|
28
|
+
this.trtc = TRTC.create(trtcOptions);
|
|
10
29
|
if (this.setup.debug) {
|
|
11
|
-
TRTC.setLogLevel(LOG_LEVEL.DEBUG);
|
|
30
|
+
TRTC.setLogLevel(1 /* LOG_LEVEL.DEBUG */);
|
|
12
31
|
}
|
|
13
32
|
}
|
|
14
33
|
async start() {
|
|
34
|
+
if (this.setup?.deviceDetector) {
|
|
35
|
+
const options = {
|
|
36
|
+
networkDetect: { sdkAppId: this.setup.sdkAppId, userId: this.setup.userId, userSig: this.setup.userSig }
|
|
37
|
+
};
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
const resultWithNetwork = await this.trtc.startPlugin('DeviceDetector', options);
|
|
40
|
+
if (resultWithNetwork) {
|
|
41
|
+
logger.info('DeviceDetector >>> ', resultWithNetwork);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
15
44
|
let streamId = `${this.setup.roomId}_${this.setup.userId}`;
|
|
16
|
-
|
|
45
|
+
logger.info(`Start ${this.type.device} TxrtcMonitor with streamId ${streamId}`);
|
|
17
46
|
if (this.active)
|
|
18
47
|
return;
|
|
19
48
|
let publishMode = PublishMode.PublishMainStreamToCDN;
|
|
@@ -32,19 +61,32 @@ export default class TxrtcMonitor extends Monitor {
|
|
|
32
61
|
if (this.setup.userDefineRecordId) {
|
|
33
62
|
params.userDefineRecordId = this.setup.userDefineRecordId;
|
|
34
63
|
}
|
|
64
|
+
// 尝试重连 10s 内超过5次重连之后抛出异常
|
|
65
|
+
const triggerNetworkDisconnect = createFrequencyController(() => {
|
|
66
|
+
const error = new NetworkError('Network disconnected', 'NetworkDisconnected');
|
|
67
|
+
logger.error('Network Error', error);
|
|
68
|
+
this.dispatchEvent('error', null, error);
|
|
69
|
+
this.stop();
|
|
70
|
+
});
|
|
35
71
|
this.trtc.on(TRTC.EVENT.ERROR, (error) => {
|
|
36
72
|
let err = error;
|
|
37
73
|
if (this.options.type.device === 'screen') {
|
|
38
74
|
err = new ScreenTypeError(error.message, 'ScreenTypeError');
|
|
39
75
|
}
|
|
76
|
+
else if (error.code === TRTC.ERROR_CODE.OPERATION_FAILED && error.extraCode === 5501) {
|
|
77
|
+
// 用户网络防火墙受限,会导致无法正常进行音视频通话。
|
|
78
|
+
// 此时引导用户更换网络 or 检查网络防火墙设置。
|
|
79
|
+
err = new NetworkError(error.message, 'FirewallRestriction');
|
|
80
|
+
}
|
|
81
|
+
logger.error('TRTCError', err);
|
|
40
82
|
this.dispatchEvent('error', null, err);
|
|
41
83
|
this.stop();
|
|
42
84
|
}).on(TRTC.EVENT.SCREEN_SHARE_STOPPED, () => {
|
|
43
85
|
this.dispatchEvent('cancel');
|
|
44
86
|
this.stop();
|
|
45
|
-
}).on(TRTC.EVENT.TRACK, () => {
|
|
87
|
+
}).on(TRTC.EVENT.TRACK, (data) => {
|
|
46
88
|
this.active = true;
|
|
47
|
-
this.dispatchEvent('start');
|
|
89
|
+
this.dispatchEvent('start', data);
|
|
48
90
|
}).on(TRTC.EVENT.VIDEO_PLAY_STATE_CHANGED, ({ state }) => {
|
|
49
91
|
if (state === 'STOPPED') {
|
|
50
92
|
this.dispatchEvent('stop');
|
|
@@ -53,10 +95,34 @@ export default class TxrtcMonitor extends Monitor {
|
|
|
53
95
|
this.dispatchEvent('playing');
|
|
54
96
|
}
|
|
55
97
|
this.element = this.options.element.querySelector('video');
|
|
98
|
+
if (!this.element) {
|
|
99
|
+
// 移动端使用的是canvas显示
|
|
100
|
+
this.element = this.options.element.querySelector('canvas');
|
|
101
|
+
}
|
|
56
102
|
}).on(TRTC.EVENT.CUSTOM_MESSAGE, (message) => {
|
|
57
103
|
let decoder = new TextDecoder('utf-8');
|
|
58
104
|
let payload = decoder.decode(message.data);
|
|
59
105
|
this.dispatchEvent('message', payload);
|
|
106
|
+
}).on(TRTC.EVENT.STATISTICS, statistics => {
|
|
107
|
+
logger.debug('TxrtcMonitor statistics', statistics);
|
|
108
|
+
this.dispatchEvent('statistics', statistics);
|
|
109
|
+
}).on(TRTC.EVENT.SEI_MESSAGE, event => {
|
|
110
|
+
this.dispatchEvent('message', event);
|
|
111
|
+
}).on(TRTC.EVENT.PUBLISH_STATE_CHANGED, event => {
|
|
112
|
+
if (event.error) {
|
|
113
|
+
logger.error('TxrtcMonitor publish error', event);
|
|
114
|
+
this.dispatchEvent('error', event, event.error);
|
|
115
|
+
this.stop();
|
|
116
|
+
}
|
|
117
|
+
}).on(TRTC.EVENT.CONNECTION_STATE_CHANGED, event => {
|
|
118
|
+
if (event.state === 'CONNECTING' && event.prevState === 'DISCONNECTED' && event.isReconnecting) {
|
|
119
|
+
// 尝试重连 10次之后抛出异常
|
|
120
|
+
triggerNetworkDisconnect();
|
|
121
|
+
}
|
|
122
|
+
}).on(TRTC.EVENT.NETWORK_QUALITY, event => {
|
|
123
|
+
logger.info(`[network-quality] uplinkNetworkQuality:${event.uplinkNetworkQuality}, downlinkNetworkQuality: ${event.downlinkNetworkQuality}`);
|
|
124
|
+
logger.info(`[network-quality] uplink rtt:${event.uplinkRTT} loss:${event.uplinkLoss}`);
|
|
125
|
+
logger.info(`[network-quality] downlink rtt:${event.downlinkRTT} loss:${event.downlinkLoss}`);
|
|
60
126
|
});
|
|
61
127
|
await this.trtc.enterRoom(params);
|
|
62
128
|
}
|
|
@@ -134,6 +200,15 @@ export class TxrtcCameraMonitor extends TxrtcMonitor {
|
|
|
134
200
|
publish: true,
|
|
135
201
|
view: this.options.element
|
|
136
202
|
});
|
|
203
|
+
// 检测人脸 需要在调用本地摄像头之后启动该插件
|
|
204
|
+
if (this.setup.faceDetection) {
|
|
205
|
+
// @ts-ignore
|
|
206
|
+
await this.trtc.startPlugin('FaceDetection', this.setup.faceDetection);
|
|
207
|
+
}
|
|
208
|
+
// 开启水印
|
|
209
|
+
if (this.setup?.watermark) {
|
|
210
|
+
await this.trtc.startPlugin('Watermark', this.setup?.watermark);
|
|
211
|
+
}
|
|
137
212
|
}
|
|
138
213
|
catch (e) {
|
|
139
214
|
this.dispatchEvent('error', null, e);
|
|
@@ -153,6 +228,15 @@ export class TxrtcCameraMonitor extends TxrtcMonitor {
|
|
|
153
228
|
}
|
|
154
229
|
}
|
|
155
230
|
async stop() {
|
|
231
|
+
// 如果启动了人脸检测插件 则停止
|
|
232
|
+
if (this.setup.faceDetection) {
|
|
233
|
+
// @ts-ignore
|
|
234
|
+
await this.trtc.stopPlugin('FaceDetection');
|
|
235
|
+
}
|
|
236
|
+
// 如果开启了水印
|
|
237
|
+
if (this.setup?.watermark) {
|
|
238
|
+
await this.trtc.stopPlugin('Watermark');
|
|
239
|
+
}
|
|
156
240
|
await this.trtc.stopLocalAudio();
|
|
157
241
|
await this.trtc.stopLocalVideo();
|
|
158
242
|
await super.stop();
|
package/package.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "sample-xmonitor-js",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "./lib/index",
|
|
6
|
-
"files": [
|
|
7
|
-
"lib/"
|
|
8
|
-
],
|
|
9
|
-
"scripts": {
|
|
10
|
-
"build": "tsc",
|
|
11
|
-
"build:babel": "tsc && babel lib --out-dir dist && cd lib && del *.js*",
|
|
12
|
-
"build:webpack": "tsc && webpack --mode production"
|
|
13
|
-
},
|
|
14
|
-
"repository": {
|
|
15
|
-
"type": "git",
|
|
16
|
-
"url": "git@codeup.aliyun.com:60b0836844816b8ed2335ed6/ui-modules/xmonitor.git"
|
|
17
|
-
},
|
|
18
|
-
"keywords": [
|
|
19
|
-
"monitor"
|
|
20
|
-
],
|
|
21
|
-
"author": "xs",
|
|
22
|
-
"license": "ISC",
|
|
23
|
-
"devDependencies": {
|
|
24
|
-
"babel-cli": "^6.26.0",
|
|
25
|
-
"babel-preset-env": "^1.7.0",
|
|
26
|
-
"babel-preset-es2015": "^6.24.1",
|
|
27
|
-
"babel-preset-stage-2": "^6.24.1",
|
|
28
|
-
"uglifyjs-webpack-plugin": "^2.2.0",
|
|
29
|
-
"webpack": "^5.64.2",
|
|
30
|
-
"webpack-cli": "^4.9.1"
|
|
31
|
-
},
|
|
32
|
-
"dependencies": {
|
|
33
|
-
"trtc-sdk-v5": "^5.
|
|
34
|
-
"xdriver": "^1.1.1"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "sample-xmonitor-js",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "./lib/index",
|
|
6
|
+
"files": [
|
|
7
|
+
"lib/"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"build:babel": "tsc && babel lib --out-dir dist && cd lib && del *.js*",
|
|
12
|
+
"build:webpack": "tsc && webpack --mode production"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git@codeup.aliyun.com:60b0836844816b8ed2335ed6/ui-modules/xmonitor.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"monitor"
|
|
20
|
+
],
|
|
21
|
+
"author": "xs",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"babel-cli": "^6.26.0",
|
|
25
|
+
"babel-preset-env": "^1.7.0",
|
|
26
|
+
"babel-preset-es2015": "^6.24.1",
|
|
27
|
+
"babel-preset-stage-2": "^6.24.1",
|
|
28
|
+
"uglifyjs-webpack-plugin": "^2.2.0",
|
|
29
|
+
"webpack": "^5.64.2",
|
|
30
|
+
"webpack-cli": "^4.9.1"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"trtc-sdk-v5": "^5.15.1",
|
|
34
|
+
"xdriver": "^1.1.1"
|
|
35
|
+
}
|
|
36
|
+
}
|