sample-xmonitor-js 0.0.3 → 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/lib/Monitor.d.ts CHANGED
@@ -8,42 +8,18 @@ export declare interface Setup {
8
8
  */
9
9
  classify?: string;
10
10
  /**
11
- * 其他参数
11
+ * 是否开启调试
12
12
  */
13
- [key: string]: any;
14
- }
15
- export declare interface DefaultSetup extends Setup {
16
- audio?: false;
13
+ debug?: true;
17
14
  /**
18
- * [点击文档](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia)
19
- * 视频配置信息
20
- * min: 最小画像宽度 max 最大画像宽度 ideal: 推荐的理想画质宽度
21
- *
22
- * user: 前置摄像头 environment:后置摄像头
23
- * frameRate: 设置帧率 ideal 推荐帧率 max 最大帧率 受限带宽传输时,低帧率可能更适宜 eg: frameRate: { ideal: 10, max: 15 }
15
+ * 其他参数
24
16
  */
25
- video?: true | {
26
- frameRate: {
27
- ideal: number;
28
- max: number;
29
- };
30
- facingMode: 'user' | 'environment';
31
- width: number | {
32
- min: number;
33
- ideal: number;
34
- max: number;
35
- };
36
- height: number | {
37
- min: number;
38
- ideal: number;
39
- max: number;
40
- };
41
- };
17
+ [key: string]: any;
42
18
  }
