@vkontakte/media-streaming 1.0.1-dev.2e8c6c4.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,760 @@
1
+ /**
2
+ * @vkontakte/media-streaming v1.0.1-dev.2e8c6c4.0
3
+ * Wed, 05 Aug 2026 12:35:22 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.2e8c6c4.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
+ this.#messagesQueue = [];
542
+ this.#connected = false;
543
+ this.#ws = undefined;
544
+ this.tracerLog("disconnected", { code: event.code });
545
+ this.callback("onDisconnected", event);
546
+ };
547
+ send(message) {
548
+ this.#messagesQueue.push(message);
549
+ if (this.socketOpened) {
550
+ this.sendMessageFromQueue();
551
+ }
552
+ }
553
+ sendMessageFromQueue() {
554
+ while (this.#messagesQueue.length > 0) {
555
+ if (this.socketOpened) {
556
+ const message = this.#messagesQueue.shift();
557
+ this.#ws?.send(JSON.stringify(message));
558
+ }
559
+ }
560
+ }
561
+ handleStatusMessage(msg) {
562
+ const { status, message } = msg;
563
+ if (status === "INFO" && message === "no_stream_data") {
564
+ this.handleError("no_stream_data" /* NO_STREAM_DATA */, msg);
565
+ }
566
+ }
567
+ handleInviteMessage(message) {
568
+ switch (message.inviteType) {
569
+ case "ANSWER":
570
+ this.handleAnswer(message.sdp);
571
+ break;
572
+ case "CANDIDATE":
573
+ if (message.candidate) {
574
+ this.tracerLog("gotRTCCandidate", { v: message.candidate });
575
+ this.handleCandidate(message.candidate);
576
+ }
577
+ break;
578
+ default:
579
+ break;
580
+ }
581
+ }
582
+ handleAnswer(sdp) {
583
+ this.#peerConnection?.setRemoteDescription(new RTCSessionDescription({ type: "answer", sdp }));
584
+ }
585
+ handleCandidate(candidate) {
586
+ this.#peerConnection?.addIceCandidate(new RTCIceCandidate(candidate));
587
+ }
588
+ handleScreenShareConstraintsError(frameRate) {
589
+ this.tracerLog("error", { type: "constraints" /* CONSTRAINTS */, frameRate });
590
+ this.callback("onError", "constraints" /* CONSTRAINTS */);
591
+ }
592
+ handleError(err, message) {
593
+ if (["no_stream_data" /* NO_STREAM_DATA */, "peerconnection" /* PEERCONNECTION */].includes(err)) {
594
+ this.closePeerConnection();
595
+ this.closeWSConnection();
596
+ }
597
+ const reason = message ? typeof message === "string" ? message : message.text ?? message.message : undefined;
598
+ this.tracerLog("error", { type: err, ...reason && { r: reason } });
599
+ this.callback("onError", err, reason);
600
+ }
601
+ async createPeerConnection() {
602
+ try {
603
+ this.#peerConnection = new RTCPeerConnection({
604
+ iceServers: this.config.stunServers ?? DEFAULT_STUN_SERVERS,
605
+ rtcpMuxPolicy: "require",
606
+ bundlePolicy: "max-compat"
607
+ });
608
+ this.#peerConnection.onicecandidate = this.handleRTCIceCandidate;
609
+ this.#peerConnection.onsignalingstatechange = this.handleRTCSignalingStateChange;
610
+ this.#peerConnection.oniceconnectionstatechange = this.handleRTCIceConnectionStateChange;
611
+ this.#stream?.getTracks().forEach((track) => {
612
+ if (track.kind === "video") {
613
+ track.contentHint = "text";
614
+ }
615
+ this.#stream && this.#peerConnection?.addTrack(track, this.#stream);
616
+ });
617
+ } catch (e) {
618
+ this.handleError("peerconnection" /* PEERCONNECTION */, "create peer connection failed");
619
+ return;
620
+ }
621
+ try {
622
+ const offer = await this.#peerConnection.createOffer();
623
+ if (this.config.lockOrientation) {
624
+ offer.sdp = offer.sdp?.replace(/a=extmap:[0-9]+ urn:3gpp:video-orientation[\r\n]+/g, "");
625
+ }
626
+ await this.setDescription(offer);
627
+ } catch (e) {
628
+ this.handleError("peerconnection" /* PEERCONNECTION */, "set offer description failed");
629
+ }
630
+ }
631
+ handleRTCIceCandidate = ({ candidate }) => {
632
+ if (candidate) {
633
+ this.tracerLog("sendRTCCandidate", { v: candidate.candidate });
634
+ this.send({
635
+ type: "INVITE",
636
+ promoCall: false,
637
+ inviteType: "CANDIDATE",
638
+ candidate
639
+ });
640
+ } else {
641
+ this.send({ type: "INVITE", promoCall: false, inviteType: "CANDIDATE" });
642
+ }
643
+ };
644
+ handleRTCSignalingStateChange = () => {
645
+ if (!this.#peerConnection) {
646
+ return;
647
+ }
648
+ switch (this.#peerConnection.signalingState) {
649
+ case "have-local-offer":
650
+ const sdp = this.#peerConnection.localDescription?.sdp;
651
+ this.send({
652
+ type: "INVITE",
653
+ inviteType: "OFFER",
654
+ promoCall: false,
655
+ streamKey: this.config.streamKey,
656
+ sdp
657
+ });
658
+ break;
659
+ case "closed":
660
+ this.handleError("peerconnection" /* PEERCONNECTION */, "signalling closed");
661
+ break;
662
+ }
663
+ };
664
+ handleRTCIceConnectionStateChange = () => {
665
+ if (!this.#peerConnection) {
666
+ return;
667
+ }
668
+ switch (this.#peerConnection.iceConnectionState) {
669
+ case "failed":
670
+ this.handleError("peerconnection" /* PEERCONNECTION */, "ice connection failed");
671
+ break;
672
+ case "disconnected":
673
+ this.disconnect();
674
+ break;
675
+ case "connected":
676
+ this.#connected = true;
677
+ this.tracerLog("connected");
678
+ this.callback("onConnected");
679
+ break;
680
+ }
681
+ };
682
+ async setDescription(description) {
683
+ try {
684
+ await this.#peerConnection?.setLocalDescription(description);
685
+ } catch (e) {
686
+ this.handleError("peerconnection" /* PEERCONNECTION */, "set local description failed");
687
+ }
688
+ }
689
+ closePeerConnection() {
690
+ if (this.#peerConnection) {
691
+ if (this.#peerConnection.signalingState !== "closed") {
692
+ this.#peerConnection.close();
693
+ }
694
+ this.#peerConnection = undefined;
695
+ }
696
+ }
697
+ closeWSConnection() {
698
+ if (this.#ws && this.#ws.readyState < WebSocket.CLOSING) {
699
+ this.#ws.close(WS_CLOSE_CODE);
700
+ }
701
+ }
702
+ parseMessage(data) {
703
+ try {
704
+ return JSON.parse(data);
705
+ } catch (e) {
706
+ return { type: "EMPTY" };
707
+ }
708
+ }
709
+ tracerLog(event, data) {
710
+ return this.config.tracer?.log(event, flattenObject(data));
711
+ }
712
+ logStat(event, data) {
713
+ this.config.statisticsLogger?.log(event, data);
714
+ }
715
+ }
716
+ __legacyDecorateClassTS([
717
+ WebRTCStreamer.notDestroying
718
+ ], WebRTCStreamer.prototype, "init", null);
719
+ __legacyDecorateClassTS([
720
+ WebRTCStreamer.notDestroying
721
+ ], WebRTCStreamer.prototype, "connect", null);
722
+ __legacyDecorateClassTS([
723
+ WebRTCStreamer.notDestroying
724
+ ], WebRTCStreamer.prototype, "switchCamera", null);
725
+ __legacyDecorateClassTS([
726
+ WebRTCStreamer.notDestroying
727
+ ], WebRTCStreamer.prototype, "switchMicrophone", null);
728
+ __legacyDecorateClassTS([
729
+ WebRTCStreamer.notDestroying
730
+ ], WebRTCStreamer.prototype, "enableScreenShare", null);
731
+ __legacyDecorateClassTS([
732
+ WebRTCStreamer.notDestroying
733
+ ], WebRTCStreamer.prototype, "disableScreenShare", null);
734
+ __legacyDecorateClassTS([
735
+ WebRTCStreamer.notDestroying
736
+ ], WebRTCStreamer.prototype, "switchMediaDevices", null);
737
+ __legacyDecorateClassTS([
738
+ WebRTCStreamer.notDestroying
739
+ ], WebRTCStreamer.prototype, "setMicMuted", null);
740
+ __legacyDecorateClassTS([
741
+ WebRTCStreamer.notDestroying
742
+ ], WebRTCStreamer.prototype, "setCameraEnabled", null);
743
+ __legacyDecorateClassTS([
744
+ WebRTCStreamer.notDestroying
745
+ ], WebRTCStreamer.prototype, "destroy", null);
746
+
747
+ // src/index.ts
748
+ var MediaUtils = { enumerateDevices, getUserStream };
749
+ export {
750
+ WebRTCStreamer,
751
+ VERSION,
752
+ StreamerError,
753
+ PermissionName,
754
+ MediaUtils,
755
+ MediaErrorType
756
+ };
757
+
758
+ //# debugId=BFC93ED0555C2A8A64756E2164756E21
759
+
760
+ //# sourceMappingURL=media-streaming.esm.js.map