@vkontakte/media-streaming 1.0.1-dev.7c4e0d2.0 → 1.0.1-dev.7e4da8a.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.
@@ -0,0 +1,761 @@
1
+ /**
2
+ * @vkontakte/media-streaming v1.0.1-dev.7e4da8a.0
3
+ * Fri, 07 Aug 2026 10:59:00 GMT
4
+ */
5
+ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
8
+ r = Reflect.decorate(decorators, target, key, desc);
9
+ else
10
+ for (var i = decorators.length - 1;i >= 0; i--)
11
+ if (d = decorators[i])
12
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
13
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
14
+ };
15
+
16
+ // src/MediaError.ts
17
+ class StreamerMediaError extends Error {
18
+ constructor(name, message) {
19
+ super(message);
20
+ this.name = name;
21
+ }
22
+ }
23
+
24
+ // src/constants.ts
25
+ var DEFAULT_STUN_SERVERS = [{ urls: ["stun:videostun.mycdn.me:19302"] }];
26
+ var DEFAULT_WS_SERVER = "wss://ovsu.mycdn.me/publish";
27
+ var WS_CLOSE_CODE = 1000;
28
+ var DEFAULT_FRAME_RATE = 15;
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 ||= {});
35
+ var MediaErrorType;
36
+ ((MediaErrorType2) => {
37
+ MediaErrorType2["UNKNOWN"] = "unknown";
38
+ MediaErrorType2["ACCESS"] = "access";
39
+ MediaErrorType2["PERMISSION"] = "permission";
40
+ MediaErrorType2["DEVICES_NOT_FOUND"] = "devices_not_found";
41
+ MediaErrorType2["DEVICES_NOT_SUPPORTED"] = "devices_not_supported";
42
+ })(MediaErrorType ||= {});
43
+ var StreamerError;
44
+ ((StreamerError2) => {
45
+ StreamerError2["SOCKET"] = "socket";
46
+ StreamerError2["CONNECTION"] = "connection";
47
+ StreamerError2["NO_STREAM_DATA"] = "no_stream_data";
48
+ StreamerError2["PEERCONNECTION"] = "peerconnection";
49
+ StreamerError2["CONSTRAINTS"] = "constraints";
50
+ })(StreamerError ||= {});
51
+
52
+ // src/MediaConstraints.ts
53
+ class MediaConstraints {
54
+ config;
55
+ audio;
56
+ video;
57
+ constructor(config) {
58
+ this.config = config;
59
+ this.audio = false;
60
+ this.video = false;
61
+ const { audio, video } = this.config;
62
+ if (audio) {
63
+ const deviceId = typeof audio === "boolean" ? null : audio.deviceId;
64
+ this.audio = deviceId ? { deviceId: { exact: deviceId } } : true;
65
+ }
66
+ if (video) {
67
+ const videoConfig = typeof video === "boolean" ? {} : video;
68
+ const { minWidth, maxWidth, minHeight, maxHeight, aspectRatio, deviceId, facingMode } = videoConfig;
69
+ this.video = {
70
+ width: { min: minWidth, max: maxWidth || 3840, ideal: 1920 },
71
+ height: { min: minHeight, max: maxHeight || 2160, ideal: 1080 },
72
+ aspectRatio: { ideal: aspectRatio || 16 / 9 }
73
+ };
74
+ if (facingMode) {
75
+ this.video.facingMode = { exact: facingMode };
76
+ } else if (deviceId) {
77
+ this.video.deviceId = { exact: deviceId };
78
+ }
79
+ }
80
+ }
81
+ getNative() {
82
+ return Object.assign({}, { audio: this.audio, video: this.video });
83
+ }
84
+ }
85
+
86
+ // src/utils.ts
87
+ var getWindow = () => {
88
+ return globalThis;
89
+ };
90
+ var getSafariVersion = (userAgent) => {
91
+ const ua = userAgent ?? (typeof navigator !== "undefined" ? navigator.userAgent : "");
92
+ if (!ua) {
93
+ return;
94
+ }
95
+ const isSafari = /webkit/i.test(ua) && /safari/i.test(ua) && !/chrome|crios|chromium|yabrowser|samsungbrowser|opr\/|edg/i.test(ua);
96
+ if (!isSafari) {
97
+ return;
98
+ }
99
+ const versionMatch = ua.match(/Version\/(\d+)/);
100
+ if (!versionMatch) {
101
+ return;
102
+ }
103
+ const version = parseInt(versionMatch[1], 10);
104
+ return isNaN(version) ? undefined : version;
105
+ };
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
+ }
134
+ }
135
+ throw new Error("unknown");
136
+ });
137
+ };
138
+ var getDisplayMedia = (constraints) => getWindow().navigator.mediaDevices.getDisplayMedia(constraints.getNative()).catch((e) => {
139
+ if (e instanceof Error) {
140
+ switch (e.name) {
141
+ case "PermissionDeniedError":
142
+ case "SecurityError":
143
+ case "NotAllowedError":
144
+ throw new Error("permission");
145
+ default:
146
+ throw new Error("access");
147
+ }
148
+ }
149
+ throw new Error("unknown");
150
+ });
151
+ var omitKeys = (data, keys) => {
152
+ return Object.fromEntries(Object.entries(data).filter(([k]) => !keys.includes(k)));
153
+ };
154
+ var serializeTrack = (track) => {
155
+ return {
156
+ on: track.enabled,
157
+ k: track.kind,
158
+ ...track.contentHint && { h: track.contentHint }
159
+ };
160
+ };
161
+ var serializeStream = (stream) => {
162
+ return {
163
+ t: stream.getTracks().map(serializeTrack)
164
+ };
165
+ };
166
+ var serializeConfig = (cfg) => {
167
+ return {
168
+ key: cfg.streamKey,
169
+ ...cfg.audioOnly && { audioOnly: true },
170
+ ...cfg.lockOrientation && { lockOrientation: true },
171
+ ...cfg.mediaConfig && { mediaConfig: cfg.mediaConfig },
172
+ ...cfg.screenShare && { screenShare: omitKeys(cfg.screenShare, ["onEnded"]) }
173
+ };
174
+ };
175
+ var serializeMediaDevices = (devices) => {
176
+ return devices.map((d) => ({
177
+ id: d.deviceId.slice(0, 8),
178
+ kind: d.kind,
179
+ label: d.label
180
+ }));
181
+ };
182
+
183
+ // src/env.ts
184
+ var VERSION = "1.0.1-dev.7e4da8a.0";
185
+ // src/WebRTCStreamer.ts
186
+ import { flattenObject } from "@vkontakte/videoplayer-shared";
187
+
188
+ // src/ScreenConstraints.ts
189
+ class ScreenConstraints extends MediaConstraints {
190
+ constructor({ width, height, frameRate = DEFAULT_FRAME_RATE }) {
191
+ super({
192
+ video: true,
193
+ audio: false
194
+ });
195
+ if (typeof this.video === "object") {
196
+ delete this.video.deviceId;
197
+ delete this.video.aspectRatio;
198
+ delete this.video.frameRate;
199
+ delete this.video.facingMode;
200
+ } else {
201
+ this.video = {};
202
+ }
203
+ this.video.cursor = "motion";
204
+ this.video.width = width;
205
+ this.video.height = height;
206
+ this.video.frameRate = frameRate;
207
+ if (getSafariVersion() === 16) {
208
+ this.video.width = { max: width };
209
+ this.video.height = { max: height };
210
+ }
211
+ }
212
+ }
213
+
214
+ // src/WebRTCStreamer.ts
215
+ class WebRTCStreamer {
216
+ config;
217
+ #messagesQueue = [];
218
+ #ws;
219
+ #peerConnection;
220
+ #stream;
221
+ #screenShareTrack = null;
222
+ #connected = false;
223
+ #destroying = false;
224
+ #cameraId;
225
+ #microphoneId;
226
+ #muted = false;
227
+ static notDestroying(_target, _propertyKey, descriptor) {
228
+ const original = descriptor.value;
229
+ descriptor.value = function(...args) {
230
+ if (this.#destroying) {
231
+ return;
232
+ }
233
+ return original.apply(this, args);
234
+ };
235
+ return descriptor;
236
+ }
237
+ constructor(config) {
238
+ this.config = config;
239
+ }
240
+ callback(name, ...args) {
241
+ const fn = this.config.callbacks[name];
242
+ if (fn && !this.#destroying) {
243
+ fn?.(...args);
244
+ }
245
+ }
246
+ get connected() {
247
+ return this.#connected;
248
+ }
249
+ async init() {
250
+ const { cameras, microphones } = await enumerateDevices();
251
+ this.tracerLog("init", {
252
+ cfg: serializeConfig(this.config),
253
+ d: serializeMediaDevices([...cameras, ...microphones])
254
+ });
255
+ let permissionName = "user_media" /* USER_MEDIA */;
256
+ const { audioOnly, mediaConfig, screenShare } = this.config;
257
+ try {
258
+ this.#stream = await getUserStream(mediaConfig ?? {
259
+ audio: true,
260
+ video: !screenShare && !audioOnly
261
+ });
262
+ if (!audioOnly && screenShare) {
263
+ permissionName = "display_media" /* DISPLAY_MEDIA */;
264
+ const screenShareTrack = await this.createScreenShareStream(screenShare);
265
+ if (screenShareTrack) {
266
+ const { frameRate = DEFAULT_FRAME_RATE } = screenShare;
267
+ try {
268
+ await screenShareTrack.applyConstraints({
269
+ frameRate: { exact: frameRate }
270
+ });
271
+ } catch (e) {
272
+ this.handleScreenShareConstraintsError(frameRate);
273
+ }
274
+ this.#stream.addTrack(screenShareTrack);
275
+ }
276
+ this.#screenShareTrack = screenShareTrack;
277
+ }
278
+ this.tracerLog("streamChanged", serializeStream(this.#stream));
279
+ this.callback("onStreamChanged", this.#stream);
280
+ } catch (err) {
281
+ if (err instanceof StreamerMediaError) {
282
+ this.tracerLog("permissionFailed", { name: permissionName, msg: err.message, code: err.name });
283
+ this.callback("onPermissionFailed", permissionName, err);
284
+ } else {
285
+ this.tracerLog("permissionFailed", { name: permissionName });
286
+ this.callback("onPermissionFailed", permissionName);
287
+ }
288
+ }
289
+ }
290
+ async connect() {
291
+ this.tracerLog("connect");
292
+ this.callback("onConnecting");
293
+ this.openSocket();
294
+ if (!this.#peerConnection) {
295
+ await this.createPeerConnection();
296
+ }
297
+ }
298
+ async switchCamera(deviceId) {
299
+ const { audioOnly } = this.config;
300
+ this.tracerLog("switchCamera", { id: deviceId, audioOnly });
301
+ if (audioOnly) {
302
+ return;
303
+ }
304
+ this.#cameraId = deviceId;
305
+ const newStream = await getUserStream({
306
+ audio: this.#microphoneId ? {
307
+ deviceId: this.#microphoneId
308
+ } : true,
309
+ video: {
310
+ deviceId: this.#cameraId
311
+ }
312
+ });
313
+ this.replaceStream(newStream);
314
+ if (this.#muted) {
315
+ this.setMicMuted(true);
316
+ }
317
+ }
318
+ async switchMicrophone(deviceId) {
319
+ this.tracerLog("switchMicrophone", { id: deviceId });
320
+ this.#microphoneId = deviceId;
321
+ const newStream = await getUserStream({
322
+ audio: {
323
+ deviceId: this.#microphoneId
324
+ },
325
+ video: this.#screenShareTrack ? false : this.#cameraId ? {
326
+ deviceId: this.#cameraId
327
+ } : true
328
+ });
329
+ if (this.#screenShareTrack) {
330
+ newStream.addTrack(this.#screenShareTrack);
331
+ }
332
+ this.replaceStream(newStream, true);
333
+ if (this.#muted) {
334
+ this.setMicMuted(true);
335
+ }
336
+ }
337
+ async enableScreenShare(config) {
338
+ this.tracerLog("enableScreenShare", omitKeys(config, ["onEnded"]));
339
+ const newStream = await getUserStream({
340
+ audio: this.#microphoneId ? { deviceId: this.#microphoneId } : true,
341
+ video: false
342
+ });
343
+ const screenShareTrack = await this.createScreenShareStream(config);
344
+ if (screenShareTrack) {
345
+ const { frameRate = DEFAULT_FRAME_RATE } = config;
346
+ try {
347
+ await screenShareTrack.applyConstraints({
348
+ frameRate: { exact: frameRate }
349
+ });
350
+ } catch (e) {
351
+ this.handleScreenShareConstraintsError(frameRate);
352
+ }
353
+ newStream.addTrack(screenShareTrack);
354
+ }
355
+ this.#screenShareTrack = screenShareTrack;
356
+ this.replaceStream(newStream);
357
+ if (this.#muted) {
358
+ this.setMicMuted(true);
359
+ }
360
+ }
361
+ async disableScreenShare(cameraDeviceId) {
362
+ this.tracerLog("disableScreenShare", cameraDeviceId ? { cid: cameraDeviceId } : undefined);
363
+ if (cameraDeviceId) {
364
+ await this.switchCamera(cameraDeviceId);
365
+ } else {
366
+ const { cameras } = await enumerateDevices();
367
+ const defaultCamera = cameras[0];
368
+ if (defaultCamera) {
369
+ await this.switchCamera(defaultCamera.deviceId);
370
+ }
371
+ }
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
+ }
426
+ setMicMuted(muted) {
427
+ this.tracerLog("setMicMuted", { m: muted });
428
+ this.#muted = muted;
429
+ this.#stream?.getAudioTracks().forEach((track) => {
430
+ track.enabled = !muted;
431
+ });
432
+ }
433
+ setCameraEnabled(enabled) {
434
+ this.tracerLog("setCameraEnabled", { on: enabled });
435
+ this.#stream?.getVideoTracks().forEach((track) => {
436
+ track.enabled = enabled;
437
+ });
438
+ }
439
+ destroy() {
440
+ this.#destroying = true;
441
+ this.tracerLog("destroy");
442
+ this.disconnect();
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;
450
+ }
451
+ }
452
+ disconnect() {
453
+ this.tracerLog("disconnect");
454
+ this.closePeerConnection();
455
+ if (this.socketOpened) {
456
+ this.send({ type: "BYE" });
457
+ this.closeWSConnection();
458
+ }
459
+ }
460
+ replaceStream(stream, keepScreenShare = false) {
461
+ this.#stream?.getTracks().forEach((track) => {
462
+ const isScreenShare = track.contentHint === SCREEN_SHARE_CONTENT_TEXTHINT;
463
+ if (!isScreenShare || !keepScreenShare) {
464
+ track.stop();
465
+ if (isScreenShare) {
466
+ this.#screenShareTrack = null;
467
+ }
468
+ }
469
+ this.#stream?.removeTrack(track);
470
+ });
471
+ this.#stream = stream;
472
+ this.tracerLog("streamChanged", serializeStream(stream));
473
+ this.callback("onStreamChanged", this.#stream);
474
+ this.replaceTracksInPeerConnection(this.#stream);
475
+ }
476
+ replaceTracksInPeerConnection(stream, keepScreenShare = false) {
477
+ if (!this.#peerConnection) {
478
+ return;
479
+ }
480
+ this.#peerConnection.getTransceivers().forEach(({ sender }) => {
481
+ const { track } = sender;
482
+ if (track && (track.contentHint !== SCREEN_SHARE_CONTENT_TEXTHINT || !keepScreenShare)) {
483
+ track.stop();
484
+ }
485
+ stream.getTracks().forEach((newTrack) => {
486
+ if (track?.kind === newTrack.kind) {
487
+ sender.replaceTrack(newTrack).then(() => {}, () => {});
488
+ }
489
+ });
490
+ });
491
+ }
492
+ get socketOpened() {
493
+ return this.#ws?.readyState === WebSocket.OPEN;
494
+ }
495
+ async createScreenShareStream(screenShare) {
496
+ const { width, height, onEnded = null } = screenShare;
497
+ const displayStream = await getDisplayMedia(new ScreenConstraints({ width, height }));
498
+ const screenShareTrack = displayStream ? displayStream.getVideoTracks()[0] : null;
499
+ if (screenShareTrack) {
500
+ screenShareTrack.contentHint = SCREEN_SHARE_CONTENT_TEXTHINT;
501
+ screenShareTrack.onended = () => {
502
+ this.#screenShareTrack = null;
503
+ onEnded?.();
504
+ };
505
+ }
506
+ return screenShareTrack;
507
+ }
508
+ openSocket() {
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;
514
+ }
515
+ }
516
+ handleWSOpen = () => {
517
+ this.sendMessageFromQueue();
518
+ };
519
+ handleWSMessage = (event) => {
520
+ if (this.#destroying || !this.#ws || this.#ws.readyState >= WebSocket.CLOSING) {
521
+ return;
522
+ }
523
+ const message = this.parseMessage(event.data);
524
+ switch (message.type) {
525
+ case "INVITE":
526
+ this.handleInviteMessage(message);
527
+ break;
528
+ case "ERROR":
529
+ this.handleError("socket" /* SOCKET */, message);
530
+ break;
531
+ case "STATUS":
532
+ this.handleStatusMessage(message);
533
+ break;
534
+ }
535
+ };
536
+ handleWSClose = (event) => {
537
+ this.closePeerConnection();
538
+ if (!this.#ws) {
539
+ return;
540
+ }
541
+ const isAbnormalClose = event.code > 1000;
542
+ this.#messagesQueue = [];
543
+ this.#connected = false;
544
+ this.#ws = undefined;
545
+ this.tracerLog("disconnected", { code: event.code });
546
+ this.callback("onDisconnected", isAbnormalClose ? "connection" /* CONNECTION */ : undefined);
547
+ };
548
+ send(message) {
549
+ this.#messagesQueue.push(message);
550
+ if (this.socketOpened) {
551
+ this.sendMessageFromQueue();
552
+ }
553
+ }
554
+ sendMessageFromQueue() {
555
+ while (this.#messagesQueue.length > 0) {
556
+ if (this.socketOpened) {
557
+ const message = this.#messagesQueue.shift();
558
+ this.#ws?.send(JSON.stringify(message));
559
+ }
560
+ }
561
+ }
562
+ handleStatusMessage(msg) {
563
+ const { status, message } = msg;
564
+ if (status === "INFO" && message === "no_stream_data") {
565
+ this.handleError("no_stream_data" /* NO_STREAM_DATA */, msg);
566
+ }
567
+ }
568
+ handleInviteMessage(message) {
569
+ switch (message.inviteType) {
570
+ case "ANSWER":
571
+ this.handleAnswer(message.sdp);
572
+ break;
573
+ case "CANDIDATE":
574
+ if (message.candidate) {
575
+ this.tracerLog("gotRTCCandidate", { v: message.candidate });
576
+ this.handleCandidate(message.candidate);
577
+ }
578
+ break;
579
+ default:
580
+ break;
581
+ }
582
+ }
583
+ handleAnswer(sdp) {
584
+ this.#peerConnection?.setRemoteDescription(new RTCSessionDescription({ type: "answer", sdp }));
585
+ }
586
+ handleCandidate(candidate) {
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 */);
592
+ }
593
+ handleError(err, message) {
594
+ if (["no_stream_data" /* NO_STREAM_DATA */, "peerconnection" /* PEERCONNECTION */].includes(err)) {
595
+ this.closePeerConnection();
596
+ this.closeWSConnection();
597
+ }
598
+ const reason = message ? typeof message === "string" ? message : message.text ?? message.message : undefined;
599
+ this.tracerLog("error", { type: err, ...reason && { r: reason } });
600
+ this.callback("onError", err, reason);
601
+ }
602
+ async createPeerConnection() {
603
+ try {
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();
624
+ if (this.config.lockOrientation) {
625
+ offer.sdp = offer.sdp?.replace(/a=extmap:[0-9]+ urn:3gpp:video-orientation[\r\n]+/g, "");
626
+ }
627
+ await this.setDescription(offer);
628
+ } catch (e) {
629
+ this.handleError("peerconnection" /* PEERCONNECTION */, "set offer description failed");
630
+ }
631
+ }
632
+ handleRTCIceCandidate = ({ candidate }) => {
633
+ if (candidate) {
634
+ this.tracerLog("sendRTCCandidate", { v: candidate.candidate });
635
+ this.send({
636
+ type: "INVITE",
637
+ promoCall: false,
638
+ inviteType: "CANDIDATE",
639
+ candidate
640
+ });
641
+ } else {
642
+ this.send({ type: "INVITE", promoCall: false, inviteType: "CANDIDATE" });
643
+ }
644
+ };
645
+ handleRTCSignalingStateChange = () => {
646
+ if (!this.#peerConnection) {
647
+ return;
648
+ }
649
+ switch (this.#peerConnection.signalingState) {
650
+ case "have-local-offer":
651
+ const sdp = this.#peerConnection.localDescription?.sdp;
652
+ this.send({
653
+ type: "INVITE",
654
+ inviteType: "OFFER",
655
+ promoCall: false,
656
+ streamKey: this.config.streamKey,
657
+ sdp
658
+ });
659
+ break;
660
+ case "closed":
661
+ this.handleError("peerconnection" /* PEERCONNECTION */, "signalling closed");
662
+ break;
663
+ }
664
+ };
665
+ handleRTCIceConnectionStateChange = () => {
666
+ if (!this.#peerConnection) {
667
+ return;
668
+ }
669
+ switch (this.#peerConnection.iceConnectionState) {
670
+ case "failed":
671
+ this.handleError("peerconnection" /* PEERCONNECTION */, "ice connection failed");
672
+ break;
673
+ case "disconnected":
674
+ this.disconnect();
675
+ break;
676
+ case "connected":
677
+ this.#connected = true;
678
+ this.tracerLog("connected");
679
+ this.callback("onConnected");
680
+ break;
681
+ }
682
+ };
683
+ async setDescription(description) {
684
+ try {
685
+ await this.#peerConnection?.setLocalDescription(description);
686
+ } catch (e) {
687
+ this.handleError("peerconnection" /* PEERCONNECTION */, "set local description failed");
688
+ }
689
+ }
690
+ closePeerConnection() {
691
+ if (this.#peerConnection) {
692
+ if (this.#peerConnection.signalingState !== "closed") {
693
+ this.#peerConnection.close();
694
+ }
695
+ this.#peerConnection = undefined;
696
+ }
697
+ }
698
+ closeWSConnection() {
699
+ if (this.#ws && this.#ws.readyState < WebSocket.CLOSING) {
700
+ this.#ws.close(WS_CLOSE_CODE);
701
+ }
702
+ }
703
+ parseMessage(data) {
704
+ try {
705
+ return JSON.parse(data);
706
+ } catch (e) {
707
+ return { type: "EMPTY" };
708
+ }
709
+ }
710
+ tracerLog(event, data) {
711
+ return this.config.tracer?.log(event, flattenObject(data));
712
+ }
713
+ logStat(event, data) {
714
+ this.config.statisticsLogger?.log(event, data);
715
+ }
716
+ }
717
+ __legacyDecorateClassTS([
718
+ WebRTCStreamer.notDestroying
719
+ ], WebRTCStreamer.prototype, "init", null);
720
+ __legacyDecorateClassTS([
721
+ WebRTCStreamer.notDestroying
722
+ ], WebRTCStreamer.prototype, "connect", null);
723
+ __legacyDecorateClassTS([
724
+ WebRTCStreamer.notDestroying
725
+ ], WebRTCStreamer.prototype, "switchCamera", null);
726
+ __legacyDecorateClassTS([
727
+ WebRTCStreamer.notDestroying
728
+ ], WebRTCStreamer.prototype, "switchMicrophone", null);
729
+ __legacyDecorateClassTS([
730
+ WebRTCStreamer.notDestroying
731
+ ], WebRTCStreamer.prototype, "enableScreenShare", null);
732
+ __legacyDecorateClassTS([
733
+ WebRTCStreamer.notDestroying
734
+ ], WebRTCStreamer.prototype, "disableScreenShare", null);
735
+ __legacyDecorateClassTS([
736
+ WebRTCStreamer.notDestroying
737
+ ], WebRTCStreamer.prototype, "switchMediaDevices", null);
738
+ __legacyDecorateClassTS([
739
+ WebRTCStreamer.notDestroying
740
+ ], WebRTCStreamer.prototype, "setMicMuted", null);
741
+ __legacyDecorateClassTS([
742
+ WebRTCStreamer.notDestroying
743
+ ], WebRTCStreamer.prototype, "setCameraEnabled", null);
744
+ __legacyDecorateClassTS([
745
+ WebRTCStreamer.notDestroying
746
+ ], WebRTCStreamer.prototype, "destroy", null);
747
+
748
+ // src/index.ts
749
+ var MediaUtils = { enumerateDevices, getUserStream };
750
+ export {
751
+ WebRTCStreamer,
752
+ VERSION,
753
+ StreamerError,
754
+ PermissionName,
755
+ MediaUtils,
756
+ MediaErrorType
757
+ };
758
+
759
+ //# debugId=679410FE5FB9A5CE64756E2164756E21
760
+
761
+ //# sourceMappingURL=media-streaming.esm.js.map