@marianmeres/webrtc 1.2.3 → 1.2.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 +102 -91
- package/package.json +2 -2
package/dist/webrtc-manager.js
CHANGED
|
@@ -241,7 +241,7 @@ export class WebRtcManager {
|
|
|
241
241
|
return devices.filter((d) => d.kind === "audioinput");
|
|
242
242
|
}
|
|
243
243
|
catch (e) {
|
|
244
|
-
this.#
|
|
244
|
+
this.#logError("Failed to enumerate devices:", e);
|
|
245
245
|
return [];
|
|
246
246
|
}
|
|
247
247
|
}
|
|
@@ -252,7 +252,7 @@ export class WebRtcManager {
|
|
|
252
252
|
*/
|
|
253
253
|
async switchMicrophone(deviceId) {
|
|
254
254
|
if (!this.#pc || !this.#localStream) {
|
|
255
|
-
this.#
|
|
255
|
+
this.#logError("Cannot switch microphone: not initialized or no active stream");
|
|
256
256
|
return false;
|
|
257
257
|
}
|
|
258
258
|
try {
|
|
@@ -288,8 +288,8 @@ export class WebRtcManager {
|
|
|
288
288
|
return true;
|
|
289
289
|
}
|
|
290
290
|
catch (e) {
|
|
291
|
-
this.#
|
|
292
|
-
this.#
|
|
291
|
+
this.#logError("Failed to switch microphone:", e);
|
|
292
|
+
this.#handleError(e);
|
|
293
293
|
return false;
|
|
294
294
|
}
|
|
295
295
|
}
|
|
@@ -299,19 +299,19 @@ export class WebRtcManager {
|
|
|
299
299
|
*/
|
|
300
300
|
async initialize() {
|
|
301
301
|
if (this.state !== WebRtcState.IDLE) {
|
|
302
|
-
this.#
|
|
302
|
+
this.#logDebug("initialize() called but state is not IDLE:", this.state);
|
|
303
303
|
return;
|
|
304
304
|
}
|
|
305
|
-
this.#
|
|
305
|
+
this.#logDebug("Initializing...");
|
|
306
306
|
this.#dispatch(WebRtcFsmEvent.INIT);
|
|
307
307
|
try {
|
|
308
308
|
this.#pc = this.#factory.createPeerConnection(this.#config.peerConfig);
|
|
309
|
-
this.#
|
|
309
|
+
this.#logDebug("Peer connection created");
|
|
310
310
|
this.#setupPcListeners();
|
|
311
311
|
// Setup device change detection now that we have a connection
|
|
312
312
|
this.#setupDeviceChangeListener();
|
|
313
313
|
if (this.#config.enableMicrophone) {
|
|
314
|
-
this.#
|
|
314
|
+
this.#logDebug("Enabling microphone (config enabled)");
|
|
315
315
|
const success = await this.enableMicrophone(true);
|
|
316
316
|
if (!success) {
|
|
317
317
|
this.#pubsub.publish(WebRtcManager.EVENT_MICROPHONE_FAILED, {
|
|
@@ -323,16 +323,17 @@ export class WebRtcManager {
|
|
|
323
323
|
// Always setup to receive audio, even if we don't enable microphone
|
|
324
324
|
// This ensures the SDP includes audio media line
|
|
325
325
|
this.#pc.addTransceiver("audio", { direction: "recvonly" });
|
|
326
|
-
this.#
|
|
326
|
+
this.#logDebug("Added recvonly audio transceiver");
|
|
327
327
|
}
|
|
328
328
|
if (this.#config.dataChannelLabel) {
|
|
329
|
-
this.#
|
|
329
|
+
this.#logDebug("Creating default data channel:", this.#config.dataChannelLabel);
|
|
330
330
|
this.createDataChannel(this.#config.dataChannelLabel);
|
|
331
331
|
}
|
|
332
|
-
this.#
|
|
332
|
+
this.#logDebug("Initialization complete");
|
|
333
333
|
}
|
|
334
334
|
catch (e) {
|
|
335
|
-
this.#
|
|
335
|
+
this.#logError(e);
|
|
336
|
+
this.#handleError(e);
|
|
336
337
|
}
|
|
337
338
|
}
|
|
338
339
|
/**
|
|
@@ -340,15 +341,15 @@ export class WebRtcManager {
|
|
|
340
341
|
* If disconnected, reinitializes the peer connection.
|
|
341
342
|
*/
|
|
342
343
|
async connect() {
|
|
343
|
-
this.#
|
|
344
|
+
this.#logDebug("connect() called, current state:", this.state);
|
|
344
345
|
// Initialize if needed
|
|
345
346
|
if (this.state === WebRtcState.IDLE) {
|
|
346
|
-
this.#
|
|
347
|
+
this.#logDebug("State is IDLE, initializing first");
|
|
347
348
|
await this.initialize();
|
|
348
349
|
}
|
|
349
350
|
// Reinitialize if disconnected (PeerConnection was closed)
|
|
350
351
|
if (this.state === WebRtcState.DISCONNECTED) {
|
|
351
|
-
this.#
|
|
352
|
+
this.#logDebug("State is DISCONNECTED, reinitializing");
|
|
352
353
|
// Clean up old connection
|
|
353
354
|
this.#cleanup();
|
|
354
355
|
// Reset to IDLE and reinitialize
|
|
@@ -359,10 +360,10 @@ export class WebRtcManager {
|
|
|
359
360
|
}
|
|
360
361
|
if (this.state === WebRtcState.CONNECTED ||
|
|
361
362
|
this.state === WebRtcState.CONNECTING) {
|
|
362
|
-
this.#
|
|
363
|
+
this.#logDebug("Already connected or connecting, skipping");
|
|
363
364
|
return;
|
|
364
365
|
}
|
|
365
|
-
this.#
|
|
366
|
+
this.#logDebug("Transitioning to CONNECTING");
|
|
366
367
|
this.#dispatch(WebRtcFsmEvent.CONNECT);
|
|
367
368
|
}
|
|
368
369
|
/**
|
|
@@ -371,19 +372,19 @@ export class WebRtcManager {
|
|
|
371
372
|
* @returns True if successful, false if failed to get user media.
|
|
372
373
|
*/
|
|
373
374
|
async enableMicrophone(enable) {
|
|
374
|
-
this.#
|
|
375
|
+
this.#logDebug("enableMicrophone() called:", enable);
|
|
375
376
|
if (enable) {
|
|
376
377
|
if (this.#localStream) {
|
|
377
|
-
this.#
|
|
378
|
+
this.#logDebug("Microphone already enabled");
|
|
378
379
|
return true;
|
|
379
380
|
}
|
|
380
381
|
try {
|
|
381
|
-
this.#
|
|
382
|
+
this.#logDebug("Requesting user media...");
|
|
382
383
|
const stream = await this.#factory.getUserMedia({
|
|
383
384
|
audio: true,
|
|
384
385
|
video: false,
|
|
385
386
|
});
|
|
386
|
-
this.#
|
|
387
|
+
this.#logDebug("User media obtained, tracks:", stream.getAudioTracks().length);
|
|
387
388
|
this.#localStream = stream;
|
|
388
389
|
this.#pubsub.publish(WebRtcManager.EVENT_LOCAL_STREAM, stream);
|
|
389
390
|
if (this.#pc) {
|
|
@@ -396,21 +397,21 @@ export class WebRtcManager {
|
|
|
396
397
|
await audioTransceiver.sender.replaceTrack(track);
|
|
397
398
|
// Update direction to sendrecv
|
|
398
399
|
audioTransceiver.direction = "sendrecv";
|
|
399
|
-
this.#
|
|
400
|
+
this.#logDebug("Replaced track in existing transceiver");
|
|
400
401
|
}
|
|
401
402
|
else {
|
|
402
403
|
// Add track normally
|
|
403
404
|
stream.getTracks().forEach((track) => {
|
|
404
405
|
this.#pc.addTrack(track, stream);
|
|
405
406
|
});
|
|
406
|
-
this.#
|
|
407
|
+
this.#logDebug("Added tracks to peer connection");
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
|
-
this.#
|
|
410
|
+
this.#logDebug("Microphone enabled successfully");
|
|
410
411
|
return true;
|
|
411
412
|
}
|
|
412
413
|
catch (e) {
|
|
413
|
-
this.#
|
|
414
|
+
this.#logError("Failed to get user media:", e);
|
|
414
415
|
this.#pubsub.publish(WebRtcManager.EVENT_MICROPHONE_FAILED, {
|
|
415
416
|
error: e,
|
|
416
417
|
});
|
|
@@ -419,10 +420,10 @@ export class WebRtcManager {
|
|
|
419
420
|
}
|
|
420
421
|
else {
|
|
421
422
|
if (!this.#localStream) {
|
|
422
|
-
this.#
|
|
423
|
+
this.#logDebug("Microphone already disabled");
|
|
423
424
|
return true;
|
|
424
425
|
}
|
|
425
|
-
this.#
|
|
426
|
+
this.#logDebug("Disabling microphone...");
|
|
426
427
|
this.#localStream.getTracks().forEach((track) => {
|
|
427
428
|
track.stop();
|
|
428
429
|
// Remove from PC if needed, or just stop sending
|
|
@@ -436,7 +437,7 @@ export class WebRtcManager {
|
|
|
436
437
|
});
|
|
437
438
|
this.#localStream = null;
|
|
438
439
|
this.#pubsub.publish(WebRtcManager.EVENT_LOCAL_STREAM, null);
|
|
439
|
-
this.#
|
|
440
|
+
this.#logDebug("Microphone disabled");
|
|
440
441
|
return true;
|
|
441
442
|
}
|
|
442
443
|
}
|
|
@@ -445,7 +446,7 @@ export class WebRtcManager {
|
|
|
445
446
|
* Transitions to DISCONNECTED state.
|
|
446
447
|
*/
|
|
447
448
|
disconnect() {
|
|
448
|
-
this.#
|
|
449
|
+
this.#logDebug("disconnect() called");
|
|
449
450
|
this.#cleanup();
|
|
450
451
|
this.#dispatch(WebRtcFsmEvent.DISCONNECT);
|
|
451
452
|
}
|
|
@@ -454,7 +455,7 @@ export class WebRtcManager {
|
|
|
454
455
|
* Cleans up all resources and allows reinitialization.
|
|
455
456
|
*/
|
|
456
457
|
reset() {
|
|
457
|
-
this.#
|
|
458
|
+
this.#logDebug("reset() called, current state:", this.state);
|
|
458
459
|
this.#cleanup();
|
|
459
460
|
// Reset from any non-IDLE state
|
|
460
461
|
if (this.state !== WebRtcState.IDLE) {
|
|
@@ -470,7 +471,7 @@ export class WebRtcManager {
|
|
|
470
471
|
this.#dispatch(WebRtcFsmEvent.RESET);
|
|
471
472
|
}
|
|
472
473
|
}
|
|
473
|
-
this.#
|
|
474
|
+
this.#logDebug("Reset complete, state:", this.state);
|
|
474
475
|
}
|
|
475
476
|
/**
|
|
476
477
|
* Creates a new data channel with the specified label.
|
|
@@ -480,24 +481,25 @@ export class WebRtcManager {
|
|
|
480
481
|
* @returns The created data channel, or null if peer connection not initialized.
|
|
481
482
|
*/
|
|
482
483
|
createDataChannel(label, options) {
|
|
483
|
-
this.#
|
|
484
|
+
this.#logDebug("createDataChannel() called:", label);
|
|
484
485
|
if (!this.#pc) {
|
|
485
|
-
this.#
|
|
486
|
+
this.#logDebug("Cannot create data channel: peer connection not initialized");
|
|
486
487
|
return null;
|
|
487
488
|
}
|
|
488
489
|
if (this.#dataChannels.has(label)) {
|
|
489
|
-
this.#
|
|
490
|
+
this.#logDebug("Returning existing data channel:", label);
|
|
490
491
|
return this.#dataChannels.get(label);
|
|
491
492
|
}
|
|
492
493
|
try {
|
|
493
494
|
const dc = this.#pc.createDataChannel(label, options);
|
|
494
495
|
this.#setupDataChannelListeners(dc);
|
|
495
496
|
this.#dataChannels.set(label, dc);
|
|
496
|
-
this.#
|
|
497
|
+
this.#logDebug("Data channel created:", label);
|
|
497
498
|
return dc;
|
|
498
499
|
}
|
|
499
500
|
catch (e) {
|
|
500
|
-
this.#
|
|
501
|
+
this.#logError(e);
|
|
502
|
+
this.#handleError(e);
|
|
501
503
|
return null;
|
|
502
504
|
}
|
|
503
505
|
}
|
|
@@ -519,11 +521,11 @@ export class WebRtcManager {
|
|
|
519
521
|
sendData(label, data) {
|
|
520
522
|
const channel = this.#dataChannels.get(label);
|
|
521
523
|
if (!channel) {
|
|
522
|
-
this.#
|
|
524
|
+
this.#logDebug(`Data channel '${label}' not found`);
|
|
523
525
|
return false;
|
|
524
526
|
}
|
|
525
527
|
if (channel.readyState !== "open") {
|
|
526
|
-
this.#
|
|
528
|
+
this.#logDebug(`Data channel '${label}' is not open (state: ${channel.readyState})`);
|
|
527
529
|
return false;
|
|
528
530
|
}
|
|
529
531
|
try {
|
|
@@ -532,7 +534,8 @@ export class WebRtcManager {
|
|
|
532
534
|
return true;
|
|
533
535
|
}
|
|
534
536
|
catch (e) {
|
|
535
|
-
this.#
|
|
537
|
+
this.#logError(e);
|
|
538
|
+
this.#handleError(e);
|
|
536
539
|
return false;
|
|
537
540
|
}
|
|
538
541
|
}
|
|
@@ -543,18 +546,19 @@ export class WebRtcManager {
|
|
|
543
546
|
* @returns The offer SDP, or null if peer connection not initialized.
|
|
544
547
|
*/
|
|
545
548
|
async createOffer(options) {
|
|
546
|
-
this.#
|
|
549
|
+
this.#logDebug("createOffer() called");
|
|
547
550
|
if (!this.#pc) {
|
|
548
|
-
this.#
|
|
551
|
+
this.#logDebug("Cannot create offer: peer connection not initialized");
|
|
549
552
|
return null;
|
|
550
553
|
}
|
|
551
554
|
try {
|
|
552
555
|
const offer = await this.#pc.createOffer(options);
|
|
553
|
-
this.#
|
|
556
|
+
this.#logDebug("Offer created:", offer.type);
|
|
554
557
|
return offer;
|
|
555
558
|
}
|
|
556
559
|
catch (e) {
|
|
557
|
-
this.#
|
|
560
|
+
this.#logError(e);
|
|
561
|
+
this.#handleError(e);
|
|
558
562
|
return null;
|
|
559
563
|
}
|
|
560
564
|
}
|
|
@@ -564,18 +568,19 @@ export class WebRtcManager {
|
|
|
564
568
|
* @returns The answer SDP, or null if peer connection not initialized.
|
|
565
569
|
*/
|
|
566
570
|
async createAnswer(options) {
|
|
567
|
-
this.#
|
|
571
|
+
this.#logDebug("createAnswer() called");
|
|
568
572
|
if (!this.#pc) {
|
|
569
|
-
this.#
|
|
573
|
+
this.#logDebug("Cannot create answer: peer connection not initialized");
|
|
570
574
|
return null;
|
|
571
575
|
}
|
|
572
576
|
try {
|
|
573
577
|
const answer = await this.#pc.createAnswer(options);
|
|
574
|
-
this.#
|
|
578
|
+
this.#logDebug("Answer created:", answer.type);
|
|
575
579
|
return answer;
|
|
576
580
|
}
|
|
577
581
|
catch (e) {
|
|
578
|
-
this.#
|
|
582
|
+
this.#logError(e);
|
|
583
|
+
this.#handleError(e);
|
|
579
584
|
return null;
|
|
580
585
|
}
|
|
581
586
|
}
|
|
@@ -585,18 +590,19 @@ export class WebRtcManager {
|
|
|
585
590
|
* @returns True if successful, false otherwise.
|
|
586
591
|
*/
|
|
587
592
|
async setLocalDescription(description) {
|
|
588
|
-
this.#
|
|
593
|
+
this.#logDebug("setLocalDescription() called:", description.type);
|
|
589
594
|
if (!this.#pc) {
|
|
590
|
-
this.#
|
|
595
|
+
this.#logDebug("Cannot set local description: peer connection not initialized");
|
|
591
596
|
return false;
|
|
592
597
|
}
|
|
593
598
|
try {
|
|
594
599
|
await this.#pc.setLocalDescription(description);
|
|
595
|
-
this.#
|
|
600
|
+
this.#logDebug("Local description set successfully");
|
|
596
601
|
return true;
|
|
597
602
|
}
|
|
598
603
|
catch (e) {
|
|
599
|
-
this.#
|
|
604
|
+
this.#logError(e);
|
|
605
|
+
this.#handleError(e);
|
|
600
606
|
return false;
|
|
601
607
|
}
|
|
602
608
|
}
|
|
@@ -606,18 +612,19 @@ export class WebRtcManager {
|
|
|
606
612
|
* @returns True if successful, false otherwise.
|
|
607
613
|
*/
|
|
608
614
|
async setRemoteDescription(description) {
|
|
609
|
-
this.#
|
|
615
|
+
this.#logDebug("setRemoteDescription() called:", description.type);
|
|
610
616
|
if (!this.#pc) {
|
|
611
|
-
this.#
|
|
617
|
+
this.#logDebug("Cannot set remote description: peer connection not initialized");
|
|
612
618
|
return false;
|
|
613
619
|
}
|
|
614
620
|
try {
|
|
615
621
|
await this.#pc.setRemoteDescription(description);
|
|
616
|
-
this.#
|
|
622
|
+
this.#logDebug("Remote description set successfully");
|
|
617
623
|
return true;
|
|
618
624
|
}
|
|
619
625
|
catch (e) {
|
|
620
|
-
this.#
|
|
626
|
+
this.#logError(e);
|
|
627
|
+
this.#handleError(e);
|
|
621
628
|
return false;
|
|
622
629
|
}
|
|
623
630
|
}
|
|
@@ -627,20 +634,21 @@ export class WebRtcManager {
|
|
|
627
634
|
* @returns True if successful, false otherwise.
|
|
628
635
|
*/
|
|
629
636
|
async addIceCandidate(candidate) {
|
|
630
|
-
this.#
|
|
637
|
+
this.#logDebug("addIceCandidate() called:", candidate ? "candidate" : "null (end-of-candidates)");
|
|
631
638
|
if (!this.#pc) {
|
|
632
|
-
this.#
|
|
639
|
+
this.#logDebug("Cannot add ICE candidate: peer connection not initialized");
|
|
633
640
|
return false;
|
|
634
641
|
}
|
|
635
642
|
try {
|
|
636
643
|
if (candidate) {
|
|
637
644
|
await this.#pc.addIceCandidate(candidate);
|
|
638
|
-
this.#
|
|
645
|
+
this.#logDebug("ICE candidate added");
|
|
639
646
|
}
|
|
640
647
|
return true;
|
|
641
648
|
}
|
|
642
649
|
catch (e) {
|
|
643
|
-
this.#
|
|
650
|
+
this.#logError(e);
|
|
651
|
+
this.#handleError(e);
|
|
644
652
|
return false;
|
|
645
653
|
}
|
|
646
654
|
}
|
|
@@ -650,19 +658,20 @@ export class WebRtcManager {
|
|
|
650
658
|
* @returns True if successful, false otherwise.
|
|
651
659
|
*/
|
|
652
660
|
async iceRestart() {
|
|
653
|
-
this.#
|
|
661
|
+
this.#logDebug("iceRestart() called");
|
|
654
662
|
if (!this.#pc) {
|
|
655
|
-
this.#
|
|
663
|
+
this.#logDebug("Cannot perform ICE restart: peer connection not initialized");
|
|
656
664
|
return false;
|
|
657
665
|
}
|
|
658
666
|
try {
|
|
659
667
|
const offer = await this.#pc.createOffer({ iceRestart: true });
|
|
660
668
|
await this.#pc.setLocalDescription(offer);
|
|
661
|
-
this.#
|
|
669
|
+
this.#logDebug("ICE restart initiated");
|
|
662
670
|
return true;
|
|
663
671
|
}
|
|
664
672
|
catch (e) {
|
|
665
|
-
this.#
|
|
673
|
+
this.#logError(e);
|
|
674
|
+
this.#handleError(e);
|
|
666
675
|
return false;
|
|
667
676
|
}
|
|
668
677
|
}
|
|
@@ -691,7 +700,8 @@ export class WebRtcManager {
|
|
|
691
700
|
return await this.#pc.getStats();
|
|
692
701
|
}
|
|
693
702
|
catch (e) {
|
|
694
|
-
this.#
|
|
703
|
+
this.#logError(e);
|
|
704
|
+
this.#handleError(e);
|
|
695
705
|
return null;
|
|
696
706
|
}
|
|
697
707
|
}
|
|
@@ -701,39 +711,40 @@ export class WebRtcManager {
|
|
|
701
711
|
this.#fsm.transition(event);
|
|
702
712
|
const newState = this.#fsm.state;
|
|
703
713
|
if (oldState !== newState) {
|
|
704
|
-
this.#
|
|
714
|
+
this.#logDebug("State transition:", oldState, "->", newState, "(event:", event + ")");
|
|
705
715
|
this.#pubsub.publish(WebRtcManager.EVENT_STATE_CHANGE, newState);
|
|
706
716
|
}
|
|
707
717
|
}
|
|
708
718
|
// deno-lint-ignore no-explicit-any
|
|
709
|
-
#
|
|
719
|
+
#logDebug(...args) {
|
|
710
720
|
if (this.#config.debug) {
|
|
711
721
|
this.#logger.debug("[WebRtcManager]", ...args);
|
|
712
722
|
}
|
|
713
723
|
}
|
|
714
724
|
// deno-lint-ignore no-explicit-any
|
|
715
725
|
#log(...args) {
|
|
716
|
-
|
|
717
|
-
this.#logger.log("[WebRtcManager]", ...args);
|
|
718
|
-
}
|
|
726
|
+
this.#logger.log("[WebRtcManager]", ...args);
|
|
719
727
|
}
|
|
720
728
|
// deno-lint-ignore no-explicit-any
|
|
721
|
-
#
|
|
729
|
+
#logWarn(...args) {
|
|
722
730
|
this.#logger.warn("[WebRtcManager]", ...args);
|
|
723
731
|
}
|
|
724
732
|
// deno-lint-ignore no-explicit-any
|
|
725
|
-
#
|
|
726
|
-
this.#logger.error("[WebRtcManager]",
|
|
733
|
+
#logError(...args) {
|
|
734
|
+
this.#logger.error("[WebRtcManager]", ...args);
|
|
735
|
+
}
|
|
736
|
+
// deno-lint-ignore no-explicit-any
|
|
737
|
+
#handleError(error) {
|
|
727
738
|
this.#dispatch(WebRtcFsmEvent.ERROR);
|
|
728
739
|
this.#pubsub.publish(WebRtcManager.EVENT_ERROR, error);
|
|
729
740
|
}
|
|
730
741
|
#setupPcListeners() {
|
|
731
742
|
if (!this.#pc)
|
|
732
743
|
return;
|
|
733
|
-
this.#
|
|
744
|
+
this.#logDebug("Setting up peer connection listeners");
|
|
734
745
|
this.#pc.onconnectionstatechange = () => {
|
|
735
746
|
const state = this.#pc.connectionState;
|
|
736
|
-
this.#
|
|
747
|
+
this.#logDebug("Connection state changed:", state);
|
|
737
748
|
if (state === "connected") {
|
|
738
749
|
// Connection successful - reset reconnect attempts and clear any pending timeout
|
|
739
750
|
this.#reconnectAttempts = 0;
|
|
@@ -757,7 +768,7 @@ export class WebRtcManager {
|
|
|
757
768
|
}
|
|
758
769
|
};
|
|
759
770
|
this.#pc.ontrack = (event) => {
|
|
760
|
-
this.#
|
|
771
|
+
this.#logDebug("Remote track received:", event.track.kind);
|
|
761
772
|
if (event.streams && event.streams[0]) {
|
|
762
773
|
this.#remoteStream = event.streams[0];
|
|
763
774
|
this.#pubsub.publish(WebRtcManager.EVENT_REMOTE_STREAM, this.#remoteStream);
|
|
@@ -765,17 +776,17 @@ export class WebRtcManager {
|
|
|
765
776
|
};
|
|
766
777
|
this.#pc.ondatachannel = (event) => {
|
|
767
778
|
const dc = event.channel;
|
|
768
|
-
this.#
|
|
779
|
+
this.#logDebug("Remote data channel received:", dc.label);
|
|
769
780
|
this.#setupDataChannelListeners(dc);
|
|
770
781
|
this.#dataChannels.set(dc.label, dc);
|
|
771
782
|
};
|
|
772
783
|
this.#pc.onicecandidate = (event) => {
|
|
773
|
-
this.#
|
|
784
|
+
this.#logDebug("ICE candidate generated:", event.candidate ? "candidate" : "null (gathering complete)");
|
|
774
785
|
this.#pubsub.publish(WebRtcManager.EVENT_ICE_CANDIDATE, event.candidate);
|
|
775
786
|
};
|
|
776
787
|
}
|
|
777
788
|
#cleanup() {
|
|
778
|
-
this.#
|
|
789
|
+
this.#logDebug("Cleanup started");
|
|
779
790
|
// Clear any pending reconnect timers
|
|
780
791
|
if (this.#reconnectTimer !== null) {
|
|
781
792
|
clearTimeout(this.#reconnectTimer);
|
|
@@ -800,25 +811,25 @@ export class WebRtcManager {
|
|
|
800
811
|
});
|
|
801
812
|
this.#dataChannels.clear();
|
|
802
813
|
if (dcCount > 0) {
|
|
803
|
-
this.#
|
|
814
|
+
this.#logDebug("Closed", dcCount, "data channel(s)");
|
|
804
815
|
}
|
|
805
816
|
// Stop local stream tracks
|
|
806
817
|
if (this.#localStream) {
|
|
807
818
|
this.#localStream.getTracks().forEach((track) => track.stop());
|
|
808
819
|
this.#localStream = null;
|
|
809
|
-
this.#
|
|
820
|
+
this.#logDebug("Local stream stopped");
|
|
810
821
|
}
|
|
811
822
|
// Close peer connection
|
|
812
823
|
if (this.#pc) {
|
|
813
824
|
this.#pc.close();
|
|
814
825
|
this.#pc = null;
|
|
815
|
-
this.#
|
|
826
|
+
this.#logDebug("Peer connection closed");
|
|
816
827
|
}
|
|
817
828
|
this.#remoteStream = null;
|
|
818
|
-
this.#
|
|
829
|
+
this.#logDebug("Cleanup complete");
|
|
819
830
|
}
|
|
820
831
|
#handleConnectionFailure() {
|
|
821
|
-
this.#
|
|
832
|
+
this.#logDebug("Handling connection failure");
|
|
822
833
|
// Only dispatch DISCONNECT if not already in a terminal state
|
|
823
834
|
if (this.state !== WebRtcState.DISCONNECTED &&
|
|
824
835
|
this.state !== WebRtcState.ERROR &&
|
|
@@ -827,13 +838,13 @@ export class WebRtcManager {
|
|
|
827
838
|
}
|
|
828
839
|
// Check if auto-reconnect is enabled
|
|
829
840
|
if (!this.#config.autoReconnect) {
|
|
830
|
-
this.#
|
|
841
|
+
this.#logDebug("Auto-reconnect disabled, not attempting reconnection");
|
|
831
842
|
return;
|
|
832
843
|
}
|
|
833
844
|
const maxAttempts = this.#config.maxReconnectAttempts ?? 5;
|
|
834
845
|
// Check if we've exceeded max attempts
|
|
835
846
|
if (this.#reconnectAttempts >= maxAttempts) {
|
|
836
|
-
this.#
|
|
847
|
+
this.#logDebug("Max reconnection attempts reached:", maxAttempts);
|
|
837
848
|
this.#pubsub.publish(WebRtcManager.EVENT_RECONNECT_FAILED, {
|
|
838
849
|
attempts: this.#reconnectAttempts,
|
|
839
850
|
});
|
|
@@ -850,7 +861,7 @@ export class WebRtcManager {
|
|
|
850
861
|
strategy,
|
|
851
862
|
});
|
|
852
863
|
if (!shouldProceed) {
|
|
853
|
-
this.#
|
|
864
|
+
this.#logDebug("Reconnection suppressed by shouldReconnect callback");
|
|
854
865
|
return;
|
|
855
866
|
}
|
|
856
867
|
}
|
|
@@ -865,7 +876,7 @@ export class WebRtcManager {
|
|
|
865
876
|
const delay = baseDelay * Math.pow(2, this.#reconnectAttempts - 1);
|
|
866
877
|
// Try ICE restart first (attempts 1-2), then full reconnect
|
|
867
878
|
const strategy = this.#reconnectAttempts <= 2 ? "ice-restart" : "full";
|
|
868
|
-
this.#
|
|
879
|
+
this.#logDebug("Attempting reconnection:", {
|
|
869
880
|
attempt: this.#reconnectAttempts,
|
|
870
881
|
strategy,
|
|
871
882
|
delay: delay + "ms",
|
|
@@ -903,13 +914,13 @@ export class WebRtcManager {
|
|
|
903
914
|
this.#fullReconnectTimeoutTimer = null;
|
|
904
915
|
// Only trigger failure if still not connected
|
|
905
916
|
if (this.state !== WebRtcState.CONNECTED) {
|
|
906
|
-
this.#
|
|
917
|
+
this.#logDebug("Full reconnection timeout reached, connection not established");
|
|
907
918
|
this.#handleConnectionFailure();
|
|
908
919
|
}
|
|
909
920
|
}, timeout);
|
|
910
921
|
}
|
|
911
922
|
catch (e) {
|
|
912
|
-
this.#
|
|
923
|
+
this.#logError("Reconnection failed:", e);
|
|
913
924
|
this.#handleConnectionFailure();
|
|
914
925
|
}
|
|
915
926
|
}
|
|
@@ -930,7 +941,7 @@ export class WebRtcManager {
|
|
|
930
941
|
this.#pubsub.publish(WebRtcManager.EVENT_DEVICE_CHANGED, devices);
|
|
931
942
|
}
|
|
932
943
|
catch (e) {
|
|
933
|
-
this.#
|
|
944
|
+
this.#logError("Error handling device change:", e);
|
|
934
945
|
}
|
|
935
946
|
};
|
|
936
947
|
navigator.mediaDevices.addEventListener("devicechange", this.#deviceChangeHandler);
|
|
@@ -954,7 +965,7 @@ export class WebRtcManager {
|
|
|
954
965
|
// Ignore "User-Initiated Abort" errors which occur during intentional close()
|
|
955
966
|
const isUserAbort = error?.error?.message?.includes("User-Initiated Abort");
|
|
956
967
|
if (!isUserAbort) {
|
|
957
|
-
this.#
|
|
968
|
+
this.#logError("Data channel error:", error);
|
|
958
969
|
this.#pubsub.publish(WebRtcManager.EVENT_ERROR, error);
|
|
959
970
|
}
|
|
960
971
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/webrtc",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.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.11.
|
|
16
|
+
"@marianmeres/fsm": "^2.11.1",
|
|
17
17
|
"@marianmeres/pubsub": "^2.4.4"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|