@vkontakte/media-streaming 1.0.1-dev.e965d10.0 → 1.0.1
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/media-streaming.cjs +248 -155
- package/media-streaming.esm.js +248 -155
- package/package.json +1 -1
- package/types/MediaConstraints.d.ts +2 -2
- package/types/MediaError.d.ts +1 -1
- package/types/WebRTCStreamer.d.ts +9 -10
- package/types/index.d.ts +10 -2
- package/types/types.d.ts +13 -8
- package/types/utils.d.ts +6 -3
package/media-streaming.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @vkontakte/media-streaming v1.0.1
|
|
3
|
-
* Mon,
|
|
2
|
+
* @vkontakte/media-streaming v1.0.1
|
|
3
|
+
* Mon, 28 Sep 2026 08:29:49 GMT
|
|
4
4
|
*/
|
|
5
5
|
var __legacyDecorateClassTS = function(decorators, target, key, desc) {
|
|
6
6
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -13,14 +13,25 @@ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
|
|
|
13
13
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
// src/
|
|
17
|
-
|
|
16
|
+
// src/MediaError.ts
|
|
17
|
+
class StreamerMediaError extends Error {
|
|
18
|
+
constructor(name, message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = name;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
// src/constants.ts
|
|
19
25
|
var DEFAULT_STUN_SERVERS = [{ urls: ["stun:videostun.mycdn.me:19302"] }];
|
|
20
26
|
var DEFAULT_WS_SERVER = "wss://ovsu.mycdn.me/publish";
|
|
21
27
|
var WS_CLOSE_CODE = 1000;
|
|
22
28
|
var DEFAULT_FRAME_RATE = 15;
|
|
23
29
|
var SCREEN_SHARE_CONTENT_TEXTHINT = "text";
|
|
30
|
+
var PermissionName;
|
|
31
|
+
((PermissionName2) => {
|
|
32
|
+
PermissionName2["DISPLAY_MEDIA"] = "display_media";
|
|
33
|
+
PermissionName2["USER_MEDIA"] = "user_media";
|
|
34
|
+
})(PermissionName ||= {});
|
|
24
35
|
var MediaErrorType;
|
|
25
36
|
((MediaErrorType2) => {
|
|
26
37
|
MediaErrorType2["UNKNOWN"] = "unknown";
|
|
@@ -37,8 +48,6 @@ var StreamerError;
|
|
|
37
48
|
StreamerError2["PEERCONNECTION"] = "peerconnection";
|
|
38
49
|
StreamerError2["CONSTRAINTS"] = "constraints";
|
|
39
50
|
})(StreamerError ||= {});
|
|
40
|
-
// src/WebRTCStreamer.ts
|
|
41
|
-
import { flattenObject } from "@vkontakte/videoplayer-shared";
|
|
42
51
|
|
|
43
52
|
// src/MediaConstraints.ts
|
|
44
53
|
class MediaConstraints {
|
|
@@ -74,14 +83,6 @@ class MediaConstraints {
|
|
|
74
83
|
}
|
|
75
84
|
}
|
|
76
85
|
|
|
77
|
-
// src/MediaError.ts
|
|
78
|
-
class MediaError2 extends Error {
|
|
79
|
-
constructor(name, message) {
|
|
80
|
-
super(message);
|
|
81
|
-
this.name = name;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
86
|
// src/utils.ts
|
|
86
87
|
var getWindow = () => {
|
|
87
88
|
return globalThis;
|
|
@@ -102,29 +103,38 @@ var getSafariVersion = (userAgent) => {
|
|
|
102
103
|
const version = parseInt(versionMatch[1], 10);
|
|
103
104
|
return isNaN(version) ? undefined : version;
|
|
104
105
|
};
|
|
105
|
-
var enumerateDevices = async () =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
106
|
+
var enumerateDevices = async () => {
|
|
107
|
+
const devices = await getWindow().navigator.mediaDevices.enumerateDevices();
|
|
108
|
+
return {
|
|
109
|
+
cameras: devices.filter((d) => d.kind === "videoinput"),
|
|
110
|
+
microphones: devices.filter((d) => d.kind === "audioinput")
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
var getUserStream = (config) => {
|
|
114
|
+
const constraints = new MediaConstraints(config).getNative();
|
|
115
|
+
return getWindow().navigator.mediaDevices.getUserMedia(constraints).catch((e) => {
|
|
116
|
+
if (e instanceof Error) {
|
|
117
|
+
switch (e.name) {
|
|
118
|
+
case "PermissionDismissedError":
|
|
119
|
+
case "PermissionDeniedError":
|
|
120
|
+
case "SecurityError":
|
|
121
|
+
case "NotAllowedError":
|
|
122
|
+
case "DOMException":
|
|
123
|
+
throw new StreamerMediaError(e.name, "permission" /* PERMISSION */);
|
|
124
|
+
case "OverconstrainedError":
|
|
125
|
+
case "NotFoundError":
|
|
126
|
+
case "TypeError":
|
|
127
|
+
throw new StreamerMediaError(e.name, "unknown" /* UNKNOWN */);
|
|
128
|
+
case "AbortError":
|
|
129
|
+
case "NotReadableError":
|
|
130
|
+
throw new StreamerMediaError(e.name, "access" /* ACCESS */);
|
|
131
|
+
default:
|
|
132
|
+
throw new StreamerMediaError(e.name, "unknown" /* UNKNOWN */);
|
|
133
|
+
}
|
|
124
134
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
135
|
+
throw new Error("unknown");
|
|
136
|
+
});
|
|
137
|
+
};
|
|
128
138
|
var getDisplayMedia = (constraints) => getWindow().navigator.mediaDevices.getDisplayMedia(constraints.getNative()).catch((e) => {
|
|
129
139
|
if (e instanceof Error) {
|
|
130
140
|
switch (e.name) {
|
|
@@ -158,7 +168,7 @@ var serializeConfig = (cfg) => {
|
|
|
158
168
|
key: cfg.streamKey,
|
|
159
169
|
...cfg.audioOnly && { audioOnly: true },
|
|
160
170
|
...cfg.lockOrientation && { lockOrientation: true },
|
|
161
|
-
...cfg.
|
|
171
|
+
...cfg.mediaConfig && { mediaConfig: cfg.mediaConfig },
|
|
162
172
|
...cfg.screenShare && { screenShare: omitKeys(cfg.screenShare, ["onEnded"]) }
|
|
163
173
|
};
|
|
164
174
|
};
|
|
@@ -170,6 +180,11 @@ var serializeMediaDevices = (devices) => {
|
|
|
170
180
|
}));
|
|
171
181
|
};
|
|
172
182
|
|
|
183
|
+
// src/env.ts
|
|
184
|
+
var VERSION = "1.0.1";
|
|
185
|
+
// src/WebRTCStreamer.ts
|
|
186
|
+
import { flattenObject } from "@vkontakte/videoplayer-shared";
|
|
187
|
+
|
|
173
188
|
// src/ScreenConstraints.ts
|
|
174
189
|
class ScreenConstraints extends MediaConstraints {
|
|
175
190
|
constructor({ width, height, frameRate = DEFAULT_FRAME_RATE }) {
|
|
@@ -199,20 +214,20 @@ class ScreenConstraints extends MediaConstraints {
|
|
|
199
214
|
// src/WebRTCStreamer.ts
|
|
200
215
|
class WebRTCStreamer {
|
|
201
216
|
config;
|
|
202
|
-
messagesQueue = [];
|
|
203
|
-
ws;
|
|
204
|
-
peerConnection;
|
|
205
|
-
stream;
|
|
206
|
-
screenShareTrack = null;
|
|
207
|
-
connected = false;
|
|
208
|
-
destroying = false;
|
|
209
|
-
cameraId;
|
|
210
|
-
microphoneId;
|
|
211
|
-
muted = false;
|
|
217
|
+
#messagesQueue = [];
|
|
218
|
+
#ws;
|
|
219
|
+
#peerConnection;
|
|
220
|
+
#stream;
|
|
221
|
+
#screenShareTrack = null;
|
|
222
|
+
#connected = false;
|
|
223
|
+
#destroying = false;
|
|
224
|
+
#cameraId;
|
|
225
|
+
#microphoneId;
|
|
226
|
+
#muted = false;
|
|
212
227
|
static notDestroying(_target, _propertyKey, descriptor) {
|
|
213
228
|
const original = descriptor.value;
|
|
214
229
|
descriptor.value = function(...args) {
|
|
215
|
-
if (this
|
|
230
|
+
if (this.#destroying) {
|
|
216
231
|
return;
|
|
217
232
|
}
|
|
218
233
|
return original.apply(this, args);
|
|
@@ -224,23 +239,26 @@ class WebRTCStreamer {
|
|
|
224
239
|
}
|
|
225
240
|
callback(name, ...args) {
|
|
226
241
|
const fn = this.config.callbacks[name];
|
|
227
|
-
if (fn && !this
|
|
242
|
+
if (fn && !this.#destroying) {
|
|
228
243
|
fn?.(...args);
|
|
229
244
|
}
|
|
230
245
|
}
|
|
246
|
+
get connected() {
|
|
247
|
+
return this.#connected;
|
|
248
|
+
}
|
|
231
249
|
async init() {
|
|
250
|
+
const { cameras, microphones } = await enumerateDevices();
|
|
232
251
|
this.tracerLog("init", {
|
|
233
252
|
cfg: serializeConfig(this.config),
|
|
234
|
-
d: serializeMediaDevices(
|
|
253
|
+
d: serializeMediaDevices([...cameras, ...microphones])
|
|
235
254
|
});
|
|
236
255
|
let permissionName = "user_media" /* USER_MEDIA */;
|
|
237
|
-
const { audioOnly,
|
|
256
|
+
const { audioOnly, mediaConfig, screenShare } = this.config;
|
|
238
257
|
try {
|
|
239
|
-
|
|
258
|
+
this.#stream = await getUserStream(mediaConfig ?? {
|
|
240
259
|
audio: true,
|
|
241
260
|
video: !screenShare && !audioOnly
|
|
242
261
|
});
|
|
243
|
-
this.stream = await getUserMedia(constraints.getNative());
|
|
244
262
|
if (!audioOnly && screenShare) {
|
|
245
263
|
permissionName = "display_media" /* DISPLAY_MEDIA */;
|
|
246
264
|
const screenShareTrack = await this.createScreenShareStream(screenShare);
|
|
@@ -251,18 +269,17 @@ class WebRTCStreamer {
|
|
|
251
269
|
frameRate: { exact: frameRate }
|
|
252
270
|
});
|
|
253
271
|
} catch (e) {
|
|
254
|
-
this.
|
|
255
|
-
this.callback("onError", "constraints" /* CONSTRAINTS */);
|
|
272
|
+
this.handleScreenShareConstraintsError(frameRate);
|
|
256
273
|
}
|
|
257
|
-
this
|
|
274
|
+
this.#stream.addTrack(screenShareTrack);
|
|
258
275
|
}
|
|
259
|
-
this
|
|
276
|
+
this.#screenShareTrack = screenShareTrack;
|
|
260
277
|
}
|
|
261
|
-
this.tracerLog("streamChanged", serializeStream(this
|
|
262
|
-
this.callback("onStreamChanged", this
|
|
278
|
+
this.tracerLog("streamChanged", serializeStream(this.#stream));
|
|
279
|
+
this.callback("onStreamChanged", this.#stream);
|
|
263
280
|
} catch (err) {
|
|
264
|
-
if (err instanceof
|
|
265
|
-
this.tracerLog("permissionFailed", { name: permissionName, msg: err.message, code: err.
|
|
281
|
+
if (err instanceof StreamerMediaError) {
|
|
282
|
+
this.tracerLog("permissionFailed", { name: permissionName, msg: err.message, code: err.name });
|
|
266
283
|
this.callback("onPermissionFailed", permissionName, err);
|
|
267
284
|
} else {
|
|
268
285
|
this.tracerLog("permissionFailed", { name: permissionName });
|
|
@@ -272,8 +289,9 @@ class WebRTCStreamer {
|
|
|
272
289
|
}
|
|
273
290
|
async connect() {
|
|
274
291
|
this.tracerLog("connect");
|
|
292
|
+
this.callback("onConnecting");
|
|
275
293
|
this.openSocket();
|
|
276
|
-
if (!this
|
|
294
|
+
if (!this.#peerConnection) {
|
|
277
295
|
await this.createPeerConnection();
|
|
278
296
|
}
|
|
279
297
|
}
|
|
@@ -283,59 +301,60 @@ class WebRTCStreamer {
|
|
|
283
301
|
if (audioOnly) {
|
|
284
302
|
return;
|
|
285
303
|
}
|
|
286
|
-
this
|
|
287
|
-
const
|
|
288
|
-
audio: this
|
|
289
|
-
deviceId: this
|
|
304
|
+
this.#cameraId = deviceId;
|
|
305
|
+
const newStream = await getUserStream({
|
|
306
|
+
audio: this.#microphoneId ? {
|
|
307
|
+
deviceId: this.#microphoneId
|
|
290
308
|
} : true,
|
|
291
309
|
video: {
|
|
292
|
-
deviceId: this
|
|
310
|
+
deviceId: this.#cameraId
|
|
293
311
|
}
|
|
294
312
|
});
|
|
295
|
-
const newStream = await getUserMedia(constraints.getNative());
|
|
296
313
|
this.replaceStream(newStream);
|
|
297
|
-
if (this
|
|
314
|
+
if (this.#muted) {
|
|
298
315
|
this.setMicMuted(true);
|
|
299
316
|
}
|
|
300
317
|
}
|
|
301
318
|
async switchMicrophone(deviceId) {
|
|
302
319
|
this.tracerLog("switchMicrophone", { id: deviceId });
|
|
303
|
-
this
|
|
304
|
-
const
|
|
320
|
+
this.#microphoneId = deviceId;
|
|
321
|
+
const newStream = await getUserStream({
|
|
305
322
|
audio: {
|
|
306
|
-
deviceId: this
|
|
323
|
+
deviceId: this.#microphoneId
|
|
307
324
|
},
|
|
308
|
-
video: this
|
|
309
|
-
deviceId: this
|
|
325
|
+
video: this.#screenShareTrack ? false : this.#cameraId ? {
|
|
326
|
+
deviceId: this.#cameraId
|
|
310
327
|
} : true
|
|
311
328
|
});
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
newStream.addTrack(this.screenShareTrack);
|
|
329
|
+
if (this.#screenShareTrack) {
|
|
330
|
+
newStream.addTrack(this.#screenShareTrack);
|
|
315
331
|
}
|
|
316
332
|
this.replaceStream(newStream, true);
|
|
317
|
-
if (this
|
|
333
|
+
if (this.#muted) {
|
|
318
334
|
this.setMicMuted(true);
|
|
319
335
|
}
|
|
320
336
|
}
|
|
321
337
|
async enableScreenShare(config) {
|
|
322
338
|
this.tracerLog("enableScreenShare", omitKeys(config, ["onEnded"]));
|
|
323
|
-
const
|
|
324
|
-
audio: this
|
|
339
|
+
const newStream = await getUserStream({
|
|
340
|
+
audio: this.#microphoneId ? { deviceId: this.#microphoneId } : true,
|
|
325
341
|
video: false
|
|
326
342
|
});
|
|
327
|
-
const newStream = await getUserMedia(constraints.getNative());
|
|
328
343
|
const screenShareTrack = await this.createScreenShareStream(config);
|
|
329
344
|
if (screenShareTrack) {
|
|
330
345
|
const { frameRate = DEFAULT_FRAME_RATE } = config;
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
346
|
+
try {
|
|
347
|
+
await screenShareTrack.applyConstraints({
|
|
348
|
+
frameRate: { exact: frameRate }
|
|
349
|
+
});
|
|
350
|
+
} catch (e) {
|
|
351
|
+
this.handleScreenShareConstraintsError(frameRate);
|
|
352
|
+
}
|
|
334
353
|
newStream.addTrack(screenShareTrack);
|
|
335
354
|
}
|
|
336
|
-
this
|
|
355
|
+
this.#screenShareTrack = screenShareTrack;
|
|
337
356
|
this.replaceStream(newStream);
|
|
338
|
-
if (this
|
|
357
|
+
if (this.#muted) {
|
|
339
358
|
this.setMicMuted(true);
|
|
340
359
|
}
|
|
341
360
|
}
|
|
@@ -344,37 +363,90 @@ class WebRTCStreamer {
|
|
|
344
363
|
if (cameraDeviceId) {
|
|
345
364
|
await this.switchCamera(cameraDeviceId);
|
|
346
365
|
} else {
|
|
347
|
-
const
|
|
348
|
-
const defaultCamera =
|
|
366
|
+
const { cameras } = await enumerateDevices();
|
|
367
|
+
const defaultCamera = cameras[0];
|
|
349
368
|
if (defaultCamera) {
|
|
350
369
|
await this.switchCamera(defaultCamera.deviceId);
|
|
351
370
|
}
|
|
352
371
|
}
|
|
353
372
|
}
|
|
373
|
+
async switchMediaDevices({
|
|
374
|
+
cameraId,
|
|
375
|
+
microphoneId,
|
|
376
|
+
screenShare
|
|
377
|
+
}) {
|
|
378
|
+
if (cameraId === undefined && microphoneId === undefined && screenShare === undefined) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
this.tracerLog("switchMediaDevices", {
|
|
382
|
+
...cameraId !== undefined && { cam: cameraId },
|
|
383
|
+
...microphoneId !== undefined && { mic: microphoneId },
|
|
384
|
+
...screenShare !== undefined && { ss: screenShare === false ? false : omitKeys(screenShare, ["onEnded"]) }
|
|
385
|
+
});
|
|
386
|
+
const { audioOnly } = this.config;
|
|
387
|
+
const changingScreenShare = screenShare !== undefined;
|
|
388
|
+
const keepScreenShare = !changingScreenShare && Boolean(this.#screenShareTrack);
|
|
389
|
+
let audio = this.#microphoneId ? { deviceId: this.#microphoneId } : true;
|
|
390
|
+
let video = this.#cameraId ? { deviceId: this.#cameraId } : true;
|
|
391
|
+
if (microphoneId) {
|
|
392
|
+
this.#microphoneId = microphoneId;
|
|
393
|
+
audio = { deviceId: microphoneId };
|
|
394
|
+
} else if (microphoneId === false) {
|
|
395
|
+
audio = false;
|
|
396
|
+
}
|
|
397
|
+
if (audioOnly || cameraId === false) {
|
|
398
|
+
video = false;
|
|
399
|
+
} else if (cameraId) {
|
|
400
|
+
this.#cameraId = cameraId;
|
|
401
|
+
video = screenShare || keepScreenShare ? false : { deviceId: cameraId };
|
|
402
|
+
}
|
|
403
|
+
const newStream = await getUserStream({ audio, video });
|
|
404
|
+
if (screenShare) {
|
|
405
|
+
const screenShareTrack = await this.createScreenShareStream(screenShare);
|
|
406
|
+
if (screenShareTrack) {
|
|
407
|
+
const { frameRate = DEFAULT_FRAME_RATE } = screenShare;
|
|
408
|
+
try {
|
|
409
|
+
await screenShareTrack.applyConstraints({
|
|
410
|
+
frameRate: { exact: frameRate }
|
|
411
|
+
});
|
|
412
|
+
} catch (e) {
|
|
413
|
+
this.handleScreenShareConstraintsError(frameRate);
|
|
414
|
+
}
|
|
415
|
+
newStream.addTrack(screenShareTrack);
|
|
416
|
+
}
|
|
417
|
+
this.#screenShareTrack = screenShareTrack;
|
|
418
|
+
} else if (keepScreenShare && this.#screenShareTrack) {
|
|
419
|
+
newStream.addTrack(this.#screenShareTrack);
|
|
420
|
+
}
|
|
421
|
+
this.replaceStream(newStream, keepScreenShare);
|
|
422
|
+
if (this.#muted) {
|
|
423
|
+
this.setMicMuted(true);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
354
426
|
setMicMuted(muted) {
|
|
355
427
|
this.tracerLog("setMicMuted", { m: muted });
|
|
356
|
-
this
|
|
357
|
-
this
|
|
428
|
+
this.#muted = muted;
|
|
429
|
+
this.#stream?.getAudioTracks().forEach((track) => {
|
|
358
430
|
track.enabled = !muted;
|
|
359
431
|
});
|
|
360
432
|
}
|
|
361
433
|
setCameraEnabled(enabled) {
|
|
362
434
|
this.tracerLog("setCameraEnabled", { on: enabled });
|
|
363
|
-
this
|
|
435
|
+
this.#stream?.getVideoTracks().forEach((track) => {
|
|
364
436
|
track.enabled = enabled;
|
|
365
437
|
});
|
|
366
438
|
}
|
|
367
439
|
destroy() {
|
|
368
|
-
this
|
|
440
|
+
this.#destroying = true;
|
|
369
441
|
this.tracerLog("destroy");
|
|
370
442
|
this.disconnect();
|
|
371
|
-
this
|
|
372
|
-
this
|
|
373
|
-
this
|
|
374
|
-
if (this
|
|
375
|
-
this
|
|
376
|
-
this
|
|
377
|
-
this
|
|
443
|
+
this.#cameraId = undefined;
|
|
444
|
+
this.#microphoneId = undefined;
|
|
445
|
+
this.#muted = false;
|
|
446
|
+
if (this.#stream) {
|
|
447
|
+
this.#stream.getTracks().forEach((track) => track.stop());
|
|
448
|
+
this.#stream = undefined;
|
|
449
|
+
this.#screenShareTrack = null;
|
|
378
450
|
}
|
|
379
451
|
}
|
|
380
452
|
disconnect() {
|
|
@@ -386,26 +458,26 @@ class WebRTCStreamer {
|
|
|
386
458
|
}
|
|
387
459
|
}
|
|
388
460
|
replaceStream(stream, keepScreenShare = false) {
|
|
389
|
-
this
|
|
461
|
+
this.#stream?.getTracks().forEach((track) => {
|
|
390
462
|
const isScreenShare = track.contentHint === SCREEN_SHARE_CONTENT_TEXTHINT;
|
|
391
463
|
if (!isScreenShare || !keepScreenShare) {
|
|
392
464
|
track.stop();
|
|
393
465
|
if (isScreenShare) {
|
|
394
|
-
this
|
|
466
|
+
this.#screenShareTrack = null;
|
|
395
467
|
}
|
|
396
468
|
}
|
|
397
|
-
this
|
|
469
|
+
this.#stream?.removeTrack(track);
|
|
398
470
|
});
|
|
399
|
-
this
|
|
471
|
+
this.#stream = stream;
|
|
400
472
|
this.tracerLog("streamChanged", serializeStream(stream));
|
|
401
|
-
this.callback("onStreamChanged", this
|
|
402
|
-
this.replaceTracksInPeerConnection(this
|
|
473
|
+
this.callback("onStreamChanged", this.#stream);
|
|
474
|
+
this.replaceTracksInPeerConnection(this.#stream);
|
|
403
475
|
}
|
|
404
476
|
replaceTracksInPeerConnection(stream, keepScreenShare = false) {
|
|
405
|
-
if (!this
|
|
477
|
+
if (!this.#peerConnection) {
|
|
406
478
|
return;
|
|
407
479
|
}
|
|
408
|
-
this
|
|
480
|
+
this.#peerConnection.getTransceivers().forEach(({ sender }) => {
|
|
409
481
|
const { track } = sender;
|
|
410
482
|
if (track && (track.contentHint !== SCREEN_SHARE_CONTENT_TEXTHINT || !keepScreenShare)) {
|
|
411
483
|
track.stop();
|
|
@@ -418,7 +490,7 @@ class WebRTCStreamer {
|
|
|
418
490
|
});
|
|
419
491
|
}
|
|
420
492
|
get socketOpened() {
|
|
421
|
-
return this
|
|
493
|
+
return this.#ws?.readyState === WebSocket.OPEN;
|
|
422
494
|
}
|
|
423
495
|
async createScreenShareStream(screenShare) {
|
|
424
496
|
const { width, height, onEnded = null } = screenShare;
|
|
@@ -427,25 +499,25 @@ class WebRTCStreamer {
|
|
|
427
499
|
if (screenShareTrack) {
|
|
428
500
|
screenShareTrack.contentHint = SCREEN_SHARE_CONTENT_TEXTHINT;
|
|
429
501
|
screenShareTrack.onended = () => {
|
|
430
|
-
this
|
|
502
|
+
this.#screenShareTrack = null;
|
|
431
503
|
onEnded?.();
|
|
432
504
|
};
|
|
433
505
|
}
|
|
434
506
|
return screenShareTrack;
|
|
435
507
|
}
|
|
436
508
|
openSocket() {
|
|
437
|
-
if (!this
|
|
438
|
-
this
|
|
439
|
-
this
|
|
440
|
-
this
|
|
441
|
-
this
|
|
509
|
+
if (!this.#ws || this.#ws.readyState === WebSocket.CLOSED) {
|
|
510
|
+
this.#ws = new WebSocket(this.config.serverUrl ?? DEFAULT_WS_SERVER);
|
|
511
|
+
this.#ws.onopen = this.handleWSOpen;
|
|
512
|
+
this.#ws.onmessage = this.handleWSMessage;
|
|
513
|
+
this.#ws.onclose = this.handleWSClose;
|
|
442
514
|
}
|
|
443
515
|
}
|
|
444
516
|
handleWSOpen = () => {
|
|
445
517
|
this.sendMessageFromQueue();
|
|
446
518
|
};
|
|
447
519
|
handleWSMessage = (event) => {
|
|
448
|
-
if (this
|
|
520
|
+
if (this.#destroying || !this.#ws || this.#ws.readyState >= WebSocket.CLOSING) {
|
|
449
521
|
return;
|
|
450
522
|
}
|
|
451
523
|
const message = this.parseMessage(event.data);
|
|
@@ -463,26 +535,27 @@ class WebRTCStreamer {
|
|
|
463
535
|
};
|
|
464
536
|
handleWSClose = (event) => {
|
|
465
537
|
this.closePeerConnection();
|
|
466
|
-
if (!this
|
|
538
|
+
if (!this.#ws) {
|
|
467
539
|
return;
|
|
468
540
|
}
|
|
469
|
-
|
|
470
|
-
this
|
|
471
|
-
this
|
|
541
|
+
const isAbnormalClose = event.code > 1000;
|
|
542
|
+
this.#messagesQueue = [];
|
|
543
|
+
this.#connected = false;
|
|
544
|
+
this.#ws = undefined;
|
|
472
545
|
this.tracerLog("disconnected", { code: event.code });
|
|
473
|
-
this.callback("onDisconnected",
|
|
546
|
+
this.callback("onDisconnected", isAbnormalClose ? "connection" /* CONNECTION */ : undefined);
|
|
474
547
|
};
|
|
475
548
|
send(message) {
|
|
476
|
-
this
|
|
549
|
+
this.#messagesQueue.push(message);
|
|
477
550
|
if (this.socketOpened) {
|
|
478
551
|
this.sendMessageFromQueue();
|
|
479
552
|
}
|
|
480
553
|
}
|
|
481
554
|
sendMessageFromQueue() {
|
|
482
|
-
while (this
|
|
555
|
+
while (this.#messagesQueue.length > 0) {
|
|
483
556
|
if (this.socketOpened) {
|
|
484
|
-
const message = this
|
|
485
|
-
this
|
|
557
|
+
const message = this.#messagesQueue.shift();
|
|
558
|
+
this.#ws?.send(JSON.stringify(message));
|
|
486
559
|
}
|
|
487
560
|
}
|
|
488
561
|
}
|
|
@@ -508,10 +581,14 @@ class WebRTCStreamer {
|
|
|
508
581
|
}
|
|
509
582
|
}
|
|
510
583
|
handleAnswer(sdp) {
|
|
511
|
-
this
|
|
584
|
+
this.#peerConnection?.setRemoteDescription(new RTCSessionDescription({ type: "answer", sdp }));
|
|
512
585
|
}
|
|
513
586
|
handleCandidate(candidate) {
|
|
514
|
-
this
|
|
587
|
+
this.#peerConnection?.addIceCandidate(new RTCIceCandidate(candidate));
|
|
588
|
+
}
|
|
589
|
+
handleScreenShareConstraintsError(frameRate) {
|
|
590
|
+
this.tracerLog("error", { type: "constraints" /* CONSTRAINTS */, frameRate });
|
|
591
|
+
this.callback("onError", "constraints" /* CONSTRAINTS */);
|
|
515
592
|
}
|
|
516
593
|
handleError(err, message) {
|
|
517
594
|
if (["no_stream_data" /* NO_STREAM_DATA */, "peerconnection" /* PEERCONNECTION */].includes(err)) {
|
|
@@ -523,22 +600,27 @@ class WebRTCStreamer {
|
|
|
523
600
|
this.callback("onError", err, reason);
|
|
524
601
|
}
|
|
525
602
|
async createPeerConnection() {
|
|
526
|
-
this.peerConnection = new RTCPeerConnection({
|
|
527
|
-
iceServers: this.config.stunServers ?? DEFAULT_STUN_SERVERS,
|
|
528
|
-
rtcpMuxPolicy: "require",
|
|
529
|
-
bundlePolicy: "max-compat"
|
|
530
|
-
});
|
|
531
|
-
this.peerConnection.onicecandidate = this.handleRTCIceCandidate;
|
|
532
|
-
this.peerConnection.onsignalingstatechange = this.handleRTCSignalingStateChange;
|
|
533
|
-
this.peerConnection.oniceconnectionstatechange = this.handleRTCIceConnectionStateChange;
|
|
534
|
-
this.stream?.getTracks().forEach((track) => {
|
|
535
|
-
if (track.kind === "video") {
|
|
536
|
-
track.contentHint = "text";
|
|
537
|
-
}
|
|
538
|
-
this.stream && this.peerConnection?.addTrack(track, this.stream);
|
|
539
|
-
});
|
|
540
603
|
try {
|
|
541
|
-
|
|
604
|
+
this.#peerConnection = new RTCPeerConnection({
|
|
605
|
+
iceServers: this.config.stunServers ?? DEFAULT_STUN_SERVERS,
|
|
606
|
+
rtcpMuxPolicy: "require",
|
|
607
|
+
bundlePolicy: "max-compat"
|
|
608
|
+
});
|
|
609
|
+
this.#peerConnection.onicecandidate = this.handleRTCIceCandidate;
|
|
610
|
+
this.#peerConnection.onsignalingstatechange = this.handleRTCSignalingStateChange;
|
|
611
|
+
this.#peerConnection.oniceconnectionstatechange = this.handleRTCIceConnectionStateChange;
|
|
612
|
+
this.#stream?.getTracks().forEach((track) => {
|
|
613
|
+
if (track.kind === "video") {
|
|
614
|
+
track.contentHint = "text";
|
|
615
|
+
}
|
|
616
|
+
this.#stream && this.#peerConnection?.addTrack(track, this.#stream);
|
|
617
|
+
});
|
|
618
|
+
} catch (e) {
|
|
619
|
+
this.handleError("peerconnection" /* PEERCONNECTION */, "create peer connection failed");
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
const offer = await this.#peerConnection.createOffer();
|
|
542
624
|
if (this.config.lockOrientation) {
|
|
543
625
|
offer.sdp = offer.sdp?.replace(/a=extmap:[0-9]+ urn:3gpp:video-orientation[\r\n]+/g, "");
|
|
544
626
|
}
|
|
@@ -561,12 +643,12 @@ class WebRTCStreamer {
|
|
|
561
643
|
}
|
|
562
644
|
};
|
|
563
645
|
handleRTCSignalingStateChange = () => {
|
|
564
|
-
if (!this
|
|
646
|
+
if (!this.#peerConnection) {
|
|
565
647
|
return;
|
|
566
648
|
}
|
|
567
|
-
switch (this
|
|
649
|
+
switch (this.#peerConnection.signalingState) {
|
|
568
650
|
case "have-local-offer":
|
|
569
|
-
const sdp = this
|
|
651
|
+
const sdp = this.#peerConnection.localDescription?.sdp;
|
|
570
652
|
this.send({
|
|
571
653
|
type: "INVITE",
|
|
572
654
|
inviteType: "OFFER",
|
|
@@ -581,10 +663,10 @@ class WebRTCStreamer {
|
|
|
581
663
|
}
|
|
582
664
|
};
|
|
583
665
|
handleRTCIceConnectionStateChange = () => {
|
|
584
|
-
if (!this
|
|
666
|
+
if (!this.#peerConnection) {
|
|
585
667
|
return;
|
|
586
668
|
}
|
|
587
|
-
switch (this
|
|
669
|
+
switch (this.#peerConnection.iceConnectionState) {
|
|
588
670
|
case "failed":
|
|
589
671
|
this.handleError("peerconnection" /* PEERCONNECTION */, "ice connection failed");
|
|
590
672
|
break;
|
|
@@ -592,7 +674,7 @@ class WebRTCStreamer {
|
|
|
592
674
|
this.disconnect();
|
|
593
675
|
break;
|
|
594
676
|
case "connected":
|
|
595
|
-
this
|
|
677
|
+
this.#connected = true;
|
|
596
678
|
this.tracerLog("connected");
|
|
597
679
|
this.callback("onConnected");
|
|
598
680
|
break;
|
|
@@ -600,22 +682,22 @@ class WebRTCStreamer {
|
|
|
600
682
|
};
|
|
601
683
|
async setDescription(description) {
|
|
602
684
|
try {
|
|
603
|
-
await this
|
|
685
|
+
await this.#peerConnection?.setLocalDescription(description);
|
|
604
686
|
} catch (e) {
|
|
605
687
|
this.handleError("peerconnection" /* PEERCONNECTION */, "set local description failed");
|
|
606
688
|
}
|
|
607
689
|
}
|
|
608
690
|
closePeerConnection() {
|
|
609
|
-
if (this
|
|
610
|
-
if (this
|
|
611
|
-
this
|
|
691
|
+
if (this.#peerConnection) {
|
|
692
|
+
if (this.#peerConnection.signalingState !== "closed") {
|
|
693
|
+
this.#peerConnection.close();
|
|
612
694
|
}
|
|
613
|
-
this
|
|
695
|
+
this.#peerConnection = undefined;
|
|
614
696
|
}
|
|
615
697
|
}
|
|
616
698
|
closeWSConnection() {
|
|
617
|
-
if (this
|
|
618
|
-
this
|
|
699
|
+
if (this.#ws && this.#ws.readyState < WebSocket.CLOSING) {
|
|
700
|
+
this.#ws.close(WS_CLOSE_CODE);
|
|
619
701
|
}
|
|
620
702
|
}
|
|
621
703
|
parseMessage(data) {
|
|
@@ -628,6 +710,9 @@ class WebRTCStreamer {
|
|
|
628
710
|
tracerLog(event, data) {
|
|
629
711
|
return this.config.tracer?.log(event, flattenObject(data));
|
|
630
712
|
}
|
|
713
|
+
logStat(event, data) {
|
|
714
|
+
this.config.statisticsLogger?.log(event, data);
|
|
715
|
+
}
|
|
631
716
|
}
|
|
632
717
|
__legacyDecorateClassTS([
|
|
633
718
|
WebRTCStreamer.notDestroying
|
|
@@ -647,6 +732,9 @@ __legacyDecorateClassTS([
|
|
|
647
732
|
__legacyDecorateClassTS([
|
|
648
733
|
WebRTCStreamer.notDestroying
|
|
649
734
|
], WebRTCStreamer.prototype, "disableScreenShare", null);
|
|
735
|
+
__legacyDecorateClassTS([
|
|
736
|
+
WebRTCStreamer.notDestroying
|
|
737
|
+
], WebRTCStreamer.prototype, "switchMediaDevices", null);
|
|
650
738
|
__legacyDecorateClassTS([
|
|
651
739
|
WebRTCStreamer.notDestroying
|
|
652
740
|
], WebRTCStreamer.prototype, "setMicMuted", null);
|
|
@@ -656,13 +744,18 @@ __legacyDecorateClassTS([
|
|
|
656
744
|
__legacyDecorateClassTS([
|
|
657
745
|
WebRTCStreamer.notDestroying
|
|
658
746
|
], WebRTCStreamer.prototype, "destroy", null);
|
|
747
|
+
|
|
748
|
+
// src/index.ts
|
|
749
|
+
var MediaUtils = { enumerateDevices, getUserStream };
|
|
659
750
|
export {
|
|
660
751
|
WebRTCStreamer,
|
|
661
752
|
VERSION,
|
|
662
753
|
StreamerError,
|
|
754
|
+
PermissionName,
|
|
755
|
+
MediaUtils,
|
|
663
756
|
MediaErrorType
|
|
664
757
|
};
|
|
665
758
|
|
|
666
|
-
//# debugId=
|
|
759
|
+
//# debugId=1BADA26CFC6F2DA564756E2164756E21
|
|
667
760
|
|
|
668
761
|
//# sourceMappingURL=media-streaming.esm.js.map
|