43
19
  declare class Watcher {
44
20
  private readonly monitor;
45
21
  private readonly driver;
46
- private readonly name;
22
+ private readonly type;
47
23
  constructor(monitor: Monitor);
48
24
  clean(): Promise<unknown>;
49
25
  shot(sharpness?: number): Promise<any>;
@@ -58,7 +34,16 @@ export declare class ScreenTypeError extends Error {
58
34
  readonly code: string;
59
35
  constructor(message: any, code?: any);
60
36
  }
61
- export type MonitorType = 'default:desktop' | 'default:camera';
37
+ export type Device = 'screen' | 'camera';
38
+ export type Provider = 'default' | 'txrtc';
39
+ export type DeviceProvider = 'default:screen' | 'default:camera' | 'txrtc:screen' | 'txrtc:camera';
40
+ export declare class MonitorType {
41
+ readonly provider: Provider;
42
+ readonly device: Device;
43
+ constructor(provider: Provider, device: Device);
44
+ toString(): string;
45
+ static parse(str: DeviceProvider): MonitorType;
46
+ }
62
47
  /**
63
48
  * cancel 用户取消 不给授权 禁用摄像头
64
49
  * start 开始录制
@@ -68,8 +53,8 @@ export type MonitorType = 'default:desktop' | 'default:camera';
68
53
  * error 发生错误
69
54
  * changed 状态改变
70
55
  */
71
- export type MonitorEvent = 'cancel' | 'start' | 'playing' | 'pause' | 'shot' | 'stop' | 'error' | 'changed';
72
- export declare const MonitorEvents: Array<string>;
56
+ export type MonitorEvent = 'cancel' | 'start' | 'playing' | 'pause' | 'shot' | 'stop' | 'error' | 'changed' | 'message';
57
+ export declare const MonitorEvents: Array<MonitorEvent>;
73
58
  export type MonitorEventObject = {
74
59
  event: MonitorEvent;
75
60
  target: Monitor;
@@ -78,10 +63,7 @@ export type MonitorEventObject = {
78
63
  };
79
64
  export declare class MObject {
80
65
  /**
81
- * 所属活动
82
- */
83
- readonly activity: string;
84
- /**
66
+ /**
85
67
  * 所属房间
86
68
  */
87
69
  readonly room: string;
@@ -90,7 +72,7 @@ export declare class MObject {
90
72
  */
91
73
  readonly id: string;
92
74
  private constructor();
93
- static create({ room, id, activity }: Record<string, any>): MObject;
75
+ static create({ room, id }: Record<string, any>): MObject;
94
76
  toString(): string;
95
77
  }
96
78
  export interface MonitorEventListener {
@@ -100,7 +82,7 @@ export type MonitorOptions = {
100
82
  object: MObject;
101
83
  type: MonitorType;
102
84
  element: HTMLDivElement;
103
- setup?: DefaultSetup;
85
+ setup?: Setup;
104
86
  };
105
87
  /**
106
88
  * 监视器
@@ -119,12 +101,18 @@ export default class Monitor {
119
101
  * 启动监控
120
102
  */
121
103
  start(): any;
104
+ send(message: string): void;
122
105
  /**
123
106
  * 停止监控
124
107
  */
125
108
  stop(): any;
126
109
  addEventListener(event: MonitorEvent, listener: MonitorEventListener): void;
127
110
  protected dispatchEvent(event: MonitorEvent, data?: any, error?: any): void;
111
+ switch(option: {
112
+ deviceId?: string;
113
+ useFrontCamera?: boolean;
114
+ [key: string]: any;
115
+ }): Promise<void>;
128
116
  removeEventListener(event: MonitorEvent): void;
129
117
  }
130
118
  export {};
package/lib/Monitor.js CHANGED
@@ -2,10 +2,10 @@ import Driver from 'xdriver';
2
2
  class Watcher {
3
3
  constructor(monitor) {
4
4
  this.monitor = monitor;
5
- this.name = monitor.type;
5
+ this.type = monitor.type;
6
6
  let driver = new Driver('x-monitor');
7
7
  driver.initialize(() => {
8
- driver.createTables(['desktop', 'camera'], {
8
+ driver.createTables(['screen', 'camera'], {
9
9
  primaryKey: 'id',
10
10
  autoIncrement: true
11
11
  });
@@ -18,7 +18,6 @@ class Watcher {
18
18
  return this.driver.drop();
19
19
  }
20
20
  shot(sharpness = 0.3) {
21
- var _a;
22
21
  if (sharpness > 1)
23
22
  sharpness = 1;
24
23
  if (sharpness < 0)
@@ -42,16 +41,12 @@ class Watcher {
42
41
  img.no = this.monitor.no;
43
42
  img.name = name;
44
43
  img.data = canvas.toDataURL('jpg');
45
- img.type = this.name;
46
- img.classify = (_a = this.monitor.options.setup) === null || _a === void 0 ? void 0 : _a.classify;
47
- let table = this.name;
48
- if (table.indexOf(':') !== -1) {
49
- table = table.split(':')[1];
50
- }
51
- return this.driver.insert(table, [img]).then(res => img);
44
+ img.type = this.type.device;
45
+ img.classify = this.monitor.options.setup?.classify;
46
+ return this.driver.insert(this.type.device, [img]).then(res => img);
52
47
  }
53
48
  select() {
54
- return this.driver.query(this.name);
49
+ return this.driver.query(this.type.device);
55
50
  }
56
51
  }
57
52
  export class NotSupportError extends Error {
@@ -66,18 +61,30 @@ export class ScreenTypeError extends Error {
66
61
  this.code = code;
67
62
  }
68
63
  }
64
+ export class MonitorType {
65
+ constructor(provider, device) {
66
+ this.provider = provider;
67
+ this.device = device;
68
+ }
69
+ toString() {
70
+ return `${this.provider}:${this.device}`;
71
+ }
72
+ static parse(str) {
73
+ let [type, device] = str.split(':');
74
+ return new MonitorType(type, device);
75
+ }
76
+ }
69
77
  export const MonitorEvents = ['cancel', 'start', 'playing', 'pause', 'shot', 'stop', 'error', 'changed'];
70
78
  export class MObject {
71
- constructor(room, id, activity = 'default') {
72
- this.activity = activity;
79
+ constructor(room, id) {
73
80
  this.room = room;
74
81
  this.id = id;
75
82
  }
76
- static create({ room, id, activity }) {
77
- return new MObject(room, id, activity);
83
+ static create({ room, id }) {
84
+ return new MObject(room, id);
78
85
  }
79
86
  toString() {
80
- return `${this.activity}-${this.room}-${this.id}`;
87
+ return `${this.room}-${this.id}`;
81
88
  }
82
89
  }
83
90
  /**
@@ -96,8 +103,7 @@ export default class Monitor {
96
103
  return this.element.paused;
97
104
  }
98
105
  shot() {
99
- var _a;
100
- return this.watcher.shot((_a = this.options.setup) === null || _a === void 0 ? void 0 : _a.sharpness).then(img => {
106
+ return this.watcher.shot(this.options.setup?.sharpness).then(img => {
101
107
  this.dispatchEvent('shot', img);
102
108
  return img;
103
109
  });
@@ -107,6 +113,9 @@ export default class Monitor {
107
113
  */
108
114
  start() {
109
115
  }
116
+ send(message) {
117
+ throw new NotSupportError('不支持发送消息');
118
+ }
110
119
  /**
111
120
  * 停止监控
112
121
  */
@@ -123,6 +132,9 @@ export default class Monitor {
123
132
  }
124
133
  console.log(event, data, error);
125
134
  }
135
+ async switch(option) {
136
+ throw new NotSupportError('不支持设备切换');
137
+ }
126
138
  removeEventListener(event) {
127
139
  this.eventPool.delete(event);
128
140
  }
@@ -0,0 +1,54 @@
1
+ import Monitor, { Device, MObject, MonitorOptions, Setup } from "../Monitor";
2
+ export interface DefaultSetup extends Setup {
3
+ audio?: false;
4
+ /**
5
+ * [点击文档](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia)
6
+ * 视频配置信息
7
+ * min: 最小画像宽度 max 最大画像宽度 ideal: 推荐的理想画质宽度
8
+ *
9
+ * user: 前置摄像头 environment:后置摄像头
10
+ * frameRate: 设置帧率 ideal 推荐帧率 max 最大帧率 受限带宽传输时,低帧率可能更适宜 eg: frameRate: { ideal: 10, max: 15 }
11
+ */
12
+ video?: true | {
13
+ deviceId?: string;
14
+ frameRate?: {
15
+ ideal: number;
16
+ max: number;
17
+ };
18
+ facingMode?: 'user' | 'environment' | {
19
+ exact: "environment";
20
+ };
21
+ width?: number | {
22
+ min: number;
23
+ ideal: number;
24
+ max: number;
25
+ };
26
+ height?: number | {
27
+ min: number;
28
+ ideal: number;
29
+ max: number;
30
+ };
31
+ };
32
+ }
33
+ export default class DefaultMonitor extends Monitor {
34
+ private stream;
35
+ private active;
36
+ constructor(options: MonitorOptions);
37
+ protected getMedia(setup: DefaultSetup): any;
38
+ start(): Promise<any>;
39
+ stop(): Promise<any>;
40
+ static getMonitor(device: Device, object: MObject, element: HTMLDivElement, setup?: Setup): DefaultMonitor;
41
+ }
42
+ export declare class DefaultScreenMonitor extends DefaultMonitor {
43
+ constructor(object: MObject, element: HTMLDivElement, setup?: DefaultSetup);
44
+ start(): Promise<any>;
45
+ protected getMedia(setup: DefaultSetup): any;
46
+ }
47
+ export declare class DefaultCameraMonitor extends DefaultMonitor {
48
+ constructor(object: MObject, element: HTMLDivElement, setup?: DefaultSetup);
49
+ protected getMedia(setup: DefaultSetup): any;
50
+ switch(option: {
51
+ cameraId?: string;
52
+ useFrontCamera?: boolean;
53
+ }): Promise<void>;
54
+ }
@@ -0,0 +1,149 @@
1
+ import Monitor, { MonitorType, NotSupportError, ScreenTypeError } from "../Monitor";
2
+ let win = window;
3
+ const navigator = win.navigator;
4
+ if (!navigator.mediaDevices) {
5
+ navigator["mediaDevices"] = {
6
+ getUserMedia: (constraints) => {
7
+ return navigator.getUserMedia ? new Promise((resolve, reject) => {
8
+ navigator.getUserMedia(constraints, resolve, reject);
9
+ }) : Promise.reject(new NotSupportError('This browser not support getUserMedia'));
10
+ }
11
+ };
12
+ }
13
+ const { mediaDevices } = navigator;
14
+ if (!mediaDevices.getDisplayMedia) {
15
+ mediaDevices.getDisplayMedia = (options) => {
16
+ return Promise.reject(new NotSupportError('This browser not support getDisplayMedia'));
17
+ };
18
+ }
19
+ export default class DefaultMonitor extends Monitor {
20
+ constructor(options) {
21
+ super(options);
22
+ this.active = false;
23
+ }
24
+ getMedia(setup) { }
25
+ async start() {
26
+ if (this.active)
27
+ return;
28
+ let video = document.createElement('video');
29
+ video.setAttribute('autoplay', 'true');
30
+ // 该属性的作用是在ios下video可以播放 否则会显示黑屏
31
+ video.setAttribute('playsinline', '');
32
+ video.style.width = "100%";
33
+ video.style.height = "100%";
34
+ this.options.element?.appendChild(video);
35
+ this.element = video;
36
+ let stream;
37
+ try {
38
+ stream = await this.getMedia({ audio: false, video: true, ...this.options.setup });
39
+ }
40
+ catch (e) {
41
+ /*
42
+ * AbortError[中止错误]
43
+ * 尽管用户和操作系统都授予了访问设备硬件的权利,而且未出现可能抛出NotReadableError异常的硬件问题,但仍然有一些问题的出现导致了设备无法被使用。
44
+ * NotAllowedError[拒绝错误]
45
+ * 用户拒绝了当前的浏览器实例的访问请求;或者用户拒绝了当前会话的访问;或者用户在全局范围内拒绝了所有媒体访问请求。
46
+ * NotFoundError[找不到错误]
47
+ * 找不到满足请求参数的媒体类型。
48
+ * NotReadableError[无法读取错误]
49
+ * 尽管用户已经授权使用相应的设备,操作系统上某个硬件、浏览器或者网页层面发生的错误导致设备无法被访问。
50
+ * OverconstrainedError[无法满足要求错误]
51
+ * 指定的要求无法被设备满足,此异常是一个类型为OverconstrainedError的对象,拥有一个constraint属性,这个属性包含了当前无法被满足的constraint对象,还拥有一个message属性,包含了阅读友好的字符串用来说明情况。
52
+ *SecurityError[安全错误]
53
+ * 在getUserMedia() 被调用的 Document 上面,使用设备媒体被禁止。这个机制是否开启或者关闭取决于单个用户的偏好设置。
54
+ * TypeError[类型错误]
55
+ *constraints 对象未设置[空],或者都被设置为false。
56
+ *
57
+ * */
58
+ if (!(e instanceof NotSupportError)) {
59
+ this.dispatchEvent('error', void 0, e);
60
+ return;
61
+ }
62
+ throw e;
63
+ }
64
+ let { element } = this;
65
+ element.srcObject = stream;
66
+ this.stream = stream;
67
+ let track = stream.getTracks()[0];
68
+ if (this.options.type.device === 'screen' && !/^screen/i.test(track.label)) {
69
+ await this.stop();
70
+ this.dispatchEvent('error', null, new ScreenTypeError('Screen share type error', 'ScreenTypeError'));
71
+ return;
72
+ }
73
+ track.onended = e => {
74
+ this.active = false;
75
+ this.dispatchEvent('cancel');
76
+ };
77
+ element.onloadedmetadata = () => {
78
+ element.play();
79
+ this.dispatchEvent('start');
80
+ };
81
+ element.onplaying = () => {
82
+ this.dispatchEvent('playing');
83
+ this.active = true;
84
+ };
85
+ element.onended = () => {
86
+ this.active = false;
87
+ this.dispatchEvent('stop');
88
+ };
89
+ return track;
90
+ }
91
+ async stop() {
92
+ this.active = false;
93
+ if (this.stream) {
94
+ this.element.pause();
95
+ this.stream.getTracks().forEach((track) => track.stop());
96
+ this.stream = void 0;
97
+ }
98
+ this.element.remove();
99
+ super.stop();
100
+ }
101
+ static getMonitor(device, object, element, setup) {
102
+ if (device === 'screen') {
103
+ return new DefaultScreenMonitor(object, element, setup);
104
+ }
105
+ else if (device === 'camera') {
106
+ return new DefaultCameraMonitor(object, element, setup);
107
+ }
108
+ throw new NotSupportError(`Device ${device} not support`, 'NotSupportError');
109
+ }
110
+ }
111
+ export class DefaultScreenMonitor extends DefaultMonitor {
112
+ constructor(object, element, setup) {
113
+ super({ type: new MonitorType('default', 'screen'), object, element, setup });
114
+ }
115
+ async start() {
116
+ return super.start();
117
+ }
118
+ getMedia(setup) {
119
+ return mediaDevices.getDisplayMedia(setup);
120
+ }
121
+ }
122
+ export class DefaultCameraMonitor extends DefaultMonitor {
123
+ constructor(object, element, setup) {
124
+ super({ type: new MonitorType('default', 'camera'), element, object, setup });
125
+ }
126
+ getMedia(setup) {
127
+ return mediaDevices.getUserMedia(setup);
128
+ }
129
+ async switch(option) {
130
+ await this.stop();
131
+ let video = {
132
+ ...this.options.setup?.video,
133
+ deviceId: option.cameraId
134
+ };
135
+ if (typeof video !== 'boolean' && video) {
136
+ if (option.useFrontCamera === true) {
137
+ video.facingMode = 'user';
138
+ }
139
+ else if (option.useFrontCamera === false) {
140
+ video.facingMode = 'environment';
141
+ }
142
+ }
143
+ this.options.setup = {
144
+ ...this.options.setup,
145
+ video
146
+ };
147
+ await this.start();
148
+ }
149
+ }
package/lib/index.d.ts CHANGED
@@ -1,9 +1,7 @@
1
- import Monitor, { DefaultSetup, MObject, MonitorEvent, MonitorEventListener, MonitorType, Setup } from "./Monitor";
1
+ import Monitor, { DeviceProvider, MObject, MonitorEvent, MonitorEventListener, MonitorType, Setup } from "./Monitor";
2
+ import { DefaultSetup } from "./default";
3
+ import { TxrtcSetup } from "./txrtc";
2
4
  export default class MonitorFactory {
3
- /**
4
- * 监控
5
- */
6
- static INSTANCE: MonitorFactory;
7
5
  private readonly monitorMap;
8
6
  private readonly pointer;
9
7
  private readonly eventPool;
@@ -23,26 +21,26 @@ export default class MonitorFactory {
23
21
  * @param {HTMLDivElement} element 视频所在div元素
24
22
  * @param {any} setup 监视器相关的参数
25
23
  */
26
- init(type: MonitorType, element: HTMLDivElement, setup?: Setup): Monitor;
24
+ init(type: MonitorType, element: HTMLDivElement, setup?: Setup): Promise<Monitor>;
27
25
  /**
28
26
  * 启动所有监视器
29
27
  */
30
- start(interval?: number): Promise<void>;
28
+ start(interval?: number): void;
31
29
  /**
32
30
  * 停用监视器
33
31
  */
34
32
  stop(): Promise<void>;
35
33
  }
34
+ export declare function reset(): void;
36
35
  /**
37
36
  * 初始化监控
38
37
  */
39
38
  export declare function getInstance(options?: {
40
- id: any;
41
- room: any;
42
- activity: any;
39
+ id: string;
40
+ room: string;
43
41
  monitors: Array<{
44
- type: MonitorType;
42
+ provider: DeviceProvider;
45
43
  element: any;
46
- setup?: DefaultSetup;
44
+ setup?: DefaultSetup | Omit<TxrtcSetup, 'roomId' | 'userId'>;
47
45
  }>;
48
46
  }): MonitorFactory;
package/lib/index.js CHANGED
@@ -1,14 +1,6 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { MObject, MonitorEvents } from "./Monitor";
11
- import { CameraMonitor, DesktopMonitor } from "./DefaultMonitor";
1
+ import { MObject, MonitorEvents, MonitorType, NotSupportError } from "./Monitor";
2
+ import DefaultMonitor from "./default";
3
+ import TxrtcMonitor from "./txrtc";
12
4
  export default class MonitorFactory {
13
5
  constructor(pointer) {
14
6
  this.monitorMap = new Map();
@@ -35,93 +27,89 @@ export default class MonitorFactory {
35
27
  * @param {HTMLDivElement} element 视频所在div元素
36
28
  * @param {any} setup 监视器相关的参数
37
29
  */
38
- init(type, element, setup) {
30
+ async init(type, element, setup) {
39
31
  let monitor;
40
- switch (type) {
41
- case "default:camera":
42
- monitor = new CameraMonitor(this.pointer, element, setup);
43
- break;
44
- case "default:desktop":
45
- monitor = new DesktopMonitor(this.pointer, element, setup);
46
- break;
47
- default:
48
- throw new Error(`Not support monitor type ${type}`);
32
+ if (type.provider === 'default') {
33
+ monitor = DefaultMonitor.getMonitor(type.device, this.pointer, element, setup);
49
34
  }
50
- MonitorEvents.forEach((evt) => {
35
+ else if (type.provider === 'txrtc') {
36
+ monitor = TxrtcMonitor.getMonitor(type.device, this.pointer, element, setup);
37
+ }
38
+ else {
39
+ throw new NotSupportError(`不支持的监视器类型${type.toString()}`, 'not_support');
40
+ }
41
+ for (const evt of MonitorEvents) {
51
42
  monitor.addEventListener(evt, event => {
52
- var _a;
53
43
  // 监视器截图
54
- (_a = this.eventPool.get(evt)) === null || _a === void 0 ? void 0 : _a.call(monitor, event);
44
+ this.eventPool.get(evt)?.call(monitor, event);
55
45
  });
56
- });
57
- this.monitorMap.set(type, monitor);
46
+ }
47
+ this.monitorMap.set(type.toString(), monitor);
58
48
  return monitor;
59
49
  }
60
50
  /**
61
51
  * 启动所有监视器
62
52
  */
63
- start() {
64
- return __awaiter(this, arguments, void 0, function* (interval = 10000) {
65
- if (interval < 5000) {
66
- throw new Error('定时捕捉画面时间间隔不能少于5秒');
53
+ start(interval = 10000) {
54
+ if (interval < 5000) {
55
+ throw new Error('定时捕捉画面时间间隔不能少于5秒');
56
+ }
57
+ clearInterval(this.intervalId);
58
+ // 启动timer定时截图
59
+ console.log(`开启定时截图,时间间隔${interval}ms`);
60
+ this.intervalId = setInterval(async () => {
61
+ try {
62
+ // 调用所有监视器截图
63
+ for (let [, monitor] of this.monitorMap) {
64
+ if (monitor.isPaused())
65
+ continue;
66
+ await monitor.shot();
67
+ }
67
68
  }
68
- // 启动所有监视器
69
- for (let [, monitor] of this.monitorMap) {
70
- yield monitor.start();
69
+ catch (e) {
70
+ console.log(e);
71
71
  }
72
- clearInterval(this.intervalId);
73
- // 启动timer定时截图
74
- this.intervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () {
75
- try {
76
- // 调用所有监视器截图
77
- for (let [, monitor] of this.monitorMap) {
78
- if (monitor.isPaused())
79
- continue;
80
- yield monitor.shot();
81
- }
82
- }
83
- catch (e) {
84
- console.log(e);
85
- }
86
- }), interval);
87
- });
72
+ }, interval);
73
+ // 启动所有监视器
74
+ for (let monitor of this.monitorMap.values()) {
75
+ console.log(`启动监视器 >>> ${monitor.type.toString()}`);
76
+ monitor.start();
77
+ }
78
+ console.log('所有监视器启动完成');
88
79
  }
89
80
  /**
90
81
  * 停用监视器
91
82
  */
92
- stop() {
93
- return __awaiter(this, void 0, void 0, function* () {
94
- clearInterval(this.intervalId);
95
- // 启动所有监视器
96
- for (let [, monitor] of this.monitorMap) {
97
- yield monitor.stop();
98
- }
99
- });
83
+ async stop() {
84
+ clearInterval(this.intervalId);
85
+ // 启动所有监视器
86
+ for (let [, monitor] of this.monitorMap) {
87
+ await monitor.stop();
88
+ }
100
89
  }
101
90
  }
91
+ let factory = undefined;
92
+ export function reset() {
93
+ factory?.stop();
94
+ factory = undefined;
95
+ }
102
96
  /**
103
97
  * 初始化监控
104
98
  */
105
99
  export function getInstance(options) {
106
- if (!MonitorFactory.INSTANCE) {
100
+ if (!factory) {
107
101
  if (!options) {
108
102
  throw new Error('实例尚未创建,参数不能为空');
109
103
  }
110
- let factory = new MonitorFactory(MObject.create(options));
111
- const types = [];
112
- for (let { element, type, setup } of options.monitors) {
104
+ factory = new MonitorFactory(MObject.create(options));
105
+ Promise.all(options.monitors.map(({ element, provider = 'default:camera', setup }) => {
113
106
  if (typeof element === 'string') {
114
107
  element = document.getElementById(element);
115
108
  }
116
- if (types.indexOf(type) !== -1) {
117
- throw new Error(`重复的监视器类型 ${type}`);
118
- }
119
- else {
120
- types.push(type);
121
- }
122
- factory.init(type, element, setup);
123
- }
124
- MonitorFactory.INSTANCE = factory;
109
+ return factory.init(MonitorType.parse(provider), element, setup);
110
+ })).then((monitors) => {
111
+ console.log(monitors);
112
+ });
125
113
  }
126
- return MonitorFactory.INSTANCE;
114
+ return factory;
127
115
  }
@@ -0,0 +1,38 @@
1
+ import Monitor, { Device, MObject, MonitorOptions, Setup } from "../Monitor";
2
+ import TRTC, { LocalVideoConfig } from "trtc-sdk-v5";
3
+ export interface TxrtcSetup extends Setup {
4
+ userId: string;
5
+ roomId: string;
6
+ sdkAppId: number;
7
+ userSig: string;
8
+ userDefineRecordId?: string;
9
+ }
10
+ export default class TxrtcMonitor extends Monitor {
11
+ private active;
12
+ protected readonly trtc: TRTC;
13
+ protected readonly setup: TxrtcSetup;
14
+ constructor(options: MonitorOptions);
15
+ start(): Promise<any>;
16
+ initEvent(): Promise<Boolean>;
17
+ /**
18
+ * 发送自定义消息
19
+ * @param message
20
+ */
21
+ send(message: string): void;
22
+ stop(): Promise<any>;
23
+ static getMonitor(device: Device, object: MObject, element: HTMLDivElement, setup?: Setup): TxrtcMonitor;
24
+ }
25
+ export declare class TxrtcScreenMonitor extends TxrtcMonitor {
26
+ constructor(object: MObject, element: HTMLDivElement, setup?: Setup);
27
+ start(): Promise<any>;
28
+ stop(): Promise<any>;
29
+ }
30
+ export declare class TxrtcCameraMonitor extends TxrtcMonitor {
31
+ constructor(object: MObject, element: HTMLDivElement, setup?: Setup);
32
+ start(): Promise<any>;
33
+ /**
34
+ * 切换摄像头
35
+ */
36
+ switch(option: LocalVideoConfig['option']): Promise<void>;
37
+ stop(): Promise<any>;
38
+ }
@@ -0,0 +1,144 @@
1
+ import Monitor, { MonitorType, NotSupportError, ScreenTypeError } from "../Monitor";
2
+ import TRTC, { LOG_LEVEL } from "trtc-sdk-v5";
3
+ import { CDNStreaming, PublishMode } from 'trtc-sdk-v5/plugins/cdn-streaming';
4
+ export default class TxrtcMonitor extends Monitor {
5
+ constructor(options) {
6
+ super(options);
7
+ this.active = false;
8
+ this.setup = options.setup;
9
+ this.trtc = TRTC.create({ plugins: [CDNStreaming] });
10
+ if (this.setup.debug) {
11
+ TRTC.setLogLevel(LOG_LEVEL.DEBUG);
12
+ }
13
+ }
14
+ async start() {
15
+ let streamId = `${this.setup.roomId}_${this.setup.userId}_${this.type.device}`;
16
+ console.debug(`Start ${this.type.device} TxrtcMonitor with streamId ${streamId}`);
17
+ if (this.active)
18
+ return;
19
+ let publishMode = PublishMode.PublishMainStreamToCDN;
20
+ if (this.type.device === 'screen') {
21
+ publishMode = PublishMode.PublishSubStreamToCDN;
22
+ }
23
+ await this.trtc.startPlugin('CDNStreaming', {
24
+ target: { publishMode, streamId }
25
+ });
26
+ let params = {
27
+ userId: this.setup.userId,
28
+ strRoomId: this.setup.roomId,
29
+ userSig: this.setup.userSig,
30
+ sdkAppId: this.setup.sdkAppId
31
+ };
32
+ if (this.setup.userDefineRecordId) {
33
+ params.userDefineRecordId = this.setup.userDefineRecordId;
34
+ }
35
+ await this.trtc.enterRoom(params);
36
+ }
37
+ async initEvent() {
38
+ return new Promise((resolve) => {
39
+ this.trtc.on(TRTC.EVENT.ERROR, (error) => {
40
+ let err = error;
41
+ if (this.options.type.device === 'screen') {
42
+ err = new ScreenTypeError(error.message, 'ScreenTypeError');
43
+ }
44
+ this.dispatchEvent('error', null, err);
45
+ this.stop();
46
+ resolve(false);
47
+ }).on(TRTC.EVENT.SCREEN_SHARE_STOPPED, () => {
48
+ this.dispatchEvent('cancel');
49
+ this.stop();
50
+ }).on(TRTC.EVENT.TRACK, () => {
51
+ +this.dispatchEvent('start');
52
+ this.active = true;
53
+ this.element = this.options.element.querySelector('video');
54
+ resolve(true);
55
+ }).on(TRTC.EVENT.VIDEO_PLAY_STATE_CHANGED, ({ state }) => {
56
+ if (state === 'STOPPED') {
57
+ this.dispatchEvent('stop');
58
+ }
59
+ else if (state === 'PLAYING') {
60
+ this.dispatchEvent('playing');
61
+ }
62
+ }).on(TRTC.EVENT.CUSTOM_MESSAGE, (message) => {
63
+ let decoder = new TextDecoder('utf-8');
64
+ let payload = decoder.decode(message.data);
65
+ this.dispatchEvent('message', payload);
66
+ });
67
+ });
68
+ }
69
+ /**
70
+ * 发送自定义消息
71
+ * @param message
72
+ */
73
+ send(message) {
74
+ this.trtc.sendCustomMessage({
75
+ cmdId: Math.ceil(Math.random() * 100),
76
+ data: new TextEncoder().encode(message).buffer
77
+ });
78
+ }
79
+ async stop() {
80
+ this.active = false;
81
+ await this.trtc.exitRoom();
82
+ await this.trtc.destroy();
83
+ super.stop();
84
+ }
85
+ static getMonitor(device, object, element, setup) {
86
+ if (device === 'screen') {
87
+ return new TxrtcScreenMonitor(object, element, setup);
88
+ }
89
+ else if (device === 'camera') {
90
+ return new TxrtcCameraMonitor(object, element, setup);
91
+ }
92
+ throw new NotSupportError(`Device ${device} not support`, 'NotSupportError');
93
+ }
94
+ }
95
+ export class TxrtcScreenMonitor extends TxrtcMonitor {
96
+ constructor(object, element, setup) {
97
+ super({
98
+ type: new MonitorType('txrtc', 'screen'), object, element, setup: {
99
+ ...setup,
100
+ userId: object.id,
101
+ roomId: object.room
102
+ }
103
+ });
104
+ }
105
+ async start() {
106
+ await super.start();
107
+ await this.trtc.startScreenShare({ publish: true, view: this.options.element });
108
+ await this.initEvent();
109
+ }
110
+ async stop() {
111
+ await this.trtc.stopScreenShare();
112
+ await super.stop();
113
+ }
114
+ }
115
+ export class TxrtcCameraMonitor extends TxrtcMonitor {
116
+ constructor(object, element, setup) {
117
+ super({
118
+ type: new MonitorType('txrtc', 'camera'), element, object, setup: {
119
+ ...setup,
120
+ userId: object.id,
121
+ roomId: object.room
122
+ }
123
+ });
124
+ }
125
+ async start() {
126
+ await super.start();
127
+ await this.trtc.startLocalAudio({ publish: true });
128
+ await this.trtc.startLocalVideo({ publish: true, view: this.options.element });
129
+ await this.initEvent();
130
+ }
131
+ /**
132
+ * 切换摄像头
133
+ */
134
+ async switch(option) {
135
+ if (option?.cameraId || option?.useFrontCamera) {
136
+ await this.trtc.updateLocalVideo({ option });
137
+ }
138
+ }
139
+ async stop() {
140
+ await this.trtc.stopLocalAudio();
141
+ await this.trtc.stopLocalVideo();
142
+ await super.stop();
143
+ }
144
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sample-xmonitor-js",
3
- "version": "0.0.3",
3
+ "version": "1.0.0",
4
4
  "description": "",
5
5
  "main": "./lib/index",
6
6
  "files": [
@@ -21,7 +21,6 @@
21
21
  "author": "xs",
22
22
  "license": "ISC",
23
23
  "devDependencies": {
24
- "@types/node": "^16.11.15",
25
24
  "babel-cli": "^6.26.0",
26
25
  "babel-preset-env": "^1.7.0",
27
26
  "babel-preset-es2015": "^6.24.1",
@@ -31,6 +30,7 @@
31
30
  "webpack-cli": "^4.9.1"
32
31
  },
33
32
  "dependencies": {
33
+ "trtc-sdk-v5": "^5.8.6",
34
34
  "xdriver": "^1.1.1"
35
35
  }
36
36
  }
@@ -1,19 +0,0 @@
1
- import Monitor, { DefaultSetup, MObject, MonitorOptions } from "./Monitor";
2
- declare class DefaultMonitor extends Monitor {
3
- private stream;
4
- private active;
5
- constructor(options: MonitorOptions);
6
- protected getMedia(setup: DefaultSetup): any;
7
- start(): Promise<any>;
8
- stop(): Promise<any>;
9
- }
10
- export declare class DesktopMonitor extends DefaultMonitor {
11
- constructor(object: MObject, element: HTMLDivElement, setup?: DefaultSetup);
12
- start(): Promise<any>;
13
- protected getMedia(setup: DefaultSetup): any;
14
- }
15
- export declare class CameraMonitor extends DefaultMonitor {
16
- constructor(object: MObject, element: HTMLDivElement, setup?: DefaultSetup);
17
- protected getMedia(setup: DefaultSetup): any;
18
- }
19
- export {};
@@ -1,140 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import Monitor, { NotSupportError, ScreenTypeError } from "./Monitor";
11
- let win = window;
12
- const navigator = win.navigator;
13
- if (!navigator.mediaDevices) {
14
- navigator["mediaDevices"] = {
15
- getUserMedia: (constraints) => {
16
- return navigator.getUserMedia ? new Promise((resolve, reject) => {
17
- navigator.getUserMedia(constraints, resolve, reject);
18
- }) : Promise.reject(new NotSupportError('This browser not support getUserMedia'));
19
- }
20
- };
21
- }
22
- const { mediaDevices } = navigator;
23
- if (!mediaDevices.getDisplayMedia) {
24
- mediaDevices.getDisplayMedia = (options) => {
25
- return Promise.reject(new NotSupportError('This browser not support getDisplayMedia'));
26
- };
27
- }
28
- class DefaultMonitor extends Monitor {
29
- constructor(options) {
30
- super(options);
31
- this.active = false;
32
- let video = document.createElement('video');
33
- video.setAttribute('autoplay', 'true');
34
- // 该属性的作用是在ios下video可以播放 否则会显示黑屏
35
- video.setAttribute('playsinline', '');
36
- video.style.width = "100%";
37
- video.style.height = "100%";
38
- options.element.appendChild(video);
39
- this.element = video;
40
- }
41
- getMedia(setup) { }
42
- start() {
43
- return __awaiter(this, void 0, void 0, function* () {
44
- if (this.active)
45
- return;
46
- let stream;
47
- try {
48
- stream = yield this.getMedia(Object.assign({ audio: false, video: true }, this.options.setup));
49
- }
50
- catch (e) {
51
- /*
52
- * AbortError[中止错误]
53
- * 尽管用户和操作系统都授予了访问设备硬件的权利,而且未出现可能抛出NotReadableError异常的硬件问题,但仍然有一些问题的出现导致了设备无法被使用。
54
- * NotAllowedError[拒绝错误]
55
- * 用户拒绝了当前的浏览器实例的访问请求;或者用户拒绝了当前会话的访问;或者用户在全局范围内拒绝了所有媒体访问请求。
56
- * NotFoundError[找不到错误]
57
- * 找不到满足请求参数的媒体类型。
58
- * NotReadableError[无法读取错误]
59
- * 尽管用户已经授权使用相应的设备,操作系统上某个硬件、浏览器或者网页层面发生的错误导致设备无法被访问。
60
- * OverconstrainedError[无法满足要求错误]
61
- * 指定的要求无法被设备满足,此异常是一个类型为OverconstrainedError的对象,拥有一个constraint属性,这个属性包含了当前无法被满足的constraint对象,还拥有一个message属性,包含了阅读友好的字符串用来说明情况。
62
- *SecurityError[安全错误]
63
- * 在getUserMedia() 被调用的 Document 上面,使用设备媒体被禁止。这个机制是否开启或者关闭取决于单个用户的偏好设置。
64
- * TypeError[类型错误]
65
- *constraints 对象未设置[空],或者都被设置为false。
66
- *
67
- * */
68
- if (!(e instanceof NotSupportError)) {
69
- this.dispatchEvent('error', void 0, e);
70
- return;
71
- }
72
- throw e;
73
- }
74
- let { element } = this;
75
- element.srcObject = stream;
76
- this.stream = stream;
77
- let track = stream.getTracks()[0];
78
- if (this.options.type === 'default:desktop' && !/^screen/i.test(track.label)) {
79
- yield this.stop();
80
- this.dispatchEvent('error', null, new ScreenTypeError('Screen share type error', 'ScreenTypeError'));
81
- return;
82
- }
83
- track.onended = e => {
84
- this.active = false;
85
- this.dispatchEvent('cancel');
86
- };
87
- element.onloadedmetadata = () => {
88
- element.play();
89
- this.dispatchEvent('start');
90
- };
91
- element.onplaying = () => {
92
- this.active = true;
93
- };
94
- element.onended = () => {
95
- this.active = false;
96
- this.dispatchEvent('stop');
97
- };
98
- return track;
99
- });
100
- }
101
- stop() {
102
- const _super = Object.create(null, {
103
- stop: { get: () => super.stop }
104
- });
105
- return __awaiter(this, void 0, void 0, function* () {
106
- this.active = false;
107
- if (this.stream) {
108
- this.element.pause();
109
- this.stream.getTracks().forEach((track) => track.stop());
110
- this.stream = void 0;
111
- }
112
- this.element.remove();
113
- _super.stop.call(this);
114
- });
115
- }
116
- }
117
- export class DesktopMonitor extends DefaultMonitor {
118
- constructor(object, element, setup) {
119
- super({ type: 'default:desktop', object, element, setup });
120
- }
121
- start() {
122
- const _super = Object.create(null, {
123
- start: { get: () => super.start }
124
- });
125
- return __awaiter(this, void 0, void 0, function* () {
126
- return _super.start.call(this);
127
- });
128
- }
129
- getMedia(setup) {
130
- return mediaDevices.getDisplayMedia(setup);
131
- }
132
- }
133
- export class CameraMonitor extends DefaultMonitor {
134
- constructor(object, element, setup) {
135
- super({ type: 'default:camera', element, object, setup });
136
- }
137
- getMedia(setup) {
138
- return mediaDevices.getUserMedia(setup);
139
- }
140
- }