@marianmeres/webrtc 1.4.4 → 1.4.5
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/dist/webrtc-manager.js +77 -81
- package/package.json +2 -2
package/dist/webrtc-manager.js
CHANGED
|
@@ -217,7 +217,7 @@ export class WebRTCManager {
|
|
|
217
217
|
return devices.filter((d) => d.kind === "audioinput");
|
|
218
218
|
}
|
|
219
219
|
catch (e) {
|
|
220
|
-
this.#logError("Failed to enumerate devices
|
|
220
|
+
this.#logError("Failed to enumerate devices.", e);
|
|
221
221
|
return [];
|
|
222
222
|
}
|
|
223
223
|
}
|
|
@@ -264,7 +264,7 @@ export class WebRTCManager {
|
|
|
264
264
|
return true;
|
|
265
265
|
}
|
|
266
266
|
catch (e) {
|
|
267
|
-
this.#logError("Failed to switch microphone
|
|
267
|
+
this.#logError("Failed to switch microphone.", e);
|
|
268
268
|
this.#handleError(e);
|
|
269
269
|
return false;
|
|
270
270
|
}
|
|
@@ -275,19 +275,19 @@ export class WebRTCManager {
|
|
|
275
275
|
*/
|
|
276
276
|
async initialize() {
|
|
277
277
|
if (this.state !== WebRTCState.IDLE) {
|
|
278
|
-
this.#logger.debug(
|
|
278
|
+
this.#logger.debug(`Initialization skipped because state is ${this.state}, not IDLE.`);
|
|
279
279
|
return;
|
|
280
280
|
}
|
|
281
|
-
this.#logger.debug("Initializing
|
|
281
|
+
this.#logger.debug("Initializing peer connection.");
|
|
282
282
|
this.#dispatch(WebRTCFsmEvent.INIT);
|
|
283
283
|
try {
|
|
284
284
|
this.#pc = this.#factory.createPeerConnection(this.#config.peerConfig);
|
|
285
|
-
this.#logger.debug("Peer connection created");
|
|
285
|
+
this.#logger.debug("Peer connection created.");
|
|
286
286
|
this.#setupPcListeners();
|
|
287
287
|
// Setup device change detection now that we have a connection
|
|
288
288
|
this.#setupDeviceChangeListener();
|
|
289
289
|
if (this.#config.enableMicrophone) {
|
|
290
|
-
this.#logger.debug("Enabling microphone
|
|
290
|
+
this.#logger.debug("Enabling microphone as per configuration.");
|
|
291
291
|
const success = await this.enableMicrophone(true);
|
|
292
292
|
if (!success) {
|
|
293
293
|
this.#pubsub.publish(WebRTCManager.EVENT_MICROPHONE_FAILED, {
|
|
@@ -299,13 +299,13 @@ export class WebRTCManager {
|
|
|
299
299
|
// Always setup to receive audio, even if we don't enable microphone
|
|
300
300
|
// This ensures the SDP includes audio media line
|
|
301
301
|
this.#pc.addTransceiver("audio", { direction: "recvonly" });
|
|
302
|
-
this.#logger.debug("Added
|
|
302
|
+
this.#logger.debug("Added receive-only audio transceiver.");
|
|
303
303
|
}
|
|
304
304
|
if (this.#config.dataChannelLabel) {
|
|
305
|
-
this.#logger.debug(
|
|
305
|
+
this.#logger.debug(`Creating default data channel '${this.#config.dataChannelLabel}'.`);
|
|
306
306
|
this.createDataChannel(this.#config.dataChannelLabel);
|
|
307
307
|
}
|
|
308
|
-
this.#logger.debug("Initialization complete");
|
|
308
|
+
this.#logger.debug("Initialization complete.");
|
|
309
309
|
}
|
|
310
310
|
catch (e) {
|
|
311
311
|
this.#logError(e);
|
|
@@ -317,15 +317,15 @@ export class WebRTCManager {
|
|
|
317
317
|
* If disconnected, reinitializes the peer connection.
|
|
318
318
|
*/
|
|
319
319
|
async connect() {
|
|
320
|
-
this.#logger.debug(
|
|
320
|
+
this.#logger.debug(`Connect called with current state ${this.state}.`);
|
|
321
321
|
// Initialize if needed
|
|
322
322
|
if (this.state === WebRTCState.IDLE) {
|
|
323
|
-
this.#logger.debug("State is IDLE, initializing first");
|
|
323
|
+
this.#logger.debug("State is IDLE, initializing first.");
|
|
324
324
|
await this.initialize();
|
|
325
325
|
}
|
|
326
326
|
// Reinitialize if disconnected (PeerConnection was closed)
|
|
327
327
|
if (this.state === WebRTCState.DISCONNECTED) {
|
|
328
|
-
this.#logger.debug("State is DISCONNECTED, reinitializing");
|
|
328
|
+
this.#logger.debug("State is DISCONNECTED, reinitializing.");
|
|
329
329
|
// Clean up old connection
|
|
330
330
|
this.#cleanup();
|
|
331
331
|
// Reset to IDLE and reinitialize
|
|
@@ -336,10 +336,10 @@ export class WebRTCManager {
|
|
|
336
336
|
}
|
|
337
337
|
if (this.state === WebRTCState.CONNECTED ||
|
|
338
338
|
this.state === WebRTCState.CONNECTING) {
|
|
339
|
-
this.#logger.debug("Already connected or connecting, skipping");
|
|
339
|
+
this.#logger.debug("Already connected or connecting, skipping.");
|
|
340
340
|
return;
|
|
341
341
|
}
|
|
342
|
-
this.#logger.debug("Transitioning to CONNECTING");
|
|
342
|
+
this.#logger.debug("Transitioning to CONNECTING state.");
|
|
343
343
|
this.#dispatch(WebRTCFsmEvent.CONNECT);
|
|
344
344
|
}
|
|
345
345
|
/**
|
|
@@ -348,19 +348,19 @@ export class WebRTCManager {
|
|
|
348
348
|
* @returns True if successful, false if failed to get user media.
|
|
349
349
|
*/
|
|
350
350
|
async enableMicrophone(enable) {
|
|
351
|
-
this.#logger.debug(
|
|
351
|
+
this.#logger.debug(`Enable microphone called with value ${enable}.`);
|
|
352
352
|
if (enable) {
|
|
353
353
|
if (this.#localStream) {
|
|
354
|
-
this.#logger.debug("Microphone already enabled");
|
|
354
|
+
this.#logger.debug("Microphone is already enabled.");
|
|
355
355
|
return true;
|
|
356
356
|
}
|
|
357
357
|
try {
|
|
358
|
-
this.#logger.debug("Requesting user media
|
|
358
|
+
this.#logger.debug("Requesting user media.");
|
|
359
359
|
const stream = await this.#factory.getUserMedia({
|
|
360
360
|
audio: true,
|
|
361
361
|
video: false,
|
|
362
362
|
});
|
|
363
|
-
this.#logger.debug(
|
|
363
|
+
this.#logger.debug(`User media obtained with ${stream.getAudioTracks().length} track(s).`);
|
|
364
364
|
this.#localStream = stream;
|
|
365
365
|
this.#pubsub.publish(WebRTCManager.EVENT_LOCAL_STREAM, stream);
|
|
366
366
|
if (this.#pc) {
|
|
@@ -373,21 +373,21 @@ export class WebRTCManager {
|
|
|
373
373
|
await audioTransceiver.sender.replaceTrack(track);
|
|
374
374
|
// Update direction to sendrecv
|
|
375
375
|
audioTransceiver.direction = "sendrecv";
|
|
376
|
-
this.#logger.debug("Replaced track in existing transceiver");
|
|
376
|
+
this.#logger.debug("Replaced track in existing transceiver.");
|
|
377
377
|
}
|
|
378
378
|
else {
|
|
379
379
|
// Add track normally
|
|
380
380
|
stream.getTracks().forEach((track) => {
|
|
381
381
|
this.#pc.addTrack(track, stream);
|
|
382
382
|
});
|
|
383
|
-
this.#logger.debug("Added tracks to peer connection");
|
|
383
|
+
this.#logger.debug("Added tracks to peer connection.");
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
|
-
this.#logger.debug("Microphone enabled successfully");
|
|
386
|
+
this.#logger.debug("Microphone enabled successfully.");
|
|
387
387
|
return true;
|
|
388
388
|
}
|
|
389
389
|
catch (e) {
|
|
390
|
-
this.#logError("Failed to get user media
|
|
390
|
+
this.#logError("Failed to get user media.", e);
|
|
391
391
|
this.#pubsub.publish(WebRTCManager.EVENT_MICROPHONE_FAILED, {
|
|
392
392
|
error: e,
|
|
393
393
|
});
|
|
@@ -396,10 +396,10 @@ export class WebRTCManager {
|
|
|
396
396
|
}
|
|
397
397
|
else {
|
|
398
398
|
if (!this.#localStream) {
|
|
399
|
-
this.#logger.debug("Microphone already disabled");
|
|
399
|
+
this.#logger.debug("Microphone is already disabled.");
|
|
400
400
|
return true;
|
|
401
401
|
}
|
|
402
|
-
this.#logger.debug("Disabling microphone
|
|
402
|
+
this.#logger.debug("Disabling microphone.");
|
|
403
403
|
this.#localStream.getTracks().forEach((track) => {
|
|
404
404
|
track.stop();
|
|
405
405
|
// Remove from PC if needed, or just stop sending
|
|
@@ -413,7 +413,7 @@ export class WebRTCManager {
|
|
|
413
413
|
});
|
|
414
414
|
this.#localStream = null;
|
|
415
415
|
this.#pubsub.publish(WebRTCManager.EVENT_LOCAL_STREAM, null);
|
|
416
|
-
this.#logger.debug("Microphone disabled");
|
|
416
|
+
this.#logger.debug("Microphone disabled.");
|
|
417
417
|
return true;
|
|
418
418
|
}
|
|
419
419
|
}
|
|
@@ -422,7 +422,7 @@ export class WebRTCManager {
|
|
|
422
422
|
* Transitions to DISCONNECTED state.
|
|
423
423
|
*/
|
|
424
424
|
disconnect() {
|
|
425
|
-
this.#logger.debug("
|
|
425
|
+
this.#logger.debug("Disconnect called.");
|
|
426
426
|
this.#cleanup();
|
|
427
427
|
this.#dispatch(WebRTCFsmEvent.DISCONNECT);
|
|
428
428
|
}
|
|
@@ -431,7 +431,7 @@ export class WebRTCManager {
|
|
|
431
431
|
* Cleans up all resources and allows reinitialization.
|
|
432
432
|
*/
|
|
433
433
|
reset() {
|
|
434
|
-
this.#logger.debug(
|
|
434
|
+
this.#logger.debug(`Reset called with current state ${this.state}.`);
|
|
435
435
|
this.#cleanup();
|
|
436
436
|
// Reset from any non-IDLE state
|
|
437
437
|
if (this.state !== WebRTCState.IDLE) {
|
|
@@ -447,7 +447,7 @@ export class WebRTCManager {
|
|
|
447
447
|
this.#dispatch(WebRTCFsmEvent.RESET);
|
|
448
448
|
}
|
|
449
449
|
}
|
|
450
|
-
this.#logger.debug(
|
|
450
|
+
this.#logger.debug(`Reset complete, state is now ${this.state}.`);
|
|
451
451
|
}
|
|
452
452
|
/**
|
|
453
453
|
* Creates a new data channel with the specified label.
|
|
@@ -457,20 +457,20 @@ export class WebRTCManager {
|
|
|
457
457
|
* @returns The created data channel, or null if peer connection not initialized.
|
|
458
458
|
*/
|
|
459
459
|
createDataChannel(label, options) {
|
|
460
|
-
this.#logger.debug(
|
|
460
|
+
this.#logger.debug(`Create data channel called for '${label}'.`);
|
|
461
461
|
if (!this.#pc) {
|
|
462
|
-
this.#logger.debug("Cannot create data channel
|
|
462
|
+
this.#logger.debug("Cannot create data channel because peer connection is not initialized.");
|
|
463
463
|
return null;
|
|
464
464
|
}
|
|
465
465
|
if (this.#dataChannels.has(label)) {
|
|
466
|
-
this.#logger.debug(
|
|
466
|
+
this.#logger.debug(`Returning existing data channel '${label}'.`);
|
|
467
467
|
return this.#dataChannels.get(label);
|
|
468
468
|
}
|
|
469
469
|
try {
|
|
470
470
|
const dc = this.#pc.createDataChannel(label, options);
|
|
471
471
|
this.#setupDataChannelListeners(dc);
|
|
472
472
|
this.#dataChannels.set(label, dc);
|
|
473
|
-
this.#logger.debug(
|
|
473
|
+
this.#logger.debug(`Data channel '${label}' created.`);
|
|
474
474
|
return dc;
|
|
475
475
|
}
|
|
476
476
|
catch (e) {
|
|
@@ -497,11 +497,11 @@ export class WebRTCManager {
|
|
|
497
497
|
sendData(label, data) {
|
|
498
498
|
const channel = this.#dataChannels.get(label);
|
|
499
499
|
if (!channel) {
|
|
500
|
-
this.#logger.debug(`Data channel '${label}' not found
|
|
500
|
+
this.#logger.debug(`Data channel '${label}' not found.`);
|
|
501
501
|
return false;
|
|
502
502
|
}
|
|
503
503
|
if (channel.readyState !== "open") {
|
|
504
|
-
this.#logger.debug(`Data channel '${label}' is not open
|
|
504
|
+
this.#logger.debug(`Data channel '${label}' is not open, state is ${channel.readyState}.`);
|
|
505
505
|
return false;
|
|
506
506
|
}
|
|
507
507
|
try {
|
|
@@ -522,14 +522,14 @@ export class WebRTCManager {
|
|
|
522
522
|
* @returns The offer SDP, or null if peer connection not initialized.
|
|
523
523
|
*/
|
|
524
524
|
async createOffer(options) {
|
|
525
|
-
this.#logger.debug("
|
|
525
|
+
this.#logger.debug("Create offer called.");
|
|
526
526
|
if (!this.#pc) {
|
|
527
|
-
this.#logger.debug("Cannot create offer
|
|
527
|
+
this.#logger.debug("Cannot create offer because peer connection is not initialized.");
|
|
528
528
|
return null;
|
|
529
529
|
}
|
|
530
530
|
try {
|
|
531
531
|
const offer = await this.#pc.createOffer(options);
|
|
532
|
-
this.#logger.debug(
|
|
532
|
+
this.#logger.debug(`Offer of type '${offer.type}' created.`);
|
|
533
533
|
return offer;
|
|
534
534
|
}
|
|
535
535
|
catch (e) {
|
|
@@ -544,14 +544,14 @@ export class WebRTCManager {
|
|
|
544
544
|
* @returns The answer SDP, or null if peer connection not initialized.
|
|
545
545
|
*/
|
|
546
546
|
async createAnswer(options) {
|
|
547
|
-
this.#logger.debug("
|
|
547
|
+
this.#logger.debug("Create answer called.");
|
|
548
548
|
if (!this.#pc) {
|
|
549
|
-
this.#logger.debug("Cannot create answer
|
|
549
|
+
this.#logger.debug("Cannot create answer because peer connection is not initialized.");
|
|
550
550
|
return null;
|
|
551
551
|
}
|
|
552
552
|
try {
|
|
553
553
|
const answer = await this.#pc.createAnswer(options);
|
|
554
|
-
this.#logger.debug(
|
|
554
|
+
this.#logger.debug(`Answer of type '${answer.type}' created.`);
|
|
555
555
|
return answer;
|
|
556
556
|
}
|
|
557
557
|
catch (e) {
|
|
@@ -566,14 +566,14 @@ export class WebRTCManager {
|
|
|
566
566
|
* @returns True if successful, false otherwise.
|
|
567
567
|
*/
|
|
568
568
|
async setLocalDescription(description) {
|
|
569
|
-
this.#logger.debug(
|
|
569
|
+
this.#logger.debug(`Set local description called with type '${description.type}'.`);
|
|
570
570
|
if (!this.#pc) {
|
|
571
|
-
this.#logger.debug("Cannot set local description
|
|
571
|
+
this.#logger.debug("Cannot set local description because peer connection is not initialized.");
|
|
572
572
|
return false;
|
|
573
573
|
}
|
|
574
574
|
try {
|
|
575
575
|
await this.#pc.setLocalDescription(description);
|
|
576
|
-
this.#logger.debug("Local description set successfully");
|
|
576
|
+
this.#logger.debug("Local description set successfully.");
|
|
577
577
|
return true;
|
|
578
578
|
}
|
|
579
579
|
catch (e) {
|
|
@@ -588,14 +588,14 @@ export class WebRTCManager {
|
|
|
588
588
|
* @returns True if successful, false otherwise.
|
|
589
589
|
*/
|
|
590
590
|
async setRemoteDescription(description) {
|
|
591
|
-
this.#logger.debug(
|
|
591
|
+
this.#logger.debug(`Set remote description called with type '${description.type}'.`);
|
|
592
592
|
if (!this.#pc) {
|
|
593
|
-
this.#logger.debug("Cannot set remote description
|
|
593
|
+
this.#logger.debug("Cannot set remote description because peer connection is not initialized.");
|
|
594
594
|
return false;
|
|
595
595
|
}
|
|
596
596
|
try {
|
|
597
597
|
await this.#pc.setRemoteDescription(description);
|
|
598
|
-
this.#logger.debug("Remote description set successfully");
|
|
598
|
+
this.#logger.debug("Remote description set successfully.");
|
|
599
599
|
return true;
|
|
600
600
|
}
|
|
601
601
|
catch (e) {
|
|
@@ -610,15 +610,15 @@ export class WebRTCManager {
|
|
|
610
610
|
* @returns True if successful, false otherwise.
|
|
611
611
|
*/
|
|
612
612
|
async addIceCandidate(candidate) {
|
|
613
|
-
this.#logger.debug(
|
|
613
|
+
this.#logger.debug(`Add ICE candidate called with ${candidate ? "a candidate" : "null (end of candidates)"}.`);
|
|
614
614
|
if (!this.#pc) {
|
|
615
|
-
this.#logger.debug("Cannot add ICE candidate
|
|
615
|
+
this.#logger.debug("Cannot add ICE candidate because peer connection is not initialized.");
|
|
616
616
|
return false;
|
|
617
617
|
}
|
|
618
618
|
try {
|
|
619
619
|
if (candidate) {
|
|
620
620
|
await this.#pc.addIceCandidate(candidate);
|
|
621
|
-
this.#logger.debug("ICE candidate added");
|
|
621
|
+
this.#logger.debug("ICE candidate added successfully.");
|
|
622
622
|
}
|
|
623
623
|
return true;
|
|
624
624
|
}
|
|
@@ -634,15 +634,15 @@ export class WebRTCManager {
|
|
|
634
634
|
* @returns True if successful, false otherwise.
|
|
635
635
|
*/
|
|
636
636
|
async iceRestart() {
|
|
637
|
-
this.#logger.debug("
|
|
637
|
+
this.#logger.debug("ICE restart called.");
|
|
638
638
|
if (!this.#pc) {
|
|
639
|
-
this.#logger.debug("Cannot perform ICE restart
|
|
639
|
+
this.#logger.debug("Cannot perform ICE restart because peer connection is not initialized.");
|
|
640
640
|
return false;
|
|
641
641
|
}
|
|
642
642
|
try {
|
|
643
643
|
const offer = await this.#pc.createOffer({ iceRestart: true });
|
|
644
644
|
await this.#pc.setLocalDescription(offer);
|
|
645
|
-
this.#logger.debug("ICE restart initiated");
|
|
645
|
+
this.#logger.debug("ICE restart initiated.");
|
|
646
646
|
return true;
|
|
647
647
|
}
|
|
648
648
|
catch (e) {
|
|
@@ -664,10 +664,10 @@ export class WebRTCManager {
|
|
|
664
664
|
}
|
|
665
665
|
const pc = this.#pc;
|
|
666
666
|
if (pc.iceGatheringState === "complete") {
|
|
667
|
-
this.#logger.debug("ICE gathering already complete");
|
|
667
|
+
this.#logger.debug("ICE gathering is already complete.");
|
|
668
668
|
return Promise.resolve();
|
|
669
669
|
}
|
|
670
|
-
this.#logger.debug("Waiting for ICE gathering to complete
|
|
670
|
+
this.#logger.debug("Waiting for ICE gathering to complete.");
|
|
671
671
|
return new Promise((resolve, reject) => {
|
|
672
672
|
const timer = setTimeout(() => {
|
|
673
673
|
cleanup();
|
|
@@ -680,7 +680,7 @@ export class WebRTCManager {
|
|
|
680
680
|
};
|
|
681
681
|
const checkState = () => {
|
|
682
682
|
if (pc.iceGatheringState === "complete") {
|
|
683
|
-
this.#logger.debug("ICE gathering complete
|
|
683
|
+
this.#logger.debug("ICE gathering complete via state change.");
|
|
684
684
|
cleanup();
|
|
685
685
|
resolve();
|
|
686
686
|
}
|
|
@@ -688,7 +688,7 @@ export class WebRTCManager {
|
|
|
688
688
|
const handleCandidate = (event) => {
|
|
689
689
|
onCandidate?.(event.candidate);
|
|
690
690
|
if (event.candidate === null) {
|
|
691
|
-
this.#logger.debug("ICE gathering complete
|
|
691
|
+
this.#logger.debug("ICE gathering complete via null candidate.");
|
|
692
692
|
cleanup();
|
|
693
693
|
resolve();
|
|
694
694
|
}
|
|
@@ -733,7 +733,7 @@ export class WebRTCManager {
|
|
|
733
733
|
this.#fsm.transition(event);
|
|
734
734
|
const newState = this.#fsm.state;
|
|
735
735
|
if (oldState !== newState) {
|
|
736
|
-
this.#logger.debug(
|
|
736
|
+
this.#logger.debug(`State transitioned from ${oldState} to ${newState} on event ${event}.`);
|
|
737
737
|
this.#pubsub.publish(WebRTCManager.EVENT_STATE_CHANGE, newState);
|
|
738
738
|
}
|
|
739
739
|
}
|
|
@@ -757,10 +757,10 @@ export class WebRTCManager {
|
|
|
757
757
|
#setupPcListeners() {
|
|
758
758
|
if (!this.#pc)
|
|
759
759
|
return;
|
|
760
|
-
this.#logger.debug("Setting up peer connection listeners");
|
|
760
|
+
this.#logger.debug("Setting up peer connection listeners.");
|
|
761
761
|
this.#pc.onconnectionstatechange = () => {
|
|
762
762
|
const state = this.#pc.connectionState;
|
|
763
|
-
this.#logger.debug(
|
|
763
|
+
this.#logger.debug(`Connection state changed to ${state}.`);
|
|
764
764
|
if (state === "connected") {
|
|
765
765
|
// Only dispatch if in CONNECTING state (FSM can handle CONNECTED event)
|
|
766
766
|
// This guards against late connection success after user has disconnected
|
|
@@ -774,7 +774,7 @@ export class WebRTCManager {
|
|
|
774
774
|
this.#dispatch(WebRTCFsmEvent.CONNECTED);
|
|
775
775
|
}
|
|
776
776
|
else {
|
|
777
|
-
this.#logger.debug(`Ignoring late connection success
|
|
777
|
+
this.#logger.debug(`Ignoring late connection success because current state is ${this.state}.`);
|
|
778
778
|
}
|
|
779
779
|
}
|
|
780
780
|
else if (state === "failed") {
|
|
@@ -791,7 +791,7 @@ export class WebRTCManager {
|
|
|
791
791
|
}
|
|
792
792
|
};
|
|
793
793
|
this.#pc.ontrack = (event) => {
|
|
794
|
-
this.#logger.debug(
|
|
794
|
+
this.#logger.debug(`Remote ${event.track.kind} track received.`);
|
|
795
795
|
if (event.streams && event.streams[0]) {
|
|
796
796
|
this.#remoteStream = event.streams[0];
|
|
797
797
|
this.#pubsub.publish(WebRTCManager.EVENT_REMOTE_STREAM, this.#remoteStream);
|
|
@@ -799,17 +799,17 @@ export class WebRTCManager {
|
|
|
799
799
|
};
|
|
800
800
|
this.#pc.ondatachannel = (event) => {
|
|
801
801
|
const dc = event.channel;
|
|
802
|
-
this.#logger.debug(
|
|
802
|
+
this.#logger.debug(`Remote data channel '${dc.label}' received.`);
|
|
803
803
|
this.#setupDataChannelListeners(dc);
|
|
804
804
|
this.#dataChannels.set(dc.label, dc);
|
|
805
805
|
};
|
|
806
806
|
this.#pc.onicecandidate = (event) => {
|
|
807
|
-
this.#logger.debug(
|
|
807
|
+
this.#logger.debug(`ICE candidate generated: ${event.candidate ? "candidate" : "null (gathering complete)"}.`);
|
|
808
808
|
this.#pubsub.publish(WebRTCManager.EVENT_ICE_CANDIDATE, event.candidate);
|
|
809
809
|
};
|
|
810
810
|
}
|
|
811
811
|
#cleanup() {
|
|
812
|
-
this.#logger.debug("Cleanup started");
|
|
812
|
+
this.#logger.debug("Cleanup started.");
|
|
813
813
|
// Clear any pending reconnect timers
|
|
814
814
|
if (this.#reconnectTimer !== null) {
|
|
815
815
|
clearTimeout(this.#reconnectTimer);
|
|
@@ -834,25 +834,25 @@ export class WebRTCManager {
|
|
|
834
834
|
});
|
|
835
835
|
this.#dataChannels.clear();
|
|
836
836
|
if (dcCount > 0) {
|
|
837
|
-
this.#logger.debug(
|
|
837
|
+
this.#logger.debug(`Closed ${dcCount} data channel(s).`);
|
|
838
838
|
}
|
|
839
839
|
// Stop local stream tracks
|
|
840
840
|
if (this.#localStream) {
|
|
841
841
|
this.#localStream.getTracks().forEach((track) => track.stop());
|
|
842
842
|
this.#localStream = null;
|
|
843
|
-
this.#logger.debug("Local stream stopped");
|
|
843
|
+
this.#logger.debug("Local stream stopped.");
|
|
844
844
|
}
|
|
845
845
|
// Close peer connection
|
|
846
846
|
if (this.#pc) {
|
|
847
847
|
this.#pc.close();
|
|
848
848
|
this.#pc = null;
|
|
849
|
-
this.#logger.debug("Peer connection closed");
|
|
849
|
+
this.#logger.debug("Peer connection closed.");
|
|
850
850
|
}
|
|
851
851
|
this.#remoteStream = null;
|
|
852
|
-
this.#logger.debug("Cleanup complete");
|
|
852
|
+
this.#logger.debug("Cleanup complete.");
|
|
853
853
|
}
|
|
854
854
|
#handleConnectionFailure() {
|
|
855
|
-
this.#logger.debug("Handling connection failure");
|
|
855
|
+
this.#logger.debug("Handling connection failure.");
|
|
856
856
|
// Only dispatch DISCONNECT if not already in a terminal state
|
|
857
857
|
if (this.state !== WebRTCState.DISCONNECTED &&
|
|
858
858
|
this.state !== WebRTCState.ERROR &&
|
|
@@ -861,13 +861,13 @@ export class WebRTCManager {
|
|
|
861
861
|
}
|
|
862
862
|
// Check if auto-reconnect is enabled
|
|
863
863
|
if (!this.#config.autoReconnect) {
|
|
864
|
-
this.#logger.debug("Auto-reconnect disabled, not attempting reconnection");
|
|
864
|
+
this.#logger.debug("Auto-reconnect is disabled, not attempting reconnection.");
|
|
865
865
|
return;
|
|
866
866
|
}
|
|
867
867
|
const maxAttempts = this.#config.maxReconnectAttempts ?? 5;
|
|
868
868
|
// Check if we've exceeded max attempts
|
|
869
869
|
if (this.#reconnectAttempts >= maxAttempts) {
|
|
870
|
-
this.#logger.debug(
|
|
870
|
+
this.#logger.debug(`Maximum reconnection attempts (${maxAttempts}) reached.`);
|
|
871
871
|
this.#pubsub.publish(WebRTCManager.EVENT_RECONNECT_FAILED, {
|
|
872
872
|
attempts: this.#reconnectAttempts,
|
|
873
873
|
});
|
|
@@ -884,7 +884,7 @@ export class WebRTCManager {
|
|
|
884
884
|
strategy,
|
|
885
885
|
});
|
|
886
886
|
if (!shouldProceed) {
|
|
887
|
-
this.#logger.debug("Reconnection suppressed by shouldReconnect callback");
|
|
887
|
+
this.#logger.debug("Reconnection suppressed by shouldReconnect callback.");
|
|
888
888
|
return;
|
|
889
889
|
}
|
|
890
890
|
}
|
|
@@ -899,11 +899,7 @@ export class WebRTCManager {
|
|
|
899
899
|
const delay = baseDelay * Math.pow(2, this.#reconnectAttempts - 1);
|
|
900
900
|
// Try ICE restart first (attempts 1-2), then full reconnect
|
|
901
901
|
const strategy = this.#reconnectAttempts <= 2 ? "ice-restart" : "full";
|
|
902
|
-
this.#logger.debug(
|
|
903
|
-
attempt: this.#reconnectAttempts,
|
|
904
|
-
strategy,
|
|
905
|
-
delay: delay + "ms",
|
|
906
|
-
});
|
|
902
|
+
this.#logger.debug(`Attempting reconnection (attempt ${this.#reconnectAttempts}, strategy: ${strategy}, delay: ${delay}ms).`);
|
|
907
903
|
this.#pubsub.publish(WebRTCManager.EVENT_RECONNECTING, {
|
|
908
904
|
attempt: this.#reconnectAttempts,
|
|
909
905
|
strategy,
|
|
@@ -937,13 +933,13 @@ export class WebRTCManager {
|
|
|
937
933
|
this.#fullReconnectTimeoutTimer = null;
|
|
938
934
|
// Only trigger failure if still not connected
|
|
939
935
|
if (this.state !== WebRTCState.CONNECTED) {
|
|
940
|
-
this.#logger.debug("Full reconnection timeout reached, connection not established");
|
|
936
|
+
this.#logger.debug("Full reconnection timeout reached, connection was not established.");
|
|
941
937
|
this.#handleConnectionFailure();
|
|
942
938
|
}
|
|
943
939
|
}, timeout);
|
|
944
940
|
}
|
|
945
941
|
catch (e) {
|
|
946
|
-
this.#logError("Reconnection failed
|
|
942
|
+
this.#logError("Reconnection failed.", e);
|
|
947
943
|
this.#handleConnectionFailure();
|
|
948
944
|
}
|
|
949
945
|
}
|
|
@@ -964,7 +960,7 @@ export class WebRTCManager {
|
|
|
964
960
|
this.#pubsub.publish(WebRTCManager.EVENT_DEVICE_CHANGED, devices);
|
|
965
961
|
}
|
|
966
962
|
catch (e) {
|
|
967
|
-
this.#logError("Error handling device change
|
|
963
|
+
this.#logError("Error handling device change.", e);
|
|
968
964
|
}
|
|
969
965
|
};
|
|
970
966
|
navigator.mediaDevices.addEventListener("devicechange", this.#deviceChangeHandler);
|
|
@@ -988,7 +984,7 @@ export class WebRTCManager {
|
|
|
988
984
|
// Ignore "User-Initiated Abort" errors which occur during intentional close()
|
|
989
985
|
const isUserAbort = error?.error?.message?.includes("User-Initiated Abort");
|
|
990
986
|
if (!isUserAbort) {
|
|
991
|
-
this.#logError("Data channel error
|
|
987
|
+
this.#logError("Data channel error occurred.", error);
|
|
992
988
|
this.#pubsub.publish(WebRTCManager.EVENT_ERROR, error);
|
|
993
989
|
}
|
|
994
990
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/webrtc",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/mod.js",
|
|
6
6
|
"types": "dist/mod.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"author": "Marian Meres",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@marianmeres/fsm": "^2.16.
|
|
16
|
+
"@marianmeres/fsm": "^2.16.4",
|
|
17
17
|
"@marianmeres/pubsub": "^2.4.4"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|